Skip to content

Commit 8b6bc60

Browse files
authored
Improve attribution of delayed invalidations (#261)
Improves on #260 by collecting all callees and callers and then doing the attribution at once (finding at least one identifiable callee). This also adds the test's Manifest.toml, which was missing on #260 but not detected because the necessary Julia version had not yet hit nightly.
1 parent 4e52869 commit 8b6bc60

File tree

3 files changed

+100
-21
lines changed

3 files changed

+100
-21
lines changed

src/invalidations.jl

+29-21
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ function Base.show(io::IO, methinvs::MethodInvalidations)
187187
sig = root.first
188188
if isa(sig, MethodInstance)
189189
# "insert_backedges_callee"/"insert_backedges" (delayed) invalidations
190-
printstyled(io, which(sig.specTypes), color = :light_cyan)
190+
printstyled(io, try which(sig.specTypes) catch _ "(unavailable)" end, color = :light_cyan)
191191
print(io, " (formerly ", sig.def, ')')
192192
else
193193
# `sig` (immediate) invalidations
@@ -290,27 +290,24 @@ function invalidation_trees(list; exclude_corecompiler::Bool=true)
290290

291291
function handle_insert_backedges(list, i, callee)
292292
ncovered = 0
293+
callees = Any[callee]
293294
while length(list) >= i+2 && list[i+2] == "insert_backedges_callee"
294-
if isa(callee, Type)
295-
newcallee = list[i+1]
296-
if isa(newcallee, MethodInstance)
297-
callee = newcallee
298-
end
299-
end
295+
push!(callees, list[i+1])
300296
i += 2
301297
end
298+
callers = MethodInstance[]
302299
while length(list) >= i+2 && list[i+2] == "insert_backedges"
303-
caller = list[i+=1]
304-
i += 1
305-
push!(delayed, callee => caller)
300+
push!(callers, list[i+1])
301+
i += 2
306302
ncovered += 1
307303
end
304+
push!(delayed, callees => callers)
308305
@assert ncovered > 0
309306
return i
310307
end
311308

312309
methodinvs = MethodInvalidations[]
313-
delayed = Pair{Any,MethodInstance}[] # from "insert_backedges" invalidations
310+
delayed = Pair{Vector{Any},Vector{MethodInstance}}[] # from "insert_backedges" invalidations
314311
leaf = nothing
315312
mt_backedges, backedges, mt_cache, mt_disable = methinv_storage()
316313
reason = nothing
@@ -418,20 +415,31 @@ function invalidation_trees(list; exclude_corecompiler::Bool=true)
418415
end
419416
end
420417
end
421-
trouble = similar(delayed, 0)
422-
for (callee, caller) in delayed
423-
if isa(callee, MethodInstance)
424-
idx = get(callee2idx, callee.def, nothing)
425-
if idx !== nothing
426-
push!(methodinvs[idx].mt_backedges, callee => caller)
427-
continue
418+
solved = Int[]
419+
for (i, (callees, callers)) in enumerate(delayed)
420+
for callee in callees
421+
if isa(callee, MethodInstance)
422+
idx = get(callee2idx, callee.def, nothing)
423+
if idx !== nothing
424+
for caller in callers
425+
push!(methodinvs[idx].mt_backedges, callee => caller)
426+
end
427+
push!(solved, i)
428+
break
429+
end
428430
end
429431
end
430-
push!(trouble, callee => caller)
431432
end
432-
if !isempty(trouble)
433+
deleteat!(delayed, solved)
434+
if !isempty(delayed)
433435
@warn "Could not attribute the following delayed invalidations:"
434-
display(trouble)
436+
for (callees, callers) in delayed
437+
@assert !isempty(callees) # this shouldn't ever happen
438+
printstyled(length(callees) == 1 ? callees[1] : callees; color = :light_cyan)
439+
print(" invalidated ")
440+
printstyled(length(callers) == 1 ? callers[1] : callers; color = :light_yellow)
441+
println()
442+
end
435443
end
436444
return sort!(methodinvs; by=countchildren)
437445
end

test/snoopr.jl

+50
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,53 @@ end
212212
@test isempty(filtermod(Outer, trees))
213213
@test length(filtermod(Outer, trees; recursive=true)) == 1
214214
end
215+
216+
@testset "Delayed invalidations" begin
217+
if Base.VERSION >= v"1.7.0-DEV.254" # julia#39132 (redirect to Pipe)
218+
# "Natural" tests are performed in the "Stale" testset of "snoopi_deep.jl"
219+
# because they are also used for precompile_blockers.
220+
# Here we craft them artificially.
221+
M = @eval Module() begin
222+
fake1(x) = 1
223+
fake2(x) = fake1(x)
224+
fake3() = nothing
225+
foo() = nothing
226+
@__MODULE__
227+
end
228+
M.fake2('a')
229+
M.fake3()
230+
callee = methodinstance(M.fake1, (Char,))
231+
caller = methodinstance(M.fake2, (Char,))
232+
othercallee = methodinstance(M.fake3, ())
233+
# failed attribution (no invalidations occurred prior to the backedges invalidations)
234+
invalidations = Any[callee, "insert_backedges_callee", othercallee, "insert_backedges_callee", caller, "insert_backedges"]
235+
pipe = Pipe()
236+
redirect_stdout(pipe) do
237+
@test_logs (:warn, "Could not attribute the following delayed invalidations:") begin
238+
trees = invalidation_trees(invalidations)
239+
@test isempty(trees)
240+
end
241+
end
242+
close(pipe.in)
243+
str = read(pipe.out, String)
244+
@test occursin(r"fake1\(::Char\).*invalidated.*fake2\(::Char\)", str)
245+
246+
m = which(M.foo, ())
247+
invalidations = Any[Any[caller, Int32(1), callee, "jl_method_table_insert", m, "jl_method_table_insert"]; invalidations]
248+
tree = @test_nowarn only(invalidation_trees(invalidations))
249+
@test tree.method == m
250+
@test tree.reason == :inserting
251+
mi1, mi2 = tree.mt_backedges[1]
252+
@test mi1 == callee
253+
@test mi2 == caller
254+
@test Core.MethodInstance(tree.backedges[1]) == callee
255+
io = IOBuffer()
256+
print(io, tree)
257+
str = String(take!(io))
258+
@test occursin(r"fake1\(x\) in.*formerly fake1\(x\) in", str)
259+
Base.delete_method(callee.def)
260+
print(io, tree)
261+
str = String(take!(io))
262+
@test occursin(r"\(unavailable\).*formerly fake1\(x\) in", str)
263+
end
264+
end

test/testmodules/Stale/Manifest.toml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# This file is machine-generated - editing it directly is not advised
2+
3+
julia_version = "1.6.3-pre.1"
4+
manifest_format = "2.0"
5+
6+
[[deps.StaleA]]
7+
path = "StaleA"
8+
uuid = "daf834c3-b832-4a67-a95b-01ec1ffe9b4d"
9+
version = "0.1.0"
10+
11+
[[deps.StaleB]]
12+
deps = ["StaleA", "StaleC"]
13+
path = "StaleB"
14+
uuid = "af730a9e-e668-4d07-a0f0-de54196c2067"
15+
version = "0.1.0"
16+
17+
[[deps.StaleC]]
18+
deps = ["StaleA"]
19+
path = "StaleC"
20+
uuid = "f6b5ece7-60fa-49fc-ba7e-b783050e37f1"
21+
version = "0.1.0"

0 commit comments

Comments
 (0)