diff --git a/.gitignore b/.gitignore index c375cd4..7c71f2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ \.~* +scratch diff --git a/Stability/src/CSVize.jl b/Stability/src/CSVize.jl index f5ff1a5..795e3c6 100644 --- a/Stability/src/CSVize.jl +++ b/Stability/src/CSVize.jl @@ -29,11 +29,25 @@ struct ModuleStatsPerInstanceRecord line :: Int end -# Convert stats to vectors of records +struct ModuleStatsInTypeRecord + modl :: String + tyname :: String + occurs :: Int +end + +# +# Convert stats dicitonaries to vectors of records +# modstats_table(ms :: ModuleStats, errio = stdout :: IO) :: - Tuple{Vector{ModuleStatsPerMethodRecord}, Vector{ModuleStatsPerInstanceRecord}} = begin + Tuple{ + Vector{ModuleStatsPerMethodRecord}, + Vector{ModuleStatsPerInstanceRecord}, + Vector{ModuleStatsInTypeRecord}} = begin + resmeth = [] resmi = [] + resty = [] + m2rettype = Dict{Method, Set{String}}() for (mi,cfgst) in ms.mistats try @@ -84,5 +98,16 @@ modstats_table(ms :: ModuleStats, errio = stdout :: IO) :: throw(err) end end - (resmeth,resmi) + for (ty,tystat) in ms.tystats + try + modl = "$(tystat.modl)" + tyname = "$(ty)" + push!(resty, + ModuleStatsInTypeRecord(modl, tyname, tystat.occurs)) + catch err + println(errio, "ERROR: modstats_table: ty-loop: $err"); + throw(err) + end + end + (resmeth,resmi,resty) end diff --git a/Stability/src/Stability.jl b/Stability/src/Stability.jl index 0e5d27f..b6ea038 100644 --- a/Stability/src/Stability.jl +++ b/Stability/src/Stability.jl @@ -9,7 +9,7 @@ using Pkg using CSV export is_concrete_type, is_grounded_call, all_mis_of_module, - MIStats, MethodStats, ModuleStats, + MIStats, MethodStats, InTypeStats, ModuleStats, module_stats, modstats_summary, modstats_table, package_stats, cfg_stats, show_comma_sep @@ -52,6 +52,7 @@ include("Utils.jl") # * stability-errors.out # * stability-stats-per-method.csv # * stability-stats-per-instance.csv +# * stability-stats-intypes.csv # * $pakg-version.txt (version stamp for future reference) # # Setting a package version: @@ -114,6 +115,7 @@ package_stats(pakg :: String, ver = nothing) = begin # txtToCsv(work_dir, "stability-stats-per-method") txtToCsv(work_dir, "stability-stats-per-instance") + txtToCsv(work_dir, "stability-stats-intypes") @myinfo pkgtag "Results successfully converted to CSV. The package is DONE!" end diff --git a/Stability/src/Stats.jl b/Stability/src/Stats.jl index e1cce1b..2b7116a 100644 --- a/Stability/src/Stats.jl +++ b/Stability/src/Stats.jl @@ -49,7 +49,6 @@ is_return(::Any) = false # ------------------------------------------- # -# # Statistics gathered per method. # # ------------------------------------------- @@ -87,6 +86,23 @@ import Base.(+) show_comma_sep(xs::Vector) = join(xs, ",") +# -------------------------------------------------------- +# +# Statistics for all types occured during instantiations. +# +# -------------------------------------------------------- + +# Note on "mutable": stats are only mutable during their calculation. +@kwdef mutable struct InTypeStats + modl :: String + occurs :: Int +end + +InTypeStats(modl :: Module) = InTypeStats("$modl", 0) +InTypeStats(modl :: String) = InTypeStats(modl, 0) + +@deriveEq(InTypeStats) + # ------------------------------------------- # # Statistics gathered per module. @@ -97,9 +113,15 @@ show_comma_sep(xs::Vector) = join(xs, ",") modl :: Module mestats :: Dict{Method, MethodStats} mistats :: Dict{MethodInstance, MIStats} + tystats :: Dict{Any, InTypeStats} end -ModuleStats(modl :: Module) = ModuleStats(modl, Dict{Method, MethodStats}(), Dict{Method, MIStats}()) +ModuleStats(modl :: Module) = ModuleStats( + modl, + Dict{Method, MethodStats}(), + Dict{Method, MIStats}(), + Dict{Any, InTypeStats}() +) @deriveEq(ModuleStats) @@ -133,17 +155,20 @@ module_stats(modl :: Module, errio :: IO = stderr) = begin res = ModuleStats(modl) mis = all_mis_of_module(modl) for mi in mis + + # Special cases: @generated, blocklisted if isdefined(mi.def, :generator) # can't handle @generated functions, Issue #11 @debug "GENERATED $(mi)" continue end - is_blocklisted(modl, mi.def.module) && (@debug "alien: $mi.def defined in $mi.def.module"; continue) + # Lookup method stats object for this `mi` fs = get!(res.mestats, mi.def, fstats_default(mi.def.nospecialize, occursin("Vararg","$(mi.def.sig)"))) try + # Get code of the `mi` for later analysis call = reconstruct_func_call(mi) if call === nothing # this mi is a constructor call - skip delete!(res.mestats, mi.def) @@ -152,7 +177,7 @@ module_stats(modl :: Module, errio :: IO = stderr) = begin fs.occurs += 1 - # handle stability/groundedness + # Check stability/groundedness of the code mi_st = false mi_gd = false (code,rettype) = run_type_inference(call...); @@ -165,8 +190,18 @@ module_stats(modl :: Module, errio :: IO = stderr) = begin end end - # handle instance CFG stats + # Handle instance CFG stats res.mistats[mi] = MIStats(mi_st, mi_gd, cfg_stats(code)..., rettype, call[2] #= input types =#) + + # Collect intypes + intypes = Base.unwrap_unionall(mi.specTypes).types[2:end] + for ty in intypes + tymodl = moduleChainOfType(ty) + tystat = get!(res.tystats, + ty, + InTypeStats(tymodl)) + tystat.occurs += 1 + end catch err fs.fail += 1 print(errio, "ERROR: "); diff --git a/Stability/src/Utils.jl b/Stability/src/Utils.jl index b17e387..0070670 100644 --- a/Stability/src/Utils.jl +++ b/Stability/src/Utils.jl @@ -46,9 +46,19 @@ end txtToCsv(work_dir :: String, basename :: String) = begin resf = joinpath(work_dir, "$basename.txt") - isfile(resf) || (@error "Stability analysis failed to produce output $resf"; return) + isfile(resf) || (throw(ErrorException("Stability analysis failed to produce output $resf"))) st = eval(Meta.parse( open(f-> read(f,String), resf,"r"))) CSV.write(joinpath(work_dir, "$basename.csv"), st) end + +moduleChainOfType(@nospecialize(ty)) :: String = begin + mod=parentmodule(ty) + res="$mod" + while parentmodule(ty) != mod + mod = parentmodule(mod) + res = "$mod." * res + end + res +end diff --git a/Stability/src/pkg-test-override.jl b/Stability/src/pkg-test-override.jl index c31a671..998cc8c 100644 --- a/Stability/src/pkg-test-override.jl +++ b/Stability/src/pkg-test-override.jl @@ -49,7 +49,8 @@ function Pkg.Operations.gen_test_code(testfile::String; open(out -> println(out, pakg * "," * show_comma_sep(summary)), joinpath(wdir, "stability-summary.out"), "w") @info "[Stability] [Package: " * pakg * "] Constructing a table from the stats..." - (methst, mist) = modstats_table(ms) + (methst, mist, tyt) = modstats_table(ms) + @info "[Stability] [Package: " * pakg * "] Table size (per method): " * string(length(methst)) outf = joinpath(wdir, "stability-stats-per-method.txt") @info "[Stability] [Package: " * pakg * "] About to store per method results to: " * outf @@ -59,6 +60,11 @@ function Pkg.Operations.gen_test_code(testfile::String; outf = joinpath(wdir, "stability-stats-per-instance.txt") @info "[Stability] [Package: " * pakg * "] About to store per instance results to: " * outf open(f-> println(f,mist), outf,"w") + + @info "[Stability] [Package: " * pakg * "] Table size (types): " * string(length(tyt)) + outf = joinpath(wdir, "stability-stats-intypes.txt") + @info "[Stability] [Package: " * pakg * "] About to store intypes to: " * outf + open(f-> println(f,tyt), outf,"w") end @info "[Stability] [Package: " * pakg * "] Finish testing + analysis" #### End diff --git a/Stability/test/runtests.jl b/Stability/test/runtests.jl index 214ead0..638d62e 100644 --- a/Stability/test/runtests.jl +++ b/Stability/test/runtests.jl @@ -22,5 +22,6 @@ finst=fmeth.specializations[1] ModuleStats(M, Dict(fmeth=>MethodStats(; occurs=1, stable=1, grounded=1, nospec=0, vararg=0, fail=0)), Dict(finst=>MIStats(; st=1, gd=1, gt=0, rt=0, rettype=Int64, intypes=Core.svec(Int64))), + Dict(Int64=>InTypeStats("Core", 1)), ) end