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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /// Collects information used to debug and analyze internal parts of the
6 /// compiler.
7 ///
8 /// Currently this is focused mainly on data from types and inference, such as
9 /// understanding of types of expressions and precision of send operations.
10 ///
11 /// This library focuses on representing the information itself.
12 /// `stats_builder.dart` contains visitors we use to collect the data, while
13 /// `tools/stats/server.dart` contains logic to visualize the data.
14 library stats;
15
16 /// All results from a single run of the compiler on an application.
17 class GlobalResult {
18 /// Results grouped by package.
19 final Map<String, BundleResult> packages = {};
20
21 /// Results for loose files, typically the entrypoint and files loaded via
22 /// relative imports.
23 final BundleResult loose = new BundleResult('*loose*');
24
25 /// Results from system libraries (dart:core, dart:async, etc).
26 final BundleResult system = new BundleResult('*system*');
27
28 /// Add the result of a library in its corresponding group.
29 void add(LibraryResult library) {
30 if (library.uri.scheme == 'package') {
31 var name = library.uri.pathSegments[0];
32 var package = packages.putIfAbsent(name, () => new BundleResult(name));
33 package.libraries.add(library);
34 } else if (library.uri.scheme == 'dart') {
35 system.libraries.add(library);
36 } else {
37 loose.libraries.add(library);
38 }
39 }
40
41 // TODO(sigmund): consider splitting the serialization in multiple files so
42 // that not all the information has to be stored in memory at once.
43 List toJson() => []
44 ..addAll(packages.values.expand((p) => p.libraries.map((l) => l.toJson())))
45 ..addAll(loose.libraries.map((l) => l.toJson()))
46 ..addAll(system.libraries.map((l) => l.toJson()));
47
48 static GlobalResult fromJson(List json) {
49 var res = new GlobalResult();
50 json.map(LibraryResult.fromJson).forEach(res.add);
51 return res;
52 }
53
54 accept(ResultVisitor v) => v.visitGlobal(this);
55 }
56
57 /// Summarizes results for a group of libraries. Used by [GlobalResult] to group
58 /// 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)
59 /// package.
60 class BundleResult {
61 /// Name of the group.
62 final String name;
63
64 /// Library results that are part of this group.
65 final List<LibraryResult> libraries = [];
66
67 BundleResult(this.name);
68 }
69
70 /// Aggregate result for all units in a library.
71 class LibraryResult {
72 final Uri uri;
73 final List<CompilationUnitResults> units = [];
74 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
75 LibraryResult(this.uri);
76 accept(ResultVisitor v) => v.visitLibrary(this);
77
78 Map toJson() => {
79 'uri': '$uri',
80 'units': units.map((p) => p.toJson()).toList(),
81 };
82
83 static LibraryResult fromJson(Map json) =>
84 new LibraryResult(Uri.parse(json['uri']))
85 ..units.addAll(json['units'].map(CompilationUnitResult.fromJson));
86 }
87
88 /// Results of a compilation unit (library or part).
89 class CompilationUnitResult {
90 /// Resolved Uri for this unit.
91 final Uri uri;
92
93 /// Results per function and method in this library
94 final List<FunctionResult> functions = [];
95
96 accept(ResultVisitor v) => v.visitUnit(this);
97
98 CompilationUnitResult(this.uri);
99
100 Map toJson() => {
101 'uri': '$uri',
102 'functions': functions.map((f) => f.toJson()).toList(),
103 };
104
105 static CompilationUnitResult fromJson(Map json) =>
106 new CompilationUnitResult(Uri.parse(json['uri']))
107 ..functions.addAll(json['functions'].map(FunctionResult.fromJson));
108 }
109
110 /// Results on a function.
111 class FunctionResult {
112 /// Name, if-any, of the function.
113 final String name;
114
115 /// Measurements collected.
116 final Measurements measurements;
117
118 FunctionResult(this.name, this.measurements);
119 accept(ResultVisitor v) => v.visitFunction(this);
120
121 Map toJson() => {
122 'name' : name,
123 'measurements': measurements.toJson(),
124 };
125
126 static FunctionResult fromJson(Map json) =>
127 new FunctionResult(json['name'],
128 Measurements.fromJson(json['measurements']));
129 }
130
131 /// Top-level set of metrics
132 const List<Metric> _topLevelMetrics = const [
133 Metric.functions,
134 Metric.send,
135 ];
136
137 /// Apply `f` on each metric in DFS order on the metric "tree".
138 visitAllMetrics(f) {
139 var parentsStack = [];
140 helper(Metric m) {
141 f(m, parentsStack);
142 if (m is GroupedMetric) {
143 parentsStack.add(m);
144 m.submetrics.forEach(helper);
145 parentsStack.removeLast();
146 }
147 }
148 _topLevelMetrics.forEach(helper);
149 }
150
151 /// A metric we intend to measure.
152 class Metric {
153 /// Name for the metric.
154 final String name;
155
156 const Metric(this.name);
157
158 String toString() => name;
159
160 /// Total functions in a library/package/program.
161 static const Metric functions = const GroupedMetric('functions', const [
162 reachableFunctions,
163 ]);
164
165 /// Subset of the functions that are reachable.
166 static const Metric reachableFunctions = const Metric('reachable functions');
167
168 /// Parent of all send metrics. We classify sends as follows:
169 ///
170 /// sends
171 /// |- monomorphic
172 /// | |- static (top-levels, statics)
173 /// | |- super
174 /// | |- local (access to a local var, call local function)
175 /// | |- constructor (like factory ctros)
176 /// | |- type variable (reading a type variable)
177 /// | |- nsm (known no such method exception)
178 /// | |- single-nsm-call (known no such method call, single target)
179 /// | |- instance (non-interceptor, only one possible target)
180 /// | '- interceptor (interceptor, known)
181 /// |
182 /// '- polymorphic
183 /// |- multi-nsm (known to be nSM, but not sure if error, or call, or
184 /// which call)
185 /// |- virtual (traditional virtual call, polymorphic equivalent of
186 /// | `instance`, no-interceptor)
187 /// |- multi-interceptor (1 of n possible interceptors)
188 /// '- dynamic (any combination of the above)
189 ///
190 static const Metric send = const GroupedMetric('send', const [
191 monomorphicSend,
192 polymorphicSend,
193 ]);
194
195 /// Parent of monomorphic sends, see [send] for details.
196 static const Metric monomorphicSend = const GroupedMetric('monomorphic',
197 const [
198 staticSend,
199 superSend,
200 localSend,
201 constructorSend,
202 typeVariableSend,
203 nsmErrorSend,
204 singleNsmCallSend,
205 instanceSend,
206 interceptorSend,
207 ]);
208
209 /// Metric for static calls, see [send] for details.
210 static const Metric staticSend = const Metric('static');
211
212 /// Metric for super calls, see [send] for details.
213 static const Metric superSend = const Metric('super');
214
215 /// Metric for local variable sends, see [send] for details.
216 static const Metric localSend = const Metric('local');
217
218 /// Metric for constructor sends, see [send] for details.
219 static const Metric constructorSend = const Metric('constructor');
220
221 /// Metric for type-variable sends, see [send] for details.
222 // TODO(sigmund): delete? is mainly associated with compile-time errors
223 static const Metric typeVariableSend = const Metric('type variable');
224
225 /// Metric for no-such-method errors, see [send] for details.
226 static const Metric nsmErrorSend = const Metric('nSM error');
227
228 /// Metric for calls to noSuchMethod methods with a known target, see [send]
229 /// for details.
230 static const Metric singleNsmCallSend = const Metric('nSM call single');
231
232 /// Metric for calls to a precisely known instance method, see [send] for
233 /// details.
234 static const Metric instanceSend = const Metric('instance');
235
236 /// Metric for calls to a precisely known interceptor method, see [send] for
237 /// details.
238 static const Metric interceptorSend = const Metric('interceptor');
239
240 /// Parent of polymorphic sends, see [send] for details.
241 static const Metric polymorphicSend = const GroupedMetric('polymorphic',
242 const [
243 multiNsmCallSend,
244 virtualSend,
245 multiInterceptorSend,
246 dynamicSend,
247 ]);
248
249 /// Metric for calls to noSuchMethod methods with more than one possible
250 /// target, see [send] for details.
251 static const Metric multiNsmCallSend = const Metric('nSM call multi');
252
253 /// Metric for calls that are dispatched virtually ar runtime, see [send] for
254 /// details.
255 static const Metric virtualSend = const Metric('virtual');
256
257 /// Metyric for calls to more than one possible interceptor, see [send] for
258 /// details.
259 static const Metric multiInterceptorSend = const Metric('interceptor multi');
260
261 /// Metyric for dynamic calls for which we know nothing about the target
262 /// method. See [send] for details.
263 static const Metric dynamicSend = const Metric('dynamic');
264
265 String toJson() => name;
266 static Map<String, Metric> _nameToMetricMap = () {
267 var res = {};
268 visitAllMetrics((m, _) => res[m.name] = m);
269 return res;
270 }();
271
272 static Metric fromJson(String name) => _nameToMetricMap[name];
273 }
274
275 /// A metric that is subdivided in smaller metrics.
276 class GroupedMetric extends Metric {
277 final List<Metric> submetrics;
278
279 const GroupedMetric(String name, this.submetrics) : super(name);
280 }
281
282 /// A measurement entry (practically a source-span location where the
283 /// measurement was seen).
284 class Entry {
285 final int begin;
286 final int end;
287 Entry(this.begin, this.end);
288 }
289
290 /// A collection of data points for each metric. Used to summarize a single
291 /// 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.
292 class Measurements {
293 final Map<Metric, List<Entry>> entries;
294 final Map<Metric, int> counters;
295
296 Measurements()
297 : entries = <Metric, List<Entry>>{},
298 counters = <Metric, int>{};
299
300 const Measurements.unreachableFunction()
301 : counters = const { Metric.functions: 1}, entries = const {};
302
303 Measurements.reachableFunction()
304 : counters = { Metric.functions: 1, Metric.reachableFunctions: 1},
305 entries = {};
306
307 /// Record [metric] was seen. The optional [begin] and [end] offsets are
308 /// included for metrics that correspond to a source range. Intended to be
309 /// used by `StatsBuilder`.
310 record(Metric metric, [int begin, int end]) {
311 if (begin != null && end != null) {
312 entries.putIfAbsent(metric, () => []).add(new Entry(begin, end));
313 }
314 counters.putIfAbsent(metric, () => 0);
315 counters[metric]++;
316 }
317
318 /// Removes a previously added entry. Intended only to be used by
319 /// `StatsBuilder`. Internally `StatsBuilder` computes redundant information
320 /// in order to check for coverage and validate invariants with
321 /// [checkInvariant]. This is used to adjust some of the redundant
322 /// information.
323 popLast(Metric metric) {
324 assert(entries[metric] != null && entries[metric].isNotEmpty);
325 entries[metric].removeLast();
326 counters[metric]--;
327 }
328
329 /// Add the counters from [other] into this set of measurements.
330 addFrom(Measurements other) {
331 other.counters.forEach((metric, value) {
332 var current = counters[metric];
333 counters[metric] = current == null ? value : current + value;
334 });
335 }
336
337 /// Check that every grouped metric totals the individual counts of it's
338 /// submetric.
339 bool checkInvariant(GroupedMetric key) {
340 // TODO(sigmund): use ?? operator.
341 int total = counters[key];
342 if (total == null) total = 0;
343 int submetricTotal = 0;
344 for (var metric in key.submetrics) {
345 var n = counters[metric];
346 if (n != null) submetricTotal += n;
347 }
348 return total == submetricTotal;
349 }
350
351 Map toJson() {
352 var jsonEntries = <String, List<Map>>{};
353 entries.forEach((metric, values) {
354 jsonEntries[metric.toJson()] =
355 values.map((e) => {'begin': e.begin, 'end': e.end}).toList();
356 });
357 var json = {'entries': jsonEntries};
358 if (counters[Metric.functions] != null) {
359 json[Metric.functions.toJson()] = counters[Metric.functions];
360 }
361 if (counters[Metric.reachableFunctions] != null) {
362 json[Metric.reachableFunctions.toJson()] =
363 counters[Metric.reachableFunctions];
364 }
365 return json;
366 }
367
368 static Measurements fromJson(Map json) {
369 var res = new Measurements();
370 for (var key in json.keys) {
371 var value = json[key];
372 if (value == null) continue;
373 if (key == 'entries') {
374 value.forEach((metric, jsonEntries) {
375 jsonEntries.forEach((v) {
376 res.record(Metric.fromJson(metric), v['begin'], v['end']);
377 });
378 });
379 } else {
380 res.counters[Metric.fromJson(key)] = value;
381 }
382 }
383 return res;
384 }
385 }
386
387 /// Simple visitor of the result hierarchy (useful for computing summaries for
388 /// quick reports).
389 abstract class ResultVisitor {
390 visitGlobal(GlobalResult global);
391 visitBundle(BundleResult group);
392 visitLibrary(LibraryResult library);
393 visitCompilationUnit(CompilationUnitResult unit);
394 visitFunction(FunctionResult functino);
395 }
396
397 /// Recursive visitor that visits every function starting from the global
398 /// results.
399 abstract class RecursiveResultVisitor extends ResultVisitor {
400 visitGlobal(GlobalResult global) {
401 global.packages.values.forEach(visitBundle);
402 visitBundle(global.system);
403 visitBundle(global.loose);
404 }
405
406 visitBundle(BundleResult group) {
407 group.libraries.forEach(visitLibrary);
408 }
409
410 visitLibrary(LibraryResult library) {
411 library.units.forEach(visitCompilationUnit);
412 }
413
414 visitCompilationUnit(CompilationUnitResult unit) {
415 unit.functions.forEach(visitFunction);
416 }
417 }
418
419 /// Color-highighted string used mainly to debug invariants.
Johnni Winther 2015/07/13 19:21:44 'highighted' -> 'highlighted'
420 String recursiveDiagnosticString(Measurements measurements, Metric metric) {
421 var sb = new StringBuffer();
422 helper(Metric m) {
423 int value = measurements.counters[m];
424 if (value == null) value = 0;
425 if (m is! GroupedMetric) {
426 sb.write(value);
427 sb.write(' ${m.name}');
428 return;
429 }
430 GroupedMetric group = m;
431
432 int expected = 0;
433 for (var sub in group.submetrics) {
434 var n = measurements.counters[sub];
435 if (n != null) expected += n;
436 }
437 if (value == expected) {
438 sb.write('');
Johnni Winther 2015/07/13 19:21:44 Note that colors are not supported on Windows by d
439 sb.write(value);
440 } else {
441 sb.write('');
442 sb.write(value);
443 sb.write('[');
444 sb.write(expected);
445 sb.write(']');
446 }
447 sb.write('');
448 sb.write(' ${group.name}');
449
450 bool first = true;
451 sb.write('(');
452 for (var sub in group.submetrics) {
453 if (first) {
454 first = false;
455 } else {
456 sb.write(' + ');
457 }
458 helper(sub);
459 }
460 sb.write(')');
461 }
462 helper(metric);
463 return sb.toString();
464 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698