Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Turn Cthulhu and JET into weakdeps #376

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,24 @@ version = "2.10.8"

[deps]
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
Cthulhu = "f68482b8-f384-11e8-15f7-abe071a5a75f"
FlameGraphs = "08572546-2f56-4bcf-ba4e-bab62c3a3f89"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Profile = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
SnoopCompileCore = "e2b509da-e806-4183-be48-004708413034"
YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"

[weakdeps]
Cthulhu = "f68482b8-f384-11e8-15f7-abe071a5a75f"
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"

[extensions]
CthulhuExt = "Cthulhu"
JETExt = ["JET", "Cthulhu"]

[compat]
AbstractTrees = "0.3, 0.4"
Cthulhu = "1.5, 2"
Expand All @@ -40,4 +45,4 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["ColorTypes", "PrettyTables", "Documenter", "FixedPointNumbers", "MethodAnalysis", "Pkg", "Random", "Test"]
test = ["ColorTypes", "PrettyTables", "Documenter", "FixedPointNumbers", "MethodAnalysis", "Pkg", "Random", "Test", "JET", "Cthulhu"]
2 changes: 1 addition & 1 deletion docs/src/jet.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ As always, you need to do the data collection in a fresh session where the calls
After restarting Julia, we can do this:

```
julia> using SnoopCompile
julia> using SnoopCompile, JET, Cthulhu

julia> list = Any[1,2,3];

Expand Down
24 changes: 24 additions & 0 deletions ext/CthulhuExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module CthulhuExt
import Cthulhu
using Core: MethodInstance
using SnoopCompile: InstanceNode, TriggerNode, Suggested, InferenceTrigger, countchildren


# Originally from invalidations.jl
Cthulhu.backedges(node::InstanceNode) = sort(node.children; by=countchildren, rev=true)
Cthulhu.method(node::InstanceNode) = Cthulhu.method(node.mi)
Cthulhu.specTypes(node::InstanceNode) = Cthulhu.specTypes(node.mi)
Cthulhu.instance(node::InstanceNode) = node.mi

# Originally from parcel_snoopi_deep.jl

Cthulhu.descend(itrig::InferenceTrigger; kwargs...) = descend(callerinstance(itrig); kwargs...)
Cthulhu.instance(itrig::InferenceTrigger) = MethodInstance(itrig.node)
Cthulhu.method(itrig::InferenceTrigger) = Method(itrig.node)
Cthulhu.specTypes(itrig::InferenceTrigger) = Cthulhu.specTypes(Cthulhu.instance(itrig))
Cthulhu.backedges(itrig::InferenceTrigger) = (itrig.callerframes,)
Cthulhu.nextnode(itrig::InferenceTrigger, edge) = (ret = callingframe(itrig); return isempty(ret.callerframes) ? nothing : ret)

Cthulhu.ascend(node::TriggerNode) = ascend(node.itrig)
Cthulhu.ascend(s::Suggested) = ascend(s.itrig)
end
79 changes: 79 additions & 0 deletions ext/JETExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
module JETExt

using Core: MethodInstance, CodeInfo
using SnoopCompile
using Cthulhu
using JET

"""
report_callee(itrig::InferenceTrigger)

Return the `JET.report_call` for the callee in `itrig`.
"""
SnoopCompile.report_callee(itrig::InferenceTrigger; jetconfigs...) = report_call(Cthulhu.specTypes(itrig); jetconfigs...)

"""
report_caller(itrig::InferenceTrigger)

Return the `JET.report_call` for the caller in `itrig`.
"""
SnoopCompile.report_caller(itrig::InferenceTrigger; jetconfigs...) = report_call(Cthulhu.specTypes(callerinstance(itrig)); jetconfigs...)

"""
report_callees(itrigs)

Filter `itrigs` for those with a non-passing `JET` report, returning the list of `itrig => report` pairs.

# Examples

```jldoctest jetfib; setup=(using SnoopCompile, JET), filter=[r"\\d direct children", r"[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?/[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?"]
julia> fib(n::Integer) = n ≤ 2 ? n : fib(n-1) + fib(n-2);

julia> function fib(str::String)
n = length(str)
return fib(m) # error is here
end
fib (generic function with 2 methods)

julia> fib(::Dict) = 0; fib(::Vector) = 0;

julia> list = [5, "hello"];

julia> mapfib(list) = map(fib, list)
mapfib (generic function with 1 method)

julia> tinf = @snoopi_deep try mapfib(list) catch end
InferenceTimingNode: 0.049825/0.071476 on Core.Compiler.Timings.ROOT() with 5 direct children

julia> @report_call mapfib(list)
No errors detected
```

JET did not catch the error because the call to `fib` is hidden behind runtime dispatch.
However, when captured by `@snoopi_deep`, we get

```jldoctest jetfib; filter=[r"@ .*", r"REPL\\[\\d+\\]|none"]
julia> report_callees(inference_triggers(tinf))
1-element Vector{Pair{InferenceTrigger, JET.JETCallResult{JET.JETAnalyzer{JET.BasicPass{typeof(JET.basic_function_filter)}}, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}}}}:
Inference triggered to call fib(::String) from iterate (./generator.jl:47) inlined into Base.collect_to!(::Vector{Int64}, ::Base.Generator{Vector{Any}, typeof(fib)}, ::Int64, ::Int64) (./array.jl:782) => ═════ 1 possible error found ═════
┌ @ none:3 fib(m)
│ variable `m` is not defined
└──────────
```
"""
function SnoopCompile.report_callees(itrigs; jetconfigs...)
function rr(itrig)
rpt = try
report_callee(itrig; jetconfigs...)
catch err
@warn "skipping $itrig due to report_callee error" exception=err
nothing
end
return itrig => rpt
end
hasreport((itrig, report)) = report !== nothing && !isempty(JET.get_reports(report))

return [itrigrpt for itrigrpt in map(rr, itrigs) if hasreport(itrigrpt)]
end

end
9 changes: 0 additions & 9 deletions src/invalidations.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using Cthulhu

export uinvalidated, invalidation_trees, filtermod, findcaller, ascend

const have_verify_methods = Base.VERSION >= v"1.9.0-DEV.1512" || Base.VERSION >= v"1.8.4"
Expand Down Expand Up @@ -852,10 +850,3 @@ function findcaller(meth::Method, node::InstanceNode)
end

findcaller(meth::Method, mi::MethodInstance) = mi.def == meth ? mi : nothing

# Cthulhu integration

Cthulhu.backedges(node::InstanceNode) = sort(node.children; by=countchildren, rev=true)
Cthulhu.method(node::InstanceNode) = Cthulhu.method(node.mi)
Cthulhu.specTypes(node::InstanceNode) = Cthulhu.specTypes(node.mi)
Cthulhu.instance(node::InstanceNode) = node.mi
95 changes: 13 additions & 82 deletions src/parcel_snoopi_deep.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
using Core.Compiler.Timings: InferenceFrameInfo
using SnoopCompileCore: InferenceTiming, InferenceTimingNode, inclusive, exclusive
using Profile
using Cthulhu
using JET

const InferenceNode = Union{InferenceFrameInfo,InferenceTiming,InferenceTimingNode}

Expand Down Expand Up @@ -890,83 +888,14 @@
AbstractTrees.children(tinf::InferenceTimingNode) = tinf.children

InteractiveUtils.edit(itrig::InferenceTrigger) = edit(Location(itrig.callerframes[end]))
Cthulhu.descend(itrig::InferenceTrigger; kwargs...) = descend(callerinstance(itrig); kwargs...)
Cthulhu.instance(itrig::InferenceTrigger) = MethodInstance(itrig.node)
Cthulhu.method(itrig::InferenceTrigger) = Method(itrig.node)
Cthulhu.specTypes(itrig::InferenceTrigger) = Cthulhu.specTypes(Cthulhu.instance(itrig))
Cthulhu.backedges(itrig::InferenceTrigger) = (itrig.callerframes,)
Cthulhu.nextnode(itrig::InferenceTrigger, edge) = (ret = callingframe(itrig); return isempty(ret.callerframes) ? nothing : ret)

"""
report_callee(itrig::InferenceTrigger)

Return the `JET.report_call` for the callee in `itrig`.
"""
report_callee(itrig::InferenceTrigger; jetconfigs...) = report_call(Cthulhu.specTypes(itrig); jetconfigs...)

"""
report_caller(itrig::InferenceTrigger)

Return the `JET.report_call` for the caller in `itrig`.
"""
report_caller(itrig::InferenceTrigger; jetconfigs...) = report_call(Cthulhu.specTypes(callerinstance(itrig)); jetconfigs...)

"""
report_callees(itrigs)

Filter `itrigs` for those with a non-passing `JET` report, returning the list of `itrig => report` pairs.

# Examples

```jldoctest jetfib; setup=(using SnoopCompile, JET), filter=[r"\\d direct children", r"[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?/[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?"]
julia> fib(n::Integer) = n ≤ 2 ? n : fib(n-1) + fib(n-2);

julia> function fib(str::String)
n = length(str)
return fib(m) # error is here
end
fib (generic function with 2 methods)

julia> fib(::Dict) = 0; fib(::Vector) = 0;

julia> list = [5, "hello"];

julia> mapfib(list) = map(fib, list)
mapfib (generic function with 1 method)

julia> tinf = @snoopi_deep try mapfib(list) catch end
InferenceTimingNode: 0.049825/0.071476 on Core.Compiler.Timings.ROOT() with 5 direct children

julia> @report_call mapfib(list)
No errors detected
```

JET did not catch the error because the call to `fib` is hidden behind runtime dispatch.
However, when captured by `@snoopi_deep`, we get

```jldoctest jetfib; filter=[r"@ .*", r"REPL\\[\\d+\\]|none"]
julia> report_callees(inference_triggers(tinf))
1-element Vector{Pair{InferenceTrigger, JET.JETCallResult{JET.JETAnalyzer{JET.BasicPass{typeof(JET.basic_function_filter)}}, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}}}}:
Inference triggered to call fib(::String) from iterate (./generator.jl:47) inlined into Base.collect_to!(::Vector{Int64}, ::Base.Generator{Vector{Any}, typeof(fib)}, ::Int64, ::Int64) (./array.jl:782) => ═════ 1 possible error found ═════
┌ @ none:3 fib(m)
│ variable `m` is not defined
└──────────
```
"""
function report_callees(itrigs; jetconfigs...)
function rr(itrig)
rpt = try
report_callee(itrig; jetconfigs...)
catch err
@warn "skipping $itrig due to report_callee error" exception=err
nothing
end
return itrig => rpt
end
hasreport((itrig, report)) = report !== nothing && !isempty(JET.get_reports(report))

return [itrigrpt for itrigrpt in map(rr, itrigs) if hasreport(itrigrpt)]
end
# Implemented in JETExt
"To use `report_caller` do `using Cthulhu, JET`"
function report_caller end
"To use `report_callee` do `using Cthulhu, JET`"
function report_callee end
"To use `report_callees` do `using Cthulhu, JET`"
function report_callees end

filtermod(mod::Module, itrigs::AbstractVector{InferenceTrigger}) = filter(==(mod) ∘ callermodule, itrigs)

Expand Down Expand Up @@ -1046,7 +975,6 @@

InteractiveUtils.edit(node::TriggerNode) = edit(node.itrig)
Base.stacktrace(node::TriggerNode) = stacktrace(node.itrig)
Cthulhu.ascend(node::TriggerNode) = ascend(node.itrig)

### tagged trigger lists
# good for organizing a collection of related triggers
Expand Down Expand Up @@ -1387,7 +1315,6 @@
unspec(s::Suggested) = any(unspec, s.categories)

Base.stacktrace(s::Suggested) = stacktrace(s.itrig)
Cthulhu.ascend(s::Suggested) = ascend(s.itrig)
InteractiveUtils.edit(s::Suggested) = edit(s.itrig)

"""
Expand Down Expand Up @@ -1581,8 +1508,12 @@
return arg.val
elseif isa(arg, Core.PartialStruct)
return arg.typ
elseif isa(arg, Core.Compiler.MaybeUndef)
return arg.typ
else
@static if VERSION <= v"1.10"

Check warning on line 1512 in src/parcel_snoopi_deep.jl

View check run for this annotation

Codecov / codecov/patch

src/parcel_snoopi_deep.jl#L1512

Added line #L1512 was not covered by tests
if isa(arg, Core.Compiler.MaybeUndef)
return arg.typ
end
end
end
return arg
end
Expand Down
7 changes: 6 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
using Test

if VERSION >= v"1.7"
import JET
import Cthulhu
end

if VERSION >= v"1.6.0-DEV.1190" # https://github.com/JuliaLang/julia/pull/37749
@testset "snoopi_deep" begin
include("snoopi_deep.jl")
Expand Down Expand Up @@ -30,7 +35,7 @@ logfile = joinpath(tempdir(), "anon.log")
end
data = SnoopCompile.read(logfile)
pc = SnoopCompile.parcel(reverse!(data[2]))
@test length(pc[:Base]) <= 1
@test_broken length(pc[:Base]) <= 1

# issue #29
keep, pcstring, topmod, name = SnoopCompile.parse_call("Tuple{getfield(JLD, Symbol(\"##s27#8\")), Any, Any, Any, Any, Any}")
Expand Down
8 changes: 4 additions & 4 deletions test/snoopi_deep.jl
Original file line number Diff line number Diff line change
Expand Up @@ -973,12 +973,12 @@ if Base.VERSION >= v"1.7"

cc = Any[Any[1,2,3]]
tinf = @snoopi_deep call_mysum(cc)
rpt = SnoopCompile.JET.@report_call call_mysum(cc)
@test isempty(SnoopCompile.JET.get_reports(rpt))
rpt = JET.@report_call call_mysum(cc)
@test isempty(JET.get_reports(rpt))
itrigs = inference_triggers(tinf)
irpts = report_callees(itrigs)
@test only(irpts).first == last(itrigs)
@test !isempty(SnoopCompile.JET.get_reports(only(irpts).second))
@test isempty(SnoopCompile.JET.get_reports(report_caller(itrigs[end])))
@test !isempty(JET.get_reports(only(irpts).second))
@test isempty(JET.get_reports(report_caller(itrigs[end])))
end
end
Loading