Home | History | Annotate | Download | only in simpleperf
      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:
     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     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 
     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 
     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:
     90   ScopedMpdecisionKiller() {
     91   }
     92 };
     93 #endif
     94 
     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 
    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 
    152 static int GetCpuCount() {
    153   return static_cast<int>(sysconf(_SC_NPROCESSORS_CONF));
    154 }
    155 
    156 class CpuOnlineRestorer {
    157  public:
    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 
    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 
    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 
    204   CpuToggleThreadArg(int cpu)
    205       : toggle_cpu(cpu), end_flag(false), cpu_hotplug_failed(false) {
    206   }
    207 };
    208 
    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.
    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.
    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 
    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.
    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.
    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 
    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