From 6011ba67b33ef3e4ea4287c5484b5c5226b1cb89 Mon Sep 17 00:00:00 2001 From: Derek Xu Date: Tue, 18 Apr 2023 19:39:05 +0000 Subject: [PATCH] Reland "Reland "[VM] Begin supporting Perfetto file recorder"" This is a reland of commit 4cd9c9c66663562d93261224eaaaa728e2a6a421 This will be relanded without changes because we discussed the .proto definition package name clashing problem with the Dart in google3 team, and we have decided that the best solution is to ignore the checked in .proto files in google3 builds. TEST=Recorded traces with the Perfetto file recorder and explored them in Perfetto's trace viewer, CI Original change's description: > 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 Change-Id: Ia97ef1f0fe73e76c7022623b9ae7e21a4ea73a46 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/295804 Commit-Queue: Derek Xu Reviewed-by: Ben Konyi --- 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 | 79 +++++ runtime/vm/timeline.cc | 335 +++++++++++++++++- runtime/vm/timeline.h | 68 +++- runtime/vm/vm_sources.gni | 8 + 28 files changed, 2607 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..0871eb67d10 --- /dev/null +++ b/runtime/vm/protos/tools/compile_perfetto_protos.dart @@ -0,0 +1,79 @@ +// 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 = r''' +// 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. +''' + + 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",