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

Side by Side Diff: runtime/bin/vmservice/client/lib/src/elements/heap_map.dart

Issue 204103009: Add class and address information to heap map; display on hover. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 6 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 | Annotate | Revision Log
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 'observatory_element.dart';
11 import 'package:observatory/service.dart'; 11 import 'package:observatory/service.dart';
12 import 'package:logging/logging.dart'; 12 import 'package:logging/logging.dart';
13 import 'package:polymer/polymer.dart'; 13 import 'package:polymer/polymer.dart';
14 import 'package:observatory/app.dart'; 14 import 'package:observatory/app.dart';
15 15
16 /// Displays an Error response. 16 // A reference to a particular pixel of ImageData.
17 class PixelReference {
18 final _data;
19 var _dataIndex;
20 static const NUM_COLOR_COMPONENTS = 4;
21
22 PixelReference(ImageData data, Point<int> point)
23 : _data = data,
24 _dataIndex = (point.y * data.width + point.x) * NUM_COLOR_COMPONENTS;
25
26 PixelReference._fromDataIndex(this._data, this._dataIndex);
27
28 Point<int> get point =>
29 new Point(index % _data.width, index ~/ _data.width);
30
31 void set color(Iterable<int> color) {
32 _data.data.setRange(
33 _dataIndex, _dataIndex + NUM_COLOR_COMPONENTS, color);
34 }
35
36 Iterable<int> get color =>
37 _data.data.getRange(_dataIndex, _dataIndex + NUM_COLOR_COMPONENTS);
38
39 // Returns the next pixel in row-major order.
40 PixelReference next() => new PixelReference._fromDataIndex(
41 _data, _dataIndex + NUM_COLOR_COMPONENTS);
42
43 // The row-major index of this pixel.
44 int get index => _dataIndex ~/ NUM_COLOR_COMPONENTS;
45 }
46
17 @CustomTag('heap-map') 47 @CustomTag('heap-map')
18 class HeapMapElement extends ObservatoryElement { 48 class HeapMapElement extends ObservatoryElement {
19 var _fragmentationCanvas; 49 var _fragmentationCanvas;
20 var _fragmentationData; 50 var _fragmentationData;
21 51 var _pageHeight;
52 var _classIdToColor = {};
53 var _colorToClassId = {};
54 var _classIdToName = {};
55
56 static final _freeColor = [255, 255, 255, 255];
57 static final _pageSeparationColor = [0, 0, 0, 255];
58 static const _PAGE_SEPARATION_HEIGHT = 4;
59
22 @observable String status; 60 @observable String status;
23 @published ServiceMap fragmentation; 61 @published ServiceMap fragmentation;
24 62
25 HeapMapElement.created() : super.created() { 63 HeapMapElement.created() : super.created() {
26 } 64 }
27 65
28 void enteredView() { 66 void enteredView() {
29 super.enteredView(); 67 super.enteredView();
30 _fragmentationCanvas = shadowRoot.querySelector("#fragmentation"); 68 _fragmentationCanvas = shadowRoot.querySelector("#fragmentation");
69 _fragmentationCanvas.onMouseMove.listen(_handleMouseMove);
31 } 70 }
32 71
33 List<int> _classIdToRGBA(int classId) { 72 // Encode color as single integer, to enable using it as a map key.
34 if (classId == fragmentation['free_class_id']) { 73 int _packColor(Iterable<int> color) {
35 return [255, 255, 255, 255]; 74 int packed = 0;
75 for (var component in color) {
76 packed = packed * 256 + component;
77 }
78 return packed;
79 }
80
81 void _addClass(int classId, String name, Iterable<int> color) {
82 _classIdToName[classId] = name;
83 _classIdToColor[classId] = color;
84 _colorToClassId[_packColor(color)] = classId;
85 }
86
87 void _updateClassList(classList, int freeClassId) {
88 for (var member in classList['members']) {
89 if (member['type'] != '@Class') {
90 Logger.root.info('$member');
91 continue;
92 }
93 var classId = int.parse(member['id'].split('/').last);
94 var color = _classIdToRGBA(classId);
95 _addClass(classId, member['user_name'], color);
96 }
97 _addClass(freeClassId, 'Free', _freeColor);
98 _addClass(0, '', _pageSeparationColor);
99 }
100
101 Iterable<int> _classIdToRGBA(int classId) {
102 // TODO(koda): Pick random hue, but fixed saturation and value.
103 var rng = new Random(classId);
104 return [rng.nextInt(128), rng.nextInt(128), rng.nextInt(128), 255];
105 }
106
107 String _classNameAt(Point<int> point) {
108 var color = new PixelReference(_fragmentationData, point).color;
109 return _classIdToName[_colorToClassId[_packColor(color)]];
110 }
111
112 // TODO(koda): Find start of object.
113 int _addressAt(Point<int> point) {
114 var pagePixels = _pageHeight * _fragmentationData.width;
115 var index = new PixelReference(_fragmentationData, point).index;
116 var pageIndex = index ~/ pagePixels;
117 var pageOffset = index % pagePixels;
118 var pages = fragmentation['pages'];
119 if (0 <= pageIndex && pageIndex < pages.length) {
120 return int.parse(pages[pageIndex]['object_start']) +
121 pageOffset * fragmentation['unit_size_bytes'];
36 } else { 122 } else {
37 // TODO(koda): Pick random hue, but fixed saturation and value. 123 return 0;
38 var rng = new Random(classId);
39 return [rng.nextInt(128),
40 rng.nextInt(128),
41 rng.nextInt(128),
42 255];
43 } 124 }
44 } 125 }
45 126
127 void _handleMouseMove(MouseEvent event) {
128 var addressString = '@ 0x${_addressAt(event.offset).toRadixString(16)}';
129 var className = _classNameAt(event.offset);
130 status = (className == '') ? '-' : '$className $addressString';
131 }
132
46 void _updateFragmentationData() { 133 void _updateFragmentationData() {
47 if (fragmentation == null || _fragmentationCanvas == null) { 134 if (fragmentation == null || _fragmentationCanvas == null) {
48 return; 135 return;
49 } 136 }
137 _updateClassList(
138 fragmentation['class_list'], fragmentation['free_class_id']);
50 var pages = fragmentation['pages']; 139 var pages = fragmentation['pages'];
51 // Calculate dimensions.
52 var numPixels = 0;
53 var colorMap = {};
54 for (var page in pages) {
55 for (int i = 0; i < page.length; i += 2) {
56 numPixels += page[i];
57 var classId = page[i + 1];
58 colorMap.putIfAbsent(classId, () => _classIdToRGBA(classId));
59 }
60 }
61 var width = _fragmentationCanvas.parent.client.width; 140 var width = _fragmentationCanvas.parent.client.width;
62 var height = (numPixels + width - 1) ~/ width; 141 _pageHeight = _PAGE_SEPARATION_HEIGHT +
63 // Render image. 142 fragmentation['page_size_bytes'] ~/
143 fragmentation['unit_size_bytes'] ~/ width;
144 var height = _pageHeight * pages.length;
64 _fragmentationData = 145 _fragmentationData =
65 _fragmentationCanvas.context2D.createImageData(width, height); 146 _fragmentationCanvas.context2D.createImageData(width, height);
66 _fragmentationCanvas.width = _fragmentationData.width; 147 _fragmentationCanvas.width = _fragmentationData.width;
67 _fragmentationCanvas.height = _fragmentationData.height; 148 _fragmentationCanvas.height = _fragmentationData.height;
68 _renderPages(0, 0, colorMap); 149 _renderPages(0);
69 } 150 }
70 151
71 // Renders and draws asynchronously, one page at a time to avoid 152 // Renders and draws asynchronously, one page at a time to avoid
72 // blocking the UI. 153 // blocking the UI.
73 void _renderPages(int startPage, int dataIndex, var colorMap) { 154 void _renderPages(int startPage) {
74 var pages = fragmentation['pages']; 155 var pages = fragmentation['pages'];
75 status = 'Loaded $startPage of ${pages.length} pages'; 156 status = 'Loaded $startPage of ${pages.length} pages';
76 if (startPage >= pages.length) { 157 if (startPage >= pages.length) {
77 return; 158 return;
78 } 159 }
79 var width = _fragmentationData.width; 160 var startY = startPage * _pageHeight;
80 var dirtyBegin = (dataIndex / 4) ~/ width; 161 var pixel = new PixelReference(_fragmentationData, new Point(0, startY));
81 var page = pages[startPage]; 162 var objects = pages[startPage]['objects'];
82 for (var i = 0; i < page.length; i += 2) { 163 for (var i = 0; i < objects.length; i += 2) {
83 var count = page[i]; 164 var count = objects[i];
84 var color = colorMap[page[i + 1]]; 165 var classId = objects[i + 1];
85 for (var j = 0; j < count; ++j) { 166 var color = _classIdToColor[classId];
86 for (var component in color) { 167 while (count-- > 0) {
87 _fragmentationData.data[dataIndex++] = component; 168 pixel.color = color;
88 } 169 pixel = pixel.next();
89 } 170 }
90 } 171 }
91 var dirtyEnd = (dataIndex / 4 + width - 1) ~/ width; 172 var endY = startY + _pageHeight;
173 while (pixel.point.y < endY) {
174 pixel.color = _pageSeparationColor;
175 pixel = pixel.next();
176 }
92 _fragmentationCanvas.context2D.putImageData( 177 _fragmentationCanvas.context2D.putImageData(
93 _fragmentationData, 0, 0, 0, dirtyBegin, width, dirtyEnd - dirtyBegin); 178 _fragmentationData, 0, 0, 0, startY, _fragmentationData.width, endY);
94 // Continue with the next page, asynchronously. 179 // Continue with the next page, asynchronously.
95 new Future(() { 180 new Future(() {
96 _renderPages(startPage + 1, dataIndex, colorMap); 181 _renderPages(startPage + 1);
97 }); 182 });
98 } 183 }
99 184
100 void refresh(var done) { 185 void refresh(var done) {
101 if (fragmentation == null) { 186 if (fragmentation == null) {
102 return; 187 return;
103 } 188 }
104 fragmentation.isolate.get('heapmap').then((ServiceMap response) { 189 fragmentation.isolate.get('heapmap').then((ServiceMap response) {
105 assert(response['type'] == 'HeapMap'); 190 assert(response['type'] == 'HeapMap');
106 fragmentation = response; 191 fragmentation = response;
107 }).catchError((e, st) { 192 }).catchError((e, st) {
108 Logger.root.info('$e $st'); 193 Logger.root.info('$e $st');
109 }).whenComplete(done); 194 }).whenComplete(done);
110 } 195 }
111 196
112 void fragmentationChanged(oldValue) { 197 void fragmentationChanged(oldValue) {
113 // Async, in case enteredView has not yet run (observed in JS version). 198 // Async, in case enteredView has not yet run (observed in JS version).
114 new Future(() { 199 new Future(() {
115 _updateFragmentationData(); 200 _updateFragmentationData();
116 }); 201 });
117 } 202 }
118 } 203 }
OLDNEW
« no previous file with comments | « runtime/bin/vmservice/client/deployed/web/index_devtools.html_bootstrap.dart.js ('k') | runtime/vm/class_table.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698