1800b99b8Sopenharmony_ci/*
2800b99b8Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
3800b99b8Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4800b99b8Sopenharmony_ci * you may not use this file except in compliance with the License.
5800b99b8Sopenharmony_ci * You may obtain a copy of the License at
6800b99b8Sopenharmony_ci *
7800b99b8Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8800b99b8Sopenharmony_ci *
9800b99b8Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10800b99b8Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11800b99b8Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12800b99b8Sopenharmony_ci * See the License for the specific language governing permissions and
13800b99b8Sopenharmony_ci * limitations under the License.
14800b99b8Sopenharmony_ci */
15800b99b8Sopenharmony_ci
16800b99b8Sopenharmony_ci#include "pid_utils.h"
17800b99b8Sopenharmony_ci#include <cerrno>
18800b99b8Sopenharmony_ci#include <cstdint>
19800b99b8Sopenharmony_ci#include <cstdio>
20800b99b8Sopenharmony_ci#include <ctime>
21800b99b8Sopenharmony_ci#include <sys/ptrace.h>
22800b99b8Sopenharmony_ci#include <sys/types.h>
23800b99b8Sopenharmony_ci#include <sys/wait.h>
24800b99b8Sopenharmony_ci#include <unistd.h>
25800b99b8Sopenharmony_ci#include "dfx_log.h"
26800b99b8Sopenharmony_ci
27800b99b8Sopenharmony_cinamespace OHOS {
28800b99b8Sopenharmony_cinamespace HiviewDFX {
29800b99b8Sopenharmony_cinamespace {
30800b99b8Sopenharmony_ci#undef LOG_DOMAIN
31800b99b8Sopenharmony_ci#undef LOG_TAG
32800b99b8Sopenharmony_ci#define LOG_DOMAIN 0xD002D11
33800b99b8Sopenharmony_ci#define LOG_TAG "DfxPidUtils"
34800b99b8Sopenharmony_ci
35800b99b8Sopenharmony_cistatic constexpr time_t MAX_WAIT_TIME_SECONDS = 30;
36800b99b8Sopenharmony_cistatic constexpr time_t USLEEP_TIME = 5000;
37800b99b8Sopenharmony_ci}
38800b99b8Sopenharmony_ci
39800b99b8Sopenharmony_cistatic bool Exited(pid_t pid)
40800b99b8Sopenharmony_ci{
41800b99b8Sopenharmony_ci    int status;
42800b99b8Sopenharmony_ci    pid_t waitPid = waitpid(pid, &status, WNOHANG);
43800b99b8Sopenharmony_ci    if (waitPid != pid) {
44800b99b8Sopenharmony_ci        return false;
45800b99b8Sopenharmony_ci    }
46800b99b8Sopenharmony_ci
47800b99b8Sopenharmony_ci    if (WIFEXITED(status)) {
48800b99b8Sopenharmony_ci        DFXLOGE("%{public}d died: Process exited with code %{public}d", pid, WEXITSTATUS(status));
49800b99b8Sopenharmony_ci    } else if (WIFSIGNALED(status)) {
50800b99b8Sopenharmony_ci        DFXLOGE("%{public}d died: Process exited due to signal %{public}d", pid, WTERMSIG(status));
51800b99b8Sopenharmony_ci    } else {
52800b99b8Sopenharmony_ci        DFXLOGE("%{public}d died: Process finished for unknown reason", pid);
53800b99b8Sopenharmony_ci    }
54800b99b8Sopenharmony_ci    return true;
55800b99b8Sopenharmony_ci}
56800b99b8Sopenharmony_ci
57800b99b8Sopenharmony_cibool PidUtils::Quiesce(pid_t pid)
58800b99b8Sopenharmony_ci{
59800b99b8Sopenharmony_ci    siginfo_t si;
60800b99b8Sopenharmony_ci    // Wait for up to 10 seconds.
61800b99b8Sopenharmony_ci    for (time_t startTime = time(nullptr); time(nullptr) - startTime < 10;) {
62800b99b8Sopenharmony_ci        if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
63800b99b8Sopenharmony_ci            return true;
64800b99b8Sopenharmony_ci        }
65800b99b8Sopenharmony_ci        if (errno != ESRCH) {
66800b99b8Sopenharmony_ci            if (errno != EINVAL) {
67800b99b8Sopenharmony_ci                DFXLOGE("ptrace getsiginfo failed");
68800b99b8Sopenharmony_ci                return false;
69800b99b8Sopenharmony_ci            }
70800b99b8Sopenharmony_ci            // The process is in group-stop state, so try and kick the process out of that state.
71800b99b8Sopenharmony_ci            if (ptrace(PTRACE_LISTEN, pid, 0, 0) == -1) {
72800b99b8Sopenharmony_ci                // Cannot recover from this, so just pretend it worked and see if we can unwind.
73800b99b8Sopenharmony_ci                return true;
74800b99b8Sopenharmony_ci            }
75800b99b8Sopenharmony_ci        }
76800b99b8Sopenharmony_ci        usleep(USLEEP_TIME);
77800b99b8Sopenharmony_ci    }
78800b99b8Sopenharmony_ci    DFXLOGE("%{public}d: Did not quiesce in 10 seconds", pid);
79800b99b8Sopenharmony_ci    return false;
80800b99b8Sopenharmony_ci}
81800b99b8Sopenharmony_ci
82800b99b8Sopenharmony_cibool PidUtils::Attach(pid_t pid)
83800b99b8Sopenharmony_ci{
84800b99b8Sopenharmony_ci    // Wait up to 45 seconds to attach.
85800b99b8Sopenharmony_ci    for (time_t startTime = time(nullptr); time(nullptr) - startTime < 45;) {
86800b99b8Sopenharmony_ci        if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
87800b99b8Sopenharmony_ci            break;
88800b99b8Sopenharmony_ci        }
89800b99b8Sopenharmony_ci        if (errno != ESRCH) {
90800b99b8Sopenharmony_ci            DFXLOGE("Failed to attach");
91800b99b8Sopenharmony_ci            return false;
92800b99b8Sopenharmony_ci        }
93800b99b8Sopenharmony_ci        usleep(USLEEP_TIME);
94800b99b8Sopenharmony_ci    }
95800b99b8Sopenharmony_ci
96800b99b8Sopenharmony_ci    if (Quiesce(pid)) {
97800b99b8Sopenharmony_ci        return true;
98800b99b8Sopenharmony_ci    }
99800b99b8Sopenharmony_ci
100800b99b8Sopenharmony_ci    if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
101800b99b8Sopenharmony_ci        DFXLOGE("Failed to detach");
102800b99b8Sopenharmony_ci    }
103800b99b8Sopenharmony_ci    return false;
104800b99b8Sopenharmony_ci}
105800b99b8Sopenharmony_ci
106800b99b8Sopenharmony_cibool PidUtils::Detach(pid_t pid)
107800b99b8Sopenharmony_ci{
108800b99b8Sopenharmony_ci    if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
109800b99b8Sopenharmony_ci        DFXLOGE("ptrace detach failed");
110800b99b8Sopenharmony_ci        return false;
111800b99b8Sopenharmony_ci    }
112800b99b8Sopenharmony_ci    return true;
113800b99b8Sopenharmony_ci}
114800b99b8Sopenharmony_ci
115800b99b8Sopenharmony_cibool PidUtils::WaitForPidState(pid_t pid, const std::function<PidRunEnum()>& stateCheckFunc)
116800b99b8Sopenharmony_ci{
117800b99b8Sopenharmony_ci    PidRunEnum status = PID_RUN_KEEP_GOING;
118800b99b8Sopenharmony_ci    for (time_t startTime = time(nullptr);
119800b99b8Sopenharmony_ci        time(nullptr) - startTime < MAX_WAIT_TIME_SECONDS && status == PID_RUN_KEEP_GOING;) {
120800b99b8Sopenharmony_ci        if (Attach(pid)) {
121800b99b8Sopenharmony_ci            status = stateCheckFunc();
122800b99b8Sopenharmony_ci            if (status == PID_RUN_PASS) {
123800b99b8Sopenharmony_ci                return true;
124800b99b8Sopenharmony_ci            }
125800b99b8Sopenharmony_ci
126800b99b8Sopenharmony_ci            if (!Detach(pid)) {
127800b99b8Sopenharmony_ci                return false;
128800b99b8Sopenharmony_ci            }
129800b99b8Sopenharmony_ci        } else if (Exited(pid)) {
130800b99b8Sopenharmony_ci            return false;
131800b99b8Sopenharmony_ci        }
132800b99b8Sopenharmony_ci        usleep(USLEEP_TIME);
133800b99b8Sopenharmony_ci    }
134800b99b8Sopenharmony_ci    if (status == PID_RUN_KEEP_GOING) {
135800b99b8Sopenharmony_ci        DFXLOGE("Timed out waiting for pid %{public}d to be ready", pid);
136800b99b8Sopenharmony_ci    }
137800b99b8Sopenharmony_ci    return status == PID_RUN_PASS;
138800b99b8Sopenharmony_ci}
139800b99b8Sopenharmony_ci
140800b99b8Sopenharmony_cibool PidUtils::WaitForPidStateAfterAttach(pid_t pid, const std::function<PidRunEnum()>& stateCheckFunc)
141800b99b8Sopenharmony_ci{
142800b99b8Sopenharmony_ci    PidRunEnum status;
143800b99b8Sopenharmony_ci    time_t startTime = time(nullptr);
144800b99b8Sopenharmony_ci    do {
145800b99b8Sopenharmony_ci        status = stateCheckFunc();
146800b99b8Sopenharmony_ci        if (status == PID_RUN_PASS) {
147800b99b8Sopenharmony_ci            return true;
148800b99b8Sopenharmony_ci        }
149800b99b8Sopenharmony_ci        if (!Detach(pid)) {
150800b99b8Sopenharmony_ci            return false;
151800b99b8Sopenharmony_ci        }
152800b99b8Sopenharmony_ci        usleep(USLEEP_TIME);
153800b99b8Sopenharmony_ci    } while (time(nullptr) - startTime < MAX_WAIT_TIME_SECONDS && status == PID_RUN_KEEP_GOING && Attach(pid));
154800b99b8Sopenharmony_ci    if (status == PID_RUN_KEEP_GOING) {
155800b99b8Sopenharmony_ci        DFXLOGE("Timed out waiting for pid %{public}d to be ready", pid);
156800b99b8Sopenharmony_ci    }
157800b99b8Sopenharmony_ci    return status == PID_RUN_PASS;
158800b99b8Sopenharmony_ci}
159800b99b8Sopenharmony_ci
160800b99b8Sopenharmony_ci}
161800b99b8Sopenharmony_ci}