1 /*
2 * Copyright (C) 2015 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 <gtest/gtest.h>
18
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #if defined(__BIONIC__)
22 #include <android-base/properties.h>
23 #endif
24
25 #include <atomic>
26 #include <chrono>
27 #include <thread>
28 #include <unordered_map>
29
30 #include <android-base/file.h>
31 #include <android-base/logging.h>
32 #include <android-base/stringprintf.h>
33
34 #include "environment.h"
35 #include "event_attr.h"
36 #include "event_fd.h"
37 #include "event_type.h"
38 #include "utils.h"
39
40 using namespace simpleperf;
41
42 static auto test_duration_for_long_tests = std::chrono::seconds(120);
43 static auto cpu_hotplug_interval = std::chrono::microseconds(1000);
44 static bool verbose_mode = false;
45
46 #if defined(__BIONIC__)
47 class ScopedMpdecisionKiller {
48 public:
ScopedMpdecisionKiller()49 ScopedMpdecisionKiller() {
50 have_mpdecision_ = IsMpdecisionRunning();
51 if (have_mpdecision_) {
52 DisableMpdecision();
53 }
54 }
55
~ScopedMpdecisionKiller()56 ~ScopedMpdecisionKiller() {
57 if (have_mpdecision_) {
58 EnableMpdecision();
59 }
60 }
61
62 private:
IsMpdecisionRunning()63 bool IsMpdecisionRunning() {
64 std::string value = android::base::GetProperty("init.svc.mpdecision", "");
65 if (value.empty() || value.find("stopped") != std::string::npos) {
66 return false;
67 }
68 return true;
69 }
70
DisableMpdecision()71 void DisableMpdecision() {
72 CHECK(android::base::SetProperty("ctl.stop", "mpdecision"));
73 // Need to wait until mpdecision is actually stopped.
74 std::this_thread::sleep_for(std::chrono::milliseconds(500));
75 CHECK(!IsMpdecisionRunning());
76 }
77
EnableMpdecision()78 void EnableMpdecision() {
79 CHECK(android::base::SetProperty("ctl.start", "mpdecision"));
80 std::this_thread::sleep_for(std::chrono::milliseconds(500));
81 CHECK(IsMpdecisionRunning());
82 }
83
84 bool have_mpdecision_;
85 };
86 #else
87 class ScopedMpdecisionKiller {
88 public:
ScopedMpdecisionKiller()89 ScopedMpdecisionKiller() {}
90 };
91 #endif
92
IsCpuOnline(int cpu,bool * has_error)93 static bool IsCpuOnline(int cpu, bool* has_error) {
94 std::string filename = android::base::StringPrintf("/sys/devices/system/cpu/cpu%d/online", cpu);
95 std::string content;
96 bool ret = android::base::ReadFileToString(filename, &content);
97 if (!ret) {
98 PLOG(ERROR) << "failed to read file " << filename;
99 *has_error = true;
100 return false;
101 }
102 *has_error = false;
103 return (content.find('1') != std::string::npos);
104 }
105
SetCpuOnline(int cpu,bool online)106 static bool SetCpuOnline(int cpu, bool online) {
107 bool has_error;
108 bool ret = IsCpuOnline(cpu, &has_error);
109 if (has_error) {
110 return false;
111 }
112 if (ret == online) {
113 return true;
114 }
115 std::string filename = android::base::StringPrintf("/sys/devices/system/cpu/cpu%d/online", cpu);
116 std::string content = online ? "1" : "0";
117 ret = android::base::WriteStringToFile(content, filename);
118 if (!ret) {
119 ret = IsCpuOnline(cpu, &has_error);
120 if (has_error) {
121 return false;
122 }
123 if (online == ret) {
124 return true;
125 }
126 PLOG(ERROR) << "failed to write " << content << " to " << filename;
127 return false;
128 }
129 // Kernel needs time to offline/online cpus, so use a loop to wait here.
130 size_t retry_count = 0;
131 while (true) {
132 ret = IsCpuOnline(cpu, &has_error);
133 if (has_error) {
134 return false;
135 }
136 if (ret == online) {
137 break;
138 }
139 LOG(ERROR) << "reading cpu retry count = " << retry_count << ", requested = " << online
140 << ", real = " << ret;
141 if (++retry_count == 10000) {
142 LOG(ERROR) << "setting cpu " << cpu << (online ? " online" : " offline")
143 << " seems not to take effect";
144 return false;
145 }
146 std::this_thread::sleep_for(std::chrono::milliseconds(1));
147 }
148 return true;
149 }
150
GetCpuCount()151 static int GetCpuCount() {
152 return static_cast<int>(sysconf(_SC_NPROCESSORS_CONF));
153 }
154
155 class CpuOnlineRestorer {
156 public:
CpuOnlineRestorer()157 CpuOnlineRestorer() {
158 for (int cpu = 1; cpu < GetCpuCount(); ++cpu) {
159 bool has_error;
160 bool ret = IsCpuOnline(cpu, &has_error);
161 if (has_error) {
162 continue;
163 }
164 online_map_[cpu] = ret;
165 }
166 }
167
~CpuOnlineRestorer()168 ~CpuOnlineRestorer() {
169 for (const auto& pair : online_map_) {
170 SetCpuOnline(pair.first, pair.second);
171 }
172 }
173
174 private:
175 std::unordered_map<int, bool> online_map_;
176 };
177
FindAHotpluggableCpu(int * hotpluggable_cpu)178 bool FindAHotpluggableCpu(int* hotpluggable_cpu) {
179 if (!IsRoot()) {
180 GTEST_LOG_(INFO) << "This test needs root privilege to hotplug cpu.";
181 return false;
182 }
183 for (int cpu = 1; cpu < GetCpuCount(); ++cpu) {
184 bool has_error;
185 bool online = IsCpuOnline(cpu, &has_error);
186 if (has_error) {
187 continue;
188 }
189 if (SetCpuOnline(cpu, !online)) {
190 *hotpluggable_cpu = cpu;
191 return true;
192 }
193 }
194 GTEST_LOG_(INFO) << "There is no hotpluggable cpu.";
195 return false;
196 }
197
198 struct CpuToggleThreadArg {
199 int toggle_cpu;
200 std::atomic<bool> end_flag;
201 std::atomic<bool> cpu_hotplug_failed;
202
CpuToggleThreadArgCpuToggleThreadArg203 CpuToggleThreadArg(int cpu) : toggle_cpu(cpu), end_flag(false), cpu_hotplug_failed(false) {}
204 };
205
CpuToggleThread(CpuToggleThreadArg * arg)206 static void CpuToggleThread(CpuToggleThreadArg* arg) {
207 while (!arg->end_flag) {
208 if (!SetCpuOnline(arg->toggle_cpu, true)) {
209 arg->cpu_hotplug_failed = true;
210 break;
211 }
212 std::this_thread::sleep_for(cpu_hotplug_interval);
213 if (!SetCpuOnline(arg->toggle_cpu, false)) {
214 arg->cpu_hotplug_failed = true;
215 break;
216 }
217 std::this_thread::sleep_for(cpu_hotplug_interval);
218 }
219 }
220
221 // http://b/25193162.
TEST(cpu_offline,offline_while_recording)222 TEST(cpu_offline, offline_while_recording) {
223 ScopedMpdecisionKiller scoped_mpdecision_killer;
224 CpuOnlineRestorer cpuonline_restorer;
225 if (GetCpuCount() == 1) {
226 GTEST_LOG_(INFO) << "This test does nothing, because there is only one cpu in the system.";
227 return;
228 }
229 // Start cpu hotpluger.
230 int test_cpu;
231 if (!FindAHotpluggableCpu(&test_cpu)) {
232 return;
233 }
234 CpuToggleThreadArg cpu_toggle_arg(test_cpu);
235 std::thread cpu_toggle_thread(CpuToggleThread, &cpu_toggle_arg);
236
237 std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
238 ASSERT_TRUE(event_type_modifier != nullptr);
239 perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
240 attr.disabled = 0;
241 attr.enable_on_exec = 0;
242
243 auto start_time = std::chrono::steady_clock::now();
244 auto cur_time = start_time;
245 auto end_time = std::chrono::steady_clock::now() + test_duration_for_long_tests;
246 auto report_step = std::chrono::seconds(15);
247 size_t iterations = 0;
248
249 while (cur_time < end_time && !cpu_toggle_arg.cpu_hotplug_failed) {
250 if (cur_time + report_step < std::chrono::steady_clock::now()) {
251 // Report test time.
252 auto diff = std::chrono::duration_cast<std::chrono::seconds>(
253 std::chrono::steady_clock::now() - start_time);
254 if (verbose_mode) {
255 GTEST_LOG_(INFO) << "Have Tested " << (diff.count() / 60.0) << " minutes.";
256 }
257 cur_time = std::chrono::steady_clock::now();
258 }
259
260 std::unique_ptr<EventFd> event_fd =
261 EventFd::OpenEventFile(attr, -1, test_cpu, nullptr, event_type_modifier->name, false);
262 if (event_fd == nullptr) {
263 // Failed to open because the test_cpu is offline.
264 continue;
265 }
266 iterations++;
267 if (verbose_mode) {
268 GTEST_LOG_(INFO) << "Test offline while recording for " << iterations << " times.";
269 }
270 }
271 if (cpu_toggle_arg.cpu_hotplug_failed) {
272 GTEST_LOG_(INFO) << "Test ends because of cpu hotplug failure.";
273 }
274 cpu_toggle_arg.end_flag = true;
275 cpu_toggle_thread.join();
276 }
277
278 // http://b/25193162.
TEST(cpu_offline,offline_while_ioctl_enable)279 TEST(cpu_offline, offline_while_ioctl_enable) {
280 ScopedMpdecisionKiller scoped_mpdecision_killer;
281 CpuOnlineRestorer cpuonline_restorer;
282 if (GetCpuCount() == 1) {
283 GTEST_LOG_(INFO) << "This test does nothing, because there is only one cpu in the system.";
284 return;
285 }
286 // Start cpu hotpluger.
287 int test_cpu;
288 if (!FindAHotpluggableCpu(&test_cpu)) {
289 return;
290 }
291 CpuToggleThreadArg cpu_toggle_arg(test_cpu);
292 std::thread cpu_toggle_thread(CpuToggleThread, &cpu_toggle_arg);
293
294 std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
295 ASSERT_TRUE(event_type_modifier != nullptr);
296 perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
297 attr.disabled = 1;
298 attr.enable_on_exec = 0;
299
300 auto start_time = std::chrono::steady_clock::now();
301 auto cur_time = start_time;
302 auto end_time = std::chrono::steady_clock::now() + test_duration_for_long_tests;
303 auto report_step = std::chrono::seconds(15);
304 size_t iterations = 0;
305
306 while (cur_time < end_time && !cpu_toggle_arg.cpu_hotplug_failed) {
307 if (cur_time + report_step < std::chrono::steady_clock::now()) {
308 // Report test time.
309 auto diff = std::chrono::duration_cast<std::chrono::seconds>(
310 std::chrono::steady_clock::now() - start_time);
311 if (verbose_mode) {
312 GTEST_LOG_(INFO) << "Have Tested " << (diff.count() / 60.0) << " minutes.";
313 }
314 cur_time = std::chrono::steady_clock::now();
315 }
316 std::unique_ptr<EventFd> event_fd =
317 EventFd::OpenEventFile(attr, -1, test_cpu, nullptr, event_type_modifier->name, false);
318 if (event_fd == nullptr) {
319 // Failed to open because the test_cpu is offline.
320 continue;
321 }
322 // Wait a little for the event to be installed on test_cpu's perf context.
323 std::this_thread::sleep_for(std::chrono::milliseconds(1));
324 ASSERT_TRUE(event_fd->SetEnableEvent(true));
325 iterations++;
326 if (verbose_mode) {
327 GTEST_LOG_(INFO) << "Test offline while ioctl(PERF_EVENT_IOC_ENABLE) for " << iterations
328 << " times.";
329 }
330 }
331 if (cpu_toggle_arg.cpu_hotplug_failed) {
332 GTEST_LOG_(INFO) << "Test ends because of cpu hotplug failure.";
333 }
334 cpu_toggle_arg.end_flag = true;
335 cpu_toggle_thread.join();
336 }
337
338 struct CpuSpinThreadArg {
339 int spin_cpu;
340 std::atomic<pid_t> tid;
341 std::atomic<bool> end_flag;
342 };
343
CpuSpinThread(CpuSpinThreadArg * arg)344 static void CpuSpinThread(CpuSpinThreadArg* arg) {
345 arg->tid = gettid();
346 while (!arg->end_flag) {
347 cpu_set_t mask;
348 CPU_ZERO(&mask);
349 CPU_SET(arg->spin_cpu, &mask);
350 // If toggle_cpu is offline, setaffinity fails. So call it in a loop to
351 // make sure current thread mostly runs on toggle_cpu.
352 sched_setaffinity(arg->tid, sizeof(mask), &mask);
353 }
354 }
355
356 // http://b/28086229.
TEST(cpu_offline,offline_while_user_process_profiling)357 TEST(cpu_offline, offline_while_user_process_profiling) {
358 ScopedMpdecisionKiller scoped_mpdecision_killer;
359 CpuOnlineRestorer cpuonline_restorer;
360 // Start cpu hotpluger.
361 int test_cpu;
362 if (!FindAHotpluggableCpu(&test_cpu)) {
363 return;
364 }
365 CpuToggleThreadArg cpu_toggle_arg(test_cpu);
366 std::thread cpu_toggle_thread(CpuToggleThread, &cpu_toggle_arg);
367
368 // Start cpu spinner.
369 CpuSpinThreadArg cpu_spin_arg;
370 cpu_spin_arg.spin_cpu = test_cpu;
371 cpu_spin_arg.tid = 0;
372 cpu_spin_arg.end_flag = false;
373 std::thread cpu_spin_thread(CpuSpinThread, &cpu_spin_arg);
374 while (cpu_spin_arg.tid == 0) {
375 std::this_thread::sleep_for(std::chrono::milliseconds(1));
376 }
377
378 std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
379 ASSERT_TRUE(event_type_modifier != nullptr);
380 perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
381 // Enable profiling in perf_event_open system call.
382 attr.disabled = 0;
383 attr.enable_on_exec = 0;
384
385 auto start_time = std::chrono::steady_clock::now();
386 auto cur_time = start_time;
387 auto end_time = start_time + test_duration_for_long_tests;
388 auto report_step = std::chrono::seconds(15);
389 size_t iterations = 0;
390
391 while (cur_time < end_time && !cpu_toggle_arg.cpu_hotplug_failed) {
392 if (cur_time + report_step < std::chrono::steady_clock::now()) {
393 auto diff = std::chrono::duration_cast<std::chrono::seconds>(
394 std::chrono::steady_clock::now() - start_time);
395 if (verbose_mode) {
396 GTEST_LOG_(INFO) << "Have Tested " << (diff.count() / 60.0) << " minutes.";
397 }
398 cur_time = std::chrono::steady_clock::now();
399 }
400 // Test if the cpu pmu is still usable.
401 ASSERT_TRUE(EventFd::OpenEventFile(attr, 0, -1, nullptr, event_type_modifier->name, true) !=
402 nullptr);
403
404 std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(
405 attr, cpu_spin_arg.tid, test_cpu, nullptr, event_type_modifier->name, false);
406 if (event_fd == nullptr) {
407 // Failed to open because the test_cpu is offline.
408 continue;
409 }
410 // profile for a while.
411 std::this_thread::sleep_for(std::chrono::milliseconds(1));
412 iterations++;
413 if (verbose_mode) {
414 GTEST_LOG_(INFO) << "Test offline while user process profiling for " << iterations
415 << " times.";
416 }
417 }
418 if (cpu_toggle_arg.cpu_hotplug_failed) {
419 GTEST_LOG_(INFO) << "Test ends because of cpu hotplug failure.";
420 }
421 cpu_toggle_arg.end_flag = true;
422 cpu_toggle_thread.join();
423 cpu_spin_arg.end_flag = true;
424 cpu_spin_thread.join();
425 // Check if the cpu-cycle event is still available on test_cpu.
426 if (SetCpuOnline(test_cpu, true)) {
427 ASSERT_TRUE(EventFd::OpenEventFile(attr, -1, test_cpu, nullptr, event_type_modifier->name,
428 true) != nullptr);
429 }
430 }
431
432 // http://b/19863147.
TEST(cpu_offline,offline_while_recording_on_another_cpu)433 TEST(cpu_offline, offline_while_recording_on_another_cpu) {
434 ScopedMpdecisionKiller scoped_mpdecision_killer;
435 CpuOnlineRestorer cpuonline_restorer;
436
437 if (GetCpuCount() == 1) {
438 GTEST_LOG_(INFO) << "This test does nothing, because there is only one cpu in the system.";
439 return;
440 }
441 int test_cpu;
442 if (!FindAHotpluggableCpu(&test_cpu)) {
443 return;
444 }
445 std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
446 perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
447 attr.disabled = 0;
448 attr.enable_on_exec = 0;
449
450 const size_t TEST_ITERATION_COUNT = 10u;
451 for (size_t i = 0; i < TEST_ITERATION_COUNT; ++i) {
452 int record_cpu = 0;
453 if (!SetCpuOnline(test_cpu, true)) {
454 break;
455 }
456 std::unique_ptr<EventFd> event_fd =
457 EventFd::OpenEventFile(attr, getpid(), record_cpu, nullptr, event_type_modifier->name);
458 ASSERT_TRUE(event_fd != nullptr);
459 if (!SetCpuOnline(test_cpu, false)) {
460 break;
461 }
462 event_fd = nullptr;
463 event_fd =
464 EventFd::OpenEventFile(attr, getpid(), record_cpu, nullptr, event_type_modifier->name);
465 ASSERT_TRUE(event_fd != nullptr);
466 }
467 }
468
main(int argc,char ** argv)469 int main(int argc, char** argv) {
470 for (int i = 1; i < argc; ++i) {
471 if (strcmp(argv[i], "--help") == 0) {
472 printf("--long_test_duration <second> Set test duration for long tests. Default is 120s.\n");
473 printf(
474 "--cpu_hotplug_interval <microseconds> Set cpu hotplug interval. Default is 1000us.\n");
475 printf("--verbose Show verbose log.\n");
476 } else if (strcmp(argv[i], "--long_test_duration") == 0) {
477 if (i + 1 < argc) {
478 int second_count = atoi(argv[i + 1]);
479 if (second_count <= 0) {
480 fprintf(stderr, "Invalid arg for --long_test_duration.\n");
481 return 1;
482 }
483 test_duration_for_long_tests = std::chrono::seconds(second_count);
484 i++;
485 }
486 } else if (strcmp(argv[i], "--cpu_hotplug_interval") == 0) {
487 if (i + 1 < argc) {
488 int microsecond_count = atoi(argv[i + 1]);
489 if (microsecond_count <= 0) {
490 fprintf(stderr, "Invalid arg for --cpu_hotplug_interval\n");
491 return 1;
492 }
493 cpu_hotplug_interval = std::chrono::microseconds(microsecond_count);
494 i++;
495 }
496 } else if (strcmp(argv[i], "--verbose") == 0) {
497 verbose_mode = true;
498 }
499 }
500 android::base::InitLogging(argv, android::base::StderrLogger);
501 testing::InitGoogleTest(&argc, argv);
502 return RUN_ALL_TESTS();
503 }
504