Chromium Code Reviews| OLD | NEW |
|---|---|
| (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('[32m'); | |
|
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('[31m'); | |
| 442 sb.write(value); | |
| 443 sb.write('[33m['); | |
| 444 sb.write(expected); | |
| 445 sb.write(']'); | |
| 446 } | |
| 447 sb.write('[0m'); | |
| 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 } | |
| OLD | NEW |