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