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

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

Issue 1786703004: Display source profiling information in Observatory (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 9 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
« 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 'dart:math'; 9 import 'dart:math';
10 import 'observatory_element.dart'; 10 import 'observatory_element.dart';
(...skipping 343 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 if (function.isOptimizable == false || 354 if (function.isOptimizable == false ||
355 function.isInlinable == false || 355 function.isInlinable == false ||
356 function.deoptimizations >0) { 356 function.deoptimizations >0) {
357 element.style.backgroundColor = "#EEA7A7"; // Low-saturation red. 357 element.style.backgroundColor = "#EEA7A7"; // Low-saturation red.
358 } 358 }
359 359
360 addLink(element, url); 360 addLink(element, url);
361 } 361 }
362 } 362 }
363 363
364 class ScriptLineProfile {
365 ScriptLineProfile(this.line, this.sampleCount);
366
367 static const kHotThreshold = 0.05; // 5%.
368 static const kMediumThreshold = 0.02; // 2%.
369
370 final int line;
371 final int sampleCount;
372
373 int selfTicks = 0;
374 int totalTicks = 0;
375
376 void process(int exclusive, int inclusive) {
377 selfTicks += exclusive;
378 totalTicks += inclusive;
379 }
380
381 String get formattedSelfTicks {
382 return Utils.formatPercent(selfTicks, sampleCount);
383 }
384
385 String get formattedTotalTicks {
386 return Utils.formatPercent(totalTicks, sampleCount);
387 }
388
389 double _percent() {
390 if (sampleCount == 0) {
391 return 0.0;
392 }
393 return totalTicks / sampleCount;
394 }
395
396 bool get isHot => _percent() > kHotThreshold;
397 bool get isMedium => _percent() > kMediumThreshold;
398 }
399
364 /// Box with script source code in it. 400 /// Box with script source code in it.
365 @CustomTag('script-inset') 401 @CustomTag('script-inset')
366 class ScriptInsetElement extends ObservatoryElement { 402 class ScriptInsetElement extends ObservatoryElement {
367 @published Script script; 403 @published Script script;
368 @published int startPos; 404 @published int startPos;
369 @published int endPos; 405 @published int endPos;
370 406
371 /// Set the height to make the script inset scroll. Otherwise it 407 /// Set the height to make the script inset scroll. Otherwise it
372 /// will show from startPos to endPos. 408 /// will show from startPos to endPos.
373 @published String height = null; 409 @published String height = null;
374 410
375 @published int currentPos; 411 @published int currentPos;
376 @published bool inDebuggerContext = false; 412 @published bool inDebuggerContext = false;
377 @published ObservableList variables; 413 @published ObservableList variables;
378 414
379 @published Element scroller; 415 @published Element scroller;
380 RefreshButtonElement _refreshButton; 416 RefreshButtonElement _refreshButton;
417 ToggleButtonElement _toggleProfileButton;
381 418
382 int _currentLine; 419 int _currentLine;
383 int _currentCol; 420 int _currentCol;
384 int _startLine; 421 int _startLine;
385 int _endLine; 422 int _endLine;
386 423
387 Map<int, List<ServiceMap>> _rangeMap = {}; 424 Map<int, List<ServiceMap>> _rangeMap = {};
388 Set _callSites = new Set<CallSite>(); 425 Set _callSites = new Set<CallSite>();
389 Set _possibleBreakpointLines = new Set<int>(); 426 Set _possibleBreakpointLines = new Set<int>();
427 Map<int, ScriptLineProfile> _profileMap = {};
390 428
391 var annotations = []; 429 var annotations = [];
392 var annotationsCursor; 430 var annotationsCursor;
393 431
394 StreamSubscription _scriptChangeSubscription; 432 StreamSubscription _scriptChangeSubscription;
395 Future<StreamSubscription> _debugSubscriptionFuture; 433 Future<StreamSubscription> _debugSubscriptionFuture;
396 StreamSubscription _scrollSubscription; 434 StreamSubscription _scrollSubscription;
397 435
398 bool hasLoadedLibraryDeclarations = false; 436 bool hasLoadedLibraryDeclarations = false;
437 bool _includeProfile = false;
399 438
400 String makeLineId(int line) { 439 String makeLineId(int line) {
401 return 'line-$line'; 440 return 'line-$line';
402 } 441 }
403 442
404 void _scrollToCurrentPos() { 443 void _scrollToCurrentPos() {
405 var line = shadowRoot.getElementById(makeLineId(_currentLine)); 444 var line = shadowRoot.getElementById(makeLineId(_currentLine));
406 if (line != null) { 445 if (line != null) {
407 line.scrollIntoView(); 446 line.scrollIntoView();
408 } 447 }
(...skipping 19 matching lines...) Expand all
428 } 467 }
429 if (_scriptChangeSubscription != null) { 468 if (_scriptChangeSubscription != null) {
430 // Don't leak. If only Dart and Javascript exposed weak references... 469 // Don't leak. If only Dart and Javascript exposed weak references...
431 _scriptChangeSubscription.cancel(); 470 _scriptChangeSubscription.cancel();
432 _scriptChangeSubscription = null; 471 _scriptChangeSubscription = null;
433 } 472 }
434 super.detached(); 473 super.detached();
435 } 474 }
436 475
437 void _onScroll(event) { 476 void _onScroll(event) {
438 if (_refreshButton == null) { 477 if (_refreshButton != null) {
439 return; 478 var newTop = _buttonTop(_refreshButton);
479 if (_refreshButton.style.top != newTop) {
480 _refreshButton.style.top = '${newTop}px';
481 }
440 } 482 }
441 var currentTop = _refreshButton.style.top; 483 if (_toggleProfileButton != null) {
442 var newTop = _refreshButtonTop(); 484 var newTop = _buttonTop(_toggleProfileButton);
443 if (currentTop != newTop) { 485 if (_toggleProfileButton.style.top != newTop) {
444 _refreshButton.style.top = '${newTop}px'; 486 _toggleProfileButton.style.top = '${newTop}px';
487 }
445 } 488 }
446 } 489 }
447 490
448 void _onDebugEvent(event) { 491 void _onDebugEvent(event) {
449 if (script == null) { 492 if (script == null) {
450 return; 493 return;
451 } 494 }
452 switch (event.kind) { 495 switch (event.kind) {
453 case ServiceEvent.kBreakpointAdded: 496 case ServiceEvent.kBreakpointAdded:
454 case ServiceEvent.kBreakpointResolved: 497 case ServiceEvent.kBreakpointResolved:
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
528 } 571 }
529 572
530 Element container; 573 Element container;
531 574
532 Future _refresh() async { 575 Future _refresh() async {
533 await update(); 576 await update();
534 } 577 }
535 578
536 // Build _rangeMap and _callSites from a source report. 579 // Build _rangeMap and _callSites from a source report.
537 Future _refreshSourceReport() async { 580 Future _refreshSourceReport() async {
581 var reports = [Isolate.kCallSitesReport,
582 Isolate.kPossibleBreakpointsReport];
583 if (_includeProfile) {
584 reports.add(Isolate.kProfileReport);
585 }
538 var sourceReport = await script.isolate.getSourceReport( 586 var sourceReport = await script.isolate.getSourceReport(
539 [Isolate.kCallSitesReport, Isolate.kPossibleBreakpointsReport], 587 reports,
540 script, startPos, endPos); 588 script, startPos, endPos);
541 _possibleBreakpointLines = getPossibleBreakpointLines(sourceReport, script); 589 _possibleBreakpointLines = getPossibleBreakpointLines(sourceReport, script);
542 _rangeMap.clear(); 590 _rangeMap.clear();
543 _callSites.clear(); 591 _callSites.clear();
592 _profileMap.clear();
544 for (var range in sourceReport['ranges']) { 593 for (var range in sourceReport['ranges']) {
545 int startLine = script.tokenToLine(range['startPos']); 594 int startLine = script.tokenToLine(range['startPos']);
546 int endLine = script.tokenToLine(range['endPos']); 595 int endLine = script.tokenToLine(range['endPos']);
547 for (var line = startLine; line <= endLine; line++) { 596 for (var line = startLine; line <= endLine; line++) {
548 var rangeList = _rangeMap[line]; 597 var rangeList = _rangeMap[line];
549 if (rangeList == null) { 598 if (rangeList == null) {
550 _rangeMap[line] = [range]; 599 _rangeMap[line] = [range];
551 } else { 600 } else {
552 rangeList.add(range); 601 rangeList.add(range);
553 } 602 }
554 } 603 }
604 if (_includeProfile && range['profile'] != null) {
605 List positions = range['profile']['positions'];
606 List exclusiveTicks = range['profile']['exclusiveTicks'];
607 List inclusiveTicks = range['profile']['inclusiveTicks'];
608 int sampleCount = range['profile']['metadata']['sampleCount'];
609 assert(positions.length == exclusiveTicks.length);
610 assert(positions.length == inclusiveTicks.length);
611 for (int i = 0; i < positions.length; i++) {
612 if (positions[i] is String) {
613 // String positions are classifying token positions.
614 // TODO(johnmccutchan): Add classifier data to UI.
615 continue;
616 }
617 int line = script.tokenToLine(positions[i]);
618 ScriptLineProfile lineProfile = _profileMap[line];
619 if (lineProfile == null) {
620 lineProfile = new ScriptLineProfile(line, sampleCount);
621 _profileMap[line] = lineProfile;
622 }
623 lineProfile.process(exclusiveTicks[i], inclusiveTicks[i]);
624 }
625 }
555 if (range['compiled']) { 626 if (range['compiled']) {
556 var rangeCallSites = range['callSites']; 627 var rangeCallSites = range['callSites'];
557 if (rangeCallSites != null) { 628 if (rangeCallSites != null) {
558 for (var callSiteMap in rangeCallSites) { 629 for (var callSiteMap in rangeCallSites) {
559 _callSites.add(new CallSite.fromMap(callSiteMap, script)); 630 _callSites.add(new CallSite.fromMap(callSiteMap, script));
560 } 631 }
561 } 632 }
562 } 633 }
563 } 634 }
564 } 635 }
(...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after
894 965
895 // Annotate locations. 966 // Annotate locations.
896 for (var location in locations) { 967 for (var location in locations) {
897 annotations.add(new LocalVariableAnnotation(location, 968 annotations.add(new LocalVariableAnnotation(location,
898 variable['value'])); 969 variable['value']));
899 } 970 }
900 } 971 }
901 } 972 }
902 } 973 }
903 974
904 int _refreshButtonTop() { 975 int _buttonTop(Element element) {
905 if (_refreshButton == null) { 976 if (element == null) {
906 return 5; 977 return 5;
907 } 978 }
908 const padding = 5; 979 const padding = 5;
909 const navbarHeight = NavBarElement.height; 980 const navbarHeight = NavBarElement.height;
910 var rect = getBoundingClientRect(); 981 var rect = getBoundingClientRect();
911 var buttonHeight = _refreshButton.clientHeight; 982 var buttonHeight = element.clientHeight;
912 return min(max(0, navbarHeight - rect.top) + padding, 983 return min(max(0, navbarHeight - rect.top) + padding,
913 rect.height - (buttonHeight + padding)); 984 rect.height - (buttonHeight + padding));
914 } 985 }
915 986
916 RefreshButtonElement _newRefreshButton() { 987 RefreshButtonElement _newRefreshButton() {
917 var button = new Element.tag('refresh-button'); 988 var button = new Element.tag('refresh-button');
918 button.style.position = 'absolute'; 989 button.style.position = 'absolute';
919 button.style.display = 'inline-block'; 990 button.style.display = 'inline-block';
920 button.style.top = '${_refreshButtonTop()}px'; 991 button.style.top = '${_buttonTop(null)}px';
921 button.style.right = '5px'; 992 button.style.right = '5px';
922 button.callback = _refresh; 993 button.callback = _refresh;
994 button.title = 'Refresh coverage';
923 return button; 995 return button;
924 } 996 }
925 997
998 ToggleButtonElement _newToggleProfileButton() {
999 ToggleButtonElement button = new Element.tag('toggle-button');
1000 button.style.position = 'absolute';
1001 button.style.display = 'inline-block';
1002 button.style.top = '${_buttonTop(null)}px';
1003 button.style.right = '30px';
1004 button.title = 'Toggle CPU profile information';
1005 final String enabledColor = 'black';
1006 final String disabledColor = 'rgba(0, 0, 0 ,.3)';
1007 button.callback = (enabled) async {
1008 _includeProfile = enabled;
1009 if (button.children.length > 0) {
1010 var content = button.children[0];
1011 if (enabled) {
1012 content.style.color = enabledColor;
1013 } else {
1014 content.style.color = disabledColor;
1015 }
1016 }
1017 await update();
1018 };
1019 button.children.add(new Element.tag('icon-whatshot'));
1020 button.children[0].style.color = disabledColor;
1021 button.enabled = _includeProfile;
1022 return button;
1023 }
1024
926 Element linesTable() { 1025 Element linesTable() {
927 var table = new DivElement(); 1026 var table = new DivElement();
928 table.classes.add("sourceTable"); 1027 table.classes.add("sourceTable");
929 1028
930 _refreshButton = _newRefreshButton(); 1029 _refreshButton = _newRefreshButton();
1030 _toggleProfileButton = _newToggleProfileButton();
931 table.append(_refreshButton); 1031 table.append(_refreshButton);
1032 table.append(_toggleProfileButton);
932 1033
933 if (_startLine == null || _endLine == null) { 1034 if (_startLine == null || _endLine == null) {
934 return table; 1035 return table;
935 } 1036 }
936 1037
937 var endLine = (endPos != null 1038 var endLine = (endPos != null
938 ? script.tokenToLine(endPos) 1039 ? script.tokenToLine(endPos)
939 : script.lines.length + script.lineOffset); 1040 : script.lines.length + script.lineOffset);
940 var lineNumPad = endLine.toString().length; 1041 var lineNumPad = endLine.toString().length;
941 1042
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
989 if (annotation.line != line) return null; 1090 if (annotation.line != line) return null;
990 annotationsCursor++; 1091 annotationsCursor++;
991 return annotation; 1092 return annotation;
992 } 1093 }
993 1094
994 Element lineElement(ScriptLine line, int lineNumPad) { 1095 Element lineElement(ScriptLine line, int lineNumPad) {
995 var e = new DivElement(); 1096 var e = new DivElement();
996 e.classes.add("sourceRow"); 1097 e.classes.add("sourceRow");
997 e.append(lineBreakpointElement(line)); 1098 e.append(lineBreakpointElement(line));
998 e.append(lineNumberElement(line, lineNumPad)); 1099 e.append(lineNumberElement(line, lineNumPad));
1100 if (_includeProfile) {
1101 e.append(lineProfileElement(line, false));
1102 e.append(lineProfileElement(line, true));
1103 }
999 e.append(lineSourceElement(line)); 1104 e.append(lineSourceElement(line));
1000 return e; 1105 return e;
1001 } 1106 }
1002 1107
1108 Element lineProfileElement(ScriptLine line, bool self) {
1109 var e = span('');
1110 e.classes.add('noCopy');
1111 if (self) {
1112 e.title = 'Self %';
1113 } else {
1114 e.title = 'Total %';
1115 }
1116
1117 if (line == null) {
1118 e.classes.add('notSourceProfile');
1119 e.text = nbsp;
1120 return e;
1121 }
1122
1123 var ranges = _rangeMap[line.line];
1124 if ((ranges == null) || ranges.isEmpty) {
1125 e.classes.add('notSourceProfile');
1126 e.text = nbsp;
1127 return e;
1128 }
1129
1130 ScriptLineProfile lineProfile = _profileMap[line.line];
1131 if (lineProfile == null) {
1132 e.classes.add('noProfile');
1133 e.text = nbsp;
1134 return e;
1135 }
1136
1137 if (self) {
1138 e.text = lineProfile.formattedSelfTicks;
1139 } else {
1140 e.text = lineProfile.formattedTotalTicks;
1141 }
1142
1143 if (lineProfile.isHot) {
1144 e.classes.add('hotProfile');
1145 } else if (lineProfile.isMedium) {
1146 e.classes.add('mediumProfile');
1147 } else {
1148 e.classes.add('coldProfile');
1149 }
1150
1151 return e;
1152 }
1153
1003 Element lineBreakpointElement(ScriptLine line) { 1154 Element lineBreakpointElement(ScriptLine line) {
1004 var e = new DivElement(); 1155 var e = new DivElement();
1005 if (line == null || !_possibleBreakpointLines.contains(line.line)) { 1156 if (line == null || !_possibleBreakpointLines.contains(line.line)) {
1006 e.classes.add('noCopy'); 1157 e.classes.add('noCopy');
1007 e.classes.add("emptyBreakpoint"); 1158 e.classes.add("emptyBreakpoint");
1008 e.text = nbsp; 1159 e.text = nbsp;
1009 return e; 1160 return e;
1010 } 1161 }
1011 1162
1012 e.text = 'B'; 1163 e.text = 'B';
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
1184 } 1335 }
1185 busy = true; 1336 busy = true;
1186 if (callback != null) { 1337 if (callback != null) {
1187 await callback(); 1338 await callback();
1188 } 1339 }
1189 busy = false; 1340 busy = false;
1190 } 1341 }
1191 } 1342 }
1192 1343
1193 1344
1345 @CustomTag('toggle-button')
1346 class ToggleButtonElement extends PolymerElement {
1347 ToggleButtonElement.created() : super.created();
1348
1349 @published var callback = null;
1350 @observable bool enabled = false;
1351
1352 Future buttonClick(var event, var b, var c) async {
1353 enabled = !enabled;
1354 if (callback != null) {
1355 await callback(enabled);
1356 }
1357 }
1358 }
1359
1360
1194 @CustomTag('source-inset') 1361 @CustomTag('source-inset')
1195 class SourceInsetElement extends PolymerElement { 1362 class SourceInsetElement extends PolymerElement {
1196 SourceInsetElement.created() : super.created(); 1363 SourceInsetElement.created() : super.created();
1197 1364
1198 @published SourceLocation location; 1365 @published SourceLocation location;
1199 @published String height = null; 1366 @published String height = null;
1200 @published int currentPos; 1367 @published int currentPos;
1201 @published bool inDebuggerContext = false; 1368 @published bool inDebuggerContext = false;
1202 @published ObservableList variables; 1369 @published ObservableList variables;
1203 @published Element scroller; 1370 @published Element scroller;
1204 } 1371 }
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