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

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

Issue 837723004: Build Observatory as part of runtime (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 11 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library heap_map_element;
6
7 import 'dart:async';
8 import 'dart:html';
9 import 'dart:math';
10 import 'observatory_element.dart';
11 import 'package:observatory/service.dart';
12 import 'package:logging/logging.dart';
13 import 'package:polymer/polymer.dart';
14
15 // A reference to a particular pixel of ImageData.
16 class PixelReference {
17 final _data;
18 var _dataIndex;
19 static const NUM_COLOR_COMPONENTS = 4;
20
21 PixelReference(ImageData data, Point<int> point)
22 : _data = data,
23 _dataIndex = (point.y * data.width + point.x) * NUM_COLOR_COMPONENTS;
24
25 PixelReference._fromDataIndex(this._data, this._dataIndex);
26
27 Point<int> get point =>
28 new Point(index % _data.width, index ~/ _data.width);
29
30 void set color(Iterable<int> color) {
31 _data.data.setRange(
32 _dataIndex, _dataIndex + NUM_COLOR_COMPONENTS, color);
33 }
34
35 Iterable<int> get color =>
36 _data.data.getRange(_dataIndex, _dataIndex + NUM_COLOR_COMPONENTS);
37
38 // Returns the next pixel in row-major order.
39 PixelReference next() => new PixelReference._fromDataIndex(
40 _data, _dataIndex + NUM_COLOR_COMPONENTS);
41
42 // The row-major index of this pixel.
43 int get index => _dataIndex ~/ NUM_COLOR_COMPONENTS;
44 }
45
46 class ObjectInfo {
47 final address;
48 final size;
49 ObjectInfo(this.address, this.size);
50 }
51
52 @CustomTag('heap-map')
53 class HeapMapElement extends ObservatoryElement {
54 var _fragmentationCanvas;
55 var _fragmentationData;
56 var _pageHeight;
57 var _classIdToColor = {};
58 var _colorToClassId = {};
59 var _classIdToName = {};
60
61 static final _freeColor = [255, 255, 255, 255];
62 static final _pageSeparationColor = [0, 0, 0, 255];
63 static const _PAGE_SEPARATION_HEIGHT = 4;
64 // Many browsers will not display a very tall canvas.
65 // TODO(koda): Improve interface for huge heaps.
66 static const _MAX_CANVAS_HEIGHT = 6000;
67
68 @observable String status;
69 @published ServiceMap fragmentation;
70
71 HeapMapElement.created() : super.created() {
72 }
73
74 @override
75 void attached() {
76 super.attached();
77 _fragmentationCanvas = shadowRoot.querySelector("#fragmentation");
78 _fragmentationCanvas.onMouseMove.listen(_handleMouseMove);
79 _fragmentationCanvas.onMouseDown.listen(_handleClick);
80 }
81
82 // Encode color as single integer, to enable using it as a map key.
83 int _packColor(Iterable<int> color) {
84 int packed = 0;
85 for (var component in color) {
86 packed = packed * 256 + component;
87 }
88 return packed;
89 }
90
91 void _addClass(int classId, String name, Iterable<int> color) {
92 _classIdToName[classId] = name.split('@')[0];
93 _classIdToColor[classId] = color;
94 _colorToClassId[_packColor(color)] = classId;
95 }
96
97 void _updateClassList(classList, int freeClassId) {
98 for (var member in classList['members']) {
99 if (member is! Class) {
100 Logger.root.info('$member');
101 continue;
102 }
103 var classId = int.parse(member.id.split('/').last);
104 var color = _classIdToRGBA(classId);
105 _addClass(classId, member.name, color);
106 }
107 _addClass(freeClassId, 'Free', _freeColor);
108 _addClass(0, '', _pageSeparationColor);
109 }
110
111 Iterable<int> _classIdToRGBA(int classId) {
112 // TODO(koda): Pick random hue, but fixed saturation and value.
113 var rng = new Random(classId);
114 return [rng.nextInt(128), rng.nextInt(128), rng.nextInt(128), 255];
115 }
116
117 String _classNameAt(Point<int> point) {
118 var color = new PixelReference(_fragmentationData, point).color;
119 return _classIdToName[_colorToClassId[_packColor(color)]];
120 }
121
122 ObjectInfo _objectAt(Point<int> point) {
123 var pagePixels = _pageHeight * _fragmentationData.width;
124 var index = new PixelReference(_fragmentationData, point).index;
125 var pageIndex = index ~/ pagePixels;
126 var pageOffset = index % pagePixels;
127 var pages = fragmentation['pages'];
128 if (pageIndex < 0 || pageIndex >= pages.length) {
129 return null;
130 }
131 // Scan the page to find start and size.
132 var page = pages[pageIndex];
133 var objects = page['objects'];
134 var offset = 0;
135 var size = 0;
136 for (var i = 0; i < objects.length; i += 2) {
137 size = objects[i];
138 offset += size;
139 if (offset > pageOffset) {
140 pageOffset = offset - size;
141 break;
142 }
143 }
144 return new ObjectInfo(int.parse(page['object_start']) +
145 pageOffset * fragmentation['unit_size_bytes'],
146 size * fragmentation['unit_size_bytes']);
147 }
148
149 void _handleMouseMove(MouseEvent event) {
150 var info = _objectAt(event.offset);
151 var addressString = '${info.size}B @ 0x${info.address.toRadixString(16)}';
152 var className = _classNameAt(event.offset);
153 status = (className == '') ? '-' : '$className $addressString';
154 }
155
156 void _handleClick(MouseEvent event) {
157 var address = _objectAt(event.offset).address.toRadixString(16);
158 app.locationManager.go(app.locationManager.makeLink(
159 "${fragmentation.isolate.relativeLink('address/$address')}"));
160 }
161
162 void _updateFragmentationData() {
163 if (fragmentation == null || _fragmentationCanvas == null) {
164 return;
165 }
166 _updateClassList(
167 fragmentation['class_list'], fragmentation['free_class_id']);
168 var pages = fragmentation['pages'];
169 var width = _fragmentationCanvas.parent.client.width;
170 _pageHeight = _PAGE_SEPARATION_HEIGHT +
171 fragmentation['page_size_bytes'] ~/
172 fragmentation['unit_size_bytes'] ~/ width;
173 var height = min(_pageHeight * pages.length, _MAX_CANVAS_HEIGHT);
174 _fragmentationData =
175 _fragmentationCanvas.context2D.createImageData(width, height);
176 _fragmentationCanvas.width = _fragmentationData.width;
177 _fragmentationCanvas.height = _fragmentationData.height;
178 _renderPages(0);
179 }
180
181 // Renders and draws asynchronously, one page at a time to avoid
182 // blocking the UI.
183 void _renderPages(int startPage) {
184 var pages = fragmentation['pages'];
185 status = 'Loaded $startPage of ${pages.length} pages';
186 var startY = startPage * _pageHeight;
187 var endY = startY + _pageHeight;
188 if (startPage >= pages.length || endY > _fragmentationData.height) {
189 return;
190 }
191 var pixel = new PixelReference(_fragmentationData, new Point(0, startY));
192 var objects = pages[startPage]['objects'];
193 for (var i = 0; i < objects.length; i += 2) {
194 var count = objects[i];
195 var classId = objects[i + 1];
196 var color = _classIdToColor[classId];
197 while (count-- > 0) {
198 pixel.color = color;
199 pixel = pixel.next();
200 }
201 }
202 while (pixel.point.y < endY) {
203 pixel.color = _pageSeparationColor;
204 pixel = pixel.next();
205 }
206 _fragmentationCanvas.context2D.putImageData(
207 _fragmentationData, 0, 0, 0, startY, _fragmentationData.width, endY);
208 // Continue with the next page, asynchronously.
209 new Future(() {
210 _renderPages(startPage + 1);
211 });
212 }
213
214 void refresh(var done) {
215 if (fragmentation == null) {
216 return;
217 }
218 fragmentation.isolate.get('heapmap').then((ServiceMap response) {
219 assert(response['type'] == 'HeapMap');
220 fragmentation = response;
221 }).catchError((e, st) {
222 Logger.root.info('$e $st');
223 }).whenComplete(done);
224 }
225
226 void fragmentationChanged(oldValue) {
227 // Async, in case attached has not yet run (observed in JS version).
228 new Future(() {
229 _updateFragmentationData();
230 });
231 }
232 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698