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