Hacker News new | past | comments | ask | show | jobs | submit login
Debugging in the Time of JITs (rfrn.org)
62 points by steeples on Dec 10, 2014 | hide | past | favorite | 9 comments



Interesting article. I'm writing a toy trace compiler at the moment and implementing side exits brings up many of the same, rather irritating problems.


Would be fun to try to compare this with a decent GC and see what the difference is likely to be. The reason why I say this is because Javas default GC pre-allocates a heap (a Java allocation is mostly just bumping a pointer), and since it "frees" memory by copying survivors to a new space you also avoid fragmentation.

If it turns out that what you mostly pay for, when using a GC, is that collection happens at one point in time instead of incrementally, then Go's plan for the evolution of their GC just became a lot more interesting.


Ahh, commented on the wrong article... Was wondering why I was downvoted :P


It seems to me that we should not make the problem more complicated than necessary. If you're debugging code written in Javascript, you may assume that the JIT optimizer is correct. If you need special features enabled (such as stack traces, etc.), then just pepper the code that enters the JIT with logging instructions, etc.

There should never be a need to completely disable the JIT optimizer, except perhaps to debug the JIT itself.


Your statement doesn't seem to make much sense to me.

Here's the example as described in the article: your page is stuck in a busy loop, but you don't have the debugger active. You'd like to debug this without needing to find a repro with the debugger active. But the JIT's compiled code doesn't expose enough information for Firefox's debugger to work with.

How is your statement relevant? To enable the debugger to work on code that is currently in a busy loop in JITted code, it needs to introspect stack frames, and modify code (one way or another, for breakpoints). The current JIT design doesn't permit this. How do you propose doing these things in a less complicated way? Either you deoptimize the existing frames (which the JIT apparently already has support for, and is described in the article) or you annotate generated code with extra debug info (which AFAIK it doesn't have support for).

I also don't know what you mean by "pepper the code that enters the JIT" in the context of stack traces. Having the stack trace of the code that entered the JIT is not necessarily the same stack trace for code that is running entirely in JITted code.


The issue with debugging JITed code is not that the optimizer may be incorrect, but rather that optimization wreaks havoc on the correspondence between source code and what actually executes – things happen in a different order or not at all, stack frames don't correspond to function calls, etc. This makes the debugging experience pretty confusing. To get a sense of this, try using gdb/lldb on code compiled at -O3 with -g turned on so that you still have debugging symbols. Things are weird. When debugging AOT compiled code, one avoids this confusing experience by recompiling the entire program in debug mode with optimizations off. In a JIT environment, what do you do? This article describes a clever approach: dynamically deoptimize the stack and transfer control from optimized code to non-optimized code, then debug the simple, non-optimized version of your program where what's executed lines up nicely with the source code.


Things happening out of order should never be observable. So this should not be an issue.


The only reason things happening out of order is invisible is because implementors jump through the kinds of hoops described in the article.

One way to understand the requirement for deoptimization is to to think about where the programmer can observe the difference between an optimized construct and the naive version. In regular execution there are relatively few places where the state of the program is observable, allowing freedom to aggressively optimize - under the debugger there are no places where the state of the program is unobservable.


You may be thinking of CPUs doing out-of-order execution, which is indeed unobservable. Compilers, however, also reorder operations which is very much observable.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: