• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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