WORK IN PROGRESS: move android_track_event.proto to protos/third_party I have not tested this, probably needs some more work. The hanges in src/tools should be splitted out, they are only to allow -I to affect the search path also for .json. Ignore them for now, i'll split them
diff --git a/protos/third_party/android/BUILD.gn b/protos/third_party/android/BUILD.gn new file mode 100644 index 0000000..96f2a28 --- /dev/null +++ b/protos/third_party/android/BUILD.gn
@@ -0,0 +1,56 @@ +# Copyright (C) 2026 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. + +import("../../../gn/perfetto.gni") + +action("extension_descriptor") { + if (host_os == "win") { + _host_executable_suffix = ".exe" + } else { + _host_executable_suffix = "" + } + + binary_label = "${perfetto_root_path}/src/tools:tracing_proto_extensions($host_toolchain)" + binary_path = get_label_info(binary_label, "root_out_dir") + + "/tracing_proto_extensions" + _host_executable_suffix + binary_rebased_path = "./" + rebase_path(binary_path, root_build_dir) + + deps = [ binary_label ] + script = "${perfetto_root_path}/gn/run_binary.py" + + outputs = [ "$target_gen_dir/android_track_event_extension.descriptor" ] + + args = [ + binary_rebased_path, + "-I", + rebase_path("${perfetto_root_path}", root_build_dir), + "-I", + rebase_path("${perfetto_root_path}/protos/third_party/android", + root_build_dir), + "--json", + rebase_path( + "${perfetto_root_path}/protos/third_party/android/system_tracing_protos.json", + root_build_dir), + "--descriptor-out", + rebase_path(outputs[0], root_build_dir), + ] + + inputs = + [ "${perfetto_root_path}/protos/third_party/android/system_tracing_protos.json" ] + + metadata = { + perfetto_action_type_for_generator = + [ "tracing_proto_extensions_descriptor" ] + } +}
diff --git a/protos/third_party/android/frameworks/base/proto/tracing/frameworks_base_tracing_protos.json b/protos/third_party/android/frameworks/base/proto/tracing/frameworks_base_tracing_protos.json new file mode 100644 index 0000000..4c2b4f4 --- /dev/null +++ b/protos/third_party/android/frameworks/base/proto/tracing/frameworks_base_tracing_protos.json
@@ -0,0 +1,21 @@ +{ + "extensions": [ + { + "scope": "perfetto.protos.TrackEvent", + "ranges": [[2004, 2006], [2008, 2199]], + "allocations": [ + { + "name": "frameworks_base", + "comment": ["//frameworks/base tracing protos"], + "ranges": [[2004, 2006], [2008, 2099]], + "proto": "frameworks/base/proto/tracing/frameworks_base_track_event.proto" + }, + { + "name": "unallocated", + "comment": ["The next allocation goes here. Shrink and take from here."], + "range": [2100, 2199] + } + ] + } + ] +}
diff --git a/protos/perfetto/trace/android/android_track_event.proto b/protos/third_party/android/frameworks/base/proto/tracing/frameworks_base_track_event.proto similarity index 86% rename from protos/perfetto/trace/android/android_track_event.proto rename to protos/third_party/android/frameworks/base/proto/tracing/frameworks_base_track_event.proto index e6d34cf..737e3c7 100644 --- a/protos/perfetto/trace/android/android_track_event.proto +++ b/protos/third_party/android/frameworks/base/proto/tracing/frameworks_base_track_event.proto
@@ -18,7 +18,7 @@ import public "protos/perfetto/trace/track_event/track_event.proto"; -package perfetto.protos; +package com.android.internal; // Information about sending and receiving messages on threads with // Looper and MessageQueue. @@ -138,84 +138,6 @@ optional int64 id = 8; } -// Information about a compositing workload submitted by SurfaceFlinger. -message AndroidSurfaceFlingerWorkload { - // ID of the "parent" requesting the workload - enum Source { - // Unknown source - UNKNOWN = 0; - // Workload was triggered by a frame signal event - FRAME_SIGNAL = 1; - } - optional Source source = 1; - - // Output that the workload is eventually compositing into - optional string output_name = 2; - - // Frame timeline vsync ID corresponding to this workload. - optional int64 vsync_id = 3; - - // Interesting properties for a workload - message Summary { - // Corresponds to Atrace slices, but with stable names - message Timings { - // Walltime for slices corresponding to CPU work done by SurfaceFlinger - message SfCpu { - // Top-level time spent signaling a frame update - optional int64 frame_signal_nanos = 1; - // Time spent committing new transactions to be composited - optional int64 commit_nanos = 2; - // Time spent for the compositing operation itself - optional int64 composite_nanos = 3; - } - optional SfCpu sf_cpu = 1; - - // Walltime for slices corresponding to HWC work, observed by - // SurfaceFlinger - message Hwc { - // Time spent presenting a frame - optional int64 present_nanos = 1; - // Time spent validating a frame - optional int64 validate_nanos = 2; - // Time spent either validating or presenting, if HWC is choosing a path - // to take - optional int64 present_or_validate_nanos = 3; - } - optional Hwc hwc = 2; - - // Walltime for slices corresponding to RenderEngine work - message Re { - // Time spent drawing layers on the CPU - optional int64 draw_layers_nanos = 1; - // Time spent waiting for a GPU fence to signal GPU completion - optional int64 gpu_completion_nanos = 2; - } - optional Re re = 3; - - // Walltime for slices corresponding to Skia work, observed by - // SurfaceFlinger - message Skia { - // Time spent flushing commands - optional int64 flush_nanos = 1; - // Time spent submitting commands to the GPU driver - optional int64 submit_nanos = 2; - } - optional Skia skia = 4; - } - optional Timings timings = 1; - - // Statistics tracked that describes a workload - message Stats { - // Number of layers that needed to be composited via the GPU. - optional int32 gpu_composited_layers = 1; - // Number of layers that needed to be composited via the DPU. - optional int32 dpu_composited_layers = 2; - } - optional Stats stats = 2; - } - optional Summary summary = 4; -} - enum ReceiverType { RECEIVER_TYPE_UNKNOWN = 0; RECEIVER_TYPE_RUNTIME = 1; @@ -658,25 +580,20 @@ optional int32 implicit_cpu_time_reasons = 12; } -message AndroidTrackEvent { - // Usable range: [2001, 2999] +message AndroidFrameworksBaseTrackEvent { + // Usable range: [2001, 2199] // Next id: 2014 - extend TrackEvent { - // The name of a binder service. - optional string binder_service_name = 2001; - // The name of a binder interface. - optional string binder_interface_name = 2002; - // The name of an apex. - optional string apex_name = 2003; - + extend perfetto.protos.TrackEvent { // MessageQueue messages. optional AndroidMessageQueue message_queue = 2004; // Bitmaps optional AndroidBitmap bitmap = 2005; // JobScheduler jobs. optional AndroidJobSchedulerJob job_scheduler_job = 2006; - // SurfaceFlinger workload. - optional AndroidSurfaceFlingerWorkload surfaceflinger_workload = 2007; + + // 2007 is AndroidSurfaceFlingerWorkload surfaceflinger_workload, moved + // to frameworks_native_track_event.proto + // Broadcasts delivered. optional AndroidBroadcastEvent broadcast_event = 2008; // Freezer event.
diff --git a/protos/third_party/android/frameworks/native/tracing/frameworks_native_tracing_protos.json b/protos/third_party/android/frameworks/native/tracing/frameworks_native_tracing_protos.json new file mode 100644 index 0000000..0a4fc1e --- /dev/null +++ b/protos/third_party/android/frameworks/native/tracing/frameworks_native_tracing_protos.json
@@ -0,0 +1,22 @@ +{ + "extensions": [ + { + "scope": "perfetto.protos.TrackEvent", + "ranges": [[2001, 2003], [2007, 2007], [2100, 2199]], + "allocations": [ + { + "name": "frameworks_native", + "comment": ["//frameworks/native tracing protos"], + "ranges": [[2001, 2003], [2007, 2007]], + "proto": "frameworks/native/tracing/frameworks_native_track_event.proto" + }, + { + "name": "unallocated", + "comment": ["The next allocation goes here. Shrink and take from here."], + "range": [2100, 2199] + } + + ] + } + ] +}
diff --git a/protos/third_party/android/frameworks/native/tracing/frameworks_native_track_event.proto b/protos/third_party/android/frameworks/native/tracing/frameworks_native_track_event.proto new file mode 100644 index 0000000..c5c8416 --- /dev/null +++ b/protos/third_party/android/frameworks/native/tracing/frameworks_native_track_event.proto
@@ -0,0 +1,118 @@ +/* + * Copyright (C) 2024 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. + */ + +syntax = "proto2"; + +import public "protos/perfetto/trace/track_event/track_event.proto"; + +package com.android.internal; + +// Information about a compositing workload submitted by SurfaceFlinger. +message AndroidSurfaceFlingerWorkload { + // ID of the "parent" requesting the workload + enum Source { + // Unknown source + UNKNOWN = 0; + // Workload was triggered by a frame signal event + FRAME_SIGNAL = 1; + } + optional Source source = 1; + + // Output that the workload is eventually compositing into + optional string output_name = 2; + + // Frame timeline vsync ID corresponding to this workload. + optional int64 vsync_id = 3; + + // Interesting properties for a workload + message Summary { + // Corresponds to Atrace slices, but with stable names + message Timings { + // Walltime for slices corresponding to CPU work done by SurfaceFlinger + message SfCpu { + // Top-level time spent signaling a frame update + optional int64 frame_signal_nanos = 1; + // Time spent committing new transactions to be composited + optional int64 commit_nanos = 2; + // Time spent for the compositing operation itself + optional int64 composite_nanos = 3; + } + optional SfCpu sf_cpu = 1; + + // Walltime for slices corresponding to HWC work, observed by + // SurfaceFlinger + message Hwc { + // Time spent presenting a frame + optional int64 present_nanos = 1; + // Time spent validating a frame + optional int64 validate_nanos = 2; + // Time spent either validating or presenting, if HWC is choosing a path + // to take + optional int64 present_or_validate_nanos = 3; + } + optional Hwc hwc = 2; + + // Walltime for slices corresponding to RenderEngine work + message Re { + // Time spent drawing layers on the CPU + optional int64 draw_layers_nanos = 1; + // Time spent waiting for a GPU fence to signal GPU completion + optional int64 gpu_completion_nanos = 2; + } + optional Re re = 3; + + // Walltime for slices corresponding to Skia work, observed by + // SurfaceFlinger + message Skia { + // Time spent flushing commands + optional int64 flush_nanos = 1; + // Time spent submitting commands to the GPU driver + optional int64 submit_nanos = 2; + } + optional Skia skia = 4; + } + optional Timings timings = 1; + + // Statistics tracked that describes a workload + message Stats { + // Number of layers that needed to be composited via the GPU. + optional int32 gpu_composited_layers = 1; + // Number of layers that needed to be composited via the DPU. + optional int32 dpu_composited_layers = 2; + } + optional Stats stats = 2; + } + optional Summary summary = 4; +} + +message AndroidFrameworksNativeTrackEvent { + // Usable range: [2001, 2003], [2007, 2007], [2200, 2299], + // We have some spurious messages in 2001-2003, 2007 for historical reasons, + // before the split between fw/native and fw/base was introduced. + // Next id: 2200 + extend perfetto.protos.TrackEvent { + // The name of a binder service. + optional string binder_service_name = 2001; + // The name of a binder interface. + optional string binder_interface_name = 2002; + // The name of an apex. + optional string apex_name = 2003; + + optional AndroidSurfaceFlingerWorkload surfaceflinger_workload = 2007; + + // Start next messages from 2200. + } +}
diff --git a/protos/third_party/android/system_tracing_protos.json b/protos/third_party/android/system_tracing_protos.json new file mode 100644 index 0000000..1f63f27 --- /dev/null +++ b/protos/third_party/android/system_tracing_protos.json
@@ -0,0 +1,30 @@ +{ + "extensions": [ + { + "scope": "perfetto.protos.TrackEvent", + "range": [2000, 2999], + "comment": [ + "This is a copy of Android's /system/core/tracing/system_tracing_protos.json" + ], + "allocations": [ + { + "name": "frameworks_base", + "ranges": [[2004, 2006], [2008, 2099]], + "registry": "frameworks/base/proto/tracing/frameworks_base_tracing_protos.json" + }, + { + "name": "frameworks_native", + "ranges": [[2000, 2003], [2007, 2007], [2100, 2199]], + "registry": "frameworks/native/tracing/frameworks_native_tracing_protos.json" + }, + { + "name": "unallocated", + "comment": [ + "The next allocation goes here. Shrink and take from here." + ], + "range": [2200, 2999] + } + ] + } + ] +}
diff --git a/src/tools/BUILD.gn b/src/tools/BUILD.gn index d9dc923..338af80 100644 --- a/src/tools/BUILD.gn +++ b/src/tools/BUILD.gn
@@ -85,7 +85,6 @@ } perfetto_host_executable("tracing_proto_extensions") { - testonly = true deps = [ ":tracing_proto_extensions_lib", "../../gn:default_deps",
diff --git a/src/tools/tracing_proto_extensions.cc b/src/tools/tracing_proto_extensions.cc index 1e1aa19..cce5779 100644 --- a/src/tools/tracing_proto_extensions.cc +++ b/src/tools/tracing_proto_extensions.cc
@@ -14,6 +14,8 @@ * limitations under the License. */ +// TODO make this skip 'protos/perfetto/*' + #include "src/tools/tracing_proto_extensions.h" #include <algorithm> @@ -565,26 +567,59 @@ std::vector<Range> ranges; }; +// Searches for |rel_path| under each directory in |search_paths|. Returns +// the first existing path found, or an error if none match. +base::StatusOr<std::string> ResolvePathInSearchDirs( + const std::string& rel_path, + const std::vector<std::string>& search_paths) { + for (const auto& dir : search_paths) { + std::string candidate = dir + "/" + rel_path; + if (base::FileExists(candidate)) { + return candidate; + } + } + return base::ErrStatus("Could not find '%s' in any -I path", + rel_path.c_str()); +} + // Walks allocations from an already-parsed registry. For sub-registries, // reads and parses them using the flat (non-wrapped) format. -base::Status CollectProtosFromRegistry(const Registry& reg, - const std::string& root_dir, - std::vector<ProtoEntry>* out) { +base::Status CollectProtosFromRegistry( + const Registry& reg, + const std::vector<std::string>& search_paths, + std::vector<ProtoEntry>* out) { for (const auto& alloc : reg.allocations) { if (!alloc.proto.empty() && alloc.repo.empty()) { - // Local proto leaf. - out->push_back({root_dir + "/" + alloc.proto, reg.scope, alloc.ranges}); + // Local proto leaf. If the proto file can't be found in any -I path, + // skip it (it may live in a different repo). + auto path_or = ResolvePathInSearchDirs(alloc.proto, search_paths); + if (!path_or.ok()) { + PERFETTO_ILOG("Skipping proto '%s' (not found in any -I path)", + alloc.proto.c_str()); + continue; + } + out->push_back({std::move(*path_or), reg.scope, alloc.ranges}); } else if (!alloc.registry.empty() && alloc.repo.empty()) { // Local sub-registry (same {"extensions": [...]} format). - std::string sub_path = root_dir + "/" + alloc.registry; - std::string contents; - if (!base::ReadFile(sub_path, &contents)) { - return base::ErrStatus("Failed to read '%s'", sub_path.c_str()); + // If the registry file can't be found in any -I path, skip it + // (it may live in a different repo). + auto sub_path_or = + ResolvePathInSearchDirs(alloc.registry, search_paths); + if (!sub_path_or.ok()) { + PERFETTO_ILOG("Skipping registry '%s' (not found in any -I path)", + alloc.registry.c_str()); + continue; } - ASSIGN_OR_RETURN(auto sub_regs, ParseRegistryFile(contents, sub_path)); + std::string contents; + if (!base::ReadFile(*sub_path_or, &contents)) { + return base::ErrStatus("Failed to read '%s'", sub_path_or->c_str()); + } + ASSIGN_OR_RETURN(auto sub_regs, + ParseRegistryFile(contents, *sub_path_or)); for (const auto& sub_reg : sub_regs) { RETURN_IF_ERROR(ValidateRegistry(sub_reg)); - RETURN_IF_ERROR(CollectProtosFromRegistry(sub_reg, root_dir, out)); + RETURN_IF_ERROR( + CollectProtosFromRegistry(sub_reg, search_paths, out)); } } // Remote entries (repo is set) are skipped. @@ -596,8 +631,7 @@ base::StatusOr<std::vector<uint8_t>> GenerateExtensionDescriptors( const std::string& root_json_path, - const std::vector<std::string>& proto_paths, - const std::string& root_dir) { + const std::vector<std::string>& proto_paths) { // 1. Read and parse the root registry file (uses the "extensions" wrapper). std::string root_contents; if (!base::ReadFile(root_json_path, &root_contents)) { @@ -610,7 +644,7 @@ std::vector<ProtoEntry> entries; for (const auto& reg : extensions) { RETURN_IF_ERROR(ValidateRegistry(reg)); - RETURN_IF_ERROR(CollectProtosFromRegistry(reg, root_dir, &entries)); + RETURN_IF_ERROR(CollectProtosFromRegistry(reg, proto_paths, &entries)); } if (entries.empty()) { @@ -635,14 +669,15 @@ protozero::HeapBuffered<pbzero::FileDescriptorSet> fds; for (const auto& entry : entries) { - // The proto path in the JSON is relative to root_dir, but protoc needs - // it relative to one of the -I paths. Since root_dir is typically one - // of the -I paths, we use the path as-is from the allocation. - // We need to derive the proto import path from the full path. + // The proto path is an absolute/resolved path from CollectProtosFromRegistry. + // Protoc needs it relative to one of the -I paths. Strip the matching + // prefix so that protoc's DiskSourceTree can find it. std::string proto_import_path = entry.proto_path; - // Strip root_dir prefix if present. - if (base::StartsWith(proto_import_path, root_dir + "/")) { - proto_import_path = proto_import_path.substr(root_dir.size() + 1); + for (const auto& path : proto_paths) { + if (base::StartsWith(proto_import_path, path + "/")) { + proto_import_path = proto_import_path.substr(path.size() + 1); + break; + } } const auto* file_desc = importer.Import(proto_import_path);
diff --git a/src/tools/tracing_proto_extensions.h b/src/tools/tracing_proto_extensions.h index 0205887..33de210 100644 --- a/src/tools/tracing_proto_extensions.h +++ b/src/tools/tracing_proto_extensions.h
@@ -70,12 +70,11 @@ // and returns a serialized FileDescriptorSet containing only the extension // descriptors (using the field subset from our descriptor.proto). // -// |proto_paths| are the -I include directories for protoc. -// |root_dir| is the base directory for resolving relative paths in the JSON. +// |proto_paths| are the -I include directories for protoc and for resolving +// relative .json registry and .proto paths referenced in allocations. base::StatusOr<std::vector<uint8_t>> GenerateExtensionDescriptors( const std::string& root_json_path, - const std::vector<std::string>& proto_paths, - const std::string& root_dir); + const std::vector<std::string>& proto_paths); } // namespace gen_proto_extensions } // namespace perfetto
diff --git a/src/tools/tracing_proto_extensions_main.cc b/src/tools/tracing_proto_extensions_main.cc index 5602a44..0f68d65 100644 --- a/src/tools/tracing_proto_extensions_main.cc +++ b/src/tools/tracing_proto_extensions_main.cc
@@ -157,12 +157,7 @@ return 1; } - // Derive root_dir from json_path (the directory containing the JSON). - // Actually, we use the first -I path as the root_dir for resolving - // relative paths in the JSON. - const std::string& root_dir = proto_paths[0]; - - auto result = GenerateExtensionDescriptors(json_path, proto_paths, root_dir); + auto result = GenerateExtensionDescriptors(json_path, proto_paths); if (!result.ok()) { PERFETTO_ELOG("Error: %s", result.status().message().c_str()); return 1;
diff --git a/src/tools/tracing_proto_extensions_unittest.cc b/src/tools/tracing_proto_extensions_unittest.cc index f753b25..95fa54e 100644 --- a/src/tools/tracing_proto_extensions_unittest.cc +++ b/src/tools/tracing_proto_extensions_unittest.cc
@@ -444,7 +444,7 @@ })"); auto result = GenerateExtensionDescriptors(tmp.AbsolutePath("registry.json"), - {tmp.path()}, tmp.path()); + {tmp.path()}); ASSERT_FALSE(result.ok()); EXPECT_THAT(result.status().message(), testing::HasSubstr("no extensions targeting")); @@ -455,7 +455,7 @@ // It requires proto include paths to work. std::string proto_path = base::GetTestDataPath( "protos/perfetto/trace/track_event/track_event_extensions.json"); - auto result = GenerateExtensionDescriptors(proto_path, {"."}, "."); + auto result = GenerateExtensionDescriptors(proto_path, {"."}); // This should succeed for local protos (test_extensions.proto and // android_track_event.proto). Remote entries (chromium) are skipped. ASSERT_TRUE(result.ok()) << result.status().message();
diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn index 0a2117e..273f642 100644 --- a/src/trace_processor/importers/proto/BUILD.gn +++ b/src/trace_processor/importers/proto/BUILD.gn
@@ -296,9 +296,9 @@ } perfetto_cc_proto_descriptor("gen_cc_android_track_event_descriptor") { - descriptor_name = "android_track_event.descriptor" + descriptor_name = "android_track_event_extension.descriptor" descriptor_target = - "../../../../protos/perfetto/trace/android:android_track_event_descriptor" + "../../../../protos/third_party/android:extension_descriptor" } source_set("unittests") {