JuliaDebug / Debugger.jl

Julia debugger
MIT License
467 stars 43 forks source link

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

Open staticfloat opened 4 years ago

staticfloat commented 4 years ago

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.

KristofferC commented 4 years ago

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

pfitzseb commented 4 years ago

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.

staticfloat commented 4 years ago

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 commented 4 years ago

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.

KristofferC commented 4 years ago

All of those might have the same line number then?

staticfloat commented 4 years ago

All of those might have the same line number then?

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

pfitzseb commented 4 years ago

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.

staticfloat commented 4 years ago

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.

feribg commented 9 months ago

Is this expected behavior, I cant step into any breakpoints inside of open do blocks which is incredibly annoying as generally any file operation wraps the whole code block in it I need to temporarily rewrite it just so i can use the debugger...

bjarthur commented 6 months ago

i similarly cannot step into a cd do block, even with @pfitzseb 's gist suggestion to use s in interpreted mode. is there a way to do this yet?