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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: sdk/lib/_internal/compiler/implementation/dump_info.dart
diff --git a/sdk/lib/_internal/compiler/implementation/dump_info.dart b/sdk/lib/_internal/compiler/implementation/dump_info.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b43e9adab7218b9001217e9602fad6ecac2765d9
--- /dev/null
+++ b/sdk/lib/_internal/compiler/implementation/dump_info.dart
@@ -0,0 +1,471 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library dump_info;
+
+import 'elements/elements.dart';
+import 'elements/visitor.dart';
+import 'dart:convert' show HtmlEscape;
+import 'dart2jslib.dart' show
+ Compiler,
+ CompilerTask,
+ CodeBuffer;
+import 'dart_types.dart' show DartType;
+import 'types/types.dart' show TypeMask;
+
+// TODO (sigurdm): A search function.
+// TODO (sigurdm): Output size of classes.
+// TODO (sigurdm): Print that we dumped the HTML-file.
+// TODO (sigurdm): Include why a given element was included in the output.
+// TODO (sigurdm): Include how much output grew because of mirror support.
+// TODO (sigurdm): Write each function with parameter names.
+// TODO (sigurdm): Write how much space the boilerplate takes.
+// TODO (sigurdm): Include javascript names of entities in the output.
+
+class CodeSizeCounter {
+ final Map<Element, int> generatedSize = new Map<Element, int>();
+
+ int getGeneratedSizeOf(Element element) {
+ int result = generatedSize[element];
+ return result == null ? 0 : result;
+ }
+
+ void countCode(Element element, int added) {
+ int before = generatedSize.putIfAbsent(element, () => 0);
+ generatedSize[element] = before + added;
+ }
+}
+
+tag(String element) {
+ return (String content, {String cls}) {
+ String classString = cls == null ? '' : ' class="$cls"';
+ return '<$element$classString>$content</$element>';
+ };
+}
+
+var div = tag('div');
+var span = tag('span');
+var code = tag('code');
+var h2 = tag('h2');
+
+var esc = const HtmlEscape().convert;
+
+String sizeDescription(int size, ProgramInfo programInfo) {
+ return size == null
+ ? ''
+ : span('${size} bytes '
+ '(${size * 100 ~/ programInfo.size}%)', cls: "size");
+}
+
+abstract class InfoNode {
+ String get name;
+
+ int get size;
+
+ void emitHtml(ProgramInfo programInfo, StringSink buffer);
+}
+
+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.
+ final String name;
+
+ final String kind;
+
+ final String type;
+
+ final String extra;
+
+ final String modifiers;
+
+ /// Describes how many bytes the code for this element takes up in the
+ /// generated output.
+ final int size;
+
+ List<InfoNode> contents;
+
+ ElementInfoNode({this.name: "",
+ this.kind: "",
+ this.type,
+ this.modifiers: "",
+ this.size,
+ this.contents,
+ this.extra: ""});
+
+ void emitHtml(ProgramInfo programInfo, StringSink buffer) {
+ String kindString = span(esc(kind), cls: 'kind');
+ String modifiersString = span(esc(modifiers), cls: "modifiers");
+
+ String nameString = span(esc(name), cls: 'name');
+ String typeString = type == null
+ ? ''
+ : span('/* ' + esc(type) + ' */', cls: 'type');
+ String extraString = span(esc(extra), cls: 'type');
+ String describe = [
+ kindString,
+ typeString,
+ modifiersString,
+ nameString,
+ sizeDescription(size, programInfo),
+ extraString].join(' ');
+
+ if (contents != null) {
+ buffer.write(div("+$describe", cls: "container"));
+ buffer.write('<div class="contained">');
+ if (contents.isEmpty) {
+ buffer.writeln("No members");
+ }
+ for (InfoNode subElementDescription in contents) {
+ subElementDescription.emitHtml(programInfo, buffer);
+ }
+ buffer.write("</div>");
+ } else {
+ buffer.writeln(describe);
+ }
+ }
+}
+
+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.
+ final String description;
+
+ final String generatedCode;
+
+ get size => generatedCode.length;
+
+ get name => "";
+
+ CodeInfoNode({this.description: "", this.generatedCode});
+
+ void emitHtml(ProgramInfo programInfo, StringBuffer buffer) {
+ buffer.write(div(description + ' ' +
+ sizeDescription(generatedCode.length, programInfo),
+ cls: 'kind') +
+ code(esc(generatedCode)));
+ }
+}
+
+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.
+ final String description;
+
+ final String name;
+
+ final String type;
+
+ get size => 0;
+
+ InferredInfoNode({this.name: "", this.description, this.type});
+
+ void emitHtml(ProgramInfo programInfo, StringSink buffer) {
+ buffer.write(div('${span("Inferred " + description, cls: "kind")} '
+ '${span(esc(name),
+ cls: "name")} '
+ '${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.
+ }
+}
+
+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.
+ final String name;
+
+ final String presentation;
+
+ final List<InfoNode> libraries;
+
+ // How many bytes is the output
+ final int size;
+
+ final DateTime compilationMoment;
+
+ final int compilationDuration;
+
+ final String dart2jsVersion;
+
+ ProgramInfo({String this.name,
+ 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.
+ this.libraries,
+ 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.
+ 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.
+}
+
+class InfoDumpVisitor extends ElementVisitor<InfoNode> {
+ final Compiler compiler;
+
+ /// Contains the elements visited on the path from the library to here.
+ List<Element> stack = new List<Element>();
+
+ InfoDumpVisitor(Compiler this.compiler);
+
+ InfoNode visitElement(Element element) {
+ compiler.internalError("This element of kind ${element.kind} "
+ "does not support --dump-info",
+ token: element.position());
+ }
+
+ InfoNode visitLibraryElement(LibraryElement element) {
+ List<InfoNode> contents = new List<InfoNode>();
+ int size = compiler.dumpInfoTask.codeSizeCounter
+ .getGeneratedSizeOf(element);
+ if (size == 0) return null;
+ stack.add(element);
+ element.forEachLocalMember((Element member) {
+ InfoNode info = member.accept(this);
+ if (info != null) {
+ contents.add(info);
+ }
+ });
+ stack.removeLast();
+ String nameString = element.getLibraryName() == ""
+ ? "<unnamed>"
+ : element.getLibraryName();
+ contents.sort((InfoNode e1, InfoNode e2) {
+ return e1.name.compareTo(e2.name);
+ });
+ return new ElementInfoNode(
+ 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.
+ kind: "library",
+ name: nameString,
+ size: size,
+ modifiers: "",
+ contents: contents);
+ }
+
+ InfoNode visitTypedefElement(TypedefElement element) {
+ return element.alias == null
+ ? null
+ : new ElementInfoNode(
+ type: element.alias.toString(),
+ kind: "typedef",
+ name: element.name);
+ }
+
+ InfoNode visitFieldElement(FieldElement element) {
+ CodeBuffer emittedCode = compiler.backend.codeOf(element);
+ int size = 0;
+ DartType type = element.computeType(compiler);
+ TypeMask inferredType = compiler.typesTask
+ .getGuaranteedTypeOfElement(element);
+ 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.
+ List<InfoNode> contents = new List<InfoNode>();
+ if (emittedCode != null) {
+ contents.add(new CodeInfoNode(
+ description: "Generated initializer",
+ generatedCode: emittedCode.getText()));
+ size = emittedCode.length;
+ }
+ contents.add(new InferredInfoNode(
+ description: "type",
+ type: inferredType.toString()));
+ stack.add(element);
+ for (Element closure in element.nestedClosures) {
+ InfoNode info = closure.accept(this);
+ if (info != null) {
+ contents.add(info);
+ size += info.size;
+ }
+ }
+ stack.removeLast();
+
+ return new ElementInfoNode(
+ kind: "field",
+ type: type.toString(),
+ name: element.name,
+ size: size,
+ modifiers: element.modifiers.toString(),
+ contents: contents);
+ }
+
+ InfoNode visitClassElement(ClassElement element) {
+ 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.
+ ? ""
+ : 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.
+ 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.
+ String supersString = element.allSupertypes == null ? "" :
+ "implements ${element.allSupertypes}";
+ List contents = [];
+ stack.add(element);
+ element.forEachLocalMember((Element member) {
+ InfoNode info = member.accept(this);
+ if (info != null) {
+ contents.add(info);
+ }
+ });
+ stack.removeLast();
+ if (contents.isEmpty) {
+ 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.
+ }
+ contents.sort((InfoNode n1, InfoNode n2) {
+ return n1.name.compareTo(n2.name);
+ });
+ return new ElementInfoNode(
+ kind: "class",
+ name: element.name,
+ extra: supersString,
+ modifiers: modifiersString,
+ contents: contents);
+ }
+
+ InfoNode visitFunctionElement(FunctionElement element) {
+ CodeBuffer emittedCode = compiler.backend.codeOf(element);
+ int size = 0;
+ String nameString = element.name;
+ 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.
+ String kindString = "function";
+ if (stack.last.isClass()) {
+ kindString = "method";
+ } else if (stack.last.isField() ||
+ 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.
+ stack.last.isConstructor()) {
Johnni Winther 2013/12/10 10:40:53 Ditto.
sigurdm 2013/12/10 11:50:39 Done.
+ kindString = "closure";
+ nameString = "<unnamed>";
+ }
+ if (element.isConstructor()) {
+ nameString = element.name == ""
+ ? "${element.enclosingElement.name}"
+ : "${element.enclosingElement.name}.${element.name}";
+ kindString = "constructor";
+ }
+ List contents = [];
+ if (emittedCode != null) {
+ FunctionSignature signature = element.computeSignature(compiler);
+ signature.forEachParameter((parameter) {
+ contents.add(new InferredInfoNode(
+ description: "parameter",
+ name: parameter.name,
+ type: compiler.typesTask
+ .getGuaranteedTypeOfElement(parameter).toString()));
+ });
+ contents.add(new InferredInfoNode(
+ description: "return type",
+ type: compiler.typesTask
+ .getGuaranteedReturnTypeOfElement(element).toString()));
+ contents.add(new InferredInfoNode(
+ description: "side effects",
+ type: compiler.world
+ .getSideEffectsOfElement(element).toString()));
+ contents.add(new CodeInfoNode(
+ description: "Generated code",
+ generatedCode: emittedCode.getText()));
+ size += emittedCode.length;
+ }
+ stack.add(element);
+ for (Element closure in element.nestedClosures) {
+ InfoNode info = closure.accept(this);
+ if (info != null) {
+ contents.add(info);
+ size += info.size;
+ }
+ }
+ stack.removeLast();
+ if (size == 0) {
+ return null;
+ }
+ return new ElementInfoNode(
+ type: element.type.toString(),
+ kind: kindString,
+ name: nameString,
+ size: size,
+ modifiers: modifiersString,
+ contents: contents);
+ }
+}
+
+class DumpInfoTask extends CompilerTask {
+ DumpInfoTask(Compiler compiler)
+ : infoDumpVisitor = new InfoDumpVisitor(compiler),
+ super(compiler);
+
+ String name = "Dump Info";
+
+ final CodeSizeCounter codeSizeCounter = new CodeSizeCounter();
+
+ final InfoDumpVisitor infoDumpVisitor;
+
+ void dumpInfo() {
+ measure(() {
+ ProgramInfo info = collectDumpInfo();
+ StringBuffer buffer = new StringBuffer();
+ dumpInfoHtml(info, buffer);
+ compiler.outputProvider('', 'info.html')
+ ..add(buffer.toString())
+ ..close();
+ });
+ }
+
+ ProgramInfo collectDumpInfo() {
+ List<LibraryElement> sortedLibraries = compiler.libraries.values.toList();
+ sortedLibraries.sort((LibraryElement l1, LibraryElement l2) {
+ if (l1.isPlatformLibrary && !l2.isPlatformLibrary) {
+ return 1;
+ } else if (!l1.isPlatformLibrary && l2.isPlatformLibrary) {
+ return -1;
+ }
+ return l1.getLibraryName().compareTo(l2.getLibraryName());
+ });
+
+ List<InfoNode> libraryInfos = new List<InfoNode>();
+ libraryInfos.addAll(sortedLibraries
+ .map((library) => infoDumpVisitor.visit(library))
+ .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.
+
+ return new ProgramInfo(
+ compilationDuration: compiler.totalCompileTime.elapsedTicks,
+ // TODO (sigurdm): Also count the size of deferred code
+ size: compiler.assembledCode.length,
+ libraries: libraryInfos,
+ compilationMoment: new DateTime.now(),
+ dart2jsVersion: compiler.hasBuildId ? compiler.buildId : null);
+ }
+
+ void dumpInfoHtml(ProgramInfo info, StringSink buffer) {
+ int totalSize = info.size;
+
+ buffer.writeln("""
+<html>
+ <head>
+ <title>Dart2JS compilation information</title>
+ <style>
+ code {margin-left: 20px; display: block;}
+ div.contained {margin-left: 20px;}
+ div {margin-top:0px;
+ margin-bottom: 0px;
+ white-space: pre; /*border: 1px solid;*/}
+ span.kind {}
+ span.modifiers {font-weight:bold;}
+ span.name {font-weight:bold; font-family: monospace;}
+ span.type {font-family: monospace; color:blue;}
+ </style>
+ </head>
+ <body>
+ <h1>Dart2js compilation information</h1>""");
+ buffer.writeln(h2('Compilation took place: '
+ '${info.compilationMoment}'));
+ buffer.writeln(h2('Compilation took: '
+ '${info.compilationDuration/1000000} seconds'));
+ buffer.writeln(h2('Output size: ${info.size} bytes'));
+ if (info.dart2jsVersion != null) {
+ buffer.writeln(h2('Dart2js version: ${info.dart2jsVersion}'));
+ }
+
+ info.libraries.forEach((InfoNode node) {
+ node.emitHtml(info, buffer);
+ });
+
+
+ // TODO (sigurdm): This script should be written in dart
+ buffer.writeln(r"""
+ <script type="text/javascript">
+ function toggler(element) {
+ return function(e) {
+ element.hidden = !element.hidden;
+ };
+ }
+ var containers = document.getElementsByClassName('container');
+ for (var i = 0; i < containers.length; i++) {
+ var container = containers[i];
+ container.addEventListener('click',
+ toggler(container.nextElementSibling), false);
+ container.nextElementSibling.hidden = true;
+ };
+ </script>
+ </body>
+</html>""");
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698