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