Hi everybody.
Today I would like to
tell you a little story
about how I solved a
bug in a couple of hours
instead of a couple of weeks.
I work on Qt, the framework.
As you may know, Qt is a C++ framework,
but today's story doesn't start from C++,
it actually starts from JavaScript.
(audience member comments)
Right.
How many of you know about JavaScript?
- [Audience Member] No one.
No one, great, okay.
So, in JavaScript, what happens
if you try to print that?
That's nothing surprising,
it's a string comparison
and out with the result.
And yes, those two strings
are not the same string,
that's false,
and if you're really,
really good at JavaScript,
you may notice that's a double
equal operator in JavaScript.
That's also a triple equal
operator but that's still false,
don't worry about that.
So everything is fine.
What does this story has to do with Qt?
Well, in Qt there is a JavaScript engine,
actually there are several
JavaScript engines.
There is V8 used by Chromium Blink,
there is JavaScriptCore used by WebKit,
there is V4 used by QML.
It's called V4 because it
runs half as fast as V8.
(audience laughs)
That joke always works.
Maybe others.
So, today's talk is about that engine V4.
So in V4, if you try to
print that string comparison,
that prints true.
Whoa, yeah, here we go.
So somebody said "Can we please fix this?"
Because of course, it's embarrassing,
and I said, "I know
nothing about JavaScript,
"not to mention JavaScript engines,
"so I have no idea where to tackle this."
So the first thing I did
was I got back into C++.
The JavaScript engine in Qt has a C++ API
that you can use to
script your application.
So I built a test case,
which just builds an object,
sets a property 100 on it, and
then prints it and it works.
But if you set the property,
which has a weird name,
then this prints in the bottom slide
an entirely different thing.
There is clearly something wrong there.
So okay, here we go.
I open GDB and start debugging
the JavaScript engine in Qt.
Well, that's not exactly a trivial task.
I don't know if you ever tried
to debug a JavaScript engine,
but a JavaScript engine is anything but
a traditional code base.
In particular a JavaScript engine,
a modern JavaScript engine will
use all sorts of trickeries
to optimize the performance.
Every time a standard says,
"Yes this is undefined behavior
but in practice it works,
"let's use it, why not?"
Let's tag all the pointers,
because when you allocate a pointer,
pointers have extra bits of unused space,
let's put information in there
and so GDB crashes in my
face a couple of times,
I couldn't go anywhere.
Great, so I was totally lost.
I had no idea how to do and I said,
"Okay, I need to learn about this engine,
"this is going to take me forever."
Then I had an idea, a terrible idea.
So I thought this, I
got a working test case,
I got a broken test case.
Could it be possible for me
to somehow trace down the
execution of both test cases
and figure out where
the execution diverges?
Because you know what,
eventually the test cases
should be deterministic
more or less.
So could it be possible to find out?
There is a way.
There is a way that has
nothing to do with debugging,
because that way, that
has to do with profiling.
How many of you know about
the minus pg option to gcc or Clang?
Yeah, some of you do.
So the pg option tells the
gcc to basically instrument
all your function calls in a way that,
every time you enter a
function or you exit a function
you can take track of that.
This is usually used by profiler
to figure out how long
you're inside a function.
I do not need that.
I just want to know which
functions I was working through
and what was the argument's
possible dysfunction
or the return values.
There is a tool that
does exactly that for me.
The tool is called uftrace,
it's an awesome tool.
There is a poster about this
downstairs, so go see that.
So uftrace is a small tool
that does exactly that.
It uses the information set in
place by pg and extracts it.
So this is an example of
an applicaton that calls.
Main calls a, a calls b, b calls c,
and uftrace is able to print that.
Great, I can use this.
I can apply uftrace to both
test cases and what if?
Why not?
Well, if it's kind of complicated
I don't want to get there.
So uftrace is actually able
to dump a Chromium flamegraph
of the function calls.
So I was able to do some
sort of visual comparison.
That was easy for me.
So I did exactly that, I dumped
everything into Chromium,
I opened it up.
On the top you can see
a working test case,
on the bottom you can
see the broken test case.
I just played a little
bit with zoom and pan
until I could identify
that region on the right
where the function call
are totally different.
And I could just zoom in and
identify the line of code
where there was a divergence.
It was that line over there.
In one case the if was taken,
in the other case the if was not taken.
And that's pretty much it.
Ultimately it was an overflow
detection that didn't work.
I fixed the overflow
detection and I fixed this bug
in two hours instead of two weeks.
Thank you very much.
(audience applauds)
