1*eb293b8fSAndroid Build Coastguard Worker /*
2*eb293b8fSAndroid Build Coastguard Worker * Copyright (C) 2022 The Android Open Source Project
3*eb293b8fSAndroid Build Coastguard Worker *
4*eb293b8fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*eb293b8fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*eb293b8fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*eb293b8fSAndroid Build Coastguard Worker *
8*eb293b8fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*eb293b8fSAndroid Build Coastguard Worker *
10*eb293b8fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*eb293b8fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*eb293b8fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*eb293b8fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*eb293b8fSAndroid Build Coastguard Worker * limitations under the License.
15*eb293b8fSAndroid Build Coastguard Worker */
16*eb293b8fSAndroid Build Coastguard Worker
17*eb293b8fSAndroid Build Coastguard Worker #include <errno.h>
18*eb293b8fSAndroid Build Coastguard Worker #include <stdint.h>
19*eb293b8fSAndroid Build Coastguard Worker #include <stdio.h>
20*eb293b8fSAndroid Build Coastguard Worker #include <sys/ptrace.h>
21*eb293b8fSAndroid Build Coastguard Worker #include <sys/types.h>
22*eb293b8fSAndroid Build Coastguard Worker #include <sys/wait.h>
23*eb293b8fSAndroid Build Coastguard Worker #include <time.h>
24*eb293b8fSAndroid Build Coastguard Worker #include <unistd.h>
25*eb293b8fSAndroid Build Coastguard Worker
26*eb293b8fSAndroid Build Coastguard Worker #include "PidUtils.h"
27*eb293b8fSAndroid Build Coastguard Worker
28*eb293b8fSAndroid Build Coastguard Worker namespace unwindstack {
29*eb293b8fSAndroid Build Coastguard Worker
Exited(pid_t pid)30*eb293b8fSAndroid Build Coastguard Worker static bool Exited(pid_t pid) {
31*eb293b8fSAndroid Build Coastguard Worker int status;
32*eb293b8fSAndroid Build Coastguard Worker pid_t wait_pid = waitpid(pid, &status, WNOHANG);
33*eb293b8fSAndroid Build Coastguard Worker if (wait_pid != pid) {
34*eb293b8fSAndroid Build Coastguard Worker return false;
35*eb293b8fSAndroid Build Coastguard Worker }
36*eb293b8fSAndroid Build Coastguard Worker
37*eb293b8fSAndroid Build Coastguard Worker if (WIFEXITED(status)) {
38*eb293b8fSAndroid Build Coastguard Worker fprintf(stderr, "%d died: Process exited with code %d\n", pid, WEXITSTATUS(status));
39*eb293b8fSAndroid Build Coastguard Worker } else if (WIFSIGNALED(status)) {
40*eb293b8fSAndroid Build Coastguard Worker fprintf(stderr, "%d died: Process exited due to signal %d\n", pid, WTERMSIG(status));
41*eb293b8fSAndroid Build Coastguard Worker } else {
42*eb293b8fSAndroid Build Coastguard Worker fprintf(stderr, "%d died: Process finished for unknown reason\n", pid);
43*eb293b8fSAndroid Build Coastguard Worker }
44*eb293b8fSAndroid Build Coastguard Worker return true;
45*eb293b8fSAndroid Build Coastguard Worker }
46*eb293b8fSAndroid Build Coastguard Worker
Quiesce(pid_t pid)47*eb293b8fSAndroid Build Coastguard Worker bool Quiesce(pid_t pid) {
48*eb293b8fSAndroid Build Coastguard Worker siginfo_t si;
49*eb293b8fSAndroid Build Coastguard Worker // Wait for up to 10 seconds.
50*eb293b8fSAndroid Build Coastguard Worker for (time_t start_time = time(nullptr); time(nullptr) - start_time < 10;) {
51*eb293b8fSAndroid Build Coastguard Worker if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
52*eb293b8fSAndroid Build Coastguard Worker return true;
53*eb293b8fSAndroid Build Coastguard Worker }
54*eb293b8fSAndroid Build Coastguard Worker if (errno != ESRCH) {
55*eb293b8fSAndroid Build Coastguard Worker if (errno == EINVAL) {
56*eb293b8fSAndroid Build Coastguard Worker // The process is in group-stop state, so try and kick the
57*eb293b8fSAndroid Build Coastguard Worker // process out of that state.
58*eb293b8fSAndroid Build Coastguard Worker if (ptrace(PTRACE_LISTEN, pid, 0, 0) == -1) {
59*eb293b8fSAndroid Build Coastguard Worker // Cannot recover from this, so just pretend it worked and see
60*eb293b8fSAndroid Build Coastguard Worker // if we can unwind.
61*eb293b8fSAndroid Build Coastguard Worker return true;
62*eb293b8fSAndroid Build Coastguard Worker }
63*eb293b8fSAndroid Build Coastguard Worker } else {
64*eb293b8fSAndroid Build Coastguard Worker perror("ptrace getsiginfo failed");
65*eb293b8fSAndroid Build Coastguard Worker return false;
66*eb293b8fSAndroid Build Coastguard Worker }
67*eb293b8fSAndroid Build Coastguard Worker }
68*eb293b8fSAndroid Build Coastguard Worker usleep(5000);
69*eb293b8fSAndroid Build Coastguard Worker }
70*eb293b8fSAndroid Build Coastguard Worker fprintf(stderr, "%d: Did not quiesce in 10 seconds\n", pid);
71*eb293b8fSAndroid Build Coastguard Worker return false;
72*eb293b8fSAndroid Build Coastguard Worker }
73*eb293b8fSAndroid Build Coastguard Worker
Attach(pid_t pid)74*eb293b8fSAndroid Build Coastguard Worker bool Attach(pid_t pid) {
75*eb293b8fSAndroid Build Coastguard Worker // Wait up to 45 seconds to attach.
76*eb293b8fSAndroid Build Coastguard Worker for (time_t start_time = time(nullptr); time(nullptr) - start_time < 45;) {
77*eb293b8fSAndroid Build Coastguard Worker if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
78*eb293b8fSAndroid Build Coastguard Worker break;
79*eb293b8fSAndroid Build Coastguard Worker }
80*eb293b8fSAndroid Build Coastguard Worker if (errno != ESRCH) {
81*eb293b8fSAndroid Build Coastguard Worker perror("Failed to attach");
82*eb293b8fSAndroid Build Coastguard Worker return false;
83*eb293b8fSAndroid Build Coastguard Worker }
84*eb293b8fSAndroid Build Coastguard Worker usleep(5000);
85*eb293b8fSAndroid Build Coastguard Worker }
86*eb293b8fSAndroid Build Coastguard Worker
87*eb293b8fSAndroid Build Coastguard Worker if (Quiesce(pid)) {
88*eb293b8fSAndroid Build Coastguard Worker return true;
89*eb293b8fSAndroid Build Coastguard Worker }
90*eb293b8fSAndroid Build Coastguard Worker
91*eb293b8fSAndroid Build Coastguard Worker if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
92*eb293b8fSAndroid Build Coastguard Worker perror("Failed to detach");
93*eb293b8fSAndroid Build Coastguard Worker }
94*eb293b8fSAndroid Build Coastguard Worker return false;
95*eb293b8fSAndroid Build Coastguard Worker }
96*eb293b8fSAndroid Build Coastguard Worker
Detach(pid_t pid)97*eb293b8fSAndroid Build Coastguard Worker bool Detach(pid_t pid) {
98*eb293b8fSAndroid Build Coastguard Worker if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
99*eb293b8fSAndroid Build Coastguard Worker perror("ptrace detach failed");
100*eb293b8fSAndroid Build Coastguard Worker return false;
101*eb293b8fSAndroid Build Coastguard Worker }
102*eb293b8fSAndroid Build Coastguard Worker return true;
103*eb293b8fSAndroid Build Coastguard Worker }
104*eb293b8fSAndroid Build Coastguard Worker
105*eb293b8fSAndroid Build Coastguard Worker static constexpr time_t kMaxWaitTimeSeconds = 30;
106*eb293b8fSAndroid Build Coastguard Worker
WaitForPidState(pid_t pid,const std::function<PidRunEnum ()> & state_check_func)107*eb293b8fSAndroid Build Coastguard Worker bool WaitForPidState(pid_t pid, const std::function<PidRunEnum()>& state_check_func) {
108*eb293b8fSAndroid Build Coastguard Worker PidRunEnum status = PID_RUN_KEEP_GOING;
109*eb293b8fSAndroid Build Coastguard Worker for (time_t start_time = time(nullptr);
110*eb293b8fSAndroid Build Coastguard Worker time(nullptr) - start_time < kMaxWaitTimeSeconds && status == PID_RUN_KEEP_GOING;) {
111*eb293b8fSAndroid Build Coastguard Worker if (Attach(pid)) {
112*eb293b8fSAndroid Build Coastguard Worker status = state_check_func();
113*eb293b8fSAndroid Build Coastguard Worker if (status == PID_RUN_PASS) {
114*eb293b8fSAndroid Build Coastguard Worker return true;
115*eb293b8fSAndroid Build Coastguard Worker }
116*eb293b8fSAndroid Build Coastguard Worker
117*eb293b8fSAndroid Build Coastguard Worker if (!Detach(pid)) {
118*eb293b8fSAndroid Build Coastguard Worker return false;
119*eb293b8fSAndroid Build Coastguard Worker }
120*eb293b8fSAndroid Build Coastguard Worker } else if (Exited(pid)) {
121*eb293b8fSAndroid Build Coastguard Worker return false;
122*eb293b8fSAndroid Build Coastguard Worker }
123*eb293b8fSAndroid Build Coastguard Worker usleep(5000);
124*eb293b8fSAndroid Build Coastguard Worker }
125*eb293b8fSAndroid Build Coastguard Worker if (status == PID_RUN_KEEP_GOING) {
126*eb293b8fSAndroid Build Coastguard Worker fprintf(stderr, "Timed out waiting for pid %d to be ready\n", pid);
127*eb293b8fSAndroid Build Coastguard Worker }
128*eb293b8fSAndroid Build Coastguard Worker return status == PID_RUN_PASS;
129*eb293b8fSAndroid Build Coastguard Worker }
130*eb293b8fSAndroid Build Coastguard Worker
WaitForPidStateAfterAttach(pid_t pid,const std::function<PidRunEnum ()> & state_check_func)131*eb293b8fSAndroid Build Coastguard Worker bool WaitForPidStateAfterAttach(pid_t pid, const std::function<PidRunEnum()>& state_check_func) {
132*eb293b8fSAndroid Build Coastguard Worker PidRunEnum status;
133*eb293b8fSAndroid Build Coastguard Worker time_t start_time = time(nullptr);
134*eb293b8fSAndroid Build Coastguard Worker do {
135*eb293b8fSAndroid Build Coastguard Worker status = state_check_func();
136*eb293b8fSAndroid Build Coastguard Worker if (status == PID_RUN_PASS) {
137*eb293b8fSAndroid Build Coastguard Worker return true;
138*eb293b8fSAndroid Build Coastguard Worker }
139*eb293b8fSAndroid Build Coastguard Worker if (!Detach(pid)) {
140*eb293b8fSAndroid Build Coastguard Worker return false;
141*eb293b8fSAndroid Build Coastguard Worker }
142*eb293b8fSAndroid Build Coastguard Worker usleep(5000);
143*eb293b8fSAndroid Build Coastguard Worker } while (time(nullptr) - start_time < kMaxWaitTimeSeconds && status == PID_RUN_KEEP_GOING &&
144*eb293b8fSAndroid Build Coastguard Worker Attach(pid));
145*eb293b8fSAndroid Build Coastguard Worker if (status == PID_RUN_KEEP_GOING) {
146*eb293b8fSAndroid Build Coastguard Worker fprintf(stderr, "Timed out waiting for pid %d to be ready\n", pid);
147*eb293b8fSAndroid Build Coastguard Worker }
148*eb293b8fSAndroid Build Coastguard Worker return status == PID_RUN_PASS;
149*eb293b8fSAndroid Build Coastguard Worker }
150*eb293b8fSAndroid Build Coastguard Worker
151*eb293b8fSAndroid Build Coastguard Worker } // namespace unwindstack
152