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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: runtime/bin/vmservice/client/lib/src/elements/heap_map.dart
===================================================================
--- runtime/bin/vmservice/client/lib/src/elements/heap_map.dart (revision 34193)
+++ runtime/bin/vmservice/client/lib/src/elements/heap_map.dart (working copy)
@@ -13,12 +13,50 @@
import 'package:polymer/polymer.dart';
import 'package:observatory/app.dart';
-/// Displays an Error response.
+// A reference to a particular pixel of ImageData.
+class PixelReference {
+ final _data;
+ var _dataIndex;
+ static const NUM_COLOR_COMPONENTS = 4;
+
+ PixelReference(ImageData data, Point<int> point)
+ : _data = data,
+ _dataIndex = (point.y * data.width + point.x) * NUM_COLOR_COMPONENTS;
+
+ PixelReference._fromDataIndex(this._data, this._dataIndex);
+
+ Point<int> get point =>
+ new Point(index % _data.width, index ~/ _data.width);
+
+ void set color(Iterable<int> color) {
+ _data.data.setRange(
+ _dataIndex, _dataIndex + NUM_COLOR_COMPONENTS, color);
+ }
+
+ Iterable<int> get color =>
+ _data.data.getRange(_dataIndex, _dataIndex + NUM_COLOR_COMPONENTS);
+
+ // Returns the next pixel in row-major order.
+ PixelReference next() => new PixelReference._fromDataIndex(
+ _data, _dataIndex + NUM_COLOR_COMPONENTS);
+
+ // The row-major index of this pixel.
+ int get index => _dataIndex ~/ NUM_COLOR_COMPONENTS;
+}
+
@CustomTag('heap-map')
class HeapMapElement extends ObservatoryElement {
var _fragmentationCanvas;
var _fragmentationData;
-
+ var _pageHeight;
+ var _classIdToColor = {};
+ var _colorToClassId = {};
+ var _classIdToName = {};
+
+ static final _freeColor = [255, 255, 255, 255];
+ static final _pageSeparationColor = [0, 0, 0, 255];
+ static const _PAGE_SEPARATION_HEIGHT = 4;
+
@observable String status;
@published ServiceMap fragmentation;
@@ -28,72 +66,119 @@
void enteredView() {
super.enteredView();
_fragmentationCanvas = shadowRoot.querySelector("#fragmentation");
+ _fragmentationCanvas.onMouseMove.listen(_handleMouseMove);
}
- List<int> _classIdToRGBA(int classId) {
- if (classId == fragmentation['free_class_id']) {
- return [255, 255, 255, 255];
+ // Encode color as single integer, to enable using it as a map key.
+ int _packColor(Iterable<int> color) {
+ int packed = 0;
+ for (var component in color) {
+ packed = packed * 256 + component;
+ }
+ return packed;
+ }
+
+ void _addClass(int classId, String name, Iterable<int> color) {
+ _classIdToName[classId] = name;
+ _classIdToColor[classId] = color;
+ _colorToClassId[_packColor(color)] = classId;
+ }
+
+ void _updateClassList(classList, int freeClassId) {
+ for (var member in classList['members']) {
+ if (member['type'] != '@Class') {
+ Logger.root.info('$member');
+ continue;
+ }
+ var classId = int.parse(member['id'].split('/').last);
+ var color = _classIdToRGBA(classId);
+ _addClass(classId, member['user_name'], color);
+ }
+ _addClass(freeClassId, 'Free', _freeColor);
+ _addClass(0, '', _pageSeparationColor);
+ }
+
+ Iterable<int> _classIdToRGBA(int classId) {
+ // TODO(koda): Pick random hue, but fixed saturation and value.
+ var rng = new Random(classId);
+ return [rng.nextInt(128), rng.nextInt(128), rng.nextInt(128), 255];
+ }
+
+ String _classNameAt(Point<int> point) {
+ var color = new PixelReference(_fragmentationData, point).color;
+ return _classIdToName[_colorToClassId[_packColor(color)]];
+ }
+
+ // TODO(koda): Find start of object.
+ int _addressAt(Point<int> point) {
+ var pagePixels = _pageHeight * _fragmentationData.width;
+ var index = new PixelReference(_fragmentationData, point).index;
+ var pageIndex = index ~/ pagePixels;
+ var pageOffset = index % pagePixels;
+ var pages = fragmentation['pages'];
+ if (0 <= pageIndex && pageIndex < pages.length) {
+ return int.parse(pages[pageIndex]['object_start']) +
+ pageOffset * fragmentation['unit_size_bytes'];
} else {
- // TODO(koda): Pick random hue, but fixed saturation and value.
- var rng = new Random(classId);
- return [rng.nextInt(128),
- rng.nextInt(128),
- rng.nextInt(128),
- 255];
+ return 0;
}
}
-
+
+ void _handleMouseMove(MouseEvent event) {
+ var addressString = '@ 0x${_addressAt(event.offset).toRadixString(16)}';
+ var className = _classNameAt(event.offset);
+ status = (className == '') ? '-' : '$className $addressString';
+ }
+
void _updateFragmentationData() {
if (fragmentation == null || _fragmentationCanvas == null) {
return;
}
+ _updateClassList(
+ fragmentation['class_list'], fragmentation['free_class_id']);
var pages = fragmentation['pages'];
- // Calculate dimensions.
- var numPixels = 0;
- var colorMap = {};
- for (var page in pages) {
- for (int i = 0; i < page.length; i += 2) {
- numPixels += page[i];
- var classId = page[i + 1];
- colorMap.putIfAbsent(classId, () => _classIdToRGBA(classId));
- }
- }
var width = _fragmentationCanvas.parent.client.width;
- var height = (numPixels + width - 1) ~/ width;
- // Render image.
+ _pageHeight = _PAGE_SEPARATION_HEIGHT +
+ fragmentation['page_size_bytes'] ~/
+ fragmentation['unit_size_bytes'] ~/ width;
+ var height = _pageHeight * pages.length;
_fragmentationData =
_fragmentationCanvas.context2D.createImageData(width, height);
_fragmentationCanvas.width = _fragmentationData.width;
_fragmentationCanvas.height = _fragmentationData.height;
- _renderPages(0, 0, colorMap);
+ _renderPages(0);
}
-
+
// Renders and draws asynchronously, one page at a time to avoid
// blocking the UI.
- void _renderPages(int startPage, int dataIndex, var colorMap) {
+ void _renderPages(int startPage) {
var pages = fragmentation['pages'];
status = 'Loaded $startPage of ${pages.length} pages';
if (startPage >= pages.length) {
return;
}
- var width = _fragmentationData.width;
- var dirtyBegin = (dataIndex / 4) ~/ width;
- var page = pages[startPage];
- for (var i = 0; i < page.length; i += 2) {
- var count = page[i];
- var color = colorMap[page[i + 1]];
- for (var j = 0; j < count; ++j) {
- for (var component in color) {
- _fragmentationData.data[dataIndex++] = component;
- }
+ var startY = startPage * _pageHeight;
+ var pixel = new PixelReference(_fragmentationData, new Point(0, startY));
+ var objects = pages[startPage]['objects'];
+ for (var i = 0; i < objects.length; i += 2) {
+ var count = objects[i];
+ var classId = objects[i + 1];
+ var color = _classIdToColor[classId];
+ while (count-- > 0) {
+ pixel.color = color;
+ pixel = pixel.next();
}
}
- var dirtyEnd = (dataIndex / 4 + width - 1) ~/ width;
+ var endY = startY + _pageHeight;
+ while (pixel.point.y < endY) {
+ pixel.color = _pageSeparationColor;
+ pixel = pixel.next();
+ }
_fragmentationCanvas.context2D.putImageData(
- _fragmentationData, 0, 0, 0, dirtyBegin, width, dirtyEnd - dirtyBegin);
+ _fragmentationData, 0, 0, 0, startY, _fragmentationData.width, endY);
// Continue with the next page, asynchronously.
new Future(() {
- _renderPages(startPage + 1, dataIndex, colorMap);
+ _renderPages(startPage + 1);
});
}
@@ -108,7 +193,7 @@
Logger.root.info('$e $st');
}).whenComplete(done);
}
-
+
void fragmentationChanged(oldValue) {
// Async, in case enteredView has not yet run (observed in JS version).
new Future(() {
« 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