ANN: Infiltrator.jl

Hey everyone,

I’d like to announce Infiltrator.jl, which is intended as complement to the existing debuggers.

What is this?

Infiltrator.jl provides a macro called @infiltrate , which sets a “breakpoint” in a local context (similar to Matlab’s keyboard function). The advantage of this macro over e.g. Debugger.jl is that all code is completely compiled, so the performance overhead should be negligible.

@infiltrate will drop you into an interactive REPL session that lets you inspect local variables and the call stack as well as execute arbitrary statements in the context of the current function’s module. You can optionally supply an argument to @infiltrate (that must evaluate to a boolean) to make it conditional.

Note that you cannot access other functions in the callstack, or step into functions. If you need that functionality, use Debugger.jl or Juno’s debugger.

Installation

pkg> add Infiltrator

Usage

julia> function f(x)
         x *= 2
         y = rand(3)
         @infiltrate
         x += 2
       end
f (generic function with 1 method)

julia> f(3)
Hit `@infiltrate` in f(::Int64) at none:4:

debug> ?
Code entered is evaluated in the current function's module. 
Note that you cannot change local variables.
The following commands are special cased:
- `@trace`: Print the current stack trace.
- `@locals`: Print local variables.

Exit this REPL mode with `Ctrl-D`.

debug> @trace
[1] f(::Int64) at none:4
[2] top-level scope at none:0

debug> @locals
- y::Array{Float64,1} = [0.187253, 0.145958, 0.183677]
- x::Int64 = 6

debug> x.+y
3-element Array{Float64,1}:
6.187252565686353
6.145958004935359
6.1836766675450034

debug>^D
8

julia>
62 Likes

Excellent name!

1 Like

@BeastyBlacksmith came up with that, but yes, I agree :slight_smile:

3 Likes

This is great! You’ve covered 90% of my use cases for debugger with this. I’m also impressed with how simple the package is as well - just over 200 lines and basically no dependencies! (I also like the name :slight_smile:)

9 Likes

I love the idea. This is a good portion of what people do when use debuggers for exploration. This still requires modifying the files, but it is a step forward.

Does it currently support/or are you planning to support showing the locals in the Juno panes? Are you also considering this as a debugging option in Juno in general (i.e. people could choose to either run the fully interpreted version with all of the flexibilty, or this faster version for just exploring the state of the computation)?

Yes. I suppose we could also re-use the breakpoint UI and pretend no-one modified their source code, but not sure if that’s a good idea or not.

3 Likes

I would be very helpful for usability of low tech debugger users. In matlab you can both modify the code/local variables and still run it full speed between breakpoints. We won’t be there in Julia for a while, so I think it is reasonable to make people choose what they want. Most cases will be of the second type where infiltrator (if I understand it) is the solution. If the UI makes the choice clear (ie high speed exploration vs interpreted full control), then people will no longer be perplexed that the code runs orders of magnitude slower while debugging

Hmm, I’m getting this

julia> cd("c:/v");using GMT, Infiltrator
[ Info: Precompiling GMT [5752ebe1-31b9-557e-87aa-f909b540aa54]
[ Info: Precompiling Infiltrator [5903a43b-9cc3-4c30-8d17-598619ec4e9b]

julia> imshow(GMT.peaks(), view=:default, zaxis=(annot=:auto, grid=:auto), Vd=2)
LoadError: UndefVarError: @infiltrate not defined

You need to make sure @infiltrate is available in whatever scope you’re using it in. In this case something like

Main.Infiltrator.@infiltrate

in your package code should work fine. The other option is, of course, to put the using Infiltrator into your package.

1 Like

Thanks, Main.Inf.... worked.

1 Like

Debugging in Julia was an issue, and just to check a variable value inside a function you had to go through the slow debugging process. But not anymore! :tada:
This is exactly what I wanted .

Atom/Juno integration will be certainly useful.

Found variable inspection to be a pain in other debuggers. Going to give this a spin

Is there a minimum Julia version required? I was able to install in v1.2. In v1.0.4, it is throwing this error:

(v1.0) pkg> add Infiltrator
  Updating registry at `~/.julia/registries/General`
  Updating git-repo `https://github.com/JuliaRegistries/General.git`
 Resolving package versions...
ERROR: Unsatisfiable requirements detected for package Infiltrator [5903a43b]:
 Infiltrator [5903a43b] log:
 ├─possible versions are: 0.1.0 or uninstalled
 ├─restricted to versions * by an explicit requirement, leaving only versions 0.1.0
 └─restricted by julia compatibility requirements to versions: uninstalled — no versions left

It is registered as compatible with julia 1.1 or higher

1 Like

Can you do conditional Infiltrator? For example

if x > 7 && y < 29
    Main.Infiltrator.@infiltrate
end

Yes

Really useful! I seem to be having a weird issue, though: first time that I type into >debug it doesn’t recognize all characters.

For example, typing @locals it only gets @lsl.

Is anyone experiencing simillar problems? Shall I open a GitHub issue?

Are you by chance using Juno’s/VSCode’s inline evaluation for executing a function that calls @infiltrate somewhere down the call stack? If so, that’s a known problem, and the only workaround is to call your function in the REPL.

1 Like

Yup, Infiltrator uses @locals, which is only available with Julia 1.1, unfortunately.

2 Likes

Ah, yes, I am. Thank you for the quick response.

I suppose this is due to some underlying Juno issue?