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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/dump_info.dart

Issue 90713003: Dart2js option to dump info about compilation (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013, 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 library dump_info;
6
7 import 'elements/elements.dart';
8 import 'elements/visitor.dart';
9 import 'dart:convert' show HtmlEscape;
10 import 'dart2jslib.dart' show
11 Compiler,
12 CompilerTask,
13 CodeBuffer;
14 import 'dart_types.dart' show DartType;
15 import 'types/types.dart' show TypeMask;
16
17 // TODO (sigurdm): A search function.
18 // TODO (sigurdm): Output size of classes.
19 // TODO (sigurdm): Print that we dumped the HTML-file.
20 // TODO (sigurdm): Include why a given element was included in the output.
21 // TODO (sigurdm): Include how much output grew because of mirror support.
22 // TODO (sigurdm): Write each function with parameter names.
23 // TODO (sigurdm): Write how much space the boilerplate takes.
24 // TODO (sigurdm): Include javascript names of entities in the output.
25
26 class CodeSizeCounter {
27 final Map<Element, int> generatedSize = new Map<Element, int>();
28
29 int getGeneratedSizeOf(Element element) {
30 int result = generatedSize[element];
31 return result == null ? 0 : result;
32 }
33
34 void countCode(Element element, int added) {
35 int before = generatedSize.putIfAbsent(element, () => 0);
36 generatedSize[element] = before + added;
37 }
38 }
39
40 tag(String element) {
41 return (String content, {String cls}) {
42 String classString = cls == null ? '' : ' class="$cls"';
43 return '<$element$classString>$content</$element>';
44 };
45 }
46
47 var div = tag('div');
48 var span = tag('span');
49 var code = tag('code');
50 var h2 = tag('h2');
51
52 var esc = const HtmlEscape().convert;
53
54 String sizeDescription(int size, ProgramInfo programInfo) {
55 return size == null
56 ? ''
57 : span('${size} bytes '
58 '(${size * 100 ~/ programInfo.size}%)', cls: "size");
59 }
60
61 /// An [InfoNode] holds information about a part the program.
62 abstract class InfoNode {
63 String get name;
64
65 int get size;
66
67 void emitHtml(ProgramInfo programInfo, StringSink buffer);
68 }
69
70 /// An [ElementNode] holds information about an [Element]
71 class ElementInfoNode implements InfoNode {
72 /// The name of the represented [Element].
73 final String name;
74
75 /// The kind of the [Element] represented. This is presented to the
76 /// user, so it might be more specific than [element.kind].
77 final String kind;
78
79 /// The static type of the represented [Element].
80 /// [null] if this kind of element has no type.
81 final String type;
82
83 /// Any extra information to display about the represented [Element].
84 final String extra;
85
86 /// A textual description of the modifiers (such as "static", "abstract") of
87 /// the represented [Element].
88 final String modifiers;
89
90 /// Describes how many bytes the code for the represented [Element] takes up
91 /// in the output.
92 final int size;
93
94 /// Subnodes containing more detailed information about the represented
95 /// [Element], and its members.
96 List<InfoNode> contents;
97
98 ElementInfoNode({this.name: "",
99 this.kind: "",
100 this.type,
101 this.modifiers: "",
102 this.size,
103 this.contents,
104 this.extra: ""});
105
106 void emitHtml(ProgramInfo programInfo, StringSink buffer) {
107 String kindString = span(esc(kind), cls: 'kind');
108 String modifiersString = span(esc(modifiers), cls: "modifiers");
109
110 String nameString = span(esc(name), cls: 'name');
111 String typeString = type == null
112 ? ''
113 : span('/* ' + esc(type) + ' */', cls: 'type');
114 String extraString = span(esc(extra), cls: 'type');
115 String describe = [
116 kindString,
117 typeString,
118 modifiersString,
119 nameString,
120 sizeDescription(size, programInfo),
121 extraString].join(' ');
122
123 if (contents != null) {
124 buffer.write(div("+$describe", cls: "container"));
125 buffer.write('<div class="contained">');
126 if (contents.isEmpty) {
127 buffer.writeln("No members");
128 }
129 for (InfoNode subElementDescription in contents) {
130 subElementDescription.emitHtml(programInfo, buffer);
131 }
132 buffer.write("</div>");
133 } else {
134 buffer.writeln(describe);
135 }
136 }
137 }
138
139 /// A [CodeInfoNode] holds information about a piece of code.
140 class CodeInfoNode implements InfoNode {
141 /// A short description of the code.
142 final String description;
143
144 final String generatedCode;
145
146 get size => generatedCode.length;
147
148 get name => "";
149
150 CodeInfoNode({this.description: "", this.generatedCode});
151
152 void emitHtml(ProgramInfo programInfo, StringBuffer buffer) {
153 buffer.write(div(description + ' ' +
154 sizeDescription(generatedCode.length, programInfo),
155 cls: 'kind') +
156 code(esc(generatedCode)));
157 }
158 }
159
160 /// Instances represent information inferred about the program such as
161 /// inferred type information or inferred side effects.
162 class InferredInfoNode implements InfoNode {
163 /// Text describing the represented information.
164 final String description;
165
166 /// The name of the entity this information is inferred about (for example the
167 /// name of a parameter).
168 final String name;
169
170 /// The inferred type/side effect.
171 final String type;
172
173 get size => 0;
174
175 InferredInfoNode({this.name: "", this.description, this.type});
176
177 void emitHtml(ProgramInfo programInfo, StringSink buffer) {
178 buffer.write(div('${span("Inferred " + description, cls: "kind")} '
179 '${span(esc(name),
180 cls: "name")} '
181 '${span(esc(type), cls: 'type')} '));
182 }
183 }
184
185 /// Instances represent information about a program.
186 class ProgramInfo {
187 /// A list of all the libraries in the program to show information about.
188 final List<InfoNode> libraries;
189
190 /// The size of the whole program in bytes.
191 final int size;
192
193 /// The time the compilation took place.
194 final DateTime compilationMoment;
195
196 /// The time the compilation took to complite.
floitsch 2013/12/11 13:04:19 complete
sigurdm 2013/12/11 14:01:08 Done.
197 final int compilationDuration;
198
199 /// The version of dart2js used to compile the program.
200 final String dart2jsVersion;
201
202 ProgramInfo({this.libraries,
203 this.size,
204 this.compilationMoment,
205 this.compilationDuration,
206 this.dart2jsVersion});
207 }
208
209 class InfoDumpVisitor extends ElementVisitor<InfoNode> {
210 final Compiler compiler;
211
212 /// Contains the elements visited on the path from the library to here.
213 List<Element> stack = new List<Element>();
214
215 Element get currentElement => stack.last;
216
217 InfoDumpVisitor(Compiler this.compiler);
218
219 InfoNode visitElement(Element element) {
220 compiler.internalError("This element of kind ${element.kind} "
221 "does not support --dump-info",
222 token: element.position());
223 }
224
225 InfoNode visitLibraryElement(LibraryElement element) {
226 List<InfoNode> contents = new List<InfoNode>();
227 int size = compiler.dumpInfoTask.codeSizeCounter
228 .getGeneratedSizeOf(element);
229 if (size == 0) return null;
230 stack.add(element);
231 element.forEachLocalMember((Element member) {
232 InfoNode info = member.accept(this);
233 if (info != null) {
234 contents.add(info);
235 }
236 });
237 stack.removeLast();
238 String nameString = element.getLibraryName() == ""
239 ? "<unnamed>"
240 : element.getLibraryName();
241 contents.sort((InfoNode e1, InfoNode e2) {
242 return e1.name.compareTo(e2.name);
243 });
244 return new ElementInfoNode(
245 extra: "${element.canonicalUri}",
246 kind: "library",
247 name: nameString,
248 size: size,
249 modifiers: "",
250 contents: contents);
251 }
252
253 InfoNode visitTypedefElement(TypedefElement element) {
254 return element.alias == null
255 ? null
256 : new ElementInfoNode(
257 type: element.alias.toString(),
258 kind: "typedef",
259 name: element.name);
260 }
261
262 InfoNode visitFieldElement(FieldElement element) {
263 CodeBuffer emittedCode = compiler.backend.codeOf(element);
264 int size = 0;
265 DartType type = element.computeType(compiler);
266 TypeMask inferredType = compiler.typesTask
267 .getGuaranteedTypeOfElement(element);
268 // If a field has an empty inferred type it is never used.
269 if ((inferredType == null || inferredType.isEmpty) && emittedCode == null) {
270 return null;
271 }
272 List<InfoNode> contents = new List<InfoNode>();
273 if (emittedCode != null) {
274 contents.add(new CodeInfoNode(
275 description: "Generated initializer",
276 generatedCode: emittedCode.getText()));
277 size = emittedCode.length;
278 }
279 if (inferredType != null) {
280 contents.add(new InferredInfoNode(
281 description: "type",
282 type: inferredType.toString()));
283 stack.add(element);
284 }
285 for (Element closure in element.nestedClosures) {
286 InfoNode info = closure.accept(this);
287 if (info != null) {
288 contents.add(info);
289 size += info.size;
290 }
291 }
292 stack.removeLast();
293
294 return new ElementInfoNode(
295 kind: "field",
296 type: "$type",
297 name: element.name,
298 size: size,
299 modifiers: "${element.modifiers}",
300 contents: contents);
301 }
302
303 InfoNode visitClassElement(ClassElement element) {
304 // If the element is not resolved it is not used in the program, and we omit
305 // it from the output.
306 if (!element.isResolved) return null;
307 String modifiersString = "${element.modifiers}";
308 String supersString = element.allSupertypes == null ? "" :
309 "implements ${element.allSupertypes}";
310 List contents = [];
311 stack.add(element);
312 element.forEachLocalMember((Element member) {
313 InfoNode info = member.accept(this);
314 if (info != null) {
315 contents.add(info);
316 }
317 });
318 stack.removeLast();
319 if (contents.isEmpty) {
320 // TODO (sigurdm): Only return here if the class is never used in type
321 // checks.
322 return null;
323 }
324 contents.sort((InfoNode n1, InfoNode n2) {
325 return n1.name.compareTo(n2.name);
326 });
327 return new ElementInfoNode(
328 kind: "class",
329 name: element.name,
330 extra: supersString,
331 modifiers: modifiersString,
332 contents: contents);
333 }
334
335 InfoNode visitFunctionElement(FunctionElement element) {
336 CodeBuffer emittedCode = compiler.backend.codeOf(element);
337 int size = 0;
338 String nameString = element.name;
339 String modifiersString = "${element.modifiers}";
340 String kindString = "function";
341 if (currentElement.isClass()) {
342 kindString = "method";
343 } else if (currentElement.isField() ||
344 currentElement.isFunction() ||
345 currentElement.isConstructor()) {
346 kindString = "closure";
347 nameString = "<unnamed>";
348 }
349 if (element.isConstructor()) {
350 nameString = element.name == ""
351 ? "${element.enclosingElement.name}"
352 : "${element.enclosingElement.name}.${element.name}";
353 kindString = "constructor";
354 }
355 List contents = [];
356 if (emittedCode != null) {
357 FunctionSignature signature = element.computeSignature(compiler);
358 signature.forEachParameter((parameter) {
359 contents.add(new InferredInfoNode(
360 description: "parameter",
361 name: parameter.name,
362 type: compiler.typesTask
363 .getGuaranteedTypeOfElement(parameter).toString()));
364 });
365 contents.add(new InferredInfoNode(
366 description: "return type",
367 type: compiler.typesTask
368 .getGuaranteedReturnTypeOfElement(element).toString()));
369 contents.add(new InferredInfoNode(
370 description: "side effects",
371 type: compiler.world
372 .getSideEffectsOfElement(element).toString()));
373 contents.add(new CodeInfoNode(
374 description: "Generated code",
375 generatedCode: emittedCode.getText()));
376 size += emittedCode.length;
377 }
378 stack.add(element);
379 for (Element closure in element.nestedClosures) {
380 InfoNode info = closure.accept(this);
381 if (info != null) {
382 contents.add(info);
383 size += info.size;
384 }
385 }
386 stack.removeLast();
387 if (size == 0) {
388 return null;
389 }
390 return new ElementInfoNode(
391 type: element.type.toString(),
392 kind: kindString,
393 name: nameString,
394 size: size,
395 modifiers: modifiersString,
396 contents: contents);
397 }
398 }
399
400 class DumpInfoTask extends CompilerTask {
401 DumpInfoTask(Compiler compiler)
402 : infoDumpVisitor = new InfoDumpVisitor(compiler),
403 super(compiler);
404
405 String name = "Dump Info";
406
407 final CodeSizeCounter codeSizeCounter = new CodeSizeCounter();
408
409 final InfoDumpVisitor infoDumpVisitor;
410
411 void dumpInfo() {
412 measure(() {
413 ProgramInfo info = collectDumpInfo();
414 StringBuffer buffer = new StringBuffer();
415 dumpInfoHtml(info, buffer);
416 compiler.outputProvider('', 'info.html')
417 ..add(buffer.toString())
418 ..close();
419 });
420 }
421
422 ProgramInfo collectDumpInfo() {
423 List<LibraryElement> sortedLibraries = compiler.libraries.values.toList();
424 sortedLibraries.sort((LibraryElement l1, LibraryElement l2) {
425 if (l1.isPlatformLibrary && !l2.isPlatformLibrary) {
426 return 1;
427 } else if (!l1.isPlatformLibrary && l2.isPlatformLibrary) {
428 return -1;
429 }
430 return l1.getLibraryName().compareTo(l2.getLibraryName());
431 });
432
433 List<InfoNode> libraryInfos = new List<InfoNode>();
434 libraryInfos.addAll(sortedLibraries
435 .map((library) => infoDumpVisitor.visit(library))
436 .where((info) => info != null));
437
438 return new ProgramInfo(
439 compilationDuration: compiler.totalCompileTime.elapsedTicks,
440 // TODO (sigurdm): Also count the size of deferred code
441 size: compiler.assembledCode.length,
442 libraries: libraryInfos,
443 compilationMoment: new DateTime.now(),
444 dart2jsVersion: compiler.hasBuildId ? compiler.buildId : null);
445 }
446
447 void dumpInfoHtml(ProgramInfo info, StringSink buffer) {
448 int totalSize = info.size;
449
450 buffer.writeln("""
451 <html>
452 <head>
453 <title>Dart2JS compilation information</title>
454 <style>
455 code {margin-left: 20px; display: block;}
456 div.contained {margin-left: 20px;}
457 div {margin-top:0px;
458 margin-bottom: 0px;
459 white-space: pre; /*border: 1px solid;*/}
460 span.kind {}
461 span.modifiers {font-weight:bold;}
462 span.name {font-weight:bold; font-family: monospace;}
463 span.type {font-family: monospace; color:blue;}
464 </style>
465 </head>
466 <body>
467 <h1>Dart2js compilation information</h1>""");
468 buffer.writeln(h2('Compilation took place: '
469 '${info.compilationMoment}'));
470 buffer.writeln(h2('Compilation took: '
471 '${info.compilationDuration/1000000} seconds'));
floitsch 2013/12/11 13:04:19 the compilationDuration is stored in ticks. A tick
sigurdm 2013/12/11 14:01:08 Good idea
472 buffer.writeln(h2('Output size: ${info.size} bytes'));
473 if (info.dart2jsVersion != null) {
474 buffer.writeln(h2('Dart2js version: ${info.dart2jsVersion}'));
475 }
476
477 info.libraries.forEach((InfoNode node) {
478 node.emitHtml(info, buffer);
479 });
480
481
482 // TODO (sigurdm): This script should be written in dart
483 buffer.writeln(r"""
484 <script type="text/javascript">
485 function toggler(element) {
486 return function(e) {
487 element.hidden = !element.hidden;
488 };
489 }
490 var containers = document.getElementsByClassName('container');
491 for (var i = 0; i < containers.length; i++) {
492 var container = containers[i];
493 container.addEventListener('click',
494 toggler(container.nextElementSibling), false);
495 container.nextElementSibling.hidden = true;
496 };
497 </script>
498 </body>
499 </html>""");
500 }
501 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698