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

Side by Side Diff: runtime/observatory/lib/src/elements/script_inset.dart

Issue 1087833005: Add annotation for function declarations. Mark functions with performance problems in red. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 8 months 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
« no previous file with comments | « no previous file | runtime/observatory/lib/src/elements/script_inset.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library script_inset_element; 5 library script_inset_element;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:html'; 8 import 'dart:html';
9 import 'observatory_element.dart'; 9 import 'observatory_element.dart';
10 import 'service_ref.dart'; 10 import 'service_ref.dart';
11 import 'package:observatory/service.dart'; 11 import 'package:observatory/service.dart';
12 import 'package:polymer/polymer.dart'; 12 import 'package:polymer/polymer.dart';
13 13
14 const nbsp = "\u00A0"; 14 const nbsp = "\u00A0";
15 15
16 void addInfoBox(content, infoBox) { 16 void addInfoBox(Element content, Function infoBoxGenerator) {
17 infoBox.style.position = 'absolute'; 17 var infoBox;
18 infoBox.style.padding = '1em';
19 infoBox.style.border = 'solid black 2px';
20 infoBox.style.zIndex = '10';
21 infoBox.style.backgroundColor = 'white';
22 infoBox.style.cursor = 'auto';
23 infoBox.style.display = 'none'; // Initially hidden.
24
25 var show = false; 18 var show = false;
19 var originalBackground = content.style.backgroundColor;
20 buildInfoBox() {
21 infoBox = infoBoxGenerator();
22 infoBox.style.position = 'absolute';
23 infoBox.style.padding = '1em';
24 infoBox.style.border = 'solid black 2px';
25 infoBox.style.zIndex = '10';
26 infoBox.style.backgroundColor = 'white';
27 infoBox.style.cursor = 'auto';
28 content.append(infoBox);
29 }
26 content.onClick.listen((event) { 30 content.onClick.listen((event) {
27 show = !show; 31 show = !show;
32 if (infoBox == null) buildInfoBox(); // Created lazily on the first click.
28 infoBox.style.display = show ? 'block' : 'none'; 33 infoBox.style.display = show ? 'block' : 'none';
29 content.style.backgroundColor = show ? 'white' : ''; 34 content.style.backgroundColor = show ? 'white' : originalBackground;
30 }); 35 });
31 36
32 // Causes infoBox to be positioned relative to the bottom-left of content. 37 // Causes infoBox to be positioned relative to the bottom-left of content.
33 content.style.display = 'inline-block'; 38 content.style.display = 'inline-block';
34 content.style.cursor = 'pointer'; 39 content.style.cursor = 'pointer';
35 content.append(infoBox);
36 } 40 }
37 41
38 abstract class Annotation { 42 abstract class Annotation implements Comparable<Annotation> {
39 int line; 43 int line;
40 int columnStart; 44 int columnStart;
41 int columnStop; 45 int columnStop;
42 46
43 void applyStyleTo(element); 47 void applyStyleTo(element);
44 }
45 48
46 class CurrentExecutionAnnotation extends Annotation { 49 int compareTo(Annotation other) {
47 void applyStyleTo(element) { 50 if (line == other.line) {
48 if (element == null) { 51 return columnStart.compareTo(other.columnStart);
49 return; // TODO(rmacnak): Handling overlapping annotations.
50 } 52 }
51 element.classes.add("currentCol"); 53 return line.compareTo(other.line);
52 element.title = "Current execution";
53 } 54 }
54 }
55 55
56 class CallSiteAnnotation extends Annotation { 56 Element table() {
57 CallSite callSite; 57 var e = new DivElement();
58 e.style.display = "table";
59 e.style.color = "#333";
60 e.style.font = "400 14px 'Montserrat', sans-serif";
61 return e;
62 }
58 63
59 Element row([content]) { 64 Element row([content]) {
60 var e = new DivElement(); 65 var e = new DivElement();
61 e.style.display = "table-row"; 66 e.style.display = "table-row";
62 if (content is String) e.text = content; 67 if (content is String) e.text = content;
68 if (content is Element) e.children.add(content);
63 return e; 69 return e;
64 } 70 }
65 71
66 Element cell(content) { 72 Element cell(content) {
67 var e = new DivElement(); 73 var e = new DivElement();
68 e.style.display = "table-cell"; 74 e.style.display = "table-cell";
69 e.style.padding = "3px"; 75 e.style.padding = "3px";
70 if (content is String) e.text = content; 76 if (content is String) e.text = content;
71 if (content is Element) e.children.add(content); 77 if (content is Element) e.children.add(content);
72 return e; 78 return e;
73 } 79 }
74 80
75 Element serviceRef(object) { 81 Element serviceRef(object) {
76 AnyServiceRefElement e = new Element.tag("any-service-ref"); 82 AnyServiceRefElement e = new Element.tag("any-service-ref");
77 e.ref = object; 83 e.ref = object;
78 return e; 84 return e;
79 } 85 }
86 }
80 87
81 Element entriesTable() { 88 class CurrentExecutionAnnotation extends Annotation {
82 var e = new DivElement(); 89 void applyStyleTo(element) {
83 e.style.display = "table"; 90 if (element == null) {
84 e.style.color = "#333"; 91 return; // TODO(rmacnak): Handling overlapping annotations.
85 e.style.font = "400 14px 'Montserrat', sans-serif"; 92 }
93 element.classes.add("currentCol");
94 element.title = "Current execution";
95 }
96 }
86 97
87 if (callSite.entries.isEmpty) { 98 class CallSiteAnnotation extends Annotation {
88 e.append(row('Did not execute')); 99 CallSite callSite;
89 } else {
90 var r = row();
91 r.append(cell("Container"));
92 r.append(cell("Count"));
93 r.append(cell("Target"));
94 e.append(r);
95 100
96 for (var entry in callSite.entries) { 101 CallSiteAnnotation(this.callSite) {
97 var r = row(); 102 line = callSite.line;
98 r.append(cell(serviceRef(entry.receiverContainer))); 103 columnStart = callSite.column - 1; // Call site is 1-origin.
99 r.append(cell(entry.count.toString())); 104 var tokenLength = callSite.name.length; // Approximate.
100 r.append(cell(serviceRef(entry.target))); 105 if (callSite.name.startsWith("get:") ||
101 e.append(r); 106 callSite.name.startsWith("set:")) tokenLength -= 4;
102 } 107 columnStop = columnStart + tokenLength;
103 }
104
105 return e;
106 } 108 }
107 109
108 void applyStyleTo(element) { 110 void applyStyleTo(element) {
111 if (element == null) {
112 return; // TODO(rmacnak): Handling overlapping annotations.
113 }
114 element.style.fontWeight = "bold";
115 element.title = "Call site: ${callSite.name}";
116
117 addInfoBox(element, () {
118 var details = table();
119 if (callSite.entries.isEmpty) {
120 details.append(row('Did not execute'));
121 } else {
122 var r = row();
123 r.append(cell("Container"));
124 r.append(cell("Count"));
125 r.append(cell("Target"));
126 details.append(r);
127
128 for (var entry in callSite.entries) {
129 var r = row();
130 r.append(cell(serviceRef(entry.receiverContainer)));
131 r.append(cell(entry.count.toString()));
132 r.append(cell(serviceRef(entry.target)));
133 details.append(r);
134 }
135 }
136 return details;
137 });
138 }
139 }
140
141
142 class FunctionDeclarationAnnotation extends Annotation {
143 ServiceFunction function;
144
145 FunctionDeclarationAnnotation(this.function) {
146 assert(function.loaded);
147 var script = function.script;
148 line = script.tokenToLine(function.tokenPos);
149 columnStart = script.tokenToCol(function.tokenPos);
150 if ((line == null) || (columnStart == null)) {
151 line = 0;
152 columnStart = 0;
153 columnStop = 0;
154 } else {
155 columnStart--; // 1-origin -> 0-origin.
156
157 // The method's token position is at the beginning of the method
158 // declaration, which may be a return type annotation, metadata, static
159 // modifier, etc. Try to scan forward to position this annotation on the
160 // function's name instead.
161 var lineSource = script.getLine(line).text;
162 var betterStart = lineSource.indexOf(function.name, columnStart);
163 if (betterStart != -1) {
164 columnStart = betterStart;
165 }
166 columnStop = columnStart + function.name.length;
167 }
168 }
169
170 void applyStyleTo(element) {
109 if (element == null) { 171 if (element == null) {
110 return; // TODO(rmacnak): Handling overlapping annotations. 172 return; // TODO(rmacnak): Handling overlapping annotations.
111 } 173 }
112 element.style.fontWeight = "bold"; 174 element.style.fontWeight = "bold";
113 element.title = "Call site: ${callSite.name}"; 175 element.title = "Function declaration: ${function.name}";
114 176
115 addInfoBox(element, entriesTable()); 177 if (function.isOptimizable == false ||
178 function.isInlinable == false ||
179 function.deoptimizations >0) {
180 element.style.backgroundColor = "red";
181 }
182
183 addInfoBox(element, () {
184 var details = table();
185 var r = row();
186 r.append(cell("Function"));
187 r.append(cell(serviceRef(function)));
188 details.append(r);
189
190 r = row();
191 r.append(cell("Usage Count"));
192 r.append(cell("${function.usageCounter}"));
193 details.append(r);
194
195 if (function.isOptimizable == false) {
196 details.append(row(cell("Unoptimizable!")));
197 }
198 if (function.isInlinable == false) {
199 details.append(row(cell("Not inlinable!")));
200 }
201 if (function.deoptimizations > 0) {
202 details.append(row("Deoptimized ${function.deoptimizations} times!"));
203 }
204 return details;
205 });
116 } 206 }
117 } 207 }
118 208
119 /// Box with script source code in it. 209 /// Box with script source code in it.
120 @CustomTag('script-inset') 210 @CustomTag('script-inset')
121 class ScriptInsetElement extends ObservatoryElement { 211 class ScriptInsetElement extends ObservatoryElement {
122 @published Script script; 212 @published Script script;
123 213
124 /// Set the height to make the script inset scroll. Otherwise it 214 /// Set the height to make the script inset scroll. Otherwise it
125 /// will show from startPos to endPos. 215 /// will show from startPos to endPos.
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 var table = linesTable(); 309 var table = linesTable();
220 if (container == null) { 310 if (container == null) {
221 // Indirect to avoid deleting the style element. 311 // Indirect to avoid deleting the style element.
222 container = new DivElement(); 312 container = new DivElement();
223 shadowRoot.append(container); 313 shadowRoot.append(container);
224 } 314 }
225 container.children.clear(); 315 container.children.clear();
226 container.children.add(table); 316 container.children.add(table);
227 } 317 }
228 318
319 void loadFunctionsOf(Library lib) {
320 lib.load().then((lib) {
321 for (var func in lib.functions) {
322 func.load();
323 }
324 for (var cls in lib.classes) {
325 cls.load().then((cls) {
326 for (var func in cls.functions) {
327 func.load();
328 }
329 });
330 }
331 });
332 }
333
229 void computeAnnotations() { 334 void computeAnnotations() {
230 startLine = (startPos != null 335 startLine = (startPos != null
231 ? script.tokenToLine(startPos) 336 ? script.tokenToLine(startPos)
232 : 1); 337 : 1);
233 currentLine = (currentPos != null 338 currentLine = (currentPos != null
234 ? script.tokenToLine(currentPos) 339 ? script.tokenToLine(currentPos)
235 : null); 340 : null);
236 currentCol = (currentPos != null 341 currentCol = (currentPos != null
237 ? (script.tokenToCol(currentPos) - 1) // make this 0-based. 342 ? (script.tokenToCol(currentPos) - 1) // make this 0-based.
238 : null); 343 : null);
239 endLine = (endPos != null 344 endLine = (endPos != null
240 ? script.tokenToLine(endPos) 345 ? script.tokenToLine(endPos)
241 : script.lines.length); 346 : script.lines.length);
242 347
243 annotations.clear(); 348 annotations.clear();
244 if (currentLine != null) { 349 if (currentLine != null) {
245 var a = new CurrentExecutionAnnotation(); 350 var a = new CurrentExecutionAnnotation();
246 a.line = currentLine; 351 a.line = currentLine;
247 a.columnStart = currentCol; 352 a.columnStart = currentCol;
248 a.columnStop = currentCol + 1; 353 a.columnStop = currentCol + 1;
249 annotations.add(a); 354 annotations.add(a);
250 } 355 }
251 356
252 for (var callSite in script.callSites) { 357 loadFunctionsOf(script.library);
253 var a = new CallSiteAnnotation(); 358
254 a.line = callSite.line; 359 for (var func in script.library.functions) {
255 a.columnStart = callSite.column - 1; // Call site is 1-origin. 360 if (func.script == script) {
256 var tokenLength = callSite.name.length; // Approximate. 361 annotations.add(new FunctionDeclarationAnnotation(func));
257 a.columnStop = a.columnStart + tokenLength; 362 }
258 a.callSite = callSite; 363 }
259 annotations.add(a); 364 for (var cls in script.library.classes) {
365 for (var func in cls.functions) {
366 if (func.script == script) {
367 annotations.add(new FunctionDeclarationAnnotation(func));
368 }
369 }
260 } 370 }
261 371
262 annotations.sort((a, b) { 372 for (var callSite in script.callSites) {
263 if (a.line == b.line) { 373 annotations.add(new CallSiteAnnotation(callSite));
264 return a.columnStart.compareTo(b.columnStart); 374 }
265 } 375
266 return a.line.compareTo(b.line); 376 annotations.sort();
267 });
268 } 377 }
269 378
270 Element linesTable() { 379 Element linesTable() {
271 var table = new DivElement(); 380 var table = new DivElement();
272 table.classes.add("sourceTable"); 381 table.classes.add("sourceTable");
273 382
274 annotationsCursor = 0; 383 annotationsCursor = 0;
275 384
276 int blankLineCount = 0; 385 int blankLineCount = 0;
277 for (int i = (startLine - 1); i <= (endLine - 1); i++) { 386 for (int i = (startLine - 1); i <= (endLine - 1); i++) {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
315 Element lineElement(ScriptLine line) { 424 Element lineElement(ScriptLine line) {
316 var e = new DivElement(); 425 var e = new DivElement();
317 e.classes.add("sourceRow"); 426 e.classes.add("sourceRow");
318 e.append(lineBreakpointElement(line)); 427 e.append(lineBreakpointElement(line));
319 e.append(lineNumberElement(line)); 428 e.append(lineNumberElement(line));
320 e.append(lineSourceElement(line)); 429 e.append(lineSourceElement(line));
321 return e; 430 return e;
322 } 431 }
323 432
324 Element lineBreakpointElement(ScriptLine line) { 433 Element lineBreakpointElement(ScriptLine line) {
325 BreakpointToggleElement e = new Element.tag("breakpoint-toggle"); 434 var e = new DivElement();
326 e.line = line; 435 var busy = false;
436 if (line == null || !line.possibleBpt) {
437 e.classes.add("emptyBreakpoint");
438 e.text = nbsp;
439 return e;
440 }
441 e.text = 'B';
442 update() {
443 if (busy) {
444 e.classes.clear();
445 e.classes.add("busyBreakpoint");
446 } else {
447 if (line.breakpoints != null) {
448 if (line.breakpointResolved) {
449 e.classes.clear();
450 e.classes.add("resolvedBreakpoint");
451 } else {
452 e.classes.clear();
453 e.classes.add("unresolvedBreakpoint");
454 }
455 } else {
456 e.classes.clear();
457 e.classes.add("possibleBreakpoint");
458 }
459 }
460 }
461 line.changes.listen((_) => update());
462 e.onClick.listen((event) {
463 if (busy) {
464 return;
465 }
466 busy = true;
467 if (line.breakpoints == null) {
468 // No breakpoint. Add it.
469 line.script.isolate.addBreakpoint(line.script, line.line).then((_) {
470 busy = false;
471 update();
472 });
473 } else {
474 // Existing breakpoint. Remove it.
475 List pending = [];
476 for (var bpt in line.breakpoints) {
477 pending.add(line.script.isolate.removeBreakpoint(bpt));
478 }
479 Future.wait(pending).then((_) {
480 busy = false;
481 update();
482 });
483 }
484 update();
485 });
486 update();
327 return e; 487 return e;
328 } 488 }
329 489
330 Element lineNumberElement(ScriptLine line) { 490 Element lineNumberElement(ScriptLine line) {
331 var lineNumber = line == null ? "..." : line.line; 491 var lineNumber = line == null ? "..." : line.line;
332 var e = span("$nbsp$lineNumber$nbsp"); 492 var e = span("$nbsp$lineNumber$nbsp");
333 493
334 if ((line == null) || (line.hits == null)) { 494 if ((line == null) || (line.hits == null)) {
335 hitsUnknown(e); 495 hitsUnknown(e);
336 } else if (line.hits == 0) { 496 } else if (line.hits == 0) {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
377 annotation.applyStyleTo(consumeUntil(annotation.columnStop)); 537 annotation.applyStyleTo(consumeUntil(annotation.columnStop));
378 } 538 }
379 consumeUntil(line.text.length); 539 consumeUntil(line.text.length);
380 } 540 }
381 541
382 return e; 542 return e;
383 } 543 }
384 544
385 ScriptInsetElement.created() : super.created(); 545 ScriptInsetElement.created() : super.created();
386 } 546 }
387
388 @CustomTag('breakpoint-toggle')
389 class BreakpointToggleElement extends ObservatoryElement {
390 @published ScriptLine line;
391 @observable bool busy = false;
392
393 void toggleBreakpoint(var a, var b, var c) {
394 if (busy) {
395 return;
396 }
397 busy = true;
398 if (line.breakpoints == null) {
399 // No breakpoint. Add it.
400 line.script.isolate.addBreakpoint(line.script, line.line).then((_) {
401 busy = false;
402 });
403 } else {
404 // Existing breakpoint. Remove it.
405 List pending = [];
406 for (var bpt in line.breakpoints) {
407 pending.add(line.script.isolate.removeBreakpoint(bpt));
408 }
409 Future.wait(pending).then((_) {
410 busy = false;
411 });
412 }
413 }
414
415 BreakpointToggleElement.created() : super.created();
416 }
OLDNEW
« no previous file with comments | « no previous file | runtime/observatory/lib/src/elements/script_inset.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698