1
2 /*
3 * Copyright (C) 2019 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include <stdlib.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21
22 #include <sys/system_properties.h>
23
24 #include "gtest/gtest.h"
25 #include "perfetto/base/logging.h"
26 #include "src/base/test/test_task_runner.h"
27 #include "test/test_helper.h"
28
29 namespace perfetto {
30 namespace {
31
32 // Size of individual (repeated) allocations done by the test apps (must be kept
33 // in sync with their sources).
34 constexpr uint64_t kTestSamplingInterval = 4096;
35 constexpr uint64_t kExpectedIndividualAllocSz = 4153;
36 // Tests rely on the sampling behaviour where allocations larger than the
37 // sampling interval are recorded at their actual size.
38 static_assert(kExpectedIndividualAllocSz > kTestSamplingInterval,
39 "kTestSamplingInterval invalid");
40
IsDebuggableBuild()41 bool IsDebuggableBuild() {
42 char buf[PROP_VALUE_MAX + 1] = {};
43 int ret = __system_property_get("ro.debuggable", buf);
44 PERFETTO_CHECK(ret >= 0);
45 std::string debuggable(buf);
46 if (debuggable == "1")
47 return true;
48 return false;
49 }
50
51 // note: cannot use gtest macros due to return type
IsAppRunning(const std::string & name)52 bool IsAppRunning(const std::string& name) {
53 std::string cmd = "pgrep -f " + name;
54 int retcode = system(cmd.c_str());
55 PERFETTO_CHECK(retcode >= 0);
56 int exit_status = WEXITSTATUS(retcode);
57 if (exit_status == 0)
58 return true;
59 if (exit_status == 1)
60 return false;
61 PERFETTO_FATAL("unexpected exit status from system(pgrep): %d", exit_status);
62 }
63
64 // invokes |callback| once the target app is in the desired state
PollRunState(bool desired_run_state,base::TestTaskRunner * task_runner,const std::string & name,std::function<void ()> callback)65 void PollRunState(bool desired_run_state,
66 base::TestTaskRunner* task_runner,
67 const std::string& name,
68 std::function<void()> callback) {
69 bool app_running = IsAppRunning(name);
70 if (app_running == desired_run_state) {
71 callback();
72 return;
73 }
74 task_runner->PostTask([desired_run_state, task_runner, name, callback] {
75 PollRunState(desired_run_state, task_runner, name, std::move(callback));
76 });
77 }
78
StartAppActivity(const std::string & app_name,const std::string & checkpoint_name,base::TestTaskRunner * task_runner,int delay_ms=1)79 void StartAppActivity(const std::string& app_name,
80 const std::string& checkpoint_name,
81 base::TestTaskRunner* task_runner,
82 int delay_ms = 1) {
83 std::string start_cmd = "am start " + app_name + "/.MainActivity";
84 int status = system(start_cmd.c_str());
85 ASSERT_TRUE(status >= 0 && WEXITSTATUS(status) == 0) << "status: " << status;
86
87 bool desired_run_state = true;
88 const auto checkpoint = task_runner->CreateCheckpoint(checkpoint_name);
89 task_runner->PostDelayedTask(
90 [desired_run_state, task_runner, app_name, checkpoint] {
91 PollRunState(desired_run_state, task_runner, app_name,
92 std::move(checkpoint));
93 },
94 delay_ms);
95 }
96
StopApp(const std::string & app_name,const std::string & checkpoint_name,base::TestTaskRunner * task_runner)97 void StopApp(const std::string& app_name,
98 const std::string& checkpoint_name,
99 base::TestTaskRunner* task_runner) {
100 std::string stop_cmd = "am force-stop " + app_name;
101 int status = system(stop_cmd.c_str());
102 ASSERT_TRUE(status >= 0 && WEXITSTATUS(status) == 0) << "status: " << status;
103
104 bool desired_run_state = false;
105 auto checkpoint = task_runner->CreateCheckpoint(checkpoint_name);
106 task_runner->PostTask([desired_run_state, task_runner, app_name, checkpoint] {
107 PollRunState(desired_run_state, task_runner, app_name,
108 std::move(checkpoint));
109 });
110 }
111
ProfileRuntime(std::string app_name)112 std::vector<protos::TracePacket> ProfileRuntime(std::string app_name) {
113 base::TestTaskRunner task_runner;
114
115 // (re)start the target app's main activity
116 if (IsAppRunning(app_name)) {
117 StopApp(app_name, "old.app.stopped", &task_runner);
118 task_runner.RunUntilCheckpoint("old.app.stopped", 1000 /*ms*/);
119 }
120 StartAppActivity(app_name, "target.app.running", &task_runner,
121 /*delay_ms=*/100);
122 task_runner.RunUntilCheckpoint("target.app.running", 1000 /*ms*/);
123
124 // set up tracing
125 TestHelper helper(&task_runner);
126 helper.ConnectConsumer();
127 helper.WaitForConsumerConnect();
128
129 TraceConfig trace_config;
130 trace_config.add_buffers()->set_size_kb(10 * 1024);
131 trace_config.set_duration_ms(2000);
132
133 auto* ds_config = trace_config.add_data_sources()->mutable_config();
134 ds_config->set_name("android.heapprofd");
135 ds_config->set_target_buffer(0);
136
137 auto* heapprofd_config = ds_config->mutable_heapprofd_config();
138 heapprofd_config->set_sampling_interval_bytes(kTestSamplingInterval);
139 *heapprofd_config->add_process_cmdline() = app_name.c_str();
140 heapprofd_config->set_all(false);
141
142 // start tracing
143 helper.StartTracing(trace_config);
144 helper.WaitForTracingDisabled(4000 /*ms*/);
145 helper.ReadData();
146 helper.WaitForReadData();
147
148 return helper.trace();
149 }
150
ProfileStartup(std::string app_name)151 std::vector<protos::TracePacket> ProfileStartup(std::string app_name) {
152 base::TestTaskRunner task_runner;
153
154 if (IsAppRunning(app_name)) {
155 StopApp(app_name, "old.app.stopped", &task_runner);
156 task_runner.RunUntilCheckpoint("old.app.stopped", 1000 /*ms*/);
157 }
158
159 // set up tracing
160 TestHelper helper(&task_runner);
161 helper.ConnectConsumer();
162 helper.WaitForConsumerConnect();
163
164 TraceConfig trace_config;
165 trace_config.add_buffers()->set_size_kb(10 * 1024);
166 trace_config.set_duration_ms(4000);
167
168 auto* ds_config = trace_config.add_data_sources()->mutable_config();
169 ds_config->set_name("android.heapprofd");
170 ds_config->set_target_buffer(0);
171
172 auto* heapprofd_config = ds_config->mutable_heapprofd_config();
173 heapprofd_config->set_sampling_interval_bytes(kTestSamplingInterval);
174 *heapprofd_config->add_process_cmdline() = app_name.c_str();
175 heapprofd_config->set_all(false);
176
177 // start tracing
178 helper.StartTracing(trace_config);
179
180 // start app
181 StartAppActivity(app_name, "target.app.running", &task_runner,
182 /*delay_ms=*/100);
183 task_runner.RunUntilCheckpoint("target.app.running", 2000 /*ms*/);
184
185 helper.WaitForTracingDisabled(8000 /*ms*/);
186 helper.ReadData();
187 helper.WaitForReadData();
188
189 return helper.trace();
190 }
191
AssertExpectedAllocationsPresent(std::vector<protos::TracePacket> packets)192 void AssertExpectedAllocationsPresent(
193 std::vector<protos::TracePacket> packets) {
194 ASSERT_GT(packets.size(), 0);
195
196 // TODO(rsavitski): assert particular stack frames once we clarify the
197 // expected behaviour of unwinding native libs within an apk.
198 // Until then, look for an allocation that is a multiple of the expected
199 // allocation size.
200 bool found_alloc = false;
201 for (const auto& packet : packets) {
202 for (const auto& proc_dump : packet.profile_packet().process_dumps()) {
203 for (const auto& sample : proc_dump.samples()) {
204 if (sample.self_allocated() > 0 &&
205 sample.self_allocated() % kExpectedIndividualAllocSz == 0) {
206 found_alloc = true;
207
208 EXPECT_TRUE(sample.self_freed() > 0 &&
209 sample.self_freed() % kExpectedIndividualAllocSz == 0)
210 << "self_freed: " << sample.self_freed();
211 }
212 }
213 }
214 }
215 ASSERT_TRUE(found_alloc);
216 }
217
AssertNoProfileContents(std::vector<protos::TracePacket> packets)218 void AssertNoProfileContents(std::vector<protos::TracePacket> packets) {
219 // If profile packets are present, they must be empty.
220 for (const auto& packet : packets) {
221 ASSERT_EQ(packet.profile_packet().process_dumps_size(), 0);
222 }
223 }
224
StopApp(std::string app_name)225 void StopApp(std::string app_name) {
226 std::string stop_cmd = "am force-stop " + app_name;
227 system(stop_cmd.c_str());
228 }
229
230 // TODO(b/118428762): look into unwinding issues on x86.
231 #if defined(__i386__) || defined(__x86_64__)
232 #define MAYBE_SKIP(x) DISABLED_##x
233 #else
234 #define MAYBE_SKIP(x) x
235 #endif
236
TEST(HeapprofdCtsTest,MAYBE_SKIP (DebuggableAppRuntime))237 TEST(HeapprofdCtsTest, MAYBE_SKIP(DebuggableAppRuntime)) {
238 std::string app_name = "android.perfetto.cts.app.debuggable";
239 const auto& packets = ProfileRuntime(app_name);
240 AssertExpectedAllocationsPresent(packets);
241 StopApp(app_name);
242 }
243
TEST(HeapprofdCtsTest,MAYBE_SKIP (DebuggableAppStartup))244 TEST(HeapprofdCtsTest, MAYBE_SKIP(DebuggableAppStartup)) {
245 std::string app_name = "android.perfetto.cts.app.debuggable";
246 const auto& packets = ProfileStartup(app_name);
247 AssertExpectedAllocationsPresent(packets);
248 StopApp(app_name);
249 }
250
TEST(HeapprofdCtsTest,MAYBE_SKIP (ReleaseAppRuntime))251 TEST(HeapprofdCtsTest, MAYBE_SKIP(ReleaseAppRuntime)) {
252 std::string app_name = "android.perfetto.cts.app.release";
253 const auto& packets = ProfileRuntime(app_name);
254
255 if (IsDebuggableBuild())
256 AssertExpectedAllocationsPresent(packets);
257 else
258 AssertNoProfileContents(packets);
259
260 StopApp(app_name);
261 }
262
TEST(HeapprofdCtsTest,MAYBE_SKIP (ReleaseAppStartup))263 TEST(HeapprofdCtsTest, MAYBE_SKIP(ReleaseAppStartup)) {
264 std::string app_name = "android.perfetto.cts.app.release";
265 const auto& packets = ProfileStartup(app_name);
266
267 if (IsDebuggableBuild())
268 AssertExpectedAllocationsPresent(packets);
269 else
270 AssertNoProfileContents(packets);
271
272 StopApp(app_name);
273 }
274
275 } // namespace
276 } // namespace perfetto
277