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

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 abstract class InfoNode {
62 String get name;
63
64 int get size;
65
66 void emitHtml(ProgramInfo programInfo, StringSink buffer);
67 }
68
69 class ElementInfoNode implements InfoNode {
Johnni Winther 2013/12/10 10:40:53 Add documentation to the class and its fields.
sigurdm 2013/12/10 11:50:39 Done.
70 final String name;
71
72 final String kind;
73
74 final String type;
75
76 final String extra;
77
78 final String modifiers;
79
80 /// Describes how many bytes the code for this element takes up in the
81 /// generated output.
82 final int size;
83
84 List<InfoNode> contents;
85
86 ElementInfoNode({this.name: "",
87 this.kind: "",
88 this.type,
89 this.modifiers: "",
90 this.size,
91 this.contents,
92 this.extra: ""});
93
94 void emitHtml(ProgramInfo programInfo, StringSink buffer) {
95 String kindString = span(esc(kind), cls: 'kind');
96 String modifiersString = span(esc(modifiers), cls: "modifiers");
97
98 String nameString = span(esc(name), cls: 'name');
99 String typeString = type == null
100 ? ''
101 : span('/* ' + esc(type) + ' */', cls: 'type');
102 String extraString = span(esc(extra), cls: 'type');
103 String describe = [
104 kindString,
105 typeString,
106 modifiersString,
107 nameString,
108 sizeDescription(size, programInfo),
109 extraString].join(' ');
110
111 if (contents != null) {
112 buffer.write(div("+$describe", cls: "container"));
113 buffer.write('<div class="contained">');
114 if (contents.isEmpty) {
115 buffer.writeln("No members");
116 }
117 for (InfoNode subElementDescription in contents) {
118 subElementDescription.emitHtml(programInfo, buffer);
119 }
120 buffer.write("</div>");
121 } else {
122 buffer.writeln(describe);
123 }
124 }
125 }
126
127 class CodeInfoNode implements InfoNode {
Johnni Winther 2013/12/10 10:40:53 Add documentation to the class and its fields.
sigurdm 2013/12/10 11:50:39 Done.
128 final String description;
129
130 final String generatedCode;
131
132 get size => generatedCode.length;
133
134 get name => "";
135
136 CodeInfoNode({this.description: "", this.generatedCode});
137
138 void emitHtml(ProgramInfo programInfo, StringBuffer buffer) {
139 buffer.write(div(description + ' ' +
140 sizeDescription(generatedCode.length, programInfo),
141 cls: 'kind') +
142 code(esc(generatedCode)));
143 }
144 }
145
146 class InferredInfoNode implements InfoNode {
Johnni Winther 2013/12/10 10:40:53 Add documentation to the class and its fields.
sigurdm 2013/12/10 11:50:39 Done.
147 final String description;
148
149 final String name;
150
151 final String type;
152
153 get size => 0;
154
155 InferredInfoNode({this.name: "", this.description, this.type});
156
157 void emitHtml(ProgramInfo programInfo, StringSink buffer) {
158 buffer.write(div('${span("Inferred " + description, cls: "kind")} '
159 '${span(esc(name),
160 cls: "name")} '
161 '${span(esc(type.toString()), cls: 'type')} '));
Johnni Winther 2013/12/10 10:40:53 Remove .toString() -- [type] is already a String.
sigurdm 2013/12/10 11:50:39 Done.
162 }
163 }
164
165 class ProgramInfo {
Johnni Winther 2013/12/10 10:40:53 Add documentation to the class and its fields.
sigurdm 2013/12/10 11:50:39 Done.
166 final String name;
167
168 final String presentation;
169
170 final List<InfoNode> libraries;
171
172 // How many bytes is the output
173 final int size;
174
175 final DateTime compilationMoment;
176
177 final int compilationDuration;
178
179 final String dart2jsVersion;
180
181 ProgramInfo({String this.name,
182 this.presentation,
Johnni Winther 2013/12/10 10:40:53 Indent this and the next lines to fit under `Strin
sigurdm 2013/12/10 11:50:39 Done.
183 this.libraries,
184 this.size, DateTime this.compilationMoment,
Johnni Winther 2013/12/10 10:40:53 Put `DateTime this.compilationMoment` on a separat
sigurdm 2013/12/10 11:50:39 Done.
185 this.compilationDuration, String this.dart2jsVersion});
Johnni Winther 2013/12/10 10:40:53 Ditto for `String this.dart2jsVersion`.
sigurdm 2013/12/10 11:50:39 Done.
186 }
187
188 class InfoDumpVisitor extends ElementVisitor<InfoNode> {
189 final Compiler compiler;
190
191 /// Contains the elements visited on the path from the library to here.
192 List<Element> stack = new List<Element>();
193
194 InfoDumpVisitor(Compiler this.compiler);
195
196 InfoNode visitElement(Element element) {
197 compiler.internalError("This element of kind ${element.kind} "
198 "does not support --dump-info",
199 token: element.position());
200 }
201
202 InfoNode visitLibraryElement(LibraryElement element) {
203 List<InfoNode> contents = new List<InfoNode>();
204 int size = compiler.dumpInfoTask.codeSizeCounter
205 .getGeneratedSizeOf(element);
206 if (size == 0) return null;
207 stack.add(element);
208 element.forEachLocalMember((Element member) {
209 InfoNode info = member.accept(this);
210 if (info != null) {
211 contents.add(info);
212 }
213 });
214 stack.removeLast();
215 String nameString = element.getLibraryName() == ""
216 ? "<unnamed>"
217 : element.getLibraryName();
218 contents.sort((InfoNode e1, InfoNode e2) {
219 return e1.name.compareTo(e2.name);
220 });
221 return new ElementInfoNode(
222 extra: element.canonicalUri.toString(),
Johnni Winther 2013/12/10 10:40:53 Maybe use '${element.canonicalUri}' instead?
sigurdm 2013/12/10 11:50:39 Done.
223 kind: "library",
224 name: nameString,
225 size: size,
226 modifiers: "",
227 contents: contents);
228 }
229
230 InfoNode visitTypedefElement(TypedefElement element) {
231 return element.alias == null
232 ? null
233 : new ElementInfoNode(
234 type: element.alias.toString(),
235 kind: "typedef",
236 name: element.name);
237 }
238
239 InfoNode visitFieldElement(FieldElement element) {
240 CodeBuffer emittedCode = compiler.backend.codeOf(element);
241 int size = 0;
242 DartType type = element.computeType(compiler);
243 TypeMask inferredType = compiler.typesTask
244 .getGuaranteedTypeOfElement(element);
245 if (inferredType == TypeMask) return null;
Johnni Winther 2013/12/10 10:40:53 Why do you compare to `TypeMask`, is that ever tru
sigurdm 2013/12/10 11:50:39 Done.
246 List<InfoNode> contents = new List<InfoNode>();
247 if (emittedCode != null) {
248 contents.add(new CodeInfoNode(
249 description: "Generated initializer",
250 generatedCode: emittedCode.getText()));
251 size = emittedCode.length;
252 }
253 contents.add(new InferredInfoNode(
254 description: "type",
255 type: inferredType.toString()));
256 stack.add(element);
257 for (Element closure in element.nestedClosures) {
258 InfoNode info = closure.accept(this);
259 if (info != null) {
260 contents.add(info);
261 size += info.size;
262 }
263 }
264 stack.removeLast();
265
266 return new ElementInfoNode(
267 kind: "field",
268 type: type.toString(),
269 name: element.name,
270 size: size,
271 modifiers: element.modifiers.toString(),
272 contents: contents);
273 }
274
275 InfoNode visitClassElement(ClassElement element) {
276 String modifiersString = element.modifiers.toString() == ""
Johnni Winther 2013/12/10 10:40:53 Change `element.modifiers.toString() == ""` to `el
sigurdm 2013/12/10 11:50:39 Done.
277 ? ""
278 : element.modifiers.toString()+" ";
Johnni Winther 2013/12/10 10:40:53 Change `element.modifiers.toString()+" "` to `'${e
sigurdm 2013/12/10 11:50:39 Done.
279 if (!element.isResolved) return null;
Johnni Winther 2013/12/10 10:40:53 Check this as the first thing in the method and ad
sigurdm 2013/12/10 11:50:39 Done.
280 String supersString = element.allSupertypes == null ? "" :
281 "implements ${element.allSupertypes}";
282 List contents = [];
283 stack.add(element);
284 element.forEachLocalMember((Element member) {
285 InfoNode info = member.accept(this);
286 if (info != null) {
287 contents.add(info);
288 }
289 });
290 stack.removeLast();
291 if (contents.isEmpty) {
292 return null;
Johnni Winther 2013/12/10 10:40:53 Why exclude a class with no members. We might stil
sigurdm 2013/12/10 11:50:39 Done.
293 }
294 contents.sort((InfoNode n1, InfoNode n2) {
295 return n1.name.compareTo(n2.name);
296 });
297 return new ElementInfoNode(
298 kind: "class",
299 name: element.name,
300 extra: supersString,
301 modifiers: modifiersString,
302 contents: contents);
303 }
304
305 InfoNode visitFunctionElement(FunctionElement element) {
306 CodeBuffer emittedCode = compiler.backend.codeOf(element);
307 int size = 0;
308 String nameString = element.name;
309 String modifiersString = element.modifiers.toString();
Johnni Winther 2013/12/10 10:40:53 Change `element.modifiers.toString()` to '${elemen
sigurdm 2013/12/10 11:50:39 Done.
310 String kindString = "function";
311 if (stack.last.isClass()) {
312 kindString = "method";
313 } else if (stack.last.isField() ||
314 stack.last.isFunction() ||
Johnni Winther 2013/12/10 10:40:53 Indent `stack.last.isFunction()` to fit under `sta
sigurdm 2013/12/10 11:50:39 Done.
315 stack.last.isConstructor()) {
Johnni Winther 2013/12/10 10:40:53 Ditto.
sigurdm 2013/12/10 11:50:39 Done.
316 kindString = "closure";
317 nameString = "<unnamed>";
318 }
319 if (element.isConstructor()) {
320 nameString = element.name == ""
321 ? "${element.enclosingElement.name}"
322 : "${element.enclosingElement.name}.${element.name}";
323 kindString = "constructor";
324 }
325 List contents = [];
326 if (emittedCode != null) {
327 FunctionSignature signature = element.computeSignature(compiler);
328 signature.forEachParameter((parameter) {
329 contents.add(new InferredInfoNode(
330 description: "parameter",
331 name: parameter.name,
332 type: compiler.typesTask
333 .getGuaranteedTypeOfElement(parameter).toString()));
334 });
335 contents.add(new InferredInfoNode(
336 description: "return type",
337 type: compiler.typesTask
338 .getGuaranteedReturnTypeOfElement(element).toString()));
339 contents.add(new InferredInfoNode(
340 description: "side effects",
341 type: compiler.world
342 .getSideEffectsOfElement(element).toString()));
343 contents.add(new CodeInfoNode(
344 description: "Generated code",
345 generatedCode: emittedCode.getText()));
346 size += emittedCode.length;
347 }
348 stack.add(element);
349 for (Element closure in element.nestedClosures) {
350 InfoNode info = closure.accept(this);
351 if (info != null) {
352 contents.add(info);
353 size += info.size;
354 }
355 }
356 stack.removeLast();
357 if (size == 0) {
358 return null;
359 }
360 return new ElementInfoNode(
361 type: element.type.toString(),
362 kind: kindString,
363 name: nameString,
364 size: size,
365 modifiers: modifiersString,
366 contents: contents);
367 }
368 }
369
370 class DumpInfoTask extends CompilerTask {
371 DumpInfoTask(Compiler compiler)
372 : infoDumpVisitor = new InfoDumpVisitor(compiler),
373 super(compiler);
374
375 String name = "Dump Info";
376
377 final CodeSizeCounter codeSizeCounter = new CodeSizeCounter();
378
379 final InfoDumpVisitor infoDumpVisitor;
380
381 void dumpInfo() {
382 measure(() {
383 ProgramInfo info = collectDumpInfo();
384 StringBuffer buffer = new StringBuffer();
385 dumpInfoHtml(info, buffer);
386 compiler.outputProvider('', 'info.html')
387 ..add(buffer.toString())
388 ..close();
389 });
390 }
391
392 ProgramInfo collectDumpInfo() {
393 List<LibraryElement> sortedLibraries = compiler.libraries.values.toList();
394 sortedLibraries.sort((LibraryElement l1, LibraryElement l2) {
395 if (l1.isPlatformLibrary && !l2.isPlatformLibrary) {
396 return 1;
397 } else if (!l1.isPlatformLibrary && l2.isPlatformLibrary) {
398 return -1;
399 }
400 return l1.getLibraryName().compareTo(l2.getLibraryName());
401 });
402
403 List<InfoNode> libraryInfos = new List<InfoNode>();
404 libraryInfos.addAll(sortedLibraries
405 .map((library) => infoDumpVisitor.visit(library))
406 .where((library) => library != null));
Johnni Winther 2013/12/10 10:40:53 Rename [library] to [info] -- it's not the library
sigurdm 2013/12/10 11:50:39 Done.
407
408 return new ProgramInfo(
409 compilationDuration: compiler.totalCompileTime.elapsedTicks,
410 // TODO (sigurdm): Also count the size of deferred code
411 size: compiler.assembledCode.length,
412 libraries: libraryInfos,
413 compilationMoment: new DateTime.now(),
414 dart2jsVersion: compiler.hasBuildId ? compiler.buildId : null);
415 }
416
417 void dumpInfoHtml(ProgramInfo info, StringSink buffer) {
418 int totalSize = info.size;
419
420 buffer.writeln("""
421 <html>
422 <head>
423 <title>Dart2JS compilation information</title>
424 <style>
425 code {margin-left: 20px; display: block;}
426 div.contained {margin-left: 20px;}
427 div {margin-top:0px;
428 margin-bottom: 0px;
429 white-space: pre; /*border: 1px solid;*/}
430 span.kind {}
431 span.modifiers {font-weight:bold;}
432 span.name {font-weight:bold; font-family: monospace;}
433 span.type {font-family: monospace; color:blue;}
434 </style>
435 </head>
436 <body>
437 <h1>Dart2js compilation information</h1>""");
438 buffer.writeln(h2('Compilation took place: '
439 '${info.compilationMoment}'));
440 buffer.writeln(h2('Compilation took: '
441 '${info.compilationDuration/1000000} seconds'));
442 buffer.writeln(h2('Output size: ${info.size} bytes'));
443 if (info.dart2jsVersion != null) {
444 buffer.writeln(h2('Dart2js version: ${info.dart2jsVersion}'));
445 }
446
447 info.libraries.forEach((InfoNode node) {
448 node.emitHtml(info, buffer);
449 });
450
451
452 // TODO (sigurdm): This script should be written in dart
453 buffer.writeln(r"""
454 <script type="text/javascript">
455 function toggler(element) {
456 return function(e) {
457 element.hidden = !element.hidden;
458 };
459 }
460 var containers = document.getElementsByClassName('container');
461 for (var i = 0; i < containers.length; i++) {
462 var container = containers[i];
463 container.addEventListener('click',
464 toggler(container.nextElementSibling), false);
465 container.nextElementSibling.hidden = true;
466 };
467 </script>
468 </body>
469 </html>""");
470 }
471 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698