OSDN Git Service

Simpleperf: add test for cpu offline whiling recording.
[android-x86/system-extras.git] / simpleperf / cpu_offline_test.cpp
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 <condition_variable>
28 #include <thread>
29 #include <unordered_map>
30
31 #include <base/file.h>
32 #include <base/logging.h>
33 #include <base/stringprintf.h>
34
35 #include "command.h"
36 #include "event_attr.h"
37 #include "event_fd.h"
38 #include "event_type.h"
39
40 static std::unique_ptr<Command> RecordCmd() {
41   return CreateCommandInstance("record");
42 }
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     usleep(500000);
75     CHECK(!IsMpdecisionRunning());
76   }
77
78   void EnableMpdecision() {
79     int ret = __system_property_set("ctl.start", "mpdecision");
80     CHECK_EQ(0, ret);
81     usleep(500000);
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) {
96   std::string filename = android::base::StringPrintf("/sys/devices/system/cpu/cpu%d/online", cpu);
97   std::string content;
98   CHECK(android::base::ReadFileToString(filename, &content)) << "failed to read file " << filename;
99   return (content.find('1') != std::string::npos);
100 }
101
102 static void SetCpuOnline(int cpu, bool online) {
103   if (IsCpuOnline(cpu) == online) {
104     return;
105   }
106   std::string filename = android::base::StringPrintf("/sys/devices/system/cpu/cpu%d/online", cpu);
107   std::string content = online ? "1" : "0";
108   CHECK(android::base::WriteStringToFile(content, filename)) << "Write " << content << " to "
109                                                              << filename << " failed";
110   CHECK_EQ(online, IsCpuOnline(cpu)) << "set cpu " << cpu << (online ? " online" : " offline")
111                                      << " failed";
112 }
113
114 static int GetCpuCount() {
115   return static_cast<int>(sysconf(_SC_NPROCESSORS_CONF));
116 }
117
118 class CpuOnlineRestorer {
119  public:
120   CpuOnlineRestorer() {
121     for (int cpu = 1; cpu < GetCpuCount(); ++cpu) {
122       online_map_[cpu] = IsCpuOnline(cpu);
123     }
124   }
125
126   ~CpuOnlineRestorer() {
127     for (const auto& pair : online_map_) {
128       SetCpuOnline(pair.first, pair.second);
129     }
130   }
131
132  private:
133   std::unordered_map<int, bool> online_map_;
134 };
135
136 struct CpuToggleThreadArg {
137   int toggle_cpu;
138   std::atomic<bool> end_flag;
139 };
140
141 static void CpuToggleThread(CpuToggleThreadArg* arg) {
142   // Wait until a record command is running.
143   sleep(1);
144   while (!arg->end_flag) {
145     SetCpuOnline(arg->toggle_cpu, false);
146     sleep(1);
147     if (arg->end_flag) {
148       break;
149     }
150     SetCpuOnline(arg->toggle_cpu, true);
151     sleep(1);
152   }
153 }
154
155 static bool RecordInChildProcess(int record_cpu, int record_duration_in_second) {
156   pid_t pid = fork();
157   CHECK(pid != -1);
158   if (pid == 0) {
159     std::string cpu_str = android::base::StringPrintf("%d", record_cpu);
160     std::string record_duration_str = android::base::StringPrintf("%d", record_duration_in_second);
161     bool ret = RecordCmd()->Run({"-a", "--cpu", cpu_str, "sleep", record_duration_str});
162     exit(ret ? 0 : 1);
163   }
164   int timeout = record_duration_in_second + 10;
165   auto end_time = std::chrono::steady_clock::now() + std::chrono::seconds(timeout);
166   bool child_success = false;
167   while (std::chrono::steady_clock::now() < end_time) {
168     int exit_state;
169     pid_t ret = waitpid(pid, &exit_state, WNOHANG);
170     if (ret == pid) {
171       if (WIFSIGNALED(exit_state) || (WIFEXITED(exit_state) && WEXITSTATUS(exit_state) != 0)) {
172         child_success = false;
173       } else {
174         child_success = true;
175       }
176       break;
177     } else if (ret == -1) {
178       child_success = false;
179       break;
180     }
181     sleep(1);
182   }
183   return child_success;
184 }
185
186 // http://b/25193162.
187 TEST(cpu_offline, offline_while_recording) {
188   ScopedMpdecisionKiller scoped_mpdecision_killer;
189   CpuOnlineRestorer cpuonline_restorer;
190
191   if (GetCpuCount() == 1) {
192     GTEST_LOG_(INFO) << "This test does nothing, because there is only one cpu in the system.";
193     return;
194   }
195
196   const size_t TEST_ITERATION_COUNT = 20u;
197   const int TEST_DURATION_IN_SECOND = 9;
198   for (size_t i = 0; i < TEST_ITERATION_COUNT; ++i) {
199     int test_cpu = GetCpuCount() - 1;
200     SetCpuOnline(test_cpu, true);
201     CpuToggleThreadArg cpu_toggle_arg;
202     cpu_toggle_arg.toggle_cpu = test_cpu;
203     cpu_toggle_arg.end_flag = false;
204     std::thread cpu_toggle_thread(CpuToggleThread, &cpu_toggle_arg);
205
206     ASSERT_TRUE(RecordInChildProcess(test_cpu, TEST_DURATION_IN_SECOND));
207     cpu_toggle_arg.end_flag = true;
208     cpu_toggle_thread.join();
209     GTEST_LOG_(INFO) << "Finish test iteration " << (i + 1) << " successfully.";
210   }
211 }
212
213 static std::unique_ptr<EventFd> OpenHardwareEventOnCpu(int cpu) {
214   std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
215   if (event_type_modifier == nullptr) {
216     return nullptr;
217   }
218   perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
219   return EventFd::OpenEventFile(attr, getpid(), cpu);
220 }
221
222 // http://b/19863147.
223 TEST(cpu_offline, offline_while_recording_on_another_cpu) {
224   ScopedMpdecisionKiller scoped_mpdecision_killer;
225   CpuOnlineRestorer cpuonline_restorer;
226
227   if (GetCpuCount() == 1) {
228     GTEST_LOG_(INFO) << "This test does nothing, because there is only one cpu in the system.";
229     return;
230   }
231
232   const size_t TEST_ITERATION_COUNT = 10u;
233   for (size_t i = 0; i < TEST_ITERATION_COUNT; ++i) {
234     int record_cpu = 0;
235     int toggle_cpu = GetCpuCount() - 1;
236     SetCpuOnline(toggle_cpu, true);
237     std::unique_ptr<EventFd> event_fd = OpenHardwareEventOnCpu(record_cpu);
238     ASSERT_TRUE(event_fd != nullptr);
239     SetCpuOnline(toggle_cpu, false);
240     event_fd = nullptr;
241     event_fd = OpenHardwareEventOnCpu(record_cpu);
242     ASSERT_TRUE(event_fd != nullptr);
243   }
244 }