Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
michael@0 | 1 | This directory holds Python code to support debugging SpiderMonkey with |
michael@0 | 2 | GDB. It includes pretty-printers for common SpiderMonkey types like jsval, |
michael@0 | 3 | jsid, and JSObject, and makes GDB "see through" the SpiderMonkey rooting |
michael@0 | 4 | types like js::Rooted and JS::Handle. For example: |
michael@0 | 5 | |
michael@0 | 6 | (gdb) frame |
michael@0 | 7 | #0 js::baseops::SetPropertyHelper (cx=0xbf3460, |
michael@0 | 8 | obj=(JSObject * const) 0x7ffff150b060 [object global] delegate, |
michael@0 | 9 | receiver=(JSObject * const) 0x7ffff150b060 [object global] delegate, |
michael@0 | 10 | id=$jsid("x"), defineHow=4, vp=$jsval(1), strict=0) |
michael@0 | 11 | at /home/jimb/moz/archer/js/src/jsobj.cpp:4495 |
michael@0 | 12 | 4495 JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_UNQUALIFIED)) == 0); |
michael@0 | 13 | (gdb) |
michael@0 | 14 | |
michael@0 | 15 | Things to note here: |
michael@0 | 16 | |
michael@0 | 17 | - obj, a JS::HandleObject, prints as: |
michael@0 | 18 | obj=(JSObject * const) 0x7ffff150b060 [object global] delegate, |
michael@0 | 19 | This immediately shows the handle's referent, along with a JavaScript-like summary |
michael@0 | 20 | of the object. |
michael@0 | 21 | |
michael@0 | 22 | - id, a JS::HandleId, prints as: |
michael@0 | 23 | id=$jsid("x"), |
michael@0 | 24 | We show the handle's referent, and print the identifier as a string. |
michael@0 | 25 | |
michael@0 | 26 | - vp, a JS::MutableHandleValue, prints as: |
michael@0 | 27 | vp=$jsval(1) |
michael@0 | 28 | We show the handle's referent, using the jsval's tag to print it in its |
michael@0 | 29 | JavaScript form. |
michael@0 | 30 | |
michael@0 | 31 | You can still see the raw form of a value with 'print/r': |
michael@0 | 32 | |
michael@0 | 33 | (gdb) p/r obj |
michael@0 | 34 | $1 = {<js::HandleBase<JSObject*>> = {<No data fields>}, ptr = 0x7fffffffca60} |
michael@0 | 35 | (gdb) |
michael@0 | 36 | |
michael@0 | 37 | You can also use GDB's 'disable pretty-printer' command to turn off |
michael@0 | 38 | individual pretty-printers; try 'info pretty-printer' first. |
michael@0 | 39 | |
michael@0 | 40 | GDB should pick these extensions up automatically when you debug the shell, by |
michael@0 | 41 | auto-loading the 'js-gdb.py' file that js/src/shell/Makefile.in places in the |
michael@0 | 42 | same directory as the 'js' executable. You may need to add a command like the |
michael@0 | 43 | following to your '$HOME/.gdbinit' file: |
michael@0 | 44 | |
michael@0 | 45 | # Tell GDB to trust auto-load files found under ~/moz. |
michael@0 | 46 | add-auto-load-safe-path ~/moz |
michael@0 | 47 | |
michael@0 | 48 | If you do need this, GDB will tell you. |
michael@0 | 49 | |
michael@0 | 50 | In general, pretty-printers for pointer types include a summary of the |
michael@0 | 51 | pointer's referent: |
michael@0 | 52 | |
michael@0 | 53 | (gdb) b math_atan2 |
michael@0 | 54 | Breakpoint 1 at 0x542e0a: file /home/jimb/moz/archer/js/src/jsmath.cpp, line 214. |
michael@0 | 55 | (gdb) run |
michael@0 | 56 | js> Math.atan2('Spleen', 42) |
michael@0 | 57 | Breakpoint 1, math_atan2 (cx=0xbf3440, argc=2, vp=0x7ffff172f0a0) |
michael@0 | 58 | (gdb) print vp[0] |
michael@0 | 59 | $1 = $jsval((JSObject *) 0x7ffff151c0c0 [object Function "atan2"]) |
michael@0 | 60 | (gdb) print vp[1] |
michael@0 | 61 | $2 = $jsval((JSObject *) 0x7ffff150d0a0 [object Math]) |
michael@0 | 62 | (gdb) print vp[2] |
michael@0 | 63 | $3 = $jsval("Spleen") |
michael@0 | 64 | (gdb) print vp[3] |
michael@0 | 65 | $4 = $jsval(42) |
michael@0 | 66 | (gdb) |
michael@0 | 67 | |
michael@0 | 68 | We used to also have pretty-printers for the actual contents of a JSString |
michael@0 | 69 | struct, that knew which union branches were live and which were dead. These were |
michael@0 | 70 | more fragile than the summary pretty-printers, and harder to test, so I've |
michael@0 | 71 | removed them until we can see how to do better. |
michael@0 | 72 | |
michael@0 | 73 | There are unit tests; see 'Running the unit tests', below. |
michael@0 | 74 | |
michael@0 | 75 | I'd love for others to pitch in. GDB's Python API is documented in the GDB |
michael@0 | 76 | manual. |
michael@0 | 77 | |
michael@0 | 78 | I've recently rewritten the printers. The new code is simpler, and more |
michael@0 | 79 | robust; unit tests are easier to write; and the new test harness can run |
michael@0 | 80 | the tests in parallel. If a printer you'd contributed to in the past was |
michael@0 | 81 | dropped in the process, I apologize; I felt we should have good test |
michael@0 | 82 | coverage for any printer landed in-tree. You may also be interested in |
michael@0 | 83 | 'Personal pretty-printers', below. |
michael@0 | 84 | |
michael@0 | 85 | Directory layout |
michael@0 | 86 | ---------------- |
michael@0 | 87 | |
michael@0 | 88 | - js/src/gdb/mozilla: The actual SpiderMonkey support code. GDB auto-loads this |
michael@0 | 89 | when you debug an executable or shared library that contains SpiderMonkey. |
michael@0 | 90 | - js/src/gdb/tests: Unit tests for the above. |
michael@0 | 91 | - Each '.py' file is a unit test, to be run by js/src/gdb/run-tests.py. |
michael@0 | 92 | - Each '.cpp' file contains C++ code fragments for some unit test to use. |
michael@0 | 93 | - js/src/gdb/lib-for-tests: Python modules used by the unit tests. |
michael@0 | 94 | |
michael@0 | 95 | In js/src/gdb: |
michael@0 | 96 | |
michael@0 | 97 | - run-tests.py: test harness for GDB SpiderMonkey support unit tests. See |
michael@0 | 98 | 'Running the unit tests', below. |
michael@0 | 99 | - taskpool.py, progressbar.py: Python modules used by run-tests.py. |
michael@0 | 100 | - gdb-tests.cpp, gdb-tests.h: Driver program for C++ code fragments. |
michael@0 | 101 | - gdb-tests-gdb.py.in: Template for GDB autoload file for gdb-tests. |
michael@0 | 102 | |
michael@0 | 103 | Personal pretty-printers |
michael@0 | 104 | ------------------------ |
michael@0 | 105 | |
michael@0 | 106 | If you'd like to write your own pretty-printers, you can put them in a |
michael@0 | 107 | module named 'my_mozilla_printers' in a directory somewhere on your Python |
michael@0 | 108 | module search path. Our autoload code tries to import 'my_mozilla_printers' |
michael@0 | 109 | after importing our other SpiderMonkey support modules. For example: |
michael@0 | 110 | |
michael@0 | 111 | $ echo $PYTHONPATH |
michael@0 | 112 | /home/jimb/python |
michael@0 | 113 | $ cat ~/python/my_mozilla_printers.py |
michael@0 | 114 | import gdb |
michael@0 | 115 | from mozilla.prettyprinters import ptr_pretty_printer |
michael@0 | 116 | |
michael@0 | 117 | # Simple jschar * printer. Doesn't show address; chases null pointers. |
michael@0 | 118 | @ptr_pretty_printer('jschar') |
michael@0 | 119 | class jscharPtr(object): |
michael@0 | 120 | def __init__(self, value, cache): self.value = value |
michael@0 | 121 | def display_hint(self): return 'string' |
michael@0 | 122 | def to_string(self): |
michael@0 | 123 | c = u'' |
michael@0 | 124 | for i in xrange(50): |
michael@0 | 125 | if self.value[i] == 0: break |
michael@0 | 126 | c += unichr(self.value[i]) |
michael@0 | 127 | return c |
michael@0 | 128 | $ |
michael@0 | 129 | ... |
michael@0 | 130 | (gdb) whatis sample |
michael@0 | 131 | type = jschar [4] |
michael@0 | 132 | (gdb) print &sample[0] |
michael@0 | 133 | $1 = "Hi!" |
michael@0 | 134 | |
michael@0 | 135 | Running the unit tests |
michael@0 | 136 | ---------------------- |
michael@0 | 137 | |
michael@0 | 138 | These extensions have unit tests, invoked as follows: |
michael@0 | 139 | |
michael@0 | 140 | $ python run-tests.py [OPTIONS] LIBDIR [TESTS...] |
michael@0 | 141 | |
michael@0 | 142 | where LIBDIR is a directory containing a compiled SpiderMonkey library, |
michael@0 | 143 | libmozjs.so; TESTS are names of selected tests to run (if omitted, we run |
michael@0 | 144 | them all); and OPTIONS are drawn from the list below. |
michael@0 | 145 | |
michael@0 | 146 | --gdb=EXECUTABLE |
michael@0 | 147 | Instead of running whatever 'gdb' we find in our search path, use |
michael@0 | 148 | EXECUTABLE to run the tests. |
michael@0 | 149 | |
michael@0 | 150 | --srcdir=SRCDIR |
michael@0 | 151 | Find the sources corresponding to LIBDIR/libmozjs.so in SRCDIR. Without |
michael@0 | 152 | this option, we use the parent of the directory containing |
michael@0 | 153 | 'run-tests.py'. Note that SRCDIR must be a complete SpiderMonkey source |
michael@0 | 154 | directory, as our tests #include internal SpiderMonkey header files (to |
michael@0 | 155 | test pretty-printers for internal types, like parse nodes.) |
michael@0 | 156 | |
michael@0 | 157 | --testdir=TESTDIR |
michael@0 | 158 | Search for Python scripts and any accompanying C++ source code in |
michael@0 | 159 | TESTDIR. If omitted, we use the 'tests' directory in the directory |
michael@0 | 160 | containing 'run-tests.py'. |
michael@0 | 161 | |
michael@0 | 162 | --builddir=BUILDDIR |
michael@0 | 163 | Build the C++ executable that GDB debugs to run the tests in BUILDDIR. |
michael@0 | 164 | If omitted, create a 'gdb-tests' subdirectory of LIBDIR. |
michael@0 | 165 | |
michael@0 | 166 | (It is safe to use relative paths for LIBDIR, SRCDIR, and so on. They are |
michael@0 | 167 | always interpreted relative to the directory that was current when |
michael@0 | 168 | run-tests.py was started.) |
michael@0 | 169 | |
michael@0 | 170 | For example, since I build in a subdirectory 'obj~' of the 'js/src' |
michael@0 | 171 | directory, I use this command from 'js/src' to run the pretty-printer unit |
michael@0 | 172 | tests: |
michael@0 | 173 | |
michael@0 | 174 | $ python gdb/run-tests.py obj~ |
michael@0 | 175 | |
michael@0 | 176 | Writing new unit tests |
michael@0 | 177 | ---------------------- |
michael@0 | 178 | |
michael@0 | 179 | Each unit test consists of a Python script, possibly with some accompanying |
michael@0 | 180 | C++ code. Running tests works like this: |
michael@0 | 181 | |
michael@0 | 182 | - The run-tests.py script calls 'make' in 'BUILDDIR/gdb' to build |
michael@0 | 183 | 'gdb-tests'. |
michael@0 | 184 | |
michael@0 | 185 | - Then, for each '.py' test script in js/src/gdb/tests, the harness starts |
michael@0 | 186 | GDB on the 'gdb-tests' executable, and then has GDB run |
michael@0 | 187 | js/src/gdb/lib-for-tests/prolog.py, passing it the test script's path as |
michael@0 | 188 | its first command-line argument. |
michael@0 | 189 | |
michael@0 | 190 | Thanks To: |
michael@0 | 191 | ---------- |
michael@0 | 192 | |
michael@0 | 193 | - David Anderson |
michael@0 | 194 | - Steve Fink |
michael@0 | 195 | - Chris Leary |
michael@0 | 196 | - Josh Matthews |
michael@0 | 197 | - Jason Orendorff |
michael@0 | 198 | - Andrew Sutherland |