Debugger.jl
Debugger.jl copied to clipboard
Unexpected `n` behavior within `open(f, ...)`
When debugging code such as the following:
function wrapper()
mktempdir() do dir
open(joinpath(dir, "test.txt"), "w") do io
println(io, "data")
end
end
end
Using n to enter a do block does not work; I think this might be expected, but I wonder if the docs (which state that n means "step to the next line" should instead say something like "step to the next expression" if that is indeed what it is doing.)
The truly unexpected piece is when trying to navigate within open to get into the println(): after si'ing and so'ing my way until I'm in open(), I am greeted by the following:
1|debug> si
In open(f, args) at io.jl:296
295 function open(f::Function, args...; kwargs...)
>296 io = open(args...; kwargs...)
297 try
298 f(io)
299 finally
300 close(io)
About to run: (NamedTuple)()
I then hit n to attempt to jump down to the f(io), but when I hit n I get:
1|debug> n
In open(f, args) at io.jl:296
295 function open(f::Function, args...; kwargs...)
>296 io = open(args...; kwargs...)
297 try
298 f(io)
299 finally
300 close(io)
About to run: return
I have somehow just skipped to the end of the open() function, and continuing to step causes me to exit out.
Using L to look at lowered code might give some insight to what is happening.
What's probably tripping you up here is that we're not automatically stepping through the kwarg wrapper for open here (see here for how you can step to the println call).
So I guess the actionable complaint here is "improve the wrapper detection heuristics".
Using n to enter a do block does not work; I think this might be expected, but I wonder if the docs (which state that n means "step to the next line" should instead say something like "step to the next expression" if that is indeed what it is doing.)
se steps to the next expression; n steps to the next expression until we end up on a new line. Both don't step into other functions for obvious reasons.
nsteps to the next expression until we end up on a new line.
Yeah, I was kind of hoping that the behavior was instead "if we are currently at file.jl:M, set a breakpoint at file.jl:(M+1), then step forward until we hit a breakpoint, error or quit." Perhaps that is a good idea, perhaps not, but I understand what n means now at least. ;)
FWIW, tools like ipdb do exactly this; e.g. running ipdb with the code:
with open("test.txt", "w") as file:
file.write("yolo")
with open("test.log", "w") as file2:
file2.write("yolo2")
file.write("hehe")
You can n from outside of the with block and it will step "into" the block, stepping over the open() code.
Using L to look at lowered code might give some insight to what is happening.
Hmmm, I'm not entirely sure I understand this:
In open(f, args) at io.jl:296
295 function open(f::Function, args...; kwargs...)
>296 io = open(args...; kwargs...)
297 try
298 f(io)
299 finally
300 close(io)
About to run: (NamedTuple)()
1|debug>
In open(f, args) at io.jl:296
>1 1 ─ %1 = (NamedTuple)()
2 │ %2 = (pairs)(%1)
3 │ %3 = (tuple)(%2, #self#, f)
4 │ %4 = (Core._apply)(Base.#open#271, %3, args)
5 └── return %4
About to run: (NamedTuple)()
1|debug> n
In open(f, args) at io.jl:296
1 1 ─ %1 = (NamedTuple)()
2 │ %2 = (pairs)(%1)
3 │ %3 = (tuple)(%2, #self#, f)
4 │ %4 = (Core._apply)(Base.#open#271, %3, args)
>5 └── return %4
About to run: return
It appears to me that what's happening when I hit n is that it jumps through the entire basic block.
Yeah, I was kind of hoping that the behavior was instead "if we are currently at file.jl:M, set a breakpoint at file.jl:(M+1), then step forward until we hit a breakpoint, error or quit." Perhaps that is a good idea, perhaps not, but I understand what n means now at least. ;)
Amusingly enough, running bp add <M+1> then c works just fine.
All of those might have the same line number then?
All of those might have the same line number then?
Can I get line numbers printed next to the lowered code?
Those statements are all on line 1 -- same as for the @code_lowered output:
julia> code_lowered(open, (Function,))
1-element Array{Core.CodeInfo,1}:
CodeInfo(
1 ─ %1 = (Core.NamedTuple)()
│ %2 = (Base.pairs)(%1)
│ %3 = (Core.tuple)(%2, #self#, f)
│ %4 = (Core._apply)(Base.:(#open#310), %3, args)
└── return %4
)
Yeah, I was kind of hoping that the behavior was instead "if we are currently at file.jl:M, set a breakpoint at file.jl:(M+1), then step forward until we hit a breakpoint, error or quit." Perhaps that is a good idea, perhaps not, but I understand what n means now at least. ;)
Amusingly enough, running
bp add <M+1>thencworks just fine.
Juno's debugger almost does that for the Step to Selected Line command (except that it tries to stay in the current frame, so your example doesn't work) -- imho it makes a lot of sense to change the meaning to what you propose (and is trivial to implement).
There's the unfortunate side effect of that not working in compiled mode (because breakpoints in general don't work in compiled mode when switching frames), so we'd either need to document that or disable compiled mode temporarily.
Those statements are all on line 1 -- same as for the
@code_loweredoutput:
But @code_lowered doesn't show line numbers? It only shows basic block numbers, right?
we'd either need to document that or disable compiled mode temporarily.
I would be okay with it not working in compiled mode.