| Index: runtime/bin/vmservice/client/lib/src/elements/heap_map.dart
|
| ===================================================================
|
| --- runtime/bin/vmservice/client/lib/src/elements/heap_map.dart (revision 0)
|
| +++ runtime/bin/vmservice/client/lib/src/elements/heap_map.dart (revision 0)
|
| @@ -0,0 +1,118 @@
|
| +// 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';
|
| +import 'package:observatory/app.dart';
|
| +
|
| +/// Displays an Error response.
|
| +@CustomTag('heap-map')
|
| +class HeapMapElement extends ObservatoryElement {
|
| + var _fragmentationCanvas;
|
| + var _fragmentationData;
|
| +
|
| + @observable String status;
|
| + @published ServiceMap fragmentation;
|
| +
|
| + HeapMapElement.created() : super.created() {
|
| + }
|
| +
|
| + void enteredView() {
|
| + super.enteredView();
|
| + _fragmentationCanvas = shadowRoot.querySelector("#fragmentation");
|
| + }
|
| +
|
| + List<int> _classIdToRGBA(int classId) {
|
| + if (classId == fragmentation['free_class_id']) {
|
| + return [255, 255, 255, 255];
|
| + } 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];
|
| + }
|
| + }
|
| +
|
| + void _updateFragmentationData() {
|
| + if (fragmentation == null || _fragmentationCanvas == null) {
|
| + return;
|
| + }
|
| + 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.
|
| + _fragmentationData =
|
| + _fragmentationCanvas.context2D.createImageData(width, height);
|
| + _fragmentationCanvas.width = _fragmentationData.width;
|
| + _fragmentationCanvas.height = _fragmentationData.height;
|
| + _renderPages(0, 0, colorMap);
|
| + }
|
| +
|
| + // Renders and draws asynchronously, one page at a time to avoid
|
| + // blocking the UI.
|
| + void _renderPages(int startPage, int dataIndex, var colorMap) {
|
| + 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 dirtyEnd = (dataIndex / 4 + width - 1) ~/ width;
|
| + _fragmentationCanvas.context2D.putImageData(
|
| + _fragmentationData, 0, 0, 0, dirtyBegin, width, dirtyEnd - dirtyBegin);
|
| + // Continue with the next page, asynchronously.
|
| + new Future(() {
|
| + _renderPages(startPage + 1, dataIndex, colorMap);
|
| + });
|
| + }
|
| +
|
| + 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 enteredView has not yet run (observed in JS version).
|
| + new Future(() {
|
| + _updateFragmentationData();
|
| + });
|
| + }
|
| +}
|
|
|