| Index: runtime/bin/vmservice/observatory/lib/src/elements/heap_map.dart
|
| diff --git a/runtime/bin/vmservice/observatory/lib/src/elements/heap_map.dart b/runtime/bin/vmservice/observatory/lib/src/elements/heap_map.dart
|
| deleted file mode 100644
|
| index 1f37c234187111f3eed73511ddc18b181fcae3e0..0000000000000000000000000000000000000000
|
| --- a/runtime/bin/vmservice/observatory/lib/src/elements/heap_map.dart
|
| +++ /dev/null
|
| @@ -1,232 +0,0 @@
|
| -// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -library heap_map_element;
|
| -
|
| -import 'dart:async';
|
| -import 'dart:html';
|
| -import 'dart:math';
|
| -import 'observatory_element.dart';
|
| -import 'package:observatory/service.dart';
|
| -import 'package:logging/logging.dart';
|
| -import 'package:polymer/polymer.dart';
|
| -
|
| -// 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;
|
| -}
|
| -
|
| -class ObjectInfo {
|
| - final address;
|
| - final size;
|
| - ObjectInfo(this.address, this.size);
|
| -}
|
| -
|
| -@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;
|
| - // Many browsers will not display a very tall canvas.
|
| - // TODO(koda): Improve interface for huge heaps.
|
| - static const _MAX_CANVAS_HEIGHT = 6000;
|
| -
|
| - @observable String status;
|
| - @published ServiceMap fragmentation;
|
| -
|
| - HeapMapElement.created() : super.created() {
|
| - }
|
| -
|
| - @override
|
| - void attached() {
|
| - super.attached();
|
| - _fragmentationCanvas = shadowRoot.querySelector("#fragmentation");
|
| - _fragmentationCanvas.onMouseMove.listen(_handleMouseMove);
|
| - _fragmentationCanvas.onMouseDown.listen(_handleClick);
|
| - }
|
| -
|
| - // 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.split('@')[0];
|
| - _classIdToColor[classId] = color;
|
| - _colorToClassId[_packColor(color)] = classId;
|
| - }
|
| -
|
| - void _updateClassList(classList, int freeClassId) {
|
| - for (var member in classList['members']) {
|
| - if (member is! Class) {
|
| - Logger.root.info('$member');
|
| - continue;
|
| - }
|
| - var classId = int.parse(member.id.split('/').last);
|
| - var color = _classIdToRGBA(classId);
|
| - _addClass(classId, member.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)]];
|
| - }
|
| -
|
| - ObjectInfo _objectAt(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 (pageIndex < 0 || pageIndex >= pages.length) {
|
| - return null;
|
| - }
|
| - // Scan the page to find start and size.
|
| - var page = pages[pageIndex];
|
| - var objects = page['objects'];
|
| - var offset = 0;
|
| - var size = 0;
|
| - for (var i = 0; i < objects.length; i += 2) {
|
| - size = objects[i];
|
| - offset += size;
|
| - if (offset > pageOffset) {
|
| - pageOffset = offset - size;
|
| - break;
|
| - }
|
| - }
|
| - return new ObjectInfo(int.parse(page['object_start']) +
|
| - pageOffset * fragmentation['unit_size_bytes'],
|
| - size * fragmentation['unit_size_bytes']);
|
| - }
|
| -
|
| - void _handleMouseMove(MouseEvent event) {
|
| - var info = _objectAt(event.offset);
|
| - var addressString = '${info.size}B @ 0x${info.address.toRadixString(16)}';
|
| - var className = _classNameAt(event.offset);
|
| - status = (className == '') ? '-' : '$className $addressString';
|
| - }
|
| -
|
| - void _handleClick(MouseEvent event) {
|
| - var address = _objectAt(event.offset).address.toRadixString(16);
|
| - app.locationManager.go(app.locationManager.makeLink(
|
| - "${fragmentation.isolate.relativeLink('address/$address')}"));
|
| - }
|
| -
|
| - void _updateFragmentationData() {
|
| - if (fragmentation == null || _fragmentationCanvas == null) {
|
| - return;
|
| - }
|
| - _updateClassList(
|
| - fragmentation['class_list'], fragmentation['free_class_id']);
|
| - var pages = fragmentation['pages'];
|
| - var width = _fragmentationCanvas.parent.client.width;
|
| - _pageHeight = _PAGE_SEPARATION_HEIGHT +
|
| - fragmentation['page_size_bytes'] ~/
|
| - fragmentation['unit_size_bytes'] ~/ width;
|
| - var height = min(_pageHeight * pages.length, _MAX_CANVAS_HEIGHT);
|
| - _fragmentationData =
|
| - _fragmentationCanvas.context2D.createImageData(width, height);
|
| - _fragmentationCanvas.width = _fragmentationData.width;
|
| - _fragmentationCanvas.height = _fragmentationData.height;
|
| - _renderPages(0);
|
| - }
|
| -
|
| - // Renders and draws asynchronously, one page at a time to avoid
|
| - // blocking the UI.
|
| - void _renderPages(int startPage) {
|
| - var pages = fragmentation['pages'];
|
| - status = 'Loaded $startPage of ${pages.length} pages';
|
| - var startY = startPage * _pageHeight;
|
| - var endY = startY + _pageHeight;
|
| - if (startPage >= pages.length || endY > _fragmentationData.height) {
|
| - return;
|
| - }
|
| - 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();
|
| - }
|
| - }
|
| - while (pixel.point.y < endY) {
|
| - pixel.color = _pageSeparationColor;
|
| - pixel = pixel.next();
|
| - }
|
| - _fragmentationCanvas.context2D.putImageData(
|
| - _fragmentationData, 0, 0, 0, startY, _fragmentationData.width, endY);
|
| - // Continue with the next page, asynchronously.
|
| - new Future(() {
|
| - _renderPages(startPage + 1);
|
| - });
|
| - }
|
| -
|
| - void refresh(var done) {
|
| - if (fragmentation == null) {
|
| - return;
|
| - }
|
| - fragmentation.isolate.get('heapmap').then((ServiceMap response) {
|
| - assert(response['type'] == 'HeapMap');
|
| - fragmentation = response;
|
| - }).catchError((e, st) {
|
| - Logger.root.info('$e $st');
|
| - }).whenComplete(done);
|
| - }
|
| -
|
| - void fragmentationChanged(oldValue) {
|
| - // Async, in case attached has not yet run (observed in JS version).
|
| - new Future(() {
|
| - _updateFragmentationData();
|
| - });
|
| - }
|
| -}
|
|
|