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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | runtime/observatory/lib/src/elements/script_inset.html » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/observatory/lib/src/elements/script_inset.dart
diff --git a/runtime/observatory/lib/src/elements/script_inset.dart b/runtime/observatory/lib/src/elements/script_inset.dart
index aa3c0a832024d7f1eb4bd5f4f08c26a4b97f2383..6528c8ddd6e7a49a9a702202d4fd924303ffa342 100644
--- a/runtime/observatory/lib/src/elements/script_inset.dart
+++ b/runtime/observatory/lib/src/elements/script_inset.dart
@@ -361,6 +361,42 @@ class FunctionDeclarationAnnotation extends DeclarationAnnotation {
}
}
+class ScriptLineProfile {
+ ScriptLineProfile(this.line, this.sampleCount);
+
+ static const kHotThreshold = 0.05; // 5%.
+ static const kMediumThreshold = 0.02; // 2%.
+
+ final int line;
+ final int sampleCount;
+
+ int selfTicks = 0;
+ int totalTicks = 0;
+
+ void process(int exclusive, int inclusive) {
+ selfTicks += exclusive;
+ totalTicks += inclusive;
+ }
+
+ String get formattedSelfTicks {
+ return Utils.formatPercent(selfTicks, sampleCount);
+ }
+
+ String get formattedTotalTicks {
+ return Utils.formatPercent(totalTicks, sampleCount);
+ }
+
+ double _percent() {
+ if (sampleCount == 0) {
+ return 0.0;
+ }
+ return totalTicks / sampleCount;
+ }
+
+ bool get isHot => _percent() > kHotThreshold;
+ bool get isMedium => _percent() > kMediumThreshold;
+}
+
/// Box with script source code in it.
@CustomTag('script-inset')
class ScriptInsetElement extends ObservatoryElement {
@@ -378,6 +414,7 @@ class ScriptInsetElement extends ObservatoryElement {
@published Element scroller;
RefreshButtonElement _refreshButton;
+ ToggleButtonElement _toggleProfileButton;
int _currentLine;
int _currentCol;
@@ -387,6 +424,7 @@ class ScriptInsetElement extends ObservatoryElement {
Map<int, List<ServiceMap>> _rangeMap = {};
Set _callSites = new Set<CallSite>();
Set _possibleBreakpointLines = new Set<int>();
+ Map<int, ScriptLineProfile> _profileMap = {};
var annotations = [];
var annotationsCursor;
@@ -396,6 +434,7 @@ class ScriptInsetElement extends ObservatoryElement {
StreamSubscription _scrollSubscription;
bool hasLoadedLibraryDeclarations = false;
+ bool _includeProfile = false;
String makeLineId(int line) {
return 'line-$line';
@@ -435,13 +474,17 @@ class ScriptInsetElement extends ObservatoryElement {
}
void _onScroll(event) {
- if (_refreshButton == null) {
- return;
+ if (_refreshButton != null) {
+ var newTop = _buttonTop(_refreshButton);
+ if (_refreshButton.style.top != newTop) {
+ _refreshButton.style.top = '${newTop}px';
+ }
}
- var currentTop = _refreshButton.style.top;
- var newTop = _refreshButtonTop();
- if (currentTop != newTop) {
- _refreshButton.style.top = '${newTop}px';
+ if (_toggleProfileButton != null) {
+ var newTop = _buttonTop(_toggleProfileButton);
+ if (_toggleProfileButton.style.top != newTop) {
+ _toggleProfileButton.style.top = '${newTop}px';
+ }
}
}
@@ -535,12 +578,18 @@ class ScriptInsetElement extends ObservatoryElement {
// Build _rangeMap and _callSites from a source report.
Future _refreshSourceReport() async {
+ var reports = [Isolate.kCallSitesReport,
+ Isolate.kPossibleBreakpointsReport];
+ if (_includeProfile) {
+ reports.add(Isolate.kProfileReport);
+ }
var sourceReport = await script.isolate.getSourceReport(
- [Isolate.kCallSitesReport, Isolate.kPossibleBreakpointsReport],
+ reports,
script, startPos, endPos);
_possibleBreakpointLines = getPossibleBreakpointLines(sourceReport, script);
_rangeMap.clear();
_callSites.clear();
+ _profileMap.clear();
for (var range in sourceReport['ranges']) {
int startLine = script.tokenToLine(range['startPos']);
int endLine = script.tokenToLine(range['endPos']);
@@ -552,6 +601,28 @@ class ScriptInsetElement extends ObservatoryElement {
rangeList.add(range);
}
}
+ if (_includeProfile && range['profile'] != null) {
+ List positions = range['profile']['positions'];
+ List exclusiveTicks = range['profile']['exclusiveTicks'];
+ List inclusiveTicks = range['profile']['inclusiveTicks'];
+ int sampleCount = range['profile']['metadata']['sampleCount'];
+ assert(positions.length == exclusiveTicks.length);
+ assert(positions.length == inclusiveTicks.length);
+ for (int i = 0; i < positions.length; i++) {
+ if (positions[i] is String) {
+ // String positions are classifying token positions.
+ // TODO(johnmccutchan): Add classifier data to UI.
+ continue;
+ }
+ int line = script.tokenToLine(positions[i]);
+ ScriptLineProfile lineProfile = _profileMap[line];
+ if (lineProfile == null) {
+ lineProfile = new ScriptLineProfile(line, sampleCount);
+ _profileMap[line] = lineProfile;
+ }
+ lineProfile.process(exclusiveTicks[i], inclusiveTicks[i]);
+ }
+ }
if (range['compiled']) {
var rangeCallSites = range['callSites'];
if (rangeCallSites != null) {
@@ -901,14 +972,14 @@ class ScriptInsetElement extends ObservatoryElement {
}
}
- int _refreshButtonTop() {
- if (_refreshButton == null) {
+ int _buttonTop(Element element) {
+ if (element == null) {
return 5;
}
const padding = 5;
const navbarHeight = NavBarElement.height;
var rect = getBoundingClientRect();
- var buttonHeight = _refreshButton.clientHeight;
+ var buttonHeight = element.clientHeight;
return min(max(0, navbarHeight - rect.top) + padding,
rect.height - (buttonHeight + padding));
}
@@ -917,9 +988,37 @@ class ScriptInsetElement extends ObservatoryElement {
var button = new Element.tag('refresh-button');
button.style.position = 'absolute';
button.style.display = 'inline-block';
- button.style.top = '${_refreshButtonTop()}px';
+ button.style.top = '${_buttonTop(null)}px';
button.style.right = '5px';
button.callback = _refresh;
+ button.title = 'Refresh coverage';
+ return button;
+ }
+
+ ToggleButtonElement _newToggleProfileButton() {
+ ToggleButtonElement button = new Element.tag('toggle-button');
+ button.style.position = 'absolute';
+ button.style.display = 'inline-block';
+ button.style.top = '${_buttonTop(null)}px';
+ button.style.right = '30px';
+ button.title = 'Toggle CPU profile information';
+ final String enabledColor = 'black';
+ final String disabledColor = 'rgba(0, 0, 0 ,.3)';
+ button.callback = (enabled) async {
+ _includeProfile = enabled;
+ if (button.children.length > 0) {
+ var content = button.children[0];
+ if (enabled) {
+ content.style.color = enabledColor;
+ } else {
+ content.style.color = disabledColor;
+ }
+ }
+ await update();
+ };
+ button.children.add(new Element.tag('icon-whatshot'));
+ button.children[0].style.color = disabledColor;
+ button.enabled = _includeProfile;
return button;
}
@@ -928,7 +1027,9 @@ class ScriptInsetElement extends ObservatoryElement {
table.classes.add("sourceTable");
_refreshButton = _newRefreshButton();
+ _toggleProfileButton = _newToggleProfileButton();
table.append(_refreshButton);
+ table.append(_toggleProfileButton);
if (_startLine == null || _endLine == null) {
return table;
@@ -996,10 +1097,60 @@ class ScriptInsetElement extends ObservatoryElement {
e.classes.add("sourceRow");
e.append(lineBreakpointElement(line));
e.append(lineNumberElement(line, lineNumPad));
+ if (_includeProfile) {
+ e.append(lineProfileElement(line, false));
+ e.append(lineProfileElement(line, true));
+ }
e.append(lineSourceElement(line));
return e;
}
+ Element lineProfileElement(ScriptLine line, bool self) {
+ var e = span('');
+ e.classes.add('noCopy');
+ if (self) {
+ e.title = 'Self %';
+ } else {
+ e.title = 'Total %';
+ }
+
+ if (line == null) {
+ e.classes.add('notSourceProfile');
+ e.text = nbsp;
+ return e;
+ }
+
+ var ranges = _rangeMap[line.line];
+ if ((ranges == null) || ranges.isEmpty) {
+ e.classes.add('notSourceProfile');
+ e.text = nbsp;
+ return e;
+ }
+
+ ScriptLineProfile lineProfile = _profileMap[line.line];
+ if (lineProfile == null) {
+ e.classes.add('noProfile');
+ e.text = nbsp;
+ return e;
+ }
+
+ if (self) {
+ e.text = lineProfile.formattedSelfTicks;
+ } else {
+ e.text = lineProfile.formattedTotalTicks;
+ }
+
+ if (lineProfile.isHot) {
+ e.classes.add('hotProfile');
+ } else if (lineProfile.isMedium) {
+ e.classes.add('mediumProfile');
+ } else {
+ e.classes.add('coldProfile');
+ }
+
+ return e;
+ }
+
Element lineBreakpointElement(ScriptLine line) {
var e = new DivElement();
if (line == null || !_possibleBreakpointLines.contains(line.line)) {
@@ -1191,6 +1342,22 @@ class RefreshButtonElement extends PolymerElement {
}
+@CustomTag('toggle-button')
+class ToggleButtonElement extends PolymerElement {
+ ToggleButtonElement.created() : super.created();
+
+ @published var callback = null;
+ @observable bool enabled = false;
+
+ Future buttonClick(var event, var b, var c) async {
+ enabled = !enabled;
+ if (callback != null) {
+ await callback(enabled);
+ }
+ }
+}
+
+
@CustomTag('source-inset')
class SourceInsetElement extends PolymerElement {
SourceInsetElement.created() : super.created();
« 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