OSDN Git Service

Merge "Perfprofd: Factor out threaded handler" into pi-dev
[android-x86/system-extras.git] / simpleperf / cpu_hotplug_test.cpp
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:
47   ScopedMpdecisionKiller() {
48     have_mpdecision_ = IsMpdecisionRunning();
49     if (have_mpdecision_) {
50       DisableMpdecision();
51     }
52   }
53
54   ~ScopedMpdecisionKiller() {
55     if (have_mpdecision_) {
56       EnableMpdecision();
57     }
58   }
59
60  private:
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
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
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:
87   ScopedMpdecisionKiller() {
88   }
89 };
90 #endif
91
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
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
149 static int GetCpuCount() {
150   return static_cast<int>(sysconf(_SC_NPROCESSORS_CONF));
151 }
152
153 class CpuOnlineRestorer {
154  public:
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
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
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
201   CpuToggleThreadArg(int cpu)
202       : toggle_cpu(cpu), end_flag(false), cpu_hotplug_failed(false) {
203   }
204 };
205
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.
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.
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
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.
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.
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
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 }