diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8e88005..06c0a32 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -18,7 +18,7 @@ jobs: fail-fast: false matrix: version: - - '1.12' + - '1.12-nightly' - 'nightly' os: - ubuntu-latest diff --git a/Project.toml b/Project.toml index 51348d2..18590f9 100644 --- a/Project.toml +++ b/Project.toml @@ -9,7 +9,7 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" [compat] DocStringExtensions = "0.9" -MethodAnalysis = "0.4" +MethodAnalysis = "0.4, 1" julia = "1.12" [extensions] diff --git a/ext/MethodAnalysisExt.jl b/ext/MethodAnalysisExt.jl index fc03e26..bc77e4f 100644 --- a/ext/MethodAnalysisExt.jl +++ b/ext/MethodAnalysisExt.jl @@ -24,4 +24,23 @@ function MethodAnalysis.methodinstances(info::PkgCacheInfo) return mis end +function PkgCacheInspector.count_internal_specializations(info::PkgCacheInfo) + spec_counts = Dict{Module,Int}() + + # Get all method instances from the cache + all_mis = methodinstances(info) + + # Count method instances by their defining module + for mi in all_mis + if isa(mi, Core.MethodInstance) && isa(mi.def, Method) + method_module = mi.def.module + if method_module in info.modules + spec_counts[method_module] = get(spec_counts, method_module, 0) + 1 + end + end + end + + return spec_counts +end + end diff --git a/src/PkgCacheInspector.jl b/src/PkgCacheInspector.jl index a2f055e..5f69b16 100644 --- a/src/PkgCacheInspector.jl +++ b/src/PkgCacheInspector.jl @@ -148,9 +148,52 @@ function Base.show(io::IO, info::PkgCacheInfo) nspecs = sort(collect(nspecs); by=last, rev=true) nspecs_tot = sum(last, nspecs; init=0) + # Count internal methods and specializations + internal_methods = count_internal_methods(info) + total_internal = sum(values(internal_methods)) + + # Try to count internal specializations if MethodAnalysis is available + internal_specs = Dict{Module,Int}() + total_internal_specs = 0 + internal_specs_sorted = Pair{Module,Int}[] + + internal_specs = count_internal_specializations(info) + if internal_specs !== nothing + internal_specs_sorted = sort(collect(internal_specs); by=last, rev=true) + total_internal_specs = sum(last, internal_specs_sorted; init=0) + end + println(io, "Contents of ", info.cachefile, ':') println(io, " modules: ", info.modules) !isempty(info.init_order) && println(io, " init order: ", info.init_order) + + # Show internal methods + if total_internal > 0 + println(io, " ", total_internal, " internal methods") + if length(internal_methods) > 1 + print(io, " (") + sorted_internal = sort(collect(internal_methods); by=last, rev=true) + for i = 1:length(sorted_internal) + mod, count = sorted_internal[i] + print(io, i==1 ? "" : ", ", nameof(mod), " ", count) + end + println(io, ")") + end + end + + # Show internal specializations + if internal_specs === nothing + println(io, " specializations of internal methods: (requires MethodAnalysis.jl)") + elseif total_internal_specs > 0 + print(io, " ", total_internal_specs, " specializations of internal methods ") + for i = 1:min(3, length(internal_specs_sorted)) + mod, count = internal_specs_sorted[i] + pct = round(100*count/total_internal_specs; digits=1) + print(io, i==1 ? "(" : ", ", nameof(mod), " ", pct, "%") + end + println(io, length(internal_specs_sorted) > 3 ? ", ...)" : ")") + end + !isempty(info.external_methods) && println(io, " ", length(info.external_methods), " external methods") if !isempty(info.new_specializations) print(io, " ", length(info.new_specializations), " new specializations of external methods ") @@ -182,6 +225,22 @@ function count_module_specializations(new_specializations) return modcount end +# count_internal_specializations is defined in MethodAnalysisExt when MethodAnalysis is loaded +count_internal_specializations(::Any) = nothing + +# Count the number of methods defined within each of the package's own modules. +# These are methods that belong to the modules stored in the package image, +# as opposed to external methods which extend functions from other modules. +function count_internal_methods(info::PkgCacheInfo) + method_counts = Dict{Module,Int}() + Base.visit(Core.GlobalMethods) do method + if method.module in info.modules + method_counts[method.module] = get(method_counts, method.module, 0) + 1 + end + end + return method_counts +end + function info_cachefile(pkg::PkgId, path::String, depmods::Vector{Any}, image_targets::Vector{Any}, isocache::Bool=false) if isocache sv = ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint, Cstring, Cint), path, depmods, true, pkg.name, false) diff --git a/test/runtests.jl b/test/runtests.jl index b5fd3ca..7486f63 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,6 +15,8 @@ module EmptyPkg end str = sprint(show, info) @test occursin("relocations", str) && occursin("new specializations", str) && occursin("targets", str) @test occursin("file size", str) + @test occursin("internal methods", str) + @test occursin("specializations of internal methods", str) mis = methodinstances(info) @test eltype(mis) === Core.MethodInstance