[VM/Timeline] Add getPerfettoVMTimeline Service RPC
TEST=Loaded the response from a getPerfettoVMTimeline request in the Perfetto trace viewer and made sure everything looked correct. Change-Id: I8d4bc35fb36601701976c28653db92b2161e4724 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/288066 Commit-Queue: Derek Xu <derekx@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com>
This commit is contained in:
@@ -28,6 +28,7 @@ src/org/dartlang/vm/service/consumer/GetStackConsumer.java
|
||||
src/org/dartlang/vm/service/consumer/InvokeConsumer.java
|
||||
src/org/dartlang/vm/service/consumer/KillConsumer.java
|
||||
src/org/dartlang/vm/service/consumer/PauseConsumer.java
|
||||
src/org/dartlang/vm/service/consumer/PerfettoTimelineConsumer.java
|
||||
src/org/dartlang/vm/service/consumer/PortListConsumer.java
|
||||
src/org/dartlang/vm/service/consumer/ProcessMemoryUsageConsumer.java
|
||||
src/org/dartlang/vm/service/consumer/ProtocolListConsumer.java
|
||||
@@ -104,6 +105,7 @@ src/org/dartlang/vm/service/element/NullRef.java
|
||||
src/org/dartlang/vm/service/element/Obj.java
|
||||
src/org/dartlang/vm/service/element/ObjRef.java
|
||||
src/org/dartlang/vm/service/element/Parameter.java
|
||||
src/org/dartlang/vm/service/element/PerfettoTimeline.java
|
||||
src/org/dartlang/vm/service/element/PortList.java
|
||||
src/org/dartlang/vm/service/element/ProcessMemoryItem.java
|
||||
src/org/dartlang/vm/service/element/ProcessMemoryUsage.java
|
||||
|
||||
@@ -1 +1 @@
|
||||
version=4.4
|
||||
version=4.5
|
||||
|
||||
@@ -28,7 +28,7 @@ export 'snapshot_graph.dart'
|
||||
HeapSnapshotObjectNoData,
|
||||
HeapSnapshotObjectNullData;
|
||||
|
||||
const String vmServiceVersion = '4.4.0';
|
||||
const String vmServiceVersion = '4.5.0';
|
||||
|
||||
/// @optional
|
||||
const String optional = 'optional';
|
||||
@@ -161,6 +161,7 @@ Map<String, Function> _typeFactories = {
|
||||
'@Object': ObjRef.parse,
|
||||
'Object': Obj.parse,
|
||||
'Parameter': Parameter.parse,
|
||||
'PerfettoTimeline': PerfettoTimeline.parse,
|
||||
'PortList': PortList.parse,
|
||||
'ProfileFunction': ProfileFunction.parse,
|
||||
'ProtocolList': ProtocolList.parse,
|
||||
@@ -218,6 +219,7 @@ Map<String, List<String>> _methodReturnTypes = {
|
||||
'getIsolateGroupMemoryUsage': const ['MemoryUsage'],
|
||||
'getScripts': const ['ScriptList'],
|
||||
'getObject': const ['Obj'],
|
||||
'getPerfettoVMTimeline': const ['PerfettoTimeline'],
|
||||
'getPorts': const ['PortList'],
|
||||
'getRetainingPath': const ['RetainingPath'],
|
||||
'getProcessMemoryUsage': const ['ProcessMemoryUsage'],
|
||||
@@ -774,6 +776,40 @@ abstract class VmServiceInterface {
|
||||
int? count,
|
||||
});
|
||||
|
||||
/// The `getPerfettoVMTimeline` RPC is used to retrieve an object which
|
||||
/// contains a VM timeline trace represented in Perfetto's proto format. See
|
||||
/// [PerfettoTimeline] for a detailed description of the response.
|
||||
///
|
||||
/// The `timeOriginMicros` parameter is the beginning of the time range used
|
||||
/// to filter timeline events. It uses the same monotonic clock as
|
||||
/// dart:developer's `Timeline.now` and the VM embedding API's
|
||||
/// `Dart_TimelineGetMicros`. See [VmServiceInterface.getVMTimelineMicros] for
|
||||
/// access to this clock through the service protocol.
|
||||
///
|
||||
/// The `timeExtentMicros` parameter specifies how large the time range used
|
||||
/// to filter timeline events should be.
|
||||
///
|
||||
/// For example, given `timeOriginMicros` and `timeExtentMicros`, only
|
||||
/// timeline events from the following time range will be returned:
|
||||
/// `(timeOriginMicros, timeOriginMicros + timeExtentMicros)`.
|
||||
///
|
||||
/// If `getPerfettoVMTimeline` is invoked while the current recorder is
|
||||
/// Callback, an [RPCError] with error code `114`, `invalid timeline request`,
|
||||
/// will be returned as timeline events are handled by the embedder in this
|
||||
/// mode.
|
||||
///
|
||||
/// If `getPerfettoVMTimeline` is invoked while the current recorder is one of
|
||||
/// Fuchsia or Macos or Systrace, an [RPCError] with error code `114`,
|
||||
/// `invalid timeline request`, will be returned as timeline events are
|
||||
/// handled by the OS in these modes.
|
||||
///
|
||||
/// If `getPerfettoVMTimeline` is invoked while the current recorder is File
|
||||
/// or Perfettofile, an [RPCError] with error code `114`, `invalid timeline
|
||||
/// request`, will be returned as timeline events are written directly to a
|
||||
/// file, and thus cannot be retrieved through the VM Service, in these modes.
|
||||
Future<PerfettoTimeline> getPerfettoVMTimeline(
|
||||
{int? timeOriginMicros, int? timeExtentMicros});
|
||||
|
||||
/// The `getPorts` RPC is used to retrieve the list of `ReceivePort` instances
|
||||
/// for a given isolate.
|
||||
///
|
||||
@@ -917,7 +953,8 @@ abstract class VmServiceInterface {
|
||||
Future<VM> getVM();
|
||||
|
||||
/// The `getVMTimeline` RPC is used to retrieve an object which contains VM
|
||||
/// timeline events.
|
||||
/// timeline events. See [Timeline] for a detailed description of the
|
||||
/// response.
|
||||
///
|
||||
/// The `timeOriginMicros` parameter is the beginning of the time range used
|
||||
/// to filter timeline events. It uses the same monotonic clock as
|
||||
@@ -1557,6 +1594,12 @@ class VmServerConnection {
|
||||
count: params['count'],
|
||||
);
|
||||
break;
|
||||
case 'getPerfettoVMTimeline':
|
||||
response = await _serviceImplementation.getPerfettoVMTimeline(
|
||||
timeOriginMicros: params!['timeOriginMicros'],
|
||||
timeExtentMicros: params['timeExtentMicros'],
|
||||
);
|
||||
break;
|
||||
case 'getPorts':
|
||||
response = await _serviceImplementation.getPorts(
|
||||
params!['isolateId'],
|
||||
@@ -2119,6 +2162,14 @@ class VmService implements VmServiceInterface {
|
||||
if (count != null) 'count': count,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<PerfettoTimeline> getPerfettoVMTimeline(
|
||||
{int? timeOriginMicros, int? timeExtentMicros}) =>
|
||||
_call('getPerfettoVMTimeline', {
|
||||
if (timeOriginMicros != null) 'timeOriginMicros': timeOriginMicros,
|
||||
if (timeExtentMicros != null) 'timeExtentMicros': timeExtentMicros,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<PortList> getPorts(String isolateId) =>
|
||||
_call('getPorts', {'isolateId': isolateId});
|
||||
@@ -7189,6 +7240,54 @@ class Parameter {
|
||||
'[Parameter parameterType: $parameterType, fixed: $fixed]';
|
||||
}
|
||||
|
||||
/// See [VmServiceInterface.getPerfettoVMTimeline];
|
||||
class PerfettoTimeline extends Response {
|
||||
static PerfettoTimeline? parse(Map<String, dynamic>? json) =>
|
||||
json == null ? null : PerfettoTimeline._fromJson(json);
|
||||
|
||||
/// A Base64 string representing the requested timeline trace in Perfetto's
|
||||
/// proto format.
|
||||
String? trace;
|
||||
|
||||
/// The start of the period of time covered by the trace.
|
||||
int? timeOriginMicros;
|
||||
|
||||
/// The duration of time covered by the trace.
|
||||
int? timeExtentMicros;
|
||||
|
||||
PerfettoTimeline({
|
||||
this.trace,
|
||||
this.timeOriginMicros,
|
||||
this.timeExtentMicros,
|
||||
});
|
||||
|
||||
PerfettoTimeline._fromJson(Map<String, dynamic> json)
|
||||
: super._fromJson(json) {
|
||||
trace = json['trace'] ?? '';
|
||||
timeOriginMicros = json['timeOriginMicros'] ?? -1;
|
||||
timeExtentMicros = json['timeExtentMicros'] ?? -1;
|
||||
}
|
||||
|
||||
@override
|
||||
String get type => 'PerfettoTimeline';
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json['type'] = type;
|
||||
json.addAll({
|
||||
'trace': trace ?? '',
|
||||
'timeOriginMicros': timeOriginMicros ?? -1,
|
||||
'timeExtentMicros': timeExtentMicros ?? -1,
|
||||
});
|
||||
return json;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => '[PerfettoTimeline ' //
|
||||
'trace: $trace, timeOriginMicros: $timeOriginMicros, timeExtentMicros: $timeExtentMicros]';
|
||||
}
|
||||
|
||||
/// A `PortList` contains a list of ports associated with some isolate.
|
||||
///
|
||||
/// See [VmServiceInterface.getPorts].
|
||||
@@ -8226,6 +8325,7 @@ class Success extends Response {
|
||||
String toString() => '[Success]';
|
||||
}
|
||||
|
||||
/// See [VmServiceInterface.getVMTimeline];
|
||||
class Timeline extends Response {
|
||||
static Timeline? parse(Map<String, dynamic>? json) =>
|
||||
json == null ? null : Timeline._fromJson(json);
|
||||
|
||||
+76
-40
@@ -4094,6 +4094,77 @@ static void GetIsolateMetric(Thread* thread, JSONStream* js) {
|
||||
HandleNativeMetric(thread, js, id);
|
||||
}
|
||||
|
||||
enum GetVMTimelineResponseFormat { JSON = 0, Perfetto = 1 };
|
||||
|
||||
inline void GetVMTimelineCommon(GetVMTimelineResponseFormat format,
|
||||
Thread* thread,
|
||||
JSONStream* js) {
|
||||
Isolate* isolate = thread->isolate();
|
||||
ASSERT(isolate != nullptr);
|
||||
StackZone zone(thread);
|
||||
Timeline::ReclaimCachedBlocksFromThreads();
|
||||
TimelineEventRecorder* timeline_recorder = Timeline::recorder();
|
||||
ASSERT(timeline_recorder != nullptr);
|
||||
const char* name = timeline_recorder->name();
|
||||
if (strcmp(name, CALLBACK_RECORDER_NAME) == 0) {
|
||||
js->PrintError(kInvalidTimelineRequest,
|
||||
"A recorder of type \"%s\" is currently in use. As a "
|
||||
"result, timeline events are handled by the embedder rather "
|
||||
"than the VM.",
|
||||
timeline_recorder->name());
|
||||
return;
|
||||
} else if (strcmp(name, FUCHSIA_RECORDER_NAME) == 0 ||
|
||||
strcmp(name, SYSTRACE_RECORDER_NAME) == 0 ||
|
||||
strcmp(name, MACOS_RECORDER_NAME) == 0) {
|
||||
js->PrintError(
|
||||
kInvalidTimelineRequest,
|
||||
"A recorder of type \"%s\" is currently in use. As a result, timeline "
|
||||
"events are handled by the OS rather than the VM. See the VM service "
|
||||
"documentation for more details on where timeline events can be found "
|
||||
"for this recorder type.",
|
||||
timeline_recorder->name());
|
||||
return;
|
||||
} else if (strcmp(name, FILE_RECORDER_NAME) == 0 ||
|
||||
strcmp(name, PERFETTO_FILE_RECORDER_NAME) == 0) {
|
||||
js->PrintError(kInvalidTimelineRequest,
|
||||
"A recorder of type \"%s\" is currently in use. As a "
|
||||
"result, timeline events are written directly to a file and "
|
||||
"thus cannot be retrieved through the VM Service.",
|
||||
timeline_recorder->name());
|
||||
return;
|
||||
}
|
||||
int64_t time_origin_micros =
|
||||
Int64Parameter::Parse(js->LookupParam("timeOriginMicros"));
|
||||
int64_t time_extent_micros =
|
||||
Int64Parameter::Parse(js->LookupParam("timeExtentMicros"));
|
||||
TimelineEventFilter filter(time_origin_micros, time_extent_micros);
|
||||
if (format == GetVMTimelineResponseFormat::JSON) {
|
||||
timeline_recorder->PrintJSON(js, &filter);
|
||||
} else if (format == GetVMTimelineResponseFormat::Perfetto) {
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
// This branch will never be reached when SUPPORT_PERFETTO is not defined,
|
||||
// because |GetPerfettoVMTimeline| is not defined when SUPPORT_PERFETTO is
|
||||
// not defined.
|
||||
timeline_recorder->PrintPerfettoTimeline(js, filter);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
static const MethodParameter* const get_perfetto_vm_timeline_params[] = {
|
||||
NO_ISOLATE_PARAMETER,
|
||||
new Int64Parameter("timeOriginMicros", /*required=*/false),
|
||||
new Int64Parameter("timeExtentMicros", /*required=*/false),
|
||||
nullptr,
|
||||
};
|
||||
|
||||
static void GetPerfettoVMTimeline(Thread* thread, JSONStream* js) {
|
||||
GetVMTimelineCommon(GetVMTimelineResponseFormat::Perfetto, thread, js);
|
||||
}
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
|
||||
static void SetVMTimelineFlags(Thread* thread, JSONStream* js) {
|
||||
#if !defined(SUPPORT_TIMELINE)
|
||||
PrintSuccess(js);
|
||||
@@ -4161,46 +4232,7 @@ static const MethodParameter* const get_vm_timeline_params[] = {
|
||||
};
|
||||
|
||||
static void GetVMTimeline(Thread* thread, JSONStream* js) {
|
||||
Isolate* isolate = thread->isolate();
|
||||
ASSERT(isolate != nullptr);
|
||||
StackZone zone(thread);
|
||||
Timeline::ReclaimCachedBlocksFromThreads();
|
||||
TimelineEventRecorder* timeline_recorder = Timeline::recorder();
|
||||
ASSERT(timeline_recorder != nullptr);
|
||||
const char* name = timeline_recorder->name();
|
||||
if (strcmp(name, CALLBACK_RECORDER_NAME) == 0) {
|
||||
js->PrintError(kInvalidTimelineRequest,
|
||||
"A recorder of type \"%s\" is currently in use. As a "
|
||||
"result, timeline events are handled by the embedder rather "
|
||||
"than the VM.",
|
||||
timeline_recorder->name());
|
||||
return;
|
||||
} else if (strcmp(name, FUCHSIA_RECORDER_NAME) == 0 ||
|
||||
strcmp(name, SYSTRACE_RECORDER_NAME) == 0 ||
|
||||
strcmp(name, MACOS_RECORDER_NAME) == 0) {
|
||||
js->PrintError(
|
||||
kInvalidTimelineRequest,
|
||||
"A recorder of type \"%s\" is currently in use. As a result, timeline "
|
||||
"events are handled by the OS rather than the VM. See the VM service "
|
||||
"documentation for more details on where timeline events can be found "
|
||||
"for this recorder type.",
|
||||
timeline_recorder->name());
|
||||
return;
|
||||
} else if (strcmp(name, FILE_RECORDER_NAME) == 0 ||
|
||||
strcmp(name, PERFETTO_FILE_RECORDER_NAME) == 0) {
|
||||
js->PrintError(kInvalidTimelineRequest,
|
||||
"A recorder of type \"%s\" is currently in use. As a "
|
||||
"result, timeline events are written directly to a file and "
|
||||
"thus cannot be retrieved through the VM Service.",
|
||||
timeline_recorder->name());
|
||||
return;
|
||||
}
|
||||
int64_t time_origin_micros =
|
||||
Int64Parameter::Parse(js->LookupParam("timeOriginMicros"));
|
||||
int64_t time_extent_micros =
|
||||
Int64Parameter::Parse(js->LookupParam("timeExtentMicros"));
|
||||
TimelineEventFilter filter(time_origin_micros, time_extent_micros);
|
||||
timeline_recorder->PrintJSON(js, &filter);
|
||||
GetVMTimelineCommon(GetVMTimelineResponseFormat::JSON, thread, js);
|
||||
}
|
||||
|
||||
static const char* const step_enum_names[] = {
|
||||
@@ -5771,6 +5803,10 @@ static const ServiceMethodDescriptor service_methods_[] = {
|
||||
get_instances_params },
|
||||
{ "getInstancesAsList", GetInstancesAsList,
|
||||
get_instances_as_list_params },
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
{ "getPerfettoVMTimeline", GetPerfettoVMTimeline,
|
||||
get_perfetto_vm_timeline_params },
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
{ "getPorts", GetPorts,
|
||||
get_ports_params },
|
||||
{ "getIsolate", GetIsolate,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Dart VM Service Protocol 4.4
|
||||
# Dart VM Service Protocol 4.5
|
||||
|
||||
> Please post feedback to the [observatory-discuss group][discuss-list]
|
||||
|
||||
This document describes of _version 4.4_ of the Dart VM Service Protocol. This
|
||||
This document describes of _version 4.5_ of the Dart VM Service Protocol. This
|
||||
protocol is used to communicate with a running Dart Virtual Machine.
|
||||
|
||||
To use the Service Protocol, start the VM with the *--observe* flag.
|
||||
@@ -1013,6 +1013,46 @@ Int32List, Int64List, Float32List, Float64List, Inst32x3List,
|
||||
Float32x4List, and Float64x2List. These parameters are otherwise
|
||||
ignored.
|
||||
|
||||
### getPerfettoVMTimeline
|
||||
|
||||
```
|
||||
PerfettoTimeline getPerfettoVMTimeline(int timeOriginMicros [optional],
|
||||
int timeExtentMicros [optional])
|
||||
```
|
||||
|
||||
The _getPerfettoVMTimeline_ RPC is used to retrieve an object which contains a
|
||||
VM timeline trace represented in Perfetto's proto format. See
|
||||
[PerfettoTimeline](#perfettotimeline) for a detailed description of the
|
||||
response.
|
||||
|
||||
The _timeOriginMicros_ parameter is the beginning of the time range used to
|
||||
filter timeline events. It uses the same monotonic clock as dart:developer's
|
||||
`Timeline.now` and the VM embedding API's `Dart_TimelineGetMicros`. See
|
||||
[getVMTimelineMicros](#getvmtimelinemicros) for access to this clock through the
|
||||
service protocol.
|
||||
|
||||
The _timeExtentMicros_ parameter specifies how large the time range used to
|
||||
filter timeline events should be.
|
||||
|
||||
For example, given _timeOriginMicros_ and _timeExtentMicros_, only timeline
|
||||
events from the following time range will be returned:
|
||||
`(timeOriginMicros, timeOriginMicros + timeExtentMicros)`.
|
||||
|
||||
If _getPerfettoVMTimeline_ is invoked while the current recorder is Callback, an
|
||||
[RPC error](#rpc-error) with error code _114_, `invalid timeline request`, will
|
||||
be returned as timeline events are handled by the embedder in this mode.
|
||||
|
||||
If _getPerfettoVMTimeline_ is invoked while the current recorder is one of
|
||||
Fuchsia or Macos or Systrace, an [RPC error](#rpc-error) with error code _114_,
|
||||
`invalid timeline request`, will be returned as timeline events are handled by
|
||||
the OS in these modes.
|
||||
|
||||
If _getPerfettoVMTimeline_ is invoked while the current recorder is File or
|
||||
Perfettofile, an [RPC error](#rpc-error) with error code _114_,
|
||||
`invalid timeline request`, will be returned as timeline events are written
|
||||
directly to a file, and thus cannot be retrieved through the VM Service, in
|
||||
these modes.
|
||||
|
||||
### getPorts
|
||||
|
||||
```
|
||||
@@ -1192,7 +1232,7 @@ Timeline getVMTimeline(int timeOriginMicros [optional],
|
||||
```
|
||||
|
||||
The _getVMTimeline_ RPC is used to retrieve an object which contains VM timeline
|
||||
events.
|
||||
events. See [Timeline](#timeline) for a detailed description of the response.
|
||||
|
||||
The _timeOriginMicros_ parameter is the beginning of the time range used to filter
|
||||
timeline events. It uses the same monotonic clock as dart:developer's `Timeline.now`
|
||||
@@ -3746,6 +3786,24 @@ A _Parameter_ is a representation of a function parameter.
|
||||
|
||||
See [Instance](#instance).
|
||||
|
||||
### PerfettoTimeline
|
||||
|
||||
```
|
||||
class PerfettoTimeline extends Response {
|
||||
// A Base64 string representing the requested timeline trace in Perfetto's
|
||||
// proto format.
|
||||
string trace;
|
||||
|
||||
// The start of the period of time covered by the trace.
|
||||
int timeOriginMicros;
|
||||
|
||||
// The duration of time covered by the trace.
|
||||
int timeExtentMicros;
|
||||
}
|
||||
```
|
||||
|
||||
See [getPerfettoVMTimeline](#getperfettovmtimeline);
|
||||
|
||||
### PortList
|
||||
|
||||
```
|
||||
@@ -4254,6 +4312,8 @@ class Timeline extends Response {
|
||||
}
|
||||
```
|
||||
|
||||
See [getVMTimeline](#getvmtimeline);
|
||||
|
||||
### TimelineEvent
|
||||
|
||||
```
|
||||
@@ -4524,5 +4584,6 @@ version | comments
|
||||
4.2 | Added `getInstancesAsList` RPC.
|
||||
4.3 | Added `isSealed`, `isMixinClass`, `isBaseClass`, `isInterfaceClass`, and `isFinal` properties to `Class`.
|
||||
4.4 | Added `label` property to `@Instance`. Added `UserTag` to `InstanceKind`.
|
||||
4.5 | Added `getPerfettoVMTimeline` RPC.
|
||||
|
||||
[discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
|
||||
|
||||
+150
-12
@@ -84,7 +84,16 @@ inline void PopulateProcessDescriptorPacket(
|
||||
}
|
||||
|
||||
inline const std::tuple<std::unique_ptr<const uint8_t[]>, intptr_t>
|
||||
GetProtoPreamble(const intptr_t size) {
|
||||
GetProtoPreamble(
|
||||
protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet) {
|
||||
ASSERT(packet != nullptr);
|
||||
|
||||
intptr_t size = 0;
|
||||
for (const protozero::ScatteredHeapBuffer::Slice& slice :
|
||||
packet->GetSlices()) {
|
||||
size += slice.size() - slice.unused_bytes();
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> preamble =
|
||||
std::make_unique<uint8_t[]>(perfetto::TracePacket::kMaxPreambleBytes);
|
||||
uint8_t* ptr = &preamble[0];
|
||||
@@ -100,6 +109,23 @@ GetProtoPreamble(const intptr_t size) {
|
||||
return std::make_tuple(std::move(preamble), preamble_size);
|
||||
}
|
||||
|
||||
inline void AppendPacketToJSONBase64String(
|
||||
JSONBase64String* jsonBase64String,
|
||||
protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet) {
|
||||
ASSERT(jsonBase64String != nullptr);
|
||||
ASSERT(packet != nullptr);
|
||||
|
||||
auto& response = perfetto_utils::GetProtoPreamble(packet);
|
||||
const uint8_t* preamble = std::get<0>(response).get();
|
||||
const intptr_t preamble_length = std::get<1>(response);
|
||||
jsonBase64String->AppendBytes(preamble, preamble_length);
|
||||
for (const protozero::ScatteredHeapBuffer::Slice& slice :
|
||||
packet->GetSlices()) {
|
||||
jsonBase64String->AppendBytes(slice.start(),
|
||||
slice.size() - slice.unused_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace perfetto_utils
|
||||
#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
|
||||
|
||||
@@ -1350,7 +1376,42 @@ void TimelineEventRecorder::PrintJSONMeta(const JSONArray& jsarr_events) {
|
||||
value->PrintJSON(jsarr_events);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void TimelineEventRecorder::PrintPerfettoMeta(
|
||||
JSONBase64String* jsonBase64String) {
|
||||
ASSERT(jsonBase64String != nullptr);
|
||||
|
||||
perfetto_utils::PopulateClockSnapshotPacket(packet_.get());
|
||||
perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String, &packet_);
|
||||
packet_.Reset();
|
||||
perfetto_utils::PopulateProcessDescriptorPacket(packet_.get());
|
||||
perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String, &packet_);
|
||||
packet_.Reset();
|
||||
|
||||
for (SimpleHashMap::Entry* entry =
|
||||
async_track_uuid_to_track_metadata().Start();
|
||||
entry != nullptr;
|
||||
entry = async_track_uuid_to_track_metadata().Next(entry)) {
|
||||
AsyncTimelineTrackMetadata* value =
|
||||
static_cast<AsyncTimelineTrackMetadata*>(entry->value);
|
||||
value->PopulateTracePacket(packet_.get());
|
||||
perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String, &packet_);
|
||||
packet_.Reset();
|
||||
}
|
||||
|
||||
MutexLocker ml(&track_uuid_to_track_metadata_lock_);
|
||||
for (SimpleHashMap::Entry* entry = track_uuid_to_track_metadata_.Start();
|
||||
entry != nullptr; entry = track_uuid_to_track_metadata_.Next(entry)) {
|
||||
TimelineTrackMetadata* value =
|
||||
static_cast<TimelineTrackMetadata*>(entry->value);
|
||||
value->PopulateTracePacket(packet_.get());
|
||||
perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String, &packet_);
|
||||
packet_.Reset();
|
||||
}
|
||||
}
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() {
|
||||
// Grab the current thread.
|
||||
@@ -1432,6 +1493,15 @@ void TimelineEventRecorder::ThreadBlockCompleteEvent(TimelineEvent* event) {
|
||||
if (event == nullptr) {
|
||||
return;
|
||||
}
|
||||
#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
|
||||
// Async track metadata is only written in Perfetto traces, and Perfetto
|
||||
// traces cannot be written when SUPPORT_PERFETTO is not defined, or when
|
||||
// PRODUCT is defined.
|
||||
if (event->event_type() == TimelineEvent::kAsyncBegin ||
|
||||
event->event_type() == TimelineEvent::kAsyncInstant) {
|
||||
AddAsyncTrackMetadataBasedOnEvent(*event);
|
||||
}
|
||||
#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
|
||||
// Grab the current thread.
|
||||
OSThread* thread = OSThread::Current();
|
||||
ASSERT(thread != nullptr);
|
||||
@@ -1625,6 +1695,19 @@ void TimelineEventFixedBufferRecorder::PrintJSONEvents(
|
||||
});
|
||||
}
|
||||
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void TimelineEventFixedBufferRecorder::PrintPerfettoEvents(
|
||||
JSONBase64String* jsonBase64String,
|
||||
const TimelineEventFilter& filter) {
|
||||
PrintEventsCommon(filter, [this,
|
||||
&jsonBase64String](const TimelineEvent& event) {
|
||||
event.PopulateTracePacket(packet().get());
|
||||
perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String, &packet());
|
||||
packet().Reset();
|
||||
});
|
||||
}
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
|
||||
void TimelineEventFixedBufferRecorder::PrintJSON(JSONStream* js,
|
||||
TimelineEventFilter* filter) {
|
||||
JSONObject topLevel(js);
|
||||
@@ -1638,6 +1721,29 @@ void TimelineEventFixedBufferRecorder::PrintJSON(JSONStream* js,
|
||||
topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
|
||||
}
|
||||
|
||||
#define PRINT_PERFETTO_TIMELINE_BODY \
|
||||
JSONObject jsobj_topLevel(js); \
|
||||
jsobj_topLevel.AddProperty("type", "PerfettoTimeline"); \
|
||||
\
|
||||
js->AppendSerializedObject("\"trace\":"); \
|
||||
{ \
|
||||
JSONBase64String jsonBase64String(js); \
|
||||
PrintPerfettoMeta(&jsonBase64String); \
|
||||
PrintPerfettoEvents(&jsonBase64String, filter); \
|
||||
} \
|
||||
\
|
||||
jsobj_topLevel.AddPropertyTimeMicros("timeOriginMicros", \
|
||||
TimeOriginMicros()); \
|
||||
jsobj_topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
|
||||
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void TimelineEventFixedBufferRecorder::PrintPerfettoTimeline(
|
||||
JSONStream* js,
|
||||
const TimelineEventFilter& filter) {
|
||||
PRINT_PERFETTO_TIMELINE_BODY
|
||||
}
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
|
||||
void TimelineEventFixedBufferRecorder::PrintTraceEvent(
|
||||
JSONStream* js,
|
||||
TimelineEventFilter* filter) {
|
||||
@@ -1645,7 +1751,7 @@ void TimelineEventFixedBufferRecorder::PrintTraceEvent(
|
||||
PrintJSONMeta(events);
|
||||
PrintJSONEvents(events, *filter);
|
||||
}
|
||||
#endif
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
TimelineEventBlock* TimelineEventFixedBufferRecorder::GetHeadBlockLocked() {
|
||||
return &blocks_[0];
|
||||
@@ -1719,12 +1825,20 @@ void TimelineEventCallbackRecorder::PrintJSON(JSONStream* js,
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void TimelineEventCallbackRecorder::PrintPerfettoTimeline(
|
||||
JSONStream* js,
|
||||
const TimelineEventFilter& filter) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
|
||||
void TimelineEventCallbackRecorder::PrintTraceEvent(
|
||||
JSONStream* js,
|
||||
TimelineEventFilter* filter) {
|
||||
JSONArray events(js);
|
||||
}
|
||||
#endif
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
TimelineEvent* TimelineEventCallbackRecorder::StartEvent() {
|
||||
TimelineEvent* event = new TimelineEvent();
|
||||
@@ -1811,12 +1925,20 @@ void TimelineEventPlatformRecorder::PrintJSON(JSONStream* js,
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void TimelineEventPlatformRecorder::PrintPerfettoTimeline(
|
||||
JSONStream* js,
|
||||
const TimelineEventFilter& filter) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
|
||||
void TimelineEventPlatformRecorder::PrintTraceEvent(
|
||||
JSONStream* js,
|
||||
TimelineEventFilter* filter) {
|
||||
JSONArray events(js);
|
||||
}
|
||||
#endif
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
TimelineEvent* TimelineEventPlatformRecorder::StartEvent() {
|
||||
TimelineEvent* event = new TimelineEvent();
|
||||
@@ -2028,13 +2150,8 @@ TimelineEventPerfettoFileRecorder::~TimelineEventPerfettoFileRecorder() {
|
||||
void TimelineEventPerfettoFileRecorder::WritePacket(
|
||||
protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet)
|
||||
const {
|
||||
intptr_t size = 0;
|
||||
for (const protozero::ScatteredHeapBuffer::Slice& slice :
|
||||
packet->GetSlices()) {
|
||||
size += slice.size() - slice.unused_bytes();
|
||||
}
|
||||
const std::tuple<std::unique_ptr<const uint8_t[]>, intptr_t>& response =
|
||||
perfetto_utils::GetProtoPreamble(size);
|
||||
perfetto_utils::GetProtoPreamble(packet);
|
||||
Write(reinterpret_cast<const char*>(std::get<0>(response).get()),
|
||||
std::get<1>(response));
|
||||
for (const protozero::ScatteredHeapBuffer::Slice& slice :
|
||||
@@ -2093,6 +2210,19 @@ void TimelineEventEndlessRecorder::PrintJSONEvents(
|
||||
});
|
||||
}
|
||||
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void TimelineEventEndlessRecorder::PrintPerfettoEvents(
|
||||
JSONBase64String* jsonBase64String,
|
||||
const TimelineEventFilter& filter) {
|
||||
PrintEventsCommon(filter, [this,
|
||||
&jsonBase64String](const TimelineEvent& event) {
|
||||
event.PopulateTracePacket(packet().get());
|
||||
perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String, &packet());
|
||||
packet().Reset();
|
||||
});
|
||||
}
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
|
||||
void TimelineEventEndlessRecorder::PrintJSON(JSONStream* js,
|
||||
TimelineEventFilter* filter) {
|
||||
JSONObject topLevel(js);
|
||||
@@ -2106,6 +2236,14 @@ void TimelineEventEndlessRecorder::PrintJSON(JSONStream* js,
|
||||
topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
|
||||
}
|
||||
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void TimelineEventEndlessRecorder::PrintPerfettoTimeline(
|
||||
JSONStream* js,
|
||||
const TimelineEventFilter& filter) {
|
||||
PRINT_PERFETTO_TIMELINE_BODY
|
||||
}
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
|
||||
void TimelineEventEndlessRecorder::PrintTraceEvent(
|
||||
JSONStream* js,
|
||||
TimelineEventFilter* filter) {
|
||||
@@ -2113,7 +2251,7 @@ void TimelineEventEndlessRecorder::PrintTraceEvent(
|
||||
PrintJSONMeta(events);
|
||||
PrintJSONEvents(events, *filter);
|
||||
}
|
||||
#endif
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
TimelineEventBlock* TimelineEventEndlessRecorder::GetHeadBlockLocked() {
|
||||
return head_;
|
||||
|
||||
+47
-8
@@ -49,6 +49,7 @@ namespace dart {
|
||||
#endif // !defined(SUPPORT_TIMELINE)
|
||||
|
||||
class JSONArray;
|
||||
class JSONBase64String;
|
||||
class JSONObject;
|
||||
class JSONStream;
|
||||
class JSONWriter;
|
||||
@@ -889,8 +890,15 @@ class TimelineEventRecorder : public MallocAllocated {
|
||||
// Interface method(s) which must be implemented.
|
||||
#ifndef PRODUCT
|
||||
virtual void PrintJSON(JSONStream* js, TimelineEventFilter* filter) = 0;
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
/*
|
||||
* Prints a PerfettoTimeline service response into |js|.
|
||||
*/
|
||||
virtual void PrintPerfettoTimeline(JSONStream* js,
|
||||
const TimelineEventFilter& filter) = 0;
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
virtual void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter) = 0;
|
||||
#endif
|
||||
#endif // !defined(PRODUCT)
|
||||
virtual const char* name() const = 0;
|
||||
virtual intptr_t Size() = 0;
|
||||
TimelineEventBlock* GetNewBlock();
|
||||
@@ -929,7 +937,14 @@ class TimelineEventRecorder : public MallocAllocated {
|
||||
// Utility method(s).
|
||||
#ifndef PRODUCT
|
||||
void PrintJSONMeta(const JSONArray& jsarr_events);
|
||||
#endif
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
/*
|
||||
* Appends metadata about the timeline in Perfetto's proto format to
|
||||
* |jsonBase64String|.
|
||||
*/
|
||||
void PrintPerfettoMeta(JSONBase64String* jsonBase64String);
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
#endif // !defined(PRODUCT)
|
||||
TimelineEvent* ThreadBlockStartEvent();
|
||||
void ThreadBlockCompleteEvent(TimelineEvent* event);
|
||||
|
||||
@@ -972,8 +987,12 @@ class TimelineEventFixedBufferRecorder : public TimelineEventRecorder {
|
||||
|
||||
#ifndef PRODUCT
|
||||
void PrintJSON(JSONStream* js, TimelineEventFilter* filter) final;
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void PrintPerfettoTimeline(JSONStream* js,
|
||||
const TimelineEventFilter& filter) final;
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter) final;
|
||||
#endif
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
intptr_t Size();
|
||||
|
||||
@@ -987,7 +1006,11 @@ class TimelineEventFixedBufferRecorder : public TimelineEventRecorder {
|
||||
#ifndef PRODUCT
|
||||
void PrintJSONEvents(const JSONArray& array,
|
||||
const TimelineEventFilter& filter);
|
||||
#endif
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void PrintPerfettoEvents(JSONBase64String* jsonBase64String,
|
||||
const TimelineEventFilter& filter);
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
VirtualMemory* memory_;
|
||||
TimelineEventBlock* blocks_;
|
||||
@@ -1040,8 +1063,12 @@ class TimelineEventCallbackRecorder : public TimelineEventRecorder {
|
||||
|
||||
#ifndef PRODUCT
|
||||
void PrintJSON(JSONStream* js, TimelineEventFilter* filter) final;
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void PrintPerfettoTimeline(JSONStream* js,
|
||||
const TimelineEventFilter& filter) final;
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter) final;
|
||||
#endif
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
// Called when |event| is completed. It is unsafe to keep a reference to
|
||||
// |event| as it may be freed as soon as this function returns.
|
||||
@@ -1090,8 +1117,12 @@ class TimelineEventEndlessRecorder : public TimelineEventRecorder {
|
||||
|
||||
#ifndef PRODUCT
|
||||
void PrintJSON(JSONStream* js, TimelineEventFilter* filter) final;
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void PrintPerfettoTimeline(JSONStream* js,
|
||||
const TimelineEventFilter& filter) final;
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter) final;
|
||||
#endif
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
const char* name() const { return ENDLESS_RECORDER_NAME; }
|
||||
intptr_t Size() { return block_index_ * sizeof(TimelineEventBlock); }
|
||||
@@ -1106,7 +1137,11 @@ class TimelineEventEndlessRecorder : public TimelineEventRecorder {
|
||||
#ifndef PRODUCT
|
||||
void PrintJSONEvents(const JSONArray& array,
|
||||
const TimelineEventFilter& filter);
|
||||
#endif
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void PrintPerfettoEvents(JSONBase64String* jsonBase64String,
|
||||
const TimelineEventFilter& filter);
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
TimelineEventBlock* head_;
|
||||
TimelineEventBlock* tail_;
|
||||
@@ -1132,8 +1167,12 @@ class TimelineEventPlatformRecorder : public TimelineEventRecorder {
|
||||
|
||||
#ifndef PRODUCT
|
||||
void PrintJSON(JSONStream* js, TimelineEventFilter* filter) final;
|
||||
#if defined(SUPPORT_PERFETTO)
|
||||
void PrintPerfettoTimeline(JSONStream* js,
|
||||
const TimelineEventFilter& filter) final;
|
||||
#endif // defined(SUPPORT_PERFETTO)
|
||||
void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter) final;
|
||||
#endif
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
// Called when |event| is completed. It is unsafe to keep a reference to
|
||||
// |event| as it may be freed as soon as this function returns.
|
||||
|
||||
Reference in New Issue
Block a user