blob: 7265cd4b1fb41209fc99b1192303cab6d259a826 [file] [log] [blame]
// Copyright (c) 2019, 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.
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import '../vm_service.dart';
class _ReadStream {
final List<ByteData> _chunks;
int _chunkIndex = 0;
int _byteIndex = 0;
_ReadStream(this._chunks);
int readByte() {
while (_byteIndex >= _chunks[_chunkIndex].lengthInBytes) {
_chunkIndex++;
_byteIndex = 0;
}
return _chunks[_chunkIndex].getUint8(_byteIndex++);
}
/// Read one ULEB128 number.
int readUnsigned() {
int result = 0;
int shift = 0;
for (;;) {
int part = readByte();
result |= (part & 0x7F) << shift;
if ((part & 0x80) == 0) {
break;
}
shift += 7;
}
return result;
}
/// Read one SLEB128 number.
int readSigned() {
int result = 0;
int shift = 0;
for (;;) {
int part = readByte();
result |= (part & 0x7F) << shift;
shift += 7;
if ((part & 0x80) == 0) {
if ((part & 0x40) != 0) {
result |= (-1 << shift);
}
break;
}
}
return result;
}
double readFloat64() {
final bytes = Uint8List(8);
for (int i = 0; i < 8; i++) {
bytes[i] = readByte();
}
return Float64List.view(bytes.buffer)[0];
}
String readUtf8() {
int len = readUnsigned();
final bytes = Uint8List(len);
for (int i = 0; i < len; i++) {
bytes[i] = readByte();
}
return Utf8Codec(allowMalformed: true).decode(bytes);
}
String readLatin1() {
int len = readUnsigned();
final codeUnits = Uint8List(len);
for (int i = 0; i < len; i++) {
codeUnits[i] = readByte();
}
return String.fromCharCodes(codeUnits);
}
String readUtf16() {
int len = readUnsigned();
final codeUnits = Uint16List(len);
for (int i = 0; i < len; i++) {
codeUnits[i] = readByte() | (readByte() << 8);
}
return String.fromCharCodes(codeUnits);
}
}
/// A representation of a field captured in a memory snapshot.
class HeapSnapshotField {
/// A 0-origin index into [HeapSnapshotObject.references].
int get index => _index;
/// The name of the field.
String get name => _name;
int _index;
String _name;
HeapSnapshotField._read(_ReadStream reader) {
// flags (reserved)
reader.readUnsigned();
_index = reader.readUnsigned();
_name = reader.readUtf8();
// reserved
reader.readUtf8();
}
}
/// A representation of a class type captured in a memory snapshot.
class HeapSnapshotClass {
/// The simple (not qualified) name of the class.
String get name => _name;
/// The name of the class's library.
String get libraryName => _libraryName;
/// The [Uri] of the class's library.
Uri get libraryUri => _libraryUri;
/// The list of fields in the class.
List<HeapSnapshotField> get fields => _fields;
String _name;
String _libraryName;
Uri _libraryUri;
List<HeapSnapshotField> _fields = <HeapSnapshotField>[];
HeapSnapshotClass._read(_ReadStream reader) {
// flags (reserved).
reader.readUnsigned();
_name = reader.readUtf8();
_libraryName = reader.readUtf8();
_libraryUri = Uri.parse(reader.readUtf8());
// reserved
reader.readUtf8();
_populateFields(reader);
}
void _populateFields(_ReadStream reader) {
final fieldCount = reader.readUnsigned();
for (int i = 0; i < fieldCount; ++i) {
_fields.add(HeapSnapshotField._read(reader));
}
}
}
/// A representation of an object instance captured in a memory snapshot.
class HeapSnapshotObject {
/// The class ID representing the type of this object.
int get classId => _classId;
/// The space used by this object in bytes.
int get shallowSize => _shallowSize;
/// Data associated with this object.
dynamic get data => _data;
/// A list of 1-origin indicies into [HeapSnapshotGraph.objects].
List<int> get references => _references;
int _classId;
int _shallowSize;
dynamic _data;
List<int> _references = <int>[];
HeapSnapshotObject._read(_ReadStream reader) {
_classId = reader.readUnsigned();
_shallowSize = reader.readUnsigned();
_data = _getNonReferenceData(reader);
_populateReferences(reader);
}
void _populateReferences(_ReadStream reader) {
final referencesCount = reader.readUnsigned();
for (int i = 0; i < referencesCount; ++i) {
_references.add(reader.readUnsigned());
}
}
}
/// A representation of an external property captured in a memory snapshot.
class HeapSnapshotExternalProperty {
/// A 1-origin index into [HeapSnapshotGraph.objects].
final int object;
/// The amount of external memory used.
final int externalSize;
/// The name of the external property.
final String name;
HeapSnapshotExternalProperty._read(_ReadStream reader)
: object = reader.readUnsigned(),
externalSize = reader.readUnsigned(),
name = reader.readUtf8();
}
/// A graph representation of a heap snapshot.
class HeapSnapshotGraph {
/// The name of the isolate represented by this heap snapshot.
String get name => _name;
int get flags => _flags;
/// The sum of shallow sizes of all objects in this graph in bytes.
int get shallowSize => _shallowSize;
/// The amount of memory reserved for this heap in bytes.
///
/// At least as large as [shallowSize].
int get capacity => _capacity;
/// The sum of sizes of all external properties in this graph in bytes.
int get externalSize => _externalSize;
/// The list of classes found in this snapshot.
List<HeapSnapshotClass> get classes => _classes;
/// At least as big as the sum of all [HeapSnapshotObject.referenceCount].
int get referenceCount => _referenceCount;
/// The list of objects found in this snapshot.
List<HeapSnapshotObject> get objects => _objects;
/// The list of external properties found in this snapshot.
List<HeapSnapshotExternalProperty> get externalProperties =>
_externalProperties;
String _name;
int _flags;
int _shallowSize;
int _capacity;
int _externalSize;
List<HeapSnapshotClass> _classes = <HeapSnapshotClass>[];
int _referenceCount;
List<HeapSnapshotObject> _objects = <HeapSnapshotObject>[];
List<HeapSnapshotExternalProperty> _externalProperties =
<HeapSnapshotExternalProperty>[];
/// Requests a heap snapshot for a given isolate and builds a
/// [HeapSnapshotGraph].
///
/// Note: this method calls [VmService.streamListen] and
/// [VmService.streamCancel] on [EventStreams.kHeapSnapshot].
static Future<HeapSnapshotGraph> getSnapshot(
VmService service, IsolateRef isolate) async {
await service.streamListen(EventStreams.kHeapSnapshot);
final completer = Completer<HeapSnapshotGraph>();
final chunks = <ByteData>[];
service.onHeapSnapshotEvent.listen((e) async {
chunks.add(e.data);
if (e.last) {
await service.streamCancel(EventStreams.kHeapSnapshot);
completer.complete(HeapSnapshotGraph.fromChunks(chunks));
}
});
await service.requestHeapSnapshot(isolate.id);
return completer.future;
}
/// Populates the [HeapSnapshotGraph] by parsing the events from the
/// `HeapSnapshot` stream.
HeapSnapshotGraph.fromChunks(List<ByteData> chunks) {
final reader = _ReadStream(chunks);
// Skip magic header
for (int i = 0; i < 8; ++i) {
reader.readByte();
}
_flags = reader.readUnsigned();
_name = reader.readUtf8();
_shallowSize = reader.readUnsigned();
_capacity = reader.readUnsigned();
_externalSize = reader.readUnsigned();
_populateClasses(reader);
_referenceCount = reader.readUnsigned();
_populateObjects(reader);
_populateExternalProperties(reader);
}
void _populateClasses(_ReadStream reader) {
final classCount = reader.readUnsigned();
for (int i = 0; i < classCount; ++i) {
_classes.add(HeapSnapshotClass._read(reader));
}
}
void _populateObjects(_ReadStream reader) {
final objectCount = reader.readUnsigned();
for (int i = 0; i < objectCount; ++i) {
_objects.add(HeapSnapshotObject._read(reader));
}
}
void _populateExternalProperties(_ReadStream reader) {
final propertiesCount = reader.readUnsigned();
for (int i = 0; i < propertiesCount; ++i) {
_externalProperties.add(HeapSnapshotExternalProperty._read(reader));
}
}
}
const _kNoData = 0;
const _kNullData = 1;
const _kBoolData = 2;
const _kIntData = 3;
const _kDoubleData = 4;
const _kLatin1Data = 5;
const _kUtf16Data = 6;
const _kLengthData = 7;
const _kNameData = 8;
dynamic _getNonReferenceData(_ReadStream reader) {
final tag = reader.readUnsigned();
switch (tag) {
case _kNoData:
return const HeapSnapshotObjectNoData();
case _kNullData:
return const HeapSnapshotObjectNullData();
case _kBoolData:
return (reader.readByte() == 1);
case _kIntData:
return reader.readUnsigned();
case _kDoubleData:
return reader.readFloat64();
case _kLatin1Data:
final len = reader.readUnsigned();
final str = reader.readLatin1();
return (str.length < len) ? '$str...' : str;
case _kUtf16Data:
final len = reader.readUnsigned();
final str = reader.readUtf16();
return (str.length < len) ? '$str...' : str;
return reader.readUtf16();
case _kLengthData:
return HeapSnapshotObjectLengthData(reader.readUnsigned());
case _kNameData:
return reader.readUtf8();
default:
throw 'Invalid tag: $tag';
}
}
/// Represents that no data is associated with an object.
class HeapSnapshotObjectNoData {
const HeapSnapshotObjectNoData();
}
/// Represents that the data associated with an object is null.
class HeapSnapshotObjectNullData {
const HeapSnapshotObjectNullData();
}
/// Represents the length of an object.
class HeapSnapshotObjectLengthData {
final int length;
HeapSnapshotObjectLengthData(this.length);
}