xref: /aosp_15_r20/system/extras/simpleperf/workload.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker  * Copyright (C) 2015 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 "workload.h"
18*288bf522SAndroid Build Coastguard Worker 
19*288bf522SAndroid Build Coastguard Worker #include <errno.h>
20*288bf522SAndroid Build Coastguard Worker #include <fcntl.h>
21*288bf522SAndroid Build Coastguard Worker #include <sched.h>
22*288bf522SAndroid Build Coastguard Worker #include <sys/prctl.h>
23*288bf522SAndroid Build Coastguard Worker #include <sys/wait.h>
24*288bf522SAndroid Build Coastguard Worker #include <unistd.h>
25*288bf522SAndroid Build Coastguard Worker 
26*288bf522SAndroid Build Coastguard Worker #include <android-base/logging.h>
27*288bf522SAndroid Build Coastguard Worker #include <android-base/strings.h>
28*288bf522SAndroid Build Coastguard Worker 
29*288bf522SAndroid Build Coastguard Worker #include "environment.h"
30*288bf522SAndroid Build Coastguard Worker 
31*288bf522SAndroid Build Coastguard Worker namespace simpleperf {
32*288bf522SAndroid Build Coastguard Worker 
CreateWorkload(const std::vector<std::string> & args)33*288bf522SAndroid Build Coastguard Worker std::unique_ptr<Workload> Workload::CreateWorkload(const std::vector<std::string>& args) {
34*288bf522SAndroid Build Coastguard Worker #if defined(__ANDROID__)
35*288bf522SAndroid Build Coastguard Worker   if (IsInAppUid()) {
36*288bf522SAndroid Build Coastguard Worker     LOG(ERROR) << "Running child command in app uid isn't allowed.";
37*288bf522SAndroid Build Coastguard Worker     return nullptr;
38*288bf522SAndroid Build Coastguard Worker   }
39*288bf522SAndroid Build Coastguard Worker #endif
40*288bf522SAndroid Build Coastguard Worker   std::unique_ptr<Workload> workload(new Workload(args, std::function<void()>()));
41*288bf522SAndroid Build Coastguard Worker   if (workload != nullptr && workload->CreateNewProcess()) {
42*288bf522SAndroid Build Coastguard Worker     return workload;
43*288bf522SAndroid Build Coastguard Worker   }
44*288bf522SAndroid Build Coastguard Worker   return nullptr;
45*288bf522SAndroid Build Coastguard Worker }
46*288bf522SAndroid Build Coastguard Worker 
CreateWorkload(const std::function<void ()> & function)47*288bf522SAndroid Build Coastguard Worker std::unique_ptr<Workload> Workload::CreateWorkload(const std::function<void()>& function) {
48*288bf522SAndroid Build Coastguard Worker   std::unique_ptr<Workload> workload(new Workload(std::vector<std::string>(), function));
49*288bf522SAndroid Build Coastguard Worker   if (workload != nullptr && workload->CreateNewProcess()) {
50*288bf522SAndroid Build Coastguard Worker     return workload;
51*288bf522SAndroid Build Coastguard Worker   }
52*288bf522SAndroid Build Coastguard Worker   return nullptr;
53*288bf522SAndroid Build Coastguard Worker }
54*288bf522SAndroid Build Coastguard Worker 
RunCmd(const std::vector<std::string> & args,bool report_error)55*288bf522SAndroid Build Coastguard Worker bool Workload::RunCmd(const std::vector<std::string>& args, bool report_error) {
56*288bf522SAndroid Build Coastguard Worker   std::string arg_str = android::base::Join(args, ' ');
57*288bf522SAndroid Build Coastguard Worker   int ret = system(arg_str.c_str());
58*288bf522SAndroid Build Coastguard Worker   if (ret != 0 && report_error) {
59*288bf522SAndroid Build Coastguard Worker     LOG(ERROR) << "Failed to run cmd " << arg_str << ", exitcode " << ret;
60*288bf522SAndroid Build Coastguard Worker     return false;
61*288bf522SAndroid Build Coastguard Worker   }
62*288bf522SAndroid Build Coastguard Worker   return ret == 0;
63*288bf522SAndroid Build Coastguard Worker }
64*288bf522SAndroid Build Coastguard Worker 
Workload(const std::vector<std::string> & args,const std::function<void ()> & function)65*288bf522SAndroid Build Coastguard Worker Workload::Workload(const std::vector<std::string>& args, const std::function<void()>& function)
66*288bf522SAndroid Build Coastguard Worker     : work_state_(NotYetCreateNewProcess),
67*288bf522SAndroid Build Coastguard Worker       child_proc_args_(args),
68*288bf522SAndroid Build Coastguard Worker       child_proc_function_(function),
69*288bf522SAndroid Build Coastguard Worker       work_pid_(-1),
70*288bf522SAndroid Build Coastguard Worker       start_signal_fd_(-1),
71*288bf522SAndroid Build Coastguard Worker       exec_child_fd_(-1) {
72*288bf522SAndroid Build Coastguard Worker   kill_function_ = [](pid_t pid) { kill(pid, SIGKILL); };
73*288bf522SAndroid Build Coastguard Worker }
74*288bf522SAndroid Build Coastguard Worker 
~Workload()75*288bf522SAndroid Build Coastguard Worker Workload::~Workload() {
76*288bf522SAndroid Build Coastguard Worker   if (work_pid_ != -1 && work_state_ != NotYetCreateNewProcess) {
77*288bf522SAndroid Build Coastguard Worker     if (!Workload::WaitChildProcess(false, false, nullptr)) {
78*288bf522SAndroid Build Coastguard Worker       kill_function_(work_pid_);
79*288bf522SAndroid Build Coastguard Worker       Workload::WaitChildProcess(true, true, nullptr);
80*288bf522SAndroid Build Coastguard Worker     }
81*288bf522SAndroid Build Coastguard Worker   }
82*288bf522SAndroid Build Coastguard Worker   if (start_signal_fd_ != -1) {
83*288bf522SAndroid Build Coastguard Worker     close(start_signal_fd_);
84*288bf522SAndroid Build Coastguard Worker   }
85*288bf522SAndroid Build Coastguard Worker   if (exec_child_fd_ != -1) {
86*288bf522SAndroid Build Coastguard Worker     close(exec_child_fd_);
87*288bf522SAndroid Build Coastguard Worker   }
88*288bf522SAndroid Build Coastguard Worker }
89*288bf522SAndroid Build Coastguard Worker 
CreateNewProcess()90*288bf522SAndroid Build Coastguard Worker bool Workload::CreateNewProcess() {
91*288bf522SAndroid Build Coastguard Worker   CHECK_EQ(work_state_, NotYetCreateNewProcess);
92*288bf522SAndroid Build Coastguard Worker 
93*288bf522SAndroid Build Coastguard Worker   int start_signal_pipe[2];
94*288bf522SAndroid Build Coastguard Worker   if (pipe2(start_signal_pipe, O_CLOEXEC) != 0) {
95*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "pipe2() failed";
96*288bf522SAndroid Build Coastguard Worker     return false;
97*288bf522SAndroid Build Coastguard Worker   }
98*288bf522SAndroid Build Coastguard Worker 
99*288bf522SAndroid Build Coastguard Worker   int exec_child_pipe[2];
100*288bf522SAndroid Build Coastguard Worker   if (pipe2(exec_child_pipe, O_CLOEXEC) != 0) {
101*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "pipe2() failed";
102*288bf522SAndroid Build Coastguard Worker     close(start_signal_pipe[0]);
103*288bf522SAndroid Build Coastguard Worker     close(start_signal_pipe[1]);
104*288bf522SAndroid Build Coastguard Worker     return false;
105*288bf522SAndroid Build Coastguard Worker   }
106*288bf522SAndroid Build Coastguard Worker 
107*288bf522SAndroid Build Coastguard Worker   pid_t pid = fork();
108*288bf522SAndroid Build Coastguard Worker   if (pid == -1) {
109*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "fork() failed";
110*288bf522SAndroid Build Coastguard Worker     close(start_signal_pipe[0]);
111*288bf522SAndroid Build Coastguard Worker     close(start_signal_pipe[1]);
112*288bf522SAndroid Build Coastguard Worker     close(exec_child_pipe[0]);
113*288bf522SAndroid Build Coastguard Worker     close(exec_child_pipe[1]);
114*288bf522SAndroid Build Coastguard Worker     return false;
115*288bf522SAndroid Build Coastguard Worker   } else if (pid == 0) {
116*288bf522SAndroid Build Coastguard Worker     // In child process.
117*288bf522SAndroid Build Coastguard Worker     close(start_signal_pipe[1]);
118*288bf522SAndroid Build Coastguard Worker     close(exec_child_pipe[0]);
119*288bf522SAndroid Build Coastguard Worker     ChildProcessFn(start_signal_pipe[0], exec_child_pipe[1]);
120*288bf522SAndroid Build Coastguard Worker     _exit(0);
121*288bf522SAndroid Build Coastguard Worker   }
122*288bf522SAndroid Build Coastguard Worker   // In parent process.
123*288bf522SAndroid Build Coastguard Worker   close(start_signal_pipe[0]);
124*288bf522SAndroid Build Coastguard Worker   close(exec_child_pipe[1]);
125*288bf522SAndroid Build Coastguard Worker   start_signal_fd_ = start_signal_pipe[1];
126*288bf522SAndroid Build Coastguard Worker   exec_child_fd_ = exec_child_pipe[0];
127*288bf522SAndroid Build Coastguard Worker   work_pid_ = pid;
128*288bf522SAndroid Build Coastguard Worker   work_state_ = NotYetStartNewProcess;
129*288bf522SAndroid Build Coastguard Worker   return true;
130*288bf522SAndroid Build Coastguard Worker }
131*288bf522SAndroid Build Coastguard Worker 
ChildProcessFn(int start_signal_fd,int exec_child_fd)132*288bf522SAndroid Build Coastguard Worker void Workload::ChildProcessFn(int start_signal_fd, int exec_child_fd) {
133*288bf522SAndroid Build Coastguard Worker   // Die if parent exits.
134*288bf522SAndroid Build Coastguard Worker   prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0);
135*288bf522SAndroid Build Coastguard Worker 
136*288bf522SAndroid Build Coastguard Worker   char start_signal = 0;
137*288bf522SAndroid Build Coastguard Worker   ssize_t nread = TEMP_FAILURE_RETRY(read(start_signal_fd, &start_signal, 1));
138*288bf522SAndroid Build Coastguard Worker   if (nread == 1 && start_signal == 1) {
139*288bf522SAndroid Build Coastguard Worker     close(start_signal_fd);
140*288bf522SAndroid Build Coastguard Worker     if (child_proc_function_) {
141*288bf522SAndroid Build Coastguard Worker       close(exec_child_fd);
142*288bf522SAndroid Build Coastguard Worker       child_proc_function_();
143*288bf522SAndroid Build Coastguard Worker     } else {
144*288bf522SAndroid Build Coastguard Worker       char* argv[child_proc_args_.size() + 1];
145*288bf522SAndroid Build Coastguard Worker       for (size_t i = 0; i < child_proc_args_.size(); ++i) {
146*288bf522SAndroid Build Coastguard Worker         argv[i] = &child_proc_args_[i][0];
147*288bf522SAndroid Build Coastguard Worker       }
148*288bf522SAndroid Build Coastguard Worker       argv[child_proc_args_.size()] = nullptr;
149*288bf522SAndroid Build Coastguard Worker       execvp(argv[0], argv);
150*288bf522SAndroid Build Coastguard Worker       // If execvp() succeed, we will not arrive here. But if it failed, we need to
151*288bf522SAndroid Build Coastguard Worker       // report the failure to the parent process by writing 1 to exec_child_fd.
152*288bf522SAndroid Build Coastguard Worker       int saved_errno = errno;
153*288bf522SAndroid Build Coastguard Worker       char exec_child_failed = 1;
154*288bf522SAndroid Build Coastguard Worker       TEMP_FAILURE_RETRY(write(exec_child_fd, &exec_child_failed, 1));
155*288bf522SAndroid Build Coastguard Worker       close(exec_child_fd);
156*288bf522SAndroid Build Coastguard Worker       errno = saved_errno;
157*288bf522SAndroid Build Coastguard Worker       PLOG(ERROR) << "child process failed to execvp(" << argv[0] << ")";
158*288bf522SAndroid Build Coastguard Worker     }
159*288bf522SAndroid Build Coastguard Worker   } else {
160*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "child process failed to receive start_signal, nread = " << nread;
161*288bf522SAndroid Build Coastguard Worker   }
162*288bf522SAndroid Build Coastguard Worker }
163*288bf522SAndroid Build Coastguard Worker 
SetCpuAffinity(int cpu)164*288bf522SAndroid Build Coastguard Worker bool Workload::SetCpuAffinity(int cpu) {
165*288bf522SAndroid Build Coastguard Worker   CHECK_EQ(work_state_, NotYetStartNewProcess);
166*288bf522SAndroid Build Coastguard Worker   cpu_set_t mask;
167*288bf522SAndroid Build Coastguard Worker   CPU_ZERO(&mask);
168*288bf522SAndroid Build Coastguard Worker   CPU_SET(cpu, &mask);
169*288bf522SAndroid Build Coastguard Worker   if (sched_setaffinity(GetPid(), sizeof(mask), &mask) != 0) {
170*288bf522SAndroid Build Coastguard Worker     PLOG(WARNING) << "sched_setaffinity failed";
171*288bf522SAndroid Build Coastguard Worker     return false;
172*288bf522SAndroid Build Coastguard Worker   }
173*288bf522SAndroid Build Coastguard Worker   return true;
174*288bf522SAndroid Build Coastguard Worker }
175*288bf522SAndroid Build Coastguard Worker 
Start()176*288bf522SAndroid Build Coastguard Worker bool Workload::Start() {
177*288bf522SAndroid Build Coastguard Worker   CHECK_EQ(work_state_, NotYetStartNewProcess);
178*288bf522SAndroid Build Coastguard Worker   char start_signal = 1;
179*288bf522SAndroid Build Coastguard Worker   ssize_t nwrite = TEMP_FAILURE_RETRY(write(start_signal_fd_, &start_signal, 1));
180*288bf522SAndroid Build Coastguard Worker   if (nwrite != 1) {
181*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "write start signal failed";
182*288bf522SAndroid Build Coastguard Worker     return false;
183*288bf522SAndroid Build Coastguard Worker   }
184*288bf522SAndroid Build Coastguard Worker   char exec_child_failed;
185*288bf522SAndroid Build Coastguard Worker   ssize_t nread = TEMP_FAILURE_RETRY(read(exec_child_fd_, &exec_child_failed, 1));
186*288bf522SAndroid Build Coastguard Worker   if (nread != 0) {
187*288bf522SAndroid Build Coastguard Worker     if (nread == -1) {
188*288bf522SAndroid Build Coastguard Worker       PLOG(ERROR) << "failed to receive error message from child process";
189*288bf522SAndroid Build Coastguard Worker     } else {
190*288bf522SAndroid Build Coastguard Worker       LOG(ERROR) << "received error message from child process";
191*288bf522SAndroid Build Coastguard Worker     }
192*288bf522SAndroid Build Coastguard Worker     return false;
193*288bf522SAndroid Build Coastguard Worker   }
194*288bf522SAndroid Build Coastguard Worker   work_state_ = Started;
195*288bf522SAndroid Build Coastguard Worker   return true;
196*288bf522SAndroid Build Coastguard Worker }
197*288bf522SAndroid Build Coastguard Worker 
WaitChildProcess(bool wait_forever,int * exit_code)198*288bf522SAndroid Build Coastguard Worker bool Workload::WaitChildProcess(bool wait_forever, int* exit_code) {
199*288bf522SAndroid Build Coastguard Worker   return WaitChildProcess(wait_forever, false, exit_code);
200*288bf522SAndroid Build Coastguard Worker }
201*288bf522SAndroid Build Coastguard Worker 
WaitChildProcess(bool wait_forever,bool is_child_killed,int * exit_code)202*288bf522SAndroid Build Coastguard Worker bool Workload::WaitChildProcess(bool wait_forever, bool is_child_killed, int* exit_code) {
203*288bf522SAndroid Build Coastguard Worker   if (work_state_ == Finished) {
204*288bf522SAndroid Build Coastguard Worker     return true;
205*288bf522SAndroid Build Coastguard Worker   }
206*288bf522SAndroid Build Coastguard Worker   bool finished = false;
207*288bf522SAndroid Build Coastguard Worker   int status;
208*288bf522SAndroid Build Coastguard Worker   pid_t result = TEMP_FAILURE_RETRY(waitpid(work_pid_, &status, (wait_forever ? 0 : WNOHANG)));
209*288bf522SAndroid Build Coastguard Worker   if (result == work_pid_) {
210*288bf522SAndroid Build Coastguard Worker     finished = true;
211*288bf522SAndroid Build Coastguard Worker     work_state_ = Finished;
212*288bf522SAndroid Build Coastguard Worker     if (WIFSIGNALED(status)) {
213*288bf522SAndroid Build Coastguard Worker       if (!(is_child_killed && WTERMSIG(status) == SIGKILL)) {
214*288bf522SAndroid Build Coastguard Worker         LOG(WARNING) << "child process was terminated by signal " << strsignal(WTERMSIG(status));
215*288bf522SAndroid Build Coastguard Worker       }
216*288bf522SAndroid Build Coastguard Worker     } else if (WIFEXITED(status)) {
217*288bf522SAndroid Build Coastguard Worker       if (exit_code != nullptr) {
218*288bf522SAndroid Build Coastguard Worker         *exit_code = WEXITSTATUS(status);
219*288bf522SAndroid Build Coastguard Worker       } else if (WEXITSTATUS(status) != 0) {
220*288bf522SAndroid Build Coastguard Worker         LOG(WARNING) << "child process exited with exit code " << WEXITSTATUS(status);
221*288bf522SAndroid Build Coastguard Worker       }
222*288bf522SAndroid Build Coastguard Worker     }
223*288bf522SAndroid Build Coastguard Worker   } else if (result == -1) {
224*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "waitpid() failed";
225*288bf522SAndroid Build Coastguard Worker   }
226*288bf522SAndroid Build Coastguard Worker   return finished;
227*288bf522SAndroid Build Coastguard Worker }
228*288bf522SAndroid Build Coastguard Worker 
229*288bf522SAndroid Build Coastguard Worker }  // namespace simpleperf
230