xref: /aosp_15_r20/external/perfetto/test/cts/heapprofd_java_test_cts.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <stdlib.h>
18 #include <sys/system_properties.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 
22 #include "perfetto/base/logging.h"
23 #include "perfetto/ext/base/android_utils.h"
24 #include "perfetto/ext/base/string_utils.h"
25 #include "perfetto/tracing/core/data_source_config.h"
26 #include "src/base/test/test_task_runner.h"
27 #include "test/android_test_utils.h"
28 #include "test/gtest_and_gmock.h"
29 #include "test/test_helper.h"
30 
31 #include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
32 #include "protos/perfetto/trace/profiling/heap_graph.gen.h"
33 #include "protos/perfetto/trace/profiling/profile_common.gen.h"
34 #include "protos/perfetto/trace/trace_packet.gen.h"
35 
36 namespace perfetto {
37 namespace {
38 
39 // Even though ART is a mainline module, there are dependencies on perfetto for
40 // OOM heap dumps to work correctly.
SupportsOomHeapDump()41 bool SupportsOomHeapDump() {
42   auto sdk = base::StringToInt32(base::GetAndroidProp("ro.build.version.sdk"));
43   if (sdk && *sdk >= 34) {
44     PERFETTO_LOG("SDK supports OOME heap dumps");
45     return true;
46   }
47   if (base::GetAndroidProp("ro.build.version.codename") == "UpsideDownCake") {
48     PERFETTO_LOG("Codename supports OOME heap dumps");
49     return true;
50   }
51   PERFETTO_LOG("OOME heap dumps not supported");
52   return false;
53 }
54 
RandomSessionName()55 std::string RandomSessionName() {
56   std::random_device rd;
57   std::default_random_engine generator(rd());
58   std::uniform_int_distribution<> distribution('a', 'z');
59 
60   constexpr size_t kSessionNameLen = 20;
61   std::string result(kSessionNameLen, '\0');
62   for (size_t i = 0; i < kSessionNameLen; ++i)
63     result[i] = static_cast<char>(distribution(generator));
64   return result;
65 }
66 
ProfileRuntime(std::string app_name)67 std::vector<protos::gen::TracePacket> ProfileRuntime(std::string app_name) {
68   base::TestTaskRunner task_runner;
69 
70   // (re)start the target app's main activity
71   if (IsAppRunning(app_name)) {
72     StopApp(app_name, "old.app.stopped", &task_runner);
73     task_runner.RunUntilCheckpoint("old.app.stopped", 10000 /*ms*/);
74   }
75   StartAppActivity(app_name, "NoopActivity", "target.app.running", &task_runner,
76                    /*delay_ms=*/100);
77   task_runner.RunUntilCheckpoint("target.app.running", 10000 /*ms*/);
78   // If we try to dump too early in app initialization, we sometimes deadlock.
79   sleep(1);
80 
81   // set up tracing
82   TestHelper helper(&task_runner);
83   helper.ConnectConsumer();
84   helper.WaitForConsumerConnect();
85 
86   TraceConfig trace_config;
87   trace_config.add_buffers()->set_size_kb(40 * 1024);
88   trace_config.set_duration_ms(3000);
89   trace_config.set_data_source_stop_timeout_ms(20000);
90   trace_config.set_unique_session_name(RandomSessionName().c_str());
91 
92   auto* ds_config = trace_config.add_data_sources()->mutable_config();
93   ds_config->set_name("android.java_hprof");
94   ds_config->set_target_buffer(0);
95 
96   protos::gen::JavaHprofConfig java_hprof_config;
97   java_hprof_config.add_process_cmdline(app_name.c_str());
98   ds_config->set_java_hprof_config_raw(java_hprof_config.SerializeAsString());
99 
100   // start tracing
101   helper.StartTracing(trace_config);
102   helper.WaitForTracingDisabled();
103   helper.ReadData();
104   helper.WaitForReadData();
105   PERFETTO_CHECK(IsAppRunning(app_name));
106   StopApp(app_name, "new.app.stopped", &task_runner);
107   task_runner.RunUntilCheckpoint("new.app.stopped", 10000 /*ms*/);
108   return helper.trace();
109 }
110 
TriggerOomHeapDump(std::string app_name,std::string heap_dump_target)111 std::vector<protos::gen::TracePacket> TriggerOomHeapDump(
112     std::string app_name,
113     std::string heap_dump_target) {
114   base::TestTaskRunner task_runner;
115 
116   // (re)start the target app's main activity
117   if (IsAppRunning(app_name)) {
118     StopApp(app_name, "old.app.stopped", &task_runner);
119     task_runner.RunUntilCheckpoint("old.app.stopped", 10000 /*ms*/);
120   }
121 
122   // set up tracing
123   TestHelper helper(&task_runner);
124   helper.ConnectConsumer();
125   helper.WaitForConsumerConnect();
126 
127   TraceConfig trace_config;
128   trace_config.add_buffers()->set_size_kb(40 * 1024);
129   trace_config.set_unique_session_name(RandomSessionName().c_str());
130   trace_config.set_data_source_stop_timeout_ms(60000);
131 
132   auto* trigger_config = trace_config.mutable_trigger_config();
133   trigger_config->set_trigger_mode(
134       perfetto::protos::gen::TraceConfig::TriggerConfig::START_TRACING);
135   trigger_config->set_trigger_timeout_ms(60000);
136   auto* oom_trigger = trigger_config->add_triggers();
137   oom_trigger->set_name("com.android.telemetry.art-outofmemory");
138   oom_trigger->set_stop_delay_ms(1000);
139 
140   auto* ds_config = trace_config.add_data_sources()->mutable_config();
141   ds_config->set_name("android.java_hprof.oom");
142   ds_config->set_target_buffer(0);
143 
144   protos::gen::JavaHprofConfig java_hprof_config;
145   java_hprof_config.add_process_cmdline(heap_dump_target.c_str());
146   ds_config->set_java_hprof_config_raw(java_hprof_config.SerializeAsString());
147 
148   // start tracing
149   helper.StartTracing(trace_config);
150   StartAppActivity(app_name, "JavaOomActivity", "target.app.running",
151                    &task_runner,
152                    /*delay_ms=*/100);
153   task_runner.RunUntilCheckpoint("target.app.running", 10000 /*ms*/);
154 
155   if (SupportsOomHeapDump()) {
156     helper.WaitForTracingDisabled();
157     helper.ReadData();
158     helper.WaitForReadData();
159   }
160 
161   PERFETTO_CHECK(IsAppRunning(app_name));
162   StopApp(app_name, "new.app.stopped", &task_runner);
163   task_runner.RunUntilCheckpoint("new.app.stopped", 10000 /*ms*/);
164   return helper.trace();
165 }
166 
AssertGraphPresent(std::vector<protos::gen::TracePacket> packets)167 void AssertGraphPresent(std::vector<protos::gen::TracePacket> packets) {
168   ASSERT_GT(packets.size(), 0u);
169 
170   size_t objects = 0;
171   size_t roots = 0;
172   for (const auto& packet : packets) {
173     objects += static_cast<size_t>(packet.heap_graph().objects_size());
174     roots += static_cast<size_t>(packet.heap_graph().roots_size());
175   }
176   ASSERT_GT(objects, 0u);
177   ASSERT_GT(roots, 0u);
178 }
179 
AssertNoProfileContents(std::vector<protos::gen::TracePacket> packets)180 void AssertNoProfileContents(std::vector<protos::gen::TracePacket> packets) {
181   // If profile packets are present, they must be empty.
182   for (const auto& packet : packets) {
183     ASSERT_EQ(packet.heap_graph().roots_size(), 0);
184     ASSERT_EQ(packet.heap_graph().objects_size(), 0);
185     ASSERT_EQ(packet.heap_graph().types_size(), 0);
186     ASSERT_EQ(packet.heap_graph().field_names_size(), 0);
187   }
188 }
189 
TEST(HeapprofdJavaCtsTest,DebuggableAppRuntime)190 TEST(HeapprofdJavaCtsTest, DebuggableAppRuntime) {
191   std::string app_name = "android.perfetto.cts.app.debuggable";
192   const auto& packets = ProfileRuntime(app_name);
193   AssertGraphPresent(packets);
194 }
195 
TEST(HeapprofdJavaCtsTest,ProfileableAppRuntime)196 TEST(HeapprofdJavaCtsTest, ProfileableAppRuntime) {
197   std::string app_name = "android.perfetto.cts.app.profileable";
198   const auto& packets = ProfileRuntime(app_name);
199   AssertGraphPresent(packets);
200 }
201 
TEST(HeapprofdJavaCtsTest,ReleaseAppRuntime)202 TEST(HeapprofdJavaCtsTest, ReleaseAppRuntime) {
203   std::string app_name = "android.perfetto.cts.app.release";
204   const auto& packets = ProfileRuntime(app_name);
205 
206   if (!IsUserBuild())
207     AssertGraphPresent(packets);
208   else
209     AssertNoProfileContents(packets);
210 }
211 
TEST(HeapprofdJavaCtsTest,DebuggableAppRuntimeByPid)212 TEST(HeapprofdJavaCtsTest, DebuggableAppRuntimeByPid) {
213   std::string app_name = "android.perfetto.cts.app.debuggable";
214 
215   base::TestTaskRunner task_runner;
216 
217   // (re)start the target app's main activity
218   if (IsAppRunning(app_name)) {
219     StopApp(app_name, "old.app.stopped", &task_runner);
220     task_runner.RunUntilCheckpoint("old.app.stopped", 10000 /*ms*/);
221   }
222   StartAppActivity(app_name, "NoopActivity", "target.app.running", &task_runner,
223                    /*delay_ms=*/100);
224   task_runner.RunUntilCheckpoint("target.app.running", 10000 /*ms*/);
225   // If we try to dump too early in app initialization, we sometimes deadlock.
226   sleep(1);
227 
228   int target_pid = PidForProcessName(app_name);
229   ASSERT_NE(target_pid, -1);
230 
231   // set up tracing
232   TestHelper helper(&task_runner);
233   helper.ConnectConsumer();
234   helper.WaitForConsumerConnect();
235 
236   TraceConfig trace_config;
237   trace_config.add_buffers()->set_size_kb(40 * 1024);
238   trace_config.set_duration_ms(3000);
239   trace_config.set_data_source_stop_timeout_ms(20000);
240   trace_config.set_unique_session_name(RandomSessionName().c_str());
241 
242   auto* ds_config = trace_config.add_data_sources()->mutable_config();
243   ds_config->set_name("android.java_hprof");
244   ds_config->set_target_buffer(0);
245 
246   protos::gen::JavaHprofConfig java_hprof_config;
247   java_hprof_config.add_pid(static_cast<uint64_t>(target_pid));
248   ds_config->set_java_hprof_config_raw(java_hprof_config.SerializeAsString());
249 
250   // start tracing
251   helper.StartTracing(trace_config);
252   helper.WaitForTracingDisabled();
253   helper.ReadData();
254   helper.WaitForReadData();
255   PERFETTO_CHECK(IsAppRunning(app_name));
256   StopApp(app_name, "new.app.stopped", &task_runner);
257   task_runner.RunUntilCheckpoint("new.app.stopped", 10000 /*ms*/);
258 
259   const auto& packets = helper.trace();
260   AssertGraphPresent(packets);
261 }
262 
TEST(HeapprofdJavaCtsTest,DebuggableAppOom)263 TEST(HeapprofdJavaCtsTest, DebuggableAppOom) {
264   std::string app_name = "android.perfetto.cts.app.debuggable";
265   const auto& packets = TriggerOomHeapDump(app_name, "*");
266   if (SupportsOomHeapDump()) {
267     AssertGraphPresent(packets);
268   }
269 }
270 
TEST(HeapprofdJavaCtsTest,ProfileableAppOom)271 TEST(HeapprofdJavaCtsTest, ProfileableAppOom) {
272   std::string app_name = "android.perfetto.cts.app.profileable";
273   const auto& packets = TriggerOomHeapDump(app_name, "*");
274   if (SupportsOomHeapDump()) {
275     AssertGraphPresent(packets);
276   }
277 }
278 
TEST(HeapprofdJavaCtsTest,ReleaseAppOom)279 TEST(HeapprofdJavaCtsTest, ReleaseAppOom) {
280   std::string app_name = "android.perfetto.cts.app.release";
281   const auto& packets = TriggerOomHeapDump(app_name, "*");
282   if (IsUserBuild()) {
283     AssertNoProfileContents(packets);
284   } else if (SupportsOomHeapDump()) {
285     AssertGraphPresent(packets);
286   }
287 }
288 
TEST(HeapprofdJavaCtsTest,DebuggableAppOomNotSelected)289 TEST(HeapprofdJavaCtsTest, DebuggableAppOomNotSelected) {
290   std::string app_name = "android.perfetto.cts.app.debuggable";
291   const auto& packets = TriggerOomHeapDump(app_name, "not.this.app");
292   AssertNoProfileContents(packets);
293 }
294 
295 }  // namespace
296 }  // namespace perfetto
297