summaryrefslogtreecommitdiff
path: root/simpleperf/cpu_hotplug_test.cpp
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2015-12-07 20:14:14 -0800
committerYabin Cui <yabinc@google.com>2015-12-08 11:48:46 -0800
commit89905b72da03366f65baaa3facfc9c91f5631ca4 (patch)
treea29491a66a7d562b7a7cd7da4dd41dcfbf0bf620 /simpleperf/cpu_hotplug_test.cpp
parentb9ca50b319d2083c5a92c468e54796b0e71a4ad9 (diff)
downloadextras-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.cpp254
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);
+ }
+}