[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:
Derek Xu
2023-04-20 16:42:23 +00:00
committed by Commit Queue
parent 4b2f4c6977
commit 32c595ea89
7 changed files with 442 additions and 66 deletions
+2
View File
@@ -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
View File
@@ -1 +1 @@
version=4.4
version=4.5
+102 -2
View File
@@ -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
View File
@@ -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,
+64 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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.