aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-03-30 05:39:26 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-03-30 05:39:26 +0000
commit00bdb00aaaaaece57e6d05d78fe70471fe21a865 (patch)
treef03afe17f29de2fc5e9a40469bd42aeb9f640e5e
parent973fd8d9e6c9eb4e51334640d34bc28d3048c7e3 (diff)
parent5ab99d1ec1f8661ff812fe5bdb855529f81a4c00 (diff)
downloadperfetto-android13-mainline-cellbroadcast-release.tar.gz
Snap for 9847922 from 5ab99d1ec1f8661ff812fe5bdb855529f81a4c00 to mainline-cellbroadcast-releaseaml_cbr_331810000android13-mainline-cellbroadcast-release
Change-Id: I31ff6bacdc57526ef02a84389e35ed9b5c78e2e7
-rw-r--r--Android.bp9
-rw-r--r--BUILD2
-rw-r--r--CHANGELOG2
-rw-r--r--TEST_MAPPING10
-rw-r--r--gn/perfetto_integrationtests.gni5
-rw-r--r--include/perfetto/ext/base/utils.h3
-rw-r--r--include/perfetto/tracing/internal/tracing_muxer.h9
-rw-r--r--include/perfetto/tracing/tracing.h9
-rw-r--r--protos/perfetto/config/android/network_trace_config.proto24
-rw-r--r--protos/perfetto/config/perfetto_config.proto24
-rw-r--r--protos/perfetto/trace/android/network_trace.proto37
-rw-r--r--protos/perfetto/trace/interned_data/BUILD.gn1
-rw-r--r--protos/perfetto/trace/interned_data/interned_data.proto6
-rw-r--r--protos/perfetto/trace/perfetto_trace.proto71
-rw-r--r--protos/perfetto/trace/trace_packet.proto5
-rw-r--r--src/base/test/tmp_dir_tree.cc6
-rw-r--r--src/base/test/tmp_dir_tree.h5
-rw-r--r--src/base/utils.cc8
-rw-r--r--src/tracing/BUILD.gn15
-rw-r--r--src/tracing/internal/tracing_muxer_fake.cc5
-rw-r--r--src/tracing/internal/tracing_muxer_fake.h1
-rw-r--r--src/tracing/internal/tracing_muxer_impl.cc36
-rw-r--r--src/tracing/internal/tracing_muxer_impl.h8
-rw-r--r--src/tracing/internal/tracing_muxer_impl_integrationtest.cc172
-rw-r--r--src/tracing/test/api_integrationtest.cc59
-rw-r--r--src/tracing/test/api_test_support.cc50
-rw-r--r--src/tracing/test/api_test_support.h26
-rw-r--r--src/tracing/tracing.cc6
-rw-r--r--test/cts/Android.bp2
-rw-r--r--test/cts/AndroidTest.xml14
-rw-r--r--test/cts/heapprofd_java_test_cts.cc103
-rw-r--r--test/cts/producer/Android.bp1
-rw-r--r--test/cts/reporter/Android.bp1
-rw-r--r--test/cts/test_apps/Android.bp4
-rwxr-xr-xtest/cts/test_apps/AndroidManifest_debuggable.xml13
-rwxr-xr-xtest/cts/test_apps/AndroidManifest_profileable.xml13
-rwxr-xr-xtest/cts/test_apps/AndroidManifest_release.xml13
-rw-r--r--test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java33
-rw-r--r--test/test_helper.cc2
-rw-r--r--test/test_helper.h62
40 files changed, 850 insertions, 25 deletions
diff --git a/Android.bp b/Android.bp
index b238396a3..e20a86d90 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1829,6 +1829,7 @@ cc_test {
":perfetto_src_tracing_core_core",
":perfetto_src_tracing_core_service",
":perfetto_src_tracing_in_process_backend",
+ ":perfetto_src_tracing_integrationtests",
":perfetto_src_tracing_ipc_common",
":perfetto_src_tracing_ipc_consumer_consumer",
":perfetto_src_tracing_ipc_default_socket",
@@ -9313,6 +9314,14 @@ filegroup {
],
}
+// GN: //src/tracing:integrationtests
+filegroup {
+ name: "perfetto_src_tracing_integrationtests",
+ srcs: [
+ "src/tracing/internal/tracing_muxer_impl_integrationtest.cc",
+ ],
+}
+
// GN: //src/tracing/ipc:common
filegroup {
name: "perfetto_src_tracing_ipc_common",
diff --git a/BUILD b/BUILD
index 76401add6..fc8ef7d4c 100644
--- a/BUILD
+++ b/BUILD
@@ -3096,6 +3096,7 @@ perfetto_proto_library(
],
deps = [
":protos_perfetto_common_protos",
+ ":protos_perfetto_trace_android_protos",
":protos_perfetto_trace_gpu_protos",
":protos_perfetto_trace_profiling_protos",
":protos_perfetto_trace_track_event_protos",
@@ -3107,6 +3108,7 @@ perfetto_cc_protozero_library(
name = "protos_perfetto_trace_interned_data_zero",
deps = [
":protos_perfetto_common_zero",
+ ":protos_perfetto_trace_android_zero",
":protos_perfetto_trace_gpu_zero",
":protos_perfetto_trace_interned_data_protos",
":protos_perfetto_trace_profiling_zero",
diff --git a/CHANGELOG b/CHANGELOG
index 4b35e3837..6a4cdbd3c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -10,6 +10,8 @@ Unreleased:
SDK:
* Added TracingInitArgs::enable_system_consumer configuration option, that
allows the linker to discard the consumer IPC, if not required.
+ * Add perfetto::Tracing::ActivateTriggers() function.
+
v25.0 - 2022-04-01:
Tracing service and probes:
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 785bb2a21..b30e56b2f 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -9,6 +9,16 @@
"name": "libsurfaceflinger_unittest"
}
],
+ "mainline-presubmit": [
+ {
+ "name": "CtsPerfettoTestCases[com.google.android.art.apex]",
+ "options": [
+ {
+ "include-filter": "HeapprofdJavaCtsTest*"
+ }
+ ]
+ }
+ ],
"hwasan-postsubmit": [
{
"name": "CtsPerfettoTestCases"
diff --git a/gn/perfetto_integrationtests.gni b/gn/perfetto_integrationtests.gni
index 7527a55d4..4e47083af 100644
--- a/gn/perfetto_integrationtests.gni
+++ b/gn/perfetto_integrationtests.gni
@@ -20,6 +20,11 @@ perfetto_integrationtests_targets = [
"src/tracing/test:client_api_integrationtests",
]
+if (enable_perfetto_ipc) {
+ perfetto_integrationtests_targets +=
+ [ "src/tracing:integrationtests" ]
+}
+
if (enable_perfetto_traced_probes) {
# enable_perfetto_traced_probes implies enable_perfetto_platform_services.
perfetto_integrationtests_targets += [
diff --git a/include/perfetto/ext/base/utils.h b/include/perfetto/ext/base/utils.h
index 1535c5e3a..4c4b66b07 100644
--- a/include/perfetto/ext/base/utils.h
+++ b/include/perfetto/ext/base/utils.h
@@ -98,6 +98,9 @@ inline bool IsAgain(int err) {
// setenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
void SetEnv(const std::string& key, const std::string& value);
+// unsetenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
+void UnsetEnv(const std::string& key);
+
// Calls mallopt(M_PURGE, 0) on Android. Does nothing on other platforms.
// This forces the allocator to release freed memory. This is used to work
// around various Scudo inefficiencies. See b/170217718.
diff --git a/include/perfetto/tracing/internal/tracing_muxer.h b/include/perfetto/tracing/internal/tracing_muxer.h
index bef7191f3..962763c97 100644
--- a/include/perfetto/tracing/internal/tracing_muxer.h
+++ b/include/perfetto/tracing/internal/tracing_muxer.h
@@ -88,6 +88,15 @@ class PERFETTO_EXPORT TracingMuxer {
InterceptorBase::TLSFactory,
InterceptorBase::TracePacketCallback) = 0;
+ // Informs the tracing services to activate any of these triggers if any
+ // tracing session was waiting for them.
+ //
+ // Sends the trigger signal to all the initialized backends that are currently
+ // connected and that connect in the next `ttl_ms` milliseconds (but returns
+ // immediately anyway).
+ virtual void ActivateTriggers(const std::vector<std::string>&,
+ uint32_t ttl_ms) = 0;
+
protected:
explicit TracingMuxer(Platform* platform) : platform_(platform) {}
diff --git a/include/perfetto/tracing/tracing.h b/include/perfetto/tracing/tracing.h
index 01d4851c5..8faa1de09 100644
--- a/include/perfetto/tracing/tracing.h
+++ b/include/perfetto/tracing/tracing.h
@@ -194,6 +194,15 @@ class PERFETTO_EXPORT Tracing {
// this call is made.
static void ResetForTesting();
+ // Informs the tracing services to activate any of these triggers if any
+ // tracing session was waiting for them.
+ //
+ // Sends the trigger signal to all the initialized backends that are currently
+ // connected and that connect in the next `ttl_ms` milliseconds (but
+ // returns immediately anyway).
+ static void ActivateTriggers(const std::vector<std::string>& triggers,
+ uint32_t ttl_ms);
+
private:
static void InitializeInternal(const TracingInitArgs&);
diff --git a/protos/perfetto/config/android/network_trace_config.proto b/protos/perfetto/config/android/network_trace_config.proto
index f58f28024..ad5196b62 100644
--- a/protos/perfetto/config/android/network_trace_config.proto
+++ b/protos/perfetto/config/android/network_trace_config.proto
@@ -27,4 +27,28 @@ message NetworkPacketTraceConfig {
// The minimum polling rate is 100ms (values below this are ignored).
// Introduced in Android 14 (U).
optional uint32 poll_ms = 1;
+
+ // The aggregation_threshold is the number of packets at which an event will
+ // switch from per-packet details to aggregate details. For example, a value
+ // of 50 means that if a particular event (grouped by the unique combinations
+ // of metadata fields: {interface, direction, uid, etc}) has fewer than 50
+ // packets, the exact timestamp and length are recorded for each packet. If
+ // there were 50 or more packets in an event, it would only record the total
+ // duration, packets, and length. A value of zero or unspecified will always
+ /// record per-packet details. A value of 1 always records aggregate details.
+ optional uint32 aggregation_threshold = 2;
+
+ // Specifies the maximum number of packet contexts to intern at a time. This
+ // prevents the interning table from growing too large and controls whether
+ // interning is enabled or disabled (a value of zero disables interning and
+ // is the default). When a data sources interning table reaches this amount,
+ // packet contexts will be inlined into NetworkPacketEvents.
+ optional uint32 intern_limit = 3;
+
+ // The following fields specify whether certain fields should be dropped from
+ // the output. Dropping fields improves normalization results, reduces the
+ // size of the interning table, and slightly reduces event size.
+ optional bool drop_local_port = 4;
+ optional bool drop_remote_port = 5;
+ optional bool drop_tcp_flags = 6;
}
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index b80985572..3b920d522 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -402,6 +402,30 @@ message NetworkPacketTraceConfig {
// The minimum polling rate is 100ms (values below this are ignored).
// Introduced in Android 14 (U).
optional uint32 poll_ms = 1;
+
+ // The aggregation_threshold is the number of packets at which an event will
+ // switch from per-packet details to aggregate details. For example, a value
+ // of 50 means that if a particular event (grouped by the unique combinations
+ // of metadata fields: {interface, direction, uid, etc}) has fewer than 50
+ // packets, the exact timestamp and length are recorded for each packet. If
+ // there were 50 or more packets in an event, it would only record the total
+ // duration, packets, and length. A value of zero or unspecified will always
+ /// record per-packet details. A value of 1 always records aggregate details.
+ optional uint32 aggregation_threshold = 2;
+
+ // Specifies the maximum number of packet contexts to intern at a time. This
+ // prevents the interning table from growing too large and controls whether
+ // interning is enabled or disabled (a value of zero disables interning and
+ // is the default). When a data sources interning table reaches this amount,
+ // packet contexts will be inlined into NetworkPacketEvents.
+ optional uint32 intern_limit = 3;
+
+ // The following fields specify whether certain fields should be dropped from
+ // the output. Dropping fields improves normalization results, reduces the
+ // size of the interning table, and slightly reduces event size.
+ optional bool drop_local_port = 4;
+ optional bool drop_remote_port = 5;
+ optional bool drop_tcp_flags = 6;
}
// End of protos/perfetto/config/android/network_trace_config.proto
diff --git a/protos/perfetto/trace/android/network_trace.proto b/protos/perfetto/trace/android/network_trace.proto
index d1c43a2e7..5fcd28b2b 100644
--- a/protos/perfetto/trace/android/network_trace.proto
+++ b/protos/perfetto/trace/android/network_trace.proto
@@ -33,7 +33,9 @@ message NetworkPacketEvent {
// The name of the interface if available (e.g. 'rmnet0').
optional string interface = 2;
- // The length of the packet in bytes (wire_size - L2_header_size).
+ // The length of the packet in bytes (wire_size - L2_header_size). Ignored
+ // when using NetworkPacketEvent as the ctx in either NetworkPacketBundle or
+ // NetworkPacketContext.
optional uint32 length = 3;
// The Linux user id associated with the packet's socket.
@@ -54,3 +56,36 @@ message NetworkPacketEvent {
// The remote udp/tcp port of the packet.
optional uint32 remote_port = 9;
}
+
+// NetworkPacketBundle bundles one or more packets sharing the same attributes.
+message NetworkPacketBundle {
+ oneof packet_context {
+ // The intern id for looking up the associated packet context.
+ uint64 iid = 1;
+
+ // The inlined context for events in this bundle.
+ NetworkPacketEvent ctx = 2;
+ }
+
+ // The timestamp of the i-th packet encoded as the nanoseconds since the
+ // enclosing TracePacket's timestamp.
+ repeated uint64 packet_timestamps = 3 [packed = true];
+
+ // The length of the i-th packet in bytes (wire_size - L2_header_size).
+ repeated uint32 packet_lengths = 4 [packed = true];
+
+ // Total number of packets in the bundle (when above aggregation_threshold).
+ optional uint32 total_packets = 5;
+
+ // Duration between first and last packet (when above aggregation_threshold).
+ optional uint64 total_duration = 6;
+
+ // Total packet length in bytes (when above aggregation_threshold).
+ optional uint64 total_length = 7;
+}
+
+// An internable packet context.
+message NetworkPacketContext {
+ optional uint64 iid = 1;
+ optional NetworkPacketEvent ctx = 2;
+}
diff --git a/protos/perfetto/trace/interned_data/BUILD.gn b/protos/perfetto/trace/interned_data/BUILD.gn
index 580bb5b1d..66436c597 100644
--- a/protos/perfetto/trace/interned_data/BUILD.gn
+++ b/protos/perfetto/trace/interned_data/BUILD.gn
@@ -17,6 +17,7 @@ import("../../../../gn/proto_library.gni")
perfetto_proto_library("@TYPE@") {
sources = [ "interned_data.proto" ]
deps = [
+ "../android:@TYPE@",
"../gpu:@TYPE@",
"../profiling:@TYPE@",
"../track_event:@TYPE@",
diff --git a/protos/perfetto/trace/interned_data/interned_data.proto b/protos/perfetto/trace/interned_data/interned_data.proto
index 86b8a9f36..19894ca44 100644
--- a/protos/perfetto/trace/interned_data/interned_data.proto
+++ b/protos/perfetto/trace/interned_data/interned_data.proto
@@ -16,6 +16,7 @@
syntax = "proto2";
+import "protos/perfetto/trace/android/network_trace.proto";
import "protos/perfetto/trace/gpu/gpu_render_stage_event.proto";
import "protos/perfetto/trace/track_event/chrome_histogram_sample.proto";
import "protos/perfetto/trace/track_event/debug_annotation.proto";
@@ -53,7 +54,7 @@ package perfetto.protos;
// emitted proactively in advance of referring to them in later packets.
//
// Next reserved id: 8 (up to 15).
-// Next id: 28.
+// Next id: 31.
message InternedData {
// TODO(eseckler): Replace iid fields inside interned messages with
// map<iid, message> type fields in InternedData.
@@ -110,4 +111,7 @@ message InternedData {
// This is is NOT the real address. This is to avoid disclosing KASLR through
// traces.
repeated InternedString kernel_symbols = 26;
+
+ // Interned packet context for android.network_packets.
+ repeated NetworkPacketContext packet_context = 30;
}
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 8056c83ef..e4e75a218 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -402,6 +402,30 @@ message NetworkPacketTraceConfig {
// The minimum polling rate is 100ms (values below this are ignored).
// Introduced in Android 14 (U).
optional uint32 poll_ms = 1;
+
+ // The aggregation_threshold is the number of packets at which an event will
+ // switch from per-packet details to aggregate details. For example, a value
+ // of 50 means that if a particular event (grouped by the unique combinations
+ // of metadata fields: {interface, direction, uid, etc}) has fewer than 50
+ // packets, the exact timestamp and length are recorded for each packet. If
+ // there were 50 or more packets in an event, it would only record the total
+ // duration, packets, and length. A value of zero or unspecified will always
+ /// record per-packet details. A value of 1 always records aggregate details.
+ optional uint32 aggregation_threshold = 2;
+
+ // Specifies the maximum number of packet contexts to intern at a time. This
+ // prevents the interning table from growing too large and controls whether
+ // interning is enabled or disabled (a value of zero disables interning and
+ // is the default). When a data sources interning table reaches this amount,
+ // packet contexts will be inlined into NetworkPacketEvents.
+ optional uint32 intern_limit = 3;
+
+ // The following fields specify whether certain fields should be dropped from
+ // the output. Dropping fields improves normalization results, reduces the
+ // size of the interning table, and slightly reduces event size.
+ optional bool drop_local_port = 4;
+ optional bool drop_remote_port = 5;
+ optional bool drop_tcp_flags = 6;
}
// End of protos/perfetto/config/android/network_trace_config.proto
@@ -2838,7 +2862,9 @@ message NetworkPacketEvent {
// The name of the interface if available (e.g. 'rmnet0').
optional string interface = 2;
- // The length of the packet in bytes (wire_size - L2_header_size).
+ // The length of the packet in bytes (wire_size - L2_header_size). Ignored
+ // when using NetworkPacketEvent as the ctx in either NetworkPacketBundle or
+ // NetworkPacketContext.
optional uint32 length = 3;
// The Linux user id associated with the packet's socket.
@@ -2860,6 +2886,39 @@ message NetworkPacketEvent {
optional uint32 remote_port = 9;
}
+// NetworkPacketBundle bundles one or more packets sharing the same attributes.
+message NetworkPacketBundle {
+ oneof packet_context {
+ // The intern id for looking up the associated packet context.
+ uint64 iid = 1;
+
+ // The inlined context for events in this bundle.
+ NetworkPacketEvent ctx = 2;
+ }
+
+ // The timestamp of the i-th packet encoded as the nanoseconds since the
+ // enclosing TracePacket's timestamp.
+ repeated uint64 packet_timestamps = 3 [packed = true];
+
+ // The length of the i-th packet in bytes (wire_size - L2_header_size).
+ repeated uint32 packet_lengths = 4 [packed = true];
+
+ // Total number of packets in the bundle (when above aggregation_threshold).
+ optional uint32 total_packets = 5;
+
+ // Duration between first and last packet (when above aggregation_threshold).
+ optional uint64 total_duration = 6;
+
+ // Total packet length in bytes (when above aggregation_threshold).
+ optional uint64 total_length = 7;
+}
+
+// An internable packet context.
+message NetworkPacketContext {
+ optional uint64 iid = 1;
+ optional NetworkPacketEvent ctx = 2;
+}
+
// End of protos/perfetto/trace/android/network_trace.proto
// Begin of protos/perfetto/trace/android/packages_list.proto
@@ -8376,7 +8435,7 @@ message EventName {
// emitted proactively in advance of referring to them in later packets.
//
// Next reserved id: 8 (up to 15).
-// Next id: 28.
+// Next id: 31.
message InternedData {
// TODO(eseckler): Replace iid fields inside interned messages with
// map<iid, message> type fields in InternedData.
@@ -8433,6 +8492,9 @@ message InternedData {
// This is is NOT the real address. This is to avoid disclosing KASLR through
// traces.
repeated InternedString kernel_symbols = 26;
+
+ // Interned packet context for android.network_packets.
+ repeated NetworkPacketContext packet_context = 30;
}
// End of protos/perfetto/trace/interned_data/interned_data.proto
@@ -10134,7 +10196,7 @@ message UiState {
// See the [Buffers and Dataflow](/docs/concepts/buffers.md) doc for details.
//
// Next reserved id: 14 (up to 15).
-// Next id: 89.
+// Next id: 93.
message TracePacket {
// The timestamp of the TracePacket.
// By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
@@ -10238,6 +10300,9 @@ message TracePacket {
// Represents a single packet sent or received by the network.
NetworkPacketEvent network_packet = 88;
+ // Represents one or more packets sent or received by the network.
+ NetworkPacketBundle network_packet_bundle = 92;
+
// This field is only used for testing.
// In previous versions of this proto this field had the id 268435455
// This caused many problems:
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index 2eb602aec..822d36012 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -88,7 +88,7 @@ package perfetto.protos;
// See the [Buffers and Dataflow](/docs/concepts/buffers.md) doc for details.
//
// Next reserved id: 14 (up to 15).
-// Next id: 89.
+// Next id: 93.
message TracePacket {
// The timestamp of the TracePacket.
// By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
@@ -192,6 +192,9 @@ message TracePacket {
// Represents a single packet sent or received by the network.
NetworkPacketEvent network_packet = 88;
+ // Represents one or more packets sent or received by the network.
+ NetworkPacketBundle network_packet_bundle = 92;
+
// This field is only used for testing.
// In previous versions of this proto this field had the id 268435455
// This caused many problems:
diff --git a/src/base/test/tmp_dir_tree.cc b/src/base/test/tmp_dir_tree.cc
index 9ed785f88..8b70ec0f2 100644
--- a/src/base/test/tmp_dir_tree.cc
+++ b/src/base/test/tmp_dir_tree.cc
@@ -44,12 +44,16 @@ void TmpDirTree::AddDir(const std::string& relative_path) {
void TmpDirTree::AddFile(const std::string& relative_path,
const std::string& content) {
- files_to_remove_.push(relative_path);
+ TrackFile(relative_path);
base::ScopedFile fd(base::OpenFile(AbsolutePath(relative_path),
O_WRONLY | O_CREAT | O_TRUNC, 0600));
PERFETTO_CHECK(base::WriteAll(fd.get(), content.c_str(), content.size()) ==
static_cast<ssize_t>(content.size()));
}
+void TmpDirTree::TrackFile(const std::string& relative_path) {
+ files_to_remove_.push(relative_path);
+}
+
} // namespace base
} // namespace perfetto
diff --git a/src/base/test/tmp_dir_tree.h b/src/base/test/tmp_dir_tree.h
index 6bb730c97..9b282f87a 100644
--- a/src/base/test/tmp_dir_tree.h
+++ b/src/base/test/tmp_dir_tree.h
@@ -47,6 +47,11 @@ class TmpDirTree {
// succeeds.
void AddFile(const std::string& relative_path, const std::string& content);
+ // Tells this object to remove `relative_path` on destruction. This is used
+ // for files created by the caller that still need to be removed on cleanup by
+ // this object.
+ void TrackFile(const std::string& relative_path);
+
private:
TmpDirTree(const TmpDirTree&) = delete;
TmpDirTree& operator=(const TmpDirTree&) = delete;
diff --git a/src/base/utils.cc b/src/base/utils.cc
index b4da80cb1..b72c269d6 100644
--- a/src/base/utils.cc
+++ b/src/base/utils.cc
@@ -172,6 +172,14 @@ void SetEnv(const std::string& key, const std::string& value) {
#endif
}
+void UnsetEnv(const std::string& key) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ PERFETTO_CHECK(::_putenv_s(key.c_str(), "") == 0);
+#else
+ PERFETTO_CHECK(::unsetenv(key.c_str()) == 0);
+#endif
+}
+
void Daemonize(std::function<int()> parent_cb) {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index ebf198ccb..25eb0c705 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -135,6 +135,21 @@ source_set("client_api_without_backends") {
}
}
+# Separate target because the embedder might not want this.
+source_set("integrationtests") {
+ testonly = true
+ deps = [
+ "../../gn:default_deps",
+ "../../gn:gtest_and_gmock",
+ "../../include/perfetto/ext/tracing/ipc",
+ "../../include/perfetto/tracing",
+ "../../protos/perfetto/trace:cpp",
+ "../base",
+ "../base:test_support",
+ ]
+ sources = [ "internal/tracing_muxer_impl_integrationtest.cc" ]
+}
+
perfetto_unittest_source_set("unittests") {
testonly = true
deps = [
diff --git a/src/tracing/internal/tracing_muxer_fake.cc b/src/tracing/internal/tracing_muxer_fake.cc
index a6700f325..07d8f67c2 100644
--- a/src/tracing/internal/tracing_muxer_fake.cc
+++ b/src/tracing/internal/tracing_muxer_fake.cc
@@ -85,5 +85,10 @@ void TracingMuxerFake::RegisterInterceptor(
FailUninitialized();
}
+void TracingMuxerFake::ActivateTriggers(const std::vector<std::string>&,
+ uint32_t) {
+ FailUninitialized();
+}
+
} // namespace internal
} // namespace perfetto
diff --git a/src/tracing/internal/tracing_muxer_fake.h b/src/tracing/internal/tracing_muxer_fake.h
index 78fc76960..e2bcd8ee9 100644
--- a/src/tracing/internal/tracing_muxer_fake.h
+++ b/src/tracing/internal/tracing_muxer_fake.h
@@ -67,6 +67,7 @@ class TracingMuxerFake : public TracingMuxer {
InterceptorFactory,
InterceptorBase::TLSFactory,
InterceptorBase::TracePacketCallback) override;
+ void ActivateTriggers(const std::vector<std::string>&, uint32_t) override;
private:
static TracingMuxerFake instance;
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index 876b90b0e..b1f650729 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -180,6 +180,7 @@ void TracingMuxerImpl::ProducerImpl::OnConnect() {
PERFETTO_DCHECK(!connected_);
connected_ = true;
muxer_->UpdateDataSourcesOnAllBackends();
+ SendOnConnectTriggers();
}
void TracingMuxerImpl::ProducerImpl::OnDisconnect() {
@@ -280,6 +281,22 @@ bool TracingMuxerImpl::ProducerImpl::SweepDeadServices() {
return dead_services_.empty();
}
+void TracingMuxerImpl::ProducerImpl::SendOnConnectTriggers() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ base::TimeMillis now = base::GetWallTimeMs();
+ std::vector<std::string> triggers;
+ while (!on_connect_triggers_.empty()) {
+ // Skip if we passed TTL.
+ if (on_connect_triggers_.front().second > now) {
+ triggers.push_back(std::move(on_connect_triggers_.front().first));
+ }
+ on_connect_triggers_.pop_front();
+ }
+ if (!triggers.empty()) {
+ service_->ActivateTriggers(triggers);
+ }
+}
+
// ----- End of TracingMuxerImpl::ProducerImpl methods.
// ----- Begin of TracingMuxerImpl::ConsumerImpl
@@ -894,6 +911,25 @@ void TracingMuxerImpl::RegisterInterceptor(
});
}
+void TracingMuxerImpl::ActivateTriggers(
+ const std::vector<std::string>& triggers,
+ uint32_t ttl_ms) {
+ base::TimeMillis expire_time =
+ base::GetWallTimeMs() + base::TimeMillis(ttl_ms);
+ task_runner_->PostTask([this, triggers, expire_time] {
+ for (RegisteredBackend& backend : backends_) {
+ if (backend.producer->connected_) {
+ backend.producer->service_->ActivateTriggers(triggers);
+ } else {
+ for (const std::string& trigger : triggers) {
+ backend.producer->on_connect_triggers_.emplace_back(trigger,
+ expire_time);
+ }
+ }
+ }
+ });
+}
+
// Called by the service of one of the backends.
void TracingMuxerImpl::SetupDataSource(TracingBackendId backend_id,
uint32_t backend_connection_id,
diff --git a/src/tracing/internal/tracing_muxer_impl.h b/src/tracing/internal/tracing_muxer_impl.h
index c545a0bc7..08a21115c 100644
--- a/src/tracing/internal/tracing_muxer_impl.h
+++ b/src/tracing/internal/tracing_muxer_impl.h
@@ -28,6 +28,7 @@
#include <memory>
#include <vector>
+#include "perfetto/base/time.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/thread_checker.h"
#include "perfetto/ext/tracing/core/basic_types.h"
@@ -116,6 +117,8 @@ class TracingMuxerImpl : public TracingMuxer {
InterceptorBase::TLSFactory,
InterceptorBase::TracePacketCallback) override;
+ void ActivateTriggers(const std::vector<std::string>&, uint32_t) override;
+
std::unique_ptr<TracingSession> CreateTracingSession(BackendType);
// Producer-side bookkeeping methods.
@@ -196,6 +199,7 @@ class TracingMuxerImpl : public TracingMuxer {
void ClearIncrementalState(const DataSourceInstanceID*, size_t) override;
bool SweepDeadServices();
+ void SendOnConnectTriggers();
PERFETTO_THREAD_CHECKER(thread_checker_)
TracingMuxerImpl* muxer_;
@@ -217,6 +221,10 @@ class TracingMuxerImpl : public TracingMuxer {
// that no longer have any writers (see SweepDeadServices).
std::list<std::shared_ptr<ProducerEndpoint>> dead_services_;
+ // Triggers that should be sent when the service connects (trigger_name,
+ // expiration).
+ std::list<std::pair<std::string, base::TimeMillis>> on_connect_triggers_;
+
// The currently active service endpoint is maintained as an atomic shared
// pointer so it won't get deleted from underneath threads that are creating
// trace writers. At any given time one endpoint can be shared (and thus
diff --git a/src/tracing/internal/tracing_muxer_impl_integrationtest.cc b/src/tracing/internal/tracing_muxer_impl_integrationtest.cc
new file mode 100644
index 000000000..e1cfbfd55
--- /dev/null
+++ b/src/tracing/internal/tracing_muxer_impl_integrationtest.cc
@@ -0,0 +1,172 @@
+#include "perfetto/tracing/tracing.h"
+
+#include <stdio.h>
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/thread_task_runner.h"
+#include "perfetto/ext/base/waitable_event.h"
+#include "perfetto/ext/tracing/ipc/service_ipc_host.h"
+#include "perfetto/tracing/backend_type.h"
+#include "protos/perfetto/config/trace_config.gen.h"
+#include "protos/perfetto/trace/trace.gen.h"
+#include "protos/perfetto/trace/trace_packet.gen.h"
+#include "protos/perfetto/trace/trigger.gen.h"
+#include "src/base/test/test_task_runner.h"
+#include "src/base/test/tmp_dir_tree.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace internal {
+namespace {
+
+using ::testing::NiceMock;
+using ::testing::NotNull;
+using ::testing::Property;
+
+class TracingMuxerImplIntegrationTest : public testing::Test {
+ protected:
+ // Sets the environment variable `name` to `value`. Restores it to the
+ // previous value when the test finishes.
+ void SetEnvVar(const char* name, const char* value) {
+ prev_state_.emplace();
+ EnvVar& var = prev_state_.top();
+ var.name = name;
+ const char* prev_value = getenv(name);
+ if (prev_value) {
+ var.value.emplace(prev_value);
+ }
+ base::SetEnv(name, value);
+ }
+
+ ~TracingMuxerImplIntegrationTest() {
+ perfetto::Tracing::ResetForTesting();
+ while (!prev_state_.empty()) {
+ const EnvVar& var = prev_state_.top();
+ if (var.value) {
+ base::SetEnv(var.name, *var.value);
+ } else {
+ base::UnsetEnv(var.name);
+ }
+ prev_state_.pop();
+ }
+ }
+
+ struct EnvVar {
+ const char* name;
+ base::Optional<std::string> value;
+ };
+ // Stores previous values of environment variables overridden by tests. We
+ // need to to this because some android integration tests need to talk to the
+ // real system tracing service and need the PERFETTO_PRODUCER_SOCK_NAME and
+ // PERFETTO_CONSUMER_SOCK_NAME to be set to their original value.
+ std::stack<EnvVar> prev_state_;
+};
+
+class TracingServiceThread {
+ public:
+ TracingServiceThread(const std::string& producer_socket,
+ const std::string& consumer_socket)
+ : runner_(base::ThreadTaskRunner::CreateAndStart("perfetto.svc")),
+ producer_socket_(producer_socket),
+ consumer_socket_(consumer_socket) {
+ runner_.PostTaskAndWaitForTesting([this]() {
+ svc_ = ServiceIPCHost::CreateInstance(&runner_);
+ bool res =
+ svc_->Start(producer_socket_.c_str(), consumer_socket_.c_str());
+ if (!res) {
+ PERFETTO_FATAL("Failed to start service listening on %s and %s",
+ producer_socket_.c_str(), consumer_socket_.c_str());
+ }
+ });
+ }
+
+ ~TracingServiceThread() {
+ runner_.PostTaskAndWaitForTesting([this]() { svc_.reset(); });
+ }
+
+ const std::string& producer_socket() const { return producer_socket_; }
+ const std::string& consumer_socket() const { return consumer_socket_; }
+
+ private:
+ base::ThreadTaskRunner runner_;
+
+ std::string producer_socket_;
+ std::string consumer_socket_;
+ std::unique_ptr<ServiceIPCHost> svc_;
+};
+
+TEST_F(TracingMuxerImplIntegrationTest, ActivateTriggers) {
+ base::TmpDirTree tmpdir_;
+
+ base::TestTaskRunner task_runner;
+
+ ASSERT_FALSE(perfetto::Tracing::IsInitialized());
+
+ tmpdir_.TrackFile("producer2.sock");
+ tmpdir_.TrackFile("consumer.sock");
+ TracingServiceThread tracing_service(tmpdir_.AbsolutePath("producer2.sock"),
+ tmpdir_.AbsolutePath("consumer.sock"));
+ // Instead of being a unix socket, producer.sock is a regular empty file.
+ tmpdir_.AddFile("producer.sock", "");
+
+ // Wrong producer socket: the producer won't connect yet, but the consumer
+ // will.
+ SetEnvVar("PERFETTO_PRODUCER_SOCK_NAME",
+ tmpdir_.AbsolutePath("producer.sock").c_str());
+ SetEnvVar("PERFETTO_CONSUMER_SOCK_NAME",
+ tmpdir_.AbsolutePath("consumer.sock").c_str());
+
+ TracingInitArgs args;
+ args.backends = perfetto::kSystemBackend;
+ perfetto::Tracing::Initialize(args);
+
+ // TracingMuxerImpl::ActivateTriggers will be called without the producer side
+ // of the service being connected. It should store the trigger for 10000ms.
+ perfetto::Tracing::ActivateTriggers({"trigger2", "trigger1"}, 10000);
+
+ perfetto::TraceConfig cfg;
+ cfg.add_buffers()->set_size_kb(1024);
+ perfetto::TraceConfig::TriggerConfig* tr_cfg = cfg.mutable_trigger_config();
+ tr_cfg->set_trigger_mode(perfetto::TraceConfig::TriggerConfig::STOP_TRACING);
+ tr_cfg->set_trigger_timeout_ms(10000);
+ perfetto::TraceConfig::TriggerConfig::Trigger* trigger =
+ tr_cfg->add_triggers();
+ trigger->set_name("trigger1");
+
+ std::unique_ptr<TracingSession> session =
+ perfetto::Tracing::NewTrace(perfetto::kSystemBackend);
+ base::WaitableEvent on_stop;
+ session->SetOnStopCallback([&on_stop] { on_stop.Notify(); });
+ session->Setup(cfg);
+
+ session->StartBlocking();
+
+ // Swap producer.sock and producer2.sock. Now the client should connect to the
+ // tracing service as a producer.
+ ASSERT_EQ(rename(tmpdir_.AbsolutePath("producer2.sock").c_str(),
+ tmpdir_.AbsolutePath("producer3.sock").c_str()),
+ 0);
+ ASSERT_EQ(rename(tmpdir_.AbsolutePath("producer.sock").c_str(),
+ tmpdir_.AbsolutePath("producer2.sock").c_str()),
+ 0);
+ ASSERT_EQ(rename(tmpdir_.AbsolutePath("producer3.sock").c_str(),
+ tmpdir_.AbsolutePath("producer.sock").c_str()),
+ 0);
+
+ on_stop.Wait();
+
+ std::vector<char> bytes = session->ReadTraceBlocking();
+ perfetto::protos::gen::Trace parsed_trace;
+ ASSERT_TRUE(parsed_trace.ParseFromArray(bytes.data(), bytes.size()));
+ EXPECT_THAT(
+ parsed_trace,
+ Property(&perfetto::protos::gen::Trace::packet,
+ Contains(Property(
+ &perfetto::protos::gen::TracePacket::trigger,
+ Property(&perfetto::protos::gen::Trigger::trigger_name,
+ "trigger1")))));
+}
+
+} // namespace
+} // namespace internal
+} // namespace perfetto
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 98963fc75..6b945f99e 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -91,6 +91,7 @@
#include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
#include "protos/perfetto/trace/track_event/track_descriptor.gen.h"
#include "protos/perfetto/trace/track_event/track_event.gen.h"
+#include "protos/perfetto/trace/trigger.gen.h"
// Events in categories starting with "dynamic" will use dynamic category
// lookup.
@@ -187,6 +188,7 @@ using perfetto::TracingInitArgs;
using perfetto::internal::TrackEventInternal;
using ::testing::_;
using ::testing::ContainerEq;
+using ::testing::Contains;
using ::testing::ElementsAre;
using ::testing::HasSubstr;
using ::testing::Invoke;
@@ -313,6 +315,8 @@ class MockTracingMuxer : public perfetto::internal::TracingMuxer {
perfetto::InterceptorBase::TLSFactory,
perfetto::InterceptorBase::TracePacketCallback) override {}
+ void ActivateTriggers(const std::vector<std::string>&, uint32_t) override {}
+
std::vector<DataSource> data_sources;
private:
@@ -452,11 +456,13 @@ class PerfettoApiTest : public ::testing::TestWithParam<perfetto::BackendType> {
g_test_tracing_policy->should_allow_consumer_connection = true;
// Start a fresh system service for this test, tearing down any previous
- // service that was running. If the system backend isn't supported, skip all
- // system backend tests.
- if (GetParam() == perfetto::kSystemBackend &&
- !perfetto::test::StartSystemService()) {
- GTEST_SKIP();
+ // service that was running.
+ if (GetParam() == perfetto::kSystemBackend) {
+ system_service_ = perfetto::test::SystemService::Start();
+ // If the system backend isn't supported, skip all system backend tests.
+ if (!system_service_.valid()) {
+ GTEST_SKIP();
+ }
}
EXPECT_FALSE(perfetto::Tracing::IsInitialized());
@@ -785,6 +791,7 @@ class PerfettoApiTest : public ::testing::TestWithParam<perfetto::BackendType> {
return 0;
}
+ perfetto::test::SystemService system_service_;
std::map<std::string, TestDataSourceHandle> data_sources_;
std::list<TestTracingSessionHandle> sessions_; // Needs stable pointers.
};
@@ -4952,10 +4959,50 @@ TEST_P(PerfettoApiTest, EmptyEvent) {
EXPECT_EQ(it, trace.packet().end());
}
+TEST_P(PerfettoApiTest, ActivateTriggers) {
+ // Create a new trace session without any data sources configured.
+ perfetto::TraceConfig cfg;
+ cfg.add_buffers()->set_size_kb(1024);
+ perfetto::TraceConfig::TriggerConfig* tr_cfg = cfg.mutable_trigger_config();
+ tr_cfg->set_trigger_mode(perfetto::TraceConfig::TriggerConfig::STOP_TRACING);
+ tr_cfg->set_trigger_timeout_ms(5000);
+ perfetto::TraceConfig::TriggerConfig::Trigger* trigger =
+ tr_cfg->add_triggers();
+ trigger->set_name("trigger1");
+ auto* tracing_session = NewTrace(cfg);
+ tracing_session->get()->StartBlocking();
+
+ perfetto::Tracing::ActivateTriggers({"trigger2", "trigger1"}, 10000);
+
+ bool done = false;
+ std::mutex mutex;
+ std::condition_variable cv;
+ std::unique_lock<std::mutex> lock(mutex);
+ tracing_session->get()->SetOnStopCallback([&]() {
+ std::lock_guard<std::mutex> inner_lock(mutex);
+ done = true;
+ cv.notify_one();
+ });
+ cv.wait(lock, [&done] { return done; });
+
+ std::vector<char> bytes = tracing_session->get()->ReadTraceBlocking();
+ perfetto::protos::gen::Trace parsed_trace;
+ ASSERT_TRUE(parsed_trace.ParseFromArray(bytes.data(), bytes.size()));
+ EXPECT_THAT(
+ parsed_trace,
+ Property(&perfetto::protos::gen::Trace::packet,
+ Contains(Property(
+ &perfetto::protos::gen::TracePacket::trigger,
+ Property(&perfetto::protos::gen::Trigger::trigger_name,
+ "trigger1")))));
+}
+
TEST(PerfettoApiInitTest, DisableSystemConsumer) {
g_test_tracing_policy->should_allow_consumer_connection = true;
- if (!perfetto::test::StartSystemService()) {
+ auto system_service = perfetto::test::SystemService::Start();
+ // If the system backend isn't supported, skip
+ if (!system_service.valid()) {
GTEST_SKIP();
}
diff --git a/src/tracing/test/api_test_support.cc b/src/tracing/test/api_test_support.cc
index a03135d5a..e67435c30 100644
--- a/src/tracing/test/api_test_support.cc
+++ b/src/tracing/test/api_test_support.cc
@@ -16,6 +16,7 @@
#include "src/tracing/test/api_test_support.h"
+#include "perfetto/base/compiler.h"
#include "perfetto/base/proc_utils.h"
#include "perfetto/base/time.h"
#include "perfetto/ext/base/temp_file.h"
@@ -45,38 +46,69 @@ class InProcessSystemService {
test_helper_.StartServiceIfRequired();
}
+ void CleanEnv() { test_helper_.CleanEnv(); }
+
private:
perfetto::base::TestTaskRunner task_runner_;
perfetto::TestHelper test_helper_;
};
-} // namespace
+InProcessSystemService* g_system_service = nullptr;
-bool StartSystemService() {
- static InProcessSystemService* system_service;
+} // namespace
+// static
+SystemService SystemService::Start() {
// If there already was a system service running, make sure the new one is
// running before tearing down the old one. This avoids a 1 second
// reconnection delay between each test since the connection to the new
// service succeeds immediately.
- std::unique_ptr<InProcessSystemService> old_service(system_service);
- system_service = new InProcessSystemService();
+ std::unique_ptr<InProcessSystemService> old_service(g_system_service);
+ if (old_service) {
+ old_service->CleanEnv();
+ }
+ g_system_service = new InProcessSystemService();
// Tear down the service at process exit to make sure temporary files get
// deleted.
static bool cleanup_registered;
if (!cleanup_registered) {
- atexit([] { delete system_service; });
+ atexit([] { delete g_system_service; });
cleanup_registered = true;
}
- return true;
+ SystemService ret;
+ ret.valid_ = true;
+ return ret;
+}
+
+void SystemService::Clean() {
+ if (valid_) {
+ if (g_system_service) {
+ // Does not really stop. We want to reuse the service in future tests. It
+ // is important to clean the environment variables, though.
+ g_system_service->CleanEnv();
+ }
+ }
+ valid_ = false;
}
#else // !PERFETTO_BUILDFLAG(PERFETTO_IPC)
-bool StartSystemService() {
- return false;
+// static
+SystemService SystemService::Start() {
+ return SystemService();
+}
+void SystemService::Clean() {
+ valid_ = false;
}
#endif // !PERFETTO_BUILDFLAG(PERFETTO_IPC)
+SystemService& SystemService::operator=(SystemService&& other) noexcept {
+ PERFETTO_CHECK(!valid_ || !other.valid_);
+ Clean();
+ valid_ = other.valid_;
+ other.valid_ = false;
+ return *this;
+}
+
int32_t GetCurrentProcessId() {
return static_cast<int32_t>(base::GetProcessId());
}
diff --git a/src/tracing/test/api_test_support.h b/src/tracing/test/api_test_support.h
index 33a7a5389..0fe4cf207 100644
--- a/src/tracing/test/api_test_support.h
+++ b/src/tracing/test/api_test_support.h
@@ -27,13 +27,37 @@
// IMPORTANT: This header must not pull any non-public perfetto header.
#include <stdint.h>
+
#include "perfetto/tracing.h"
namespace perfetto {
namespace test {
int32_t GetCurrentProcessId();
-bool StartSystemService();
+
+// RAII wrapper to start and stop an in process system service. Only one at a
+// time can be started.
+class SystemService {
+ public:
+ static SystemService Start();
+ SystemService() = default;
+ SystemService(SystemService&& other) noexcept { *this = std::move(other); }
+ SystemService& operator=(SystemService&&) noexcept;
+
+ ~SystemService() { Clean(); }
+
+ // Returns true if this SystemService has been started successfully and can be
+ // used.
+ bool valid() const { return valid_; }
+
+ void Clean();
+
+ private:
+ SystemService(const SystemService&) = delete;
+ SystemService& operator=(const SystemService&) = delete;
+ bool valid_ = false;
+};
+
void SyncProducers();
void SetBatchCommitsDuration(uint32_t batch_commits_duration_ms,
diff --git a/src/tracing/tracing.cc b/src/tracing/tracing.cc
index ab495cfa7..542bb26f3 100644
--- a/src/tracing/tracing.cc
+++ b/src/tracing/tracing.cc
@@ -84,6 +84,12 @@ std::unique_ptr<TracingSession> Tracing::NewTrace(BackendType backend) {
->CreateTracingSession(backend);
}
+// static
+void Tracing::ActivateTriggers(const std::vector<std::string>& triggers,
+ uint32_t ttl_ms) {
+ internal::TracingMuxer::Get()->ActivateTriggers(triggers, ttl_ms);
+}
+
// Can be called from any thread.
bool TracingSession::FlushBlocking(uint32_t timeout_ms) {
std::atomic<bool> flush_result;
diff --git a/test/cts/Android.bp b/test/cts/Android.bp
index 3eef756ef..d066b23eb 100644
--- a/test/cts/Android.bp
+++ b/test/cts/Android.bp
@@ -37,6 +37,7 @@ cc_test {
],
test_suites: [
"cts",
+ "mts",
"vts10",
"general-tests",
],
@@ -59,6 +60,7 @@ cc_test {
":CtsPerfettoReporterApp"
],
stl: "libc++_static",
+ min_sdk_version: "31",
defaults: [
"perfetto_defaults",
],
diff --git a/test/cts/AndroidTest.xml b/test/cts/AndroidTest.xml
index db14fe6ae..84e72bc66 100644
--- a/test/cts/AndroidTest.xml
+++ b/test/cts/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsPerfettoProducerApp.apk" />
@@ -44,4 +45,17 @@
<!-- test-timeout unit is ms -->
<option name="native-test-timeout" value="40000" />
</test>
+
+ <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
+ one of the Mainline modules below is present on the device used for testing. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <!-- ART Mainline Module (internal version). -->
+ <option name="mainline-module-package-name" value="com.google.android.art" />
+ <!-- ART Mainline Module (external (AOSP) version). -->
+ <option name="mainline-module-package-name" value="com.android.art" />
+ </object>
+
+ <!-- Only run tests if the device under test is SDK version 31 (Android 12)
+ or above, where ART is an updatable Mainline module. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
</configuration>
diff --git a/test/cts/heapprofd_java_test_cts.cc b/test/cts/heapprofd_java_test_cts.cc
index 7d8112819..6435f7601 100644
--- a/test/cts/heapprofd_java_test_cts.cc
+++ b/test/cts/heapprofd_java_test_cts.cc
@@ -20,6 +20,8 @@
#include <sys/wait.h>
#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/android_utils.h"
+#include "perfetto/ext/base/string_utils.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "src/base/test/test_task_runner.h"
#include "test/android_test_utils.h"
@@ -34,6 +36,22 @@
namespace perfetto {
namespace {
+// Even though ART is a mainline module, there are dependencies on perfetto for
+// OOM heap dumps to work correctly.
+bool SupportsOomHeapDump() {
+ auto sdk = base::StringToInt32(base::GetAndroidProp("ro.build.version.sdk"));
+ if (sdk && *sdk >= 34) {
+ PERFETTO_LOG("SDK supports OOME heap dumps");
+ return true;
+ }
+ if (base::GetAndroidProp("ro.build.version.codename") == "UpsideDownCake") {
+ PERFETTO_LOG("Codename supports OOME heap dumps");
+ return true;
+ }
+ PERFETTO_LOG("OOME heap dumps not supported");
+ return false;
+}
+
std::string RandomSessionName() {
std::random_device rd;
std::default_random_engine generator(rd());
@@ -89,6 +107,59 @@ std::vector<protos::gen::TracePacket> ProfileRuntime(std::string app_name) {
return helper.trace();
}
+std::vector<protos::gen::TracePacket> TriggerOomHeapDump(std::string app_name,
+ std::string heap_dump_target) {
+ base::TestTaskRunner task_runner;
+
+ // (re)start the target app's main activity
+ if (IsAppRunning(app_name)) {
+ StopApp(app_name, "old.app.stopped", &task_runner);
+ task_runner.RunUntilCheckpoint("old.app.stopped", 10000 /*ms*/);
+ }
+
+ // set up tracing
+ TestHelper helper(&task_runner);
+ helper.ConnectConsumer();
+ helper.WaitForConsumerConnect();
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(40 * 1024);
+ trace_config.set_unique_session_name(RandomSessionName().c_str());
+ trace_config.set_data_source_stop_timeout_ms(60000);
+
+ auto* trigger_config = trace_config.mutable_trigger_config();
+ trigger_config->set_trigger_mode(perfetto::protos::gen::TraceConfig::TriggerConfig::START_TRACING);
+ trigger_config->set_trigger_timeout_ms(60000);
+ auto* oom_trigger = trigger_config->add_triggers();
+ oom_trigger->set_name("com.android.telemetry.art-outofmemory");
+ oom_trigger->set_stop_delay_ms(10000);
+
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.java_hprof.oom");
+ ds_config->set_target_buffer(0);
+
+ protos::gen::JavaHprofConfig java_hprof_config;
+ java_hprof_config.add_process_cmdline(heap_dump_target.c_str());
+ ds_config->set_java_hprof_config_raw(java_hprof_config.SerializeAsString());
+
+ // start tracing
+ helper.StartTracing(trace_config);
+ StartAppActivity(app_name, "JavaOomActivity", "target.app.running", &task_runner,
+ /*delay_ms=*/100);
+ task_runner.RunUntilCheckpoint("target.app.running", 10000 /*ms*/);
+
+ if (SupportsOomHeapDump()) {
+ helper.WaitForTracingDisabled();
+ helper.ReadData();
+ helper.WaitForReadData();
+ }
+
+ PERFETTO_CHECK(IsAppRunning(app_name));
+ StopApp(app_name, "new.app.stopped", &task_runner);
+ task_runner.RunUntilCheckpoint("new.app.stopped", 10000 /*ms*/);
+ return helper.trace();
+}
+
void AssertGraphPresent(std::vector<protos::gen::TracePacket> packets) {
ASSERT_GT(packets.size(), 0u);
@@ -184,5 +255,37 @@ TEST(HeapprofdJavaCtsTest, DebuggableAppRuntimeByPid) {
AssertGraphPresent(packets);
}
+TEST(HeapprofdJavaCtsTest, DebuggableAppOom) {
+ std::string app_name = "android.perfetto.cts.app.debuggable";
+ const auto& packets = TriggerOomHeapDump(app_name, "*");
+ if (SupportsOomHeapDump()) {
+ AssertGraphPresent(packets);
+ }
+}
+
+TEST(HeapprofdJavaCtsTest, ProfileableAppOom) {
+ std::string app_name = "android.perfetto.cts.app.profileable";
+ const auto& packets = TriggerOomHeapDump(app_name, "*");
+ if (SupportsOomHeapDump()) {
+ AssertGraphPresent(packets);
+ }
+}
+
+TEST(HeapprofdJavaCtsTest, ReleaseAppOom) {
+ std::string app_name = "android.perfetto.cts.app.release";
+ const auto& packets = TriggerOomHeapDump(app_name, "*");
+ if (IsUserBuild()) {
+ AssertNoProfileContents(packets);
+ } else if (SupportsOomHeapDump()) {
+ AssertGraphPresent(packets);
+ }
+}
+
+TEST(HeapprofdJavaCtsTest, DebuggableAppOomNotSelected) {
+ std::string app_name = "android.perfetto.cts.app.debuggable";
+ const auto& packets = TriggerOomHeapDump(app_name, "not.this.app");
+ AssertNoProfileContents(packets);
+}
+
} // namespace
} // namespace perfetto
diff --git a/test/cts/producer/Android.bp b/test/cts/producer/Android.bp
index 1503a39ca..76d8a4e4b 100644
--- a/test/cts/producer/Android.bp
+++ b/test/cts/producer/Android.bp
@@ -33,6 +33,7 @@ android_test_helper_app {
compile_multilib: "both",
srcs: ["src/**/*.java"],
sdk_version: "current",
+ min_sdk_version: "31",
jni_libs: [
"libperfettocts_jni",
],
diff --git a/test/cts/reporter/Android.bp b/test/cts/reporter/Android.bp
index ab2633b56..39f2d8928 100644
--- a/test/cts/reporter/Android.bp
+++ b/test/cts/reporter/Android.bp
@@ -27,4 +27,5 @@ android_test_helper_app {
srcs: ["src/**/*.java"],
platform_apis: true,
privileged: true,
+ min_sdk_version: "31",
}
diff --git a/test/cts/test_apps/Android.bp b/test/cts/test_apps/Android.bp
index 1172be5b0..a29caf388 100644
--- a/test/cts/test_apps/Android.bp
+++ b/test/cts/test_apps/Android.bp
@@ -35,6 +35,7 @@ android_test_helper_app {
compile_multilib: "both",
srcs: ["src/**/*.java"],
sdk_version: "current",
+ min_sdk_version: "31",
jni_libs: [
"libperfettocts_native",
],
@@ -55,6 +56,7 @@ android_test_helper_app {
compile_multilib: "both",
srcs: ["src/**/*.java"],
sdk_version: "current",
+ min_sdk_version: "31",
jni_libs: [
"libperfettocts_native",
],
@@ -75,6 +77,7 @@ android_test_helper_app {
compile_multilib: "both",
srcs: ["src/**/*.java"],
sdk_version: "current",
+ min_sdk_version: "31",
jni_libs: [
"libperfettocts_native",
],
@@ -95,6 +98,7 @@ android_test_helper_app {
compile_multilib: "both",
srcs: ["src/**/*.java"],
sdk_version: "current",
+ min_sdk_version: "31",
jni_libs: [
"libperfettocts_native",
],
diff --git a/test/cts/test_apps/AndroidManifest_debuggable.xml b/test/cts/test_apps/AndroidManifest_debuggable.xml
index cb746c17e..291469e5a 100755
--- a/test/cts/test_apps/AndroidManifest_debuggable.xml
+++ b/test/cts/test_apps/AndroidManifest_debuggable.xml
@@ -58,5 +58,18 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
+ <activity
+ android:name="android.perfetto.cts.app.JavaOomActivity"
+ android:exported="true">
+ </activity>
+ <activity-alias
+ android:name="android.perfetto.cts.app.debuggable.JavaOomActivity"
+ android:targetActivity="android.perfetto.cts.app.JavaOomActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
</application>
</manifest>
diff --git a/test/cts/test_apps/AndroidManifest_profileable.xml b/test/cts/test_apps/AndroidManifest_profileable.xml
index bac8a6632..077fd959b 100755
--- a/test/cts/test_apps/AndroidManifest_profileable.xml
+++ b/test/cts/test_apps/AndroidManifest_profileable.xml
@@ -59,6 +59,19 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
+ <activity
+ android:name="android.perfetto.cts.app.JavaOomActivity"
+ android:exported="true">
+ </activity>
+ <activity-alias
+ android:name="android.perfetto.cts.app.profileable.JavaOomActivity"
+ android:targetActivity="android.perfetto.cts.app.JavaOomActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
</application>
</manifest>
diff --git a/test/cts/test_apps/AndroidManifest_release.xml b/test/cts/test_apps/AndroidManifest_release.xml
index 5bc0f5be3..417a5397c 100755
--- a/test/cts/test_apps/AndroidManifest_release.xml
+++ b/test/cts/test_apps/AndroidManifest_release.xml
@@ -58,5 +58,18 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
+ <activity
+ android:name="android.perfetto.cts.app.JavaOomActivity"
+ android:exported="true">
+ </activity>
+ <activity-alias
+ android:name="android.perfetto.cts.app.release.JavaOomActivity"
+ android:targetActivity="android.perfetto.cts.app.JavaOomActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
</application>
</manifest>
diff --git a/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java b/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java
new file mode 100644
index 000000000..f73c13523
--- /dev/null
+++ b/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+package android.perfetto.cts.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class JavaOomActivity extends Activity {
+ @Override
+ public void onCreate(Bundle state) {
+ super.onCreate(state);
+ new Thread(() -> {
+ try {
+ byte[] alloc = new byte[Integer.MAX_VALUE];
+ } catch (OutOfMemoryError e) {
+ }
+ }).start();
+ }
+}
diff --git a/test/test_helper.cc b/test/test_helper.cc
index 79c054a04..b3839d537 100644
--- a/test/test_helper.cc
+++ b/test/test_helper.cc
@@ -112,7 +112,7 @@ void TestHelper::OnTraceData(std::vector<TracePacket> packets, bool has_more) {
void TestHelper::StartServiceIfRequired() {
if (mode_ == Mode::kStartDaemons)
- service_thread_.Start();
+ env_cleaner_ = service_thread_.Start();
}
FakeProducer* TestHelper::ConnectFakeProducer() {
diff --git a/test/test_helper.h b/test/test_helper.h
index 7331c6441..69dee23ae 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -64,6 +64,54 @@ inline const char* GetTestProducerSockName() {
#endif
}
+// Captures the values of some environment variables when constructed and
+// restores them when destroyed.
+class TestEnvCleaner {
+ public:
+ TestEnvCleaner() {}
+ TestEnvCleaner(std::initializer_list<const char*> env_vars) {
+ prev_state_.reserve(env_vars.size());
+ for (const char* name : env_vars) {
+ prev_state_.emplace_back();
+ Var& var = prev_state_.back();
+ var.name = name;
+ const char* prev_value = getenv(name);
+ if (prev_value) {
+ var.value.emplace(prev_value);
+ }
+ }
+ }
+ ~TestEnvCleaner() { Clean(); }
+
+ TestEnvCleaner(const TestEnvCleaner&) = delete;
+ TestEnvCleaner(TestEnvCleaner&& obj) noexcept { *this = std::move(obj); }
+ TestEnvCleaner& operator=(const TestEnvCleaner&) = delete;
+ TestEnvCleaner& operator=(TestEnvCleaner&& obj) noexcept {
+ PERFETTO_CHECK(prev_state_.empty());
+ this->prev_state_ = std::move(obj.prev_state_);
+ obj.prev_state_.clear();
+ return *this;
+ }
+
+ void Clean() {
+ for (const Var& var : prev_state_) {
+ if (var.value) {
+ base::SetEnv(var.name, *var.value);
+ } else {
+ base::UnsetEnv(var.name);
+ }
+ }
+ prev_state_.clear();
+ }
+
+ private:
+ struct Var {
+ const char* name;
+ base::Optional<std::string> value;
+ };
+ std::vector<Var> prev_state_;
+};
+
// This is used only in daemon starting integrations tests.
class ServiceThread {
public:
@@ -77,7 +125,9 @@ class ServiceThread {
runner_->PostTaskAndWaitForTesting([this]() { svc_.reset(); });
}
- void Start() {
+ TestEnvCleaner Start() {
+ TestEnvCleaner env_cleaner(
+ {"PERFETTO_PRODUCER_SOCK_NAME", "PERFETTO_CONSUMER_SOCK_NAME"});
runner_ = base::ThreadTaskRunner::CreateAndStart("perfetto.svc");
runner_->PostTaskAndWaitForTesting([this]() {
svc_ = ServiceIPCHost::CreateInstance(runner_->get());
@@ -98,6 +148,7 @@ class ServiceThread {
producer_socket_.c_str(), consumer_socket_.c_str());
}
});
+ return env_cleaner;
}
base::ThreadTaskRunner* runner() { return runner_ ? &*runner_ : nullptr; }
@@ -293,6 +344,13 @@ class TestHelper : public Consumer {
}
const std::vector<protos::gen::TracePacket>& trace() { return trace_; }
+ // Some fixtures want to reuse a global TestHelper in different testcases
+ // without destroying and recreating it, but they still need to avoid
+ // polluting environment variables.
+ //
+ // This restores the previous environment variables.
+ void CleanEnv() { env_cleaner_.Clean(); }
+
private:
static uint64_t next_instance_num_;
uint64_t instance_num_;
@@ -315,6 +373,8 @@ class TestHelper : public Consumer {
ServiceThread service_thread_;
FakeProducerThread fake_producer_thread_;
+ TestEnvCleaner env_cleaner_;
+
std::unique_ptr<TracingService::ConsumerEndpoint> endpoint_; // Keep last.
};