1 /*
2 * Copyright (c) 2021-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 "dfx_unwind_remote.h"
17
18 #include <algorithm>
19 #include <cstdio>
20 #include <cstring>
21 #include <elf.h>
22 #include <link.h>
23 #include <securec.h>
24
25 #include "crash_exception.h"
26 #include "dfx_unwind_async_thread.h"
27 #include "dfx_config.h"
28 #include "dfx_define.h"
29 #include "dfx_frame_formatter.h"
30 #include "dfx_kernel_stack.h"
31 #include "dfx_logger.h"
32 #include "dfx_maps.h"
33 #include "dfx_process.h"
34 #include "dfx_ptrace.h"
35 #include "dfx_regs.h"
36 #include "dfx_ring_buffer_wrapper.h"
37 #include "dfx_symbols.h"
38 #include "dfx_thread.h"
39 #include "dfx_trace.h"
40 #include "dfx_util.h"
41 #ifdef PARSE_LOCK_OWNER
42 #include "lock_parser.h"
43 #endif
44 #ifndef is_ohos_lite
45 #include "parameter.h"
46 #include "parameters.h"
47 #endif // !is_ohos_lite
48 #include "process_dumper.h"
49 #include "printer.h"
50
51 namespace OHOS {
52 namespace HiviewDFX {
53 namespace {
GetThreadKernelStack(std::shared_ptr<DfxThread> thread)54 void GetThreadKernelStack(std::shared_ptr<DfxThread> thread)
55 {
56 std::string threadKernelStack;
57 pid_t tid = thread->threadInfo_.nsTid;
58 DfxThreadStack threadStack;
59 if (DfxGetKernelStack(tid, threadKernelStack) == 0 && FormatThreadKernelStack(threadKernelStack, threadStack)) {
60 DFXLOGI("Failed to get tid(%{public}d) user stack, try kernel", tid);
61 #ifndef is_ohos_lite
62 if (OHOS::system::GetParameter("const.logsystem.versiontype", "false") == "beta") {
63 size_t step = LOG_BUF_LEN - 1;
64 for (size_t i = 0; i < threadKernelStack.length(); i += step) {
65 DFXLOGI("%{public}s", threadKernelStack.substr(i, step).c_str());
66 }
67 }
68 #endif
69 thread->SetFrames(threadStack.frames);
70 }
71 }
72 }
73
GetInstance()74 DfxUnwindRemote &DfxUnwindRemote::GetInstance()
75 {
76 static DfxUnwindRemote ins;
77 return ins;
78 }
79
UnwindProcess(std::shared_ptr<ProcessDumpRequest> request, std::shared_ptr<DfxProcess> process, std::shared_ptr<Unwinder> unwinder, pid_t vmPid)80 bool DfxUnwindRemote::UnwindProcess(std::shared_ptr<ProcessDumpRequest> request, std::shared_ptr<DfxProcess> process,
81 std::shared_ptr<Unwinder> unwinder, pid_t vmPid)
82 {
83 DFX_TRACE_SCOPED("UnwindProcess");
84 DFXLOGW("start unwind process.");
85 if (process == nullptr || unwinder == nullptr) {
86 DFXLOGW("%{public}s::process or unwinder is not initialized.", __func__);
87 return false;
88 }
89
90 SetCrashProcInfo(process->processInfo_.processName, process->processInfo_.pid,
91 process->processInfo_.uid);
92 int unwCnt = UnwindKeyThread(request, process, unwinder, vmPid) ? 1 : 0;
93
94 // dumpt -p -t will not unwind other thread
95 if (ProcessDumper::GetInstance().IsCrash() || request->siginfo.si_value.sival_int == 0) {
96 unwCnt += UnwindOtherThread(process, unwinder, vmPid);
97 }
98
99 if (ProcessDumper::GetInstance().IsCrash()) {
100 if (request->dumpMode == SPLIT_MODE) {
101 if (process->vmThread_ == nullptr) {
102 DFXLOGW("%{public}s::unwind vm thread is not initialized.", __func__);
103 } else {
104 Printer::PrintThreadFaultStackByConfig(process, process->vmThread_, unwinder);
105 }
106 } else {
107 if (process->keyThread_ == nullptr) {
108 DFXLOGW("%{public}s::unwind key thread is not initialized.", __func__);
109 } else {
110 pid_t nsTid = process->keyThread_->threadInfo_.nsTid;
111 process->keyThread_->threadInfo_.nsTid = vmPid; // read registers from vm process
112 Printer::PrintThreadFaultStackByConfig(process, process->keyThread_, unwinder);
113 process->keyThread_->threadInfo_.nsTid = nsTid;
114 }
115 }
116 Printer::PrintProcessMapsByConfig(unwinder->GetMaps());
117 Printer::PrintThreadOpenFiles(process);
118 }
119
120 if (isVmProcAttach) {
121 DfxPtrace::Detach(vmPid);
122 }
123 DFXLOGW("success unwind thread cnt is %{public}d", unwCnt);
124 return unwCnt > 0;
125 }
126
UnwindKeyThread(std::shared_ptr<ProcessDumpRequest> request, std::shared_ptr<DfxProcess> process, std::shared_ptr<Unwinder> unwinder, pid_t vmPid)127 bool DfxUnwindRemote::UnwindKeyThread(std::shared_ptr<ProcessDumpRequest> request, std::shared_ptr<DfxProcess> process,
128 std::shared_ptr<Unwinder> unwinder, pid_t vmPid)
129 {
130 bool result = false;
131 std::shared_ptr<DfxThread> unwThread = process->keyThread_;
132 if (ProcessDumper::GetInstance().IsCrash() && (process->vmThread_ != nullptr)) {
133 unwThread = process->vmThread_;
134 }
135 if (unwThread == nullptr) {
136 DFXLOGW("%{public}s::unwind thread is not initialized.", __func__);
137 return false;
138 }
139 if (request == nullptr) {
140 DFXLOGW("%{public}s::request is not initialized.", __func__);
141 return false;
142 }
143 unwinder->SetIsJitCrashFlag(ProcessDumper::GetInstance().IsCrash());
144 auto unwindAsyncThread = std::make_shared<DfxUnwindAsyncThread>(unwThread, unwinder, request->stackId);
145 if ((vmPid != 0)) {
146 if (DfxPtrace::Attach(vmPid, PTRACE_ATTATCH_KEY_THREAD_TIMEOUT)) {
147 isVmProcAttach = true;
148 if (unwThread->GetThreadRegs() != nullptr) {
149 result = unwindAsyncThread->UnwindStack(vmPid);
150 } else {
151 GetThreadKernelStack(unwThread);
152 }
153 }
154 } else {
155 result = unwindAsyncThread->UnwindStack();
156 }
157
158 std::string fatalMsg = process->GetFatalMessage() + unwindAsyncThread->tip;
159 if (!unwindAsyncThread->tip.empty()) {
160 if (ProcessDumper::GetInstance().IsCrash()) {
161 ReportCrashException(process->processInfo_.processName, process->processInfo_.pid,
162 process->processInfo_.uid, CrashExceptionCode::CRASH_UNWIND_ESTACK);
163 }
164 }
165 process->SetFatalMessage(fatalMsg);
166 Printer::PrintDumpHeader(request, process, unwinder);
167 Printer::PrintThreadHeaderByConfig(process->keyThread_, true);
168 Printer::PrintThreadBacktraceByConfig(unwThread, true);
169 if (ProcessDumper::GetInstance().IsCrash()) {
170 // Registers of unwThread has been changed, we should print regs from request context.
171 process->regs_ = DfxRegs::CreateFromUcontext(request->context);
172 Printer::PrintRegsByConfig(process->regs_);
173 }
174 return result;
175 }
176
UnwindOtherThread(std::shared_ptr<DfxProcess> process, std::shared_ptr<Unwinder> unwinder, pid_t vmPid)177 int DfxUnwindRemote::UnwindOtherThread(std::shared_ptr<DfxProcess> process, std::shared_ptr<Unwinder> unwinder,
178 pid_t vmPid)
179 {
180 if ((!DfxConfig::GetConfig().dumpOtherThreads) || (vmPid != 0 && !isVmProcAttach)) {
181 return 0;
182 }
183 unwinder->SetIsJitCrashFlag(false);
184 size_t index = 0;
185 int unwCnt = 0;
186 for (auto &thread : process->GetOtherThreads()) {
187 if ((index == 0) && ProcessDumper::GetInstance().IsCrash()) {
188 Printer::PrintOtherThreadHeaderByConfig();
189 }
190
191 if (isVmProcAttach || thread->Attach(PTRACE_ATTATCH_OTHER_THREAD_TIMEOUT)) {
192 Printer::PrintThreadHeaderByConfig(thread, false);
193 auto regs = thread->GetThreadRegs();
194 unwinder->SetRegs(regs);
195 bool withRegs = regs != nullptr;
196 pid_t tid = thread->threadInfo_.nsTid;
197 DFXLOGD("%{public}s, unwind tid(%{public}d) start", __func__, tid);
198 if (isVmProcAttach && !withRegs) {
199 GetThreadKernelStack(thread);
200 Printer::PrintThreadBacktraceByConfig(thread, false);
201 thread->Detach();
202 continue;
203 }
204 auto pid = (vmPid != 0 && isVmProcAttach) ? vmPid : tid;
205 DFX_TRACE_START("OtherThreadUnwindRemote:%d", tid);
206 bool ret = unwinder->UnwindRemote(pid, withRegs, DfxConfig::GetConfig().maxFrameNums);
207 DFX_TRACE_FINISH();
208 #ifdef PARSE_LOCK_OWNER
209 DFX_TRACE_START("OtherThreadGetFrames:%d", tid);
210 thread->SetFrames(unwinder->GetFrames());
211 DFX_TRACE_FINISH();
212 LockParser::ParseLockInfo(unwinder, pid, tid);
213 #else
214 thread->Detach();
215 DFX_TRACE_START("OtherThreadGetFrames:%d", tid);
216 thread->SetFrames(unwinder->GetFrames());
217 DFX_TRACE_FINISH();
218 #endif
219 if (ProcessDumper::GetInstance().IsCrash()) {
220 ReportUnwinderException(unwinder->GetLastErrorCode());
221 }
222 if (!ret) {
223 DFXLOGW("%{public}s, unwind tid(%{public}d) finish ret(%{public}d).", __func__, tid, ret);
224 } else {
225 unwCnt++;
226 }
227 Printer::PrintThreadBacktraceByConfig(thread, false);
228 }
229 index++;
230 }
231 return unwCnt;
232 }
233
InitTargetKeyThreadRegs(std::shared_ptr<ProcessDumpRequest> request, std::shared_ptr<DfxProcess> process)234 bool DfxUnwindRemote::InitTargetKeyThreadRegs(std::shared_ptr<ProcessDumpRequest> request,
235 std::shared_ptr<DfxProcess> process)
236 {
237 auto regs = DfxRegs::CreateFromUcontext(request->context);
238 if (regs == nullptr) {
239 return false;
240 }
241 process->keyThread_->SetThreadRegs(regs);
242 return true;
243 }
244
InitOtherThreadRegs(std::shared_ptr<DfxProcess> process)245 void DfxUnwindRemote::InitOtherThreadRegs(std::shared_ptr<DfxProcess> process)
246 {
247 if (!DfxConfig::GetConfig().dumpOtherThreads) {
248 return;
249 }
250
251 for (auto &thread : process->GetOtherThreads()) {
252 if (thread->Attach(PTRACE_ATTATCH_OTHER_THREAD_TIMEOUT)) {
253 thread->SetThreadRegs(DfxRegs::CreateRemoteRegs(thread->threadInfo_.nsTid));
254 }
255 }
256 }
257
InitProcessAllThreadRegs(std::shared_ptr<ProcessDumpRequest> request, std::shared_ptr<DfxProcess> process)258 bool DfxUnwindRemote::InitProcessAllThreadRegs(std::shared_ptr<ProcessDumpRequest> request,
259 std::shared_ptr<DfxProcess> process)
260 {
261 if (!InitTargetKeyThreadRegs(request, process)) {
262 DFXLOGE("get key thread regs fail");
263 return false;
264 }
265 InitOtherThreadRegs(process);
266 return true;
267 }
268 } // namespace HiviewDFX
269 } // namespace OHOS
270