Merge: doc: fixed some typos and other misc. corrections
[nit.git] / src / metrics / rta_metrics.nit
index cebf766..dc3077e 100644 (file)
 # Metrics from RTA
 module rta_metrics
 
-import modelbuilder
 private import rapid_type_analysis
+import metrics_base
+import mmodules_metrics
+import mclasses_metrics
 
-# 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)
-
-       print "--- RTA metrics ---"
-       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 method definitions: {analysis.live_methoddefs.length}"
-       if analysis.live_methoddefs.length < 8 then print "\t{analysis.live_methoddefs.join(" ")}"
-       print "Number of live customized method definitions: {analysis.live_customized_methoddefs.length}"
-       if analysis.live_customized_methoddefs.length < 8 then print "\t{analysis.live_customized_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(" ")}"
+redef class ToolContext
+
+       # RTA related metrics phase
+       var rta_metrics_phase: Phase = new RTAMetricsPhase(self, null)
+end
+
+private class RTAMetricsPhase
+       super Phase
+       redef fun process_mainmodule(mainmodule, given_mmodules)
+       do
+               if not toolcontext.opt_rta.value and not toolcontext.opt_all.value then return
+               var csv = toolcontext.opt_csv.value
+               var out = "{toolcontext.opt_dir.value or else "metrics"}/rta"
+               out.mkdir
+
+               var model = toolcontext.modelbuilder.model
+               var filter = new ModelFilter(min_visibility = protected_visibility)
+
+               print toolcontext.format_h1("\n# RTA metrics")
+
+               print toolcontext.format_h2("\n ## Live instances by mainmodules")
+               var mmetrics = new MetricSet
+               mmetrics.register(new MNLC(model, mainmodule, filter, toolcontext.modelbuilder))
+               mmetrics.register(new MNLT(model, mainmodule, filter, toolcontext.modelbuilder))
+               mmetrics.register(new MNCT(model, mainmodule, filter, toolcontext.modelbuilder))
+               mmetrics.register(new MNLI(model, mainmodule, filter, toolcontext.modelbuilder))
+               mmetrics.register(new MNLM(model, mainmodule, filter, toolcontext.modelbuilder))
+               mmetrics.register(new MNLMD(model, mainmodule, filter, toolcontext.modelbuilder))
+               mmetrics.register(new MNLDD(model, mainmodule, filter, 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 MetricsRapidTypeAnalysis(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
+
+# RTA related metric that needs a `modelbuilder`
+class RTAMetric
+       super MModuleMetric
+
+       # Modelbuilder used to access AST
+       var modelbuilder: ModelBuilder
+end
+
+# MModule Metric: Number of Live Types
+class MNLI
+       super RTAMetric
+       super IntMetric
+       redef fun name do return "mnli"
+       redef fun desc do return "number of live instances in a mmodule"
+
+
+       redef fun collect(mainmodules) do
+               for mainmodule in mainmodules do
+                       var analysis = new MetricsRapidTypeAnalysis(modelbuilder, mainmodule)
+                       analysis.run_analysis
+                       values[mainmodule] = analysis.tnli.sum
+               end
+       end
+end
+
+# MModule Metric: Number of Live Types
+class MNLT
+       super RTAMetric
+       super IntMetric
+       redef fun name do return "mnlt"
+       redef fun desc do return "number of live mtypes in a mmodule"
+
+       redef fun collect(mainmodules) do
+               for mainmodule in mainmodules do
+                       var analysis = new MetricsRapidTypeAnalysis(modelbuilder, mainmodule)
+                       analysis.run_analysis
+                       values[mainmodule] = analysis.live_types.length
+               end
+       end
+end
+
+# MModule Metric: Number of Live Cast Types
+class MNCT
+       super RTAMetric
+       super IntMetric
+       redef fun name do return "mnct"
+       redef fun desc do return "number of live cast mtypes in a mmodule"
+
+       redef fun collect(mainmodules) do
+               for mainmodule in mainmodules do
+                       var analysis = new MetricsRapidTypeAnalysis(modelbuilder, mainmodule)
+                       analysis.run_analysis
+                       values[mainmodule] = analysis.live_cast_types.length
+               end
+       end
+end
+
+# MModule Metric: Number of Live Classes
+class MNLC
+       super RTAMetric
+       super IntMetric
+       redef fun name do return "mnlc"
+       redef fun desc do return "number of live mclasses in a mmodule"
+
+       redef fun collect(mainmodules) do
+               for mainmodule in mainmodules do
+                       var live = new HashSet[MClass]
+                       var analysis = new MetricsRapidTypeAnalysis(modelbuilder, mainmodule)
+                       analysis.run_analysis
+                       for mtype in analysis.live_types do
+                               live.add(mtype.mclass)
+                       end
+                       values[mainmodule] = live.length
+               end
+       end
+end
+
+# MModule Metric: Number of Live Methods
+class MNLM
+       super RTAMetric
+       super IntMetric
+       redef fun name do return "mnlm"
+       redef fun desc do return "number of live methods in a mmodule"
+
+       redef fun collect(mainmodules) do
+               for mainmodule in mainmodules do
+                       var analysis = new MetricsRapidTypeAnalysis(modelbuilder, mainmodule)
+                       analysis.run_analysis
+                       values[mainmodule] = analysis.live_methods.length
+               end
+       end
+end
+
+# MModule Metric: Number of Live MethodDefs
+class MNLMD
+       super RTAMetric
+       super IntMetric
+       redef fun name do return "mnlmd"
+       redef fun desc do return "number of live method definitions in a mmodule"
+
+       redef fun collect(mainmodules) do
+               for mainmodule in mainmodules do
+                       var analysis = new MetricsRapidTypeAnalysis(modelbuilder, mainmodule)
+                       analysis.run_analysis
+                       values[mainmodule] = analysis.live_methoddefs.length
+               end
+       end
+end
+
+# MModule Metric: Number of Dead MethodDefs
+class MNLDD
+       super RTAMetric
+       super IntMetric
+       redef fun name do return "mnldd"
+       redef fun desc do return "number of dead method definitions in a mmodule"
+
+       redef fun collect(mainmodules) do
+               for mainmodule in mainmodules do
+                       var dead = 0
+                       var analysis = new MetricsRapidTypeAnalysis(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
+                       values[mainmodule] = dead
+               end
+       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
+               end
+       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
+
+# Custom RTA analyzer
+class MetricsRapidTypeAnalysis
+       super RapidTypeAnalysis
+
+       # Class Live Instances
+       var cnli: CNLI is lazy do return new CNLI(modelbuilder.model, mainmodule)
+
+       # Class Live Casts
+       var cnlc: CNLC is lazy do return new CNLC(modelbuilder.model, mainmodule)
+
+       # Type Live Instances
+       var tnli = new TNLI
+
+       # Rtpe Live Casts
+       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
+
+               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
 end