{"id":384,"date":"2025-05-07T10:47:57","date_gmt":"2025-05-07T17:47:57","guid":{"rendered":"https:\/\/blog.mozilla.org\/sfink\/?p=384"},"modified":"2025-05-07T10:47:57","modified_gmt":"2025-05-07T17:47:57","slug":"sinful-debugging","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/sfink\/2025\/05\/07\/sinful-debugging\/","title":{"rendered":"Sinful Debugging"},"content":{"rendered":"<p>Recently, I was debugging my SpiderMonkey changes when running a JS test script, and got annoyed at the length of the feedback cycle: I&#8217;d make a change to the test script or the C++ code, rerun (under rr), go into the debugger, stop execution at a point where I knew what variable was what, set convenience variables to their pointer values, then run to where the interesting stuff was happening.<\/p>\n<p>One part I had already solved: if I have some JS variables, say <code>base<\/code> and <code>str<\/code>, and I want to capture their pointer values in the debugger, I&#8217;ll call <code>Math.sin(0, base, str)<\/code> and set a breakpoint on <code>math_sin<\/code>. Why the leading 0? In the past, I&#8217;d run into problems when <code>Math.sin<\/code> converted its first argument to a number, which disrupted what I was trying to look at. So now I feed it a number first and put my &#8220;real&#8221; stuff after it where it&#8217;s ignored, even when it doesn&#8217;t matter (and it usually doesn&#8217;t).<\/p>\n<p>But it&#8217;s painful to get set up again. It looks something like:<\/p>\n<pre>\r\n(rr) b math_sin\r\n(rr) c # ontinue\r\n(rr) p vp[3].toString()\r\n$1 = (JSString *) 0x33315dc003c0\r\n(rr) set $base=$\r\n(rr) p vp[4].toString()\r\n$2 = (JSString *) 0x33315dc01828\r\n(rr) set $str=$\r\n<\/pre>\n<p>Yay, now I can do things like <code>p $str->dump()<\/code> and it will work!<\/p>\n<p>But even worse, sometimes I&#8217;d want to add or remove variables. So I hacked around it:<\/p>\n<p>First, instead of just passing in the variables, I pass in names along with them. An actual example:<\/p>\n<pre>\r\nMath.sin(0, \"ND2\", ND2, \"TD3\", TD3, \"NB4\", NB4, \"NB5\", NB5, \"TD6\", TD6);<\/pre>\n<p>(yes, those names mean something to me). Then the setup becomes more like:<\/p>\n<pre>\r\n(rr) b math_sin\r\n(rr) c # ontinue\r\n(rr) p vp[3]\r\n$7 = $JS::Value(\"ND2\")\r\n(rr) set $ND2=vp[4].toString()\r\n(rr) p vp[5]\r\n$7 = $JS::Value(\"TD3\")\r\n(rr) set $ND2=vp[6].toString()\r\n<\/pre>\n<p>Ok, that&#8217;s longer, and requires cutting &#038; pasting. I know, shut up.<\/p>\n<p>The next set is to automate with a gdbinit script. Here&#8217;s a slightly modified version of mine:<\/p>\n<pre>\r\ndefine mlabel\r\n  set $_VP=vp\r\n  python\r\nimport re\r\nargc = int(gdb.parse_and_eval(\"argc\"))\r\nfor i in range(3, argc + 2, 2):\r\n  namer = f\"$_VP[{i}]\"\r\n  m = re.search(r'::Value\\(\"(.*?)\"',\r\n                str(gdb.parse_and_eval(namer)))\r\n  if not m:\r\n    print(f\"Failed to match: {namer}\")\r\n    continue\r\n  name = m.group(1)\r\n  setter = f\"set ${name}=$_VP[{i+1}].toGCThing()\"\r\n  gdb.execute(setter)\r\nend\r\nend\r\ndocument mlabel\r\nSpecial-purpose tool for grabbing out things passed to Math.sin(0, \"name1\", val1, \"name2\", ...) and converting them to labels.\r\nend\r\n<\/pre>\n<p>&#8220;mlabel&#8221; stands for &#8220;multi-label&#8221;, because it&#8230; well, it doesn&#8217;t label anything, but in my <strong>real<\/strong> version, it runs <a href=\"https:\/\/github.com\/hotsphink\/sfink-tools\/blob\/master\/conf\/gdbinit.py#L146-L148\">my own command<\/a> <\/p>\n<pre>label name=value<\/pre>\n<p> that does some other magic besides setting a <a href=\"https:\/\/sourceware.org\/gdb\/current\/onlinedocs\/gdb.html\/Convenience-Vars.html\">gdb convenience variable<\/a> (yes, they&#8217;re actually called that).<\/p>\n<p>I don&#8217;t remember why I went through the extra step of setting a <code>$_VP<\/code> variable rather than using <code>vp<\/code> directly. But it&#8217;s probably specific to my scenario, so you&#8217;ll have to adapt this anyway. This post is meant more to give you an idea.<\/p>\n<p>The result is my JS test code talking to my gdb session and spilling its secrets. Now when I&#8217;m debugging the interesting stuff, I can do <code>(rr) print $TD3->dump()<\/code> and it will do something useful.<\/p>\n<p>Here&#8217;s a log of an actual session:<\/p>\n<pre>\r\n--------------------------------------------------\r\n ---> Reached target process 3902272 at event 14.\r\n--------------------------------------------------\r\n(rr) Working directory \/home\/sfink\/src\/mozilla-ff\/js\/src\/jit-test\/tests\/gc.\r\n(rr) pretty\r\nLoading JavaScript value pretty-printers; see js\/src\/gdb\/README.\r\nIf they cause trouble, type: disable pretty-printer .* SpiderMonkey\r\nSpiderMonkey unwinder is disabled by default, to enable it type:\r\n\tenable unwinder .* SpiderMonkey\r\n(rr) b math_sin\r\nBreakpoint 1 at 0x564d91b3942b: file \/home\/sfink\/src\/mozilla-ff\/js\/src\/jsmath.cpp, line 649.\r\n(rr) c\r\nContinuing.\r\n\r\nThread 1 hit Breakpoint 1, math_sin (cx=cx@entry=0x7f20e5d3a200, argc=11, vp=0x7f20d5ba8168)\r\n    at \/home\/sfink\/src\/mozilla-ff\/js\/src\/jsmath.cpp:649\r\nstopped at breakpoint 1: (N\/A) -> (N\/A)\r\n(rr) mlabel\r\nall occurrences of 0x33315dc003c0 will be replaced with $ND2 of type js::gc::Cell *\r\nall occurrences of 0x38deaf678670 will be replaced with $TD3 of type js::gc::Cell *\r\nall occurrences of 0x33315dc00340 will be replaced with $NB4 of type js::gc::Cell *\r\nall occurrences of 0x33315dc00188 will be replaced with $NB5 of type js::gc::Cell *\r\nall occurrences of 0x38deaf678658 will be replaced with $TD6 of type js::gc::Cell *\r\n(rr) b promoteString\r\nBreakpoint 2 at 0x564d92737698: file \/home\/sfink\/src\/mozilla-ff\/js\/src\/gc\/Tenuring.cpp, line 882.\r\n(rr) c\r\nContinuing.\r\n\r\nThread 1 hit Breakpoint 2, js::gc::TenuringTracer::promoteString (this=this@entry=0x7ffcf5f9cb40, \r\n    src=\"MY YOUNGEST MEMORY IS OF A TOE, A GIANT BLUE TOE, IT MADE FUN OF ME INCESSANTLY BUT THAT DID NOT BOTHER ME IN THE LEAST. MY MOTHER WOULD HAVE BEEN HORRIFIED, BUT SHE WAS A GOOSE AND HAD ALREADY LAID T\"...)\r\n    at \/home\/sfink\/src\/mozilla-ff\/js\/src\/gc\/Tenuring.cpp:882\r\nstopped at breakpoint 2: (N\/A) -> (N\/A)\r\n(rr) p (void*)src\r\n$1 = (void *) $NB5\r\n(rr) \r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Recently, I was debugging my SpiderMonkey changes when running a JS test script, and got annoyed at the length of the feedback cycle: I&#8217;d make a change to the test script or the C++ code, rerun (under rr), go into the debugger, stop execution at a point where I knew what variable was what, set [&hellip;]<\/p>\n","protected":false},"author":206,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[228,666,137,610],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/sfink\/wp-json\/wp\/v2\/posts\/384"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/sfink\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/sfink\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/sfink\/wp-json\/wp\/v2\/users\/206"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/sfink\/wp-json\/wp\/v2\/comments?post=384"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/sfink\/wp-json\/wp\/v2\/posts\/384\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/sfink\/wp-json\/wp\/v2\/media?parent=384"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/sfink\/wp-json\/wp\/v2\/categories?post=384"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/sfink\/wp-json\/wp\/v2\/tags?post=384"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}