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