[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 <bkonyi@google.com>
Commit-Queue: Derek Xu <derekx@google.com>
This commit is contained in:
Derek Xu 2023-02-27 18:37:48 +00:00 committed by Commit Queue
parent 0ac2d98e29
commit 7424295ce9
28 changed files with 2562 additions and 33 deletions

View file

@ -293,6 +293,7 @@ library_for_all_configs("libdart") {
extra_deps = [
":generate_version_cc_file",
"third_party/double-conversion/src:libdouble_conversion",
"vm:libprotozero",
]
if (is_fuchsia) {
if (using_fuchsia_gn_sdk) {

View file

@ -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.

View file

@ -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",
]

View file

@ -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)

View file

@ -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<void> 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<String> compilerFlagsForFile(String filepath) {
@ -17,7 +41,9 @@ List<String> compilerFlagsForFile(String filepath) {
'-Iruntime/include',
'-Ithird_party/tcmalloc/gperftools/src',
'-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',
@ -124,6 +150,8 @@ final Set<String> excludedFiles = Set<String>.from([
]);
main(List<String> files) async {
await generatePerfettoBuildFlags();
bool isFirstFailure = true;
files = files.where((filepath) => !excludedFiles.contains(filepath)).toList();

View file

@ -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")
@ -66,6 +67,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 = [
@ -76,9 +150,10 @@ library_for_all_configs("libdart_vm") {
"//third_party/icu:icui18n",
"//third_party/icu:icuuc",
]
extra_deps = [ ":libprotozero" ]
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",
@ -91,7 +166,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",
@ -102,7 +177,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",
@ -130,13 +205,14 @@ library_for_all_configs_with_compiler("libdart_compiler") {
public_configs = [ ":libdart_vm_config" ]
sources = rebase_path(compiler_sources, ".", "./compiler/")
include_dirs = [ ".." ]
extra_deps = [ ":libprotozero" ]
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",
]
@ -146,13 +222,14 @@ library_for_all_configs_with_compiler("libdart_compiler") {
library_for_all_configs("libdart_lib") {
target_type = "source_set"
extra_deps = [ ":libprotozero" ]
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",
]
@ -231,6 +308,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",
@ -242,6 +326,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",

View file

@ -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)
#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))
#define SUPPORT_TIMELINE 1
#endif

3
runtime/vm/protos/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# The protozero compiler generates empty .pbzero.cc files for compatibility
# reasons (crbug.com/998165). We can ignore them.
*.pbzero.cc

View file

@ -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 <stddef.h>
#include <stdint.h>
#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.

View file

@ -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;
}

View file

@ -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 <stddef.h>
#include <stdint.h>
#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<const uint8_t*>(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<FieldMetadata<...>>
// 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 <typename T = ClockSnapshot_Clock>
T* add_clocks() {
return BeginNestedMessage<T>(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<FieldMetadata<...>>
// 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<const uint8_t*>(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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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.

View file

@ -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;
}

View file

@ -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 <stddef.h>
#include <stdint.h>
#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<const uint8_t*>(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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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 <typename T = ClockSnapshot>
T* set_clock_snapshot() {
return BeginNestedMessage<T>(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<FieldMetadata<...>>
// 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 <typename T = TrackEvent>
T* set_track_event() {
return BeginNestedMessage<T>(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<FieldMetadata<...>>
// 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 <typename T = TrackDescriptor>
T* set_track_descriptor() {
return BeginNestedMessage<T>(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<FieldMetadata<...>>
// 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.

View file

@ -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;
}
}

View file

@ -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 <stddef.h>
#include <stdint.h>
#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<const uint8_t*>(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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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.

View file

@ -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;
}
}

View file

@ -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 <stddef.h>
#include <stdint.h>
#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<const uint8_t*>(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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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.

View file

@ -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;
}

View file

@ -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 <stddef.h>
#include <stdint.h>
#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<const uint8_t*>(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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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.

View file

@ -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;
}

View file

@ -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 <stddef.h>
#include <stdint.h>
#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<const uint8_t*>(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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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 <typename T = ProcessDescriptor>
T* set_process() {
return BeginNestedMessage<T>(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<FieldMetadata<...>>
// 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 <typename T = ThreadDescriptor>
T* set_thread() {
return BeginNestedMessage<T>(4);
}
};
} // namespace pbzero
} // namespace protos
} // namespace perfetto
#endif // Include guard.

View file

@ -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;
}

View file

@ -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 <stddef.h>
#include <stdint.h>
#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<const uint8_t*>(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<uint64_t> flow_ids() const {
return GetRepeated<uint64_t>(47);
}
bool has_terminating_flow_ids() const { return at<48>().valid(); }
::protozero::RepeatedFieldIterator<uint64_t> terminating_flow_ids() const {
return GetRepeated<uint64_t>(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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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<FieldMetadata<...>>
// 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 <typename T = DebugAnnotation>
T* add_debug_annotations() {
return BeginNestedMessage<T>(4);
}
};
} // namespace pbzero
} // namespace protos
} // namespace perfetto
#endif // Include guard.

View file

@ -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;
}

View file

@ -0,0 +1,77 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:io';
Future<void> 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<void> copyPerfettoProtoHeaders() async {
final copySource = Directory('./out/DebugX64/gen/runtime/vm/protos').path;
final copyDestination = Directory('./runtime/vm').path;
late final executable;
late final args;
if (Platform.operatingSystem == 'windows') {
executable = 'xcopy';
args = [copySource, copyDestination, '/e', '/i'];
} else {
executable = 'cp';
args = ['-R', copySource, copyDestination];
}
final processResult = await Process.run(executable, args);
final int exitCode = processResult.exitCode;
final String stdout = processResult.stdout.trim();
final String stderr = processResult.stderr.trim();
if (exitCode != 0) {
print('exit-code: $exitCode');
print('stdout:');
print('${stdout}');
print('stderr:');
print('${stderr}');
}
for (final file
in Directory('./runtime/vm/protos').listSync(recursive: true)) {
if (!(file is File) || !file.path.endsWith('.pbzero.h')) {
continue;
}
final contentsIncludingPrependedNotices =
'// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file\n' +
'// for details. All rights reserved. Use of this source code is governed by a\n' +
'// BSD-style license that can be found in the LICENSE file.\n\n' +
'// IMPORTANT: This file should only ever be modified by modifying the\n' +
'// corresponding .proto file and then running\n' +
'// `dart runtime/vm/protos/tools/compile_perfetto_protos.dart` from the SDK root\n' +
'// directory.\n' +
file.readAsStringSync();
file.writeAsStringSync(contentsIncludingPrependedNotices, flush: true);
}
}
main(List<String> 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();
}

View file

@ -11,8 +11,11 @@
#include <fcntl.h>
#include <cstdlib>
#include <memory>
#include <tuple>
#include <utility>
#include "perfetto/ext/tracing/core/trace_packet.h"
#include "platform/atomic.h"
#include "platform/hashmap.h"
#include "vm/isolate.h"
@ -20,6 +23,14 @@
#include "vm/lockers.h"
#include "vm/log.h"
#include "vm/object.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"
#include "vm/service.h"
#include "vm/service_event.h"
#include "vm/thread.h"
@ -28,11 +39,11 @@ namespace dart {
#if defined(PRODUCT)
#define DEFAULT_TIMELINE_RECORDER "none"
#define SUPPORTED_TIMELINE_RECORDERS "systrace, file, callback"
#define SUPPORTED_TIMELINE_RECORDERS "systrace, file, callback, perfetto"
#else
#define DEFAULT_TIMELINE_RECORDER "ring"
#define SUPPORTED_TIMELINE_RECORDERS \
"ring, endless, startup, systrace, file, callback"
"ring, endless, startup, systrace, file, callback, perfetto"
#endif
DEFINE_FLAG(bool, complete_timeline, false, "Record the complete timeline");
@ -43,12 +54,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,
NULL,
"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,
NULL,
"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,
NULL,
@ -161,6 +172,20 @@ static TimelineEventRecorder* CreateTimelineRecorder() {
return new TimelineEventEmbedderCallbackRecorder();
}
{
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];
FLAG_timeline_dir = nullptr;
return new TimelineEventPerfettoFileRecorder(filename);
}
}
#if !defined(PRODUCT)
// Recorders below do nothing useful in PRODUCT mode. You can't extract
// information available in them without vm-service.
@ -836,7 +861,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_);
@ -847,7 +872,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_);
@ -884,6 +909,28 @@ void TimelineTrackMetadata::PrintJSON(const JSONArray& jsarr_events) const {
}
#endif // !defined(PRODUCT)
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());
}
TimelineStream::TimelineStream(const char* name,
const char* fuchsia_name,
bool has_static_labels,
@ -1707,6 +1754,246 @@ void TimelineEventFileRecorder::DrainImpl(const TimelineEvent& event) {
free(output);
}
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<uword>(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<TimelineTrackMetadata*>(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<perfetto::protos::pbzero::TracePacket>*>(
entry->value);
WritePacket(value);
delete value;
}
}
inline const std::tuple<std::unique_ptr<char[]>, intptr_t> GetProtoPreamble(
const intptr_t size) {
std::unique_ptr<char[]> preamble =
std::make_unique<char[]>(perfetto::TracePacket::kMaxPreambleBytes);
uint8_t* ptr = reinterpret_cast<uint8_t*>(&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<intptr_t>(ptr) -
reinterpret_cast<intptr_t>(&preamble[0]);
return std::make_tuple(std::move(preamble), preamble_size);
}
void TimelineEventPerfettoFileRecorder::WritePacket(
protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet)
const {
intptr_t size = 0;
for (const protozero::ScatteredHeapBuffer::Slice& slice :
packet->GetSlices()) {
size += slice.size() - slice.unused_bytes();
}
const std::tuple<std::unique_ptr<char[]>, 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<char*>(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<void*>(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<perfetto::protos::pbzero::TracePacket>&
track_descriptor_packet = *(
new protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>);
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();
}
TimelineEventEndlessRecorder::TimelineEventEndlessRecorder()
: head_(nullptr), tail_(nullptr), block_index_(0) {}

View file

@ -16,6 +16,11 @@
#include "vm/os.h"
#include "vm/os_thread.h"
#if defined(SUPPORT_TIMELINE)
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "vm/protos/perfetto/trace/trace_packet.pbzero.h"
#endif // defined(SUPPORT_TIMELINE)
#if defined(FUCHSIA_SDK) || defined(DART_HOST_OS_FUCHSIA)
#include <lib/trace-engine/context.h>
#include <lib/trace-engine/instrumentation.h>
@ -34,6 +39,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 +60,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 +82,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 +143,7 @@ class TimelineStream {
#endif
};
#if defined(SUPPORT_TIMELINE)
class RecorderSynchronizationLock : public AllStatic {
public:
static void Init() {
@ -386,7 +401,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) {
@ -434,8 +449,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;
@ -588,6 +603,7 @@ class TimelineEvent {
friend class TimelineEventPlatformRecorder;
friend class TimelineEventFuchsiaRecorder;
friend class TimelineEventMacosRecorder;
friend class TimelineEventPerfettoFileRecorder;
friend class TimelineStream;
friend class TimelineTestHelper;
DISALLOW_COPY_AND_ASSIGN(TimelineEvent);
@ -609,6 +625,12 @@ class TimelineTrackMetadata {
*/
void PrintJSON(const JSONArray& jsarr_events) const;
#endif // !defined(PRODUCT)
/*
* 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);
private:
// The ID of the process that this track is associated with.
@ -619,7 +641,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) \
@ -631,11 +652,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 {
@ -859,6 +875,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
@ -1163,6 +1183,28 @@ class TimelineEventFileRecorder : public TimelineEventFileRecorderBase {
bool first_;
};
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<perfetto::protos::pbzero::TracePacket>* 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<perfetto::protos::pbzero::TracePacket> packet_;
};
class DartTimelineEventHelpers : public AllStatic {
public:
static void ReportTaskEvent(TimelineEvent* event,
@ -1171,6 +1213,7 @@ class DartTimelineEventHelpers : public AllStatic {
char* name,
char* args);
};
#endif // defined(SUPPORT_TIMELINE)
} // namespace dart

View file

@ -241,6 +241,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",