Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1740)

Unified Diff: pkg/compiler/lib/src/stats/stats.dart

Issue 1220043005: dart2js send stats, includes: (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/compiler/lib/src/stats/stats.dart
diff --git a/pkg/compiler/lib/src/stats/stats.dart b/pkg/compiler/lib/src/stats/stats.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b95b156a9252aae14bf6af6b8ebec7d6181f3a2c
--- /dev/null
+++ b/pkg/compiler/lib/src/stats/stats.dart
@@ -0,0 +1,464 @@
+// 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.
+
+/// Collects information used to debug and analyze internal parts of the
+/// compiler.
+///
+/// Currently this is focused mainly on data from types and inference, such as
+/// understanding of types of expressions and precision of send operations.
+///
+/// This library focuses on representing the information itself.
+/// `stats_builder.dart` contains visitors we use to collect the data, while
+/// `tools/stats/server.dart` contains logic to visualize the data.
+library stats;
+
+/// All results from a single run of the compiler on an application.
+class GlobalResult {
+ /// Results grouped by package.
+ final Map<String, BundleResult> packages = {};
+
+ /// Results for loose files, typically the entrypoint and files loaded via
+ /// relative imports.
+ final BundleResult loose = new BundleResult('*loose*');
+
+ /// Results from system libraries (dart:core, dart:async, etc).
+ final BundleResult system = new BundleResult('*system*');
+
+ /// Add the result of a library in its corresponding group.
+ void add(LibraryResult library) {
+ if (library.uri.scheme == 'package') {
+ var name = library.uri.pathSegments[0];
+ var package = packages.putIfAbsent(name, () => new BundleResult(name));
+ package.libraries.add(library);
+ } else if (library.uri.scheme == 'dart') {
+ system.libraries.add(library);
+ } else {
+ loose.libraries.add(library);
+ }
+ }
+
+ // TODO(sigmund): consider splitting the serialization in multiple files so
+ // that not all the information has to be stored in memory at once.
+ List toJson() => []
+ ..addAll(packages.values.expand((p) => p.libraries.map((l) => l.toJson())))
+ ..addAll(loose.libraries.map((l) => l.toJson()))
+ ..addAll(system.libraries.map((l) => l.toJson()));
+
+ static GlobalResult fromJson(List json) {
+ var res = new GlobalResult();
+ json.map(LibraryResult.fromJson).forEach(res.add);
+ return res;
+ }
+
+ accept(ResultVisitor v) => v.visitGlobal(this);
+}
+
+/// Summarizes results for a group of libraries. Used by [GlobalResult] to group
+/// the systems libraries, loose libraries, and to create a separate a group per
Johnni Winther 2015/07/13 19:21:44 'a separate a group' -> 'a separate group'
Siggi Cherem (dart-lang) 2015/09/29 01:39:34 Ack. (this went away after refactoring)
+/// package.
+class BundleResult {
+ /// Name of the group.
+ final String name;
+
+ /// Library results that are part of this group.
+ final List<LibraryResult> libraries = [];
+
+ BundleResult(this.name);
+}
+
+/// Aggregate result for all units in a library.
+class LibraryResult {
+ final Uri uri;
+ final List<CompilationUnitResults> units = [];
+ final List<String> classes = [];
Johnni Winther 2015/07/13 19:21:44 What is this used for?
Siggi Cherem (dart-lang) 2015/09/29 01:39:34 it was a placeholder for the future. now that I'm
+ LibraryResult(this.uri);
+ accept(ResultVisitor v) => v.visitLibrary(this);
+
+ Map toJson() => {
+ 'uri': '$uri',
+ 'units': units.map((p) => p.toJson()).toList(),
+ };
+
+ static LibraryResult fromJson(Map json) =>
+ new LibraryResult(Uri.parse(json['uri']))
+ ..units.addAll(json['units'].map(CompilationUnitResult.fromJson));
+}
+
+/// Results of a compilation unit (library or part).
+class CompilationUnitResult {
+ /// Resolved Uri for this unit.
+ final Uri uri;
+
+ /// Results per function and method in this library
+ final List<FunctionResult> functions = [];
+
+ accept(ResultVisitor v) => v.visitUnit(this);
+
+ CompilationUnitResult(this.uri);
+
+ Map toJson() => {
+ 'uri': '$uri',
+ 'functions': functions.map((f) => f.toJson()).toList(),
+ };
+
+ static CompilationUnitResult fromJson(Map json) =>
+ new CompilationUnitResult(Uri.parse(json['uri']))
+ ..functions.addAll(json['functions'].map(FunctionResult.fromJson));
+}
+
+/// Results on a function.
+class FunctionResult {
+ /// Name, if-any, of the function.
+ final String name;
+
+ /// Measurements collected.
+ final Measurements measurements;
+
+ FunctionResult(this.name, this.measurements);
+ accept(ResultVisitor v) => v.visitFunction(this);
+
+ Map toJson() => {
+ 'name' : name,
+ 'measurements': measurements.toJson(),
+ };
+
+ static FunctionResult fromJson(Map json) =>
+ new FunctionResult(json['name'],
+ Measurements.fromJson(json['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".
+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.
+ 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
+/// fucntion, a library, a package, or an entire program.
Johnni Winther 2015/07/13 19:21:44 'fucntion' -> 'function'
Siggi Cherem (dart-lang) 2015/09/29 01:39:34 Done.
+class Measurements {
+ final Map<Metric, List<Entry>> entries;
+ final Map<Metric, int> counters;
+
+ Measurements()
+ : entries = <Metric, List<Entry>>{},
+ counters = <Metric, int>{};
+
+ const Measurements.unreachableFunction()
+ : counters = const { Metric.functions: 1}, entries = const {};
+
+ Measurements.reachableFunction()
+ : 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) {
+ 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) {
+ // TODO(sigmund): use ?? operator.
+ int total = counters[key];
+ if (total == null) total = 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.map((e) => {'begin': e.begin, 'end': e.end}).toList();
+ });
+ var json = {'entries': jsonEntries};
+ 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;
+ }
+
+ static Measurements fromJson(Map json) {
+ var res = new Measurements();
+ for (var key in json.keys) {
+ var value = json[key];
+ if (value == null) continue;
+ if (key == 'entries') {
+ value.forEach((metric, jsonEntries) {
+ jsonEntries.forEach((v) {
+ res.record(Metric.fromJson(metric), v['begin'], v['end']);
+ });
+ });
+ } else {
+ res.counters[Metric.fromJson(key)] = value;
+ }
+ }
+ return res;
+ }
+}
+
+/// Simple visitor of the result hierarchy (useful for computing summaries for
+/// quick reports).
+abstract class ResultVisitor {
+ visitGlobal(GlobalResult global);
+ visitBundle(BundleResult group);
+ visitLibrary(LibraryResult library);
+ visitCompilationUnit(CompilationUnitResult unit);
+ visitFunction(FunctionResult functino);
+}
+
+/// Recursive visitor that visits every function starting from the global
+/// results.
+abstract class RecursiveResultVisitor extends ResultVisitor {
+ visitGlobal(GlobalResult global) {
+ global.packages.values.forEach(visitBundle);
+ visitBundle(global.system);
+ visitBundle(global.loose);
+ }
+
+ visitBundle(BundleResult group) {
+ group.libraries.forEach(visitLibrary);
+ }
+
+ visitLibrary(LibraryResult library) {
+ library.units.forEach(visitCompilationUnit);
+ }
+
+ visitCompilationUnit(CompilationUnitResult unit) {
+ unit.functions.forEach(visitFunction);
+ }
+}
+
+/// Color-highighted string used mainly to debug invariants.
Johnni Winther 2015/07/13 19:21:44 'highighted' -> 'highlighted'
+String recursiveDiagnosticString(Measurements measurements, Metric metric) {
+ var sb = new StringBuffer();
+ helper(Metric m) {
+ int value = measurements.counters[m];
+ if (value == null) value = 0;
+ if (m is! GroupedMetric) {
+ sb.write(value);
+ sb.write(' ${m.name}');
+ return;
+ }
+ GroupedMetric group = m;
+
+ int expected = 0;
+ for (var sub in group.submetrics) {
+ var n = measurements.counters[sub];
+ if (n != null) expected += n;
+ }
+ if (value == expected) {
+ sb.write('');
Johnni Winther 2015/07/13 19:21:44 Note that colors are not supported on Windows by d
+ sb.write(value);
+ } else {
+ sb.write('');
+ sb.write(value);
+ sb.write('[');
+ sb.write(expected);
+ sb.write(']');
+ }
+ sb.write('');
+ sb.write(' ${group.name}');
+
+ bool first = true;
+ sb.write('(');
+ for (var sub in group.submetrics) {
+ if (first) {
+ first = false;
+ } else {
+ sb.write(' + ');
+ }
+ helper(sub);
+ }
+ sb.write(')');
+ }
+ helper(metric);
+ return sb.toString();
+}

Powered by Google App Engine
This is Rietveld 408576698