Index: lib/src/measurements.dart |
diff --git a/lib/src/measurements.dart b/lib/src/measurements.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..499f00173aa3cad09a6a0e8a6644b83d77881f40 |
--- /dev/null |
+++ b/lib/src/measurements.dart |
@@ -0,0 +1,251 @@ |
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+/// Measurements collected about individual functions. Currently we compute |
+/// data about "sends", to classify whether we know the target or not. |
+library dart2js_info.src.measurements; |
+ |
+/// Top-level set of metrics |
+const List<Metric> _topLevelMetrics = const [ |
+ Metric.functions, |
+ Metric.send, |
+]; |
+ |
+/// Apply `f` on each metric in DFS order on the metric tree. [Metric.functions] |
+/// and [Metric.send] are the top level metrics. See those declarations for |
+/// details on the subtrees. |
+visitAllMetrics(f) { |
+ var parentsStack = []; |
+ helper(Metric m) { |
+ f(m, parentsStack); |
+ if (m is GroupedMetric) { |
+ parentsStack.add(m); |
+ m.submetrics.forEach(helper); |
+ parentsStack.removeLast(); |
+ } |
+ } |
+ _topLevelMetrics.forEach(helper); |
+} |
+ |
+/// A metric we intend to measure. |
+class Metric { |
+ /// Name for the metric. |
+ final String name; |
+ |
+ const Metric(this.name); |
+ |
+ String toString() => name; |
+ |
+ /// Total functions in a library/package/program. Parent of |
+ /// [reachableFunction]. |
+ static const Metric functions = const GroupedMetric('functions', const [ |
+ reachableFunctions, |
+ ]); |
+ |
+ /// Subset of the functions that are reachable. |
+ static const Metric reachableFunctions = const Metric('reachable functions'); |
+ |
+ /// Parent of all send metrics. We classify sends as follows: |
+ /// |
+ /// sends |
+ /// |- monomorphic |
+ /// | |- static (top-levels, statics) |
+ /// | |- super |
+ /// | |- local (access to a local var, call local function) |
+ /// | |- constructor (like factory ctros) |
+ /// | |- type variable (reading a type variable) |
+ /// | |- nsm (known no such method exception) |
+ /// | |- single-nsm-call (known no such method call, single target) |
+ /// | |- instance (non-interceptor, only one possible target) |
+ /// | '- interceptor (interceptor, known) |
+ /// | |
+ /// '- polymorphic |
+ /// |- multi-nsm (known to be nSM, but not sure if error, or call, or |
+ /// which call) |
+ /// |- virtual (traditional virtual call, polymorphic equivalent of |
+ /// | `instance`, no-interceptor) |
+ /// |- multi-interceptor (1 of n possible interceptors) |
+ /// '- dynamic (any combination of the above) |
+ /// |
+ static const Metric send = const GroupedMetric('send', const [ |
+ monomorphicSend, |
+ polymorphicSend, |
+ ]); |
+ |
+ /// Parent of monomorphic sends, see [send] for details. |
+ static const Metric monomorphicSend = const GroupedMetric('monomorphic', |
+ const [ |
+ staticSend, |
+ superSend, |
+ localSend, |
+ constructorSend, |
+ typeVariableSend, |
+ nsmErrorSend, |
+ singleNsmCallSend, |
+ instanceSend, |
+ interceptorSend, |
+ ]); |
+ |
+ /// Metric for static calls, see [send] for details. |
+ static const Metric staticSend = const Metric('static'); |
+ |
+ /// Metric for super calls, see [send] for details. |
+ static const Metric superSend = const Metric('super'); |
+ |
+ /// Metric for local variable sends, see [send] for details. |
+ static const Metric localSend = const Metric('local'); |
+ |
+ /// Metric for constructor sends, see [send] for details. |
+ static const Metric constructorSend = const Metric('constructor'); |
+ |
+ /// Metric for type-variable sends, see [send] for details. |
+ // TODO(sigmund): delete? is mainly associated with compile-time errors |
+ static const Metric typeVariableSend = const Metric('type variable'); |
+ |
+ /// Metric for no-such-method errors, see [send] for details. |
+ static const Metric nsmErrorSend = const Metric('nSM error'); |
+ |
+ /// Metric for calls to noSuchMethod methods with a known target, see [send] |
+ /// for details. |
+ static const Metric singleNsmCallSend = const Metric('nSM call single'); |
+ |
+ /// Metric for calls to a precisely known instance method, see [send] for |
+ /// details. |
+ static const Metric instanceSend = const Metric('instance'); |
+ |
+ /// Metric for calls to a precisely known interceptor method, see [send] for |
+ /// details. |
+ static const Metric interceptorSend = const Metric('interceptor'); |
+ |
+ /// Parent of polymorphic sends, see [send] for details. |
+ static const Metric polymorphicSend = const GroupedMetric('polymorphic', |
+ const [ |
+ multiNsmCallSend, |
+ virtualSend, |
+ multiInterceptorSend, |
+ dynamicSend, |
+ ]); |
+ |
+ /// Metric for calls to noSuchMethod methods with more than one possible |
+ /// target, see [send] for details. |
+ static const Metric multiNsmCallSend = const Metric('nSM call multi'); |
+ |
+ /// Metric for calls that are dispatched virtually ar runtime, see [send] for |
+ /// details. |
+ static const Metric virtualSend = const Metric('virtual'); |
+ |
+ /// Metyric for calls to more than one possible interceptor, see [send] for |
+ /// details. |
+ static const Metric multiInterceptorSend = const Metric('interceptor multi'); |
+ |
+ /// Metyric for dynamic calls for which we know nothing about the target |
+ /// method. See [send] for details. |
+ static const Metric dynamicSend = const Metric('dynamic'); |
+ |
+ String toJson() => name; |
+ static Map<String, Metric> _nameToMetricMap = () { |
+ var res = {}; |
+ visitAllMetrics((m, _) => res[m.name] = m); |
+ return res; |
+ }(); |
+ |
+ static Metric fromJson(String name) => _nameToMetricMap[name]; |
+} |
+ |
+/// A metric that is subdivided in smaller metrics. |
+class GroupedMetric extends Metric { |
+ final List<Metric> submetrics; |
+ |
+ const GroupedMetric(String name, this.submetrics) : super(name); |
+} |
+ |
+/// A measurement entry (practically a source-span location where the |
+/// measurement was seen). |
+class Entry { |
+ final int begin; |
+ final int end; |
+ Entry(this.begin, this.end); |
+} |
+ |
+/// A collection of data points for each metric. Used to summarize a single |
+/// function, a library, a package, or an entire program. |
+class Measurements { |
+ final Uri uri; |
+ final Map<Metric, List<Entry>> entries; |
+ final Map<Metric, int> counters; |
+ |
+ Measurements([this.uri]) |
+ : entries = <Metric, List<Entry>>{}, |
+ counters = <Metric, int>{}; |
+ |
+ const Measurements.unreachableFunction() |
+ : counters = const { Metric.functions: 1}, entries = const {}, uri = null; |
+ |
+ Measurements.reachableFunction([this.uri]) |
+ : counters = { Metric.functions: 1, Metric.reachableFunctions: 1}, |
+ entries = {}; |
+ |
+ /// Record [metric] was seen. The optional [begin] and [end] offsets are |
+ /// included for metrics that correspond to a source range. Intended to be |
+ /// used by `StatsBuilder`. |
+ record(Metric metric, [int begin, int end]) { |
+ if (begin != null && end != null) { |
+ assert(uri != null); |
+ entries.putIfAbsent(metric, () => []).add(new Entry(begin, end)); |
+ } |
+ counters.putIfAbsent(metric, () => 0); |
+ counters[metric]++; |
+ } |
+ |
+ /// Removes a previously added entry. Intended only to be used by |
+ /// `StatsBuilder`. Internally `StatsBuilder` computes redundant information |
+ /// in order to check for coverage and validate invariants with |
+ /// [checkInvariant]. This is used to adjust some of the redundant |
+ /// information. |
+ popLast(Metric metric) { |
+ assert(entries[metric] != null && entries[metric].isNotEmpty); |
+ entries[metric].removeLast(); |
+ counters[metric]--; |
+ } |
+ |
+ /// Add the counters from [other] into this set of measurements. |
+ addFrom(Measurements other) { |
+ other.counters.forEach((metric, value) { |
+ var current = counters[metric]; |
+ counters[metric] = current == null ? value : current + value; |
+ }); |
+ } |
+ |
+ /// Check that every grouped metric totals the individual counts of it's |
+ /// submetric. |
+ bool checkInvariant(GroupedMetric key) { |
+ int total = counters[key] ?? 0; |
+ int submetricTotal = 0; |
+ for (var metric in key.submetrics) { |
+ var n = counters[metric]; |
+ if (n != null) submetricTotal += n; |
+ } |
+ return total == submetricTotal; |
+ } |
+ |
+ Map toJson() { |
+ var jsonEntries = <String, List<Map>>{}; |
+ entries.forEach((metric, values) { |
+ jsonEntries[metric.toJson()] = |
+ values.expand((e) => [e.begin, e.end]).toList(); |
+ }); |
+ var json = {'entries': jsonEntries}; |
+ // TODO(sigmund): encode uri as an offset of the URIs available in the parts |
+ // of the library info. |
+ if (uri != null) json['sourceFile'] = '$uri'; |
+ if (counters[Metric.functions] != null) { |
+ json[Metric.functions.toJson()] = counters[Metric.functions]; |
+ } |
+ if (counters[Metric.reachableFunctions] != null) { |
+ json[Metric.reachableFunctions.toJson()] = |
+ counters[Metric.reachableFunctions]; |
+ } |
+ return json; |
+ } |
+} |