blob: 15dde6fb04b555fbb6198cc388798e68c94fad28 [file] [log] [blame]
Andreas Gamped662d882018-06-21 16:04:33 -07001/*
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
Andreas Gampeb116d3e2018-07-16 16:07:48 -070021#include <inttypes.h>
Andreas Gamped662d882018-06-21 16:04:33 -070022#include <signal.h>
23#include <sys/wait.h>
24#include <unistd.h>
25
Andreas Gamped61d6992018-06-21 16:30:57 -070026#include <algorithm>
Andreas Gamped662d882018-06-21 16:04:33 -070027#include <cerrno>
28#include <cstdio>
29#include <cstring>
30#include <memory>
Andreas Gamped61d6992018-06-21 16:30:57 -070031#include <vector>
Andreas Gamped662d882018-06-21 16:04:33 -070032
33#include <android-base/file.h>
34#include <android-base/logging.h>
35#include <android-base/stringprintf.h>
Andreas Gampe725faa22018-06-21 16:07:48 -070036#include <android-base/strings.h>
Andreas Gampeb116d3e2018-07-16 16:07:48 -070037#include <android-base/unique_fd.h>
Andreas Gamped662d882018-06-21 16:04:33 -070038
39#include "config.h"
40
41namespace android {
42namespace perfprofd {
43
Andreas Gampeb116d3e2018-07-16 16:07:48 -070044namespace {
45
46std::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
Andreas Gamped662d882018-06-21 16:04:33 -070053//
54// Invoke "perf record". Return value is OK_PROFILE_COLLECTION for
55// success, or some other error code if something went wrong.
56//
57PerfResult 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{
Andreas Gamped61d6992018-06-21 16:30:57 -070064 std::vector<std::string> argv_backing;
65 std::vector<const char*> argv_vector;
Yabin Cui5edb7fc2018-08-29 14:54:19 -070066 char paranoid_env[] = "PERFPROFD_DISABLE_PERF_EVENT_PARANOID_CHANGE=1";
67 char* envp[2] = {paranoid_env, nullptr};
Andreas Gamped61d6992018-06-21 16:30:57 -070068
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
Andreas Gampe725faa22018-06-21 16:07:48 -070091 if (!config.event_config.empty()) {
Andreas Gampee0fd7b12018-07-19 12:18:47 -070092 const std::unordered_set<std::string>& supported = GetSupportedPerfCountersInternal();
Andreas Gampe725faa22018-06-21 16:07:48 -070093 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
Andreas Gampee0fd7b12018-07-19 12:18:47 -070099 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
Andreas Gampe725faa22018-06-21 16:07:48 -0700120 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");
Andreas Gampee0fd7b12018-07-19 12:18:47 -0700125 add(event_str.str());
Andreas Gampe725faa22018-06-21 16:07:48 -0700126 }
127 }
128
Andreas Gamped61d6992018-06-21 16:30:57 -0700129 // -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
Andreas Gamped662d882018-06-21 16:04:33 -0700161 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
Andreas Gamped662d882018-06-21 16:04:33 -0700180 // record the final command line in the error output file for
181 // posterity/debugging purposes
182 fprintf(stderr, "perf invocation (pid=%d):\n", getpid());
Andreas Gamped61d6992018-06-21 16:30:57 -0700183 for (unsigned i = 0; argv_vector[i] != nullptr; ++i) {
184 fprintf(stderr, "%s%s", i ? " " : "", argv_vector[i]);
Andreas Gamped662d882018-06-21 16:04:33 -0700185 }
186 fprintf(stderr, "\n");
187
188 // exec
Yabin Cui5edb7fc2018-08-29 14:54:19 -0700189 execvpe(argv_vector[0], const_cast<char* const*>(argv_vector.data()), envp);
Andreas Gamped662d882018-06-21 16:04:33 -0700190 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
Andreas Gampeb116d3e2018-07-16 16:07:48 -0700238bool FindSupportedPerfCounters(const std::string& perf_path) {
239 const char* argv[] = { perf_path.c_str(), "list", nullptr };
Yabin Cui5edb7fc2018-08-29 14:54:19 -0700240 char paranoid_env[] = "PERFPROFD_DISABLE_PERF_EVENT_PARANOID_CHANGE=1";
241 char* envp[2] = {paranoid_env, nullptr};
Andreas Gampeb116d3e2018-07-16 16:07:48 -0700242
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
Yabin Cui5edb7fc2018-08-29 14:54:19 -0700273 execvpe(argv[0], const_cast<char* const*>(argv), envp);
Andreas Gampeb116d3e2018-07-16 16:07:48 -0700274 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
327const std::unordered_set<std::string>& GetSupportedPerfCounters() {
328 return GetSupportedPerfCountersInternal();
329}
330
Andreas Gamped662d882018-06-21 16:04:33 -0700331} // namespace perfprofd
332} // namespace android