1 /*
2 **
3 ** Copyright 2015, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 ** http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17
18 #include "perfprofd_perf.h"
19
20
21 #include <inttypes.h>
22 #include <signal.h>
23 #include <sys/wait.h>
24 #include <unistd.h>
25
26 #include <algorithm>
27 #include <cerrno>
28 #include <cstdio>
29 #include <cstring>
30 #include <memory>
31 #include <vector>
32
33 #include <android-base/file.h>
34 #include <android-base/logging.h>
35 #include <android-base/stringprintf.h>
36 #include <android-base/strings.h>
37 #include <android-base/unique_fd.h>
38
39 #include "config.h"
40
41 namespace android {
42 namespace perfprofd {
43
44 namespace {
45
GetSupportedPerfCountersInternal()46 std::unordered_set<std::string>& GetSupportedPerfCountersInternal() {
47 static std::unordered_set<std::string>& vec = *new std::unordered_set<std::string>();
48 return vec;
49 }
50
51 } // namespace
52
53 //
54 // Invoke "perf record". Return value is OK_PROFILE_COLLECTION for
55 // success, or some other error code if something went wrong.
56 //
InvokePerf(Config & config,const std::string & perf_path,const char * stack_profile_opt,unsigned duration,const std::string & data_file_path,const std::string & perf_stderr_path)57 PerfResult InvokePerf(Config& config,
58 const std::string &perf_path,
59 const char *stack_profile_opt,
60 unsigned duration,
61 const std::string &data_file_path,
62 const std::string &perf_stderr_path)
63 {
64 std::vector<std::string> argv_backing;
65 std::vector<const char*> argv_vector;
66 char paranoid_env[] = "PERFPROFD_DISABLE_PERF_EVENT_PARANOID_CHANGE=1";
67 char* envp[2] = {paranoid_env, nullptr};
68
69 {
70 auto add = [&argv_backing](auto arg) {
71 argv_backing.push_back(arg);
72 };
73
74 add(perf_path);
75 add("record");
76
77 // -o perf.data
78 add("-o");
79 add(data_file_path);
80
81 // -c/f N
82 std::string p_str;
83 if (config.sampling_frequency > 0) {
84 add("-f");
85 add(android::base::StringPrintf("%u", config.sampling_frequency));
86 } else if (config.sampling_period > 0) {
87 add("-c");
88 add(android::base::StringPrintf("%u", config.sampling_period));
89 }
90
91 if (!config.event_config.empty()) {
92 const std::unordered_set<std::string>& supported = GetSupportedPerfCountersInternal();
93 for (const auto& event_set : config.event_config) {
94 if (event_set.events.empty()) {
95 LOG(WARNING) << "Unexpected empty event set";
96 continue;
97 }
98
99 std::ostringstream event_str;
100 bool added = false;
101 for (const std::string& event : event_set.events) {
102 if (supported.find(event) == supported.end()) {
103 LOG(WARNING) << "Event " << event << " is unsupported.";
104 if (config.fail_on_unsupported_events) {
105 return PerfResult::kUnsupportedEvent;
106 }
107 continue;
108 }
109 if (added) {
110 event_str << ',';
111 }
112 event_str << event;
113 added = true;
114 }
115
116 if (!added) {
117 continue;
118 }
119
120 if (event_set.sampling_period > 0) {
121 add("-c");
122 add(std::to_string(event_set.sampling_period));
123 }
124 add(event_set.group ? "--group" : "-e");
125 add(event_str.str());
126 }
127 }
128
129 // -g if desired
130 if (stack_profile_opt != nullptr) {
131 add(stack_profile_opt);
132 add("-m");
133 add("8192");
134 }
135
136 if (config.process < 0) {
137 // system wide profiling
138 add("-a");
139 } else {
140 add("-p");
141 add(std::to_string(config.process));
142 }
143
144 // no need for kernel or other symbols
145 add("--no-dump-kernel-symbols");
146 add("--no-dump-symbols");
147
148 // sleep <duration>
149 add("--duration");
150 add(android::base::StringPrintf("%u", duration));
151
152
153 // Now create the char* buffer.
154 argv_vector.resize(argv_backing.size() + 1, nullptr);
155 std::transform(argv_backing.begin(),
156 argv_backing.end(),
157 argv_vector.begin(),
158 [](const std::string& in) { return in.c_str(); });
159 }
160
161 pid_t pid = fork();
162
163 if (pid == -1) {
164 PLOG(ERROR) << "Fork failed";
165 return PerfResult::kForkFailed;
166 }
167
168 if (pid == 0) {
169 // child
170
171 // Open file to receive stderr/stdout from perf
172 FILE *efp = fopen(perf_stderr_path.c_str(), "w");
173 if (efp) {
174 dup2(fileno(efp), STDERR_FILENO);
175 dup2(fileno(efp), STDOUT_FILENO);
176 } else {
177 PLOG(WARNING) << "unable to open " << perf_stderr_path << " for writing";
178 }
179
180 // record the final command line in the error output file for
181 // posterity/debugging purposes
182 fprintf(stderr, "perf invocation (pid=%d):\n", getpid());
183 for (unsigned i = 0; argv_vector[i] != nullptr; ++i) {
184 fprintf(stderr, "%s%s", i ? " " : "", argv_vector[i]);
185 }
186 fprintf(stderr, "\n");
187
188 // exec
189 execvpe(argv_vector[0], const_cast<char* const*>(argv_vector.data()), envp);
190 fprintf(stderr, "exec failed: %s\n", strerror(errno));
191 exit(1);
192
193 } else {
194 // parent
195
196 // Try to sleep.
197 config.Sleep(duration);
198
199 // We may have been woken up to stop profiling.
200 if (config.ShouldStopProfiling()) {
201 // Send SIGHUP to simpleperf to make it stop.
202 kill(pid, SIGHUP);
203 }
204
205 // Wait for the child, so it's reaped correctly.
206 int st = 0;
207 pid_t reaped = TEMP_FAILURE_RETRY(waitpid(pid, &st, 0));
208
209 auto print_perferr = [&perf_stderr_path]() {
210 std::string tmp;
211 if (android::base::ReadFileToString(perf_stderr_path, &tmp)) {
212 LOG(WARNING) << tmp;
213 } else {
214 PLOG(WARNING) << "Could not read " << perf_stderr_path;
215 }
216 };
217
218 if (reaped == -1) {
219 PLOG(WARNING) << "waitpid failed";
220 } else if (WIFSIGNALED(st)) {
221 if (WTERMSIG(st) == SIGHUP && config.ShouldStopProfiling()) {
222 // That was us...
223 return PerfResult::kOK;
224 }
225 LOG(WARNING) << "perf killed by signal " << WTERMSIG(st);
226 print_perferr();
227 } else if (WEXITSTATUS(st) != 0) {
228 LOG(WARNING) << "perf bad exit status " << WEXITSTATUS(st);
229 print_perferr();
230 } else {
231 return PerfResult::kOK;
232 }
233 }
234
235 return PerfResult::kRecordFailed;
236 }
237
FindSupportedPerfCounters(const std::string & perf_path)238 bool FindSupportedPerfCounters(const std::string& perf_path) {
239 const char* argv[] = { perf_path.c_str(), "list", nullptr };
240 char paranoid_env[] = "PERFPROFD_DISABLE_PERF_EVENT_PARANOID_CHANGE=1";
241 char* envp[2] = {paranoid_env, nullptr};
242
243 base::unique_fd link[2];
244 {
245 int link_fd[2];
246
247 if (pipe(link_fd) == -1) {
248 PLOG(ERROR) << "Pipe failed";
249 return false;
250 }
251 link[0].reset(link_fd[0]);
252 link[1].reset(link_fd[1]);
253 }
254
255 pid_t pid = fork();
256
257 if (pid == -1) {
258 PLOG(ERROR) << "Fork failed";
259 return PerfResult::kForkFailed;
260 }
261
262 if (pid == 0) {
263 // Child
264
265 // Redirect stdout and stderr.
266 dup2(link[1].get(), STDOUT_FILENO);
267 dup2(link[1].get(), STDERR_FILENO);
268
269 link[0].reset();
270 link[1].reset();
271
272 // exec
273 execvpe(argv[0], const_cast<char* const*>(argv), envp);
274 PLOG(WARNING) << "exec failed";
275 exit(1);
276 __builtin_unreachable();
277 }
278
279 link[1].reset();
280
281 std::string result;
282 if (!android::base::ReadFdToString(link[0].get(), &result)) {
283 PLOG(WARNING) << perf_path << " list reading failed.";
284 }
285
286 link[0].reset();
287
288 int status_code;
289 if (waitpid(pid, &status_code, 0) == -1) {
290 LOG(WARNING) << "Failed to wait for " << perf_path << " list";
291 return false;
292 }
293
294 if (!WIFEXITED(status_code) || WEXITSTATUS(status_code) != 0) {
295 LOG(WARNING) << perf_path << " list did not exit normally.";
296 return false;
297 }
298
299 std::unordered_set<std::string>& supported = GetSupportedPerfCountersInternal();
300 supported.clear();
301
302 // Could implement something with less memory requirements. But for now this is good
303 // enough.
304 std::vector<std::string> lines = base::Split(result, "\n");
305 for (const std::string& line : lines) {
306 if (line.length() < 2 || line.compare(0, 2, " ") != 0) {
307 continue;
308 }
309 const size_t comment = line.find('#');
310 const size_t space = line.find(' ', 2);
311 size_t end = std::min(space, comment);
312 if (end != std::string::npos) {
313 // Scan backwards.
314 --end;
315 while (end > 2 && isspace(line[end])) {
316 end--;
317 }
318 }
319 if (end > 2) {
320 supported.insert(line.substr(2, end - 2));
321 }
322 }
323
324 return true;
325 }
326
GetSupportedPerfCounters()327 const std::unordered_set<std::string>& GetSupportedPerfCounters() {
328 return GetSupportedPerfCountersInternal();
329 }
330
331 } // namespace perfprofd
332 } // namespace android
333