Extremely slow debugging in VS Code

Is the VSCode plugin based on Interpreter.jl ? The remark “unoffical workaround” was referring to Infiltrator.jl which is a very different piece of SW.

1 Like

Sorry to butt in, but I never knew about the “Apply default compiled modules/functions” bit. Suddenly the debugger is usable - thanks!

5 Likes

The ALL_MODULES_EXCEPT_MAIN method has never worked for me outside of standalone scripts though.

Are you sure the modules are actually interpreted? For me, all the modules are listed as interpreted under “All”, but they are not. E.g. Plots is listed there, but if I put a breakpoint in the plot function, it doesn’t work. If I remove ALL_MODULES_EXCEPT_MAIN from “Compiled”, the breakpoint works.

And this setup for my own package seems to work:

Yep, me too. Crashing frequency and non-stoping-at-breakpoints (or keep stopping on cleared ones) seems to have increased over time.

As it has been stated several times in past reincarnations of this discussion, a debugger a lot more than just a debugging tool. It’s a tool to understand code flows. I simply can’t understand how people can pretend that testing or variations of printf are alternatives.

6 Likes

I did recently fix a few more bugs in the VS Code link to the debugger, but I can’t push it out at the moment because there is another part in the extension that needs a few more bug fixes before I can tag a new prerelease build.

I think there are broadly two “big” ideas on the table at the moment how the debugger could be made more performant for good. One is to stick with the basic current design of an interpreter, but make some drastic changes to how it works to make it faster. I think WIP: a new serialization format for optimizing & executing lowered IR by timholy · Pull Request #309 · JuliaDebug/JuliaInterpreter.jl · GitHub was the start of something along those lines. The other big idea is to go more in the direction of other native-code debuggers, i.e. probably something like emit a debug version of the compiled code, and then work with that (maybe with similar tricks of handling breakpoints etc as a native code debugger). AFAIK there is no one working on either of those options at the moment.

The other question is whether there are more short-term things one could do to improve the situation. One reason debuggers in interpreted languages like Python have a much better out-of-the-box experience is that they automatically have pretty sensible defaults in terms of which code parts to “skip” from debugging: if you read a CSV file from Python, most likely that code is written in C, and the interpreter debugger won’t try to step through that, but just run the compiled version of that. In Julia most things are written in Julia itself, so we don’t have these “pretty reasonable” points in the code where one jumps out of the interpreter and just runs a compiled version of things. But maybe we could improve that situation: one option might be that in DebugAdapter.jl we just keep a list of methods in popular packages that should be compiled. Or maybe a package could somehow itself declare which methods should be added to the list of compiled methods. It might well be that if we can by default have things like file loading etc compiled that the entire experience would be quite a bit better for newcomers.

13 Likes

Don’t know if in the same spirit of the list of compiled methods but one thing I find very annoying is that when stepping into functions (F11) I have to keep going to “Step into target” because otherwise I have to keep pressing F11 lots of times till it consumes all function arguments that call other Julia code. If that could be avoided, that is a F11 always go directly to the function, that would skip also potentially quite a lot of visited code.

1 Like

I agree, but that is really a design choice of the VS Code debugger UI… There is a feature where you can right click in the source (while the debugger is paused) and select Step Into Target and then select which function call exactly you want to step into. But that is well hidden, cumbersome and also we don’t exactly generate the most readable list there :wink:

Maybe another small fix could be an option to never step into the getproperty method that just calls getfield, that is where this annoys me the most…

1 Like

If the implication is that the Julia extension for VS Code is official, then AFAIK that’s not the case; the debugger in REPL mode is not part of Julia’s standard library. As notable as it is, it seems to be as separately developed as other third-party libraries.

An interactive REPL can execute function calls one at a time, just like a debugger can. Along with printf debugging, you can get and change targeted information. Not at all the same as a fully featured source debugger program, even one that’s not attached to an IDE. Infiltrator also differentiates itself from “actual debuggers.”

Thing is MATLAB’s runtime is not mostly interpreted. It has a JIT compiler to help, and core numeric routines are compiled from C/C++ that you’ll never see. Python doesn’t have a JIT (not by default anyway), but number crunching tends to be compiled from C, too. Their debuggers greatly benefit from stepping over other languages. Julia has a core compiled from C, but the number crunching tends to be implemented in Julia; the plus is we can often step much more deeply into code, the minus is we can’t compile code we step into or put breakpoints in because compilation doesn’t preserve the source’s structure. Bugs aside, compile mode and excluding packages from interpretation are designed to promise the debugger that we won’t pause some calls, even if we had set breakpoints in their methods, so it can run compiled code instead; in a way, it’s the inverse of compiled code being unapproachable by interactive source debuggers.

2 Likes

in a way, it’s the inverse of compiled code being unapproachable by interactive source debuggers.

I suppose this hits at the heart of the controversy (thus spawning plenty of debugger discussion on this forum):

  1. The state of debugging (and code flow analysis) in Julia is confusing to beginners, and still frustrating to plenty of experienced Julians.
  2. Julia is a very transparent language allowing people to explore deep into its bowels, while striking a balance between compiled and interpreted languages (as originally promised).

Both statements are valid and not mutually exclusive. People are trying to defend both points of view here. I think 1. can be improved while keeping 2. in place. @davidanthoff shows there are ideas to achieve that and he clearly could use your help. Hopefully the attention of discussions like these will lead to that support materializing at some point.

5 Likes

I really don’t understand this part of the gui. It looks to me like you have lots of stuff that is running interpreted. I mean the list under ‘All’: AliasTables, ArgTools, etc. even Base. It says ‘Base interpreted’. And why is ‘testpkg’ listed under ‘Compiled’, when it clearly says ‘interpreted’ next to it?

For me, it looks like this


It says ‘Base compiled (all)’, but underneath, there’s a huge list of low-level functions that are apparently ‘interpreted’. And in settings.json, these are listed under “julia.debuggerDefaultCompiled” as default exceptions to being compiled. I understand I can change it, but why would _collect and ! be excluded by default?

Furthermore, there’s this thing:
image
image
Which of the above means that compiled mode is enabled? I wish it would say
“Julia: Compiled mode enabled
and
“Julia: Compiled mode disabled
because now, I think the filled circle means that it’s enabled, but it looks like it means ‘Click to enable compiled mode’.

At any rate, whether the disc is filled or empty I cannot tell any difference in debugging speed, and the debugger steps into every low level convert, merge(::NamedTuple) etc. etc. either way.

The good news is that the debugger is much more snappy after a full reboot, but unfortunately crashes if I try to run anything in the debug console.

6 Likes

Just to continue on this:

It says ‘Base compiled (all)’, but not all are compiled.

Then it says ‘Core compiled’, does this mean not everything in Core is compiled? And what is the difference between ‘compiled’ and ‘compiled (all)’, when the latter doesn’t actually mean that everything is compiled?

Presumably, much of this is due to the vs code interface, and is hard to do anything about in the julia plugin.

1 Like

JULIA: COMPILED CODE → All is really confusing, because a bunch of modules ,even Base, are shown as “interpreted”. This post below raises the same issue, and I hope it gets attention.

Without reducing the importance of concerns of what the OP says (they are! especially for newcomers) I found that not having a viable debugger made me develop an extensive set of unit tests for every little function (anb keep them small and simple) and implicitly a better programmer … Just saying … :slight_smile: - but I do remember an impossible to work with Debugger and wish I had the knowledge to help in some way

1 Like

I haven’t actually used the debugger much, so worried I’m talking above my pay grade here, but from my testing just now:

It seems that if you add ALL_MODULES_EXCEPT_MAIN to your “Compiled” section, then that takes precedence over everything listed under “All”. So you can just ignore everything under “All”.

If I have a package testpkg that depends on testpkg2, and my “Compiled” section looks like this (I’ve added ALL_MODULES_EXCEPT_MAIN and -testpkg):


then breakpoints in testpkg work, but breakpoints in testpkg2 do not, and I can’t step into a function from testpkg2.

I agree the interface is strange and confusing, but things seem to be working okay for me.

3 Likes

I’ve only tried using the debugger again after reading this thread. What worked for me is editing the json file as described in the manual, i.e. adding ALL_MODULES_EXCEPT_MAIN as well as the individual modules to keep as interpreted. You seem to have to restart VSCode after making changes. The modules under All still seem to be marked as interpreted but the debugger steps over them anyway.

I also seem to have to reload a function if I’ve added/removed breakpoints (I usually run @run from the REPL).

I wish I’d known all this a week ago when I was trying to debug my beamformer processing.

I’d like to add a thank-you to the maintainers - this must be a very difficult thing to develop!

1 Like

Do you know if there is any way to add the exception (testpkg) other than by editing the settings.json file?

Yeah, click the + by “Julia: Compiled Code” in the “Run and debug” tab, and type “-testpkg”

edit: also accessible via command palette under “Julia: add symbol to compiled modules/functions”

1 Like

Oh, the old “add minus” :grin:

It’s not super obvious that adding a ‘negated module’ to the compiled list is different from removing it from the list of compiled modules by clicking ‘-’, but I shouldn’t expect everything to be intuitively discoverable, I guess😅

1 Like