• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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