Skip to content

Commit 1d90f66

Browse files
committed
Add perfetto tracing to host tooling
Example cvd create trace: https://ui.perfetto.dev/#!/?s=315a987bda469405966eb46797201e11b12d247c Example cvd fetch trace: https://ui.perfetto.dev/#!/?s=8e6e182ffab8e92f0f0a690b205c73313ec71f17 Perfetto uses a shared memory buffer between producer processes and the traced consumer daemon. `fork()` is problematic for this as the parent and child processes would end up clobbering each other when the shared memory buffer is inherited by the child. To prevent this, we introduce a perfetto wrapper that uses `pthread_atfork()` to register handlers that shutdown and restart tracing before and after forking. Perfetto also depends on global static data and explicitly calls out that it can not be restarted unless this data is cleared [1]. With this, the Perfetto wrapper utilizes `dlopen()` and `dlclose()` during init and shutdown. Perfetto also uses thread local data which requires extra care around `dlclose()` to prevent some thread local destructors from running after `dlclose()` has already unloaded the Perfetto library. To work around this, the Perfetto wrapper isolates all Perfetto calls into a single worker thread. [1] https://github.com/google/perfetto/blob/ab21398d658ce3bccc192b226d2731add6c02890/include/perfetto/tracing/tracing.h#L252 Bug: b/378560215
1 parent 00d5bd0 commit 1d90f66

41 files changed

Lines changed: 1292 additions & 19 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

base/cvd/MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ bazel_dep(name = "nasm", version = "2.16.03.bcr.1")
3737
bazel_dep(name = "pcre2", version = "10.45")
3838
bazel_dep(name = "platforms", version = "1.0.0")
3939
bazel_dep(name = "protobuf", version = "31.1")
40+
bazel_dep(name = "re2")
4041
bazel_dep(name = "rootcanal")
4142
bazel_dep(name = "rules_cc", version = "0.2.16")
4243
bazel_dep(name = "rules_flex", version = "0.4")

base/cvd/build_external/build_external.MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ include("//build_external/mkbootimg:mkbootimg.MODULE.bazel")
4848
include("//build_external/ms-tpm-20-ref:ms-tpm-20-ref.MODULE.bazel")
4949
include("//build_external/mtools:mtools.MODULE.bazel")
5050
include("//build_external/opengl_headers:opengl_headers.MODULE.bazel")
51+
include("//build_external/perfetto:perfetto.MODULE.bazel")
5152
include("//build_external/pyyaml:pyyaml.MODULE.bazel")
5253
include("//build_external/re2:re2.MODULE.bazel")
5354
include("//build_external/rootcanal:rootcanal.MODULE.bazel")

base/cvd/build_external/perfetto/BUILD

Whitespace-only changes.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
From 45c2b6299505b133fdce117649f457e36640d824 Mon Sep 17 00:00:00 2001
2+
From: Jason Macnak <natsu@google.com>
3+
Date: Fri, 15 May 2026 11:28:54 -0700
4+
Subject: [PATCH 1/4] Disable Android components to avoid extra dependencies
5+
6+
---
7+
bazel/rules.bzl | 8 +++-----
8+
1 file changed, 3 insertions(+), 5 deletions(-)
9+
10+
diff --git a/bazel/rules.bzl b/bazel/rules.bzl
11+
index 6195465a67..a3e367cde8 100644
12+
--- a/bazel/rules.bzl
13+
+++ b/bazel/rules.bzl
14+
@@ -13,9 +13,7 @@
15+
# limitations under the License.
16+
17+
load("@perfetto//bazel:proto_gen.bzl", "proto_descriptor_gen", "proto_gen")
18+
-load("@perfetto//bazel:run_ait_with_adb.bzl", "android_instrumentation_test")
19+
load("@perfetto_cfg//:perfetto_cfg.bzl", "PERFETTO_CONFIG")
20+
-load("@rules_android//android:rules.bzl", "android_binary", "android_library")
21+
22+
# +----------------------------------------------------------------------------+
23+
# | Base C++ rules. |
24+
@@ -113,11 +111,11 @@ def perfetto_jspb_proto_library(**kwargs):
25+
# +----------------------------------------------------------------------------+
26+
def perfetto_android_binary(**kwargs):
27+
if not _rule_override("android_binary", **kwargs):
28+
- android_binary(**kwargs)
29+
+ return
30+
31+
def perfetto_android_library(**kwargs):
32+
if not _rule_override("android_library", **kwargs):
33+
- android_library(**kwargs)
34+
+ return
35+
36+
def perfetto_android_jni_library(**kwargs):
37+
if not _rule_override("android_jni_library", **kwargs):
38+
@@ -171,7 +169,7 @@ def _perfetto_android_jni_library(
39+
40+
def perfetto_android_instrumentation_test(**kwargs):
41+
if not _rule_override("android_instrumentation_test", **kwargs):
42+
- android_instrumentation_test(**kwargs)
43+
+ return
44+
45+
# +----------------------------------------------------------------------------+
46+
# | Misc rules. |
47+
--
48+
2.54.0.563.g4f69b47b94-goog
49+
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
From 5e991031fa757ade8a7be54e5bfab69ec627a432 Mon Sep 17 00:00:00 2001
2+
From: Jason Macnak <natsu@google.com>
3+
Date: Fri, 15 May 2026 11:29:26 -0700
4+
Subject: [PATCH 2/4] Add load statements for Bazel 9
5+
6+
---
7+
bazel/proto_gen.bzl | 2 ++
8+
bazel/rules.bzl | 30 ++++++++++++++++++------------
9+
2 files changed, 20 insertions(+), 12 deletions(-)
10+
11+
diff --git a/bazel/proto_gen.bzl b/bazel/proto_gen.bzl
12+
index 74f91cad89..1764cb2d9a 100644
13+
--- a/bazel/proto_gen.bzl
14+
+++ b/bazel/proto_gen.bzl
15+
@@ -15,6 +15,8 @@
16+
# This file defines the proto_gen() rule that is used for generating protos
17+
# with custom plugins (ipc and protozero).
18+
19+
+load("@protobuf//bazel/common:proto_info.bzl", "ProtoInfo")
20+
+
21+
def _proto_gen_impl(ctx):
22+
proto_src = [
23+
f
24+
diff --git a/bazel/rules.bzl b/bazel/rules.bzl
25+
index a3e367cde8..15f6d52d5b 100644
26+
--- a/bazel/rules.bzl
27+
+++ b/bazel/rules.bzl
28+
@@ -14,6 +14,12 @@
29+
30+
load("@perfetto//bazel:proto_gen.bzl", "proto_descriptor_gen", "proto_gen")
31+
load("@perfetto_cfg//:perfetto_cfg.bzl", "PERFETTO_CONFIG")
32+
+load("@protobuf//bazel:cc_proto_library.bzl", "cc_proto_library")
33+
+load("@protobuf//bazel:proto_library.bzl", "proto_library")
34+
+load("@protobuf//bazel:java_lite_proto_library.bzl", "java_lite_proto_library")
35+
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
36+
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
37+
+load("@rules_python//python:defs.bzl", "py_binary", "py_library")
38+
39+
# +----------------------------------------------------------------------------+
40+
# | Base C++ rules. |
41+
@@ -38,7 +44,7 @@ def default_cc_args():
42+
43+
def perfetto_build_config_cc_library(**kwargs):
44+
if not _rule_override("cc_library", **kwargs):
45+
- native.cc_library(**kwargs)
46+
+ cc_library(**kwargs)
47+
48+
def perfetto_filegroup(**kwargs):
49+
if not _rule_override("filegroup", **kwargs):
50+
@@ -51,20 +57,20 @@ def perfetto_genrule(**kwargs):
51+
def perfetto_cc_library(**kwargs):
52+
args = _merge_dicts(default_cc_args(), kwargs)
53+
if not _rule_override("cc_library", **args):
54+
- native.cc_library(**args)
55+
+ cc_library(**args)
56+
57+
def perfetto_cc_binary(**kwargs):
58+
args = _merge_dicts(default_cc_args(), kwargs)
59+
if not _rule_override("cc_binary", **args):
60+
- native.cc_binary(**args)
61+
+ cc_binary(**args)
62+
63+
def perfetto_py_binary(**kwargs):
64+
if not _rule_override("py_binary", **kwargs):
65+
- native.py_binary(**kwargs)
66+
+ py_binary(**kwargs)
67+
68+
def perfetto_py_library(**kwargs):
69+
if not _rule_override("py_library", **kwargs):
70+
- native.py_library(**kwargs)
71+
+ py_library(**kwargs)
72+
73+
# +----------------------------------------------------------------------------+
74+
# | Proto-related rules |
75+
@@ -72,11 +78,11 @@ def perfetto_py_library(**kwargs):
76+
77+
def perfetto_proto_library(**kwargs):
78+
if not _rule_override("proto_library", **kwargs):
79+
- native.proto_library(**kwargs)
80+
+ proto_library(**kwargs)
81+
82+
def perfetto_cc_proto_library(**kwargs):
83+
if not _rule_override("cc_proto_library", **kwargs):
84+
- native.cc_proto_library(**kwargs)
85+
+ cc_proto_library(**kwargs)
86+
87+
def perfetto_java_proto_library(**kwargs):
88+
if not _rule_override("java_proto_library", **kwargs):
89+
@@ -84,7 +90,7 @@ def perfetto_java_proto_library(**kwargs):
90+
91+
def perfetto_java_lite_proto_library(**kwargs):
92+
if not _rule_override("java_lite_proto_library", **kwargs):
93+
- native.java_lite_proto_library(**kwargs)
94+
+ java_lite_proto_library(**kwargs)
95+
96+
# Unlike the other rules, this is an noop by default because Bazel does not
97+
# support Go proto libraries.
98+
@@ -142,7 +148,7 @@ def _perfetto_android_jni_library(
99+
if not (binary_name.startswith("lib") and binary_name.endswith(".so")):
100+
fail("'binary_name' should sharts with 'lib' and ends with '.so'" +
101+
", got %s instead" % binary_name)
102+
- # We strip the name, since `native.cc_binary` adds prefix and suffix
103+
+ # We strip the name, since `cc_binary` adds prefix and suffix
104+
# to the generated library name.
105+
binary_target_name = binary_name.removeprefix("lib").removesuffix(".so")
106+
input_cc_library_name = name + "_input"
107+
@@ -150,18 +156,18 @@ def _perfetto_android_jni_library(
108+
# exclude them from being build when invoke `bazel build :all`,
109+
# since these targets won't be able to compile anyway, see
110+
# https://bazel.build/docs/android-ndk#cclibrary-android.
111+
- native.cc_library(
112+
+ cc_library(
113+
name = input_cc_library_name,
114+
target_compatible_with = ["@platforms//os:android"],
115+
**input_cc_library_kwargs
116+
)
117+
- native.cc_binary(
118+
+ cc_binary(
119+
name = binary_target_name,
120+
linkshared = True,
121+
deps = [input_cc_library_name],
122+
target_compatible_with = ["@platforms//os:android"],
123+
)
124+
- native.cc_library(
125+
+ cc_library(
126+
name = name,
127+
srcs = [binary_target_name],
128+
target_compatible_with = ["@platforms//os:android"],
129+
--
130+
2.54.0.563.g4f69b47b94-goog
131+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
From 8ae64deaf5cb13bcdfb87d576b6d207a2fef13df Mon Sep 17 00:00:00 2001
2+
From: Jason Macnak <natsu@google.com>
3+
Date: Fri, 15 May 2026 11:33:40 -0700
4+
Subject: [PATCH 3/4] Expose shutdown in perfetto_c
5+
6+
---
7+
include/perfetto/public/abi/producer_abi.h | 3 +++
8+
src/shared_lib/producer.cc | 4 ++++
9+
2 files changed, 7 insertions(+)
10+
11+
diff --git a/include/perfetto/public/abi/producer_abi.h b/include/perfetto/public/abi/producer_abi.h
12+
index 8899d0098e..e43da137c5 100644
13+
--- a/include/perfetto/public/abi/producer_abi.h
14+
+++ b/include/perfetto/public/abi/producer_abi.h
15+
@@ -79,6 +79,9 @@ PERFETTO_SDK_EXPORT void PerfettoProducerActivateTriggers(
16+
const char* trigger_names[],
17+
uint32_t ttl_ms);
18+
19+
+
20+
+PERFETTO_SDK_EXPORT void PerfettoProducerShutdown(void);
21+
+
22+
#ifdef __cplusplus
23+
}
24+
#endif
25+
diff --git a/src/shared_lib/producer.cc b/src/shared_lib/producer.cc
26+
index 06599f97e1..8f90bedb2d 100644
27+
--- a/src/shared_lib/producer.cc
28+
+++ b/src/shared_lib/producer.cc
29+
@@ -81,3 +81,7 @@ void PerfettoProducerActivateTriggers(const char* trigger_names[],
30+
}
31+
perfetto::Tracing::ActivateTriggers(triggers, ttl_ms);
32+
}
33+
+
34+
+void PerfettoProducerShutdown(void) {
35+
+ perfetto::Tracing::Shutdown();
36+
+}
37+
--
38+
2.54.0.563.g4f69b47b94-goog
39+
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
From 3c24b5504feba80a1f48c22cf4bfaf27733aee34 Mon Sep 17 00:00:00 2001
2+
From: Jason Macnak <natsu@google.com>
3+
Date: Fri, 15 May 2026 11:37:20 -0700
4+
Subject: [PATCH 4/4] Expose flush in libperfetto_c
5+
6+
---
7+
include/perfetto/public/abi/track_event_abi.h | 2 ++
8+
src/shared_lib/track_event/ds.h | 4 ++++
9+
src/shared_lib/track_event/track_event.cc | 4 ++++
10+
3 files changed, 10 insertions(+)
11+
12+
diff --git a/include/perfetto/public/abi/track_event_abi.h b/include/perfetto/public/abi/track_event_abi.h
13+
index 6c07b30bd4..5c315ea942 100644
14+
--- a/include/perfetto/public/abi/track_event_abi.h
15+
+++ b/include/perfetto/public/abi/track_event_abi.h
16+
@@ -145,6 +145,8 @@ enum PerfettoTeType {
17+
PERFETTO_TE_TYPE_COUNTER = 4,
18+
};
19+
20+
+PERFETTO_SDK_EXPORT void PerfettoTeFlush(void);
21+
+
22+
#ifdef __cplusplus
23+
}
24+
#endif
25+
diff --git a/src/shared_lib/track_event/ds.h b/src/shared_lib/track_event/ds.h
26+
index 199a1e3d6a..be13a0593b 100644
27+
--- a/src/shared_lib/track_event/ds.h
28+
+++ b/src/shared_lib/track_event/ds.h
29+
@@ -127,6 +127,10 @@ class TrackEvent
30+
Register(dsd);
31+
}
32+
33+
+ static void Flush() {
34+
+ TrackEvent::Trace([](TrackEvent::TraceContext ctx) { ctx.Flush(); });
35+
+ }
36+
+
37+
static void UpdateDescriptorFromCategories(DataSourceDescriptor dsd) {
38+
dsd.set_name("track_event");
39+
UpdateDescriptor(dsd);
40+
diff --git a/src/shared_lib/track_event/track_event.cc b/src/shared_lib/track_event/track_event.cc
41+
index b6f9000ad3..70dd68d61b 100644
42+
--- a/src/shared_lib/track_event/track_event.cc
43+
+++ b/src/shared_lib/track_event/track_event.cc
44+
@@ -86,3 +86,7 @@ void PerfettoTeCategoryImplDestroy(struct PerfettoTeCategoryImpl* cat) {
45+
perfetto::shlib::GlobalState::Instance().UnregisterCategory(cat);
46+
delete cat;
47+
}
48+
+
49+
+void PerfettoTeFlush(void) {
50+
+ perfetto::shlib::TrackEvent::Flush();
51+
+}
52+
--
53+
2.54.0.563.g4f69b47b94-goog
54+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
perfetto_ext = use_extension("//build_external/perfetto:repositories.bzl", "perfetto_extension")
2+
use_repo(perfetto_ext, "perfetto")
3+
use_repo(perfetto_ext, "perfetto_cfg")
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
2+
3+
# https://github.com/google/perfetto/issues/2787 for Perfetto getting into the
4+
# Central Repository.
5+
def _perfetto_extension_impl(_):
6+
# From commit 877553985565ab10fe20ea844891aacabd0f70c7:
7+
URL = "https://github.com/google/perfetto/archive/refs/tags/v55.2.tar.gz"
8+
http_archive(
9+
name = "perfetto",
10+
url = URL,
11+
strip_prefix = "perfetto-55.2",
12+
patch_args = ["-p1"],
13+
patches = [
14+
"@//build_external/perfetto:PATCH.0001_disable_android_deps.patch",
15+
"@//build_external/perfetto:PATCH.0002_add_load_statements.patch",
16+
"@//build_external/perfetto:PATCH.0003_expose_shutdown.patch",
17+
"@//build_external/perfetto:PATCH.0004_expose_flush.patch",
18+
],
19+
)
20+
http_archive(
21+
name = "perfetto_cfg",
22+
url = URL,
23+
strip_prefix = "perfetto-55.2/bazel/standalone",
24+
build_file_content = "# empty BUILD to make a bazel package",
25+
patch_cmds = [
26+
"sed -i 's|@com_google_protobuf|@protobuf|g' perfetto_cfg.bzl",
27+
],
28+
)
29+
30+
perfetto_extension = module_extension(
31+
implementation = _perfetto_extension_impl,
32+
)

base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ cf_cc_binary(
7272
"//cuttlefish/host/libs/config/fastboot",
7373
"//cuttlefish/host/libs/feature:inject",
7474
"//cuttlefish/host/libs/log_names",
75+
"//cuttlefish/host/libs/tracing",
7576
"//cuttlefish/posix:symlink",
7677
"//cuttlefish/pretty:vector",
7778
"//libbase",
@@ -169,6 +170,7 @@ cf_cc_library(
169170
"//cuttlefish/common/libs/utils:subprocess",
170171
"//cuttlefish/common/libs/utils:subprocess_managed_stdio",
171172
"//cuttlefish/host/libs/config:config_utils",
173+
"//cuttlefish/host/libs/tracing",
172174
"//cuttlefish/posix:strerror",
173175
"//cuttlefish/result",
174176
"//libbase",
@@ -223,6 +225,7 @@ cf_cc_library(
223225
"//cuttlefish/host/libs/config:file_source",
224226
"//cuttlefish/host/libs/config:instance_nums",
225227
"//cuttlefish/host/libs/feature:inject",
228+
"//cuttlefish/host/libs/tracing",
226229
"//cuttlefish/result",
227230
"//libbase",
228231
"@abseil-cpp//absl/log",
@@ -240,6 +243,7 @@ cf_cc_library(
240243
"//cuttlefish/host/libs/config:vmm_mode",
241244
"//cuttlefish/host/libs/image_aggregator",
242245
"//cuttlefish/host/libs/image_aggregator:qcow2",
246+
"//cuttlefish/host/libs/tracing",
243247
"//cuttlefish/result",
244248
"//libbase",
245249
"@abseil-cpp//absl/log",
@@ -357,6 +361,7 @@ cf_cc_library(
357361
"//cuttlefish/host/libs/config:secure_hals",
358362
"//cuttlefish/host/libs/config:vmm_mode",
359363
"//cuttlefish/host/libs/config/defaults",
364+
"//cuttlefish/host/libs/tracing",
360365
"//cuttlefish/host/libs/vhal_proxy_server",
361366
"//cuttlefish/host/libs/vm_manager",
362367
"//cuttlefish/result",
@@ -405,6 +410,7 @@ cf_cc_library(
405410
"//cuttlefish/host/libs/config:config_utils",
406411
"//cuttlefish/host/libs/config:display",
407412
"//cuttlefish/host/libs/config:gpu_mode",
413+
"//cuttlefish/host/libs/tracing",
408414
"//cuttlefish/pretty",
409415
"//cuttlefish/pretty:optional",
410416
"//cuttlefish/pretty:string",
@@ -434,6 +440,7 @@ cf_cc_library(
434440
"//cuttlefish/host/libs/config:guest_hwui_renderer",
435441
"//cuttlefish/host/libs/config:guest_renderer_preload",
436442
"//cuttlefish/host/libs/config:vmm_mode",
443+
"//cuttlefish/host/libs/tracing",
437444
"//cuttlefish/result",
438445
"//libbase",
439446
"@abseil-cpp//absl/log",

0 commit comments

Comments
 (0)