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

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

Issue 2279973002: Converted Observatory heap-map element (Closed)
Patch Set: Fixed tests Created 4 years, 3 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
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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 heap_map_element; 5 library heap_map_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 'package:observatory/models.dart' as M;
11 import 'package:observatory/service.dart'; 11 import 'package:observatory/service.dart' as S;
12 import 'package:logging/logging.dart'; 12 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
13 import 'package:polymer/polymer.dart'; 13 import 'package:observatory/src/elements/helpers/tag.dart';
14 import 'package:observatory/src/elements/helpers/uris.dart';
15 import 'package:observatory/src/elements/nav/bar.dart';
16 import 'package:observatory/src/elements/nav/isolate_menu.dart';
17 import 'package:observatory/src/elements/nav/menu.dart';
18 import 'package:observatory/src/elements/nav/notify.dart';
19 import 'package:observatory/src/elements/nav/refresh.dart';
20 import 'package:observatory/src/elements/nav/top_menu.dart';
21 import 'package:observatory/src/elements/nav/vm_menu.dart';
22 class HeapMapElement extends HtmlElement implements Renderable {
23 static const tag = const Tag<HeapMapElement>('heap-map',
24 dependencies: const [
25 NavBarElement.tag,
26 NavTopMenuElement.tag,
27 NavVMMenuElement.tag,
28 NavIsolateMenuElement.tag,
29 NavMenuElement.tag,
30 NavRefreshElement.tag,
31 NavNotifyElement.tag,
32 ]);
14 33
15 // A reference to a particular pixel of ImageData. 34 RenderingScheduler<HeapMapElement> _r;
16 class PixelReference {
17 final _data;
18 var _dataIndex;
19 static const NUM_COLOR_COMPONENTS = 4;
20 35
21 PixelReference(ImageData data, Point<int> point) 36 Stream<RenderedEvent<HeapMapElement>> get onRendered =>
22 : _data = data, 37 _r.onRendered;
23 _dataIndex = (point.y * data.width + point.x) * NUM_COLOR_COMPONENTS;
24 38
25 PixelReference._fromDataIndex(this._data, this._dataIndex); 39 M.VM _vm;
40 M.IsolateRef _isolate;
41 M.EventRepository _events;
42 M.NotificationRepository _notifications;
43 M.VMRef get vm => _vm;
44 M.IsolateRef get isolate => _isolate;
45 M.NotificationRepository get notifications => _notifications;
26 46
27 Point<int> get point => 47 factory HeapMapElement(M.VM vm, M.IsolateRef isolate,
28 new Point(index % _data.width, index ~/ _data.width); 48 M.EventRepository events,
29 49 M.NotificationRepository notifications,
30 void set color(Iterable<int> color) { 50 {RenderingQueue queue}) {
31 _data.data.setRange( 51 assert(vm != null);
32 _dataIndex, _dataIndex + NUM_COLOR_COMPONENTS, color); 52 assert(isolate != null);
53 assert(events != null);
54 assert(notifications != null);
55 HeapMapElement e = document.createElement(tag.name);
56 e._r = new RenderingScheduler(e, queue: queue);
57 e._vm = vm;
58 e._isolate = isolate;
59 e._events = events;
60 e._notifications = notifications;
61 return e;
33 } 62 }
34 63
35 Iterable<int> get color => 64 HeapMapElement.created() : super.created();
36 _data.data.getRange(_dataIndex, _dataIndex + NUM_COLOR_COMPONENTS);
37 65
38 // Returns the next pixel in row-major order. 66 @override
39 PixelReference next() => new PixelReference._fromDataIndex( 67 attached() {
40 _data, _dataIndex + NUM_COLOR_COMPONENTS); 68 super.attached();
69 _r.enable();
70 _refresh();
71 }
41 72
42 // The row-major index of this pixel. 73 @override
43 int get index => _dataIndex ~/ NUM_COLOR_COMPONENTS; 74 detached() {
44 } 75 super.detached();
76 _r.disable(notify: true);
77 children = [];
78 }
45 79
46 class ObjectInfo { 80 CanvasElement _canvas;
47 final address;
48 final size;
49 ObjectInfo(this.address, this.size);
50 }
51
52 @CustomTag('heap-map')
53 class HeapMapElement extends ObservatoryElement {
54 CanvasElement _fragmentationCanvas;
55 var _fragmentationData; 81 var _fragmentationData;
56 var _pageHeight; 82 double _pageHeight;
57 var _classIdToColor = {}; 83 final _classIdToColor = {};
58 var _colorToClassId = {}; 84 final _colorToClassId = {};
59 var _classIdToName = {}; 85 final _classIdToName = {};
60 86
61 static final _freeColor = [255, 255, 255, 255]; 87 static final _freeColor = [255, 255, 255, 255];
62 static final _pageSeparationColor = [0, 0, 0, 255]; 88 static final _pageSeparationColor = [0, 0, 0, 255];
63 static const _PAGE_SEPARATION_HEIGHT = 4; 89 static const _PAGE_SEPARATION_HEIGHT = 4;
64 // Many browsers will not display a very tall canvas. 90 // Many browsers will not display a very tall canvas.
65 // TODO(koda): Improve interface for huge heaps. 91 // TODO(koda): Improve interface for huge heaps.
66 static const _MAX_CANVAS_HEIGHT = 6000; 92 static const _MAX_CANVAS_HEIGHT = 6000;
67 93
68 @observable String status; 94 String _status;
69 @published Isolate isolate; 95 S.ServiceMap _fragmentation;
70 @observable ServiceMap fragmentation;
71 96
72 HeapMapElement.created() : super.created() { 97 void render() {
73 } 98 if (_canvas == null) {
74 99 _canvas = new CanvasElement()
75 @override 100 ..width = 1
76 void attached() { 101 ..height= 1
77 super.attached(); 102 ..onMouseMove.listen(_handleMouseMove)
78 _fragmentationCanvas = shadowRoot.querySelector("#fragmentation"); 103 ..onMouseDown.listen(_handleClick);
79 _fragmentationCanvas.onMouseMove.listen(_handleMouseMove); 104 }
80 _fragmentationCanvas.onMouseDown.listen(_handleClick); 105 children = [
106 new NavBarElement(queue: _r.queue)
107 ..children = [
108 new NavTopMenuElement(queue: _r.queue),
109 new NavVMMenuElement(_vm, _events, queue: _r.queue),
110 new NavIsolateMenuElement(_isolate, _events, queue: _r.queue),
111 new NavMenuElement('heap map', last: true,
112 link: Uris.heapMap(_isolate), queue: _r.queue),
113 new NavRefreshElement(queue: _r.queue)
114 ..onRefresh.listen((_) => _refresh()),
115 new NavNotifyElement(_notifications, queue: _r.queue)
116 ],
117 new DivElement()..classes = const ['content-centered-big']
118 ..children = [
119 new HeadingElement.h2()..text = _status,
120 new HRElement(),
121 ],
122 new DivElement()..classes = ['flex-row']
123 ..children = [_canvas]
124 ];
81 } 125 }
82 126
83 // Encode color as single integer, to enable using it as a map key. 127 // Encode color as single integer, to enable using it as a map key.
84 int _packColor(Iterable<int> color) { 128 int _packColor(Iterable<int> color) {
85 int packed = 0; 129 int packed = 0;
86 for (var component in color) { 130 for (var component in color) {
87 packed = packed * 256 + component; 131 packed = packed * 256 + component;
88 } 132 }
89 return packed; 133 return packed;
90 } 134 }
91 135
92 void _addClass(int classId, String name, Iterable<int> color) { 136 void _addClass(int classId, String name, Iterable<int> color) {
93 _classIdToName[classId] = name.split('@')[0]; 137 _classIdToName[classId] = name.split('@')[0];
94 _classIdToColor[classId] = color; 138 _classIdToColor[classId] = color;
95 _colorToClassId[_packColor(color)] = classId; 139 _colorToClassId[_packColor(color)] = classId;
96 } 140 }
97 141
98 void _updateClassList(classList, int freeClassId) { 142 void _updateClassList(classList, int freeClassId) {
99 for (var member in classList['classes']) { 143 for (var member in classList['classes']) {
100 if (member is! Class) { 144 if (member is! S.Class) {
101 // TODO(turnidge): The printing for some of these non-class 145 // TODO(turnidge): The printing for some of these non-class
102 // members is broken. Fix this: 146 // members is broken. Fix this:
103 // 147 //
104 // Logger.root.info('$member'); 148 // Logger.root.info('$member');
105 Logger.root.info('Ignoring non-class in class list'); 149 print('Ignoring non-class in class list');
106 continue; 150 continue;
107 } 151 }
108 var classId = int.parse(member.id.split('/').last); 152 var classId = int.parse(member.id.split('/').last);
109 var color = _classIdToRGBA(classId); 153 var color = _classIdToRGBA(classId);
110 _addClass(classId, member.name, color); 154 _addClass(classId, member.name, color);
111 } 155 }
112 _addClass(freeClassId, 'Free', _freeColor); 156 _addClass(freeClassId, 'Free', _freeColor);
113 _addClass(0, '', _pageSeparationColor); 157 _addClass(0, '', _pageSeparationColor);
114 } 158 }
115 159
116 Iterable<int> _classIdToRGBA(int classId) { 160 Iterable<int> _classIdToRGBA(int classId) {
117 // TODO(koda): Pick random hue, but fixed saturation and value. 161 // TODO(koda): Pick random hue, but fixed saturation and value.
118 var rng = new Random(classId); 162 var rng = new Random(classId);
119 return [rng.nextInt(128), rng.nextInt(128), rng.nextInt(128), 255]; 163 return [rng.nextInt(128), rng.nextInt(128), rng.nextInt(128), 255];
120 } 164 }
121 165
122 String _classNameAt(Point<int> point) { 166 String _classNameAt(Point<int> point) {
123 var color = new PixelReference(_fragmentationData, point).color; 167 var color = new PixelReference(_fragmentationData, point).color;
124 return _classIdToName[_colorToClassId[_packColor(color)]]; 168 return _classIdToName[_colorToClassId[_packColor(color)]];
125 } 169 }
126 170
127 ObjectInfo _objectAt(Point<int> point) { 171 ObjectInfo _objectAt(Point<int> point) {
128 if (fragmentation == null || _fragmentationCanvas == null) { 172 if (_fragmentation == null || _canvas == null) {
129 return null; 173 return null;
130 } 174 }
131 var pagePixels = _pageHeight * _fragmentationData.width; 175 var pagePixels = _pageHeight * _fragmentationData.width;
132 var index = new PixelReference(_fragmentationData, point).index; 176 var index = new PixelReference(_fragmentationData, point).index;
133 var pageIndex = index ~/ pagePixels; 177 var pageIndex = index ~/ pagePixels;
134 var pageOffset = index % pagePixels; 178 var pageOffset = index % pagePixels;
135 var pages = fragmentation['pages']; 179 var pages = _fragmentation['pages'];
136 if (pageIndex < 0 || pageIndex >= pages.length) { 180 if (pageIndex < 0 || pageIndex >= pages.length) {
137 return null; 181 return null;
138 } 182 }
139 // Scan the page to find start and size. 183 // Scan the page to find start and size.
140 var page = pages[pageIndex]; 184 var page = pages[pageIndex];
141 var objects = page['objects']; 185 var objects = page['objects'];
142 var offset = 0; 186 var offset = 0;
143 var size = 0; 187 var size = 0;
144 for (var i = 0; i < objects.length; i += 2) { 188 for (var i = 0; i < objects.length; i += 2) {
145 size = objects[i]; 189 size = objects[i];
146 offset += size; 190 offset += size;
147 if (offset > pageOffset) { 191 if (offset > pageOffset) {
148 pageOffset = offset - size; 192 pageOffset = offset - size;
149 break; 193 break;
150 } 194 }
151 } 195 }
152 return new ObjectInfo(int.parse(page['objectStart']) + 196 return new ObjectInfo(int.parse(page['objectStart']) +
153 pageOffset * fragmentation['unitSizeBytes'], 197 pageOffset * _fragmentation['unitSizeBytes'],
154 size * fragmentation['unitSizeBytes']); 198 size * _fragmentation['unitSizeBytes']);
155 } 199 }
156 200
157 void _handleMouseMove(MouseEvent event) { 201 void _handleMouseMove(MouseEvent event) {
158 var info = _objectAt(event.offset); 202 var info = _objectAt(event.offset);
159 if (info == null) { 203 if (info == null) {
160 status = ''; 204 _status = '';
205 _r.dirty();
161 return; 206 return;
162 } 207 }
163 var addressString = '${info.size}B @ 0x${info.address.toRadixString(16)}'; 208 var addressString = '${info.size}B @ 0x${info.address.toRadixString(16)}';
164 var className = _classNameAt(event.offset); 209 var className = _classNameAt(event.offset);
165 status = (className == '') ? '-' : '$className $addressString'; 210 _status = (className == '') ? '-' : '$className $addressString';
211 _r.dirty();
166 } 212 }
167 213
168 void _handleClick(MouseEvent event) { 214 void _handleClick(MouseEvent event) {
169 var address = _objectAt(event.offset).address.toRadixString(16); 215 final isolate = _isolate as S.Isolate;
216 final address = _objectAt(event.offset).address.toRadixString(16);
170 isolate.getObjectByAddress(address).then((result) { 217 isolate.getObjectByAddress(address).then((result) {
171 if (result.type != 'Sentinel') { 218 if (result.type != 'Sentinel') {
172 app.locationManager.go(gotoLink('/inspect', result)); 219 new AnchorElement(
220 href: Uris.inspect(_isolate, object: result as S.HeapObject)
221 ).click();
173 } 222 }
174 }); 223 });
175 } 224 }
176 225
177 void _updateFragmentationData() { 226 void _updateFragmentationData() {
178 if (fragmentation == null || _fragmentationCanvas == null) { 227 if (_fragmentation == null || _canvas == null) {
179 return; 228 return;
180 } 229 }
181 _updateClassList( 230 _updateClassList(
182 fragmentation['classList'], fragmentation['freeClassId']); 231 _fragmentation['classList'], _fragmentation['freeClassId']);
183 var pages = fragmentation['pages']; 232 var pages = _fragmentation['pages'];
184 var width = _fragmentationCanvas.parent.client.width; 233 var width = _canvas.parent.client.width;
185 _pageHeight = _PAGE_SEPARATION_HEIGHT + 234 _pageHeight = _PAGE_SEPARATION_HEIGHT +
186 fragmentation['pageSizeBytes'] ~/ 235 _fragmentation['pageSizeBytes'] ~/
187 fragmentation['unitSizeBytes'] ~/ width; 236 _fragmentation['unitSizeBytes'] ~/ width;
188 var height = min(_pageHeight * pages.length, _MAX_CANVAS_HEIGHT); 237 var height = min(_pageHeight * pages.length, _MAX_CANVAS_HEIGHT);
189 _fragmentationData = 238 _fragmentationData =
190 _fragmentationCanvas.context2D.createImageData(width, height); 239 _canvas.context2D.createImageData(width, height);
191 _fragmentationCanvas.width = _fragmentationData.width; 240 _canvas.width = _fragmentationData.width;
192 _fragmentationCanvas.height = _fragmentationData.height; 241 _canvas.height = _fragmentationData.height;
193 _renderPages(0); 242 _renderPages(0);
194 } 243 }
195 244
196 // Renders and draws asynchronously, one page at a time to avoid 245 // Renders and draws asynchronously, one page at a time to avoid
197 // blocking the UI. 246 // blocking the UI.
198 void _renderPages(int startPage) { 247 void _renderPages(int startPage) {
199 var pages = fragmentation['pages']; 248 var pages = _fragmentation['pages'];
200 status = 'Loaded $startPage of ${pages.length} pages'; 249 _status = 'Loaded $startPage of ${pages.length} pages';
250 _r.dirty();
201 var startY = startPage * _pageHeight; 251 var startY = startPage * _pageHeight;
202 var endY = startY + _pageHeight; 252 var endY = startY + _pageHeight;
203 if (startPage >= pages.length || endY > _fragmentationData.height) { 253 if (startPage >= pages.length || endY > _fragmentationData.height) {
204 return; 254 return;
205 } 255 }
206 var pixel = new PixelReference(_fragmentationData, new Point(0, startY)); 256 var pixel = new PixelReference(_fragmentationData, new Point(0, startY));
207 var objects = pages[startPage]['objects']; 257 var objects = pages[startPage]['objects'];
208 for (var i = 0; i < objects.length; i += 2) { 258 for (var i = 0; i < objects.length; i += 2) {
209 var count = objects[i]; 259 var count = objects[i];
210 var classId = objects[i + 1]; 260 var classId = objects[i + 1];
211 var color = _classIdToColor[classId]; 261 var color = _classIdToColor[classId];
212 while (count-- > 0) { 262 while (count-- > 0) {
213 pixel.color = color; 263 pixel.color = color;
214 pixel = pixel.next(); 264 pixel = pixel.next();
215 } 265 }
216 } 266 }
217 while (pixel.point.y < endY) { 267 while (pixel.point.y < endY) {
218 pixel.color = _pageSeparationColor; 268 pixel.color = _pageSeparationColor;
219 pixel = pixel.next(); 269 pixel = pixel.next();
220 } 270 }
221 _fragmentationCanvas.context2D.putImageData( 271 _canvas.context2D.putImageData(
222 _fragmentationData, 0, 0, 0, startY, _fragmentationData.width, endY); 272 _fragmentationData, 0, 0, 0, startY, _fragmentationData.width, endY);
223 // Continue with the next page, asynchronously. 273 // Continue with the next page, asynchronously.
224 new Future(() { 274 new Future(() {
225 _renderPages(startPage + 1); 275 _renderPages(startPage + 1);
226 }); 276 });
227 } 277 }
228 278
229 void isolateChanged(oldValue) { 279 Future _refresh() {
230 if (isolate == null) { 280 final isolate = _isolate as S.Isolate;
231 fragmentation = null; 281 return isolate.invokeRpc('_getHeapMap', {}).then((S.ServiceMap response) {
232 return;
233 }
234 isolate.invokeRpc('_getHeapMap', {}).then((ServiceMap response) {
235 assert(response['type'] == 'HeapMap'); 282 assert(response['type'] == 'HeapMap');
236 fragmentation = response; 283 _fragmentation = response;
237 }).catchError((e, st) {
238 Logger.root.info('$e $st');
239 });
240 }
241
242 Future refresh() {
243 if (isolate == null) {
244 return new Future.value(null);
245 }
246 return isolate.invokeRpc('_getHeapMap', {}).then((ServiceMap response) {
247 assert(response['type'] == 'HeapMap');
248 fragmentation = response;
249 });
250 }
251
252 void fragmentationChanged(oldValue) {
253 // Async, in case attached has not yet run (observed in JS version).
254 new Future(() {
255 _updateFragmentationData(); 284 _updateFragmentationData();
256 }); 285 });
257 } 286 }
258 } 287 }
288
289 // A reference to a particular pixel of ImageData.
290 class PixelReference {
291 final _data;
292 var _dataIndex;
293 static const NUM_COLOR_COMPONENTS = 4;
294
295 PixelReference(ImageData data, Point<int> point)
296 : _data = data,
297 _dataIndex = (point.y * data.width + point.x) * NUM_COLOR_COMPONENTS;
298
299 PixelReference._fromDataIndex(this._data, this._dataIndex);
300
301 Point<int> get point =>
302 new Point(index % _data.width, index ~/ _data.width);
303
304 void set color(Iterable<int> color) {
305 _data.data.setRange(
306 _dataIndex, _dataIndex + NUM_COLOR_COMPONENTS, color);
307 }
308
309 Iterable<int> get color =>
310 _data.data.getRange(_dataIndex, _dataIndex + NUM_COLOR_COMPONENTS);
311
312 // Returns the next pixel in row-major order.
313 PixelReference next() => new PixelReference._fromDataIndex(
314 _data, _dataIndex + NUM_COLOR_COMPONENTS);
315
316 // The row-major index of this pixel.
317 int get index => _dataIndex ~/ NUM_COLOR_COMPONENTS;
318 }
319
320 class ObjectInfo {
321 final address;
322 final size;
323 ObjectInfo(this.address, this.size);
324 }
OLDNEW
« no previous file with comments | « runtime/observatory/lib/src/elements/css/shared.css ('k') | runtime/observatory/lib/src/elements/heap_map.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698