From 4cd9c9c66663562d93261224eaaaa728e2a6a421 Mon Sep 17 00:00:00 2001 From: Derek Xu Date: Mon, 17 Apr 2023 14:42:50 +0000 Subject: [PATCH] Reland "[VM] Begin supporting Perfetto file recorder" This is a reland of commit 7424295ce95366a22321f4ec25040972ef17a728 The differences between this reland and the original CL are: now the Perfetto file recorder does not get built in PRODUCT builds, and it does not get built unless the SUPPORT_PERFETTO macro is defined. TEST=Recorded traces with the Perfetto file recorder and explored them in Perfetto's trace viewer, CI Original change's description: > [VM] Begin supporting Perfetto file recorder > > This CL adds the `TimelineEventPerfettoFileRecorder` class, which is a > timeline recorder that writes a trace to file in Perfetto's proto > format. This CL supports the recording of all types of timeline events > except flow events. Support for flow events will be added in a future > CL. > > TEST=Recorded traces with the Perfetto file recorder and explored them > in Perfetto's trace viewer, CI > > Change-Id: Iaa2051e536589a473c5e15f9de9bb9c251f13d38 > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278942 > Reviewed-by: Ben Konyi > Commit-Queue: Derek Xu Change-Id: I8713f704b5fbeed5f1231012bce8a32aaf566ae4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/286020 Reviewed-by: Ben Konyi Commit-Queue: Derek Xu --- runtime/BUILD.gn | 6 +- runtime/PRESUBMIT.py | 6 +- runtime/bin/BUILD.gn | 1 + runtime/platform/address_sanitizer.h | 2 +- runtime/tools/run_clang_tidy.dart | 28 ++ runtime/vm/BUILD.gn | 112 +++++- runtime/vm/globals.h | 7 +- runtime/vm/protos/.gitignore | 3 + .../perfetto/common/builtin_clock.pbzero.h | 46 +++ .../perfetto/common/builtin_clock.proto | 31 ++ .../perfetto/trace/clock_snapshot.pbzero.h | 195 ++++++++++ .../perfetto/trace/clock_snapshot.proto | 56 +++ .../perfetto/trace/trace_packet.pbzero.h | 223 ++++++++++++ .../protos/perfetto/trace/trace_packet.proto | 88 +++++ .../track_event/debug_annotation.pbzero.h | 161 +++++++++ .../trace/track_event/debug_annotation.proto | 84 +++++ .../track_event/process_descriptor.pbzero.h | 115 ++++++ .../track_event/process_descriptor.proto | 36 ++ .../track_event/thread_descriptor.pbzero.h | 143 ++++++++ .../trace/track_event/thread_descriptor.proto | 38 ++ .../track_event/track_descriptor.pbzero.h | 192 ++++++++++ .../trace/track_event/track_descriptor.proto | 85 +++++ .../trace/track_event/track_event.pbzero.h | 316 +++++++++++++++++ .../trace/track_event/track_event.proto | 176 +++++++++ .../protos/tools/compile_perfetto_protos.dart | 77 ++++ runtime/vm/timeline.cc | 335 +++++++++++++++++- runtime/vm/timeline.h | 68 +++- runtime/vm/vm_sources.gni | 8 + 28 files changed, 2605 insertions(+), 33 deletions(-) create mode 100644 runtime/vm/protos/.gitignore create mode 100644 runtime/vm/protos/perfetto/common/builtin_clock.pbzero.h create mode 100644 runtime/vm/protos/perfetto/common/builtin_clock.proto create mode 100644 runtime/vm/protos/perfetto/trace/clock_snapshot.pbzero.h create mode 100644 runtime/vm/protos/perfetto/trace/clock_snapshot.proto create mode 100644 runtime/vm/protos/perfetto/trace/trace_packet.pbzero.h create mode 100644 runtime/vm/protos/perfetto/trace/trace_packet.proto create mode 100644 runtime/vm/protos/perfetto/trace/track_event/debug_annotation.pbzero.h create mode 100644 runtime/vm/protos/perfetto/trace/track_event/debug_annotation.proto create mode 100644 runtime/vm/protos/perfetto/trace/track_event/process_descriptor.pbzero.h create mode 100644 runtime/vm/protos/perfetto/trace/track_event/process_descriptor.proto create mode 100644 runtime/vm/protos/perfetto/trace/track_event/thread_descriptor.pbzero.h create mode 100644 runtime/vm/protos/perfetto/trace/track_event/thread_descriptor.proto create mode 100644 runtime/vm/protos/perfetto/trace/track_event/track_descriptor.pbzero.h create mode 100644 runtime/vm/protos/perfetto/trace/track_event/track_descriptor.proto create mode 100644 runtime/vm/protos/perfetto/trace/track_event/track_event.pbzero.h create mode 100644 runtime/vm/protos/perfetto/trace/track_event/track_event.proto create mode 100644 runtime/vm/protos/tools/compile_perfetto_protos.dart diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index 2034a6db068..49a5eb3ea45 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -146,7 +146,10 @@ config("dart_arch_config") { } config("dart_config") { - defines = [] + # We temporarily need to define this to exclude code that depends on Perfetto + # from being built in google3, as there is currently a problem linking + # Perfetto into google3 builds. + defines = [ "SUPPORT_PERFETTO" ] if (dart_debug) { defines += [ "DEBUG" ] @@ -291,6 +294,7 @@ source_set("dart_api") { library_for_all_configs("libdart") { target_type = dart_component_kind + extra_nonproduct_deps = [ "vm:libprotozero" ] extra_deps = [ ":generate_version_cc_file", "third_party/double-conversion/src:libdouble_conversion", diff --git a/runtime/PRESUBMIT.py b/runtime/PRESUBMIT.py index 2077f5a9881..25a577b0108 100644 --- a/runtime/PRESUBMIT.py +++ b/runtime/PRESUBMIT.py @@ -39,7 +39,11 @@ def RunLint(input_api, output_api): # Find all .cc and .h files in the change list. for git_file in input_api.AffectedTextFiles(): filename = git_file.AbsoluteLocalPath() - if filename.endswith('.cc') or filename.endswith('.h'): + if filename.endswith('.cc') or ( + # cpplint complains about the style of #ifndefs in our .pbzero.h + # files, but they are generated by the protozero compiler, so we + # can't fix this. + not filename.endswith('.pbzero.h') and filename.endswith('.h')): # Run cpplint on the file. cpplint.ProcessFile(filename, 1) # Check for memcpy use. diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn index e757b71d483..39984f5057c 100644 --- a/runtime/bin/BUILD.gn +++ b/runtime/bin/BUILD.gn @@ -957,6 +957,7 @@ executable("run_vm_tests") { ":dart_snapshot_cc", ":standalone_dart_io", "..:libdart_precompiler_testing", + "../vm:libprotozero", # for timeline_test "//third_party/boringssl", # for secure_socket_utils_test "//third_party/zlib", ] diff --git a/runtime/platform/address_sanitizer.h b/runtime/platform/address_sanitizer.h index 80a54dbc46e..71b80244f9e 100644 --- a/runtime/platform/address_sanitizer.h +++ b/runtime/platform/address_sanitizer.h @@ -16,7 +16,7 @@ #endif #if defined(USING_ADDRESS_SANITIZER) -extern "C" void __asan_unpoison_memory_region(void*, size_t); +extern "C" void __asan_unpoison_memory_region(void const volatile*, size_t); #define NO_SANITIZE_ADDRESS __attribute__((no_sanitize("address"))) #define ASAN_UNPOISON(ptr, len) __asan_unpoison_memory_region(ptr, len) #else // defined(USING_ADDRESS_SANITIZER) diff --git a/runtime/tools/run_clang_tidy.dart b/runtime/tools/run_clang_tidy.dart index 4980d3b62f8..298a2e73293 100644 --- a/runtime/tools/run_clang_tidy.dart +++ b/runtime/tools/run_clang_tidy.dart @@ -8,6 +8,30 @@ import 'dart:math'; import 'package:pool/pool.dart'; +// "perfetto_build_flags.h" is included by some Perfetto headers that we +// include, but it is generated at build-time. We prevent clang-tidy from +// reporting a "file not found" error by ensuring that this file exists in +// Iout/DebugX64/gen and passing +// -Iout/DebugX64/gen/third_party/perfetto/build_config to clang-tidy below. +Future generatePerfettoBuildFlags() async { + final processResult = await Process.run( + './tools/build.py', + ['-mdebug', '-ax64', '--no-goma', 'third_party/perfetto/gn:gen_buildflags'], + ); + + final int exitCode = processResult.exitCode; + final String stdout = processResult.stdout.trim(); + final String stderr = processResult.stderr.trim(); + + if (exitCode != 0) { + print('exit-code: $exitCode'); + print('stdout:'); + print('${stdout}'); + print('stderr:'); + print('${stderr}'); + } +} + const String clangTidy = './buildtools/linux-x64/clang/bin/clang-tidy'; List compilerFlagsForFile(String filepath) { @@ -16,7 +40,9 @@ List compilerFlagsForFile(String filepath) { '-Ithird_party', '-Iruntime/include', '-Ithird_party/boringssl/src/include', + '-Ithird_party/perfetto/include', '-Ithird_party/zlib', + '-Iout/DebugX64/gen/third_party/perfetto/build_config', '-DTARGET_ARCH_X64', '-DDEBUG', '-DDART_TARGET_OS_LINUX', @@ -123,6 +149,8 @@ final Set excludedFiles = Set.from([ ]); main(List files) async { + await generatePerfettoBuildFlags(); + bool isFirstFailure = true; files = files.where((filepath) => !excludedFiles.contains(filepath)).toList(); diff --git a/runtime/vm/BUILD.gn b/runtime/vm/BUILD.gn index a9f3286aca7..2ac3de566c3 100644 --- a/runtime/vm/BUILD.gn +++ b/runtime/vm/BUILD.gn @@ -2,6 +2,7 @@ # for details. All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. +import("//third_party/protobuf/proto_library.gni") import("../../build/executable_suffix.gni") import("../../sdk/lib/async/async_sources.gni") import("../../sdk/lib/collection/collection_sources.gni") @@ -67,6 +68,79 @@ config("libdart_vm_config") { } } +# This is a modified copy of Perfetto's template of the same name defined in +# //third_party/perfetto/gn/proto_library.gni. +# This is equivalent to the proto_library template (generation of .h/.cc from +# .proto files) but enables generation using the protozero plugin. +# The generated files will have the .pbzero.{cc,h} suffix, as opposed to the +# .pb.{cc,h} of the official proto library. +template("protozero_library") { + proto_library(target_name) { + perfetto_root_path = "//third_party/perfetto/" + + generate_cc = false + generate_python = false + generator_plugin_label = + perfetto_root_path + "src/protozero/protoc_plugin:protozero_plugin" + generator_plugin_suffix = ".pbzero" + + if (defined(invoker.deps)) { + deps = invoker.deps + } else { + deps = [] + } + + forward_variables_from(invoker, + [ + "defines", + "generator_plugin_options", + "include_dirs", + "proto_in_dir", + "proto_out_dir", + "sources", + "testonly", + "visibility", + "generate_descriptor", + "propagate_imports_configs", + "import_dirs", + ]) + } +} + +# This target is not a dependency of any other GN target. It is required to make +# ./protos/tools/compile_perfetto_protos.dart work though. +protozero_library("perfetto_protos") { + proto_in_dir = "." + proto_out_dir = "//runtime/vm" + generator_plugin_options = "wrapper_namespace=pbzero" + sources = [ + "protos/perfetto/common/builtin_clock.proto", + "protos/perfetto/trace/clock_snapshot.proto", + "protos/perfetto/trace/trace_packet.proto", + "protos/perfetto/trace/track_event/debug_annotation.proto", + "protos/perfetto/trace/track_event/process_descriptor.proto", + "protos/perfetto/trace/track_event/thread_descriptor.proto", + "protos/perfetto/trace/track_event/track_descriptor.proto", + "protos/perfetto/trace/track_event/track_event.proto", + ] +} + +# This config needs to be propagated to all targets that depend on +# ":libprotozero". +config("libprotozero_config") { + include_dirs = [ + "//third_party/perfetto/include", + "$root_gen_dir/third_party/perfetto/build_config", + ] +} + +# This target includes Perfetto's protozero target, which we need to serialize +# proto messages. This target also propagates ":libprotozero_config". +source_set("libprotozero") { + public_configs = [ ":libprotozero_config" ] + deps = [ "//third_party/perfetto/src/protozero:protozero" ] +} + library_for_all_configs("libdart_vm") { target_type = "source_set" extra_product_deps = [ @@ -74,12 +148,14 @@ library_for_all_configs("libdart_vm") { "//third_party/icu:icuuc_hidden_visibility", ] extra_nonproduct_deps = [ + ":libprotozero", "//third_party/icu:icui18n", "//third_party/icu:icuuc", ] + extra_deps = [] if (is_fuchsia) { if (using_fuchsia_gn_sdk) { - extra_deps = [ + extra_deps += [ "$fuchsia_sdk_root/fidl/fuchsia.intl", "$fuchsia_sdk_root/pkg/async", "$fuchsia_sdk_root/pkg/async-default", @@ -92,7 +168,7 @@ library_for_all_configs("libdart_vm") { "$fuchsia_sdk_root/pkg/trace-engine", ] } else if (using_fuchsia_sdk) { - extra_deps = [ + extra_deps += [ "$fuchsia_sdk_root/fidl:fuchsia.intl", "$fuchsia_sdk_root/pkg:async-loop", "$fuchsia_sdk_root/pkg:async-loop-default", @@ -103,7 +179,7 @@ library_for_all_configs("libdart_vm") { "$fuchsia_sdk_root/pkg:trace-engine", ] } else { - extra_deps = [ + extra_deps += [ "//sdk/fidl/fuchsia.intl", "//sdk/lib/sys/cpp", "//sdk/lib/sys/inspect/cpp", @@ -131,13 +207,15 @@ library_for_all_configs_with_compiler("libdart_compiler") { public_configs = [ ":libdart_vm_config" ] sources = rebase_path(compiler_sources, ".", "./compiler/") include_dirs = [ ".." ] + extra_nonproduct_deps = [ ":libprotozero" ] + extra_deps = [] if (is_fuchsia) { if (using_fuchsia_gn_sdk) { - extra_deps = [ "$fuchsia_sdk_root/pkg/trace-engine" ] + extra_deps += [ "$fuchsia_sdk_root/pkg/trace-engine" ] } else if (using_fuchsia_sdk) { - extra_deps = [ "$fuchsia_sdk_root/pkg:trace-engine" ] + extra_deps += [ "$fuchsia_sdk_root/pkg:trace-engine" ] } else { - extra_deps = [ + extra_deps += [ "//zircon/public/lib/fbl", "//zircon/system/ulib/trace-engine", ] @@ -147,13 +225,15 @@ library_for_all_configs_with_compiler("libdart_compiler") { library_for_all_configs("libdart_lib") { target_type = "source_set" + extra_nonproduct_deps = [ ":libprotozero" ] + extra_deps = [] if (is_fuchsia) { if (using_fuchsia_gn_sdk) { - extra_deps = [ "$fuchsia_sdk_root/pkg/trace-engine" ] + extra_deps += [ "$fuchsia_sdk_root/pkg/trace-engine" ] } else if (using_fuchsia_sdk) { - extra_deps = [ "$fuchsia_sdk_root/pkg:trace-engine" ] + extra_deps += [ "$fuchsia_sdk_root/pkg:trace-engine" ] } else { - extra_deps = [ + extra_deps += [ "//zircon/public/lib/fbl", "//zircon/system/ulib/trace-engine", ] @@ -232,6 +312,13 @@ group("kernel_platform_files") { } executable("offsets_extractor") { + # The timeline cannot be accessed from the generated executable, so we define + # DART_DISABLE_TIMELINE to strip out the timeline source code. The precise + # reason why we do this is to avoid missing header errors, as the Perfetto + # proto headers are not built as a dependency of this target, but are + # transitively included in this target when DART_DISABLE_TIMELINE is not + # defined. + defines = [ "DART_DISABLE_TIMELINE" ] configs += [ "..:dart_arch_config", "..:dart_config", @@ -243,6 +330,13 @@ executable("offsets_extractor") { } executable("offsets_extractor_precompiled_runtime") { + # The timeline cannot be accessed from the generated executable, so we define + # DART_DISABLE_TIMELINE to strip out the timeline source code. The precise + # reason why we do this is to avoid missing header errors, as the Perfetto + # proto headers are not built as a dependency of this target, but are + # transitively included in this target when DART_DISABLE_TIMELINE is not + # defined. + defines = [ "DART_DISABLE_TIMELINE" ] configs += [ "..:dart_arch_config", "..:dart_config", diff --git a/runtime/vm/globals.h b/runtime/vm/globals.h index 3af311ebfdf..ad9ca5b86f5 100644 --- a/runtime/vm/globals.h +++ b/runtime/vm/globals.h @@ -113,9 +113,10 @@ const intptr_t kDefaultNewGenSemiMaxSize = (kWordSize <= 4) ? 8 : 16; #define NOT_IN_PRECOMPILED_RUNTIME(code) code #endif // defined(DART_PRECOMPILED_RUNTIME) -#if defined(DART_ENABLE_TIMELINE) || !defined(PRODUCT) || \ - defined(DART_HOST_OS_FUCHSIA) || defined(DART_TARGET_OS_FUCHSIA) || \ - defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS) +#if !defined(DART_DISABLE_TIMELINE) && \ + (defined(DART_ENABLE_TIMELINE) || !defined(PRODUCT) || \ + defined(DART_HOST_OS_FUCHSIA) || defined(DART_TARGET_OS_FUCHSIA) || \ + defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS)) #define SUPPORT_TIMELINE 1 #endif diff --git a/runtime/vm/protos/.gitignore b/runtime/vm/protos/.gitignore new file mode 100644 index 00000000000..3ac7cc474a9 --- /dev/null +++ b/runtime/vm/protos/.gitignore @@ -0,0 +1,3 @@ +# The protozero compiler generates empty .pbzero.cc files for compatibility +# reasons (crbug.com/998165). We can ignore them. +*.pbzero.cc diff --git a/runtime/vm/protos/perfetto/common/builtin_clock.pbzero.h b/runtime/vm/protos/perfetto/common/builtin_clock.pbzero.h new file mode 100644 index 00000000000..64d03652968 --- /dev/null +++ b/runtime/vm/protos/perfetto/common/builtin_clock.pbzero.h @@ -0,0 +1,46 @@ +// 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. + +// IMPORTANT: This file should only ever be modified by modifying the +// corresponding .proto file and then running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the SDK root +// directory. +// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT. + +#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_COMMON_BUILTIN_CLOCK_PROTO_H_ +#define PERFETTO_PROTOS_PROTOS_PERFETTO_COMMON_BUILTIN_CLOCK_PROTO_H_ + +#include +#include + +#include "perfetto/protozero/field_writer.h" +#include "perfetto/protozero/message.h" +#include "perfetto/protozero/packed_repeated_fields.h" +#include "perfetto/protozero/proto_decoder.h" +#include "perfetto/protozero/proto_utils.h" + +namespace perfetto { +namespace protos { +namespace pbzero { + +enum BuiltinClock : int32_t { + BUILTIN_CLOCK_MONOTONIC = 3, +}; + +constexpr BuiltinClock BuiltinClock_MIN = BuiltinClock::BUILTIN_CLOCK_MONOTONIC; +constexpr BuiltinClock BuiltinClock_MAX = BuiltinClock::BUILTIN_CLOCK_MONOTONIC; + +PERFETTO_PROTOZERO_CONSTEXPR14_OR_INLINE +const char* BuiltinClock_Name(::perfetto::protos::pbzero::BuiltinClock value) { + switch (value) { + case ::perfetto::protos::pbzero::BuiltinClock::BUILTIN_CLOCK_MONOTONIC: + return "BUILTIN_CLOCK_MONOTONIC"; + } + return "PBZERO_UNKNOWN_ENUM_VALUE"; +} + +} // namespace pbzero +} // namespace protos +} // namespace perfetto +#endif // Include guard. diff --git a/runtime/vm/protos/perfetto/common/builtin_clock.proto b/runtime/vm/protos/perfetto/common/builtin_clock.proto new file mode 100644 index 00000000000..8a639cddc58 --- /dev/null +++ b/runtime/vm/protos/perfetto/common/builtin_clock.proto @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTE: This is a manually minified version of Perfetto's +// builtin_clock.proto. + +// IMPORTANT: The coresponding .pbzero.h file must be regenerated after +// any change is made to this file. This can be done by running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the +// SDK root directory. + +syntax = "proto2"; + +package perfetto.protos; + +enum BuiltinClock { + BUILTIN_CLOCK_MONOTONIC = 3; +} diff --git a/runtime/vm/protos/perfetto/trace/clock_snapshot.pbzero.h b/runtime/vm/protos/perfetto/trace/clock_snapshot.pbzero.h new file mode 100644 index 00000000000..08795532490 --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/clock_snapshot.pbzero.h @@ -0,0 +1,195 @@ +// 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. + +// IMPORTANT: This file should only ever be modified by modifying the +// corresponding .proto file and then running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the SDK root +// directory. +// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT. + +#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_CLOCK_SNAPSHOT_PROTO_H_ +#define PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_CLOCK_SNAPSHOT_PROTO_H_ + +#include +#include + +#include "perfetto/protozero/field_writer.h" +#include "perfetto/protozero/message.h" +#include "perfetto/protozero/packed_repeated_fields.h" +#include "perfetto/protozero/proto_decoder.h" +#include "perfetto/protozero/proto_utils.h" + +namespace perfetto { +namespace protos { +namespace pbzero { + +class ClockSnapshot_Clock; +enum BuiltinClock : int32_t; + +class ClockSnapshot_Decoder : public ::protozero::TypedProtoDecoder< + /*MAX_FIELD_ID=*/2, + /*HAS_NONPACKED_REPEATED_FIELDS=*/true> { + public: + ClockSnapshot_Decoder(const uint8_t* data, size_t len) + : TypedProtoDecoder(data, len) {} + explicit ClockSnapshot_Decoder(const std::string& raw) + : TypedProtoDecoder(reinterpret_cast(raw.data()), + raw.size()) {} + explicit ClockSnapshot_Decoder(const ::protozero::ConstBytes& raw) + : TypedProtoDecoder(raw.data, raw.size) {} + bool has_clocks() const { return at<1>().valid(); } + ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> clocks() const { + return GetRepeated<::protozero::ConstBytes>(1); + } + bool has_primary_trace_clock() const { return at<2>().valid(); } + int32_t primary_trace_clock() const { return at<2>().as_int32(); } +}; + +class ClockSnapshot : public ::protozero::Message { + public: + using Decoder = ClockSnapshot_Decoder; + enum : int32_t { + kClocksFieldNumber = 1, + kPrimaryTraceClockFieldNumber = 2, + }; + static constexpr const char* GetName() { + return ".perfetto.protos.ClockSnapshot"; + } + + using Clock = ::perfetto::protos::pbzero::ClockSnapshot_Clock; + + using FieldMetadata_Clocks = ::protozero::proto_utils::FieldMetadata< + 1, + ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked, + ::protozero::proto_utils::ProtoSchemaType::kMessage, + ClockSnapshot_Clock, + ClockSnapshot>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Clocks kClocks() { return {}; } + template + T* add_clocks() { + return BeginNestedMessage(1); + } + + using FieldMetadata_PrimaryTraceClock = + ::protozero::proto_utils::FieldMetadata< + 2, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kEnum, + ::perfetto::protos::pbzero::BuiltinClock, + ClockSnapshot>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_PrimaryTraceClock kPrimaryTraceClock() { + return {}; + } + void set_primary_trace_clock(::perfetto::protos::pbzero::BuiltinClock value) { + static constexpr uint32_t field_id = + FieldMetadata_PrimaryTraceClock::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kEnum>::Append(*this, + field_id, + value); + } +}; + +class ClockSnapshot_Clock_Decoder + : public ::protozero::TypedProtoDecoder< + /*MAX_FIELD_ID=*/2, + /*HAS_NONPACKED_REPEATED_FIELDS=*/false> { + public: + ClockSnapshot_Clock_Decoder(const uint8_t* data, size_t len) + : TypedProtoDecoder(data, len) {} + explicit ClockSnapshot_Clock_Decoder(const std::string& raw) + : TypedProtoDecoder(reinterpret_cast(raw.data()), + raw.size()) {} + explicit ClockSnapshot_Clock_Decoder(const ::protozero::ConstBytes& raw) + : TypedProtoDecoder(raw.data, raw.size) {} + bool has_clock_id() const { return at<1>().valid(); } + uint32_t clock_id() const { return at<1>().as_uint32(); } + bool has_timestamp() const { return at<2>().valid(); } + uint64_t timestamp() const { return at<2>().as_uint64(); } +}; + +class ClockSnapshot_Clock : public ::protozero::Message { + public: + using Decoder = ClockSnapshot_Clock_Decoder; + enum : int32_t { + kClockIdFieldNumber = 1, + kTimestampFieldNumber = 2, + }; + static constexpr const char* GetName() { + return ".perfetto.protos.ClockSnapshot.Clock"; + } + + using FieldMetadata_ClockId = ::protozero::proto_utils::FieldMetadata< + 1, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint32, + uint32_t, + ClockSnapshot_Clock>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_ClockId kClockId() { return {}; } + void set_clock_id(uint32_t value) { + static constexpr uint32_t field_id = FieldMetadata_ClockId::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kUint32>::Append(*this, + field_id, + value); + } + + using FieldMetadata_Timestamp = ::protozero::proto_utils::FieldMetadata< + 2, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint64, + uint64_t, + ClockSnapshot_Clock>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Timestamp kTimestamp() { return {}; } + void set_timestamp(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_Timestamp::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 +} // namespace protos +} // namespace perfetto +#endif // Include guard. diff --git a/runtime/vm/protos/perfetto/trace/clock_snapshot.proto b/runtime/vm/protos/perfetto/trace/clock_snapshot.proto new file mode 100644 index 00000000000..e0968afe0e0 --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/clock_snapshot.proto @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTE: This is a manually minified version of Perfetto's +// clock_snapshot.proto. + +// IMPORTANT: The coresponding .pbzero.h file must be regenerated after +// any change is made to this file. This can be done by running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the +// SDK root directory. + +syntax = "proto2"; + +import "protos/perfetto/common/builtin_clock.proto"; + +package perfetto.protos; + +// A snapshot of clock readings to allow for trace alignment. +message ClockSnapshot { + message Clock { + // Clock IDs have the following semantic: + // [1, 63]: Builtin types, see BuiltinClock from + // ../common/builtin_clock.proto. + // [64, 127]: User-defined clocks. These clocks are sequence-scoped. They + // are only valid within the same |trusted_packet_sequence_id| + // (i.e. only for TracePacket(s) emitted by the same TraceWriter + // that emitted the clock snapshot). + // [128, MAX]: Reserved for future use. The idea is to allow global clock + // IDs and setting this ID to hash(full_clock_name) & ~127. + optional uint32 clock_id = 1; + + // Absolute timestamp. Unit is ns unless specified otherwise by the + // unit_multiplier_ns field below. + optional uint64 timestamp = 2; + } + repeated Clock clocks = 1; + + // The authoritative clock domain for the trace. Defaults to BOOTTIME, but can + // be overridden in TraceConfig's builtin_data_sources. Trace processor will + // attempt to translate packet/event timestamps from various data sources (and + // their chosen clock domains) to this domain during import. + optional BuiltinClock primary_trace_clock = 2; +} diff --git a/runtime/vm/protos/perfetto/trace/trace_packet.pbzero.h b/runtime/vm/protos/perfetto/trace/trace_packet.pbzero.h new file mode 100644 index 00000000000..ab7c52252a5 --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/trace_packet.pbzero.h @@ -0,0 +1,223 @@ +// 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. + +// IMPORTANT: This file should only ever be modified by modifying the +// corresponding .proto file and then running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the SDK root +// directory. +// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT. + +#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_TRACE_PACKET_PROTO_H_ +#define PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_TRACE_PACKET_PROTO_H_ + +#include +#include + +#include "perfetto/protozero/field_writer.h" +#include "perfetto/protozero/message.h" +#include "perfetto/protozero/packed_repeated_fields.h" +#include "perfetto/protozero/proto_decoder.h" +#include "perfetto/protozero/proto_utils.h" + +namespace perfetto { +namespace protos { +namespace pbzero { + +class ClockSnapshot; +class TrackDescriptor; +class TrackEvent; + +class TracePacket_Decoder : public ::protozero::TypedProtoDecoder< + /*MAX_FIELD_ID=*/60, + /*HAS_NONPACKED_REPEATED_FIELDS=*/false> { + public: + TracePacket_Decoder(const uint8_t* data, size_t len) + : TypedProtoDecoder(data, len) {} + explicit TracePacket_Decoder(const std::string& raw) + : TypedProtoDecoder(reinterpret_cast(raw.data()), + raw.size()) {} + explicit TracePacket_Decoder(const ::protozero::ConstBytes& raw) + : TypedProtoDecoder(raw.data, raw.size) {} + bool has_timestamp() const { return at<8>().valid(); } + uint64_t timestamp() const { return at<8>().as_uint64(); } + bool has_timestamp_clock_id() const { return at<58>().valid(); } + uint32_t timestamp_clock_id() const { return at<58>().as_uint32(); } + bool has_clock_snapshot() const { return at<6>().valid(); } + ::protozero::ConstBytes clock_snapshot() const { return at<6>().as_bytes(); } + bool has_track_event() const { return at<11>().valid(); } + ::protozero::ConstBytes track_event() const { return at<11>().as_bytes(); } + bool has_track_descriptor() const { return at<60>().valid(); } + ::protozero::ConstBytes track_descriptor() const { + return at<60>().as_bytes(); + } + bool has_trusted_packet_sequence_id() const { return at<10>().valid(); } + uint32_t trusted_packet_sequence_id() const { return at<10>().as_uint32(); } +}; + +class TracePacket : public ::protozero::Message { + public: + using Decoder = TracePacket_Decoder; + enum : int32_t { + kTimestampFieldNumber = 8, + kTimestampClockIdFieldNumber = 58, + kClockSnapshotFieldNumber = 6, + kTrackEventFieldNumber = 11, + kTrackDescriptorFieldNumber = 60, + kTrustedPacketSequenceIdFieldNumber = 10, + }; + static constexpr const char* GetName() { + return ".perfetto.protos.TracePacket"; + } + + using FieldMetadata_Timestamp = ::protozero::proto_utils::FieldMetadata< + 8, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint64, + uint64_t, + TracePacket>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Timestamp kTimestamp() { return {}; } + void set_timestamp(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_Timestamp::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_TimestampClockId = + ::protozero::proto_utils::FieldMetadata< + 58, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint32, + uint32_t, + TracePacket>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_TimestampClockId kTimestampClockId() { + return {}; + } + void set_timestamp_clock_id(uint32_t value) { + static constexpr uint32_t field_id = + FieldMetadata_TimestampClockId::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kUint32>::Append(*this, + field_id, + value); + } + + using FieldMetadata_ClockSnapshot = ::protozero::proto_utils::FieldMetadata< + 6, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kMessage, + ClockSnapshot, + TracePacket>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_ClockSnapshot kClockSnapshot() { return {}; } + template + T* set_clock_snapshot() { + return BeginNestedMessage(6); + } + + using FieldMetadata_TrackEvent = ::protozero::proto_utils::FieldMetadata< + 11, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kMessage, + TrackEvent, + TracePacket>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_TrackEvent kTrackEvent() { return {}; } + template + T* set_track_event() { + return BeginNestedMessage(11); + } + + using FieldMetadata_TrackDescriptor = ::protozero::proto_utils::FieldMetadata< + 60, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kMessage, + TrackDescriptor, + TracePacket>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_TrackDescriptor kTrackDescriptor() { + return {}; + } + template + T* set_track_descriptor() { + return BeginNestedMessage(60); + } + + using FieldMetadata_TrustedPacketSequenceId = + ::protozero::proto_utils::FieldMetadata< + 10, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint32, + uint32_t, + TracePacket>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_TrustedPacketSequenceId + kTrustedPacketSequenceId() { + return {}; + } + void set_trusted_packet_sequence_id(uint32_t value) { + static constexpr uint32_t field_id = + FieldMetadata_TrustedPacketSequenceId::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kUint32>::Append(*this, + field_id, + value); + } +}; + +} // namespace pbzero +} // namespace protos +} // namespace perfetto +#endif // Include guard. diff --git a/runtime/vm/protos/perfetto/trace/trace_packet.proto b/runtime/vm/protos/perfetto/trace/trace_packet.proto new file mode 100644 index 00000000000..31a6254497c --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/trace_packet.proto @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTE: This is a manually minified version of Perfetto's +// trace_packet.proto. + +// IMPORTANT: The coresponding .pbzero.h file must be regenerated after +// any change is made to this file. This can be done by running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the +// SDK root directory. + +syntax = "proto2"; + +import "protos/perfetto/trace/clock_snapshot.proto"; +import "protos/perfetto/trace/track_event/track_descriptor.proto"; +import "protos/perfetto/trace/track_event/track_event.proto"; + +package perfetto.protos; + +// TracePacket is the root object of a Perfetto trace. +// A Perfetto trace is a linear sequence of TracePacket(s). +// +// The tracing service guarantees that all TracePacket(s) written by a given +// TraceWriter are seen in-order, without gaps or duplicates. If, for any +// reason, a TraceWriter sequence becomes invalid, no more packets are returned +// to the Consumer (or written into the trace file). +// TracePacket(s) written by different TraceWriter(s), hence even different +// data sources, can be seen in arbitrary order. +// The consumer can re-establish a total order, if interested, using the packet +// timestamps, after having synchronized the different clocks onto a global +// clock. +// +// The tracing service is agnostic of the content of TracePacket, with the +// exception of few fields (e.g.. trusted_*, trace_config) that are written by +// the service itself. +// +// See the [Buffers and Dataflow](/docs/concepts/buffers.md) doc for details. +// +// Next reserved id: 14 (up to 15). +// Next id: 88. +message TracePacket { + // The timestamp of the TracePacket. + // By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on + // Android). It can be overridden using a different timestamp_clock_id. + // The clock domain definition in ClockSnapshot can also override: + // - The unit (default: 1ns). + // - The absolute vs delta encoding (default: absolute timestamp). + optional uint64 timestamp = 8; + + // Specifies the ID of the clock used for the TracePacket |timestamp|. Can be + // one of the built-in types from ClockSnapshot::BuiltinClocks, or a + // producer-defined clock id. + // If unspecified and if no default per-sequence value has been provided via + // TracePacketDefaults, it defaults to BuiltinClocks::BOOTTIME. + optional uint32 timestamp_clock_id = 58; + + oneof data { + ClockSnapshot clock_snapshot = 6; + TrackEvent track_event = 11; + + // IDs up to 15 are reserved. They take only one byte to encode their + // preamble so should be used for frequent events. + + // Only used by TrackEvent. + TrackDescriptor track_descriptor = 60; + } + + // Service-assigned identifier of the packet sequence this packet belongs to. + // Uniquely identifies a producer + writer pair within the tracing session. A + // value of zero denotes an invalid ID. Keep in sync with + // TrustedPacket.trusted_packet_sequence_id. + oneof optional_trusted_packet_sequence_id { + uint32 trusted_packet_sequence_id = 10; + } +} 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 new file mode 100644 index 00000000000..dd80e0bdd4d --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/track_event/debug_annotation.pbzero.h @@ -0,0 +1,161 @@ +// 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. + +// IMPORTANT: This file should only ever be modified by modifying the +// corresponding .proto file and then running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the SDK root +// directory. +// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT. + +#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_TRACK_EVENT_DEBUG_ANNOTATION_PROTO_H_ +#define PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_TRACK_EVENT_DEBUG_ANNOTATION_PROTO_H_ + +#include +#include + +#include "perfetto/protozero/field_writer.h" +#include "perfetto/protozero/message.h" +#include "perfetto/protozero/packed_repeated_fields.h" +#include "perfetto/protozero/proto_decoder.h" +#include "perfetto/protozero/proto_utils.h" + +namespace perfetto { +namespace protos { +namespace pbzero { + +class DebugAnnotation_Decoder : public ::protozero::TypedProtoDecoder< + /*MAX_FIELD_ID=*/10, + /*HAS_NONPACKED_REPEATED_FIELDS=*/false> { + public: + DebugAnnotation_Decoder(const uint8_t* data, size_t len) + : TypedProtoDecoder(data, len) {} + explicit DebugAnnotation_Decoder(const std::string& raw) + : TypedProtoDecoder(reinterpret_cast(raw.data()), + raw.size()) {} + explicit DebugAnnotation_Decoder(const ::protozero::ConstBytes& raw) + : TypedProtoDecoder(raw.data, raw.size) {} + 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(); + } +}; + +class DebugAnnotation : public ::protozero::Message { + public: + using Decoder = DebugAnnotation_Decoder; + enum : int32_t { + kNameFieldNumber = 10, + kStringValueFieldNumber = 6, + kLegacyJsonValueFieldNumber = 9, + }; + static constexpr const char* GetName() { + return ".perfetto.protos.DebugAnnotation"; + } + + using FieldMetadata_Name = ::protozero::proto_utils::FieldMetadata< + 10, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kString, + std::string, + DebugAnnotation>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Name kName() { return {}; } + 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); + } + + using FieldMetadata_StringValue = ::protozero::proto_utils::FieldMetadata< + 6, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kString, + std::string, + DebugAnnotation>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_StringValue kStringValue() { return {}; } + 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, + ::protozero::proto_utils::ProtoSchemaType::kString, + std::string, + DebugAnnotation>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_LegacyJsonValue kLegacyJsonValue() { + return {}; + } + void set_legacy_json_value(const char* data, size_t size) { + AppendBytes(FieldMetadata_LegacyJsonValue::kFieldId, data, size); + } + void set_legacy_json_value(::protozero::ConstChars chars) { + AppendBytes(FieldMetadata_LegacyJsonValue::kFieldId, chars.data, + chars.size); + } + void set_legacy_json_value(std::string value) { + static constexpr uint32_t field_id = + FieldMetadata_LegacyJsonValue::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); + } +}; + +} // namespace pbzero +} // namespace protos +} // namespace perfetto +#endif // Include guard. diff --git a/runtime/vm/protos/perfetto/trace/track_event/debug_annotation.proto b/runtime/vm/protos/perfetto/trace/track_event/debug_annotation.proto new file mode 100644 index 00000000000..c9470a5e1ae --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/track_event/debug_annotation.proto @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTE: This is a manually minified version of Perfetto's +// debug_annotation.proto. + +// IMPORTANT: The coresponding .pbzero.h file must be regenerated after +// any change is made to this file. This can be done by running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the +// SDK root directory. + +syntax = "proto2"; + +package perfetto.protos; + +// Proto representation of untyped key/value annotations provided in TRACE_EVENT +// macros. Users of the Perfetto SDK should prefer to use the +// perfetto::TracedValue API to fill these protos, rather than filling them +// manually. +// +// Debug annotations are intended for debug use and are not considered a stable +// API of the trace contents. Trace-based metrics that use debug annotation +// values are prone to breakage, so please rely on typed TrackEvent fields for +// these instead. +// +// DebugAnnotations support nested arrays and dictionaries. Each entry is +// encoded as a single DebugAnnotation message. Only dictionary entries +// set the "name" field. The TrackEvent message forms an implicit root +// dictionary. +// +// Example TrackEvent with nested annotations: +// track_event { +// debug_annotations { +// name: "foo" +// dict_entries { +// name: "a" +// bool_value: true +// } +// dict_entries { +// name: "b" +// int_value: 123 +// } +// } +// debug_annotations { +// name: "bar" +// array_values { +// string_value: "hello" +// } +// array_values { +// string_value: "world" +// } +// } +// } +// +// Next ID: 17. +// Reserved ID: 15 +message DebugAnnotation { + // Name fields are set only for dictionary entries. + oneof name_field { + // 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; + } +} diff --git a/runtime/vm/protos/perfetto/trace/track_event/process_descriptor.pbzero.h b/runtime/vm/protos/perfetto/trace/track_event/process_descriptor.pbzero.h new file mode 100644 index 00000000000..238cc351f3d --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/track_event/process_descriptor.pbzero.h @@ -0,0 +1,115 @@ +// 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. + +// IMPORTANT: This file should only ever be modified by modifying the +// corresponding .proto file and then running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the SDK root +// directory. +// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT. + +#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_TRACK_EVENT_PROCESS_DESCRIPTOR_PROTO_H_ +#define PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_TRACK_EVENT_PROCESS_DESCRIPTOR_PROTO_H_ + +#include +#include + +#include "perfetto/protozero/field_writer.h" +#include "perfetto/protozero/message.h" +#include "perfetto/protozero/packed_repeated_fields.h" +#include "perfetto/protozero/proto_decoder.h" +#include "perfetto/protozero/proto_utils.h" + +namespace perfetto { +namespace protos { +namespace pbzero { + +class ProcessDescriptor_Decoder : public ::protozero::TypedProtoDecoder< + /*MAX_FIELD_ID=*/6, + /*HAS_NONPACKED_REPEATED_FIELDS=*/false> { + public: + ProcessDescriptor_Decoder(const uint8_t* data, size_t len) + : TypedProtoDecoder(data, len) {} + explicit ProcessDescriptor_Decoder(const std::string& raw) + : TypedProtoDecoder(reinterpret_cast(raw.data()), + raw.size()) {} + explicit ProcessDescriptor_Decoder(const ::protozero::ConstBytes& raw) + : TypedProtoDecoder(raw.data, raw.size) {} + bool has_pid() const { return at<1>().valid(); } + int32_t pid() const { return at<1>().as_int32(); } + bool has_process_name() const { return at<6>().valid(); } + ::protozero::ConstChars process_name() const { return at<6>().as_string(); } +}; + +class ProcessDescriptor : public ::protozero::Message { + public: + using Decoder = ProcessDescriptor_Decoder; + enum : int32_t { + kPidFieldNumber = 1, + kProcessNameFieldNumber = 6, + }; + static constexpr const char* GetName() { + return ".perfetto.protos.ProcessDescriptor"; + } + + using FieldMetadata_Pid = ::protozero::proto_utils::FieldMetadata< + 1, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kInt32, + int32_t, + ProcessDescriptor>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Pid kPid() { return {}; } + void set_pid(int32_t value) { + static constexpr uint32_t field_id = FieldMetadata_Pid::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kInt32>::Append(*this, + field_id, + value); + } + + using FieldMetadata_ProcessName = ::protozero::proto_utils::FieldMetadata< + 6, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kString, + std::string, + ProcessDescriptor>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_ProcessName kProcessName() { return {}; } + void set_process_name(const char* data, size_t size) { + AppendBytes(FieldMetadata_ProcessName::kFieldId, data, size); + } + void set_process_name(::protozero::ConstChars chars) { + AppendBytes(FieldMetadata_ProcessName::kFieldId, chars.data, chars.size); + } + void set_process_name(std::string value) { + static constexpr uint32_t field_id = FieldMetadata_ProcessName::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); + } +}; + +} // namespace pbzero +} // namespace protos +} // namespace perfetto +#endif // Include guard. diff --git a/runtime/vm/protos/perfetto/trace/track_event/process_descriptor.proto b/runtime/vm/protos/perfetto/trace/track_event/process_descriptor.proto new file mode 100644 index 00000000000..f9c34c21056 --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/track_event/process_descriptor.proto @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTE: This is a manually minified version of Perfetto's +// process_descriptor.proto. + +// IMPORTANT: The coresponding .pbzero.h file must be regenerated after +// any change is made to this file. This can be done by running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the +// SDK root directory. + +syntax = "proto2"; + +package perfetto.protos; + +// Describes a process's attributes. Emitted as part of a TrackDescriptor, +// usually by the process's main thread. +// +// Next id: 9. +message ProcessDescriptor { + optional int32 pid = 1; + optional string process_name = 6; +} diff --git a/runtime/vm/protos/perfetto/trace/track_event/thread_descriptor.pbzero.h b/runtime/vm/protos/perfetto/trace/track_event/thread_descriptor.pbzero.h new file mode 100644 index 00000000000..eee49273c64 --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/track_event/thread_descriptor.pbzero.h @@ -0,0 +1,143 @@ +// 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. + +// IMPORTANT: This file should only ever be modified by modifying the +// corresponding .proto file and then running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the SDK root +// directory. +// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT. + +#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_TRACK_EVENT_THREAD_DESCRIPTOR_PROTO_H_ +#define PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_TRACK_EVENT_THREAD_DESCRIPTOR_PROTO_H_ + +#include +#include + +#include "perfetto/protozero/field_writer.h" +#include "perfetto/protozero/message.h" +#include "perfetto/protozero/packed_repeated_fields.h" +#include "perfetto/protozero/proto_decoder.h" +#include "perfetto/protozero/proto_utils.h" + +namespace perfetto { +namespace protos { +namespace pbzero { + +class ThreadDescriptor_Decoder : public ::protozero::TypedProtoDecoder< + /*MAX_FIELD_ID=*/5, + /*HAS_NONPACKED_REPEATED_FIELDS=*/false> { + public: + ThreadDescriptor_Decoder(const uint8_t* data, size_t len) + : TypedProtoDecoder(data, len) {} + explicit ThreadDescriptor_Decoder(const std::string& raw) + : TypedProtoDecoder(reinterpret_cast(raw.data()), + raw.size()) {} + explicit ThreadDescriptor_Decoder(const ::protozero::ConstBytes& raw) + : TypedProtoDecoder(raw.data, raw.size) {} + bool has_pid() const { return at<1>().valid(); } + int32_t pid() const { return at<1>().as_int32(); } + bool has_tid() const { return at<2>().valid(); } + int32_t tid() const { return at<2>().as_int32(); } + bool has_thread_name() const { return at<5>().valid(); } + ::protozero::ConstChars thread_name() const { return at<5>().as_string(); } +}; + +class ThreadDescriptor : public ::protozero::Message { + public: + using Decoder = ThreadDescriptor_Decoder; + enum : int32_t { + kPidFieldNumber = 1, + kTidFieldNumber = 2, + kThreadNameFieldNumber = 5, + }; + static constexpr const char* GetName() { + return ".perfetto.protos.ThreadDescriptor"; + } + + using FieldMetadata_Pid = ::protozero::proto_utils::FieldMetadata< + 1, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kInt32, + int32_t, + ThreadDescriptor>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Pid kPid() { return {}; } + void set_pid(int32_t value) { + static constexpr uint32_t field_id = FieldMetadata_Pid::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kInt32>::Append(*this, + field_id, + value); + } + + using FieldMetadata_Tid = ::protozero::proto_utils::FieldMetadata< + 2, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kInt32, + int32_t, + ThreadDescriptor>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Tid kTid() { return {}; } + void set_tid(int32_t value) { + static constexpr uint32_t field_id = FieldMetadata_Tid::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kInt32>::Append(*this, + field_id, + value); + } + + using FieldMetadata_ThreadName = ::protozero::proto_utils::FieldMetadata< + 5, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kString, + std::string, + ThreadDescriptor>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_ThreadName kThreadName() { return {}; } + void set_thread_name(const char* data, size_t size) { + AppendBytes(FieldMetadata_ThreadName::kFieldId, data, size); + } + void set_thread_name(::protozero::ConstChars chars) { + AppendBytes(FieldMetadata_ThreadName::kFieldId, chars.data, chars.size); + } + void set_thread_name(std::string value) { + static constexpr uint32_t field_id = FieldMetadata_ThreadName::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); + } +}; + +} // namespace pbzero +} // namespace protos +} // namespace perfetto +#endif // Include guard. diff --git a/runtime/vm/protos/perfetto/trace/track_event/thread_descriptor.proto b/runtime/vm/protos/perfetto/trace/track_event/thread_descriptor.proto new file mode 100644 index 00000000000..e8a0eb874d3 --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/track_event/thread_descriptor.proto @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTE: This is a manually minified version of Perfetto's +// thread_descriptor.proto. + +// IMPORTANT: The coresponding .pbzero.h file must be regenerated after +// any change is made to this file. This can be done by running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the +// SDK root directory. + +syntax = "proto2"; + +package perfetto.protos; + +// Describes a thread's attributes. Emitted as part of a TrackDescriptor, +// usually by the thread's trace writer. +// +// Next id: 9. +message ThreadDescriptor { + optional int32 pid = 1; + optional int32 tid = 2; + + optional string thread_name = 5; +} diff --git a/runtime/vm/protos/perfetto/trace/track_event/track_descriptor.pbzero.h b/runtime/vm/protos/perfetto/trace/track_event/track_descriptor.pbzero.h new file mode 100644 index 00000000000..7d9c8eb492f --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/track_event/track_descriptor.pbzero.h @@ -0,0 +1,192 @@ +// 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. + +// IMPORTANT: This file should only ever be modified by modifying the +// corresponding .proto file and then running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the SDK root +// directory. +// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT. + +#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_TRACK_EVENT_TRACK_DESCRIPTOR_PROTO_H_ +#define PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_TRACK_EVENT_TRACK_DESCRIPTOR_PROTO_H_ + +#include +#include + +#include "perfetto/protozero/field_writer.h" +#include "perfetto/protozero/message.h" +#include "perfetto/protozero/packed_repeated_fields.h" +#include "perfetto/protozero/proto_decoder.h" +#include "perfetto/protozero/proto_utils.h" + +namespace perfetto { +namespace protos { +namespace pbzero { + +class ProcessDescriptor; +class ThreadDescriptor; + +class TrackDescriptor_Decoder : public ::protozero::TypedProtoDecoder< + /*MAX_FIELD_ID=*/5, + /*HAS_NONPACKED_REPEATED_FIELDS=*/false> { + public: + TrackDescriptor_Decoder(const uint8_t* data, size_t len) + : TypedProtoDecoder(data, len) {} + explicit TrackDescriptor_Decoder(const std::string& raw) + : TypedProtoDecoder(reinterpret_cast(raw.data()), + raw.size()) {} + explicit TrackDescriptor_Decoder(const ::protozero::ConstBytes& raw) + : TypedProtoDecoder(raw.data, raw.size) {} + bool has_uuid() const { return at<1>().valid(); } + uint64_t uuid() const { return at<1>().as_uint64(); } + bool has_parent_uuid() const { return at<5>().valid(); } + uint64_t parent_uuid() const { return at<5>().as_uint64(); } + bool has_name() const { return at<2>().valid(); } + ::protozero::ConstChars name() const { return at<2>().as_string(); } + bool has_process() const { return at<3>().valid(); } + ::protozero::ConstBytes process() const { return at<3>().as_bytes(); } + bool has_thread() const { return at<4>().valid(); } + ::protozero::ConstBytes thread() const { return at<4>().as_bytes(); } +}; + +class TrackDescriptor : public ::protozero::Message { + public: + using Decoder = TrackDescriptor_Decoder; + enum : int32_t { + kUuidFieldNumber = 1, + kParentUuidFieldNumber = 5, + kNameFieldNumber = 2, + kProcessFieldNumber = 3, + kThreadFieldNumber = 4, + }; + static constexpr const char* GetName() { + return ".perfetto.protos.TrackDescriptor"; + } + + using FieldMetadata_Uuid = ::protozero::proto_utils::FieldMetadata< + 1, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint64, + uint64_t, + TrackDescriptor>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Uuid kUuid() { return {}; } + void set_uuid(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_Uuid::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_ParentUuid = ::protozero::proto_utils::FieldMetadata< + 5, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint64, + uint64_t, + TrackDescriptor>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_ParentUuid kParentUuid() { return {}; } + void set_parent_uuid(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_ParentUuid::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, + TrackDescriptor>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Name kName() { return {}; } + 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); + } + + using FieldMetadata_Process = ::protozero::proto_utils::FieldMetadata< + 3, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kMessage, + ProcessDescriptor, + TrackDescriptor>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Process kProcess() { return {}; } + template + T* set_process() { + return BeginNestedMessage(3); + } + + using FieldMetadata_Thread = ::protozero::proto_utils::FieldMetadata< + 4, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kMessage, + ThreadDescriptor, + TrackDescriptor>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Thread kThread() { return {}; } + template + T* set_thread() { + return BeginNestedMessage(4); + } +}; + +} // namespace pbzero +} // namespace protos +} // namespace perfetto +#endif // Include guard. diff --git a/runtime/vm/protos/perfetto/trace/track_event/track_descriptor.proto b/runtime/vm/protos/perfetto/trace/track_event/track_descriptor.proto new file mode 100644 index 00000000000..fb099f9492e --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/track_event/track_descriptor.proto @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTE: This is a manually minified version of Perfetto's +// track_descriptor.proto. + +// IMPORTANT: The coresponding .pbzero.h file must be regenerated after +// any change is made to this file. This can be done by running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the +// SDK root directory. + +syntax = "proto2"; + +import "protos/perfetto/trace/track_event/process_descriptor.proto"; +import "protos/perfetto/trace/track_event/thread_descriptor.proto"; + +package perfetto.protos; + +// Defines a track for TrackEvents. Slices and instant events on the same track +// will be nested based on their timestamps, see TrackEvent::Type. +// +// A TrackDescriptor only needs to be emitted by one trace writer / producer and +// is valid for the entirety of the trace. To ensure the descriptor isn't lost +// when the ring buffer wraps, it should be reemitted whenever incremental state +// is cleared. +// +// As a fallback, TrackEvents emitted without an explicit track association will +// be associated with an implicit trace-global track (uuid = 0), see also +// |TrackEvent::track_uuid|. It is possible but not necessary to emit a +// TrackDescriptor for this implicit track. +// +// Next id: 9. +message TrackDescriptor { + // Unique ID that identifies this track. This ID is global to the whole trace. + // Producers should ensure that it is unlikely to clash with IDs emitted by + // other producers. A value of 0 denotes the implicit trace-global track. + // + // For example, legacy TRACE_EVENT macros may use a hash involving the async + // event id + id_scope, pid, and/or tid to compute this ID. + optional uint64 uuid = 1; + + // A parent track reference can be used to describe relationships between + // tracks. For example, to define an asynchronous track which is scoped to a + // specific process, specify the uuid for that process's process track here. + // Similarly, to associate a COUNTER_THREAD_TIME_NS counter track with a + // thread, specify the uuid for that thread's thread track here. + optional uint64 parent_uuid = 5; + + // Name of the track. Optional - if unspecified, it may be derived from the + // process/thread name (process/thread tracks), the first event's name (async + // tracks), or counter name (counter tracks). + optional string name = 2; + + // Associate the track with a process, making it the process-global track. + // There should only be one such track per process (usually for instant + // events; trace processor uses this fact to detect pid reuse). If you need + // more (e.g. for asynchronous events), create child tracks using parent_uuid. + // + // Trace processor will merge events on a process track with slice-type events + // from other sources (e.g. ftrace) for the same process into a single + // timeline view. + optional ProcessDescriptor process = 3; + + // Associate the track with a thread, indicating that the track's events + // describe synchronous code execution on the thread. There should only be one + // such track per thread (trace processor uses this fact to detect tid reuse). + // + // Trace processor will merge events on a thread track with slice-type events + // from other sources (e.g. ftrace) for the same thread into a single timeline + // view. + optional ThreadDescriptor thread = 4; +} 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 new file mode 100644 index 00000000000..ec04d0af6f3 --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/track_event/track_event.pbzero.h @@ -0,0 +1,316 @@ +// 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. + +// IMPORTANT: This file should only ever be modified by modifying the +// corresponding .proto file and then running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the SDK root +// directory. +// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT. + +#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_TRACK_EVENT_TRACK_EVENT_PROTO_H_ +#define PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_TRACK_EVENT_TRACK_EVENT_PROTO_H_ + +#include +#include + +#include "perfetto/protozero/field_writer.h" +#include "perfetto/protozero/message.h" +#include "perfetto/protozero/packed_repeated_fields.h" +#include "perfetto/protozero/proto_decoder.h" +#include "perfetto/protozero/proto_utils.h" + +namespace perfetto { +namespace protos { +namespace pbzero { + +class DebugAnnotation; +namespace perfetto_pbzero_enum_TrackEvent { +enum Type : int32_t; +} // namespace perfetto_pbzero_enum_TrackEvent +using TrackEvent_Type = perfetto_pbzero_enum_TrackEvent::Type; + +namespace perfetto_pbzero_enum_TrackEvent { +enum Type : int32_t { + TYPE_SLICE_BEGIN = 1, + TYPE_SLICE_END = 2, + TYPE_INSTANT = 3, +}; +} // namespace perfetto_pbzero_enum_TrackEvent +using TrackEvent_Type = perfetto_pbzero_enum_TrackEvent::Type; + +constexpr TrackEvent_Type TrackEvent_Type_MIN = + TrackEvent_Type::TYPE_SLICE_BEGIN; +constexpr TrackEvent_Type TrackEvent_Type_MAX = TrackEvent_Type::TYPE_INSTANT; + +PERFETTO_PROTOZERO_CONSTEXPR14_OR_INLINE +const char* TrackEvent_Type_Name( + ::perfetto::protos::pbzero::TrackEvent_Type value) { + switch (value) { + case ::perfetto::protos::pbzero::TrackEvent_Type::TYPE_SLICE_BEGIN: + return "TYPE_SLICE_BEGIN"; + + case ::perfetto::protos::pbzero::TrackEvent_Type::TYPE_SLICE_END: + return "TYPE_SLICE_END"; + + case ::perfetto::protos::pbzero::TrackEvent_Type::TYPE_INSTANT: + return "TYPE_INSTANT"; + } + return "PBZERO_UNKNOWN_ENUM_VALUE"; +} + +class TrackEvent_Decoder : public ::protozero::TypedProtoDecoder< + /*MAX_FIELD_ID=*/48, + /*HAS_NONPACKED_REPEATED_FIELDS=*/true> { + public: + TrackEvent_Decoder(const uint8_t* data, size_t len) + : TypedProtoDecoder(data, len) {} + explicit TrackEvent_Decoder(const std::string& raw) + : TypedProtoDecoder(reinterpret_cast(raw.data()), + raw.size()) {} + explicit TrackEvent_Decoder(const ::protozero::ConstBytes& raw) + : TypedProtoDecoder(raw.data, raw.size) {} + bool has_categories() const { return at<22>().valid(); } + ::protozero::RepeatedFieldIterator<::protozero::ConstChars> categories() + const { + return GetRepeated<::protozero::ConstChars>(22); + } + 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(); } + int32_t type() const { return at<9>().as_int32(); } + bool has_track_uuid() const { return at<11>().valid(); } + uint64_t track_uuid() const { return at<11>().as_uint64(); } + bool has_flow_ids() const { return at<47>().valid(); } + ::protozero::RepeatedFieldIterator flow_ids() const { + return GetRepeated(47); + } + bool has_terminating_flow_ids() const { return at<48>().valid(); } + ::protozero::RepeatedFieldIterator terminating_flow_ids() const { + return GetRepeated(48); + } + bool has_debug_annotations() const { return at<4>().valid(); } + ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> + debug_annotations() const { + return GetRepeated<::protozero::ConstBytes>(4); + } +}; + +class TrackEvent : public ::protozero::Message { + public: + using Decoder = TrackEvent_Decoder; + enum : int32_t { + kCategoriesFieldNumber = 22, + kNameFieldNumber = 23, + kTypeFieldNumber = 9, + kTrackUuidFieldNumber = 11, + kFlowIdsFieldNumber = 47, + kTerminatingFlowIdsFieldNumber = 48, + kDebugAnnotationsFieldNumber = 4, + }; + static constexpr const char* GetName() { + return ".perfetto.protos.TrackEvent"; + } + + using Type = ::perfetto::protos::pbzero::TrackEvent_Type; + static inline const char* Type_Name(Type value) { + return ::perfetto::protos::pbzero::TrackEvent_Type_Name(value); + } + static const Type TYPE_SLICE_BEGIN = Type::TYPE_SLICE_BEGIN; + static const Type TYPE_SLICE_END = Type::TYPE_SLICE_END; + static const Type TYPE_INSTANT = Type::TYPE_INSTANT; + + using FieldMetadata_Categories = ::protozero::proto_utils::FieldMetadata< + 22, + ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked, + ::protozero::proto_utils::ProtoSchemaType::kString, + std::string, + TrackEvent>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Categories kCategories() { return {}; } + void add_categories(const char* data, size_t size) { + AppendBytes(FieldMetadata_Categories::kFieldId, data, size); + } + void add_categories(::protozero::ConstChars chars) { + AppendBytes(FieldMetadata_Categories::kFieldId, chars.data, chars.size); + } + void add_categories(std::string value) { + static constexpr uint32_t field_id = FieldMetadata_Categories::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_Name = ::protozero::proto_utils::FieldMetadata< + 23, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kString, + std::string, + TrackEvent>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Name kName() { return {}; } + 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); + } + + using FieldMetadata_Type = ::protozero::proto_utils::FieldMetadata< + 9, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kEnum, + ::perfetto::protos::pbzero::TrackEvent_Type, + TrackEvent>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_Type kType() { return {}; } + void set_type(::perfetto::protos::pbzero::TrackEvent_Type value) { + static constexpr uint32_t field_id = FieldMetadata_Type::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kEnum>::Append(*this, + field_id, + value); + } + + using FieldMetadata_TrackUuid = ::protozero::proto_utils::FieldMetadata< + 11, + ::protozero::proto_utils::RepetitionType::kNotRepeated, + ::protozero::proto_utils::ProtoSchemaType::kUint64, + uint64_t, + TrackEvent>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_TrackUuid kTrackUuid() { return {}; } + void set_track_uuid(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_TrackUuid::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_FlowIds = ::protozero::proto_utils::FieldMetadata< + 47, + ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked, + ::protozero::proto_utils::ProtoSchemaType::kFixed64, + uint64_t, + TrackEvent>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_FlowIds kFlowIds() { return {}; } + void add_flow_ids(uint64_t value) { + static constexpr uint32_t field_id = FieldMetadata_FlowIds::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kFixed64>::Append(*this, + field_id, + value); + } + + using FieldMetadata_TerminatingFlowIds = + ::protozero::proto_utils::FieldMetadata< + 48, + ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked, + ::protozero::proto_utils::ProtoSchemaType::kFixed64, + uint64_t, + TrackEvent>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_TerminatingFlowIds kTerminatingFlowIds() { + return {}; + } + void add_terminating_flow_ids(uint64_t value) { + static constexpr uint32_t field_id = + FieldMetadata_TerminatingFlowIds::kFieldId; + // Call the appropriate protozero::Message::Append(field_id, ...) + // method based on the type of the field. + ::protozero::internal::FieldWriter< + ::protozero::proto_utils::ProtoSchemaType::kFixed64>::Append(*this, + field_id, + value); + } + + using FieldMetadata_DebugAnnotations = + ::protozero::proto_utils::FieldMetadata< + 4, + ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked, + ::protozero::proto_utils::ProtoSchemaType::kMessage, + DebugAnnotation, + TrackEvent>; + + // Ceci n'est pas une pipe. + // This is actually a variable of FieldMetadataHelper> + // type (and users are expected to use it as such, hence kCamelCase name). + // It is declared as a function to keep protozero bindings header-only as + // inline constexpr variables are not available until C++17 (while inline + // functions are). + // TODO(altimin): Use inline variable instead after adopting C++17. + static constexpr FieldMetadata_DebugAnnotations kDebugAnnotations() { + return {}; + } + template + T* add_debug_annotations() { + return BeginNestedMessage(4); + } +}; + +} // namespace pbzero +} // namespace protos +} // namespace perfetto +#endif // Include guard. diff --git a/runtime/vm/protos/perfetto/trace/track_event/track_event.proto b/runtime/vm/protos/perfetto/trace/track_event/track_event.proto new file mode 100644 index 00000000000..003e8f8a37c --- /dev/null +++ b/runtime/vm/protos/perfetto/trace/track_event/track_event.proto @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTE: This is a manually minified version of Perfetto's +// track_event.proto. + +// IMPORTANT: The coresponding .pbzero.h file must be regenerated after +// any change is made to this file. This can be done by running +// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the +// SDK root directory. + +syntax = "proto2"; + +import "protos/perfetto/trace/track_event/debug_annotation.proto"; + +package perfetto.protos; + +// NOTE: Full TrackEvent support in the client lib and chrome is WIP, thus these +// protos are still subject to change. Don't depend on them staying as they are. + +// Trace events emitted by client instrumentation library (TRACE_EVENT macros), +// which describe activity on a track, such as a thread or asynchronous event +// track. The track is specified using separate TrackDescriptor messages and +// referred to via the track's UUID. +// +// A simple TrackEvent packet specifies a timestamp, category, name and type: +// ```protobuf +// trace_packet { +// timestamp: 1000 +// track_event { +// categories: ["my_cat"] +// name: "my_event" +// type: TYPE_INSTANT +// } +// } +// ``` +// +// To associate an event with a custom track (e.g. a thread), the track is +// defined in a separate packet and referred to from the TrackEvent by its UUID: +// ```protobuf +// trace_packet { +// track_descriptor { +// track_uuid: 1234 +// name: "my_track" +// +// // Optionally, associate the track with a thread. +// thread_descriptor { +// pid: 10 +// tid: 10 +// .. +// } +// } +// } +// ``` +// +// A pair of TYPE_SLICE_BEGIN and _END events form a slice on the track: +// +// ```protobuf +// trace_packet { +// timestamp: 1200 +// track_event { +// track_uuid: 1234 +// categories: ["my_cat"] +// name: "my_slice" +// type: TYPE_SLICE_BEGIN +// } +// } +// trace_packet { +// timestamp: 1400 +// track_event { +// track_uuid: 1234 +// type: TYPE_SLICE_END +// } +// } +// ``` +// TrackEvents also support optimizations to reduce data repetition and encoded +// data size, e.g. through data interning (names, categories, ...) and delta +// encoding of timestamps/counters. For details, see the InternedData message. +// Further, default values for attributes of events on the same sequence (e.g. +// their default track association) can be emitted as part of a +// TrackEventDefaults message. +// +// Next reserved id: 13 (up to 15). Next id: 50. +message TrackEvent { + repeated string categories = 22; + + // Optional name of the event for its display in trace viewer. May be left + // unspecified for events with typed arguments. + // + // Note that metrics should not rely on event names, as they are prone to + // changing. Instead, they should use typed arguments to identify the events + // they are interested in. + oneof name_field { + // non-interned variant. + string name = 23; + } + + // TODO(eseckler): Support using binary symbols for category/event names. + + // Type of the TrackEvent (required if |phase| in LegacyEvent is not set). + enum Type { + // Slice events are events that have a begin and end timestamp, i.e. a + // duration. They can be nested similar to a callstack: If, on the same + // track, event B begins after event A, but before A ends, B is a child + // event of A and will be drawn as a nested event underneath A in the UI. + // Note that child events should always end before their parents (e.g. B + // before A). + // + // Each slice event is formed by a pair of BEGIN + END events. The END event + // does not need to repeat any TrackEvent fields it has in common with its + // corresponding BEGIN event. Arguments and debug annotations of the BEGIN + + // END pair will be merged during trace import. + // + // Note that we deliberately chose not to support COMPLETE events (which + // would specify a duration directly) since clients would need to delay + // writing them until the slice is completed, which can result in reordered + // events in the trace and loss of unfinished events at the end of a trace. + TYPE_SLICE_BEGIN = 1; + TYPE_SLICE_END = 2; + + // Instant events are nestable events without duration. They can be children + // of slice events on the same track. + TYPE_INSTANT = 3; + } + optional Type type = 9; + + // 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. + optional uint64 track_uuid = 11; + + // IDs of flows originating, passing through, or ending at this event. + // Flow IDs are global within a trace. + // + // A flow connects a sequence of TrackEvents within or across tracks, e.g. + // an input event may be handled on one thread but cause another event on + // a different thread - a flow between the two events can associate them. + // + // The direction of the flows between events is inferred from the events' + // timestamps. The earliest event with the same flow ID becomes the source + // of the flow. Any events thereafter are intermediate steps of the flow, + // until the flow terminates at the last event with the flow ID. + // + // Flows can also be explicitly terminated (see |terminating_flow_ids|), so + // that the same ID can later be reused for another flow. + repeated fixed64 flow_ids = 47; + + // 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. + repeated fixed64 terminating_flow_ids = 48; + + // --------------------------------------------------------------------------- + // TrackEvent arguments: + // --------------------------------------------------------------------------- + + // Unstable key/value annotations shown in the trace viewer but not intended + // for metrics use. + repeated DebugAnnotation debug_annotations = 4; +} diff --git a/runtime/vm/protos/tools/compile_perfetto_protos.dart b/runtime/vm/protos/tools/compile_perfetto_protos.dart new file mode 100644 index 00000000000..4d45eca7219 --- /dev/null +++ b/runtime/vm/protos/tools/compile_perfetto_protos.dart @@ -0,0 +1,77 @@ +// 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. + +import 'dart:async'; +import 'dart:io'; + +Future compilePerfettoProtos() async { + final processResult = await Process.run( + './tools/build.py', + ['-mdebug', '-ax64', '--no-goma', 'runtime/vm:perfetto_protos'], + ); + + final int exitCode = processResult.exitCode; + final String stdout = processResult.stdout.trim(); + final String stderr = processResult.stderr.trim(); + if (exitCode != 0) { + print('exit-code: $exitCode'); + print('stdout:'); + print('${stdout}'); + print('stderr:'); + print('${stderr}'); + } +} + +Future copyPerfettoProtoHeaders() async { + final copySource = Directory('./out/DebugX64/gen/runtime/vm/protos').path; + final copyDestination = Directory('./runtime/vm').path; + + late final executable; + late final args; + if (Platform.operatingSystem == 'windows') { + executable = 'xcopy'; + args = [copySource, copyDestination, '/e', '/i']; + } else { + executable = 'cp'; + args = ['-R', copySource, copyDestination]; + } + final processResult = await Process.run(executable, args); + + final int exitCode = processResult.exitCode; + final String stdout = processResult.stdout.trim(); + final String stderr = processResult.stderr.trim(); + if (exitCode != 0) { + print('exit-code: $exitCode'); + print('stdout:'); + print('${stdout}'); + print('stderr:'); + print('${stderr}'); + } + + for (final file + in Directory('./runtime/vm/protos').listSync(recursive: true)) { + if (!(file is File) || !file.path.endsWith('.pbzero.h')) { + continue; + } + final contentsIncludingPrependedNotices = + '// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file\n' + + '// for details. All rights reserved. Use of this source code is governed by a\n' + + '// BSD-style license that can be found in the LICENSE file.\n\n' + + '// IMPORTANT: This file should only ever be modified by modifying the\n' + + '// corresponding .proto file and then running\n' + + '// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the SDK root\n' + + '// directory.\n' + + file.readAsStringSync(); + file.writeAsStringSync(contentsIncludingPrependedNotices, flush: true); + } +} + +main(List files) async { + if (!Directory('./runtime/vm').existsSync()) { + print('Error: this tool must be run from the root directory of the SDK.'); + return; + } + await compilePerfettoProtos(); + await copyPerfettoProtoHeaders(); +} diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc index 9cd59129d66..300b3465b6d 100644 --- a/runtime/vm/timeline.cc +++ b/runtime/vm/timeline.cc @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include "platform/atomic.h" @@ -24,6 +26,18 @@ #include "vm/service_event.h" #include "vm/thread.h" +#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT) +#include "perfetto/ext/tracing/core/trace_packet.h" +#include "vm/protos/perfetto/common/builtin_clock.pbzero.h" +#include "vm/protos/perfetto/trace/clock_snapshot.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" +#include "vm/protos/perfetto/trace/track_event/thread_descriptor.pbzero.h" +#include "vm/protos/perfetto/trace/track_event/track_descriptor.pbzero.h" +#include "vm/protos/perfetto/trace/track_event/track_event.pbzero.h" +#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT) + namespace dart { #if defined(PRODUCT) @@ -31,9 +45,14 @@ namespace dart { #define SUPPORTED_TIMELINE_RECORDERS "systrace, file, callback" #else #define DEFAULT_TIMELINE_RECORDER "ring" +#if defined(SUPPORT_PERFETTO) +#define SUPPORTED_TIMELINE_RECORDERS \ + "ring, endless, startup, systrace, file, callback, perfetto" +#else #define SUPPORTED_TIMELINE_RECORDERS \ "ring, endless, startup, systrace, file, callback" #endif +#endif DEFINE_FLAG(bool, complete_timeline, false, "Record the complete timeline"); DEFINE_FLAG(bool, startup_timeline, false, "Record the startup timeline"); @@ -43,12 +62,12 @@ DEFINE_FLAG( false, "Record the timeline to the platform's tracing service if there is one"); DEFINE_FLAG(bool, trace_timeline, false, "Trace timeline backend"); -DEFINE_FLAG( - charp, - timeline_dir, - nullptr, - "Enable all timeline trace streams and output VM global trace " - "into specified directory. This flag is ignored by the file recorder."); +DEFINE_FLAG(charp, + timeline_dir, + nullptr, + "Enable all timeline trace streams and output VM global trace " + "into specified directory. This flag is ignored by the file and " + "perfetto recorders."); DEFINE_FLAG(charp, timeline_streams, nullptr, @@ -153,6 +172,7 @@ static TimelineEventRecorder* CreateTimelineRecorder() { if (Utils::StrStartsWith(flag, "file") && (flag[4] == '\0' || flag[4] == ':' || flag[4] == '=')) { const char* filename = flag[4] == '\0' ? "dart-timeline.json" : &flag[5]; + free(const_cast(FLAG_timeline_dir)); FLAG_timeline_dir = nullptr; return new TimelineEventFileRecorder(filename); } @@ -162,6 +182,25 @@ static TimelineEventRecorder* CreateTimelineRecorder() { } #if !defined(PRODUCT) +#if defined(SUPPORT_PERFETTO) + // The Perfetto file recorder is disabled in PRODUCT mode to avoid the large + // binary size increase that it brings. + { + const intptr_t kLengthOfWordPerfetto = 8; + if (Utils::StrStartsWith(flag, "perfetto") && + (flag[kLengthOfWordPerfetto] == '\0' || + flag[kLengthOfWordPerfetto] == ':' || + flag[kLengthOfWordPerfetto] == '=')) { + const char* filename = flag[kLengthOfWordPerfetto] == '\0' + ? "dart.perfetto-trace" + : &flag[kLengthOfWordPerfetto + 1]; + free(const_cast(FLAG_timeline_dir)); + FLAG_timeline_dir = nullptr; + return new TimelineEventPerfettoFileRecorder(filename); + } + } +#endif // defined(SUPPORT_PERFETTO) + // Recorders below do nothing useful in PRODUCT mode. You can't extract // information available in them without vm-service. if (strcmp("endless", flag) == 0) { @@ -175,7 +214,7 @@ static TimelineEventRecorder* CreateTimelineRecorder() { if (strcmp("ring", flag) == 0) { return new TimelineEventRingRecorder(); } -#endif +#endif // !defined(PRODUCT) if (strlen(flag) > 0 && strcmp(flag, DEFAULT_TIMELINE_RECORDER) != 0) { OS::PrintErr( @@ -792,7 +831,7 @@ bool TimelineEvent::HasIsolateGroupId() const { return isolate_group_id_ != ILLEGAL_ISOLATE_GROUP_ID; } -const char* TimelineEvent::GetFormattedIsolateId() const { +char* TimelineEvent::GetFormattedIsolateId() const { ASSERT(HasIsolateId()); intptr_t formatted_isolate_id_len = Utils::SNPrint(nullptr, 0, ISOLATE_SERVICE_ID_FORMAT_STRING, isolate_id_); @@ -803,7 +842,7 @@ const char* TimelineEvent::GetFormattedIsolateId() const { return formatted_isolate_id; } -const char* TimelineEvent::GetFormattedIsolateGroupId() const { +char* TimelineEvent::GetFormattedIsolateGroupId() const { ASSERT(HasIsolateGroupId()); intptr_t formatted_isolate_group_id_len = Utils::SNPrint( nullptr, 0, ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING, isolate_group_id_); @@ -838,6 +877,32 @@ void TimelineTrackMetadata::PrintJSON(const JSONArray& jsarr_events) const { jsobj_args.AddProperty("mode", "basic"); } } + +#if defined(SUPPORT_PERFETTO) +inline void SetTrustedPacketSequenceId( + perfetto::protos::pbzero::TracePacket* packet) { + // trusted_packet_sequence_id uniquely identifies a trace producer + writer + // pair. We set the trusted_packet_sequence_id of all packets written by the + // Perfetto file recorder to the arbitrary value of 1. + packet->set_trusted_packet_sequence_id(1); +} + +void TimelineTrackMetadata::PopulateTracePacket( + perfetto::protos::pbzero::TracePacket* track_descriptor_packet) { + SetTrustedPacketSequenceId(track_descriptor_packet); + + perfetto::protos::pbzero::TrackDescriptor& track_descriptor = + *track_descriptor_packet->set_track_descriptor(); + track_descriptor.set_parent_uuid(pid()); + track_descriptor.set_uuid(tid()); + + perfetto::protos::pbzero::ThreadDescriptor& thread_descriptor = + *track_descriptor.set_thread(); + thread_descriptor.set_pid(pid()); + thread_descriptor.set_tid(tid()); + thread_descriptor.set_thread_name(track_name()); +} +#endif // defined(SUPPORT_PERFETTO) #endif // !defined(PRODUCT) TimelineStream::TimelineStream(const char* name, @@ -1664,6 +1729,258 @@ void TimelineEventFileRecorder::DrainImpl(const TimelineEvent& event) { free(output); } +#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT) +TimelineEventPerfettoFileRecorder::TimelineEventPerfettoFileRecorder( + const char* path) + : TimelineEventFileRecorderBase(path), + async_track_uuid_to_track_descriptor_( + &SimpleHashMap::SamePointerValue, + TimelineEventPerfettoFileRecorder:: + kAsyncTrackUuidToTrackDescriptorInitialCapacity) { + SetTrustedPacketSequenceId(packet_.get()); + + perfetto::protos::pbzero::ClockSnapshot& clock_snapshot = + *packet_->set_clock_snapshot(); + clock_snapshot.set_primary_trace_clock( + perfetto::protos::pbzero::BuiltinClock::BUILTIN_CLOCK_MONOTONIC); + + perfetto::protos::pbzero::ClockSnapshot_Clock& clock = + *clock_snapshot.add_clocks(); + clock.set_clock_id( + perfetto::protos::pbzero::BuiltinClock::BUILTIN_CLOCK_MONOTONIC); + clock.set_timestamp(OS::GetCurrentMonotonicMicrosForTimeline() * 1000); + + WritePacket(&packet_); + packet_.Reset(); + + SetTrustedPacketSequenceId(packet_.get()); + + perfetto::protos::pbzero::TrackDescriptor& track_descriptor = + *packet_->set_track_descriptor(); + const int64_t pid = OS::ProcessId(); + track_descriptor.set_uuid(pid); + + perfetto::protos::pbzero::ProcessDescriptor& process_descriptor = + *track_descriptor.set_process(); + process_descriptor.set_pid(pid); + // TODO(derekx): Add the process name. + + WritePacket(&packet_); + packet_.Reset(); + + OSThread::Start("TimelineEventPerfettoFileRecorder", + TimelineEventFileRecorderBaseStart, + reinterpret_cast(this)); +} + +TimelineEventPerfettoFileRecorder::~TimelineEventPerfettoFileRecorder() { + ShutDown(); + for (SimpleHashMap::Entry* entry = track_uuid_to_track_metadata().Start(); + entry != nullptr; entry = track_uuid_to_track_metadata().Next(entry)) { + TimelineTrackMetadata* value = + static_cast(entry->value); + packet_.Reset(); + value->PopulateTracePacket(packet_.get()); + WritePacket(&packet_); + } + for (SimpleHashMap::Entry* entry = + async_track_uuid_to_track_descriptor_.Start(); + entry != nullptr; + entry = async_track_uuid_to_track_descriptor_.Next(entry)) { + auto* value = static_cast< + protozero::HeapBuffered*>( + entry->value); + WritePacket(value); + delete value; + } +} + +inline const std::tuple, intptr_t> GetProtoPreamble( + const intptr_t size) { + auto preamble = + std::make_unique(perfetto::TracePacket::kMaxPreambleBytes); + uint8_t* ptr = reinterpret_cast(&preamble[0]); + + const uint8_t tag = protozero::proto_utils::MakeTagLengthDelimited( + perfetto::TracePacket::kPacketFieldNumber); + static_assert(tag < 0x80, "TracePacket tag should fit in one byte"); + *(ptr++) = tag; + + ptr = protozero::proto_utils::WriteVarInt(size, ptr); + intptr_t preamble_size = reinterpret_cast(ptr) - + reinterpret_cast(&preamble[0]); + return std::make_tuple(std::move(preamble), preamble_size); +} + +void TimelineEventPerfettoFileRecorder::WritePacket( + protozero::HeapBuffered* packet) + const { + intptr_t size = 0; + for (const protozero::ScatteredHeapBuffer::Slice& slice : + packet->GetSlices()) { + size += slice.size() - slice.unused_bytes(); + } + const std::tuple, intptr_t>& response = + GetProtoPreamble(size); + Write(std::get<0>(response).get(), std::get<1>(response)); + for (const protozero::ScatteredHeapBuffer::Slice& slice : + packet->GetSlices()) { + Write(reinterpret_cast(slice.start()), + slice.size() - slice.unused_bytes()); + } +} + +inline void AddSyncEventFields( + perfetto::protos::pbzero::TrackEvent* track_event, + const TimelineEvent& event) { + track_event->set_track_uuid(OSThread::ThreadIdToIntPtr(event.thread())); +} + +inline void AddAsyncEventFields( + perfetto::protos::pbzero::TrackEvent* track_event, + const TimelineEvent& event) { + track_event->set_track_uuid(event.Id()); +} + +inline void AddBeginAndInstantEventCommonFields( + perfetto::protos::pbzero::TrackEvent* track_event, + const TimelineEvent& event) { + track_event->set_name(event.label()); +} + +inline void AddBeginEventFields( + perfetto::protos::pbzero::TrackEvent* track_event, + const TimelineEvent& event) { + AddBeginAndInstantEventCommonFields(track_event, event); + track_event->set_type( + perfetto::protos::pbzero::TrackEvent::Type::TYPE_SLICE_BEGIN); +} + +inline void AddInstantEventFields( + perfetto::protos::pbzero::TrackEvent* track_event, + const TimelineEvent& event) { + AddBeginAndInstantEventCommonFields(track_event, event); + track_event->set_type( + perfetto::protos::pbzero::TrackEvent::Type::TYPE_INSTANT); +} + +inline void AddEndEventFields( + perfetto::protos::pbzero::TrackEvent* track_event) { + track_event->set_type( + perfetto::protos::pbzero::TrackEvent::Type::TYPE_SLICE_END); +} + +inline void AddTrackDescriptorForAsyncTrack( + SimpleHashMap* async_track_uuid_to_track_descriptor, + const TimelineEvent& event) { + void* key = reinterpret_cast(event.Id()); + const intptr_t hash = Utils::WordHash(event.Id()); + SimpleHashMap::Entry* entry = + async_track_uuid_to_track_descriptor->Lookup(key, hash, true); + if (entry->value == nullptr) { + protozero::HeapBuffered& + track_descriptor_packet = *( + new protozero::HeapBuffered); + SetTrustedPacketSequenceId(track_descriptor_packet.get()); + + perfetto::protos::pbzero::TrackDescriptor& track_descriptor = + *track_descriptor_packet->set_track_descriptor(); + track_descriptor.set_parent_uuid(OS::ProcessId()); + track_descriptor.set_uuid(event.Id()); + + entry->value = &track_descriptor_packet; + } +} + +void TimelineEventPerfettoFileRecorder::DrainImpl(const TimelineEvent& event) { + SetTrustedPacketSequenceId(packet_.get()); + // TODO(derekx): We should be able to set the unit_multiplier_ns field in a + // ClockSnapshot to avoid manually converting from microseconds to + // nanoseconds, but I haven't been able to get it to work. + packet_->set_timestamp(event.TimeOrigin() * 1000); + packet_->set_timestamp_clock_id( + perfetto::protos::pbzero::BuiltinClock::BUILTIN_CLOCK_MONOTONIC); + + perfetto::protos::pbzero::TrackEvent* track_event = + packet_->set_track_event(); + track_event->add_categories(event.stream()->name()); + + switch (event.event_type()) { + case TimelineEvent::kBegin: { + AddSyncEventFields(track_event, event); + AddBeginEventFields(track_event, event); + break; + } + case TimelineEvent::kEnd: { + AddSyncEventFields(track_event, event); + AddEndEventFields(track_event); + break; + } + case TimelineEvent::kInstant: { + AddSyncEventFields(track_event, event); + AddInstantEventFields(track_event, event); + break; + } + case TimelineEvent::kAsyncBegin: { + AddTrackDescriptorForAsyncTrack(&async_track_uuid_to_track_descriptor_, + event); + AddAsyncEventFields(track_event, event); + AddBeginEventFields(track_event, event); + break; + } + case TimelineEvent::kAsyncEnd: { + AddAsyncEventFields(track_event, event); + AddEndEventFields(track_event); + break; + } + case TimelineEvent::kAsyncInstant: { + AddTrackDescriptorForAsyncTrack(&async_track_uuid_to_track_descriptor_, + event); + AddAsyncEventFields(track_event, event); + AddInstantEventFields(track_event, event); + break; + } + default: + break; + } + if (event.GetNumArguments() > 0) { + if (event.pre_serialized_args()) { + ASSERT(event.GetNumArguments() == 1); + perfetto::protos::pbzero::DebugAnnotation& debug_annotation = + *track_event->add_debug_annotations(); + debug_annotation.set_name(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); + } + } + } + if (event.HasIsolateId()) { + perfetto::protos::pbzero::DebugAnnotation& debug_annotation = + *track_event->add_debug_annotations(); + debug_annotation.set_name("isolateId"); + char* formatted_isolate_id = event.GetFormattedIsolateId(); + debug_annotation.set_string_value(formatted_isolate_id); + free(formatted_isolate_id); + } + if (event.HasIsolateGroupId()) { + perfetto::protos::pbzero::DebugAnnotation& debug_annotation = + *track_event->add_debug_annotations(); + debug_annotation.set_name("isolateGroupId"); + char* formatted_isolate_group = event.GetFormattedIsolateGroupId(); + debug_annotation.set_string_value(formatted_isolate_group); + free(formatted_isolate_group); + } + + WritePacket(&packet_); + packet_.Reset(); +} +#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT) + TimelineEventEndlessRecorder::TimelineEventEndlessRecorder() : head_(nullptr), tail_(nullptr), block_index_(0) {} diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h index cc75762d3a9..c3a9678b05d 100644 --- a/runtime/vm/timeline.h +++ b/runtime/vm/timeline.h @@ -16,6 +16,12 @@ #include "vm/os.h" #include "vm/os_thread.h" +#if defined(SUPPORT_TIMELINE) && defined(SUPPORT_PERFETTO) && !defined(PRODUCT) +#include "perfetto/protozero/scattered_heap_buffer.h" +#include "vm/protos/perfetto/trace/trace_packet.pbzero.h" +#endif // defined(SUPPORT_TIMELINE) && defined(SUPPORT_PERFETTO) && \ + // !defined(PRODUCT) + #if defined(FUCHSIA_SDK) || defined(DART_HOST_OS_FUCHSIA) #include #include @@ -34,6 +40,12 @@ namespace dart { +#if !defined(SUPPORT_TIMELINE) +#define TIMELINE_DURATION(thread, stream, name) +#define TIMELINE_FUNCTION_COMPILATION_DURATION(thread, name, function) +#define TIMELINE_FUNCTION_GC_DURATION(thread, name) +#endif // !defined(SUPPORT_TIMELINE) + class JSONArray; class JSONObject; class JSONStream; @@ -49,11 +61,13 @@ class TimelineStream; class VirtualMemory; class Zone; +#if defined(SUPPORT_TIMELINE) #define CALLBACK_RECORDER_NAME "Callback" #define ENDLESS_RECORDER_NAME "Endless" #define FILE_RECORDER_NAME "File" #define FUCHSIA_RECORDER_NAME "Fuchsia" #define MACOS_RECORDER_NAME "Macos" +#define PERFETTO_RECORDER_NAME "Perfetto" #define RING_RECORDER_NAME "Ring" #define STARTUP_RECORDER_NAME "Startup" #define SYSTRACE_RECORDER_NAME "Systrace" @@ -69,6 +83,7 @@ class Zone; V(GC, "dart:gc", true) \ V(Isolate, "dart:isolate", true) \ V(VM, "dart:vm", true) +#endif // defined(SUPPORT_TIMELINE) // A stream of timeline events. A stream has a name and can be enabled or // disabled (globally and per isolate). @@ -129,6 +144,7 @@ class TimelineStream { #endif }; +#if defined(SUPPORT_TIMELINE) class RecorderSynchronizationLock : public AllStatic { public: static void Init() { @@ -376,7 +392,7 @@ class TimelineEvent { void CompleteWithPreSerializedArgs(char* args_json); // Get/Set the number of arguments in the event. - intptr_t GetNumArguments() { return arguments_.length(); } + intptr_t GetNumArguments() const { return arguments_.length(); } void SetNumArguments(intptr_t length) { arguments_.SetNumArguments(length); } // |name| must be a compile time constant. Takes ownership of |argument|. void SetArgument(intptr_t i, const char* name, char* argument) { @@ -420,8 +436,8 @@ class TimelineEvent { bool HasIsolateId() const; bool HasIsolateGroupId() const; - const char* GetFormattedIsolateId() const; - const char* GetFormattedIsolateGroupId() const; + char* GetFormattedIsolateId() const; + char* GetFormattedIsolateGroupId() const; // The lowest time value stored in this event. int64_t LowTime() const; @@ -562,6 +578,9 @@ class TimelineEvent { friend class TimelineEventPlatformRecorder; friend class TimelineEventFuchsiaRecorder; friend class TimelineEventMacosRecorder; +#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT) + friend class TimelineEventPerfettoFileRecorder; +#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT) friend class TimelineStream; friend class TimelineTestHelper; DISALLOW_COPY_AND_ASSIGN(TimelineEvent); @@ -582,6 +601,14 @@ class TimelineTrackMetadata { * object into |jsarr_events|. */ void PrintJSON(const JSONArray& jsarr_events) const; +#if defined(SUPPORT_PERFETTO) + /* + * Populates the fields of a |perfetto::protos::pbzero::TracePacket| with the + * metadata stored by this object. + */ + void PopulateTracePacket( + perfetto::protos::pbzero::TracePacket* track_descriptor_packet); +#endif // defined(SUPPORT_PERFETTO) #endif // !defined(PRODUCT) private: @@ -593,7 +620,6 @@ class TimelineTrackMetadata { Utils::CStringUniquePtr track_name_; }; -#ifdef SUPPORT_TIMELINE #define TIMELINE_DURATION(thread, stream, name) \ TimelineBeginEndScope tbes(thread, Timeline::Get##stream##Stream(), name); #define TIMELINE_FUNCTION_COMPILATION_DURATION(thread, name, function) \ @@ -605,11 +631,6 @@ class TimelineTrackMetadata { #define TIMELINE_FUNCTION_GC_DURATION(thread, name) \ TimelineBeginEndScope tbes(thread, Timeline::GetGCStream(), name); -#else -#define TIMELINE_DURATION(thread, stream, name) -#define TIMELINE_FUNCTION_COMPILATION_DURATION(thread, name, function) -#define TIMELINE_FUNCTION_GC_DURATION(thread, name) -#endif // SUPPORT_TIMELINE // See |TimelineBeginEndScope|. class TimelineEventScope : public StackResource { @@ -833,6 +854,10 @@ class TimelineEventRecorder : public MallocAllocated { const char* thread_name); protected: + SimpleHashMap& track_uuid_to_track_metadata() { + return track_uuid_to_track_metadata_; + } + #ifndef PRODUCT void WriteTo(const char* directory); #endif @@ -1137,6 +1162,30 @@ class TimelineEventFileRecorder : public TimelineEventFileRecorderBase { bool first_; }; +#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT) +class TimelineEventPerfettoFileRecorder : public TimelineEventFileRecorderBase { + public: + explicit TimelineEventPerfettoFileRecorder(const char* path); + virtual ~TimelineEventPerfettoFileRecorder(); + + const char* name() const final { return PERFETTO_RECORDER_NAME; } + + private: + void WritePacket( + protozero::HeapBuffered* packet) + const; + void DrainImpl(const TimelineEvent& event) final; + + static const intptr_t kAsyncTrackUuidToTrackDescriptorInitialCapacity = 1 + << 4; + SimpleHashMap async_track_uuid_to_track_descriptor_; + // We allocate one heap-buffered packet as a class member, because it lets us + // continuously follow a cycle of resetting the buffer and writing its to + // contents to the file. + protozero::HeapBuffered packet_; +}; +#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT) + class DartTimelineEventHelpers : public AllStatic { public: static void ReportTaskEvent(TimelineEvent* event, @@ -1145,6 +1194,7 @@ class DartTimelineEventHelpers : public AllStatic { char* name, char* args); }; +#endif // defined(SUPPORT_TIMELINE) } // namespace dart diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni index 60fcab4dd97..eee843820d3 100644 --- a/runtime/vm/vm_sources.gni +++ b/runtime/vm/vm_sources.gni @@ -233,6 +233,14 @@ vm_sources = [ "profiler_service.h", "program_visitor.cc", "program_visitor.h", + "protos/perfetto/common/builtin_clock.pbzero.h", + "protos/perfetto/trace/clock_snapshot.pbzero.h", + "protos/perfetto/trace/trace_packet.pbzero.h", + "protos/perfetto/trace/track_event/debug_annotation.pbzero.h", + "protos/perfetto/trace/track_event/process_descriptor.pbzero.h", + "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h", + "protos/perfetto/trace/track_event/track_descriptor.pbzero.h", + "protos/perfetto/trace/track_event/track_event.pbzero.h", "random.cc", "random.h", "raw_object.cc",