1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker * Copyright (C) 2019 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker *
4*288bf522SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker *
8*288bf522SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker *
10*288bf522SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker */
16*288bf522SAndroid Build Coastguard Worker
17*288bf522SAndroid Build Coastguard Worker #include "simpleperf.h"
18*288bf522SAndroid Build Coastguard Worker
19*288bf522SAndroid Build Coastguard Worker #include <limits.h>
20*288bf522SAndroid Build Coastguard Worker #include <stdarg.h>
21*288bf522SAndroid Build Coastguard Worker #include <stdlib.h>
22*288bf522SAndroid Build Coastguard Worker #include <string.h>
23*288bf522SAndroid Build Coastguard Worker #include <sys/socket.h>
24*288bf522SAndroid Build Coastguard Worker #include <sys/stat.h>
25*288bf522SAndroid Build Coastguard Worker #include <sys/wait.h>
26*288bf522SAndroid Build Coastguard Worker #include <time.h>
27*288bf522SAndroid Build Coastguard Worker #include <unistd.h>
28*288bf522SAndroid Build Coastguard Worker
29*288bf522SAndroid Build Coastguard Worker #include <android/log.h>
30*288bf522SAndroid Build Coastguard Worker #include <mutex>
31*288bf522SAndroid Build Coastguard Worker #include <sstream>
32*288bf522SAndroid Build Coastguard Worker
33*288bf522SAndroid Build Coastguard Worker namespace simpleperf {
34*288bf522SAndroid Build Coastguard Worker
35*288bf522SAndroid Build Coastguard Worker constexpr int AID_USER_OFFSET = 100000;
36*288bf522SAndroid Build Coastguard Worker
37*288bf522SAndroid Build Coastguard Worker enum RecordCmd {
38*288bf522SAndroid Build Coastguard Worker CMD_PAUSE_RECORDING = 1,
39*288bf522SAndroid Build Coastguard Worker CMD_RESUME_RECORDING,
40*288bf522SAndroid Build Coastguard Worker };
41*288bf522SAndroid Build Coastguard Worker
42*288bf522SAndroid Build Coastguard Worker class RecordOptionsImpl {
43*288bf522SAndroid Build Coastguard Worker public:
44*288bf522SAndroid Build Coastguard Worker std::string output_filename;
45*288bf522SAndroid Build Coastguard Worker std::string event = "cpu-cycles";
46*288bf522SAndroid Build Coastguard Worker size_t freq = 4000;
47*288bf522SAndroid Build Coastguard Worker double duration_in_second = 0.0;
48*288bf522SAndroid Build Coastguard Worker std::vector<pid_t> threads;
49*288bf522SAndroid Build Coastguard Worker bool dwarf_callgraph = false;
50*288bf522SAndroid Build Coastguard Worker bool fp_callgraph = false;
51*288bf522SAndroid Build Coastguard Worker bool trace_offcpu = false;
52*288bf522SAndroid Build Coastguard Worker };
53*288bf522SAndroid Build Coastguard Worker
RecordOptions()54*288bf522SAndroid Build Coastguard Worker RecordOptions::RecordOptions() : impl_(new RecordOptionsImpl) {}
55*288bf522SAndroid Build Coastguard Worker
~RecordOptions()56*288bf522SAndroid Build Coastguard Worker RecordOptions::~RecordOptions() {
57*288bf522SAndroid Build Coastguard Worker delete impl_;
58*288bf522SAndroid Build Coastguard Worker }
59*288bf522SAndroid Build Coastguard Worker
SetOutputFilename(const std::string & filename)60*288bf522SAndroid Build Coastguard Worker RecordOptions& RecordOptions::SetOutputFilename(const std::string& filename) {
61*288bf522SAndroid Build Coastguard Worker impl_->output_filename = filename;
62*288bf522SAndroid Build Coastguard Worker return *this;
63*288bf522SAndroid Build Coastguard Worker }
64*288bf522SAndroid Build Coastguard Worker
SetEvent(const std::string & event)65*288bf522SAndroid Build Coastguard Worker RecordOptions& RecordOptions::SetEvent(const std::string& event) {
66*288bf522SAndroid Build Coastguard Worker impl_->event = event;
67*288bf522SAndroid Build Coastguard Worker return *this;
68*288bf522SAndroid Build Coastguard Worker }
69*288bf522SAndroid Build Coastguard Worker
SetSampleFrequency(size_t freq)70*288bf522SAndroid Build Coastguard Worker RecordOptions& RecordOptions::SetSampleFrequency(size_t freq) {
71*288bf522SAndroid Build Coastguard Worker impl_->freq = freq;
72*288bf522SAndroid Build Coastguard Worker return *this;
73*288bf522SAndroid Build Coastguard Worker }
74*288bf522SAndroid Build Coastguard Worker
SetDuration(double duration_in_second)75*288bf522SAndroid Build Coastguard Worker RecordOptions& RecordOptions::SetDuration(double duration_in_second) {
76*288bf522SAndroid Build Coastguard Worker impl_->duration_in_second = duration_in_second;
77*288bf522SAndroid Build Coastguard Worker return *this;
78*288bf522SAndroid Build Coastguard Worker }
79*288bf522SAndroid Build Coastguard Worker
SetSampleThreads(const std::vector<pid_t> & threads)80*288bf522SAndroid Build Coastguard Worker RecordOptions& RecordOptions::SetSampleThreads(const std::vector<pid_t>& threads) {
81*288bf522SAndroid Build Coastguard Worker impl_->threads = threads;
82*288bf522SAndroid Build Coastguard Worker return *this;
83*288bf522SAndroid Build Coastguard Worker }
84*288bf522SAndroid Build Coastguard Worker
RecordDwarfCallGraph()85*288bf522SAndroid Build Coastguard Worker RecordOptions& RecordOptions::RecordDwarfCallGraph() {
86*288bf522SAndroid Build Coastguard Worker impl_->dwarf_callgraph = true;
87*288bf522SAndroid Build Coastguard Worker impl_->fp_callgraph = false;
88*288bf522SAndroid Build Coastguard Worker return *this;
89*288bf522SAndroid Build Coastguard Worker }
90*288bf522SAndroid Build Coastguard Worker
RecordFramePointerCallGraph()91*288bf522SAndroid Build Coastguard Worker RecordOptions& RecordOptions::RecordFramePointerCallGraph() {
92*288bf522SAndroid Build Coastguard Worker impl_->fp_callgraph = true;
93*288bf522SAndroid Build Coastguard Worker impl_->dwarf_callgraph = false;
94*288bf522SAndroid Build Coastguard Worker return *this;
95*288bf522SAndroid Build Coastguard Worker }
96*288bf522SAndroid Build Coastguard Worker
TraceOffCpu()97*288bf522SAndroid Build Coastguard Worker RecordOptions& RecordOptions::TraceOffCpu() {
98*288bf522SAndroid Build Coastguard Worker impl_->trace_offcpu = true;
99*288bf522SAndroid Build Coastguard Worker return *this;
100*288bf522SAndroid Build Coastguard Worker }
101*288bf522SAndroid Build Coastguard Worker
GetDefaultOutputFilename()102*288bf522SAndroid Build Coastguard Worker static std::string GetDefaultOutputFilename() {
103*288bf522SAndroid Build Coastguard Worker time_t t = time(nullptr);
104*288bf522SAndroid Build Coastguard Worker struct tm tm;
105*288bf522SAndroid Build Coastguard Worker if (localtime_r(&t, &tm) != &tm) {
106*288bf522SAndroid Build Coastguard Worker return "perf.data";
107*288bf522SAndroid Build Coastguard Worker }
108*288bf522SAndroid Build Coastguard Worker char* buf = nullptr;
109*288bf522SAndroid Build Coastguard Worker asprintf(&buf, "perf-%02d-%02d-%02d-%02d-%02d.data", tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
110*288bf522SAndroid Build Coastguard Worker tm.tm_min, tm.tm_sec);
111*288bf522SAndroid Build Coastguard Worker std::string result = buf;
112*288bf522SAndroid Build Coastguard Worker free(buf);
113*288bf522SAndroid Build Coastguard Worker return result;
114*288bf522SAndroid Build Coastguard Worker }
115*288bf522SAndroid Build Coastguard Worker
ToRecordArgs() const116*288bf522SAndroid Build Coastguard Worker std::vector<std::string> RecordOptions::ToRecordArgs() const {
117*288bf522SAndroid Build Coastguard Worker std::vector<std::string> args;
118*288bf522SAndroid Build Coastguard Worker std::string output_filename = impl_->output_filename;
119*288bf522SAndroid Build Coastguard Worker if (output_filename.empty()) {
120*288bf522SAndroid Build Coastguard Worker output_filename = GetDefaultOutputFilename();
121*288bf522SAndroid Build Coastguard Worker }
122*288bf522SAndroid Build Coastguard Worker args.insert(args.end(), {"-o", output_filename});
123*288bf522SAndroid Build Coastguard Worker args.insert(args.end(), {"-e", impl_->event});
124*288bf522SAndroid Build Coastguard Worker args.insert(args.end(), {"-f", std::to_string(impl_->freq)});
125*288bf522SAndroid Build Coastguard Worker if (impl_->duration_in_second != 0.0) {
126*288bf522SAndroid Build Coastguard Worker args.insert(args.end(), {"--duration", std::to_string(impl_->duration_in_second)});
127*288bf522SAndroid Build Coastguard Worker }
128*288bf522SAndroid Build Coastguard Worker if (impl_->threads.empty()) {
129*288bf522SAndroid Build Coastguard Worker args.insert(args.end(), {"-p", std::to_string(getpid())});
130*288bf522SAndroid Build Coastguard Worker } else {
131*288bf522SAndroid Build Coastguard Worker std::ostringstream os;
132*288bf522SAndroid Build Coastguard Worker os << *(impl_->threads.begin());
133*288bf522SAndroid Build Coastguard Worker for (auto it = std::next(impl_->threads.begin()); it != impl_->threads.end(); ++it) {
134*288bf522SAndroid Build Coastguard Worker os << "," << *it;
135*288bf522SAndroid Build Coastguard Worker }
136*288bf522SAndroid Build Coastguard Worker args.insert(args.end(), {"-t", os.str()});
137*288bf522SAndroid Build Coastguard Worker }
138*288bf522SAndroid Build Coastguard Worker if (impl_->dwarf_callgraph) {
139*288bf522SAndroid Build Coastguard Worker args.push_back("-g");
140*288bf522SAndroid Build Coastguard Worker } else if (impl_->fp_callgraph) {
141*288bf522SAndroid Build Coastguard Worker args.insert(args.end(), {"--call-graph", "fp"});
142*288bf522SAndroid Build Coastguard Worker }
143*288bf522SAndroid Build Coastguard Worker if (impl_->trace_offcpu) {
144*288bf522SAndroid Build Coastguard Worker args.push_back("--trace-offcpu");
145*288bf522SAndroid Build Coastguard Worker }
146*288bf522SAndroid Build Coastguard Worker return args;
147*288bf522SAndroid Build Coastguard Worker }
148*288bf522SAndroid Build Coastguard Worker
Abort(const char * fmt,...)149*288bf522SAndroid Build Coastguard Worker static void Abort(const char* fmt, ...) {
150*288bf522SAndroid Build Coastguard Worker va_list vl;
151*288bf522SAndroid Build Coastguard Worker va_start(vl, fmt);
152*288bf522SAndroid Build Coastguard Worker __android_log_vprint(ANDROID_LOG_FATAL, "simpleperf", fmt, vl);
153*288bf522SAndroid Build Coastguard Worker va_end(vl);
154*288bf522SAndroid Build Coastguard Worker abort();
155*288bf522SAndroid Build Coastguard Worker }
156*288bf522SAndroid Build Coastguard Worker
157*288bf522SAndroid Build Coastguard Worker class ProfileSessionImpl {
158*288bf522SAndroid Build Coastguard Worker public:
ProfileSessionImpl(const std::string & app_data_dir)159*288bf522SAndroid Build Coastguard Worker ProfileSessionImpl(const std::string& app_data_dir)
160*288bf522SAndroid Build Coastguard Worker : app_data_dir_(app_data_dir), simpleperf_data_dir_(app_data_dir + "/simpleperf_data") {}
161*288bf522SAndroid Build Coastguard Worker ~ProfileSessionImpl();
162*288bf522SAndroid Build Coastguard Worker void StartRecording(const std::vector<std::string>& args);
163*288bf522SAndroid Build Coastguard Worker void PauseRecording();
164*288bf522SAndroid Build Coastguard Worker void ResumeRecording();
165*288bf522SAndroid Build Coastguard Worker void StopRecording();
166*288bf522SAndroid Build Coastguard Worker
167*288bf522SAndroid Build Coastguard Worker private:
168*288bf522SAndroid Build Coastguard Worker std::string FindSimpleperf();
169*288bf522SAndroid Build Coastguard Worker std::string FindSimpleperfInTempDir();
170*288bf522SAndroid Build Coastguard Worker void CheckIfPerfEnabled();
171*288bf522SAndroid Build Coastguard Worker std::string GetProperty(const std::string& name);
172*288bf522SAndroid Build Coastguard Worker void CreateSimpleperfDataDir();
173*288bf522SAndroid Build Coastguard Worker void CreateSimpleperfProcess(const std::string& simpleperf_path,
174*288bf522SAndroid Build Coastguard Worker const std::vector<std::string>& record_args);
175*288bf522SAndroid Build Coastguard Worker void SendCmd(const std::string& cmd);
176*288bf522SAndroid Build Coastguard Worker std::string ReadReply();
177*288bf522SAndroid Build Coastguard Worker
178*288bf522SAndroid Build Coastguard Worker enum State {
179*288bf522SAndroid Build Coastguard Worker NOT_YET_STARTED,
180*288bf522SAndroid Build Coastguard Worker STARTED,
181*288bf522SAndroid Build Coastguard Worker PAUSED,
182*288bf522SAndroid Build Coastguard Worker STOPPED,
183*288bf522SAndroid Build Coastguard Worker };
184*288bf522SAndroid Build Coastguard Worker
185*288bf522SAndroid Build Coastguard Worker const std::string app_data_dir_;
186*288bf522SAndroid Build Coastguard Worker const std::string simpleperf_data_dir_;
187*288bf522SAndroid Build Coastguard Worker std::mutex lock_; // Protect all members below.
188*288bf522SAndroid Build Coastguard Worker State state_ = NOT_YET_STARTED;
189*288bf522SAndroid Build Coastguard Worker pid_t simpleperf_pid_ = -1;
190*288bf522SAndroid Build Coastguard Worker int control_fd_ = -1;
191*288bf522SAndroid Build Coastguard Worker int reply_fd_ = -1;
192*288bf522SAndroid Build Coastguard Worker bool trace_offcpu_ = false;
193*288bf522SAndroid Build Coastguard Worker };
194*288bf522SAndroid Build Coastguard Worker
~ProfileSessionImpl()195*288bf522SAndroid Build Coastguard Worker ProfileSessionImpl::~ProfileSessionImpl() {
196*288bf522SAndroid Build Coastguard Worker if (control_fd_ != -1) {
197*288bf522SAndroid Build Coastguard Worker close(control_fd_);
198*288bf522SAndroid Build Coastguard Worker }
199*288bf522SAndroid Build Coastguard Worker if (reply_fd_ != -1) {
200*288bf522SAndroid Build Coastguard Worker close(reply_fd_);
201*288bf522SAndroid Build Coastguard Worker }
202*288bf522SAndroid Build Coastguard Worker }
203*288bf522SAndroid Build Coastguard Worker
StartRecording(const std::vector<std::string> & args)204*288bf522SAndroid Build Coastguard Worker void ProfileSessionImpl::StartRecording(const std::vector<std::string>& args) {
205*288bf522SAndroid Build Coastguard Worker std::lock_guard<std::mutex> guard(lock_);
206*288bf522SAndroid Build Coastguard Worker if (state_ != NOT_YET_STARTED) {
207*288bf522SAndroid Build Coastguard Worker Abort("startRecording: session in wrong state %d", state_);
208*288bf522SAndroid Build Coastguard Worker }
209*288bf522SAndroid Build Coastguard Worker for (const auto& arg : args) {
210*288bf522SAndroid Build Coastguard Worker if (arg == "--trace-offcpu") {
211*288bf522SAndroid Build Coastguard Worker trace_offcpu_ = true;
212*288bf522SAndroid Build Coastguard Worker }
213*288bf522SAndroid Build Coastguard Worker }
214*288bf522SAndroid Build Coastguard Worker std::string simpleperf_path = FindSimpleperf();
215*288bf522SAndroid Build Coastguard Worker CheckIfPerfEnabled();
216*288bf522SAndroid Build Coastguard Worker CreateSimpleperfDataDir();
217*288bf522SAndroid Build Coastguard Worker CreateSimpleperfProcess(simpleperf_path, args);
218*288bf522SAndroid Build Coastguard Worker state_ = STARTED;
219*288bf522SAndroid Build Coastguard Worker }
220*288bf522SAndroid Build Coastguard Worker
PauseRecording()221*288bf522SAndroid Build Coastguard Worker void ProfileSessionImpl::PauseRecording() {
222*288bf522SAndroid Build Coastguard Worker std::lock_guard<std::mutex> guard(lock_);
223*288bf522SAndroid Build Coastguard Worker if (state_ != STARTED) {
224*288bf522SAndroid Build Coastguard Worker Abort("pauseRecording: session in wrong state %d", state_);
225*288bf522SAndroid Build Coastguard Worker }
226*288bf522SAndroid Build Coastguard Worker if (trace_offcpu_) {
227*288bf522SAndroid Build Coastguard Worker Abort("--trace-offcpu doesn't work well with pause/resume recording");
228*288bf522SAndroid Build Coastguard Worker }
229*288bf522SAndroid Build Coastguard Worker SendCmd("pause");
230*288bf522SAndroid Build Coastguard Worker state_ = PAUSED;
231*288bf522SAndroid Build Coastguard Worker }
232*288bf522SAndroid Build Coastguard Worker
ResumeRecording()233*288bf522SAndroid Build Coastguard Worker void ProfileSessionImpl::ResumeRecording() {
234*288bf522SAndroid Build Coastguard Worker std::lock_guard<std::mutex> guard(lock_);
235*288bf522SAndroid Build Coastguard Worker if (state_ != PAUSED) {
236*288bf522SAndroid Build Coastguard Worker Abort("resumeRecording: session in wrong state %d", state_);
237*288bf522SAndroid Build Coastguard Worker }
238*288bf522SAndroid Build Coastguard Worker SendCmd("resume");
239*288bf522SAndroid Build Coastguard Worker state_ = STARTED;
240*288bf522SAndroid Build Coastguard Worker }
241*288bf522SAndroid Build Coastguard Worker
StopRecording()242*288bf522SAndroid Build Coastguard Worker void ProfileSessionImpl::StopRecording() {
243*288bf522SAndroid Build Coastguard Worker std::lock_guard<std::mutex> guard(lock_);
244*288bf522SAndroid Build Coastguard Worker if (state_ != STARTED && state_ != PAUSED) {
245*288bf522SAndroid Build Coastguard Worker Abort("stopRecording: session in wrong state %d", state_);
246*288bf522SAndroid Build Coastguard Worker }
247*288bf522SAndroid Build Coastguard Worker // Send SIGINT to simpleperf to stop recording.
248*288bf522SAndroid Build Coastguard Worker if (kill(simpleperf_pid_, SIGINT) == -1) {
249*288bf522SAndroid Build Coastguard Worker Abort("failed to stop simpleperf: %s", strerror(errno));
250*288bf522SAndroid Build Coastguard Worker }
251*288bf522SAndroid Build Coastguard Worker int status;
252*288bf522SAndroid Build Coastguard Worker pid_t result = TEMP_FAILURE_RETRY(waitpid(simpleperf_pid_, &status, 0));
253*288bf522SAndroid Build Coastguard Worker if (result == -1) {
254*288bf522SAndroid Build Coastguard Worker Abort("failed to call waitpid: %s", strerror(errno));
255*288bf522SAndroid Build Coastguard Worker }
256*288bf522SAndroid Build Coastguard Worker if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
257*288bf522SAndroid Build Coastguard Worker Abort("simpleperf exited with error, status = 0x%x", status);
258*288bf522SAndroid Build Coastguard Worker }
259*288bf522SAndroid Build Coastguard Worker state_ = STOPPED;
260*288bf522SAndroid Build Coastguard Worker }
261*288bf522SAndroid Build Coastguard Worker
SendCmd(const std::string & cmd)262*288bf522SAndroid Build Coastguard Worker void ProfileSessionImpl::SendCmd(const std::string& cmd) {
263*288bf522SAndroid Build Coastguard Worker std::string data = cmd + "\n";
264*288bf522SAndroid Build Coastguard Worker if (TEMP_FAILURE_RETRY(write(control_fd_, &data[0], data.size())) !=
265*288bf522SAndroid Build Coastguard Worker static_cast<ssize_t>(data.size())) {
266*288bf522SAndroid Build Coastguard Worker Abort("failed to send cmd to simpleperf: %s", strerror(errno));
267*288bf522SAndroid Build Coastguard Worker }
268*288bf522SAndroid Build Coastguard Worker if (ReadReply() != "ok") {
269*288bf522SAndroid Build Coastguard Worker Abort("failed to run cmd in simpleperf: %s", cmd.c_str());
270*288bf522SAndroid Build Coastguard Worker }
271*288bf522SAndroid Build Coastguard Worker }
272*288bf522SAndroid Build Coastguard Worker
IsExecutableFile(const std::string & path)273*288bf522SAndroid Build Coastguard Worker static bool IsExecutableFile(const std::string& path) {
274*288bf522SAndroid Build Coastguard Worker struct stat st;
275*288bf522SAndroid Build Coastguard Worker if (stat(path.c_str(), &st) == 0) {
276*288bf522SAndroid Build Coastguard Worker if (S_ISREG(st.st_mode) && (st.st_mode & S_IXUSR)) {
277*288bf522SAndroid Build Coastguard Worker return true;
278*288bf522SAndroid Build Coastguard Worker }
279*288bf522SAndroid Build Coastguard Worker }
280*288bf522SAndroid Build Coastguard Worker return false;
281*288bf522SAndroid Build Coastguard Worker }
282*288bf522SAndroid Build Coastguard Worker
ReadFile(FILE * fp)283*288bf522SAndroid Build Coastguard Worker static std::string ReadFile(FILE* fp) {
284*288bf522SAndroid Build Coastguard Worker std::string s;
285*288bf522SAndroid Build Coastguard Worker if (fp == nullptr) {
286*288bf522SAndroid Build Coastguard Worker return s;
287*288bf522SAndroid Build Coastguard Worker }
288*288bf522SAndroid Build Coastguard Worker char buf[200];
289*288bf522SAndroid Build Coastguard Worker while (true) {
290*288bf522SAndroid Build Coastguard Worker ssize_t n = fread(buf, 1, sizeof(buf), fp);
291*288bf522SAndroid Build Coastguard Worker if (n <= 0) {
292*288bf522SAndroid Build Coastguard Worker break;
293*288bf522SAndroid Build Coastguard Worker }
294*288bf522SAndroid Build Coastguard Worker s.insert(s.end(), buf, buf + n);
295*288bf522SAndroid Build Coastguard Worker }
296*288bf522SAndroid Build Coastguard Worker fclose(fp);
297*288bf522SAndroid Build Coastguard Worker return s;
298*288bf522SAndroid Build Coastguard Worker }
299*288bf522SAndroid Build Coastguard Worker
RunCmd(std::vector<const char * > args,std::string * stdout)300*288bf522SAndroid Build Coastguard Worker static bool RunCmd(std::vector<const char*> args, std::string* stdout) {
301*288bf522SAndroid Build Coastguard Worker int stdout_fd[2];
302*288bf522SAndroid Build Coastguard Worker if (pipe(stdout_fd) != 0) {
303*288bf522SAndroid Build Coastguard Worker return false;
304*288bf522SAndroid Build Coastguard Worker }
305*288bf522SAndroid Build Coastguard Worker args.push_back(nullptr);
306*288bf522SAndroid Build Coastguard Worker // Fork handlers (like gsl_library_close) may hang in a multi-thread environment.
307*288bf522SAndroid Build Coastguard Worker // So we use vfork instead of fork to avoid calling them.
308*288bf522SAndroid Build Coastguard Worker int pid = vfork();
309*288bf522SAndroid Build Coastguard Worker if (pid == -1) {
310*288bf522SAndroid Build Coastguard Worker return false;
311*288bf522SAndroid Build Coastguard Worker }
312*288bf522SAndroid Build Coastguard Worker if (pid == 0) {
313*288bf522SAndroid Build Coastguard Worker // child process
314*288bf522SAndroid Build Coastguard Worker close(stdout_fd[0]);
315*288bf522SAndroid Build Coastguard Worker dup2(stdout_fd[1], 1);
316*288bf522SAndroid Build Coastguard Worker close(stdout_fd[1]);
317*288bf522SAndroid Build Coastguard Worker execvp(const_cast<char*>(args[0]), const_cast<char**>(args.data()));
318*288bf522SAndroid Build Coastguard Worker _exit(1);
319*288bf522SAndroid Build Coastguard Worker }
320*288bf522SAndroid Build Coastguard Worker // parent process
321*288bf522SAndroid Build Coastguard Worker close(stdout_fd[1]);
322*288bf522SAndroid Build Coastguard Worker int status;
323*288bf522SAndroid Build Coastguard Worker pid_t result = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
324*288bf522SAndroid Build Coastguard Worker if (result == -1) {
325*288bf522SAndroid Build Coastguard Worker Abort("failed to call waitpid: %s", strerror(errno));
326*288bf522SAndroid Build Coastguard Worker }
327*288bf522SAndroid Build Coastguard Worker if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
328*288bf522SAndroid Build Coastguard Worker return false;
329*288bf522SAndroid Build Coastguard Worker }
330*288bf522SAndroid Build Coastguard Worker if (stdout == nullptr) {
331*288bf522SAndroid Build Coastguard Worker close(stdout_fd[0]);
332*288bf522SAndroid Build Coastguard Worker } else {
333*288bf522SAndroid Build Coastguard Worker *stdout = ReadFile(fdopen(stdout_fd[0], "r"));
334*288bf522SAndroid Build Coastguard Worker }
335*288bf522SAndroid Build Coastguard Worker return true;
336*288bf522SAndroid Build Coastguard Worker }
337*288bf522SAndroid Build Coastguard Worker
FindSimpleperf()338*288bf522SAndroid Build Coastguard Worker std::string ProfileSessionImpl::FindSimpleperf() {
339*288bf522SAndroid Build Coastguard Worker // 1. Try /data/local/tmp/simpleperf first. Probably it's newer than /system/bin/simpleperf.
340*288bf522SAndroid Build Coastguard Worker std::string simpleperf_path = FindSimpleperfInTempDir();
341*288bf522SAndroid Build Coastguard Worker if (!simpleperf_path.empty()) {
342*288bf522SAndroid Build Coastguard Worker return simpleperf_path;
343*288bf522SAndroid Build Coastguard Worker }
344*288bf522SAndroid Build Coastguard Worker // 2. Try /system/bin/simpleperf, which is available on Android >= Q.
345*288bf522SAndroid Build Coastguard Worker simpleperf_path = "/system/bin/simpleperf";
346*288bf522SAndroid Build Coastguard Worker if (IsExecutableFile(simpleperf_path)) {
347*288bf522SAndroid Build Coastguard Worker return simpleperf_path;
348*288bf522SAndroid Build Coastguard Worker }
349*288bf522SAndroid Build Coastguard Worker Abort("can't find simpleperf on device. Please run api_profiler.py.");
350*288bf522SAndroid Build Coastguard Worker return "";
351*288bf522SAndroid Build Coastguard Worker }
352*288bf522SAndroid Build Coastguard Worker
FindSimpleperfInTempDir()353*288bf522SAndroid Build Coastguard Worker std::string ProfileSessionImpl::FindSimpleperfInTempDir() {
354*288bf522SAndroid Build Coastguard Worker const std::string path = "/data/local/tmp/simpleperf";
355*288bf522SAndroid Build Coastguard Worker if (!IsExecutableFile(path)) {
356*288bf522SAndroid Build Coastguard Worker return "";
357*288bf522SAndroid Build Coastguard Worker }
358*288bf522SAndroid Build Coastguard Worker // Copy it to app_dir to execute it.
359*288bf522SAndroid Build Coastguard Worker const std::string to_path = app_data_dir_ + "/simpleperf";
360*288bf522SAndroid Build Coastguard Worker if (!RunCmd({"/system/bin/cp", path.c_str(), to_path.c_str()}, nullptr)) {
361*288bf522SAndroid Build Coastguard Worker return "";
362*288bf522SAndroid Build Coastguard Worker }
363*288bf522SAndroid Build Coastguard Worker // For apps with target sdk >= 29, executing app data file isn't allowed.
364*288bf522SAndroid Build Coastguard Worker // For android R, app context isn't allowed to use perf_event_open.
365*288bf522SAndroid Build Coastguard Worker // So test executing downloaded simpleperf.
366*288bf522SAndroid Build Coastguard Worker std::string s;
367*288bf522SAndroid Build Coastguard Worker if (!RunCmd({to_path.c_str(), "list", "sw"}, &s)) {
368*288bf522SAndroid Build Coastguard Worker return "";
369*288bf522SAndroid Build Coastguard Worker }
370*288bf522SAndroid Build Coastguard Worker if (s.find("cpu-clock") == std::string::npos) {
371*288bf522SAndroid Build Coastguard Worker return "";
372*288bf522SAndroid Build Coastguard Worker }
373*288bf522SAndroid Build Coastguard Worker return to_path;
374*288bf522SAndroid Build Coastguard Worker }
375*288bf522SAndroid Build Coastguard Worker
CheckIfPerfEnabled()376*288bf522SAndroid Build Coastguard Worker void ProfileSessionImpl::CheckIfPerfEnabled() {
377*288bf522SAndroid Build Coastguard Worker if (GetProperty("persist.simpleperf.profile_app_uid") == std::to_string(getuid())) {
378*288bf522SAndroid Build Coastguard Worker std::string time_str = GetProperty("persist.simpleperf.profile_app_expiration_time");
379*288bf522SAndroid Build Coastguard Worker if (!time_str.empty()) {
380*288bf522SAndroid Build Coastguard Worker errno = 0;
381*288bf522SAndroid Build Coastguard Worker uint64_t expiration_time = strtoull(time_str.data(), nullptr, 10);
382*288bf522SAndroid Build Coastguard Worker if (errno == 0 && expiration_time > time(nullptr)) {
383*288bf522SAndroid Build Coastguard Worker return;
384*288bf522SAndroid Build Coastguard Worker }
385*288bf522SAndroid Build Coastguard Worker }
386*288bf522SAndroid Build Coastguard Worker }
387*288bf522SAndroid Build Coastguard Worker if (GetProperty("security.perf_harden") == "1") {
388*288bf522SAndroid Build Coastguard Worker Abort("Recording app isn't enabled on the device. Please run api_profiler.py.");
389*288bf522SAndroid Build Coastguard Worker }
390*288bf522SAndroid Build Coastguard Worker }
391*288bf522SAndroid Build Coastguard Worker
GetProperty(const std::string & name)392*288bf522SAndroid Build Coastguard Worker std::string ProfileSessionImpl::GetProperty(const std::string& name) {
393*288bf522SAndroid Build Coastguard Worker std::string s;
394*288bf522SAndroid Build Coastguard Worker if (!RunCmd({"/system/bin/getprop", name.c_str()}, &s)) {
395*288bf522SAndroid Build Coastguard Worker return "";
396*288bf522SAndroid Build Coastguard Worker }
397*288bf522SAndroid Build Coastguard Worker return s;
398*288bf522SAndroid Build Coastguard Worker }
399*288bf522SAndroid Build Coastguard Worker
CreateSimpleperfDataDir()400*288bf522SAndroid Build Coastguard Worker void ProfileSessionImpl::CreateSimpleperfDataDir() {
401*288bf522SAndroid Build Coastguard Worker struct stat st;
402*288bf522SAndroid Build Coastguard Worker if (stat(simpleperf_data_dir_.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
403*288bf522SAndroid Build Coastguard Worker return;
404*288bf522SAndroid Build Coastguard Worker }
405*288bf522SAndroid Build Coastguard Worker if (mkdir(simpleperf_data_dir_.c_str(), 0700) == -1) {
406*288bf522SAndroid Build Coastguard Worker Abort("failed to create simpleperf data dir %s: %s", simpleperf_data_dir_.c_str(),
407*288bf522SAndroid Build Coastguard Worker strerror(errno));
408*288bf522SAndroid Build Coastguard Worker }
409*288bf522SAndroid Build Coastguard Worker }
410*288bf522SAndroid Build Coastguard Worker
CreateSimpleperfProcess(const std::string & simpleperf_path,const std::vector<std::string> & record_args)411*288bf522SAndroid Build Coastguard Worker void ProfileSessionImpl::CreateSimpleperfProcess(const std::string& simpleperf_path,
412*288bf522SAndroid Build Coastguard Worker const std::vector<std::string>& record_args) {
413*288bf522SAndroid Build Coastguard Worker // 1. Create control/reply pips.
414*288bf522SAndroid Build Coastguard Worker int control_fd[2];
415*288bf522SAndroid Build Coastguard Worker int reply_fd[2];
416*288bf522SAndroid Build Coastguard Worker if (pipe(control_fd) != 0 || pipe(reply_fd) != 0) {
417*288bf522SAndroid Build Coastguard Worker Abort("failed to call pipe: %s", strerror(errno));
418*288bf522SAndroid Build Coastguard Worker }
419*288bf522SAndroid Build Coastguard Worker
420*288bf522SAndroid Build Coastguard Worker // 2. Prepare simpleperf arguments.
421*288bf522SAndroid Build Coastguard Worker std::vector<std::string> args;
422*288bf522SAndroid Build Coastguard Worker args.emplace_back(simpleperf_path);
423*288bf522SAndroid Build Coastguard Worker args.emplace_back("record");
424*288bf522SAndroid Build Coastguard Worker args.emplace_back("--log-to-android-buffer");
425*288bf522SAndroid Build Coastguard Worker args.insert(args.end(), {"--log", "debug"});
426*288bf522SAndroid Build Coastguard Worker args.emplace_back("--stdio-controls-profiling");
427*288bf522SAndroid Build Coastguard Worker args.emplace_back("--in-app");
428*288bf522SAndroid Build Coastguard Worker args.insert(args.end(), {"--tracepoint-events", "/data/local/tmp/tracepoint_events"});
429*288bf522SAndroid Build Coastguard Worker args.insert(args.end(), record_args.begin(), record_args.end());
430*288bf522SAndroid Build Coastguard Worker char* argv[args.size() + 1];
431*288bf522SAndroid Build Coastguard Worker for (size_t i = 0; i < args.size(); ++i) {
432*288bf522SAndroid Build Coastguard Worker argv[i] = &args[i][0];
433*288bf522SAndroid Build Coastguard Worker }
434*288bf522SAndroid Build Coastguard Worker argv[args.size()] = nullptr;
435*288bf522SAndroid Build Coastguard Worker
436*288bf522SAndroid Build Coastguard Worker // 3. Start simpleperf process.
437*288bf522SAndroid Build Coastguard Worker // Fork handlers (like gsl_library_close) may hang in a multi-thread environment.
438*288bf522SAndroid Build Coastguard Worker // So we use vfork instead of fork to avoid calling them.
439*288bf522SAndroid Build Coastguard Worker int pid = vfork();
440*288bf522SAndroid Build Coastguard Worker if (pid == -1) {
441*288bf522SAndroid Build Coastguard Worker Abort("failed to fork: %s", strerror(errno));
442*288bf522SAndroid Build Coastguard Worker }
443*288bf522SAndroid Build Coastguard Worker if (pid == 0) {
444*288bf522SAndroid Build Coastguard Worker // child process
445*288bf522SAndroid Build Coastguard Worker close(control_fd[1]);
446*288bf522SAndroid Build Coastguard Worker dup2(control_fd[0], 0); // simpleperf read control cmd from fd 0.
447*288bf522SAndroid Build Coastguard Worker close(control_fd[0]);
448*288bf522SAndroid Build Coastguard Worker close(reply_fd[0]);
449*288bf522SAndroid Build Coastguard Worker dup2(reply_fd[1], 1); // simpleperf writes reply to fd 1.
450*288bf522SAndroid Build Coastguard Worker close(reply_fd[0]);
451*288bf522SAndroid Build Coastguard Worker chdir(simpleperf_data_dir_.c_str());
452*288bf522SAndroid Build Coastguard Worker execvp(argv[0], argv);
453*288bf522SAndroid Build Coastguard Worker Abort("failed to call exec: %s", strerror(errno));
454*288bf522SAndroid Build Coastguard Worker }
455*288bf522SAndroid Build Coastguard Worker // parent process
456*288bf522SAndroid Build Coastguard Worker close(control_fd[0]);
457*288bf522SAndroid Build Coastguard Worker control_fd_ = control_fd[1];
458*288bf522SAndroid Build Coastguard Worker close(reply_fd[1]);
459*288bf522SAndroid Build Coastguard Worker reply_fd_ = reply_fd[0];
460*288bf522SAndroid Build Coastguard Worker simpleperf_pid_ = pid;
461*288bf522SAndroid Build Coastguard Worker
462*288bf522SAndroid Build Coastguard Worker // 4. Wait until simpleperf starts recording.
463*288bf522SAndroid Build Coastguard Worker std::string start_flag = ReadReply();
464*288bf522SAndroid Build Coastguard Worker if (start_flag != "started") {
465*288bf522SAndroid Build Coastguard Worker Abort("failed to receive simpleperf start flag");
466*288bf522SAndroid Build Coastguard Worker }
467*288bf522SAndroid Build Coastguard Worker }
468*288bf522SAndroid Build Coastguard Worker
ReadReply()469*288bf522SAndroid Build Coastguard Worker std::string ProfileSessionImpl::ReadReply() {
470*288bf522SAndroid Build Coastguard Worker std::string s;
471*288bf522SAndroid Build Coastguard Worker while (true) {
472*288bf522SAndroid Build Coastguard Worker char c;
473*288bf522SAndroid Build Coastguard Worker ssize_t result = TEMP_FAILURE_RETRY(read(reply_fd_, &c, 1));
474*288bf522SAndroid Build Coastguard Worker if (result <= 0 || c == '\n') {
475*288bf522SAndroid Build Coastguard Worker break;
476*288bf522SAndroid Build Coastguard Worker }
477*288bf522SAndroid Build Coastguard Worker s.push_back(c);
478*288bf522SAndroid Build Coastguard Worker }
479*288bf522SAndroid Build Coastguard Worker return s;
480*288bf522SAndroid Build Coastguard Worker }
481*288bf522SAndroid Build Coastguard Worker
ProfileSession()482*288bf522SAndroid Build Coastguard Worker ProfileSession::ProfileSession() {
483*288bf522SAndroid Build Coastguard Worker FILE* fp = fopen("/proc/self/cmdline", "r");
484*288bf522SAndroid Build Coastguard Worker if (fp == nullptr) {
485*288bf522SAndroid Build Coastguard Worker Abort("failed to open /proc/self/cmdline: %s", strerror(errno));
486*288bf522SAndroid Build Coastguard Worker }
487*288bf522SAndroid Build Coastguard Worker std::string s = ReadFile(fp);
488*288bf522SAndroid Build Coastguard Worker for (int i = 0; i < s.size(); i++) {
489*288bf522SAndroid Build Coastguard Worker if (s[i] == '\0') {
490*288bf522SAndroid Build Coastguard Worker s = s.substr(0, i);
491*288bf522SAndroid Build Coastguard Worker break;
492*288bf522SAndroid Build Coastguard Worker }
493*288bf522SAndroid Build Coastguard Worker }
494*288bf522SAndroid Build Coastguard Worker std::string app_data_dir = "/data/data/" + s;
495*288bf522SAndroid Build Coastguard Worker int uid = getuid();
496*288bf522SAndroid Build Coastguard Worker if (uid >= AID_USER_OFFSET) {
497*288bf522SAndroid Build Coastguard Worker int user_id = uid / AID_USER_OFFSET;
498*288bf522SAndroid Build Coastguard Worker app_data_dir = "/data/user/" + std::to_string(user_id) + "/" + s;
499*288bf522SAndroid Build Coastguard Worker }
500*288bf522SAndroid Build Coastguard Worker impl_ = new ProfileSessionImpl(app_data_dir);
501*288bf522SAndroid Build Coastguard Worker }
502*288bf522SAndroid Build Coastguard Worker
ProfileSession(const std::string & app_data_dir)503*288bf522SAndroid Build Coastguard Worker ProfileSession::ProfileSession(const std::string& app_data_dir)
504*288bf522SAndroid Build Coastguard Worker : impl_(new ProfileSessionImpl(app_data_dir)) {}
505*288bf522SAndroid Build Coastguard Worker
~ProfileSession()506*288bf522SAndroid Build Coastguard Worker ProfileSession::~ProfileSession() {
507*288bf522SAndroid Build Coastguard Worker delete impl_;
508*288bf522SAndroid Build Coastguard Worker }
509*288bf522SAndroid Build Coastguard Worker
StartRecording(const RecordOptions & options)510*288bf522SAndroid Build Coastguard Worker void ProfileSession::StartRecording(const RecordOptions& options) {
511*288bf522SAndroid Build Coastguard Worker StartRecording(options.ToRecordArgs());
512*288bf522SAndroid Build Coastguard Worker }
513*288bf522SAndroid Build Coastguard Worker
StartRecording(const std::vector<std::string> & record_args)514*288bf522SAndroid Build Coastguard Worker void ProfileSession::StartRecording(const std::vector<std::string>& record_args) {
515*288bf522SAndroid Build Coastguard Worker impl_->StartRecording(record_args);
516*288bf522SAndroid Build Coastguard Worker }
517*288bf522SAndroid Build Coastguard Worker
PauseRecording()518*288bf522SAndroid Build Coastguard Worker void ProfileSession::PauseRecording() {
519*288bf522SAndroid Build Coastguard Worker impl_->PauseRecording();
520*288bf522SAndroid Build Coastguard Worker }
521*288bf522SAndroid Build Coastguard Worker
ResumeRecording()522*288bf522SAndroid Build Coastguard Worker void ProfileSession::ResumeRecording() {
523*288bf522SAndroid Build Coastguard Worker impl_->ResumeRecording();
524*288bf522SAndroid Build Coastguard Worker }
525*288bf522SAndroid Build Coastguard Worker
StopRecording()526*288bf522SAndroid Build Coastguard Worker void ProfileSession::StopRecording() {
527*288bf522SAndroid Build Coastguard Worker impl_->StopRecording();
528*288bf522SAndroid Build Coastguard Worker }
529*288bf522SAndroid Build Coastguard Worker
530*288bf522SAndroid Build Coastguard Worker } // namespace simpleperf
531