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

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 /// Instances represent a piece of information about a part the program.
Johnni Winther 2013/12/10 12:05:19 `Instances represent` -> `An [InfoNode] holds` ?
sigurdm 2013/12/10 12:40:25 Done.
62 abstract class InfoNode {
63 String get name;
64
65 int get size;
66
67 void emitHtml(ProgramInfo programInfo, StringSink buffer);
68 }
69
70 /// Instances represent information about an [Element]
Johnni Winther 2013/12/10 12:05:19 `Instance represent` -> `An [ElementInfoNode] hold
sigurdm 2013/12/10 12:40:25 Done.
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 for presenting to the
Johnni Winther 2013/12/10 12:05:19 `This is for presenting` -> `This is presented` ?
sigurdm 2013/12/10 12:40:25 Done.
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 about its members.
Johnni Winther 2013/12/10 12:05:19 `, and about` -> `and`
sigurdm 2013/12/10 12:40:25 Done.
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 /// Instances represent information about a piece of code.
Johnni Winther 2013/12/10 12:05:19 An [CodeInfoNode] holds ....
sigurdm 2013/12/10 12:40:25 Done.
140 class CodeInfoNode implements InfoNode {
141 final String description;
Johnni Winther 2013/12/10 12:05:19 Comment here?
sigurdm 2013/12/10 12:40:25 Done.
142
143 final String generatedCode;
144
145 get size => generatedCode.length;
146
147 get name => "";
148
149 CodeInfoNode({this.description: "", this.generatedCode});
150
151 void emitHtml(ProgramInfo programInfo, StringBuffer buffer) {
152 buffer.write(div(description + ' ' +
153 sizeDescription(generatedCode.length, programInfo),
154 cls: 'kind') +
155 code(esc(generatedCode)));
156 }
157 }
158
159 /// Instances represent information inferred about an [Element] such as a type
Johnni Winther 2013/12/10 12:05:19 `type` -> `type information`
sigurdm 2013/12/10 12:40:25 Done.
160 /// or description of side effects.
161 class InferredInfoNode implements InfoNode {
162 /// Text describing the represented information.
163 final String description;
164
165 /// The name of the entity this information is inferred about (for example the
166 /// name of a parameter).
167 final String name;
168
169 /// The inferred type/side effect.
170 final String type;
171
172 get size => 0;
173
174 InferredInfoNode({this.name: "", this.description, this.type});
175
176 void emitHtml(ProgramInfo programInfo, StringSink buffer) {
177 buffer.write(div('${span("Inferred " + description, cls: "kind")} '
178 '${span(esc(name),
179 cls: "name")} '
180 '${span(esc(type), cls: 'type')} '));
181 }
182 }
183
184 /// Instances represent information about a program.
185 class ProgramInfo {
186 /// A list of all the libraries in the program to show information about.
187 final List<InfoNode> libraries;
188
189 /// How many bytes does the whole program take up.
Johnni Winther 2013/12/10 12:05:19 Maybe `The size of the whole program in bytes.` to
sigurdm 2013/12/10 12:40:25 Done.
190 final int size;
191
192 /// When did the compilation take place.
193 final DateTime compilationMoment;
194
195 /// How long time did the compilation take.
196 final int compilationDuration;
197
198 /// What version of dart2js was used to compile the program.
199 final String dart2jsVersion;
200
201 ProgramInfo({this.libraries,
202 this.size,
203 this.compilationMoment,
204 this.compilationDuration,
205 this.dart2jsVersion});
206 }
207
208 class InfoDumpVisitor extends ElementVisitor<InfoNode> {
209 final Compiler compiler;
210
211 /// Contains the elements visited on the path from the library to here.
212 List<Element> stack = new List<Element>();
213
214 Element get currentElement => stack.last;
215
216 InfoDumpVisitor(Compiler this.compiler);
217
218 InfoNode visitElement(Element element) {
219 compiler.internalError("This element of kind ${element.kind} "
220 "does not support --dump-info",
221 token: element.position());
222 }
223
224 InfoNode visitLibraryElement(LibraryElement element) {
225 List<InfoNode> contents = new List<InfoNode>();
226 int size = compiler.dumpInfoTask.codeSizeCounter
227 .getGeneratedSizeOf(element);
228 if (size == 0) return null;
229 stack.add(element);
230 element.forEachLocalMember((Element member) {
231 InfoNode info = member.accept(this);
232 if (info != null) {
233 contents.add(info);
234 }
235 });
236 stack.removeLast();
237 String nameString = element.getLibraryName() == ""
238 ? "<unnamed>"
239 : element.getLibraryName();
240 contents.sort((InfoNode e1, InfoNode e2) {
241 return e1.name.compareTo(e2.name);
242 });
243 return new ElementInfoNode(
244 extra: "${element.canonicalUri}",
245 kind: "library",
246 name: nameString,
247 size: size,
248 modifiers: "",
249 contents: contents);
250 }
251
252 InfoNode visitTypedefElement(TypedefElement element) {
253 return element.alias == null
254 ? null
255 : new ElementInfoNode(
256 type: element.alias.toString(),
257 kind: "typedef",
258 name: element.name);
259 }
260
261 InfoNode visitFieldElement(FieldElement element) {
262 CodeBuffer emittedCode = compiler.backend.codeOf(element);
263 int size = 0;
264 DartType type = element.computeType(compiler);
265 TypeMask inferredType = compiler.typesTask
266 .getGuaranteedTypeOfElement(element);
267 if (inferredType == null && emittedCode == null) return null;
268 List<InfoNode> contents = new List<InfoNode>();
269 if (emittedCode != null) {
270 contents.add(new CodeInfoNode(
271 description: "Generated initializer",
272 generatedCode: emittedCode.getText()));
273 size = emittedCode.length;
274 }
275 if (inferredType != null) {
276 contents.add(new InferredInfoNode(
277 description: "type",
278 type: inferredType.toString()));
279 stack.add(element);
280 }
281 for (Element closure in element.nestedClosures) {
282 InfoNode info = closure.accept(this);
283 if (info != null) {
284 contents.add(info);
285 size += info.size;
286 }
287 }
288 stack.removeLast();
289
290 return new ElementInfoNode(
291 kind: "field",
292 type: "$type",
293 name: element.name,
294 size: size,
295 modifiers: "${element.modifiers}",
296 contents: contents);
297 }
298
299 InfoNode visitClassElement(ClassElement element) {
300 // If the element is not resolved it is not used in the program, and we omit
301 // it from the output.
302 if (!element.isResolved) return null;
303 String modifiersString = "${element.modifiers}";
304 String supersString = element.allSupertypes == null ? "" :
305 "implements ${element.allSupertypes}";
306 List contents = [];
307 stack.add(element);
308 element.forEachLocalMember((Element member) {
309 InfoNode info = member.accept(this);
310 if (info != null) {
311 contents.add(info);
312 }
313 });
314 stack.removeLast();
315 if (contents.isEmpty) {
316 // TODO (sigurdm): Only return here if the class is never used in type
317 // checks.
318 return null;
319 }
320 contents.sort((InfoNode n1, InfoNode n2) {
321 return n1.name.compareTo(n2.name);
322 });
323 return new ElementInfoNode(
324 kind: "class",
325 name: element.name,
326 extra: supersString,
327 modifiers: modifiersString,
328 contents: contents);
329 }
330
331 InfoNode visitFunctionElement(FunctionElement element) {
332 CodeBuffer emittedCode = compiler.backend.codeOf(element);
333 int size = 0;
334 String nameString = element.name;
335 String modifiersString = "${element.modifiers}";
336 String kindString = "function";
337 if (currentElement.isClass()) {
338 kindString = "method";
339 } else if (currentElement.isField() ||
340 currentElement.isFunction() ||
341 currentElement.isConstructor()) {
342 kindString = "closure";
343 nameString = "<unnamed>";
344 }
345 if (element.isConstructor()) {
346 nameString = element.name == ""
347 ? "${element.enclosingElement.name}"
348 : "${element.enclosingElement.name}.${element.name}";
349 kindString = "constructor";
350 }
351 List contents = [];
352 if (emittedCode != null) {
353 FunctionSignature signature = element.computeSignature(compiler);
354 signature.forEachParameter((parameter) {
355 contents.add(new InferredInfoNode(
356 description: "parameter",
357 name: parameter.name,
358 type: compiler.typesTask
359 .getGuaranteedTypeOfElement(parameter).toString()));
360 });
361 contents.add(new InferredInfoNode(
362 description: "return type",
363 type: compiler.typesTask
364 .getGuaranteedReturnTypeOfElement(element).toString()));
365 contents.add(new InferredInfoNode(
366 description: "side effects",
367 type: compiler.world
368 .getSideEffectsOfElement(element).toString()));
369 contents.add(new CodeInfoNode(
370 description: "Generated code",
371 generatedCode: emittedCode.getText()));
372 size += emittedCode.length;
373 }
374 stack.add(element);
375 for (Element closure in element.nestedClosures) {
376 InfoNode info = closure.accept(this);
377 if (info != null) {
378 contents.add(info);
379 size += info.size;
380 }
381 }
382 stack.removeLast();
383 if (size == 0) {
384 return null;
385 }
386 return new ElementInfoNode(
387 type: element.type.toString(),
388 kind: kindString,
389 name: nameString,
390 size: size,
391 modifiers: modifiersString,
392 contents: contents);
393 }
394 }
395
396 class DumpInfoTask extends CompilerTask {
397 DumpInfoTask(Compiler compiler)
398 : infoDumpVisitor = new InfoDumpVisitor(compiler),
399 super(compiler);
400
401 String name = "Dump Info";
402
403 final CodeSizeCounter codeSizeCounter = new CodeSizeCounter();
404
405 final InfoDumpVisitor infoDumpVisitor;
406
407 void dumpInfo() {
408 measure(() {
409 ProgramInfo info = collectDumpInfo();
410 StringBuffer buffer = new StringBuffer();
411 dumpInfoHtml(info, buffer);
412 compiler.outputProvider('', 'info.html')
413 ..add(buffer.toString())
414 ..close();
415 });
416 }
417
418 ProgramInfo collectDumpInfo() {
419 List<LibraryElement> sortedLibraries = compiler.libraries.values.toList();
420 sortedLibraries.sort((LibraryElement l1, LibraryElement l2) {
421 if (l1.isPlatformLibrary && !l2.isPlatformLibrary) {
422 return 1;
423 } else if (!l1.isPlatformLibrary && l2.isPlatformLibrary) {
424 return -1;
425 }
426 return l1.getLibraryName().compareTo(l2.getLibraryName());
427 });
428
429 List<InfoNode> libraryInfos = new List<InfoNode>();
430 libraryInfos.addAll(sortedLibraries
431 .map((library) => infoDumpVisitor.visit(library))
432 .where((info) => info != null));
433
434 return new ProgramInfo(
435 compilationDuration: compiler.totalCompileTime.elapsedTicks,
436 // TODO (sigurdm): Also count the size of deferred code
437 size: compiler.assembledCode.length,
438 libraries: libraryInfos,
439 compilationMoment: new DateTime.now(),
440 dart2jsVersion: compiler.hasBuildId ? compiler.buildId : null);
441 }
442
443 void dumpInfoHtml(ProgramInfo info, StringSink buffer) {
444 int totalSize = info.size;
445
446 buffer.writeln("""
447 <html>
448 <head>
449 <title>Dart2JS compilation information</title>
450 <style>
451 code {margin-left: 20px; display: block;}
452 div.contained {margin-left: 20px;}
453 div {margin-top:0px;
454 margin-bottom: 0px;
455 white-space: pre; /*border: 1px solid;*/}
456 span.kind {}
457 span.modifiers {font-weight:bold;}
458 span.name {font-weight:bold; font-family: monospace;}
459 span.type {font-family: monospace; color:blue;}
460 </style>
461 </head>
462 <body>
463 <h1>Dart2js compilation information</h1>""");
464 buffer.writeln(h2('Compilation took place: '
465 '${info.compilationMoment}'));
466 buffer.writeln(h2('Compilation took: '
467 '${info.compilationDuration/1000000} seconds'));
468 buffer.writeln(h2('Output size: ${info.size} bytes'));
469 if (info.dart2jsVersion != null) {
470 buffer.writeln(h2('Dart2js version: ${info.dart2jsVersion}'));
471 }
472
473 info.libraries.forEach((InfoNode node) {
474 node.emitHtml(info, buffer);
475 });
476
477
478 // TODO (sigurdm): This script should be written in dart
479 buffer.writeln(r"""
480 <script type="text/javascript">
481 function toggler(element) {
482 return function(e) {
483 element.hidden = !element.hidden;
484 };
485 }
486 var containers = document.getElementsByClassName('container');
487 for (var i = 0; i < containers.length; i++) {
488 var container = containers[i];
489 container.addEventListener('click',
490 toggler(container.nextElementSibling), false);
491 container.nextElementSibling.hidden = true;
492 };
493 </script>
494 </body>
495 </html>""");
496 }
497 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698