| OLD | NEW |
| 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 isolate_profile_element; | 5 library cpu_profile_element; |
| 6 | 6 |
| 7 import 'dart:html'; | 7 import 'dart:html'; |
| 8 import 'observatory_element.dart'; | 8 import 'observatory_element.dart'; |
| 9 import 'package:logging/logging.dart'; | 9 import 'package:logging/logging.dart'; |
| 10 import 'package:observatory/service.dart'; | 10 import 'package:observatory/service.dart'; |
| 11 import 'package:observatory/app.dart'; | 11 import 'package:observatory/app.dart'; |
| 12 import 'package:observatory/elements.dart'; |
| 12 import 'package:polymer/polymer.dart'; | 13 import 'package:polymer/polymer.dart'; |
| 13 | 14 |
| 14 class ProfileCodeTrieNodeTreeRow extends TableTreeRow { | 15 class ProfileCodeTrieNodeTreeRow extends TableTreeRow { |
| 15 final ServiceMap profile; | 16 final ServiceMap profile; |
| 16 @reflectable final CodeTrieNode root; | 17 @reflectable final CodeTrieNode root; |
| 17 @reflectable final CodeTrieNode node; | 18 @reflectable final CodeTrieNode node; |
| 18 @reflectable Code get code => node.code; | 19 @reflectable Code get code => node.code; |
| 19 | 20 |
| 20 @reflectable String tipKind = ''; | 21 @reflectable String tipKind = ''; |
| 21 @reflectable String tipParent = ''; | 22 @reflectable String tipParent = ''; |
| 22 @reflectable String tipExclusive = ''; | 23 @reflectable String tipExclusive = ''; |
| 23 @reflectable String tipTicks = ''; | 24 @reflectable String tipTicks = ''; |
| 24 @reflectable String tipTime = ''; | 25 @reflectable String tipTime = ''; |
| 25 | 26 |
| 26 ProfileCodeTrieNodeTreeRow(this.profile, this.root, this.node, | 27 ProfileCodeTrieNodeTreeRow(this.profile, this.root, this.node, |
| 28 TableTree tree, |
| 27 ProfileCodeTrieNodeTreeRow parent) | 29 ProfileCodeTrieNodeTreeRow parent) |
| 28 : super(parent) { | 30 : super(tree, parent) { |
| 29 assert(root != null); | 31 assert(root != null); |
| 30 assert(node != null); | 32 assert(node != null); |
| 31 tipTicks = '${node.count}'; | 33 tipTicks = '${node.count}'; |
| 32 var period = profile['period']; | 34 var period = profile['period']; |
| 33 var MICROSECONDS_PER_SECOND = 1000000.0; | 35 var MICROSECONDS_PER_SECOND = 1000000.0; |
| 34 var seconds = (period * node.count) / MICROSECONDS_PER_SECOND; // seconds | 36 var seconds = (period * node.count) / MICROSECONDS_PER_SECOND; // seconds |
| 35 tipTime = Utils.formatTimePrecise(seconds); | 37 tipTime = Utils.formatTimePrecise(seconds); |
| 36 if (code.kind == CodeKind.Tag) { | 38 if (code.kind == CodeKind.Tag) { |
| 37 tipKind = 'Tag (category)'; | 39 tipKind = 'Tag (category)'; |
| 38 if (parent == null) { | 40 if (parent == null) { |
| 39 tipParent = Utils.formatPercent(node.count, root.count); | 41 tipParent = Utils.formatPercent(node.count, root.count); |
| 40 } else { | 42 } else { |
| 41 tipParent = Utils.formatPercent(node.count, parent.node.count); | 43 tipParent = Utils.formatPercent(node.count, parent.node.count); |
| 42 } | 44 } |
| 43 tipExclusive = Utils.formatPercent(node.count, root.count); | 45 tipExclusive = Utils.formatPercent(node.count, root.count); |
| 44 } else { | 46 } else { |
| 45 if ((code.kind == CodeKind.Collected) || | 47 if ((code.kind == CodeKind.Collected) || |
| 46 (code.kind == CodeKind.Reused)) { | 48 (code.kind == CodeKind.Reused)) { |
| 47 tipKind = 'Garbage Collected Code'; | 49 tipKind = 'Garbage Collected Code'; |
| 48 } else { | 50 } else { |
| 49 tipKind = '${code.kind} (Function)'; | 51 tipKind = '${code.kind} (Function)'; |
| 50 } | 52 } |
| 51 if (parent == null) { | 53 if (parent == null) { |
| 52 tipParent = Utils.formatPercent(node.count, root.count); | 54 tipParent = Utils.formatPercent(node.count, root.count); |
| 53 } else { | 55 } else { |
| 54 tipParent = Utils.formatPercent(node.count, parent.node.count); | 56 tipParent = Utils.formatPercent(node.count, parent.node.count); |
| 55 } | 57 } |
| 56 tipExclusive = Utils.formatPercent(node.code.exclusiveTicks, root.count); | 58 tipExclusive = Utils.formatPercent(node.code.exclusiveTicks, root.count); |
| 57 } | 59 } |
| 58 columns.add(tipParent); | |
| 59 columns.add(tipExclusive); | |
| 60 } | 60 } |
| 61 | 61 |
| 62 bool shouldDisplayChild(CodeTrieNode childNode, double threshold) { | 62 bool shouldDisplayChild(CodeTrieNode childNode, double threshold) { |
| 63 return ((childNode.count / node.count) > threshold) || | 63 return ((childNode.count / node.count) > threshold) || |
| 64 ((childNode.code.exclusiveTicks / root.count) > threshold); | 64 ((childNode.code.exclusiveTicks / root.count) > threshold); |
| 65 } | 65 } |
| 66 | 66 |
| 67 void onShow() { | 67 void _buildTooltip(DivElement memberList, Map<String, String> items) { |
| 68 var threshold = profile['threshold']; | 68 items.forEach((k, v) { |
| 69 if (children.length > 0) { | 69 var item = new DivElement(); |
| 70 // Child rows already created. | 70 item.classes.add('memberItem'); |
| 71 return; | 71 var name = new DivElement(); |
| 72 } | 72 name.classes.add('memberName'); |
| 73 for (var childNode in node.children) { | 73 name.classes.add('white'); |
| 74 if (!shouldDisplayChild(childNode, threshold)) { | 74 name.text = k; |
| 75 continue; | 75 var value = new DivElement(); |
| 76 } | 76 value.classes.add('memberValue'); |
| 77 var row = new ProfileCodeTrieNodeTreeRow(profile, root, childNode, this); | 77 value.classes.add('white'); |
| 78 children.add(row); | 78 value.text = v; |
| 79 } | 79 item.children.add(name); |
| 80 item.children.add(value); |
| 81 memberList.children.add(item); |
| 82 }); |
| 80 } | 83 } |
| 81 | 84 |
| 82 void onHide() { | 85 void onShow() { |
| 86 super.onShow(); |
| 87 if (children.length == 0) { |
| 88 var threshold = profile['threshold']; |
| 89 for (var childNode in node.children) { |
| 90 if (!shouldDisplayChild(childNode, threshold)) { |
| 91 continue; |
| 92 } |
| 93 var row = |
| 94 new ProfileCodeTrieNodeTreeRow(profile, root, childNode, tree, this)
; |
| 95 children.add(row); |
| 96 } |
| 97 } |
| 98 var row = tr; |
| 99 |
| 100 var methodCell = tableColumns[0]; |
| 101 // Enable expansion by clicking anywhere on the method column. |
| 102 methodCell.onClick.listen(onClick); |
| 103 |
| 104 // Insert the parent percentage |
| 105 var parentPercent = new DivElement(); |
| 106 parentPercent.style.position = 'relative'; |
| 107 parentPercent.style.display = 'inline'; |
| 108 parentPercent.text = tipParent; |
| 109 methodCell.children.add(parentPercent); |
| 110 |
| 111 var codeRef = new Element.tag('code-ref'); |
| 112 codeRef.ref = code; |
| 113 methodCell.children.add(codeRef); |
| 114 |
| 115 var selfCell = tableColumns[1]; |
| 116 selfCell.style.position = 'relative'; |
| 117 selfCell.text = tipExclusive; |
| 118 |
| 119 var tooltipDiv = new DivElement(); |
| 120 tooltipDiv.classes.add('tooltip'); |
| 121 |
| 122 var memberListDiv = new DivElement(); |
| 123 memberListDiv.classes.add('memberList'); |
| 124 tooltipDiv.children.add(memberListDiv); |
| 125 _buildTooltip(memberListDiv, { |
| 126 'Kind' : tipKind, |
| 127 'Percent of Parent' : tipParent, |
| 128 'Sample Count' : tipTicks, |
| 129 'Approximate Execution Time': tipTime, |
| 130 }); |
| 131 selfCell.children.add(tooltipDiv); |
| 83 } | 132 } |
| 84 | 133 |
| 85 bool hasChildren() { | 134 bool hasChildren() { |
| 86 return node.children.length > 0; | 135 return node.children.length > 0; |
| 87 } | 136 } |
| 88 } | 137 } |
| 89 | 138 |
| 90 /// Displays an IsolateProfile | 139 /// Displays a CpuProfile |
| 91 @CustomTag('isolate-profile') | 140 @CustomTag('cpu-profile') |
| 92 class IsolateProfileElement extends ObservatoryElement { | 141 class CpuProfileElement extends ObservatoryElement { |
| 93 IsolateProfileElement.created() : super.created(); | 142 CpuProfileElement.created() : super.created(); |
| 94 @published ServiceMap profile; | 143 @published ServiceMap profile; |
| 95 @observable bool hideTagsChecked; | 144 @observable bool hideTagsChecked; |
| 96 @observable String sampleCount = ''; | 145 @observable String sampleCount = ''; |
| 97 @observable String refreshTime = ''; | 146 @observable String refreshTime = ''; |
| 98 @observable String sampleRate = ''; | 147 @observable String sampleRate = ''; |
| 99 @observable String sampleDepth = ''; | 148 @observable String sampleDepth = ''; |
| 100 @observable String displayCutoff = ''; | 149 @observable String displayCutoff = ''; |
| 101 @observable String timeSpan = ''; | 150 @observable String timeSpan = ''; |
| 102 @reflectable double displayThreshold = 0.0002; // 0.02%. | 151 @reflectable double displayThreshold = 0.0002; // 0.02%. |
| 103 | 152 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 123 displayCutoff = '${(displayThreshold * 100.0).toString()}%'; | 172 displayCutoff = '${(displayThreshold * 100.0).toString()}%'; |
| 124 profile.isolate.processProfile(profile); | 173 profile.isolate.processProfile(profile); |
| 125 profile['threshold'] = displayThreshold; | 174 profile['threshold'] = displayThreshold; |
| 126 _update(); | 175 _update(); |
| 127 } | 176 } |
| 128 | 177 |
| 129 | 178 |
| 130 @override | 179 @override |
| 131 void attached() { | 180 void attached() { |
| 132 super.attached(); | 181 super.attached(); |
| 133 tree = new TableTree(); | 182 var tableBody = shadowRoot.querySelector('#tableTreeBody'); |
| 183 assert(tableBody != null); |
| 184 tree = new TableTree(tableBody, 2); |
| 134 _update(); | 185 _update(); |
| 135 } | 186 } |
| 136 | 187 |
| 137 void tagSelectorChanged(oldValue) { | 188 void tagSelectorChanged(oldValue) { |
| 138 refresh(null); | 189 refresh(null); |
| 139 } | 190 } |
| 140 | 191 |
| 141 void refresh(var done) { | 192 void refresh(var done) { |
| 142 var request = 'profile?tags=$tagSelector'; | 193 var request = 'profile?tags=$tagSelector'; |
| 143 profile.isolate.get(request).then((ServiceMap m) { | 194 profile.isolate.get(request).then((ServiceMap m) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 154 _buildTree(); | 205 _buildTree(); |
| 155 } | 206 } |
| 156 | 207 |
| 157 void _buildStackTree() { | 208 void _buildStackTree() { |
| 158 var root = profile.isolate.profileTrieRoot; | 209 var root = profile.isolate.profileTrieRoot; |
| 159 if (root == null) { | 210 if (root == null) { |
| 160 return; | 211 return; |
| 161 } | 212 } |
| 162 try { | 213 try { |
| 163 tree.initialize( | 214 tree.initialize( |
| 164 new ProfileCodeTrieNodeTreeRow(profile, root, root, null)); | 215 new ProfileCodeTrieNodeTreeRow(profile, root, root, tree, null)); |
| 165 } catch (e, stackTrace) { | 216 } catch (e, stackTrace) { |
| 217 print(e); |
| 218 print(stackTrace); |
| 166 Logger.root.warning('_buildStackTree', e, stackTrace); | 219 Logger.root.warning('_buildStackTree', e, stackTrace); |
| 167 } | 220 } |
| 168 // Check if we only have one node at the root and expand it. | 221 // Check if we only have one node at the root and expand it. |
| 169 if (tree.rows.length == 1) { | 222 if (tree.rows.length == 1) { |
| 170 tree.toggle(0); | 223 tree.toggle(tree.rows[0]); |
| 171 } | 224 } |
| 172 notifyPropertyChange(#tree, null, tree); | 225 notifyPropertyChange(#tree, null, tree); |
| 173 } | 226 } |
| 174 | 227 |
| 175 void _buildTree() { | 228 void _buildTree() { |
| 176 _buildStackTree(); | 229 _buildStackTree(); |
| 177 } | 230 } |
| 178 | |
| 179 @observable String padding(TableTreeRow row) { | |
| 180 return 'padding-left: ${row.depth * 16}px;'; | |
| 181 } | |
| 182 | |
| 183 @observable String coloring(TableTreeRow row) { | |
| 184 const colors = const ['rowColor0', 'rowColor1', 'rowColor2', 'rowColor3', | |
| 185 'rowColor4', 'rowColor5', 'rowColor6', 'rowColor7', | |
| 186 'rowColor8']; | |
| 187 var index = (row.depth - 1) % colors.length; | |
| 188 return colors[index]; | |
| 189 } | |
| 190 | |
| 191 @observable void toggleExpanded(Event e, var detail, Element target) { | |
| 192 // We only want to expand a tree row if the target of the click is | |
| 193 // the table cell (passed in as target) or the span containing the | |
| 194 // expander symbol (#expand). | |
| 195 var eventTarget = e.target; | |
| 196 if ((eventTarget.id != 'expand') && (e.target != target)) { | |
| 197 // Target of click was not the expander span or the table cell. | |
| 198 return; | |
| 199 } | |
| 200 var row = target.parent; | |
| 201 if (row is TableRowElement) { | |
| 202 // Subtract 1 to get 0 based indexing. | |
| 203 try { | |
| 204 tree.toggle(row.rowIndex - 1); | |
| 205 } catch (e, stackTrace) { | |
| 206 Logger.root.warning('toggleExpanded', e, stackTrace); | |
| 207 } | |
| 208 } | |
| 209 } | |
| 210 } | 231 } |
| OLD | NEW |