Julia debugging is extremely slow

I was programming quite a bit in julia about 5-6 years ago but ended up quitting since the debugger was just too unuseable. Now I’m testing it out again for a small side project and I once again find myself with the same issue and I’m wondering whether nothing has really happened in these 5-6 years?

In particular I’m trying to adapt an old fortran code to Julia and want to be able to step through my adaptation line by line and compare whether things are behaving the same in both languages. However it seems I am unable to do so since this line:
df_met = CSV.read(met_file, DataFrame)
Is apparently too much for the debugger to handle?
I really do not understand how it can have trouble with this. I have previously loaded in a very small csv file using a similar call (that file was 307 bytes), the file I am trying to load in now is a bit bigger but still only 731.4 kb (it loads in around ~0.01s) when I time it and do not use the debugger.
However when using the debugger it completely halts my program and seems unable to pass that line (I have waited up to 5 minutes without it completing…)

Is this really still the state of debugging in Julia or do I need to change the default debugging setup somehow for things to work?

In other similar threads I see people mentioning that they are using the repl instead of a debugger but I don’t really see how that is supposed to work?

3 Likes

I would recommend just using Infiltrator.jl and putting an @infiltrate everwhere you want to inspect variables, and remove all of them when you are done.

10 Likes

Well, you can select which libraries shall be compiled. If you are not debugging the CSV library, just compile it: Debugging · Julia in VS Code

You can also try https://plugins.jetbrains.com/plugin/29356-flexible-julia, which is a commercial offering, so you need to pay for it unless you work at a university. On the other hand, if you have a bug report or feature request they address that very fast.

3 Likes

I haven’t followed the jetbrains plugin. Do you know what the state of debugging is there?

My debugging struggles in vs code is more often that it just won’t start for some code.

I second Infiltrator.jl. You can write Main.@infiltrate in the script, so that you don’t need to adding Infiltrator to your Project.toml and only need to run using Infiltartor in the REPL.

6 Likes

On a somewhat related note, Main.@infiltrate is quite long for something that needs to be used all the time to develop packages. I end up using modules and just add “using Infiltrator” and then “@infiltrate”. Would it make sense if a debugger came out of the box? I must say that the experience of just having to type “keyboard” in Matlab was second to none.

1 Like

Unfortunately, it only got worse (crashes more frequently, setting/unsetting BPs is crazy).

What I found in these cases is that if you set the first BP after that line (half the times the BPs work :slight_smile:) than you don’t have to wait that long.

See: New: Julia for JetBrains IDEs (Flexible Julia) - #156 by madppiper

Just give it a try and report any issues you find.

So considering that I want to essentially step through this 1000 line code, I would need to add @infiltrate in front off every line or how exactly would this work? I will check out the library shortly and see whether I can find a nice way of using it, but to me this does not sound like a viable option.

I am using VS code, and I should already be compiling everything but main from what I can see in the settings.

I already tried your solution with only adding breakpoints after the troublesome line of code, but unfortunately that does not seem to make any difference, it still just ends up hanging on that particular line of code.

Using a paid tool completely runs against the ideological reasons for why I want to work in Julia in the first place. So if I cannot find any other viable way to use Julia then I will likely stay out of Julia. However I will get the 7 day trial and see what the experience is like.

I just tried flexible Julia and while it was able to pass the troublesome line without issue, it unfortunately crashes on a later line. However the debugger is missing a debugging console and the error message it returns when crashing is not really very useful.
So while it seems promising it also doesn’t seem to have all the options I need in order to use it for general Julia debugging.

2 Likes

From everything I can read online people in the Julia community seems really happy about using the REPL for debugging, but I cannot wrap my head around how one uses this effectively. Does anyone have some concrete guides or tutorials for how to effective debug in this way? I tried search for youtube videos of people actually coding in Julia to see whether anyone was showing how they might code up a project, but didn’t find much.

My current code for instance looks like this:
I have a main.jl file.
In this file I have a

function main(args)
  ...
  x1 = subfunction1(subargs)
  x2 = subfunction2(subargs)
  for ...
  ...
  end
end

When I try to use the REPL on this it just ends up running the entire main function, which is not what I want. I would want to add the lines one at a time to the REPL such that I can inspect the REPL after each line and see what happens, and whether the line will even run.

1 Like

Hey @tue,

I’m the guy behind Flexible Julia. During debug sessions, you can either use JetBrains inbuilt debug viewer or the Julia tab. They both show the variables, plots and dataframes.

You mentioned a crash. Can you tell me what made it crash so i can fix it? Btw, the sources mentioned in the error should be clickable via ctrl+click. And you can set and remove breakpoints on the file gutter. So that should make it easier for you. You may also try the inbuilt run/debug. It may be more like what you are used to.

Lastly: Unfortunately, I am developing this full time and got to cover my own expenses. I am making it free for the majority of people and giving large discounts to everyone who needs it. So if you fall out of the group (even startups get 50% off), I’m happy to send you a 20% discount for being part of this community. Nobody should be stuck with a bad debugging experience :slight_smile:

5 Likes

No, that‘s clearly not feasible. You can use a bisection approach. Start by checking the final output. If it is wrong, put another infiltration point in the middle of your function. Recursively do this until you find the line where the intermediate result starts to diverge. Obviously this is inferior to a proper debugger, but it can work.

1 Like

We understand what you want, but a debugger as good as those for Python or MATLAB is currently not available, so you must live with it or stick to another language. And if you want to help the community improve the currently available debugger, please provide a reproducible example that shows what is crashing.

My development workflow looks like this:

  • test some code in the REPL
  • if it works, put it into a script
  • if the code works well, move it into a function
  • for debugging of the function, I temporarily define the variables I want to look at as global and then run the scripts such that I can inspect them in the REPL
  • if the function works well, put it into a package
  • add unit tests; they can be generated by AI
  • if, at a later stage, debugging is required, you can use the unit tests as a starting point if you know which function to debug; if you don’t know that yet, use infiltrator.jl to catch bugs that appear only after a long-running simulation

Therefore, I never need a classical debugger to step through existing code.

2 Likes

Hey @madppiper nice to hear from you!

First of, I completely understand having to get paid for your work I have nothing against that and it seems like your are trying to develop exactly what I would need in order to actually be able to program in Julia, so huge props for that :slight_smile:
As I said it is more from an ideological reason that I find myself against paying for software or start relying on software that isn’t open source.

Now onto the actual problem I am seeing.
The crash I am seeing is for sure because I have written something nonsensical in Julia, so it is completely fair that the code stops running at that point, my main problem is that it detaches the debugger and does not really give me a nice informative error stack.

See the following error stack for instance:

Debugger stopped: step
[Julia] Debug session ended with error:
[Julia] ArgumentError: invalid Array dimensions
[Julia] Stacktrace:
[Julia]   [1] checked_dims
[Julia]     @ ./boot.jl:641 [inlined]
[Julia]   [2] reshape(a::Vector{Float64}, dims::Tuple{Int64, Int64})
[Julia]     @ Base ./reshapedarray.jl:44
[Julia]   [3] macro expansion
[Julia]     @ ./some.jl:158 [inlined]
[Julia]   [4] native_call
[Julia]     @ ~/.julia/packages/JuliaInterpreter/zxqbD/src/interpret.jl:211 [inlined]
[Julia]   [5] evaluate_call!(interp::JuliaInterpreter.RecursiveInterpreter, frame::JuliaInterpreter.Frame, fargs::Vector{Any}, enter_generated::Bool)
[Julia]     @ JuliaInterpreter ~/.julia/packages/JuliaInterpreter/zxqbD/src/interpret.jl:274
[Julia]   [6] evaluate_call!(interp::JuliaInterpreter.RecursiveInterpreter, frame::JuliaInterpreter.Frame, call_expr::Expr, enter_generated::Bool)
[Julia]     @ JuliaInterpreter ~/.julia/packages/JuliaInterpreter/zxqbD/src/interpret.jl:249
[Julia]   [7] evaluate_call!(interp::JuliaInterpreter.RecursiveInterpreter, frame::JuliaInterpreter.Frame, call_expr::Expr)
[Julia]     @ JuliaInterpreter ~/.julia/packages/JuliaInterpreter/zxqbD/src/interpret.jl:242
[Julia]   [8] eval_rhs(interp::JuliaInterpreter.RecursiveInterpreter, frame::JuliaInterpreter.Frame, node::Expr)
[Julia]     @ JuliaInterpreter ~/.julia/packages/JuliaInterpreter/zxqbD/src/interpret.jl:399
[Julia]   [9] step_expr!(interp::JuliaInterpreter.RecursiveInterpreter, frame::JuliaInterpreter.Frame, node::Any, istoplevel::Bool)
[Julia]     @ JuliaInterpreter ~/.julia/packages/JuliaInterpreter/zxqbD/src/interpret.jl:485
[Julia]  [10] step_expr!
[Julia]     @ ~/.julia/packages/JuliaInterpreter/zxqbD/src/interpret.jl:620 [inlined]
[Julia]  [11] next_until!(predicate::Any, interp::JuliaInterpreter.RecursiveInterpreter, frame::JuliaInterpreter.Frame, istoplevel::Bool)
[Julia]     @ JuliaInterpreter ~/.julia/packages/JuliaInterpreter/zxqbD/src/commands.jl:102
[Julia]  [12] _next_line!
[Julia]     @ ~/.julia/packages/JuliaInterpreter/zxqbD/src/commands.jl:187 [inlined]
[Julia]  [13] next_line!(interp::JuliaInterpreter.RecursiveInterpreter, frame::JuliaInterpreter.Frame, istoplevel::Bool)
[Julia]     @ JuliaInterpreter ~/.julia/packages/JuliaInterpreter/zxqbD/src/commands.jl:184
[Julia]  [14] debug_command(interp::JuliaInterpreter.RecursiveInterpreter, frame::JuliaInterpreter.Frame, cmd::Symbol, rootistoplevel::Bool; line::Nothing)
[Julia]     @ JuliaInterpreter ~/.julia/packages/JuliaInterpreter/zxqbD/src/commands.jl:497
[Julia]  [15] debug_command(interp::JuliaInterpreter.RecursiveInterpreter, frame::JuliaInterpreter.Frame, cmd::Symbol, rootistoplevel::Bool)
[Julia]     @ JuliaInterpreter ~/.julia/packages/JuliaInterpreter/zxqbD/src/commands.jl:477
[Julia]  [16] our_debug_command(debug_engine::DebugAdapter.DebugEngines.DebugEngine, cmd::Symbol)
[Julia]     @ DebugAdapter.DebugEngines ~/.julia/packages/DebugAdapter/6PfG9/src/DebugEngines.jl:246
[Julia]  [17] run(debug_engine::DebugAdapter.DebugEngines.DebugEngine)
[Julia]     @ DebugAdapter.DebugEngines ~/.julia/packages/DebugAdapter/6PfG9/src/DebugEngines.jl:335
[Julia]  [18] run(debug_session::DebugAdapter.DebugSession, error_handler::Nothing)
[Julia]     @ DebugAdapter ~/.julia/packages/DebugAdapter/6PfG9/src/packagedef.jl:162
[Julia]  [19] run
[Julia]     @ ~/.julia/packages/DebugAdapter/6PfG9/src/packagedef.jl:60 [inlined]
[Julia]  [20] start_dap_server(base_port::Int64, max_attempts::Int64)
[Julia]     @ Main /tmp/julia_dap_bootstrap_17231867484774971137.jl:122
[Julia]  [21] start_dap_server(base_port::Int64)
[Julia]     @ Main /tmp/julia_dap_bootstrap_17231867484774971137.jl:93
[Julia]  [22] top-level scope
[Julia]     @ /tmp/julia_dap_bootstrap_17231867484774971137.jl:138
[Julia]  [23] include(mod::Module, _path::String)
[Julia]     @ Base ./Base.jl:306
[Julia]  [24] exec_options(opts::Base.JLOptions)
[Julia]     @ Base ./client.jl:317
[Julia]  [25] _start()
[Julia]     @ Base ./client.jl:550

[Debugger] Connection to debug adapter lost.
[Julia] Done reading

The code that makes this crash is the following:


And it crashes on line 815, because I wrongly used -1 instead of : for the undefined dimension in a reshape argument. However Nowhere in the error message do I actually see line 815 mentioned.

Rerunning the code and stopping the line before it crashes I get the following:


I just realized that I can actually see the variables now. They do not show up by default, but if I switches from console to the one called “Frames | Variables” and I click local scope it fetches them and I can inspect the variable values (The variables in the top right window called Julia does not seem to reflect this local scope though). However I still seem to have no way to actually interact with my code at this state (I usually use the debugging console to query things in at this point, but I do not see one anywhere here)

Ah. Yeah, that is not really a debugger issue.

The debug session stops when your program hard crashes. It is the same in other languages actually (once the program exits, the debugger detaches and all your state management is lost). Though you can wrap your code in try catch and should be able to stay in the actual program.

You can also print variables or so. Our Julia panel will keep the last state of the known variables/dataframe/plots even after the session has ended. And yip - the Local Scope view is the variable window that comes with JetBrains IDEs that I mentioned. I always get confused myself, because unfortunately JetBrains has the habbit of jumping to the terminal, instead of that variable panel.

That said, if you add try catch blocks, you should be able to properly step over and debug the program of yours. The only thing that could improve on that is probably a way to step into a function like you would in java. I have to check against Julias Debugger.jl if it is easy to add :slight_smile:

2 Likes

Okay then your REPL development process is what I imagined. Thank you for sharing.

To me that just seem like a lot of tedious extra steps just to circumvent using a debugger, but I understand that everyone develops code in a different way.
I am quite surprised that the state of debugging haven’t changed much in the last 5 years in Julia, but I guess the people that requires a debugger have left Julia, such that the only people actually using julia at this point are people who are happy to code without a debugger.

3 Likes

I get that you are frustrated, and look I just shared a couple of options for you^^

Also happy to improve our plugin if you can describe the way you’d expect it to run. (I am used to other languages too, so I understand how debuggers differ at times). Afaik, our current debugger works the way most others do in JetBrains (except for stepping into functions). But happy to understand if there is a difference to you.

1 Like