diff options
author | Yabin Cui <yabinc@google.com> | 2015-12-07 20:14:14 -0800 |
---|---|---|
committer | Yabin Cui <yabinc@google.com> | 2015-12-08 11:48:46 -0800 |
commit | 89905b72da03366f65baaa3facfc9c91f5631ca4 (patch) | |
tree | a29491a66a7d562b7a7cd7da4dd41dcfbf0bf620 /simpleperf/cpu_hotplug_test.cpp | |
parent | b9ca50b319d2083c5a92c468e54796b0e71a4ad9 (diff) | |
download | extras-89905b72da03366f65baaa3facfc9c91f5631ca4.tar.gz |
Simpleperf: add separate cpu_hotplug_test.
cpu_hotplug_test runs much longer than unit tests, so separate it.
It uses the same strategy as the shell scripts I used to find all
kernel bugs.
Bug: 26032318
Bug: 25193162
Change-Id: I2f26d4f0d65e680b597e40cce9e775b9568599ef
Diffstat (limited to 'simpleperf/cpu_hotplug_test.cpp')
-rw-r--r-- | simpleperf/cpu_hotplug_test.cpp | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/simpleperf/cpu_hotplug_test.cpp b/simpleperf/cpu_hotplug_test.cpp new file mode 100644 index 00000000..68d4ea73 --- /dev/null +++ b/simpleperf/cpu_hotplug_test.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <sys/stat.h> +#include <unistd.h> +#if defined(__BIONIC__) +#include <sys/system_properties.h> +#endif + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <thread> +#include <unordered_map> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/stringprintf.h> + +#include "command.h" +#include "event_attr.h" +#include "event_fd.h" +#include "event_type.h" + +static std::unique_ptr<Command> RecordCmd() { + return CreateCommandInstance("record"); +} + +#if defined(__BIONIC__) +class ScopedMpdecisionKiller { + public: + ScopedMpdecisionKiller() { + have_mpdecision_ = IsMpdecisionRunning(); + if (have_mpdecision_) { + DisableMpdecision(); + } + } + + ~ScopedMpdecisionKiller() { + if (have_mpdecision_) { + EnableMpdecision(); + } + } + + private: + bool IsMpdecisionRunning() { + char value[PROP_VALUE_MAX]; + int len = __system_property_get("init.svc.mpdecision", value); + if (len == 0 || (len > 0 && strstr(value, "stopped") != nullptr)) { + return false; + } + return true; + } + + void DisableMpdecision() { + int ret = __system_property_set("ctl.stop", "mpdecision"); + CHECK_EQ(0, ret); + // Need to wait until mpdecision is actually stopped. + usleep(500000); + CHECK(!IsMpdecisionRunning()); + } + + void EnableMpdecision() { + int ret = __system_property_set("ctl.start", "mpdecision"); + CHECK_EQ(0, ret); + usleep(500000); + CHECK(IsMpdecisionRunning()); + } + + bool have_mpdecision_; +}; +#else +class ScopedMpdecisionKiller { + public: + ScopedMpdecisionKiller() { + } +}; +#endif + +static bool IsCpuOnline(int cpu) { + std::string filename = android::base::StringPrintf("/sys/devices/system/cpu/cpu%d/online", cpu); + std::string content; + CHECK(android::base::ReadFileToString(filename, &content)) << "failed to read file " << filename; + return (content.find('1') != std::string::npos); +} + +static void SetCpuOnline(int cpu, bool online) { + if (IsCpuOnline(cpu) == online) { + return; + } + std::string filename = android::base::StringPrintf("/sys/devices/system/cpu/cpu%d/online", cpu); + std::string content = online ? "1" : "0"; + CHECK(android::base::WriteStringToFile(content, filename)) << "Write " << content << " to " + << filename << " failed"; + CHECK_EQ(online, IsCpuOnline(cpu)) << "set cpu " << cpu << (online ? " online" : " offline") + << " failed"; +} + +static int GetCpuCount() { + return static_cast<int>(sysconf(_SC_NPROCESSORS_CONF)); +} + +class CpuOnlineRestorer { + public: + CpuOnlineRestorer() { + for (int cpu = 1; cpu < GetCpuCount(); ++cpu) { + online_map_[cpu] = IsCpuOnline(cpu); + } + } + + ~CpuOnlineRestorer() { + for (const auto& pair : online_map_) { + SetCpuOnline(pair.first, pair.second); + } + } + + private: + std::unordered_map<int, bool> online_map_; +}; + +struct CpuToggleThreadArg { + int toggle_cpu; + std::atomic<bool> end_flag; +}; + +static void CpuToggleThread(CpuToggleThreadArg* arg) { + while (!arg->end_flag) { + SetCpuOnline(arg->toggle_cpu, true); + sleep(1); + SetCpuOnline(arg->toggle_cpu, false); + sleep(1); + } +} + +static bool RecordInChildProcess(int record_cpu, int record_duration_in_second) { + pid_t pid = fork(); + CHECK(pid != -1); + if (pid == 0) { + std::string cpu_str = android::base::StringPrintf("%d", record_cpu); + std::string record_duration_str = android::base::StringPrintf("%d", record_duration_in_second); + bool ret = RecordCmd()->Run({"-a", "--cpu", cpu_str, "sleep", record_duration_str}); + extern bool system_wide_perf_event_open_failed; + // It is not an error if perf_event_open failed because of cpu-hotplug. + if (!ret && !system_wide_perf_event_open_failed) { + exit(1); + } + exit(0); + } + int timeout = record_duration_in_second + 10; + auto end_time = std::chrono::steady_clock::now() + std::chrono::seconds(timeout); + bool child_success = false; + while (std::chrono::steady_clock::now() < end_time) { + int exit_state; + pid_t ret = waitpid(pid, &exit_state, WNOHANG); + if (ret == pid) { + if (WIFSIGNALED(exit_state) || (WIFEXITED(exit_state) && WEXITSTATUS(exit_state) != 0)) { + child_success = false; + } else { + child_success = true; + } + break; + } else if (ret == -1) { + child_success = false; + break; + } + sleep(1); + } + return child_success; +} + +// http://b/25193162. +TEST(cpu_offline, offline_while_recording) { + ScopedMpdecisionKiller scoped_mpdecision_killer; + CpuOnlineRestorer cpuonline_restorer; + + if (GetCpuCount() == 1) { + GTEST_LOG_(INFO) << "This test does nothing, because there is only one cpu in the system."; + return; + } + for (int i = 1; i < GetCpuCount(); ++i) { + if (!IsCpuOnline(i)) { + SetCpuOnline(i, true); + } + } + // Start cpu hotplugger. + int test_cpu = GetCpuCount() - 1; + CpuToggleThreadArg cpu_toggle_arg; + cpu_toggle_arg.toggle_cpu = test_cpu; + cpu_toggle_arg.end_flag = false; + std::thread cpu_toggle_thread(CpuToggleThread, &cpu_toggle_arg); + + const std::chrono::hours test_duration(10); // Test for 10 hours. + const double RECORD_DURATION_IN_SEC = 2.9; + const double SLEEP_DURATION_IN_SEC = 1.3; + + auto end_time = std::chrono::steady_clock::now() + test_duration; + size_t iterations = 0; + while (std::chrono::steady_clock::now() < end_time) { + iterations++; + GTEST_LOG_(INFO) << "Test for " << iterations << " times."; + ASSERT_TRUE(RecordInChildProcess(test_cpu, RECORD_DURATION_IN_SEC)); + usleep(static_cast<useconds_t>(SLEEP_DURATION_IN_SEC * 1e6)); + } + cpu_toggle_arg.end_flag = true; + cpu_toggle_thread.join(); +} + +static std::unique_ptr<EventFd> OpenHardwareEventOnCpu(int cpu) { + std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles"); + if (event_type_modifier == nullptr) { + return nullptr; + } + perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type); + return EventFd::OpenEventFile(attr, getpid(), cpu); +} + +// http://b/19863147. +TEST(cpu_offline, offline_while_recording_on_another_cpu) { + ScopedMpdecisionKiller scoped_mpdecision_killer; + CpuOnlineRestorer cpuonline_restorer; + + if (GetCpuCount() == 1) { + GTEST_LOG_(INFO) << "This test does nothing, because there is only one cpu in the system."; + return; + } + + const size_t TEST_ITERATION_COUNT = 10u; + for (size_t i = 0; i < TEST_ITERATION_COUNT; ++i) { + int record_cpu = 0; + int toggle_cpu = GetCpuCount() - 1; + SetCpuOnline(toggle_cpu, true); + std::unique_ptr<EventFd> event_fd = OpenHardwareEventOnCpu(record_cpu); + ASSERT_TRUE(event_fd != nullptr); + SetCpuOnline(toggle_cpu, false); + event_fd = nullptr; + event_fd = OpenHardwareEventOnCpu(record_cpu); + ASSERT_TRUE(event_fd != nullptr); + } +} |