blob: 2eb3e210e9897ac514d5808fe44d5910cda317fa [file] [log] [blame]
// Copyright 2020 The Chromium Authors. 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:convert';
/// Monitor heap object allocations (in the VM). The allocation monitor will
/// cause 'start' event exist in the HeapSample. Immediately afterwards a
/// 'continues' event is added on each subsequent timestamp tick (HeapSample)
/// until another monitor start event. A 'reset' event stops the 'continues'
/// event for one timestamp tick with a 'reset' event. Immediately after the
/// reset event a 'continues' event will again appear in the HeapSample's
/// MemoryEventInfo - until a new monitor is started. One monitor exists per
/// VM connection.
class AllocationAccumulator {
AllocationAccumulator(this._start, this._continues, this._reset);
AllocationAccumulator.start()
: _start = true,
_continues = false,
_reset = false;
AllocationAccumulator.continues()
: _start = false,
_continues = true,
_reset = false;
AllocationAccumulator.reset()
: _start = false,
_continues = false,
_reset = true;
factory AllocationAccumulator.fromJson(Map<String, dynamic> json) =>
AllocationAccumulator(
json['start'] as bool,
json['continues'] as bool,
json['reset'] as bool,
);
Map<String, dynamic> toJson() => <String, dynamic>{
'start': _start,
'continues': _continues,
'reset': _reset,
};
static AllocationAccumulator empty() =>
AllocationAccumulator(false, false, false);
bool get isEmpty => !isStart && !isContinuesVisible && !isReset;
final bool _start;
final bool _continues;
bool continuesVisible = false;
final bool _reset;
bool get isStart => _start;
bool get isContinues => _continues;
bool get isContinuesVisible => isContinues && continuesVisible;
bool get isReset => _reset;
@override
String toString() => '[AllocationAccumulator '
'${const JsonEncoder.withIndent(' ').convert(toJson())}]';
}
class ExtensionEvent {
ExtensionEvent(this.timestamp, this.eventKind, this.data)
: customEventName = null;
ExtensionEvent.custom(
this.timestamp,
this.eventKind,
this.customEventName,
this.data,
);
factory ExtensionEvent.fromJson(Map<String, dynamic> json) =>
ExtensionEvent.custom(
json['timestamp'] as int?,
json['eventKind'] as String?,
json['customEventName'] as String?,
(json['data'] as Map?)?.cast<String, Object>(),
);
Map<String, dynamic> toJson() => <String, dynamic>{
'timestamp': timestamp,
'eventKind': eventKind,
'data': data,
'customEventName': customEventName,
};
static ExtensionEvent empty() =>
ExtensionEvent.custom(null, null, null, null);
bool get isEmpty =>
timestamp == null &&
eventKind == null &&
data == null &&
customEventName == null;
final int? timestamp;
final String? eventKind;
final Map<String, Object>? data;
final String? customEventName;
@override
String toString() => '[ExtensionEvent '
'${const JsonEncoder.withIndent(' ').convert(toJson())}]';
}
class ExtensionEvents {
ExtensionEvents(List<ExtensionEvent> events) {
theEvents.addAll(events);
}
factory ExtensionEvents.fromJson(Map<String, Object> json) {
final List<ExtensionEvent> events = [];
json.forEach((key, value) {
final event = ExtensionEvent.fromJson(value as Map<String, dynamic>);
events.add(event);
});
return ExtensionEvents(events);
}
final theEvents = <ExtensionEvent>[];
bool get isEmpty => theEvents.isEmpty;
bool get isNotEmpty => theEvents.isNotEmpty;
void clear() => theEvents.clear();
Map<String, dynamic> toJson() {
final eventsAsJson = <String, dynamic>{};
var index = 0;
for (var event in theEvents) {
eventsAsJson['$index'] = event.toJson();
index++;
}
return eventsAsJson;
}
@override
String toString() => '[ExtensionEvents = '
'${const JsonEncoder.withIndent(' ').convert(toJson())}]';
}
class EventSample {
EventSample(
this.timestamp,
this.isEventGC,
this.isEventSnapshot,
this.isEventSnapshotAuto,
this.allocationAccumulator,
this.extensionEvents,
);
EventSample.gcEvent(this.timestamp, {ExtensionEvents? events})
: isEventGC = true,
isEventSnapshot = false,
isEventSnapshotAuto = false,
allocationAccumulator = null,
extensionEvents = events;
EventSample.snapshotEvent(
this.timestamp, {
snapshotAuto = false,
ExtensionEvents? events,
}) : isEventGC = false,
isEventSnapshot = !snapshotAuto,
isEventSnapshotAuto = snapshotAuto,
allocationAccumulator = null,
extensionEvents = events;
EventSample.accumulatorStart(
this.timestamp, {
ExtensionEvents? events,
}) : isEventGC = false,
isEventSnapshot = false,
isEventSnapshotAuto = false,
allocationAccumulator = AllocationAccumulator.start(),
extensionEvents = events;
EventSample.accumulatorContinues(
this.timestamp, {
ExtensionEvents? events,
}) : isEventGC = false,
isEventSnapshot = false,
isEventSnapshotAuto = false,
allocationAccumulator = AllocationAccumulator.continues(),
extensionEvents = events;
EventSample.accumulatorReset(
this.timestamp, {
ExtensionEvents? events,
}) : isEventGC = false,
isEventSnapshot = false,
isEventSnapshotAuto = false,
allocationAccumulator = AllocationAccumulator.reset(),
extensionEvents = events;
EventSample.extensionEvent(this.timestamp, this.extensionEvents)
: isEventGC = false,
isEventSnapshot = false,
isEventSnapshotAuto = false,
allocationAccumulator = null;
factory EventSample.fromJson(Map<String, dynamic> json) {
final extensionEvents =
(json['extensionEvents'] as Map?)?.cast<String, Object>();
return EventSample(
json['timestamp'] as int,
(json['gcEvent'] as bool?) ?? false,
(json['snapshotEvent'] as bool?) ?? false,
(json['snapshotAutoEvent'] as bool?) ?? false,
json['allocationAccumulatorEvent'] != null
? AllocationAccumulator.fromJson(json['allocationAccumulatorEvent'])
: null,
extensionEvents != null
? ExtensionEvents.fromJson(extensionEvents)
: null,
);
}
Map<String, dynamic> toJson() => <String, dynamic>{
'timestamp': timestamp,
'gcEvent': isEventGC,
'snapshotEvent': isEventSnapshot,
'snapshotAutoEvent': isEventSnapshotAuto,
'allocationAccumulatorEvent': allocationAccumulator?.toJson(),
'extensionEvents': extensionEvents?.toJson(),
};
EventSample clone(int timestamp, {ExtensionEvents? extensionEvents}) =>
EventSample(
timestamp,
isEventGC,
isEventSnapshot,
isEventSnapshotAuto,
allocationAccumulator,
extensionEvents,
);
/// Create an empty event (all values are nothing)
static EventSample empty() => EventSample(
-1,
false,
false,
false,
AllocationAccumulator.empty(),
null,
);
bool get isEmpty => timestamp == -1;
/// Version of EventSample JSON payload.
static const version = 1;
final int timestamp;
final bool isEventGC;
final bool isEventSnapshot;
final bool isEventSnapshotAuto;
bool get isEventAllocationAccumulator => allocationAccumulator != null;
bool get hasExtensionEvents => extensionEvents != null;
final AllocationAccumulator? allocationAccumulator;
final ExtensionEvents? extensionEvents;
@override
String toString() => '[EventSample timestamp: $timestamp = '
'${const JsonEncoder.withIndent(' ').convert(toJson())}]';
}
/// Engine's Raster Cache estimates.
class RasterCache {
RasterCache._({required this.layerBytes, required this.pictureBytes});
factory RasterCache.fromJson(Map<String, dynamic> json) {
return RasterCache._(
layerBytes: json['layerBytes'],
pictureBytes: json['pictureBytes'],
);
}
static RasterCache empty() => RasterCache._(layerBytes: 0, pictureBytes: 0);
static RasterCache? parse(Map<String, dynamic>? json) =>
json == null ? null : RasterCache.fromJson(json);
int layerBytes;
int pictureBytes;
Map<String, dynamic> toJson() => {
'layerBytes': layerBytes,
'pictureBytes': pictureBytes,
};
@override
String toString() => '[RasterCache '
'${const JsonEncoder.withIndent(' ').convert(toJson())}]';
}