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 27namespace OHOS { 28namespace HiviewDFX { 29namespace { 30#undef LOG_DOMAIN 31#undef LOG_TAG 32#define LOG_DOMAIN 0xD002D11 33#define LOG_TAG "DfxPidUtils" 34 35static constexpr time_t MAX_WAIT_TIME_SECONDS = 30; 36static constexpr time_t USLEEP_TIME = 5000; 37} 38 39static 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 DFXLOGE("%{public}d died: Process exited with code %{public}d", pid, WEXITSTATUS(status)); 49 } else if (WIFSIGNALED(status)) { 50 DFXLOGE("%{public}d died: Process exited due to signal %{public}d", pid, WTERMSIG(status)); 51 } else { 52 DFXLOGE("%{public}d died: Process finished for unknown reason", pid); 53 } 54 return true; 55} 56 57bool 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 DFXLOGE("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 DFXLOGE("%{public}d: Did not quiesce in 10 seconds", pid); 79 return false; 80} 81 82bool 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 DFXLOGE("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 DFXLOGE("Failed to detach"); 102 } 103 return false; 104} 105 106bool PidUtils::Detach(pid_t pid) 107{ 108 if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) { 109 DFXLOGE("ptrace detach failed"); 110 return false; 111 } 112 return true; 113} 114 115bool 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 DFXLOGE("Timed out waiting for pid %{public}d to be ready", pid); 136 } 137 return status == PID_RUN_PASS; 138} 139 140bool 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 DFXLOGE("Timed out waiting for pid %{public}d to be ready", pid); 156 } 157 return status == PID_RUN_PASS; 158} 159 160} 161}