/* * 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 #include #include #if defined(__BIONIC__) #include #endif #include #include #include #include #include #include #include #include #include "command.h" #include "event_attr.h" #include "event_fd.h" #include "event_type.h" static std::unique_ptr 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(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 online_map_; }; struct CpuToggleThreadArg { int toggle_cpu; std::atomic 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(SLEEP_DURATION_IN_SEC * 1e6)); } cpu_toggle_arg.end_flag = true; cpu_toggle_thread.join(); } static std::unique_ptr OpenHardwareEventOnCpu(int cpu) { std::unique_ptr 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 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); } } int main(int argc, char** argv) { InitLogging(argv, android::base::StderrLogger); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }