# Metrics from RTA
module rta_metrics
-import modelbuilder
private import rapid_type_analysis
-private import metrics_base
-import frontend
+import metrics_base
+import mmodules_metrics
+import mclasses_metrics
redef class ToolContext
- var rta_metrics_phase = new RTAMetricsPhase(self, null)
+ var rta_metrics_phase: Phase = new RTAMetricsPhase(self, null)
end
private class RTAMetricsPhase
super Phase
- redef fun process_mainmodule(mainmodule)
+ redef fun process_mainmodule(mainmodule, given_mmodules)
do
if not toolcontext.opt_rta.value and not toolcontext.opt_all.value then return
- compute_rta_metrics(toolcontext.modelbuilder, mainmodule)
+ var csv = toolcontext.opt_csv.value
+ var out = "{toolcontext.opt_dir.value or else "metrics"}/rta"
+ out.mkdir
+
+ print toolcontext.format_h1("\n# RTA metrics")
+
+ print toolcontext.format_h2("\n ## Live instances by mainmodules")
+ var mmetrics = new MetricSet
+ mmetrics.register(new MNLC(toolcontext.modelbuilder))
+ mmetrics.register(new MNLT(toolcontext.modelbuilder))
+ mmetrics.register(new MNCT(toolcontext.modelbuilder))
+ mmetrics.register(new MNLI(toolcontext.modelbuilder))
+ mmetrics.register(new MNLM(toolcontext.modelbuilder))
+ mmetrics.register(new MNLMD(toolcontext.modelbuilder))
+ mmetrics.register(new MNLDD(toolcontext.modelbuilder))
+ mmetrics.collect(new HashSet[MModule].from([mainmodule]))
+ mmetrics.to_console(1, not toolcontext.opt_nocolors.value)
+ if csv then mmetrics.to_csv.write_to_file("{out}/{mainmodule}.csv")
+
+ var mtypes = new HashSet[MType]
+ var analysis = new RapidTypeAnalysis(toolcontext.modelbuilder, mainmodule)
+ analysis.run_analysis
+ mtypes.add_all(analysis.live_types)
+ mtypes.add_all(analysis.live_cast_types)
+
+ print toolcontext.format_h2("\n ## Total live instances by mclasses")
+ var cmetrics = new MetricSet
+ cmetrics.register(analysis.cnli)
+ cmetrics.register(analysis.cnlc)
+ cmetrics.to_console(1, not toolcontext.opt_nocolors.value)
+ if csv then cmetrics.to_csv.write_to_file("{out}/mclasses.csv")
+
+ print toolcontext.format_h2("\n ## Total live instances by mtypes")
+ var tmetrics = new MetricSet
+ tmetrics.register(analysis.tnli)
+ tmetrics.register(analysis.tnlc)
+ tmetrics.to_console(1, not toolcontext.opt_nocolors.value)
+ if csv then tmetrics.to_csv.write_to_file("{out}/mtypes.csv")
+
+ print toolcontext.format_h2("\n ## MType complexity")
+ var gmetrics = new MetricSet
+ gmetrics.register(new TAGS)
+ gmetrics.register(new TDGS)
+ gmetrics.collect(mtypes)
+ gmetrics.to_console(1, not toolcontext.opt_nocolors.value)
+ if csv then gmetrics.to_csv.write_to_file("{out}/complexity.csv")
+
+ callsite_info(analysis)
+
+ # dump type and method infos
+ if csv then
+ analysis.live_types_to_csv.write_to_file("{out}/rta_types.csv")
+ analysis.live_methods_to_tree.write_to_file("{out}/rta_methods.dat")
+ end
+ end
+
+ fun callsite_info(rta: RapidTypeAnalysis)
+ do
+ print toolcontext.format_h2("\n ## Callsites")
+ print "* {rta.live_callsites.length} live callsites"
+
+ var csep = new Counter[MPropDef]
+ var cglo = new Counter[MPropDef]
+ var morphisme = new Counter[Int]
+ for cs in rta.live_callsites do
+ csep.inc(cs.mpropdef)
+ var targets = rta.live_targets(cs)
+ for d in targets do
+ cglo.inc(d)
+ end
+ morphisme.inc(targets.length)
+ end
+
+ print toolcontext.format_h3("MMethodDef locally designated (by number of CallSites)")
+ csep.print_summary
+ csep.print_elements(5)
+
+ print toolcontext.format_h3("MMethodDef possibly invoked at runtime (by number of CallSites)")
+ cglo.print_summary
+ cglo.print_elements(5)
end
end
+# Summary metrics
-redef class MType
- private var nlvt: Int = 0
- private var nlct: Int = 0
+# MModule Metric: Number of Live Types
+class MNLI
+ super MModuleMetric
+ super IntMetric
+ redef fun name do return "mnli"
+ redef fun desc do return "number of live instances in a mmodule"
+
+ var modelbuilder: ModelBuilder
+ init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
- private fun is_standard: Bool do
- var mtype = self
- if mtype isa MNullableType then mtype = mtype.mtype
- return self.as(MClassType).mclass.is_standard
+ redef fun collect(mainmodules) do
+ for mainmodule in mainmodules do
+ var analysis = new RapidTypeAnalysis(modelbuilder, mainmodule)
+ analysis.run_analysis
+ values[mainmodule] = analysis.tnli.sum
+ end
end
+end
- private fun get_depth: Int do
- var mtype = self
- if mtype isa MNullableType then mtype = mtype.mtype
- if not mtype isa MGenericType then return 0
+# MModule Metric: Number of Live Types
+class MNLT
+ super MModuleMetric
+ super IntMetric
+ redef fun name do return "mnlt"
+ redef fun desc do return "number of live mtypes in a mmodule"
- var depth = 0
- for ft in mtype.arguments do
- if ft.get_depth > depth then depth = ft.get_depth
+ var modelbuilder: ModelBuilder
+ init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
+
+ redef fun collect(mainmodules) do
+ for mainmodule in mainmodules do
+ var analysis = new RapidTypeAnalysis(modelbuilder, mainmodule)
+ analysis.run_analysis
+ values[mainmodule] = analysis.live_types.length
end
- return depth + 1
end
end
-redef class MClass
- private var nlvt: Int = 0
- private var nlct: Int = 0
- private var live_types: Set[MType] = new HashSet[MType]
- private var cast_types: Set[MType] = new HashSet[MType]
-end
-
-# Run a runtime type analysis and print metrics
-fun compute_rta_metrics(modelbuilder: ModelBuilder, mainmodule: MModule)
-do
- var analysis = modelbuilder.do_rapid_type_analysis(mainmodule)
-
- var nlvt = 0 # NLVT Number of Live Type
- var nlvtg = 0 # NLVTG Number of Generic Live Type
- var nlvtslud = 0 # NLCTSLUD Number of Live Type defined in SL and init in UD
- var nlvtgslud = 0 # NLVTGSLUD Number of Generic Live Type defined in SL and init in UD
- var nlvtudud = 0 # NLVTUDUD Number of Live Type defined in UD and init in UD
- var nlvtgudud = 0 # NLVTGUDUD Number of Generic Live Type defined in UD and init in UD
-
- var nlct = 0 # NLCT Number of Live Cast Type
- var nlctg = 0 # NLCTG Number of Generic Live Cast Type
- var nlctslud = 0 # NLCTSLUD Number of Live Cast Type defined in SL and init in UD
- var nlctgslud = 0 # NLCTGSLUD Number of Generic Live Cast Type defined in SL and init in UD
- var nlctudud = 0 # NLCTUDUD Number of Live Cast Type defined in UD and init in UD
- var nlctgudud = 0 # NLCTGUDUD Number of Generic Live Cast Type defined in UD and init in UD
-
- var mtypes = new HashSet[MClassType]
-
- for mtype in analysis.live_types do
- mtypes.add(mtype)
- nlvt += 1
- mtype.mclass.nlvt += 1
- mtype.mclass.live_types.add(mtype)
- if mtype isa MGenericType then nlvtg += 1
- if not mtype.is_standard then
- nlvtudud += 1
- if mtype isa MGenericType then nlvtgudud += 1
- else
- nlvtslud += 1
- if mtype isa MGenericType then nlvtgslud += 1
+# MModule Metric: Number of Live Cast Types
+class MNCT
+ super MModuleMetric
+ super IntMetric
+ redef fun name do return "mnct"
+ redef fun desc do return "number of live cast mtypes in a mmodule"
+
+ var modelbuilder: ModelBuilder
+ init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
+
+ redef fun collect(mainmodules) do
+ for mainmodule in mainmodules do
+ var analysis = new RapidTypeAnalysis(modelbuilder, mainmodule)
+ analysis.run_analysis
+ values[mainmodule] = analysis.live_cast_types.length
end
end
+end
+
+# MModule Metric: Number of Live Classes
+class MNLC
+ super MModuleMetric
+ super IntMetric
+ redef fun name do return "mnlc"
+ redef fun desc do return "number of live mclasses in a mmodule"
+
+ var modelbuilder: ModelBuilder
+ init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
- for mtype in analysis.live_cast_types do
- if mtype isa MNullableType then mtype = mtype.mtype
- if not mtype isa MClassType then continue
- mtypes.add(mtype)
- nlct += 1
- mtype.mclass.nlct += 1
- mtype.mclass.cast_types.add(mtype)
- if mtype isa MGenericType then nlctg += 1
- if not mtype.is_standard then
- nlctudud += 1
- if mtype isa MGenericType then nlctgudud += 1
- else
- nlctslud += 1
- if mtype isa MGenericType then nlctgslud += 1
+ redef fun collect(mainmodules) do
+ for mainmodule in mainmodules do
+ var live = new HashSet[MClass]
+ var analysis = new RapidTypeAnalysis(modelbuilder, mainmodule)
+ analysis.run_analysis
+ for mtype in analysis.live_types do
+ live.add(mtype.mclass)
+ end
+ values[mainmodule] = live.length
end
end
+end
- # CSV generation
- if modelbuilder.toolcontext.opt_generate_csv.value then
- var summaryCSV = new CSVDocument
- summaryCSV.set_header("scope", "NLVT", "NLVTG", "NLCT", "NLVCTG")
- summaryCSV.add_line("global", nlvt, nlvtg, nlct, nlctg)
- summaryCSV.add_line("SLUD", nlvtslud, nlvtgslud, nlctslud, nlctgslud)
- summaryCSV.add_line("UDUD", nlvtudud, nlvtgudud, nlctudud, nlctgudud)
- summaryCSV.save(modelbuilder.toolcontext.output_dir.join_path("rta_sum_metrics.csv"))
+# MModule Metric: Number of Live Methods
+class MNLM
+ super MModuleMetric
+ super IntMetric
+ redef fun name do return "mnlm"
+ redef fun desc do return "number of live methods in a mmodule"
- var scalarCSV = new CSVDocument
- var udscalarCSV = new CSVDocument
- scalarCSV.set_header("Type", "AGS", "DGS", "NLVT", "NLCT")
- udscalarCSV.set_header("Type", "AGS", "DGS", "NLVT", "NLCT")
+ var modelbuilder: ModelBuilder
+ init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
- for mtype in mtypes do
- var arity = 0
- if mtype isa MGenericType then arity = mtype.arguments.length
- if not mtype.is_standard then
- udscalarCSV.add_line(mtype, arity, mtype.get_depth, mtype.nlvt, mtype.nlct)
+ redef fun collect(mainmodules) do
+ for mainmodule in mainmodules do
+ var analysis = new RapidTypeAnalysis(modelbuilder, mainmodule)
+ analysis.run_analysis
+ values[mainmodule] = analysis.live_methods.length
+ end
+ end
+end
+
+# MModule Metric: Number of Live MethodDefs
+class MNLMD
+ super MModuleMetric
+ super IntMetric
+ redef fun name do return "mnlmd"
+ redef fun desc do return "number of live method definitions in a mmodule"
+
+ var modelbuilder: ModelBuilder
+ init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
+
+ redef fun collect(mainmodules) do
+ for mainmodule in mainmodules do
+ var analysis = new RapidTypeAnalysis(modelbuilder, mainmodule)
+ analysis.run_analysis
+ values[mainmodule] = analysis.live_methoddefs.length
+ end
+ end
+end
+
+# MModule Metric: Number of Dead MethodDefs
+class MNLDD
+ super MModuleMetric
+ super IntMetric
+ redef fun name do return "mnldd"
+ redef fun desc do return "number of dead method definitions in a mmodule"
+
+ var modelbuilder: ModelBuilder
+ init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
+
+ redef fun collect(mainmodules) do
+ for mainmodule in mainmodules do
+ var dead = 0
+ var analysis = new RapidTypeAnalysis(modelbuilder, mainmodule)
+ analysis.run_analysis
+ for mmethod in analysis.live_methods do
+ for mdef in mmethod.mpropdefs do
+ if analysis.live_methoddefs.has(mdef) or mdef.is_abstract then continue
+ dead += 1
+ end
end
- scalarCSV.add_line(mtype, arity, mtype.get_depth, mtype.nlvt, mtype.nlct)
+ values[mainmodule] = dead
end
- scalarCSV.save(modelbuilder.toolcontext.output_dir.join_path("rta_scalar_metrics.csv"))
- udscalarCSV.save(modelbuilder.toolcontext.output_dir.join_path("rta_ud_scalar_metrics.csv"))
-
- scalarCSV = new CSVDocument
- udscalarCSV = new CSVDocument
- scalarCSV.set_header("Class", "AGS", "NLVV", "NLVT")
- udscalarCSV.set_header("Class", "AGS", "NLVV", "inst")
-
- for mclass in modelbuilder.model.mclasses do
- if not mclass.is_class or mclass.is_abstract then continue
- if not mclass.is_standard then
- udscalarCSV.add_line(mclass.mclass_type, mclass.arity, mclass.live_types.length, mclass.nlvt)
+ end
+end
+
+# MClass metrics
+
+# Class Metric: Number of Live Instances
+#
+# count all the `new` made on each mclass
+class CNLI
+ super MClassMetric
+ super IntMetric
+ redef fun name do return "cnli"
+ redef fun desc do return "number of live instances for a mclass"
+
+ redef fun collect(mclasses) do end
+end
+
+# Class Metric: Number of Live Cast
+#
+# count all the cast made on each mclass type
+class CNLC
+ super MClassMetric
+ super IntMetric
+ redef fun name do return "cnlc"
+ redef fun desc do return "number of live cast for a mclass type"
+
+ redef fun collect(mclasses) do end
+end
+
+# MType metrics
+
+# A metric about MType
+interface MTypeMetric
+ super Metric
+ redef type ELM: MType
+end
+
+# Type Metric: Number of Live Instances
+#
+# count all the `new` made on each types
+class TNLI
+ super MTypeMetric
+ super IntMetric
+ redef fun name do return "tnli"
+ redef fun desc do return "number of live instances for a mtype"
+
+ redef fun collect(mtypes) do end
+end
+
+# Type Metric: Number of Live Cast
+#
+# count all the cast made to each types
+class TNLC
+ super MTypeMetric
+ super IntMetric
+ redef fun name do return "tnlc"
+ redef fun desc do return "number of live casts to a mtype"
+
+ redef fun collect(mtypes) do end
+end
+
+# Type Metric: Arity of Generic Signature
+#
+# tags(List[X]) = 1
+# tags(Map[X, Y]) = 2
+class TAGS
+ super MTypeMetric
+ super IntMetric
+ redef fun name do return "tags"
+ redef fun desc do return "arity of generic signature"
+
+ redef fun collect(mtypes) do
+ for mtype in mtypes do
+ if mtype isa MGenericType then
+ values[mtype] = mtype.arguments.length
+ else
+ values[mtype] = 0
end
- scalarCSV.add_line(mclass.mclass_type, mclass.arity, mclass.live_types.length, mclass.nlvt)
end
- scalarCSV.save(modelbuilder.toolcontext.output_dir.join_path("rta_scalar_class_metrics.csv"))
- udscalarCSV.save(modelbuilder.toolcontext.output_dir.join_path("rta_ud_scalar_class_metrics.csv"))
end
+end
+
+# Type Metric: Depth of Generic Signature
+#
+# tdgs(List[X]) = 1
+# tdgs(Map[X, List[Y]]) = 2
+class TDGS
+ super MTypeMetric
+ super IntMetric
+ redef fun name do return "tdos"
+ redef fun desc do return "depth of generic signature"
+
+ redef fun collect(mtypes) do
+ for mtype in mtypes do
+ values[mtype] = mtype.signature_depth
+ end
+ end
+end
+
+# rta redef
+
+redef class RapidTypeAnalysis
+ var cnli = new CNLI
+ var cnlc = new CNLC
+ var tnli = new TNLI
+ var tnlc = new TNLC
+
+ redef fun add_new(recv, mtype) do
+ super
+ tnli.values.inc(mtype)
+ cnli.values.inc(mtype.mclass)
+ end
+
+ redef fun add_cast(mtype) do
+ super
+ tnlc.values.inc(mtype)
+
+ mtype = mtype.undecorate
+ if mtype isa MClassType then
+ cnlc.values.inc(mtype.mclass)
+ end
+ end
+end
+
+# model redefs
+
+redef class MType
+ private fun signature_depth: Int do
+ var mtype = self.undecorate
+ if not mtype isa MGenericType then return 0
- print "--- RTA metrics ---"
- print "Number of live runtime classes: {analysis.live_classes.length}"
- if analysis.live_classes.length < 8 then print "\t{analysis.live_classes.join(" ")}"
- print "Number of live runtime types (instantied resolved type): {analysis.live_types.length}"
- if analysis.live_types.length < 8 then print "\t{analysis.live_types.join(" ")}"
- print "Number of live methods: {analysis.live_methods.length}"
- if analysis.live_methods.length < 8 then print "\t{analysis.live_methods.join(" ")}"
- print "Number of live method definitions: {analysis.live_methoddefs.length}"
- if analysis.live_methoddefs.length < 8 then print "\t{analysis.live_methoddefs.join(" ")}"
- print "Number of live runtime cast types (ie used in as and isa): {analysis.live_cast_types.length}"
- if analysis.live_cast_types.length < 8 then print "\t{analysis.live_cast_types.join(" ")}"
-
- var x = 0
- for p in analysis.live_methods do
- for d in p.mpropdefs do
- if analysis.live_methoddefs.has(d) or d.is_abstract then continue
- x += 1
+ var depth = 0
+ for ft in mtype.arguments do
+ var ftd = ft.signature_depth
+ if ftd > depth then depth = ftd
end
+ return depth + 1
end
- print "Number of dead method definitions of live methods: {x}"
end
+