1 /*
2 * Copyright (c) 2021-2025 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 "dfx_process.h"
17
18 #include <climits>
19 #include <cstdio>
20 #include <cstdlib>
21 #include <cstring>
22 #include <dirent.h>
23 #include <securec.h>
24 #include <sys/types.h>
25 #include <sys/sysinfo.h>
26 #include <unistd.h>
27 #include <vector>
28
29 #include "process_dump_config.h"
30 #include "crash_exception.h"
31 #include "dfx_define.h"
32
33 #include "dfx_buffer_writer.h"
34 #include "dfx_log.h"
35 #include "dfx_signal.h"
36 #include "dfx_util.h"
37 #include "procinfo.h"
38 #include "unique_fd.h"
39
40 namespace OHOS {
41 namespace HiviewDFX {
InitProcessInfo(pid_t pid,pid_t nsPid,uid_t uid,const std::string & processName)42 void DfxProcess::InitProcessInfo(pid_t pid, pid_t nsPid, uid_t uid, const std::string& processName)
43 {
44 processInfo_.pid = pid;
45 processInfo_.nsPid = nsPid;
46 processInfo_.uid = uid;
47 processInfo_.processName = processName;
48 }
49
InitKeyThread(const ProcessDumpRequest & request)50 bool DfxProcess::InitKeyThread(const ProcessDumpRequest& request)
51 {
52 pid_t nsTid = request.tid;
53 pid_t tid = ChangeTid(nsTid, true);
54 keyThread_ = DfxThread::Create(processInfo_.pid, tid, nsTid);
55 if (!keyThread_->Attach(PTRACE_ATTATCH_KEY_THREAD_TIMEOUT)) {
56 DFXLOGE("Failed to attach key thread(%{public}d).", nsTid);
57 ReportCrashException(CrashExceptionCode::CRASH_DUMP_EATTACH);
58 if (request.type == ProcessDumpType::DUMP_TYPE_DUMP_CATCH) {
59 return false;
60 }
61 }
62 if (keyThread_->GetThreadInfo().threadName.empty()) {
63 keyThread_->SetThreadName(std::string(request.threadName));
64 }
65 keyThread_->SetThreadRegs(DfxRegs::CreateFromUcontext(request.context));
66 if (request.type == ProcessDumpType::DUMP_TYPE_DUMP_CATCH) { // dumpcatch set target thread to key thread
67 pid_t dumpCatchTargetTid = request.siginfo.si_value.sival_int == 0 ?
68 request.nsPid : request.siginfo.si_value.sival_int;
69 DFXLOGE("dumpCatchTargetTid(%{public}d).", dumpCatchTargetTid);
70 if (dumpCatchTargetTid != tid) {
71 otherThreads_.emplace_back(keyThread_);
72 keyThread_ = DfxThread::Create(processInfo_.pid, dumpCatchTargetTid, dumpCatchTargetTid);
73 if (keyThread_ != nullptr && keyThread_->Attach(PTRACE_ATTATCH_OTHER_THREAD_TIMEOUT)) {
74 keyThread_->SetThreadRegs(DfxRegs::CreateRemoteRegs(dumpCatchTargetTid));
75 }
76 }
77 }
78 return true;
79 }
80
InitOtherThreads(pid_t requestTid)81 bool DfxProcess::InitOtherThreads(pid_t requestTid)
82 {
83 std::vector<int> tids;
84 std::vector<int> nstids;
85 if (!GetTidsByPid(processInfo_.pid, tids, nstids)) {
86 return false;
87 }
88 pid_t keyThreadNsTid = 0;
89 if (keyThread_ != nullptr) {
90 keyThreadNsTid = keyThread_->GetThreadInfo().nsTid;
91 }
92 for (size_t i = 0; i < nstids.size(); ++i) {
93 // KeyThread and requestThread have been initialized, skip directly
94 if ((nstids[i] == keyThreadNsTid) || nstids[i] == requestTid) {
95 continue;
96 }
97 auto thread = DfxThread::Create(processInfo_.pid, tids[i], nstids[i]);
98 if (thread->Attach(PTRACE_ATTATCH_OTHER_THREAD_TIMEOUT)) {
99 thread->SetThreadRegs(DfxRegs::CreateRemoteRegs(thread->GetThreadInfo().nsTid));
100 }
101 otherThreads_.push_back(thread);
102 }
103 return true;
104 }
105
ChangeTid(pid_t tid,bool ns)106 pid_t DfxProcess::ChangeTid(pid_t tid, bool ns)
107 {
108 if (processInfo_.pid == processInfo_.nsPid) {
109 return tid;
110 }
111
112 if (kvThreads_.empty()) {
113 std::vector<int> tids;
114 std::vector<int> nstids;
115 if (!GetTidsByPid(processInfo_.pid, tids, nstids)) {
116 return tid;
117 }
118 for (size_t i = 0; i < nstids.size(); ++i) {
119 kvThreads_[nstids[i]] = tids[i];
120 }
121 }
122
123 for (auto iter = kvThreads_.begin(); iter != kvThreads_.end(); iter++) {
124 if (ns && (iter->first == tid)) {
125 return iter->second;
126 }
127 if (!ns && (iter->second == tid)) {
128 return iter->first;
129 }
130 }
131 return tid;
132 }
133
GetOtherThreads()134 std::vector<std::shared_ptr<DfxThread>>& DfxProcess::GetOtherThreads()
135 {
136 return otherThreads_;
137 }
138
ClearOtherThreads()139 void DfxProcess::ClearOtherThreads()
140 {
141 otherThreads_.clear();
142 }
143
Attach(bool hasKey)144 void DfxProcess::Attach(bool hasKey)
145 {
146 if (hasKey && keyThread_) {
147 keyThread_->Attach(PTRACE_ATTATCH_KEY_THREAD_TIMEOUT);
148 }
149
150 if (otherThreads_.empty()) {
151 return;
152 }
153 for (auto& thread : otherThreads_) {
154 if (thread->GetThreadInfo().nsTid == processInfo_.nsPid) {
155 thread->Attach(PTRACE_ATTATCH_KEY_THREAD_TIMEOUT);
156 continue;
157 }
158 thread->Attach(PTRACE_ATTATCH_OTHER_THREAD_TIMEOUT);
159 }
160 }
161
Detach()162 void DfxProcess::Detach()
163 {
164 if (keyThread_ != nullptr) {
165 keyThread_->Detach();
166 }
167
168 for (const auto& thread : otherThreads_) {
169 if (thread != nullptr) {
170 thread->Detach();
171 }
172 }
173 }
174
AppendFatalMessage(const std::string & msg)175 void DfxProcess::AppendFatalMessage(const std::string &msg)
176 {
177 fatalMsg_ += msg;
178 }
179
GetFatalMessage() const180 const std::string& DfxProcess::GetFatalMessage() const
181 {
182 return fatalMsg_;
183 }
184
185 namespace {
GetProcessStartTime(pid_t tid,unsigned long long & startTime)186 bool GetProcessStartTime(pid_t tid, unsigned long long &startTime)
187 {
188 std::string path = "/proc/" +std::to_string(tid);
189 UniqueFd dirFd(open(path.c_str(), O_DIRECTORY | O_RDONLY));
190 if (dirFd == -1) {
191 DFXLOGE("GetProcessInfo open %{public}s fail. errno %{public}d", path.c_str(), errno);
192 return false;
193 }
194
195 UniqueFd statFd(openat(dirFd.Get(), "stat", O_RDONLY | O_CLOEXEC));
196 if (statFd == -1) {
197 DFXLOGE("GetProcessInfo open %{public}s/stat fail. errno %{public}d", path.c_str(), errno);
198 return false;
199 }
200
201 std::string statStr;
202 if (!ReadFdToString(statFd.Get(), statStr)) {
203 DFXLOGE("GetProcessInfo read string fail.");
204 return false;
205 }
206
207 std::string eoc = statStr.substr(statStr.find_last_of(")"));
208 std::istringstream is(eoc);
209 constexpr int startTimePos = 21;
210 constexpr int base = 10;
211 int pos = 0;
212 std::string tmp;
213 while (is >> tmp && pos <= startTimePos) {
214 pos++;
215 if (pos == startTimePos) {
216 startTime = strtoull(tmp.c_str(), nullptr, base);
217 return true;
218 }
219 }
220 DFXLOGE("GetProcessInfo Get process info fail.");
221 return false;
222 }
223 }
224
GetProcessLifeCycle()225 std::string DfxProcess::GetProcessLifeCycle()
226 {
227 struct timespec ts;
228 (void)clock_gettime(CLOCK_BOOTTIME, &ts);
229 uint64_t sysUpTime = static_cast<uint64_t>(ts.tv_sec + static_cast<time_t>(ts.tv_nsec != 0 ? 1L : 0L));
230
231 unsigned long long startTime = 0;
232 if (GetProcessStartTime(processInfo_.pid, startTime)) {
233 auto clkTck = sysconf(_SC_CLK_TCK);
234 if (clkTck == -1) {
235 DFXLOGE("Get _SC_CLK_TCK fail. errno %{public}d", errno);
236 return "";
237 }
238 uint64_t procUpTime = sysUpTime - startTime / static_cast<uint32_t>(clkTck);
239 constexpr uint64_t invalidTimeLimit = 10 * 365 * 24 * 3600; // 10 year
240 if (procUpTime > invalidTimeLimit) {
241 DFXLOGE("invalid system upTime %{public}" PRIu64" proc upTime: %{public}" PRIu64 ", "
242 "startTime: %{public}llu.", sysUpTime, procUpTime, startTime);
243 return "";
244 }
245 return std::to_string(procUpTime) + "s";
246 }
247 return "";
248 }
249 } // namespace HiviewDFX
250 } // namespace OHOS
251