diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-03-30 05:39:26 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-03-30 05:39:26 +0000 |
commit | 00bdb00aaaaaece57e6d05d78fe70471fe21a865 (patch) | |
tree | f03afe17f29de2fc5e9a40469bd42aeb9f640e5e | |
parent | 973fd8d9e6c9eb4e51334640d34bc28d3048c7e3 (diff) | |
parent | 5ab99d1ec1f8661ff812fe5bdb855529f81a4c00 (diff) | |
download | perfetto-android13-mainline-cellbroadcast-release.tar.gz |
Snap for 9847922 from 5ab99d1ec1f8661ff812fe5bdb855529f81a4c00 to mainline-cellbroadcast-releaseaml_cbr_331810000android13-mainline-cellbroadcast-release
Change-Id: I31ff6bacdc57526ef02a84389e35ed9b5c78e2e7
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", @@ -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", @@ -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. }; |