Dumping Bytecode with SpiderMonkey

While trying to implement my code coverage tool, SpiderMonkey's -D flag was brought to my attention.

You need to have a debug build of SpiderMonkey. You can find instructions on how to get the source and build here.

For the given input file foo.js:

var a = 1 + 1;
var b = 2 + 2;
var c = a + b;

Running with the command-line switch -D gives:

$ js -D foo.js
--- PC COUNTS foo.js:1 ---
loc counts x line op
----- ---------------- ---- --
00000:1/0/0 x 1 bindgname "a"
00003:1/0/0 x 1 int8 2
00005:1/0/0 x 1 setgname "a"
00008:0/0/0 x 1 pop
00009:1/0/0 x 2 bindgname "b"
00012:1/0/0 x 2 int8 4
00014:1/0/0 x 2 setgname "b"
00017:0/0/0 x 2 pop
00018:1/0/0 x 3 bindgname "c"
00021:1/0/0 x 3 getglobal "a"
00024:1/0/0 x 3 getglobal "b"
00027:1/0/0 x 3 add
00028:1/0/0 x 3 setgname "c"
00031:0/0/0 x 3 pop
00032:1/0/0 x 3 stop

--- END PC COUNTS foo.js:1 ---

Useful and interesting.


sfink said...

Yeah, I meant to point that out earlier too.

The jsdbg2 branch is working on providing a nicer JS interface to the underlying data. See https://bugzilla.mozilla.org/show_bug.cgi?id=671602

I think the platform really ought to provide some sort of functionality for helping with code coverage. The -D flag (which mostly maps to JSOPTION_PCCOUNT) is a fairly raw facility. If you can come up with nicer way to expose the info, I'd be happy to help discuss/design/implement it. (I wrote the -D support.)

For example, it would be pretty easy to make an option or environment variable that would spew out filename:lineno pairs to a file whenever a script was destroyed.

Your CoverageHook forces running in the interpreter, which may be good enough but is eventually going to suck for performance. You could augment it slightly to use "single step mode" and gain support for the method JIT, but I still think the JSOPTION_PCCOUNT route is the better route.

Donovan Preston said...

Thanks sfink. I ended up writing a patch which gives -D an argument which is the file to write the disassembly to instead of stdout.


Then I changed my coverage parser to parse the -D format. The benefit of this is I can tell the difference between lines that simply weren't executed and lines that aren't executable. The output is very pretty.


Another advantage is that I also get the count of the number of bytecodes that were executed on each line. This is visible in the screenshot above in the right column. I was also able to do a quick sort on this column and produce some profiler type output.