Skip to content

Commit 0d1c51b

Browse files
authored
Breakpoint setting for inverted linetable (#673)
1 parent c3739d1 commit 0d1c51b

File tree

3 files changed

+49
-68
lines changed

3 files changed

+49
-68
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
99
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
1010

1111
[compat]
12-
CodeTracking = "0.5.9, 1"
12+
CodeTracking = "1.3.9"
1313
julia = "1.10"
1414

1515
[extras]

src/utils.jl

Lines changed: 25 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ function linetable(arg, i::Integer; macro_caller::Bool=false, def=:var"n/a")::Un
269269
# TODO: decode the linetable at this frame efficiently by reimplementing this here
270270
nodes = Base.IRShow.buildLineInfoNode(lt, def, i)
271271
isempty(nodes) && return nothing
272-
return nodes[1] # ignore all inlining / macro expansion / etc :(
272+
return nodes[macro_caller ? 1 : end]
273273
else # VERSION < v"1.12.0-DEV.173"
274274
lin = lt[i]::Union{Expr,LineTypes}
275275
if macro_caller
@@ -450,60 +450,36 @@ function statementnumbers(framecode::FrameCode, line::Integer, file::Symbol)
450450
0
451451
end
452452

453-
lt = linetable(framecode)
454-
455-
# Check if the exact line number exist
456-
idxs = findall(entry::Union{LineInfoNode,LineNumberNode} -> entry.line + offset == line && entry.file == file, lt)
457-
locs = codelocs(framecode)
458-
if !isempty(idxs)
459-
stmtidxs = Int[]
460-
stmtidx = 1
461-
while stmtidx <= length(locs)
462-
loc = locs[stmtidx]
463-
if loc in idxs
464-
push!(stmtidxs, stmtidx)
465-
stmtidx += 1
466-
# Skip continous statements that are on the same line
467-
while stmtidx <= length(locs) && loc == locs[stmtidx]
468-
stmtidx += 1
469-
end
470-
else
471-
stmtidx += 1
472-
end
473-
end
474-
return stmtidxs
453+
linetarget = line - offset
454+
455+
lts = CodeTracking.linetable_scopes(framecode.src, scope)
456+
for lt in lts
457+
filter!(l -> l.file === file, lt) # filter out line scopes that do not match the file we are looking for
475458
end
476459

477-
# If the exact line number does not exist in the line table, take the one that is closest after that line
478-
# restricted to the line range of the current scope.
479-
scope = framecode.scope
480-
range = (scope isa Method && !is_generated(scope)) ? compute_corrected_linerange(scope) : compute_linerange(framecode)
481-
if line in range
482-
closest = nothing
483-
closest_idx = nothing
484-
for (i, entry) in enumerate(lt)
485-
entry = entry::Union{LineInfoNode,LineNumberNode}
486-
if entry.file == file && entry.line in range && entry.line >= line
487-
if closest === nothing
488-
closest = entry
489-
closest_idx = i
490-
else
491-
if entry.line < closest.line
492-
closest = entry
493-
closest_idx = i
494-
end
495-
end
496-
end
460+
stmtidxs = Int[] # will store the statement indices that match the line
461+
first_in_block, block_recorded = nothing, false
462+
for (i, lt) in enumerate(lts)
463+
# If someone asks for a breakpoint on, e.g., `end`, there may not be a line that matches exactly.
464+
# In this case, we should make sure that the scope encompasses the requested line and then return the first statement index
465+
# immediately after.
466+
if isempty(lt)
467+
first_in_block = nothing # start new block
468+
continue
469+
end
470+
thisscope = first(lt)
471+
if first_in_block === nothing && !iszero(thisscope.line)
472+
first_in_block, block_recorded = thisscope.line, false
497473
end
498-
if closest_idx !== nothing
499-
idx = let closest_idx=closest_idx # julia #15276
500-
findfirst(i-> i==closest_idx, locs)
474+
if !block_recorded # only record the first match in a contiguous block
475+
# if it's either an exact match or it's the first larger line in a block that spans the requested line, store it
476+
if thisscope.line == linetarget || (thisscope.line > linetarget && something(first_in_block, typemax(typeof(thisscope.line))) < linetarget)
477+
push!(stmtidxs, i)
478+
block_recorded = true
501479
end
502-
return idx === nothing ? nothing : Int[idx]
503480
end
504481
end
505-
506-
return nothing
482+
return stmtidxs
507483
end
508484

509485
## Printing

test/breakpoints.jl

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ end
2020
close(io)
2121
include(tmppath)
2222

23-
using JuliaInterpreter, CodeTracking, Test
23+
# Don't move these to the top, because line numbers matter for the tests below
24+
using JuliaInterpreter, CodeTracking, Test, Logging
2425

2526
function stacklength(frame)
2627
n = 1
@@ -215,7 +216,7 @@ struct Squarer end
215216
end
216217
fr, bp = @interpret f_outer_bp(3)
217218
@test leaf(fr).framecode.scope.name === :g_inner_bp
218-
@test bp.stmtidx == (@static VERSION >= v"1.11-" ? 4 : 3)
219+
@test bp.stmtidx == (@static VERSION >= v"1.12-" ? 5 : VERSION >= v"1.11-" ? 4 : 3)
219220

220221
# Breakpoints on types
221222
remove()
@@ -247,25 +248,25 @@ mktemp() do path, io
247248
include(path)
248249
frame, bp = @interpret somefunc(2, 3)
249250
@test bp isa BreakpointRef
250-
@test JuliaInterpreter.whereis(frame) == (path, 3)
251+
@test whereis(frame) == (path, 3)
251252
breakpoint(path, 2)
252253
frame, bp = @interpret somefunc(2, 3)
253254
@test bp isa BreakpointRef
254-
@test JuliaInterpreter.whereis(frame) == (path, 2)
255+
@test whereis(frame) == (path, 2)
255256
remove()
256257
# Test relative paths
257258
mktempdir(dirname(path)) do tmp
258259
cd(tmp) do
259260
breakpoint(joinpath("..", basename(path)), 3)
260261
frame, bp = @interpret somefunc(2, 3)
261262
@test bp isa BreakpointRef
262-
@test JuliaInterpreter.whereis(frame) == (path, 3)
263+
@test whereis(frame) == (path, 3)
263264
remove()
264265
breakpoint(joinpath("..", basename(path)), 3)
265266
cd(homedir()) do
266267
frame, bp = @interpret somefunc(2, 3)
267268
@test bp isa BreakpointRef
268-
@test JuliaInterpreter.whereis(frame) == (path, 3)
269+
@test whereis(frame) == (path, 3)
269270
end
270271
end
271272
end
@@ -302,7 +303,7 @@ using Dates
302303
breakpoint(f, l)
303304
frame, bp = @interpret now() - Month(2)
304305
@test bp isa BreakpointRef
305-
@test JuliaInterpreter.whereis(frame)[2] == l
306+
@test whereis(frame)[2] == l
306307
end
307308
end
308309

@@ -315,7 +316,7 @@ end
315316
breakpoint(f, l)
316317
frame, bp = @interpret sin(2.0)
317318
@test bp isa BreakpointRef
318-
@test JuliaInterpreter.whereis(frame)[2] == l
319+
@test whereis(frame)[2] == l
319320
end
320321
end
321322

@@ -499,7 +500,7 @@ end
499500
bp = breakpoint(@__FILE__, ln + 4)
500501
frame, bpref = @interpret f_emptylines()
501502
@test bpref isa BreakpointRef
502-
@test JuliaInterpreter.whereis(frame) == (@__FILE__, ln + 6)
503+
@test whereis(frame) == (@__FILE__, ln + 6)
503504
remove(bp)
504505

505506
# Don't break if the line is outside the function
@@ -518,23 +519,27 @@ end
518519
frame = JuliaInterpreter.enter_call(f_macro)
519520
file_logging = String(only(methods(var"@info")).file)
520521
line_logging = 0
521-
for entry in frame.framecode.src.linetable
522-
if entry.file === Symbol(file_logging)
523-
line_logging = entry.line
524-
break
522+
lts = CodeTracking.linetable_scopes(JuliaInterpreter.scopeof(frame))
523+
for lt in lts
524+
for entry in lt
525+
if entry.file === Symbol(file_logging)
526+
line_logging = entry.line
527+
break
528+
end
525529
end
530+
line_logging > 0 && break
526531
end
532+
@assert line_logging > 0
527533
bp_log = breakpoint(file_logging, line_logging)
528534
with_logger(NullLogger()) do
529535
frame, bp = @interpret f_macro()
530536
@test bp isa BreakpointRef
531-
file, ln = JuliaInterpreter.whereis(frame)
537+
file, ln = whereis(frame)
532538
@test ln == line_logging
533539
@test basename(file) == basename(file_logging)
534540
bp = JuliaInterpreter.finish_stack!(frame)
535541
@test bp isa BreakpointRef
536-
frame = leaf(frame)
537-
ret = JuliaInterpreter.finish_stack!(frame)
542+
ret = JuliaInterpreter.finish_stack!(leaf(frame))
538543
@test ret == 2
539544
end
540545

@@ -565,14 +570,14 @@ end
565570

566571
with_logger(NullLogger()) do
567572
frame, bp = @interpret f_check(1)
568-
file, ln = JuliaInterpreter.whereis(frame)
573+
file, ln = whereis(frame)
569574
@test file == path # Should not have stopped in logging.jl at line `line_logging`
570575
@test ln == line_logging
571576
remove(bp_f)
572577
@test (@interpret f_check(1)) == 1
573578
breakpoint(f_check, line_logging)
574579
frame, bp = @interpret f_check(1)
575-
file, ln = JuliaInterpreter.whereis(frame)
580+
file, ln = whereis(frame)
576581
@test file == path # Should not have stopped in logging.jl at line `line_logging`
577582
@test ln == line_logging
578583
end

0 commit comments

Comments
 (0)