From 2a437c54a043d840f65848d752cce604934d8f69 Mon Sep 17 00:00:00 2001 From: Vyacheslav Egorov Date: Thu, 27 Mar 2025 06:44:23 -0700 Subject: [PATCH] [vm] Intern strings when writing Perfetto timeline Intern the following fields: * Category names * Event labels * Debug annotation keys and values This significantly reduces the size of the timeline (e.g. a timeline containing 60k slices goes from 15Mb to 5Mb timeline) This relands commit f2614d24f8841c04d2f675a10bcb6d3416adaa20 with a fix for Android build. TEST=ci and manually Cq-Include-Trybots: luci.dart.try:vm-ffi-android-release-arm64c-try Change-Id: I88d4c5e1142ff66b270a22b82bacd1e9313fa953 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/418220 Reviewed-by: Derek Xu --- .../get_perfetto_vm_timeline_rpc_test.dart | 110 +++++- .../trace/interned_data/interned_data.pb.dart | 87 +++-- .../interned_data/interned_data.pbjson.dart | 50 ++- .../perfetto/trace/trace_packet.pb.dart | 28 +- .../track_event/debug_annotation.pb.dart | 154 ++++++++- .../track_event/debug_annotation.pbjson.dart | 32 +- .../track_event/track_descriptor.pb.dart | 28 +- .../trace/track_event/track_event.pb.dart | 220 +++++++++++- .../trace/track_event/track_event.pbjson.dart | 45 ++- runtime/platform/growable_array.h | 4 + runtime/vm/perfetto_utils.h | 138 ++++++++ .../interned_data/interned_data.pbzero.h | 84 ++++- .../trace/interned_data/interned_data.proto | 6 + .../track_event/debug_annotation.pbzero.h | 170 ++++++++-- .../trace/track_event/debug_annotation.proto | 18 +- .../trace/track_event/track_event.pbzero.h | 186 +++++++++++ .../trace/track_event/track_event.proto | 21 ++ runtime/vm/timeline.cc | 315 +++++++++++++++--- runtime/vm/timeline.h | 2 - runtime/vm/zone.h | 4 + 20 files changed, 1523 insertions(+), 179 deletions(-) diff --git a/pkg/vm_service/test/get_perfetto_vm_timeline_rpc_test.dart b/pkg/vm_service/test/get_perfetto_vm_timeline_rpc_test.dart index 1cfec638820..dcd2c7b75cd 100644 --- a/pkg/vm_service/test/get_perfetto_vm_timeline_rpc_test.dart +++ b/pkg/vm_service/test/get_perfetto_vm_timeline_rpc_test.dart @@ -1,10 +1,14 @@ // Copyright (c) 2023, 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. +// +// VMOptions= +// VMOptions=--intern_strings_when_writing_perfetto_timeline import 'dart:collection'; import 'dart:convert'; import 'dart:developer'; +import 'dart:io' show Platform; import 'package:test/test.dart'; import 'package:vm_service/vm_service.dart' hide Timeline; @@ -35,12 +39,108 @@ void primeTimeline() { Timeline.finishSync(); } -Iterable extractTrackEventsFromTracePackets( +class Deinterner { + final bool stringsShouldBeInterned = Platform.executableArguments + .contains('--intern_strings_when_writing_perfetto_timeline'); + + final Map debugAnnotationNames = {}; + final Map debugAnnotationStringValues = {}; + final Map eventNames = {}; + final Map eventCategories = {}; + + /// Update the state of the interning dictionaries using [InternedData] + /// from the given packet. + void update(TracePacket packet) { + // Clear the state if [TracePacket.sequenceFlags] instructs us to do so. + if (packet.sequenceFlags & + TracePacket_SequenceFlags.SEQ_INCREMENTAL_STATE_CLEARED.value != + 0) { + debugAnnotationNames.clear(); + debugAnnotationStringValues.clear(); + eventNames.clear(); + eventCategories.clear(); + } + + if (!packet.hasInternedData()) { + return; + } + + final internedData = packet.internedData; + for (var e in internedData.debugAnnotationNames) { + debugAnnotationNames[e.iid.toInt()] = e.name; + } + for (var e in internedData.debugAnnotationStringValues) { + debugAnnotationStringValues[e.iid.toInt()] = utf8.decode(e.str); + } + for (var e in internedData.eventNames) { + eventNames[e.iid.toInt()] = e.name; + } + for (var e in internedData.eventCategories) { + eventCategories[e.iid.toInt()] = e.name; + } + } + + /// Deintern contents of the given [TrackEvent]. + void deintern(TrackEvent event) { + if (event.hasName()) { + expect(stringsShouldBeInterned, isFalse); + } + if (event.hasNameIid()) { + expect(stringsShouldBeInterned, isTrue); + expect(event.hasName(), isFalse); + event.name = eventNames[event.nameIid.toInt()]!; + event.clearNameIid(); + } + + if (event.categories.isNotEmpty) { + expect(stringsShouldBeInterned, isFalse); + } + if (event.categoryIids.isNotEmpty) { + expect(stringsShouldBeInterned, isTrue); + expect(event.categories.isEmpty, isTrue); + for (var iid in event.categoryIids) { + event.categories.add(eventCategories[iid.toInt()]!); + } + event.categoryIids.clear(); + } + for (var annotation in event.debugAnnotations) { + if (annotation.hasStringValue()) { + expect(stringsShouldBeInterned, isFalse); + } + if (annotation.hasStringValueIid()) { + expect(stringsShouldBeInterned, isTrue); + expect(annotation.hasStringValue(), isFalse); + annotation.stringValue = + debugAnnotationStringValues[annotation.stringValueIid.toInt()]!; + annotation.clearStringValueIid(); + } + + if (annotation.hasName()) { + expect(stringsShouldBeInterned, isFalse); + } + if (annotation.hasNameIid()) { + expect(stringsShouldBeInterned, isTrue); + expect(annotation.hasName(), isFalse); + annotation.name = debugAnnotationNames[annotation.nameIid.toInt()]!; + annotation.clearNameIid(); + } + } + } +} + +List extractTrackEventsFromTracePackets( List packets, ) { - return packets - .where((packet) => packet.hasTrackEvent()) - .map((packet) => packet.trackEvent); + final result = []; + final deinterner = Deinterner(); + for (var packet in packets) { + deinterner.update(packet); + if (packet.hasTrackEvent()) { + deinterner.deintern(packet.trackEvent); + result.add(packet.trackEvent); + } + } + return result; } Map mapFromListOfDebugAnnotations( @@ -60,7 +160,7 @@ Map mapFromListOfDebugAnnotations( } void checkThatAllEventsHaveIsolateNumbers(Iterable events) { - for (TrackEvent event in events) { + for (final event in events) { final debugAnnotations = mapFromListOfDebugAnnotations(event.debugAnnotations); expect(debugAnnotations['isolateGroupId'], isNotNull); diff --git a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/interned_data/interned_data.pb.dart b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/interned_data/interned_data.pb.dart index adefed4bed1..4b343185a59 100644 --- a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/interned_data/interned_data.pb.dart +++ b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/interned_data/interned_data.pb.dart @@ -21,7 +21,9 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; -import '../profiling/profile_common.pb.dart' as $2; +import '../profiling/profile_common.pb.dart' as $3; +import '../track_event/debug_annotation.pb.dart' as $1; +import '../track_event/track_event.pb.dart' as $2; export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; @@ -36,13 +38,26 @@ export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; /// Next id: 29. class InternedData extends $pb.GeneratedMessage { factory InternedData({ - $core.Iterable<$2.InternedString>? functionNames, - $core.Iterable<$2.Frame>? frames, - $core.Iterable<$2.Callstack>? callstacks, - $core.Iterable<$2.InternedString>? mappingPaths, - $core.Iterable<$2.Mapping>? mappings, + $core.Iterable<$2.EventCategory>? eventCategories, + $core.Iterable<$2.EventName>? eventNames, + $core.Iterable<$1.DebugAnnotationName>? debugAnnotationNames, + $core.Iterable<$3.InternedString>? functionNames, + $core.Iterable<$3.Frame>? frames, + $core.Iterable<$3.Callstack>? callstacks, + $core.Iterable<$3.InternedString>? mappingPaths, + $core.Iterable<$3.Mapping>? mappings, + $core.Iterable<$3.InternedString>? debugAnnotationStringValues, }) { final $result = create(); + if (eventCategories != null) { + $result.eventCategories.addAll(eventCategories); + } + if (eventNames != null) { + $result.eventNames.addAll(eventNames); + } + if (debugAnnotationNames != null) { + $result.debugAnnotationNames.addAll(debugAnnotationNames); + } if (functionNames != null) { $result.functionNames.addAll(functionNames); } @@ -58,6 +73,9 @@ class InternedData extends $pb.GeneratedMessage { if (mappings != null) { $result.mappings.addAll(mappings); } + if (debugAnnotationStringValues != null) { + $result.debugAnnotationStringValues.addAll(debugAnnotationStringValues); + } return $result; } InternedData._() : super(); @@ -73,19 +91,33 @@ class InternedData extends $pb.GeneratedMessage { package: const $pb.PackageName(_omitMessageNames ? '' : 'perfetto.protos'), createEmptyInstance: create) - ..pc<$2.InternedString>( + ..pc<$2.EventCategory>( + 1, _omitFieldNames ? '' : 'eventCategories', $pb.PbFieldType.PM, + subBuilder: $2.EventCategory.create) + ..pc<$2.EventName>( + 2, _omitFieldNames ? '' : 'eventNames', $pb.PbFieldType.PM, + subBuilder: $2.EventName.create) + ..pc<$1.DebugAnnotationName>( + 3, _omitFieldNames ? '' : 'debugAnnotationNames', $pb.PbFieldType.PM, + subBuilder: $1.DebugAnnotationName.create) + ..pc<$3.InternedString>( 5, _omitFieldNames ? '' : 'functionNames', $pb.PbFieldType.PM, - subBuilder: $2.InternedString.create) - ..pc<$2.Frame>(6, _omitFieldNames ? '' : 'frames', $pb.PbFieldType.PM, - subBuilder: $2.Frame.create) - ..pc<$2.Callstack>( + subBuilder: $3.InternedString.create) + ..pc<$3.Frame>(6, _omitFieldNames ? '' : 'frames', $pb.PbFieldType.PM, + subBuilder: $3.Frame.create) + ..pc<$3.Callstack>( 7, _omitFieldNames ? '' : 'callstacks', $pb.PbFieldType.PM, - subBuilder: $2.Callstack.create) - ..pc<$2.InternedString>( + subBuilder: $3.Callstack.create) + ..pc<$3.InternedString>( 17, _omitFieldNames ? '' : 'mappingPaths', $pb.PbFieldType.PM, - subBuilder: $2.InternedString.create) - ..pc<$2.Mapping>(19, _omitFieldNames ? '' : 'mappings', $pb.PbFieldType.PM, - subBuilder: $2.Mapping.create) + subBuilder: $3.InternedString.create) + ..pc<$3.Mapping>(19, _omitFieldNames ? '' : 'mappings', $pb.PbFieldType.PM, + subBuilder: $3.Mapping.create) + ..pc<$3.InternedString>( + 29, + _omitFieldNames ? '' : 'debugAnnotationStringValues', + $pb.PbFieldType.PM, + subBuilder: $3.InternedString.create) ..hasRequiredFields = false; @$core.Deprecated('Using this can add significant overhead to your binary. ' @@ -111,25 +143,38 @@ class InternedData extends $pb.GeneratedMessage { $pb.GeneratedMessage.$_defaultFor(create); static InternedData? _defaultInstance; + @$pb.TagNumber(1) + $pb.PbList<$2.EventCategory> get eventCategories => $_getList(0); + + @$pb.TagNumber(2) + $pb.PbList<$2.EventName> get eventNames => $_getList(1); + + @$pb.TagNumber(3) + $pb.PbList<$1.DebugAnnotationName> get debugAnnotationNames => $_getList(2); + /// Names of functions used in frames below. @$pb.TagNumber(5) - $pb.PbList<$2.InternedString> get functionNames => $_getList(0); + $pb.PbList<$3.InternedString> get functionNames => $_getList(3); /// Frames of callstacks of a program. @$pb.TagNumber(6) - $pb.PbList<$2.Frame> get frames => $_getList(1); + $pb.PbList<$3.Frame> get frames => $_getList(4); /// A callstack of a program. @$pb.TagNumber(7) - $pb.PbList<$2.Callstack> get callstacks => $_getList(2); + $pb.PbList<$3.Callstack> get callstacks => $_getList(5); /// Paths to executable files. @$pb.TagNumber(17) - $pb.PbList<$2.InternedString> get mappingPaths => $_getList(3); + $pb.PbList<$3.InternedString> get mappingPaths => $_getList(6); /// Executable files mapped into processes. @$pb.TagNumber(19) - $pb.PbList<$2.Mapping> get mappings => $_getList(4); + $pb.PbList<$3.Mapping> get mappings => $_getList(7); + + /// Interned string values in the DebugAnnotation proto. + @$pb.TagNumber(29) + $pb.PbList<$3.InternedString> get debugAnnotationStringValues => $_getList(8); } const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); diff --git a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/interned_data/interned_data.pbjson.dart b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/interned_data/interned_data.pbjson.dart index 0f2587062fb..1ac1448a5cf 100644 --- a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/interned_data/interned_data.pbjson.dart +++ b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/interned_data/interned_data.pbjson.dart @@ -25,6 +25,30 @@ import 'dart:typed_data' as $typed_data; const InternedData$json = { '1': 'InternedData', '2': [ + { + '1': 'event_categories', + '3': 1, + '4': 3, + '5': 11, + '6': '.perfetto.protos.EventCategory', + '10': 'eventCategories' + }, + { + '1': 'event_names', + '3': 2, + '4': 3, + '5': 11, + '6': '.perfetto.protos.EventName', + '10': 'eventNames' + }, + { + '1': 'debug_annotation_names', + '3': 3, + '4': 3, + '5': 11, + '6': '.perfetto.protos.DebugAnnotationName', + '10': 'debugAnnotationNames' + }, { '1': 'mapping_paths', '3': 17, @@ -65,14 +89,28 @@ const InternedData$json = { '6': '.perfetto.protos.Callstack', '10': 'callstacks' }, + { + '1': 'debug_annotation_string_values', + '3': 29, + '4': 3, + '5': 11, + '6': '.perfetto.protos.InternedString', + '10': 'debugAnnotationStringValues' + }, ], }; /// Descriptor for `InternedData`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List internedDataDescriptor = $convert.base64Decode( - 'CgxJbnRlcm5lZERhdGESRAoNbWFwcGluZ19wYXRocxgRIAMoCzIfLnBlcmZldHRvLnByb3Rvcy' - '5JbnRlcm5lZFN0cmluZ1IMbWFwcGluZ1BhdGhzEkYKDmZ1bmN0aW9uX25hbWVzGAUgAygLMh8u' - 'cGVyZmV0dG8ucHJvdG9zLkludGVybmVkU3RyaW5nUg1mdW5jdGlvbk5hbWVzEjQKCG1hcHBpbm' - 'dzGBMgAygLMhgucGVyZmV0dG8ucHJvdG9zLk1hcHBpbmdSCG1hcHBpbmdzEi4KBmZyYW1lcxgG' - 'IAMoCzIWLnBlcmZldHRvLnByb3Rvcy5GcmFtZVIGZnJhbWVzEjoKCmNhbGxzdGFja3MYByADKA' - 'syGi5wZXJmZXR0by5wcm90b3MuQ2FsbHN0YWNrUgpjYWxsc3RhY2tz'); + 'CgxJbnRlcm5lZERhdGESSQoQZXZlbnRfY2F0ZWdvcmllcxgBIAMoCzIeLnBlcmZldHRvLnByb3' + 'Rvcy5FdmVudENhdGVnb3J5Ug9ldmVudENhdGVnb3JpZXMSOwoLZXZlbnRfbmFtZXMYAiADKAsy' + 'Gi5wZXJmZXR0by5wcm90b3MuRXZlbnROYW1lUgpldmVudE5hbWVzEloKFmRlYnVnX2Fubm90YX' + 'Rpb25fbmFtZXMYAyADKAsyJC5wZXJmZXR0by5wcm90b3MuRGVidWdBbm5vdGF0aW9uTmFtZVIU' + 'ZGVidWdBbm5vdGF0aW9uTmFtZXMSRAoNbWFwcGluZ19wYXRocxgRIAMoCzIfLnBlcmZldHRvLn' + 'Byb3Rvcy5JbnRlcm5lZFN0cmluZ1IMbWFwcGluZ1BhdGhzEkYKDmZ1bmN0aW9uX25hbWVzGAUg' + 'AygLMh8ucGVyZmV0dG8ucHJvdG9zLkludGVybmVkU3RyaW5nUg1mdW5jdGlvbk5hbWVzEjQKCG' + '1hcHBpbmdzGBMgAygLMhgucGVyZmV0dG8ucHJvdG9zLk1hcHBpbmdSCG1hcHBpbmdzEi4KBmZy' + 'YW1lcxgGIAMoCzIWLnBlcmZldHRvLnByb3Rvcy5GcmFtZVIGZnJhbWVzEjoKCmNhbGxzdGFja3' + 'MYByADKAsyGi5wZXJmZXR0by5wcm90b3MuQ2FsbHN0YWNrUgpjYWxsc3RhY2tzEmQKHmRlYnVn' + 'X2Fubm90YXRpb25fc3RyaW5nX3ZhbHVlcxgdIAMoCzIfLnBlcmZldHRvLnByb3Rvcy5JbnRlcm' + '5lZFN0cmluZ1IbZGVidWdBbm5vdGF0aW9uU3RyaW5nVmFsdWVz'); diff --git a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/trace_packet.pb.dart b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/trace_packet.pb.dart index e663b3a3893..8b79536625f 100644 --- a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/trace_packet.pb.dart +++ b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/trace_packet.pb.dart @@ -22,11 +22,11 @@ import 'dart:core' as $core; import 'package:fixnum/fixnum.dart' as $fixnum; import 'package:protobuf/protobuf.dart' as $pb; -import 'clock_snapshot.pb.dart' as $5; +import 'clock_snapshot.pb.dart' as $6; import 'interned_data/interned_data.pb.dart' as $7; import 'profiling/profile_packet.pb.dart' as $9; import 'track_event/track_descriptor.pb.dart' as $8; -import 'track_event/track_event.pb.dart' as $6; +import 'track_event/track_event.pb.dart' as $2; export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; @@ -68,10 +68,10 @@ enum TracePacket_OptionalTrustedPacketSequenceId { /// Next id: 88. class TracePacket extends $pb.GeneratedMessage { factory TracePacket({ - $5.ClockSnapshot? clockSnapshot, + $6.ClockSnapshot? clockSnapshot, $fixnum.Int64? timestamp, $core.int? trustedPacketSequenceId, - $6.TrackEvent? trackEvent, + $2.TrackEvent? trackEvent, $7.InternedData? internedData, $core.int? sequenceFlags, $core.int? timestampClockId, @@ -135,15 +135,15 @@ class TracePacket extends $pb.GeneratedMessage { createEmptyInstance: create) ..oo(0, [6, 11, 60, 66]) ..oo(1, [10]) - ..aOM<$5.ClockSnapshot>(6, _omitFieldNames ? '' : 'clockSnapshot', - subBuilder: $5.ClockSnapshot.create) + ..aOM<$6.ClockSnapshot>(6, _omitFieldNames ? '' : 'clockSnapshot', + subBuilder: $6.ClockSnapshot.create) ..a<$fixnum.Int64>( 8, _omitFieldNames ? '' : 'timestamp', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) ..a<$core.int>(10, _omitFieldNames ? '' : 'trustedPacketSequenceId', $pb.PbFieldType.OU3) - ..aOM<$6.TrackEvent>(11, _omitFieldNames ? '' : 'trackEvent', - subBuilder: $6.TrackEvent.create) + ..aOM<$2.TrackEvent>(11, _omitFieldNames ? '' : 'trackEvent', + subBuilder: $2.TrackEvent.create) ..aOM<$7.InternedData>(12, _omitFieldNames ? '' : 'internedData', subBuilder: $7.InternedData.create) ..a<$core.int>( @@ -187,9 +187,9 @@ class TracePacket extends $pb.GeneratedMessage { void clearOptionalTrustedPacketSequenceId() => $_clearField($_whichOneof(1)); @$pb.TagNumber(6) - $5.ClockSnapshot get clockSnapshot => $_getN(0); + $6.ClockSnapshot get clockSnapshot => $_getN(0); @$pb.TagNumber(6) - set clockSnapshot($5.ClockSnapshot v) { + set clockSnapshot($6.ClockSnapshot v) { $_setField(6, v); } @@ -198,7 +198,7 @@ class TracePacket extends $pb.GeneratedMessage { @$pb.TagNumber(6) void clearClockSnapshot() => $_clearField(6); @$pb.TagNumber(6) - $5.ClockSnapshot ensureClockSnapshot() => $_ensure(0); + $6.ClockSnapshot ensureClockSnapshot() => $_ensure(0); /// The timestamp of the TracePacket. /// By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on @@ -231,9 +231,9 @@ class TracePacket extends $pb.GeneratedMessage { void clearTrustedPacketSequenceId() => $_clearField(10); @$pb.TagNumber(11) - $6.TrackEvent get trackEvent => $_getN(3); + $2.TrackEvent get trackEvent => $_getN(3); @$pb.TagNumber(11) - set trackEvent($6.TrackEvent v) { + set trackEvent($2.TrackEvent v) { $_setField(11, v); } @@ -242,7 +242,7 @@ class TracePacket extends $pb.GeneratedMessage { @$pb.TagNumber(11) void clearTrackEvent() => $_clearField(11); @$pb.TagNumber(11) - $6.TrackEvent ensureTrackEvent() => $_ensure(3); + $2.TrackEvent ensureTrackEvent() => $_ensure(3); /// Incrementally emitted interned data, valid only on the packet's sequence /// (packets with the same |trusted_packet_sequence_id|). The writer will diff --git a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/debug_annotation.pb.dart b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/debug_annotation.pb.dart index e2550e732b5..e4f37412b3f 100644 --- a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/debug_annotation.pb.dart +++ b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/debug_annotation.pb.dart @@ -19,13 +19,19 @@ import 'dart:core' as $core; +import 'package:fixnum/fixnum.dart' as $fixnum; import 'package:protobuf/protobuf.dart' as $pb; export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; -enum DebugAnnotation_NameField { name, notSet } +enum DebugAnnotation_NameField { nameIid, name, notSet } -enum DebugAnnotation_Value { stringValue, legacyJsonValue, notSet } +enum DebugAnnotation_Value { + stringValue, + legacyJsonValue, + stringValueIid, + notSet +} /// Proto representation of untyped key/value annotations provided in TRACE_EVENT /// macros. Users of the Perfetto SDK should prefer to use the @@ -70,11 +76,16 @@ enum DebugAnnotation_Value { stringValue, legacyJsonValue, notSet } /// Reserved ID: 15 class DebugAnnotation extends $pb.GeneratedMessage { factory DebugAnnotation({ + $fixnum.Int64? nameIid, $core.String? stringValue, $core.String? legacyJsonValue, $core.String? name, + $fixnum.Int64? stringValueIid, }) { final $result = create(); + if (nameIid != null) { + $result.nameIid = nameIid; + } if (stringValue != null) { $result.stringValue = stringValue; } @@ -84,6 +95,9 @@ class DebugAnnotation extends $pb.GeneratedMessage { if (name != null) { $result.name = name; } + if (stringValueIid != null) { + $result.stringValueIid = stringValueIid; + } return $result; } DebugAnnotation._() : super(); @@ -96,6 +110,7 @@ class DebugAnnotation extends $pb.GeneratedMessage { static const $core.Map<$core.int, DebugAnnotation_NameField> _DebugAnnotation_NameFieldByTag = { + 1: DebugAnnotation_NameField.nameIid, 10: DebugAnnotation_NameField.name, 0: DebugAnnotation_NameField.notSet }; @@ -103,6 +118,7 @@ class DebugAnnotation extends $pb.GeneratedMessage { _DebugAnnotation_ValueByTag = { 6: DebugAnnotation_Value.stringValue, 9: DebugAnnotation_Value.legacyJsonValue, + 17: DebugAnnotation_Value.stringValueIid, 0: DebugAnnotation_Value.notSet }; static final $pb.BuilderInfo _i = $pb.BuilderInfo( @@ -110,11 +126,16 @@ class DebugAnnotation extends $pb.GeneratedMessage { package: const $pb.PackageName(_omitMessageNames ? '' : 'perfetto.protos'), createEmptyInstance: create) - ..oo(0, [10]) - ..oo(1, [6, 9]) + ..oo(0, [1, 10]) + ..oo(1, [6, 9, 17]) + ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'nameIid', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) ..aOS(6, _omitFieldNames ? '' : 'stringValue') ..aOS(9, _omitFieldNames ? '' : 'legacyJsonValue') ..aOS(10, _omitFieldNames ? '' : 'name') + ..a<$fixnum.Int64>( + 17, _omitFieldNames ? '' : 'stringValueIid', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) ..hasRequiredFields = false; @$core.Deprecated('Using this can add significant overhead to your binary. ' @@ -148,44 +169,151 @@ class DebugAnnotation extends $pb.GeneratedMessage { _DebugAnnotation_ValueByTag[$_whichOneof(1)]!; void clearValue() => $_clearField($_whichOneof(1)); + /// interned DebugAnnotationName. + @$pb.TagNumber(1) + $fixnum.Int64 get nameIid => $_getI64(0); + @$pb.TagNumber(1) + set nameIid($fixnum.Int64 v) { + $_setInt64(0, v); + } + + @$pb.TagNumber(1) + $core.bool hasNameIid() => $_has(0); + @$pb.TagNumber(1) + void clearNameIid() => $_clearField(1); + + /// interned and non-interned variants of strings. @$pb.TagNumber(6) - $core.String get stringValue => $_getSZ(0); + $core.String get stringValue => $_getSZ(1); @$pb.TagNumber(6) set stringValue($core.String v) { - $_setString(0, v); + $_setString(1, v); } @$pb.TagNumber(6) - $core.bool hasStringValue() => $_has(0); + $core.bool hasStringValue() => $_has(1); @$pb.TagNumber(6) void clearStringValue() => $_clearField(6); /// Legacy instrumentation may not support conversion of nested data to /// NestedValue yet. @$pb.TagNumber(9) - $core.String get legacyJsonValue => $_getSZ(1); + $core.String get legacyJsonValue => $_getSZ(2); @$pb.TagNumber(9) set legacyJsonValue($core.String v) { - $_setString(1, v); + $_setString(2, v); } @$pb.TagNumber(9) - $core.bool hasLegacyJsonValue() => $_has(1); + $core.bool hasLegacyJsonValue() => $_has(2); @$pb.TagNumber(9) void clearLegacyJsonValue() => $_clearField(9); /// non-interned variant. @$pb.TagNumber(10) - $core.String get name => $_getSZ(2); + $core.String get name => $_getSZ(3); @$pb.TagNumber(10) set name($core.String v) { - $_setString(2, v); + $_setString(3, v); } @$pb.TagNumber(10) - $core.bool hasName() => $_has(2); + $core.bool hasName() => $_has(3); @$pb.TagNumber(10) void clearName() => $_clearField(10); + + /// Corresponds to |debug_annotation_string_values| field in InternedData. + @$pb.TagNumber(17) + $fixnum.Int64 get stringValueIid => $_getI64(4); + @$pb.TagNumber(17) + set stringValueIid($fixnum.Int64 v) { + $_setInt64(4, v); + } + + @$pb.TagNumber(17) + $core.bool hasStringValueIid() => $_has(4); + @$pb.TagNumber(17) + void clearStringValueIid() => $_clearField(17); +} + +class DebugAnnotationName extends $pb.GeneratedMessage { + factory DebugAnnotationName({ + $fixnum.Int64? iid, + $core.String? name, + }) { + final $result = create(); + if (iid != null) { + $result.iid = iid; + } + if (name != null) { + $result.name = name; + } + return $result; + } + DebugAnnotationName._() : super(); + factory DebugAnnotationName.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory DebugAnnotationName.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'DebugAnnotationName', + package: + const $pb.PackageName(_omitMessageNames ? '' : 'perfetto.protos'), + createEmptyInstance: create) + ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'iid', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..aOS(2, _omitFieldNames ? '' : 'name') + ..hasRequiredFields = false; + + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + DebugAnnotationName clone() => DebugAnnotationName()..mergeFromMessage(this); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + DebugAnnotationName copyWith(void Function(DebugAnnotationName) updates) => + super.copyWith((message) => updates(message as DebugAnnotationName)) + as DebugAnnotationName; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DebugAnnotationName create() => DebugAnnotationName._(); + DebugAnnotationName createEmptyInstance() => create(); + static $pb.PbList createRepeated() => + $pb.PbList(); + @$core.pragma('dart2js:noInline') + static DebugAnnotationName getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static DebugAnnotationName? _defaultInstance; + + @$pb.TagNumber(1) + $fixnum.Int64 get iid => $_getI64(0); + @$pb.TagNumber(1) + set iid($fixnum.Int64 v) { + $_setInt64(0, v); + } + + @$pb.TagNumber(1) + $core.bool hasIid() => $_has(0); + @$pb.TagNumber(1) + void clearIid() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get name => $_getSZ(1); + @$pb.TagNumber(2) + set name($core.String v) { + $_setString(1, v); + } + + @$pb.TagNumber(2) + $core.bool hasName() => $_has(1); + @$pb.TagNumber(2) + void clearName() => $_clearField(2); } const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); diff --git a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/debug_annotation.pbjson.dart b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/debug_annotation.pbjson.dart index 2799fb2e97d..60ac7418151 100644 --- a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/debug_annotation.pbjson.dart +++ b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/debug_annotation.pbjson.dart @@ -25,8 +25,8 @@ import 'dart:typed_data' as $typed_data; const DebugAnnotation$json = { '1': 'DebugAnnotation', '2': [ + {'1': 'name_iid', '3': 1, '4': 1, '5': 4, '9': 0, '10': 'nameIid'}, {'1': 'name', '3': 10, '4': 1, '5': 9, '9': 0, '10': 'name'}, - {'1': 'string_value', '3': 6, '4': 1, '5': 9, '9': 1, '10': 'stringValue'}, { '1': 'legacy_json_value', '3': 9, @@ -35,6 +35,15 @@ const DebugAnnotation$json = { '9': 1, '10': 'legacyJsonValue' }, + {'1': 'string_value', '3': 6, '4': 1, '5': 9, '9': 1, '10': 'stringValue'}, + { + '1': 'string_value_iid', + '3': 17, + '4': 1, + '5': 4, + '9': 1, + '10': 'stringValueIid' + }, ], '8': [ {'1': 'name_field'}, @@ -44,6 +53,21 @@ const DebugAnnotation$json = { /// Descriptor for `DebugAnnotation`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List debugAnnotationDescriptor = $convert.base64Decode( - 'Cg9EZWJ1Z0Fubm90YXRpb24SFAoEbmFtZRgKIAEoCUgAUgRuYW1lEiMKDHN0cmluZ192YWx1ZR' - 'gGIAEoCUgBUgtzdHJpbmdWYWx1ZRIsChFsZWdhY3lfanNvbl92YWx1ZRgJIAEoCUgBUg9sZWdh' - 'Y3lKc29uVmFsdWVCDAoKbmFtZV9maWVsZEIHCgV2YWx1ZQ=='); + 'Cg9EZWJ1Z0Fubm90YXRpb24SGwoIbmFtZV9paWQYASABKARIAFIHbmFtZUlpZBIUCgRuYW1lGA' + 'ogASgJSABSBG5hbWUSLAoRbGVnYWN5X2pzb25fdmFsdWUYCSABKAlIAVIPbGVnYWN5SnNvblZh' + 'bHVlEiMKDHN0cmluZ192YWx1ZRgGIAEoCUgBUgtzdHJpbmdWYWx1ZRIqChBzdHJpbmdfdmFsdW' + 'VfaWlkGBEgASgESAFSDnN0cmluZ1ZhbHVlSWlkQgwKCm5hbWVfZmllbGRCBwoFdmFsdWU='); + +@$core.Deprecated('Use debugAnnotationNameDescriptor instead') +const DebugAnnotationName$json = { + '1': 'DebugAnnotationName', + '2': [ + {'1': 'iid', '3': 1, '4': 1, '5': 4, '10': 'iid'}, + {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'}, + ], +}; + +/// Descriptor for `DebugAnnotationName`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List debugAnnotationNameDescriptor = $convert.base64Decode( + 'ChNEZWJ1Z0Fubm90YXRpb25OYW1lEhAKA2lpZBgBIAEoBFIDaWlkEhIKBG5hbWUYAiABKAlSBG' + '5hbWU='); diff --git a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/track_descriptor.pb.dart b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/track_descriptor.pb.dart index d72c86ca622..d3f87e69042 100644 --- a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/track_descriptor.pb.dart +++ b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/track_descriptor.pb.dart @@ -22,8 +22,8 @@ import 'dart:core' as $core; import 'package:fixnum/fixnum.dart' as $fixnum; import 'package:protobuf/protobuf.dart' as $pb; -import 'process_descriptor.pb.dart' as $3; -import 'thread_descriptor.pb.dart' as $4; +import 'process_descriptor.pb.dart' as $4; +import 'thread_descriptor.pb.dart' as $5; export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; @@ -45,8 +45,8 @@ class TrackDescriptor extends $pb.GeneratedMessage { factory TrackDescriptor({ $fixnum.Int64? uuid, $core.String? name, - $3.ProcessDescriptor? process, - $4.ThreadDescriptor? thread, + $4.ProcessDescriptor? process, + $5.ThreadDescriptor? thread, $fixnum.Int64? parentUuid, }) { final $result = create(); @@ -83,10 +83,10 @@ class TrackDescriptor extends $pb.GeneratedMessage { ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'uuid', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) ..aOS(2, _omitFieldNames ? '' : 'name') - ..aOM<$3.ProcessDescriptor>(3, _omitFieldNames ? '' : 'process', - subBuilder: $3.ProcessDescriptor.create) - ..aOM<$4.ThreadDescriptor>(4, _omitFieldNames ? '' : 'thread', - subBuilder: $4.ThreadDescriptor.create) + ..aOM<$4.ProcessDescriptor>(3, _omitFieldNames ? '' : 'process', + subBuilder: $4.ProcessDescriptor.create) + ..aOM<$5.ThreadDescriptor>(4, _omitFieldNames ? '' : 'thread', + subBuilder: $5.ThreadDescriptor.create) ..a<$fixnum.Int64>( 5, _omitFieldNames ? '' : 'parentUuid', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) @@ -157,9 +157,9 @@ class TrackDescriptor extends $pb.GeneratedMessage { /// from other sources (e.g. ftrace) for the same process into a single /// timeline view. @$pb.TagNumber(3) - $3.ProcessDescriptor get process => $_getN(2); + $4.ProcessDescriptor get process => $_getN(2); @$pb.TagNumber(3) - set process($3.ProcessDescriptor v) { + set process($4.ProcessDescriptor v) { $_setField(3, v); } @@ -168,7 +168,7 @@ class TrackDescriptor extends $pb.GeneratedMessage { @$pb.TagNumber(3) void clearProcess() => $_clearField(3); @$pb.TagNumber(3) - $3.ProcessDescriptor ensureProcess() => $_ensure(2); + $4.ProcessDescriptor ensureProcess() => $_ensure(2); /// Associate the track with a thread, indicating that the track's events /// describe synchronous code execution on the thread. There should only be one @@ -178,9 +178,9 @@ class TrackDescriptor extends $pb.GeneratedMessage { /// from other sources (e.g. ftrace) for the same thread into a single timeline /// view. @$pb.TagNumber(4) - $4.ThreadDescriptor get thread => $_getN(3); + $5.ThreadDescriptor get thread => $_getN(3); @$pb.TagNumber(4) - set thread($4.ThreadDescriptor v) { + set thread($5.ThreadDescriptor v) { $_setField(4, v); } @@ -189,7 +189,7 @@ class TrackDescriptor extends $pb.GeneratedMessage { @$pb.TagNumber(4) void clearThread() => $_clearField(4); @$pb.TagNumber(4) - $4.ThreadDescriptor ensureThread() => $_ensure(3); + $5.ThreadDescriptor ensureThread() => $_ensure(3); /// A parent track reference can be used to describe relationships between /// tracks. For example, to define an asynchronous track which is scoped to a diff --git a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/track_event.pb.dart b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/track_event.pb.dart index 16cb0eabd4d..f420f5ce008 100644 --- a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/track_event.pb.dart +++ b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/track_event.pb.dart @@ -29,7 +29,7 @@ export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; export 'track_event.pbenum.dart'; -enum TrackEvent_NameField { name, notSet } +enum TrackEvent_NameField { nameIid, name, notSet } /// Trace events emitted by client instrumentation library (TRACE_EVENT macros), /// which describe activity on a track, such as a thread or asynchronous event @@ -96,8 +96,10 @@ enum TrackEvent_NameField { name, notSet } /// Next reserved id: 13 (up to 15). Next id: 50. class TrackEvent extends $pb.GeneratedMessage { factory TrackEvent({ + $core.Iterable<$fixnum.Int64>? categoryIids, $core.Iterable<$1.DebugAnnotation>? debugAnnotations, TrackEvent_Type? type, + $fixnum.Int64? nameIid, $fixnum.Int64? trackUuid, $core.Iterable<$core.String>? categories, $core.String? name, @@ -105,12 +107,18 @@ class TrackEvent extends $pb.GeneratedMessage { $core.Iterable<$fixnum.Int64>? terminatingFlowIds, }) { final $result = create(); + if (categoryIids != null) { + $result.categoryIids.addAll(categoryIids); + } if (debugAnnotations != null) { $result.debugAnnotations.addAll(debugAnnotations); } if (type != null) { $result.type = type; } + if (nameIid != null) { + $result.nameIid = nameIid; + } if (trackUuid != null) { $result.trackUuid = trackUuid; } @@ -138,6 +146,7 @@ class TrackEvent extends $pb.GeneratedMessage { static const $core.Map<$core.int, TrackEvent_NameField> _TrackEvent_NameFieldByTag = { + 10: TrackEvent_NameField.nameIid, 23: TrackEvent_NameField.name, 0: TrackEvent_NameField.notSet }; @@ -146,7 +155,9 @@ class TrackEvent extends $pb.GeneratedMessage { package: const $pb.PackageName(_omitMessageNames ? '' : 'perfetto.protos'), createEmptyInstance: create) - ..oo(0, [23]) + ..oo(0, [10, 23]) + ..p<$fixnum.Int64>( + 3, _omitFieldNames ? '' : 'categoryIids', $pb.PbFieldType.PU6) ..pc<$1.DebugAnnotation>( 4, _omitFieldNames ? '' : 'debugAnnotations', $pb.PbFieldType.PM, subBuilder: $1.DebugAnnotation.create) @@ -154,6 +165,9 @@ class TrackEvent extends $pb.GeneratedMessage { defaultOrMaker: TrackEvent_Type.TYPE_UNSPECIFIED, valueOf: TrackEvent_Type.valueOf, enumValues: TrackEvent_Type.values) + ..a<$fixnum.Int64>( + 10, _omitFieldNames ? '' : 'nameIid', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) ..a<$fixnum.Int64>( 11, _omitFieldNames ? '' : 'trackUuid', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) @@ -190,53 +204,73 @@ class TrackEvent extends $pb.GeneratedMessage { _TrackEvent_NameFieldByTag[$_whichOneof(0)]!; void clearNameField() => $_clearField($_whichOneof(0)); + /// Names of categories of the event. In the client library, categories are a + /// way to turn groups of individual events on or off. + /// interned EventCategoryName. + @$pb.TagNumber(3) + $pb.PbList<$fixnum.Int64> get categoryIids => $_getList(0); + /// Unstable key/value annotations shown in the trace viewer but not intended /// for metrics use. @$pb.TagNumber(4) - $pb.PbList<$1.DebugAnnotation> get debugAnnotations => $_getList(0); + $pb.PbList<$1.DebugAnnotation> get debugAnnotations => $_getList(1); @$pb.TagNumber(9) - TrackEvent_Type get type => $_getN(1); + TrackEvent_Type get type => $_getN(2); @$pb.TagNumber(9) set type(TrackEvent_Type v) { $_setField(9, v); } @$pb.TagNumber(9) - $core.bool hasType() => $_has(1); + $core.bool hasType() => $_has(2); @$pb.TagNumber(9) void clearType() => $_clearField(9); + /// interned EventName. + @$pb.TagNumber(10) + $fixnum.Int64 get nameIid => $_getI64(3); + @$pb.TagNumber(10) + set nameIid($fixnum.Int64 v) { + $_setInt64(3, v); + } + + @$pb.TagNumber(10) + $core.bool hasNameIid() => $_has(3); + @$pb.TagNumber(10) + void clearNameIid() => $_clearField(10); + /// Identifies the track of the event. The default value may be overridden /// using TrackEventDefaults, e.g., to specify the track of the TraceWriter's /// sequence (in most cases sequence = one thread). If no value is specified /// here or in TrackEventDefaults, the TrackEvent will be associated with an /// implicit trace-global track (uuid 0). See TrackDescriptor::uuid. @$pb.TagNumber(11) - $fixnum.Int64 get trackUuid => $_getI64(2); + $fixnum.Int64 get trackUuid => $_getI64(4); @$pb.TagNumber(11) set trackUuid($fixnum.Int64 v) { - $_setInt64(2, v); + $_setInt64(4, v); } @$pb.TagNumber(11) - $core.bool hasTrackUuid() => $_has(2); + $core.bool hasTrackUuid() => $_has(4); @$pb.TagNumber(11) void clearTrackUuid() => $_clearField(11); + /// non-interned variant. @$pb.TagNumber(22) - $pb.PbList<$core.String> get categories => $_getList(3); + $pb.PbList<$core.String> get categories => $_getList(5); /// non-interned variant. @$pb.TagNumber(23) - $core.String get name => $_getSZ(4); + $core.String get name => $_getSZ(6); @$pb.TagNumber(23) set name($core.String v) { - $_setString(4, v); + $_setString(6, v); } @$pb.TagNumber(23) - $core.bool hasName() => $_has(4); + $core.bool hasName() => $_has(6); @$pb.TagNumber(23) void clearName() => $_clearField(23); @@ -255,14 +289,172 @@ class TrackEvent extends $pb.GeneratedMessage { /// Flows can also be explicitly terminated (see |terminating_flow_ids|), so /// that the same ID can later be reused for another flow. @$pb.TagNumber(47) - $pb.PbList<$fixnum.Int64> get flowIds => $_getList(5); + $pb.PbList<$fixnum.Int64> get flowIds => $_getList(7); /// List of flow ids which should terminate on this event, otherwise same as /// |flow_ids|. /// Any one flow ID should be either listed as part of |flow_ids| OR /// |terminating_flow_ids|, not both. @$pb.TagNumber(48) - $pb.PbList<$fixnum.Int64> get terminatingFlowIds => $_getList(6); + $pb.PbList<$fixnum.Int64> get terminatingFlowIds => $_getList(8); +} + +class EventCategory extends $pb.GeneratedMessage { + factory EventCategory({ + $fixnum.Int64? iid, + $core.String? name, + }) { + final $result = create(); + if (iid != null) { + $result.iid = iid; + } + if (name != null) { + $result.name = name; + } + return $result; + } + EventCategory._() : super(); + factory EventCategory.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory EventCategory.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'EventCategory', + package: + const $pb.PackageName(_omitMessageNames ? '' : 'perfetto.protos'), + createEmptyInstance: create) + ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'iid', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..aOS(2, _omitFieldNames ? '' : 'name') + ..hasRequiredFields = false; + + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + EventCategory clone() => EventCategory()..mergeFromMessage(this); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + EventCategory copyWith(void Function(EventCategory) updates) => + super.copyWith((message) => updates(message as EventCategory)) + as EventCategory; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static EventCategory create() => EventCategory._(); + EventCategory createEmptyInstance() => create(); + static $pb.PbList createRepeated() => + $pb.PbList(); + @$core.pragma('dart2js:noInline') + static EventCategory getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static EventCategory? _defaultInstance; + + @$pb.TagNumber(1) + $fixnum.Int64 get iid => $_getI64(0); + @$pb.TagNumber(1) + set iid($fixnum.Int64 v) { + $_setInt64(0, v); + } + + @$pb.TagNumber(1) + $core.bool hasIid() => $_has(0); + @$pb.TagNumber(1) + void clearIid() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get name => $_getSZ(1); + @$pb.TagNumber(2) + set name($core.String v) { + $_setString(1, v); + } + + @$pb.TagNumber(2) + $core.bool hasName() => $_has(1); + @$pb.TagNumber(2) + void clearName() => $_clearField(2); +} + +class EventName extends $pb.GeneratedMessage { + factory EventName({ + $fixnum.Int64? iid, + $core.String? name, + }) { + final $result = create(); + if (iid != null) { + $result.iid = iid; + } + if (name != null) { + $result.name = name; + } + return $result; + } + EventName._() : super(); + factory EventName.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory EventName.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'EventName', + package: + const $pb.PackageName(_omitMessageNames ? '' : 'perfetto.protos'), + createEmptyInstance: create) + ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'iid', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..aOS(2, _omitFieldNames ? '' : 'name') + ..hasRequiredFields = false; + + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + EventName clone() => EventName()..mergeFromMessage(this); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + EventName copyWith(void Function(EventName) updates) => + super.copyWith((message) => updates(message as EventName)) as EventName; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static EventName create() => EventName._(); + EventName createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static EventName getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static EventName? _defaultInstance; + + @$pb.TagNumber(1) + $fixnum.Int64 get iid => $_getI64(0); + @$pb.TagNumber(1) + set iid($fixnum.Int64 v) { + $_setInt64(0, v); + } + + @$pb.TagNumber(1) + $core.bool hasIid() => $_has(0); + @$pb.TagNumber(1) + void clearIid() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get name => $_getSZ(1); + @$pb.TagNumber(2) + set name($core.String v) { + $_setString(1, v); + } + + @$pb.TagNumber(2) + $core.bool hasName() => $_has(1); + @$pb.TagNumber(2) + void clearName() => $_clearField(2); } const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); diff --git a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/track_event.pbjson.dart b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/track_event.pbjson.dart index a4c26bd0dce..74bca176ffd 100644 --- a/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/track_event.pbjson.dart +++ b/pkg/vm_service_protos/lib/src/protos/perfetto/trace/track_event/track_event.pbjson.dart @@ -25,7 +25,9 @@ import 'dart:typed_data' as $typed_data; const TrackEvent$json = { '1': 'TrackEvent', '2': [ + {'1': 'category_iids', '3': 3, '4': 3, '5': 4, '10': 'categoryIids'}, {'1': 'categories', '3': 22, '4': 3, '5': 9, '10': 'categories'}, + {'1': 'name_iid', '3': 10, '4': 1, '5': 4, '9': 0, '10': 'nameIid'}, {'1': 'name', '3': 23, '4': 1, '5': 9, '9': 0, '10': 'name'}, { '1': 'type', @@ -72,11 +74,38 @@ const TrackEvent_Type$json = { /// Descriptor for `TrackEvent`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List trackEventDescriptor = $convert.base64Decode( - 'CgpUcmFja0V2ZW50Eh4KCmNhdGVnb3JpZXMYFiADKAlSCmNhdGVnb3JpZXMSFAoEbmFtZRgXIA' - 'EoCUgAUgRuYW1lEjQKBHR5cGUYCSABKA4yIC5wZXJmZXR0by5wcm90b3MuVHJhY2tFdmVudC5U' - 'eXBlUgR0eXBlEh0KCnRyYWNrX3V1aWQYCyABKARSCXRyYWNrVXVpZBIZCghmbG93X2lkcxgvIA' - 'MoBlIHZmxvd0lkcxIwChR0ZXJtaW5hdGluZ19mbG93X2lkcxgwIAMoBlISdGVybWluYXRpbmdG' - 'bG93SWRzEk0KEWRlYnVnX2Fubm90YXRpb25zGAQgAygLMiAucGVyZmV0dG8ucHJvdG9zLkRlYn' - 'VnQW5ub3RhdGlvblIQZGVidWdBbm5vdGF0aW9ucyJYCgRUeXBlEhQKEFRZUEVfVU5TUEVDSUZJ' - 'RUQQABIUChBUWVBFX1NMSUNFX0JFR0lOEAESEgoOVFlQRV9TTElDRV9FTkQQAhIQCgxUWVBFX0' - 'lOU1RBTlQQA0IMCgpuYW1lX2ZpZWxk'); + 'CgpUcmFja0V2ZW50EiMKDWNhdGVnb3J5X2lpZHMYAyADKARSDGNhdGVnb3J5SWlkcxIeCgpjYX' + 'RlZ29yaWVzGBYgAygJUgpjYXRlZ29yaWVzEhsKCG5hbWVfaWlkGAogASgESABSB25hbWVJaWQS' + 'FAoEbmFtZRgXIAEoCUgAUgRuYW1lEjQKBHR5cGUYCSABKA4yIC5wZXJmZXR0by5wcm90b3MuVH' + 'JhY2tFdmVudC5UeXBlUgR0eXBlEh0KCnRyYWNrX3V1aWQYCyABKARSCXRyYWNrVXVpZBIZCghm' + 'bG93X2lkcxgvIAMoBlIHZmxvd0lkcxIwChR0ZXJtaW5hdGluZ19mbG93X2lkcxgwIAMoBlISdG' + 'VybWluYXRpbmdGbG93SWRzEk0KEWRlYnVnX2Fubm90YXRpb25zGAQgAygLMiAucGVyZmV0dG8u' + 'cHJvdG9zLkRlYnVnQW5ub3RhdGlvblIQZGVidWdBbm5vdGF0aW9ucyJYCgRUeXBlEhQKEFRZUE' + 'VfVU5TUEVDSUZJRUQQABIUChBUWVBFX1NMSUNFX0JFR0lOEAESEgoOVFlQRV9TTElDRV9FTkQQ' + 'AhIQCgxUWVBFX0lOU1RBTlQQA0IMCgpuYW1lX2ZpZWxk'); + +@$core.Deprecated('Use eventCategoryDescriptor instead') +const EventCategory$json = { + '1': 'EventCategory', + '2': [ + {'1': 'iid', '3': 1, '4': 1, '5': 4, '10': 'iid'}, + {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'}, + ], +}; + +/// Descriptor for `EventCategory`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List eventCategoryDescriptor = $convert.base64Decode( + 'Cg1FdmVudENhdGVnb3J5EhAKA2lpZBgBIAEoBFIDaWlkEhIKBG5hbWUYAiABKAlSBG5hbWU='); + +@$core.Deprecated('Use eventNameDescriptor instead') +const EventName$json = { + '1': 'EventName', + '2': [ + {'1': 'iid', '3': 1, '4': 1, '5': 4, '10': 'iid'}, + {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'}, + ], +}; + +/// Descriptor for `EventName`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List eventNameDescriptor = $convert.base64Decode( + 'CglFdmVudE5hbWUSEAoDaWlkGAEgASgEUgNpaWQSEgoEbmFtZRgCIAEoCVIEbmFtZQ=='); diff --git a/runtime/platform/growable_array.h b/runtime/platform/growable_array.h index b0a2cb67c85..8d73b4108fc 100644 --- a/runtime/platform/growable_array.h +++ b/runtime/platform/growable_array.h @@ -268,6 +268,10 @@ class Malloc : public AllStatic { static inline void Free(T* old_array, intptr_t old_len) { free(old_array); } + + // Allow templated containers to check if this allocator supports + // freeing individual allocations. + static constexpr bool kSupportsFreeingIndividualAllocations = true; }; template diff --git a/runtime/vm/perfetto_utils.h b/runtime/vm/perfetto_utils.h index 7a39f9d0682..d9cbc8be912 100644 --- a/runtime/vm/perfetto_utils.h +++ b/runtime/vm/perfetto_utils.h @@ -13,6 +13,7 @@ #include "perfetto/ext/tracing/core/trace_packet.h" #include "perfetto/protozero/scattered_heap_buffer.h" +#include "vm/hash_map.h" #include "vm/json_stream.h" #include "vm/os.h" #include "vm/protos/perfetto/common/builtin_clock.pbzero.h" @@ -120,6 +121,143 @@ inline void AppendPacketToJSONBase64String( } } +// Sequence of elements which can be interned by |BytesInterner|. +// +// Equality and hash are defined in terms of raw byte content. +template +struct InternedBytes { + InternedBytes(const T* data, intptr_t length) + : data(data), + length(length), + hash(HashBytes(reinterpret_cast(data), + length * sizeof(T))), + iid(0) {} + + InternedBytes(const T* data, intptr_t length, uword hash, uint64_t iid) + : data(data), length(length), hash(hash), iid(iid) {} + + bool Equals(const InternedBytes& other) const { + if (length != other.length) { + return false; + } + return memcmp(data, other.data, length * sizeof(T)) == 0; + } + + uword Hash() const { return hash; } + + const T* const data; + const intptr_t length; + const uword hash; + + // Interning id. Only set after interning and does not participate in + // equality or hash computations. + const uint64_t iid; +}; + +// Interning dictionary used to construct various parts of |InternedData| +// message. +template +class BytesInterner + : public BaseDirectChainedHashMap>, + ValueObject, + Allocator> { + using Base = + BaseDirectChainedHashMap>, + ValueObject, + Allocator>; + + public: + explicit BytesInterner(Allocator* allocator = nullptr) : Base(allocator) {} + + ~BytesInterner() { + if constexpr (Allocator::kSupportsFreeingIndividualAllocations) { + auto it = Base::GetIterator(); + while (auto pair = it.Next()) { + Dispose(*pair); + } + } + } + + uint64_t Intern(const T* data, const intptr_t length) { + InternedBytes key(data, length); + if (auto interned = Base::Lookup(&key)) { + return (*interned)->iid; + } + + const uint64_t iid = Base::Size() + 1; + Base::Insert(Copy(key, iid)); + return iid; + } + + // Enumerate all entries added to this interner since the last call to this + // function. + template + void FlushNewlyInternedTo(F&& callback) { + // Note: we never remove elements from this map so we can just iterate + // |pairs_| linearly. + for (uint32_t i = first_to_flush_; i < Base::next_pair_index_; i++) { + callback(*Base::pairs_[i]); + } + first_to_flush_ = Base::next_pair_index_; + } + + // Returns |true| if there are entries added to this interner since the + // last call to |FlushNewlyInternedTo| + bool HasNewlyInternedEntries() const { + return first_to_flush_ < Base::next_pair_index_; + } + + private: + Allocator* allocator() const { return Base::allocator_; } + + InternedBytes* Copy(const InternedBytes& interned, uint64_t iid) const { + auto data_copy = allocator()->template Alloc(interned.length); + memcpy(data_copy, interned.data, interned.length * sizeof(T)); // NOLINT + auto copy = allocator()->template Alloc>(1); + new (copy) InternedBytes(data_copy, interned.length, interned.hash, iid); + return copy; + } + + void Dispose(InternedBytes* interned) { + if constexpr (Allocator::kSupportsFreeingIndividualAllocations) { + allocator()->Free(const_cast(interned->data), + interned->length * sizeof(T)); + allocator()->Free(interned, 1); + } + } + + // The index of the first entry which was not flushed via + // |FlushNewlyInternedTo|. + uint32_t first_to_flush_ = 0; +}; + +template +class StringInterner : public ValueObject { + public: + explicit StringInterner(Allocator* allocator = nullptr) + : bytes_interner_(allocator) {} + + uint64_t Intern(const char* str) { + // +1 to include terminating NUL character. + return bytes_interner_.Intern(str, strlen(str) + 1); + } + + bool HasNewlyInternedEntries() const { + return bytes_interner_.HasNewlyInternedEntries(); + } + + template + void FlushNewlyInternedTo(F&& callback) { + bytes_interner_.FlushNewlyInternedTo( + [callback = std::move(callback)](const auto& interned_bytes) { + callback(interned_bytes.iid, interned_bytes.data); + }); + } + + private: + BytesInterner bytes_interner_; +}; + } // namespace perfetto_utils } // namespace dart diff --git a/runtime/vm/protos/perfetto/trace/interned_data/interned_data.pbzero.h b/runtime/vm/protos/perfetto/trace/interned_data/interned_data.pbzero.h index bfefc37e4ca..1078dd53a69 100644 --- a/runtime/vm/protos/perfetto/trace/interned_data/interned_data.pbzero.h +++ b/runtime/vm/protos/perfetto/trace/interned_data/interned_data.pbzero.h @@ -25,12 +25,15 @@ namespace protos { namespace pbzero { class Callstack; +class DebugAnnotationName; +class EventCategory; +class EventName; class Frame; class InternedString; class Mapping; class InternedData_Decoder : public ::protozero::TypedProtoDecoder< - /*MAX_FIELD_ID=*/19, + /*MAX_FIELD_ID=*/29, /*HAS_NONPACKED_REPEATED_FIELDS=*/true> { public: InternedData_Decoder(const uint8_t* data, size_t len) @@ -40,6 +43,21 @@ class InternedData_Decoder : public ::protozero::TypedProtoDecoder< raw.size()) {} explicit InternedData_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {} + bool has_event_categories() const { return at<1>().valid(); } + ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> event_categories() + const { + return GetRepeated<::protozero::ConstBytes>(1); + } + bool has_event_names() const { return at<2>().valid(); } + ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> event_names() + const { + return GetRepeated<::protozero::ConstBytes>(2); + } + bool has_debug_annotation_names() const { return at<3>().valid(); } + ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> + debug_annotation_names() const { + return GetRepeated<::protozero::ConstBytes>(3); + } bool has_mapping_paths() const { return at<17>().valid(); } ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> mapping_paths() const { @@ -63,22 +81,71 @@ class InternedData_Decoder : public ::protozero::TypedProtoDecoder< const { return GetRepeated<::protozero::ConstBytes>(7); } + bool has_debug_annotation_string_values() const { return at<29>().valid(); } + ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> + debug_annotation_string_values() const { + return GetRepeated<::protozero::ConstBytes>(29); + } }; class InternedData : public ::protozero::Message { public: using Decoder = InternedData_Decoder; enum : int32_t { + kEventCategoriesFieldNumber = 1, + kEventNamesFieldNumber = 2, + kDebugAnnotationNamesFieldNumber = 3, kMappingPathsFieldNumber = 17, kFunctionNamesFieldNumber = 5, kMappingsFieldNumber = 19, kFramesFieldNumber = 6, kCallstacksFieldNumber = 7, + kDebugAnnotationStringValuesFieldNumber = 29, }; static constexpr const char* GetName() { return ".perfetto.protos.InternedData"; } + using FieldMetadata_EventCategories = ::protozero::proto_utils::FieldMetadata< + 1, + ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked, + ::protozero::proto_utils::ProtoSchemaType::kMessage, + EventCategory, + InternedData>; + + static constexpr FieldMetadata_EventCategories kEventCategories{}; + template + T* add_event_categories() { + return BeginNestedMessage(1); + } + + using FieldMetadata_EventNames = ::protozero::proto_utils::FieldMetadata< + 2, + ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked, + ::protozero::proto_utils::ProtoSchemaType::kMessage, + EventName, + InternedData>; + + static constexpr FieldMetadata_EventNames kEventNames{}; + template + T* add_event_names() { + return BeginNestedMessage(2); + } + + using FieldMetadata_DebugAnnotationNames = + ::protozero::proto_utils::FieldMetadata< + 3, + ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked, + ::protozero::proto_utils::ProtoSchemaType::kMessage, + DebugAnnotationName, + InternedData>; + + static constexpr FieldMetadata_DebugAnnotationNames kDebugAnnotationNames{}; + template + T* add_debug_annotation_names() { + return BeginNestedMessage(3); + } + using FieldMetadata_MappingPaths = ::protozero::proto_utils::FieldMetadata< 17, ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked, @@ -143,6 +210,21 @@ class InternedData : public ::protozero::Message { T* add_callstacks() { return BeginNestedMessage(7); } + + using FieldMetadata_DebugAnnotationStringValues = + ::protozero::proto_utils::FieldMetadata< + 29, + ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked, + ::protozero::proto_utils::ProtoSchemaType::kMessage, + InternedString, + InternedData>; + + static constexpr FieldMetadata_DebugAnnotationStringValues + kDebugAnnotationStringValues{}; + template + T* add_debug_annotation_string_values() { + return BeginNestedMessage(29); + } }; } // namespace pbzero diff --git a/runtime/vm/protos/perfetto/trace/interned_data/interned_data.proto b/runtime/vm/protos/perfetto/trace/interned_data/interned_data.proto index 418379778b3..be5731b6a42 100644 --- a/runtime/vm/protos/perfetto/trace/interned_data/interned_data.proto +++ b/runtime/vm/protos/perfetto/trace/interned_data/interned_data.proto @@ -59,6 +59,10 @@ package perfetto.protos; // Next reserved id: 8 (up to 15). // Next id: 29. message InternedData { + repeated EventCategory event_categories = 1; + repeated EventName event_names = 2; + repeated DebugAnnotationName debug_annotation_names = 3; + // Note: field IDs up to 15 should be used for frequent data only. // Paths to executable files. @@ -72,4 +76,6 @@ message InternedData { repeated Frame frames = 6; // A callstack of a program. repeated Callstack callstacks = 7; + // Interned string values in the DebugAnnotation proto. + repeated InternedString debug_annotation_string_values = 29; } diff --git a/runtime/vm/protos/perfetto/trace/track_event/debug_annotation.pbzero.h b/runtime/vm/protos/perfetto/trace/track_event/debug_annotation.pbzero.h index 315b6be1c6a..13a8d983587 100644 --- a/runtime/vm/protos/perfetto/trace/track_event/debug_annotation.pbzero.h +++ b/runtime/vm/protos/perfetto/trace/track_event/debug_annotation.pbzero.h @@ -24,8 +24,80 @@ namespace perfetto { namespace protos { namespace pbzero { +class DebugAnnotationName_Decoder + : public ::protozero::TypedProtoDecoder< + /*MAX_FIELD_ID=*/2, + /*HAS_NONPACKED_REPEATED_FIELDS=*/false> { + public: + DebugAnnotationName_Decoder(const uint8_t* data, size_t len) + : TypedProtoDecoder(data, len) {} + explicit DebugAnnotationName_Decoder(const std::string& raw) + : TypedProtoDecoder(reinterpret_cast(raw.data()), + raw.size()) {} + explicit DebugAnnotationName_Decoder(const ::protozero::ConstBytes& raw) + : TypedProtoDecoder(raw.data, raw.size) {} + bool has_iid() const { return at<1>().valid(); } + uint64_t iid() const { return at<1>().as_uint64(); } + bool has_name() const { return at<2>().valid(); } + ::protozero::ConstChars name() const { return at<2>().as_string(); } +}; + +class DebugAnnotationName : public ::protozero::Message { + public: + using Decoder = DebugAnnotationName_Decoder; + enum : int32_t { + kIidFieldNumber = 1, + kNameFieldNumber = 2, + }; + static constexpr const char* GetName() { + return ".perfetto.protos.DebugAnnotationName"; + } + + using FieldMetadata_Iid = ::protozero::proto_utils::FieldMetadata< + 1, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint64, + uint64_t, + DebugAnnotationName>; + + static constexpr FieldMetadata_Iid kIid{}; + void set_iid(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_Iid::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kUint64>::Append(*this, + field_id, + value); + } + + using FieldMetadata_Name = ::protozero::proto_utils::FieldMetadata< + 2, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kString, + std::string, + DebugAnnotationName>; + + static constexpr FieldMetadata_Name kName{}; + void set_name(const char* data, size_t size) { + AppendBytes(FieldMetadata_Name::kFieldId, data, size); + } + void set_name(::protozero::ConstChars chars) { + AppendBytes(FieldMetadata_Name::kFieldId, chars.data, chars.size); + } + void set_name(std::string value) { + static constexpr uint32_t field_id = FieldMetadata_Name::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kString>::Append(*this, + field_id, + value); + } +}; + class DebugAnnotation_Decoder : public ::protozero::TypedProtoDecoder< - /*MAX_FIELD_ID=*/10, + /*MAX_FIELD_ID=*/17, /*HAS_NONPACKED_REPEATED_FIELDS=*/false> { public: DebugAnnotation_Decoder(const uint8_t* data, size_t len) @@ -35,28 +107,52 @@ class DebugAnnotation_Decoder : public ::protozero::TypedProtoDecoder< raw.size()) {} explicit DebugAnnotation_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {} + bool has_name_iid() const { return at<1>().valid(); } + uint64_t name_iid() const { return at<1>().as_uint64(); } bool has_name() const { return at<10>().valid(); } ::protozero::ConstChars name() const { return at<10>().as_string(); } - bool has_string_value() const { return at<6>().valid(); } - ::protozero::ConstChars string_value() const { return at<6>().as_string(); } bool has_legacy_json_value() const { return at<9>().valid(); } ::protozero::ConstChars legacy_json_value() const { return at<9>().as_string(); } + bool has_string_value() const { return at<6>().valid(); } + ::protozero::ConstChars string_value() const { return at<6>().as_string(); } + bool has_string_value_iid() const { return at<17>().valid(); } + uint64_t string_value_iid() const { return at<17>().as_uint64(); } }; class DebugAnnotation : public ::protozero::Message { public: using Decoder = DebugAnnotation_Decoder; enum : int32_t { + kNameIidFieldNumber = 1, kNameFieldNumber = 10, - kStringValueFieldNumber = 6, kLegacyJsonValueFieldNumber = 9, + kStringValueFieldNumber = 6, + kStringValueIidFieldNumber = 17, }; static constexpr const char* GetName() { return ".perfetto.protos.DebugAnnotation"; } + using FieldMetadata_NameIid = ::protozero::proto_utils::FieldMetadata< + 1, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint64, + uint64_t, + DebugAnnotation>; + + static constexpr FieldMetadata_NameIid kNameIid{}; + void set_name_iid(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_NameIid::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kUint64>::Append(*this, + field_id, + value); + } + using FieldMetadata_Name = ::protozero::proto_utils::FieldMetadata< 10, ::protozero::proto_utils::RepetitionType::kNotRepeated, @@ -81,30 +177,6 @@ class DebugAnnotation : public ::protozero::Message { value); } - using FieldMetadata_StringValue = ::protozero::proto_utils::FieldMetadata< - 6, - ::protozero::proto_utils::RepetitionType::kNotRepeated, - ::protozero::proto_utils::ProtoSchemaType::kString, - std::string, - DebugAnnotation>; - - static constexpr FieldMetadata_StringValue kStringValue{}; - void set_string_value(const char* data, size_t size) { - AppendBytes(FieldMetadata_StringValue::kFieldId, data, size); - } - void set_string_value(::protozero::ConstChars chars) { - AppendBytes(FieldMetadata_StringValue::kFieldId, chars.data, chars.size); - } - void set_string_value(std::string value) { - static constexpr uint32_t field_id = FieldMetadata_StringValue::kFieldId; - // Call the appropriate protozero::Message::Append(field_id, ...) - // method based on the type of the field. - ::protozero::internal::FieldWriter< - ::protozero::proto_utils::ProtoSchemaType::kString>::Append(*this, - field_id, - value); - } - using FieldMetadata_LegacyJsonValue = ::protozero::proto_utils::FieldMetadata< 9, ::protozero::proto_utils::RepetitionType::kNotRepeated, @@ -130,6 +202,48 @@ class DebugAnnotation : public ::protozero::Message { field_id, value); } + + using FieldMetadata_StringValue = ::protozero::proto_utils::FieldMetadata< + 6, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kString, + std::string, + DebugAnnotation>; + + static constexpr FieldMetadata_StringValue kStringValue{}; + void set_string_value(const char* data, size_t size) { + AppendBytes(FieldMetadata_StringValue::kFieldId, data, size); + } + void set_string_value(::protozero::ConstChars chars) { + AppendBytes(FieldMetadata_StringValue::kFieldId, chars.data, chars.size); + } + void set_string_value(std::string value) { + static constexpr uint32_t field_id = FieldMetadata_StringValue::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kString>::Append(*this, + field_id, + value); + } + + using FieldMetadata_StringValueIid = ::protozero::proto_utils::FieldMetadata< + 17, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint64, + uint64_t, + DebugAnnotation>; + + static constexpr FieldMetadata_StringValueIid kStringValueIid{}; + void set_string_value_iid(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_StringValueIid::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kUint64>::Append(*this, + field_id, + value); + } }; } // namespace pbzero diff --git a/runtime/vm/protos/perfetto/trace/track_event/debug_annotation.proto b/runtime/vm/protos/perfetto/trace/track_event/debug_annotation.proto index c9470a5e1ae..5070d2bc41f 100644 --- a/runtime/vm/protos/perfetto/trace/track_event/debug_annotation.proto +++ b/runtime/vm/protos/perfetto/trace/track_event/debug_annotation.proto @@ -70,15 +70,29 @@ package perfetto.protos; message DebugAnnotation { // Name fields are set only for dictionary entries. oneof name_field { + // interned DebugAnnotationName. + uint64 name_iid = 1; // non-interned variant. string name = 10; } oneof value { - string string_value = 6; - // Legacy instrumentation may not support conversion of nested data to // NestedValue yet. string legacy_json_value = 9; + + // interned and non-interned variants of strings. + string string_value = 6; + // Corresponds to |debug_annotation_string_values| field in InternedData. + uint64 string_value_iid = 17; } } + +// -------------------- +// Interned data types: +// -------------------- + +message DebugAnnotationName { + optional uint64 iid = 1; + optional string name = 2; +} diff --git a/runtime/vm/protos/perfetto/trace/track_event/track_event.pbzero.h b/runtime/vm/protos/perfetto/trace/track_event/track_event.pbzero.h index 77ca4aa80db..11f6e2cd684 100644 --- a/runtime/vm/protos/perfetto/trace/track_event/track_event.pbzero.h +++ b/runtime/vm/protos/perfetto/trace/track_event/track_event.pbzero.h @@ -63,6 +63,148 @@ const char* TrackEvent_Type_Name( return "PBZERO_UNKNOWN_ENUM_VALUE"; } +class EventName_Decoder : public ::protozero::TypedProtoDecoder< + /*MAX_FIELD_ID=*/2, + /*HAS_NONPACKED_REPEATED_FIELDS=*/false> { + public: + EventName_Decoder(const uint8_t* data, size_t len) + : TypedProtoDecoder(data, len) {} + explicit EventName_Decoder(const std::string& raw) + : TypedProtoDecoder(reinterpret_cast(raw.data()), + raw.size()) {} + explicit EventName_Decoder(const ::protozero::ConstBytes& raw) + : TypedProtoDecoder(raw.data, raw.size) {} + bool has_iid() const { return at<1>().valid(); } + uint64_t iid() const { return at<1>().as_uint64(); } + bool has_name() const { return at<2>().valid(); } + ::protozero::ConstChars name() const { return at<2>().as_string(); } +}; + +class EventName : public ::protozero::Message { + public: + using Decoder = EventName_Decoder; + enum : int32_t { + kIidFieldNumber = 1, + kNameFieldNumber = 2, + }; + static constexpr const char* GetName() { + return ".perfetto.protos.EventName"; + } + + using FieldMetadata_Iid = ::protozero::proto_utils::FieldMetadata< + 1, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint64, + uint64_t, + EventName>; + + static constexpr FieldMetadata_Iid kIid{}; + void set_iid(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_Iid::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kUint64>::Append(*this, + field_id, + value); + } + + using FieldMetadata_Name = ::protozero::proto_utils::FieldMetadata< + 2, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kString, + std::string, + EventName>; + + static constexpr FieldMetadata_Name kName{}; + void set_name(const char* data, size_t size) { + AppendBytes(FieldMetadata_Name::kFieldId, data, size); + } + void set_name(::protozero::ConstChars chars) { + AppendBytes(FieldMetadata_Name::kFieldId, chars.data, chars.size); + } + void set_name(std::string value) { + static constexpr uint32_t field_id = FieldMetadata_Name::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kString>::Append(*this, + field_id, + value); + } +}; + +class EventCategory_Decoder : public ::protozero::TypedProtoDecoder< + /*MAX_FIELD_ID=*/2, + /*HAS_NONPACKED_REPEATED_FIELDS=*/false> { + public: + EventCategory_Decoder(const uint8_t* data, size_t len) + : TypedProtoDecoder(data, len) {} + explicit EventCategory_Decoder(const std::string& raw) + : TypedProtoDecoder(reinterpret_cast(raw.data()), + raw.size()) {} + explicit EventCategory_Decoder(const ::protozero::ConstBytes& raw) + : TypedProtoDecoder(raw.data, raw.size) {} + bool has_iid() const { return at<1>().valid(); } + uint64_t iid() const { return at<1>().as_uint64(); } + bool has_name() const { return at<2>().valid(); } + ::protozero::ConstChars name() const { return at<2>().as_string(); } +}; + +class EventCategory : public ::protozero::Message { + public: + using Decoder = EventCategory_Decoder; + enum : int32_t { + kIidFieldNumber = 1, + kNameFieldNumber = 2, + }; + static constexpr const char* GetName() { + return ".perfetto.protos.EventCategory"; + } + + using FieldMetadata_Iid = ::protozero::proto_utils::FieldMetadata< + 1, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint64, + uint64_t, + EventCategory>; + + static constexpr FieldMetadata_Iid kIid{}; + void set_iid(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_Iid::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kUint64>::Append(*this, + field_id, + value); + } + + using FieldMetadata_Name = ::protozero::proto_utils::FieldMetadata< + 2, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kString, + std::string, + EventCategory>; + + static constexpr FieldMetadata_Name kName{}; + void set_name(const char* data, size_t size) { + AppendBytes(FieldMetadata_Name::kFieldId, data, size); + } + void set_name(::protozero::ConstChars chars) { + AppendBytes(FieldMetadata_Name::kFieldId, chars.data, chars.size); + } + void set_name(std::string value) { + static constexpr uint32_t field_id = FieldMetadata_Name::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kString>::Append(*this, + field_id, + value); + } +}; + class TrackEvent_Decoder : public ::protozero::TypedProtoDecoder< /*MAX_FIELD_ID=*/48, /*HAS_NONPACKED_REPEATED_FIELDS=*/true> { @@ -74,11 +216,17 @@ class TrackEvent_Decoder : public ::protozero::TypedProtoDecoder< raw.size()) {} explicit TrackEvent_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {} + bool has_category_iids() const { return at<3>().valid(); } + ::protozero::RepeatedFieldIterator category_iids() const { + return GetRepeated(3); + } bool has_categories() const { return at<22>().valid(); } ::protozero::RepeatedFieldIterator<::protozero::ConstChars> categories() const { return GetRepeated<::protozero::ConstChars>(22); } + bool has_name_iid() const { return at<10>().valid(); } + uint64_t name_iid() const { return at<10>().as_uint64(); } bool has_name() const { return at<23>().valid(); } ::protozero::ConstChars name() const { return at<23>().as_string(); } bool has_type() const { return at<9>().valid(); } @@ -104,7 +252,9 @@ class TrackEvent : public ::protozero::Message { public: using Decoder = TrackEvent_Decoder; enum : int32_t { + kCategoryIidsFieldNumber = 3, kCategoriesFieldNumber = 22, + kNameIidFieldNumber = 10, kNameFieldNumber = 23, kTypeFieldNumber = 9, kTrackUuidFieldNumber = 11, @@ -125,6 +275,24 @@ class TrackEvent : public ::protozero::Message { static inline const Type TYPE_SLICE_END = Type::TYPE_SLICE_END; static inline const Type TYPE_INSTANT = Type::TYPE_INSTANT; + using FieldMetadata_CategoryIids = ::protozero::proto_utils::FieldMetadata< + 3, + ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked, + ::protozero::proto_utils::ProtoSchemaType::kUint64, + uint64_t, + TrackEvent>; + + static constexpr FieldMetadata_CategoryIids kCategoryIids{}; + void add_category_iids(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_CategoryIids::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kUint64>::Append(*this, + field_id, + value); + } + using FieldMetadata_Categories = ::protozero::proto_utils::FieldMetadata< 22, ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked, @@ -149,6 +317,24 @@ class TrackEvent : public ::protozero::Message { value); } + using FieldMetadata_NameIid = ::protozero::proto_utils::FieldMetadata< + 10, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint64, + uint64_t, + TrackEvent>; + + static constexpr FieldMetadata_NameIid kNameIid{}; + void set_name_iid(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_NameIid::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kUint64>::Append(*this, + field_id, + value); + } + using FieldMetadata_Name = ::protozero::proto_utils::FieldMetadata< 23, ::protozero::proto_utils::RepetitionType::kNotRepeated, diff --git a/runtime/vm/protos/perfetto/trace/track_event/track_event.proto b/runtime/vm/protos/perfetto/trace/track_event/track_event.proto index b75c01e8ea4..afbc9df6fae 100644 --- a/runtime/vm/protos/perfetto/trace/track_event/track_event.proto +++ b/runtime/vm/protos/perfetto/trace/track_event/track_event.proto @@ -95,6 +95,11 @@ package perfetto.protos; // // Next reserved id: 13 (up to 15). Next id: 50. message TrackEvent { + // Names of categories of the event. In the client library, categories are a + // way to turn groups of individual events on or off. + // interned EventCategoryName. + repeated uint64 category_iids = 3; + // non-interned variant. repeated string categories = 22; // Optional name of the event for its display in trace viewer. May be left @@ -104,6 +109,8 @@ message TrackEvent { // changing. Instead, they should use typed arguments to identify the events // they are interested in. oneof name_field { + // interned EventName. + uint64 name_iid = 10; // non-interned variant. string name = 23; } @@ -176,3 +183,17 @@ message TrackEvent { // for metrics use. repeated DebugAnnotation debug_annotations = 4; } + +// -------------------- +// Interned data types: +// -------------------- + +message EventCategory { + optional uint64 iid = 1; + optional string name = 2; +} + +message EventName { + optional uint64 iid = 1; + optional string name = 2; +} diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc index 276c340fad1..3511439b42f 100644 --- a/runtime/vm/timeline.cc +++ b/runtime/vm/timeline.cc @@ -32,6 +32,7 @@ #include "vm/perfetto_utils.h" #include "vm/protos/perfetto/common/builtin_clock.pbzero.h" #include "vm/protos/perfetto/trace/clock_snapshot.pbzero.h" +#include "vm/protos/perfetto/trace/interned_data/interned_data.pbzero.h" #include "vm/protos/perfetto/trace/trace_packet.pbzero.h" #include "vm/protos/perfetto/trace/track_event/debug_annotation.pbzero.h" #include "vm/protos/perfetto/trace/track_event/process_descriptor.pbzero.h" @@ -82,6 +83,10 @@ DEFINE_FLAG(charp, DEFAULT_TIMELINE_RECORDER, "Select the timeline recorder used. " "Valid values: none, " SUPPORTED_TIMELINE_RECORDERS) +DEFINE_FLAG(bool, + intern_strings_when_writing_perfetto_timeline, + false, + "Intern strings when writing timeline in perfetto format.") // Implementation notes: // @@ -802,6 +807,156 @@ void TimelineEvent::PrintJSON(JSONWriter* writer) const { #if defined(SUPPORT_PERFETTO) && !defined(PRODUCT) namespace { +// Trait used to map 64-bit ids (e.g. isolate or isolate group id) to +// interned id of a corresponding string representation. +// +// This way we only need to generate formatted string once, instead of +// repeatedly formatting it and then interning resulting string to get an +// iid. +class IdToIidTrait { + public: + struct Pair { + uint64_t id; + uint64_t formatted_iid; + }; + using Key = uint64_t; + using Value = uint64_t; + + static Key KeyOf(const Pair& kv) { return kv.id; } + static Value ValueOf(const Pair& kv) { return kv.formatted_iid; } + static uword Hash(Key key) { + return Utils::WordHash(static_cast(key)); + } + static bool IsKeyEqual(const Pair& kv, Key key) { return kv.id == key; } +}; + +using IdToIidMap = MallocDirectChainedHashMap; + +class InternedDataBuilder : public ValueObject { + private: + using SequenceFlags = perfetto::protos::pbzero::TracePacket_SequenceFlags; + + public: + // InternedData contains multiple independent interning dictionaries which + // are used for different attributes. +#define PERFETTO_INTERNED_STRINGS_FIELDS_LIST(V) \ + V(event_categories, name) \ + V(event_names, name) \ + V(debug_annotation_names, name) \ + V(debug_annotation_string_values, str) + + // Direct access for known strings. +#define PERFETTO_COMMON_INTERNED_STRINGS_LIST(V) \ + V(debug_annotation_names, isolateId) \ + V(debug_annotation_names, isolateGroupId) + + InternedDataBuilder() = default; + + // Emit all strings added since the last invocation of |AttachInternedDataTo| + // into |interned_data| of the given |TracePacket|. + // + // Mark the packet as depending on incremental state. + void AttachInternedDataTo(perfetto::protos::pbzero::TracePacket* packet) { + if (!AnyInternerHasNewlyInternedEntries()) { + return; + } + + packet->set_sequence_flags(sequence_flags_); + // The first packet will have SEQ_INCREMENTAL_STATE_CLEARED + // the rest will just have SEQ_NEEDS_INCREMENTAL_STATE. + sequence_flags_ &= ~SequenceFlags::SEQ_INCREMENTAL_STATE_CLEARED; + + auto interned_data = packet->set_interned_data(); + + // Flush individual interning dictionaries. +#define FLUSH_FIELD(name, proto_field) \ + name##_.FlushNewlyInternedTo([interned_data](auto& iid, auto& str) { \ + auto entry = interned_data->add_##name(); \ + entry->set_iid(iid); \ + entry->set_##proto_field(str); \ + }); + + PERFETTO_INTERNED_STRINGS_FIELDS_LIST(FLUSH_FIELD) +#undef FLUSH_FIELD + } + +#define DEFINE_GETTER(name, proto_field) \ + perfetto_utils::StringInterner& name() { return name##_; } + PERFETTO_INTERNED_STRINGS_FIELDS_LIST(DEFINE_GETTER) +#undef DEFINE_GETTER + +#define DEFINE_GETTER_FOR_COMMON_STRING(category, str) \ + uint64_t iid_##str() { \ + if (iid_##str##_ == 0) { \ + iid_##str##_ = category().Intern(#str); \ + } \ + return iid_##str##_; \ + } + + PERFETTO_COMMON_INTERNED_STRINGS_LIST(DEFINE_GETTER_FOR_COMMON_STRING) + +#undef DEFINE_GETTER_FOR_COMMON_STRING + + uint64_t InternFormattedIsolateId(uint64_t isolate_id) { + return InternFormattedIdForDebugAnnotation( + isolate_id_to_iid_of_formatted_string_, + ISOLATE_SERVICE_ID_FORMAT_STRING, isolate_id); + } + + uint64_t InternFormattedIsolateGroupId(uint64_t isolate_group_id) { + return InternFormattedIdForDebugAnnotation( + isolate_group_id_to_iid_of_formatted_string_, + ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING, isolate_group_id); + } + + private: + template + uint64_t InternFormattedIdForDebugAnnotation(IdToIidMap& cache, + const char (&format)[kFormatLen], + uint64_t id) { + if (auto iid = cache.Lookup(id)) { + return iid->formatted_iid; + } + + // 20 characters is enough to format any uint64_t (or int64_t) value. + char formatted[kFormatLen + 20]; + Utils::SNPrint(formatted, ARRAY_SIZE(formatted), format, id); + + auto formatted_iid = debug_annotation_string_values().Intern(formatted); + cache.Insert({id, formatted_iid}); + return formatted_iid; + } + + bool AnyInternerHasNewlyInternedEntries() const { +#define CHECK_FIELD(name, proto_field) \ + if (name##_.HasNewlyInternedEntries()) return true; + + PERFETTO_INTERNED_STRINGS_FIELDS_LIST(CHECK_FIELD) +#undef CHECK_FIELD + return false; + } + + uint32_t sequence_flags_ = SequenceFlags::SEQ_INCREMENTAL_STATE_CLEARED | + SequenceFlags::SEQ_NEEDS_INCREMENTAL_STATE; + + // These are interned in debug_annotation_string_values space. + IdToIidMap isolate_id_to_iid_of_formatted_string_; + IdToIidMap isolate_group_id_to_iid_of_formatted_string_; + +#define DEFINE_FIELD_FOR_COMMON_STRING(category, str) uint64_t iid_##str##_ = 0; + + PERFETTO_COMMON_INTERNED_STRINGS_LIST(DEFINE_FIELD_FOR_COMMON_STRING) + +#undef DEFINE_FIELD_FOR_COMMON_STRING + +#define DEFINE_FIELD(name, proto_field) \ + perfetto_utils::StringInterner name##_; + PERFETTO_INTERNED_STRINGS_FIELDS_LIST(DEFINE_FIELD) +#undef DEFINE_FIELD + + DISALLOW_COPY_AND_ASSIGN(InternedDataBuilder); +}; + class TracePacketWriter : public ValueObject { public: using TracePacket = perfetto::protos::pbzero::TracePacket; @@ -811,8 +966,11 @@ class TracePacketWriter : public ValueObject { std::function&)>; TracePacketWriter(protozero::HeapBuffered& packet, - WriteCallback&& write_callback) - : packet_(packet), write_callback_(std::move(write_callback)) {} + WriteCallback&& write_callback, + bool intern_strings) + : packet_(packet), + write_callback_(std::move(write_callback)), + intern_strings_(intern_strings) {} // Converting contents of the given |TimelineEvent| into one or more // Perfetto packets and write them out using |write_callback_| which @@ -870,6 +1028,7 @@ class TracePacketWriter : public ValueObject { int64_t timestamp, const TimelineEvent& event) { PopulatePacket(event_type, timestamp, event); + interned_data_builder_.AttachInternedDataTo(packet_.get()); write_callback_(packet_); packet_.Reset(); } @@ -884,7 +1043,7 @@ class TracePacketWriter : public ValueObject { perfetto_utils::SetTimestampAndMonotonicClockId(packet_.get(), timestamp); TrackEvent* track_event = packet_->set_track_event(); - track_event->add_categories(event.stream()->name()); + SetTrackEventCategory(track_event, event.stream()->name()); track_event->set_track_uuid(IsSync(event_type) ? OSThread::ThreadIdToIntPtr(event.thread()) @@ -892,7 +1051,7 @@ class TracePacketWriter : public ValueObject { const auto perfetto_type = ToPerfettoType(event_type); track_event->set_type(perfetto_type); if (perfetto_type != TrackEvent::Type::TYPE_SLICE_END) { - track_event->set_name(event.label()); + SetTrackEventName(track_event, event.label()); for (intptr_t i = 0; i < event.flow_id_count(); ++i) { // TODO(derekx): |TrackEvent|s have a |terminating_flow_ids| field that // we aren't able to populate right now because we aren't keeping track @@ -911,37 +1070,120 @@ class TracePacketWriter : public ValueObject { ASSERT(event.GetNumArguments() == 1); perfetto::protos::pbzero::DebugAnnotation& debug_annotation = *track_event->add_debug_annotations(); - debug_annotation.set_name(event.arguments()[0].name); + SetDebugAnnotationName(debug_annotation, event.arguments()[0].name); debug_annotation.set_legacy_json_value(event.arguments()[0].value); } else { for (intptr_t i = 0; i < event.GetNumArguments(); ++i) { perfetto::protos::pbzero::DebugAnnotation& debug_annotation = *track_event->add_debug_annotations(); - debug_annotation.set_name(event.arguments()[i].name); - debug_annotation.set_string_value(event.arguments()[i].value); + SetDebugAnnotationName(debug_annotation, event.arguments()[0].name); + SetDebugAnnotationStringValue(debug_annotation, + event.arguments()[i].value); } } } if (event.HasIsolateId()) { perfetto::protos::pbzero::DebugAnnotation& debug_annotation = *track_event->add_debug_annotations(); - debug_annotation.set_name("isolateId"); - std::unique_ptr formatted_isolate_id = - event.GetFormattedIsolateId(); - debug_annotation.set_string_value(formatted_isolate_id.get()); + SetDebugAnnotationName(debug_annotation, "isolateId", [this](auto name) { + return interned_data_builder_.iid_isolateId(); + }); + SetDebugAnnotationStringValueFromFormattedId( + debug_annotation, ISOLATE_SERVICE_ID_FORMAT_STRING, + event.isolate_id(), [this](auto id) { + return interned_data_builder_.InternFormattedIsolateId(id); + }); } if (event.HasIsolateGroupId()) { perfetto::protos::pbzero::DebugAnnotation& debug_annotation = *track_event->add_debug_annotations(); - debug_annotation.set_name("isolateGroupId"); - std::unique_ptr formatted_isolate_group = - event.GetFormattedIsolateGroupId(); - debug_annotation.set_string_value(formatted_isolate_group.get()); + SetDebugAnnotationName( + debug_annotation, "isolateGroupId", [this](auto name) { + return interned_data_builder_.iid_isolateGroupId(); + }); + SetDebugAnnotationStringValueFromFormattedId( + debug_annotation, ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING, + event.isolate_group_id(), [this](auto id) { + return interned_data_builder_.InternFormattedIsolateGroupId(id); + }); + } + } + + // Helpers for setting string valued properties on |TrackEvent| and + // |DebugAnnotation|, these can use interning if |intern_strings_| is + // |true|. + + void SetTrackEventCategory(TrackEvent* track_event, const char* value) { + if (intern_strings_) { + track_event->add_category_iids( + interned_data_builder_.event_categories().Intern(value)); + } else { + track_event->add_categories(value); + } + } + + void SetTrackEventName(TrackEvent* track_event, const char* value) { + if (intern_strings_) { + track_event->set_name_iid( + interned_data_builder_.event_names().Intern(value)); + } else { + track_event->set_name(value); + } + } + + template + void SetDebugAnnotationName( + perfetto::protos::pbzero::DebugAnnotation& debug_annotation, + const char* name, + F&& intern) { + if (intern_strings_) { + debug_annotation.set_name_iid(intern(name)); + } else { + debug_annotation.set_name(name); + } + } + + void SetDebugAnnotationName( + perfetto::protos::pbzero::DebugAnnotation& debug_annotation, + const char* name) { + SetDebugAnnotationName(debug_annotation, name, [this](auto name) { + return interned_data_builder_.debug_annotation_names().Intern(name); + }); + } + + void SetDebugAnnotationStringValue( + perfetto::protos::pbzero::DebugAnnotation& debug_annotation, + const char* value) { + if (intern_strings_) { + debug_annotation.set_string_value_iid( + interned_data_builder_.debug_annotation_string_values().Intern( + value)); + } else { + debug_annotation.set_string_value(value); + } + } + + template + void SetDebugAnnotationStringValueFromFormattedId( + perfetto::protos::pbzero::DebugAnnotation& debug_annotation, + const char (&format)[kFormatLen], + uint64_t id, + F&& intern_id) { + if (intern_strings_) { + debug_annotation.set_string_value_iid(intern_id(id)); + } else { + // 20 characters is enough to format any uint64_t (or int64_t) value. + char formatted[kFormatLen + 20]; + Utils::SNPrint(formatted, ARRAY_SIZE(formatted), format, id); + debug_annotation.set_string_value(formatted); } } protozero::HeapBuffered& packet_; WriteCallback write_callback_; + const bool intern_strings_; + + InternedDataBuilder interned_data_builder_; DISALLOW_COPY_AND_ASSIGN(TracePacketWriter); }; @@ -978,34 +1220,6 @@ bool TimelineEvent::HasIsolateGroupId() const { return isolate_group_id_ != ILLEGAL_ISOLATE_GROUP_ID; } -std::unique_ptr TimelineEvent::GetFormattedIsolateId() const { - ASSERT(HasIsolateId()); - intptr_t formatted_isolate_id_buffer_size = - Utils::SNPrint(nullptr, 0, ISOLATE_SERVICE_ID_FORMAT_STRING, - isolate_id_) + - 1; - auto formatted_isolate_id = - std::make_unique(formatted_isolate_id_buffer_size); - Utils::SNPrint(formatted_isolate_id.get(), formatted_isolate_id_buffer_size, - ISOLATE_SERVICE_ID_FORMAT_STRING, isolate_id_); - return formatted_isolate_id; -} - -std::unique_ptr TimelineEvent::GetFormattedIsolateGroupId() - const { - ASSERT(HasIsolateGroupId()); - intptr_t formatted_isolate_group_id_buffer_size = - Utils::SNPrint(nullptr, 0, ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING, - isolate_group_id_) + - 1; - auto formatted_isolate_group_id = - std::make_unique(formatted_isolate_group_id_buffer_size); - Utils::SNPrint(formatted_isolate_group_id.get(), - formatted_isolate_group_id_buffer_size, - ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING, isolate_group_id_); - return formatted_isolate_group_id; -} - TimelineTrackMetadata::TimelineTrackMetadata(intptr_t pid, intptr_t tid, CStringUniquePtr&& track_name) @@ -1669,9 +1883,13 @@ void TimelineEventBufferedRecorder::PrintJSONEvents( void TimelineEventBufferedRecorder::PrintPerfettoEvents( JSONBase64String* jsonBase64String, const TimelineEventFilter& filter) { - TracePacketWriter writer(packet(), [&jsonBase64String](auto& packet) { - perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String, &packet); - }); + TracePacketWriter writer( + packet(), + [&jsonBase64String](auto& packet) { + perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String, + &packet); + }, + FLAG_intern_strings_when_writing_perfetto_timeline); PrintEventsCommon(filter, [&writer](const TimelineEvent& event) { writer.WriteEvent(event); @@ -2124,7 +2342,10 @@ static TimelineEventRecorder* CreateTimelineEventPerfettoFileRecorder( TimelineEventPerfettoFileRecorder::TimelineEventPerfettoFileRecorder( const char* path) : TimelineEventFileRecorderBase(path), - writer_(packet(), [this](auto& packet) { this->WritePacket(&packet); }) { + writer_( + packet(), + [this](auto& packet) { this->WritePacket(&packet); }, + FLAG_intern_strings_when_writing_perfetto_timeline) { protozero::HeapBuffered& packet = this->packet(); diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h index b0e978f24cc..a4a17348bb3 100644 --- a/runtime/vm/timeline.h +++ b/runtime/vm/timeline.h @@ -456,8 +456,6 @@ class TimelineEvent { bool HasIsolateId() const; bool HasIsolateGroupId() const; - std::unique_ptr GetFormattedIsolateId() const; - std::unique_ptr GetFormattedIsolateGroupId() const; // The lowest time value stored in this event. int64_t LowTime() const; diff --git a/runtime/vm/zone.h b/runtime/vm/zone.h index 60003764301..e5b5aac86fe 100644 --- a/runtime/vm/zone.h +++ b/runtime/vm/zone.h @@ -93,6 +93,10 @@ class Zone { static void ClearCache(); static intptr_t Size() { return total_size_; } + // Allow templated containers to check if this allocator supports + // freeing individual allocations. + static constexpr bool kSupportsFreeingIndividualAllocations = false; + private: Zone(); ~Zone(); // Delete all memory associated with the zone.