Debugger.jl icon indicating copy to clipboard operation
Debugger.jl copied to clipboard

Unexpected `n` behavior within `open(f, ...)`

Open staticfloat opened this issue 6 years ago • 10 comments

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.

staticfloat avatar Nov 27 '19 23:11 staticfloat

Using L to look at lowered code might give some insight to what is happening.

KristofferC avatar Nov 28 '19 07:11 KristofferC

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.

pfitzseb avatar Nov 28 '19 10:11 pfitzseb

n steps 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.

staticfloat avatar Nov 30 '19 21:11 staticfloat

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.

staticfloat avatar Nov 30 '19 22:11 staticfloat

All of those might have the same line number then?

KristofferC avatar Nov 30 '19 22:11 KristofferC

All of those might have the same line number then?

Can I get line numbers printed next to the lowered code?

staticfloat avatar Nov 30 '19 22:11 staticfloat

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> then c works 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.

pfitzseb avatar Dec 01 '19 11:12 pfitzseb

Those statements are all on line 1 -- same as for the @code_lowered output:

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.

staticfloat avatar Dec 02 '19 19:12 staticfloat