How do you use debuggers?

FYI, this thread is old, and you can now debug your code using @enter macro in Juno. Although there is not a working breakpoint, the step to selected line somehow eases the debug procedure. I guess there will be a full-featured debugger after Juliav1.0 is released. Please ignore this comment if you already know :slight_smile:

4 Likes

@Gnimuc - thanks.
I saw the thread was old while I was looking to see why my debugger wasn’t working on JuliaPro-0.6.2.1 :slight_smile:
(I asked on the ASTInterpreter2 page, but if you can provide a working example that would be awesome - all I got was that “@enter” was undefined or other errors)

I think the consensus was that it was just incredibly hard to get a stable debugger while Julia itself was still undergoing such rapid development. My impression was that the project was abandoned with the intention of starting in earnest once 0.7 was out.

2 Likes

I fervently hope strong debugger support and development continues.

I am surprised by the many comments saying that one can just add print() statements and debug by thinking logically. While that is true, the debugger allows one to do the same thing a million times faster.

With a debugger, I don’t have to stop and add a print statement at every step I think I might want to examine some values; I merely step through the code and can immediately examine as I go. Often the problem turns out not to be where I would have first added a print statement, but somewhere else, which I would have found only after the 5th or 10th round of sprinkling print() statements into my code.

Print statements can indeed get you there. The point is that debuggers can get you there so much faster.

As a parallel example, I can take the mean of 10,000 numbers in my head. But a mean() function gets me there so much faster.

For my own programming practice, debuggers are essential to moving fast.

6 Likes

Everyone agrees that a debugger is desirable. Making a debugger work really well in a JIT language is very hard and expensive project.

3 Likes

How does the JIT make it any harder?

1 Like

In an interpreter, all code is data and the stack is a literal stack of interpreter states which you can walk and trivially examine and modify values. The executions of code in an interpreter mirrors the code pretty much exactly.

Debugging compiled code is far harder. Values may or may not even be explicitly stored anywhere. You can’t really debug C code at all without recompiling your entire program and causing its execution to become much slower.

Debugging JIT code is even harder. This is partly because the tooling is less well developed and tested. There are plenty of compilers that can generated debug code and plenty of JITs but JIT compilers that can generate debug code? Not so much. LLVM’s JIT couldn’t generate usable debug code until @Keno fixed it to do so and it’s still pretty broken, unfortunately.

A debugger for a JIT language is also held to a higher standard than one for an interpreted language or a compiled language: people exepect the features of a debugger for an interpreted language but if it was as slow as that then it would be unusable so they still want the speed of a compiled language. If we recompiled everything in debug mode like one does in a compiled lamguage, then you’d have to wait as long as it takes to rebuild Julia whenever you decided to use the debugger.

There are ways to work though all of these issues and get a good debugger for a JIT language, but it is an extremely hard problem and most high quality debuggers for JIT languages cost multiple millions of dollars.

12 Likes

Right, but is JIT-compiled any harder than AOT-compiled? Compiled Common Lisp implementations usually had great debuggers, and its core semantics look pretty close to Julia


1 Like

See my edits. I didn’t say it was impossible. It’s just hard and costly. Compiled Lisps were usually quite expensive. And more people saying they want it doesn’t get us any closed to having one.

1 Like

Agree that it’s a hard problem.

It may not be a fair comparison
 how does Java end up with excellent debugging capabilities when the JVM heavily JIT compile code? (probably because they have 100x more developers working on it :slight_smile: )

1 Like

Is there a way to make debugging possible by taking a performance hit? // like running code in debug mode

Implement an interpreter, then run your code in the interpreter. It will, however, be truly unusable since interpreted Julia is much slower than Python or R.

1 Like

The 100 billion dollars that have been spent on Java may have something to do with it.

6 Likes

Just for curiosity, is there any reason why julia is uniquely bad for efficient interpreters?

Yes, Julia’s dispatch semantics are much fancier than any other dynamic language. By allowing people to express more program behavior with dispatch, the compiler can understand and optimize it much better. But if you don’t do that analysis and optimization and do fully dynamic dispatch all the time, it is really, really slow.

5 Likes

I wonder what kind of tooling will be enabled by GitHub - JuliaLabs/Cassette.jl: Overdub Your Julia Code. It can essentially “run code in debug mode”, AFAICT.

1 Like

That depends on what level of debugging you need - even with all optimizations turned on, you can tell the compiler to keep enough debugging information so that you can still get a lot of good information debugging and step through code without slowing it down or recompiling.
That is often enough to help you solve the problem, along with code inspection, and if not, you can either add assertions just in the part of the code where the problem is occurring, or recompile just that module with full debugging information (turning off optimizations that prevent full debugging).

Both in C and Julia, sometimes, if you are dealing with a set of cooperating processes (possibly on multiple machines), then the only way of debugging isn’t with a debugger at all, and you need good logging facilities, and the ability to fine-tune just which areas are logged (much like setting a breakpoint in a debugger) (that’s how we developed our Julia code when I was at Dynactionize, since that was all designed around different REST services (mostly written in Julia, communicating via ZMQ).

One feature that I really enjoyed in InterSystems’ CachĂ© ObjectScript, was the ability to set a breakpoint (including conditional breakpoints) on any CachĂ© process running on the machine, connect to the process, and debug it, single stepping, displaying and modifying variables, etc.
That’s something I’d love to see in Julia some day.

Why couldn’t both “release” and “debug” forms be compiled for methods, and then if a particular method needed to be debugged, the calls could switch to calling the debug form? Alternatively, for a debug build, it would compile both forms, and simply have check of a debugging mask at the top of each method, that normally runs the release code, but if the specific bit is set (possibly kept in a register), jump to the debug code.
There are many ways that this can be optimized, so that switching between fast and “debug” code is fairly fast.

I have a feeling that with Julia’s hybrid JIT/AOT nature, this could be handled better than any other languages.

1 Like

There’s no shortage of ideas or possibile solutions. There is a shortage of people with the skill and time to do the work.

9 Likes

You seemed to be making it out as if the problem would be too difficult do be tackled without loads of money and people. My point is that it might not be such a difficult task as you have presented.

Almost all of the activities of people using the “inspector” usage pattern are simple, so Gallium/Juno doesn’t need to add in fancy features or things focused on true debugging (e.g. stacktrace connections on an assert, etc.).

Since this thread is seeing something of a revival, I just wanted to post my full endorsement of @jlperla’s explanation here. When I’m utilizing someone else’s code or learning how to use a new package for the first time, being able to ‘inspect’ the code through a debugger is extremely helpful (particularly in time-constrained work scenarios). Even with thorough documentation, I find it easier to reason through the code while going along with the computation.

I guess this might be related to different modes of learning/mastering a particular subject? For example: I don’t tend to pick up a subject well through reading about it alone. I come to a much more thorough understanding through having a conversation with the subject matter expert, going away and trying something out, failing and coming back to ask pointed questions. In that sense, while I can reason through the code as written (i.e. non-interactively) it can be difficult to understand where my lack of knowledge is hampering my understanding of the code since I’m not getting any feedback from the code beside what is right there on the screen and in my brain (this is assuming I don’t have access to the person/group that wrote the code).