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