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