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 DFXLOG_INFO("Failed to get tid(%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 DFXLOG_INFO("%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 if (process == nullptr || unwinder == nullptr) {
85 DFXLOG_WARN("%s::process or unwinder is not initialized.", __func__);
86 return false;
87 }
88
89 SetCrashProcInfo(process->processInfo_.processName, process->processInfo_.pid,
90 process->processInfo_.uid);
91 int unwCnt = UnwindKeyThread(request, process, unwinder, vmPid) ? 1 : 0;
92
93 // dumpt -p -t will not unwind other thread
94 if (ProcessDumper::GetInstance().IsCrash() || request->siginfo.si_value.sival_int == 0) {
95 unwCnt += UnwindOtherThread(process, unwinder, vmPid);
96 }
97
98 if (ProcessDumper::GetInstance().IsCrash()) {
99 if (request->dumpMode == SPLIT_MODE) {
100 if (process->vmThread_ == nullptr) {
101 DFXLOG_WARN("%s::unwind vm thread is not initialized.", __func__);
102 } else {
103 Printer::PrintThreadFaultStackByConfig(process, process->vmThread_, unwinder);
104 }
105 } else {
106 if (process->keyThread_ == nullptr) {
107 DFXLOG_WARN("%s::unwind key thread is not initialized.", __func__);
108 } else {
109 pid_t nsTid = process->keyThread_->threadInfo_.nsTid;
110 process->keyThread_->threadInfo_.nsTid = vmPid; // read registers from vm process
111 Printer::PrintThreadFaultStackByConfig(process, process->keyThread_, unwinder);
112 process->keyThread_->threadInfo_.nsTid = nsTid;
113 }
114 }
115 Printer::PrintProcessMapsByConfig(unwinder->GetMaps());
116 Printer::PrintThreadOpenFiles(process);
117 }
118
119 if (isVmProcAttach) {
120 DfxPtrace::Detach(vmPid);
121 }
122 DFXLOG_INFO("success unwind thread cnt is %d", unwCnt);
123 return unwCnt > 0;
124 }
125
UnwindKeyThread(std::shared_ptr<ProcessDumpRequest> request,std::shared_ptr<DfxProcess> process,std::shared_ptr<Unwinder> unwinder,pid_t vmPid)126 bool DfxUnwindRemote::UnwindKeyThread(std::shared_ptr<ProcessDumpRequest> request, std::shared_ptr<DfxProcess> process,
127 std::shared_ptr<Unwinder> unwinder, pid_t vmPid)
128 {
129 bool result = false;
130 std::shared_ptr<DfxThread> unwThread = process->keyThread_;
131 if (ProcessDumper::GetInstance().IsCrash() && (process->vmThread_ != nullptr)) {
132 unwThread = process->vmThread_;
133 }
134 if (unwThread == nullptr) {
135 DFXLOG_WARN("%s::unwind thread is not initialized.", __func__);
136 return false;
137 }
138 if (request == nullptr) {
139 DFXLOG_WARN("%s::request is not initialized.", __func__);
140 return false;
141 }
142 unwinder->SetIsJitCrashFlag(ProcessDumper::GetInstance().IsCrash());
143 auto unwindAsyncThread = std::make_shared<DfxUnwindAsyncThread>(unwThread, unwinder, request->stackId);
144 if ((vmPid != 0)) {
145 if (DfxPtrace::Attach(vmPid, PTRACE_ATTATCH_KEY_THREAD_TIMEOUT)) {
146 isVmProcAttach = true;
147 if (unwThread->GetThreadRegs() != nullptr) {
148 result = unwindAsyncThread->UnwindStack(vmPid);
149 } else {
150 GetThreadKernelStack(unwThread);
151 }
152 }
153 } else {
154 result = unwindAsyncThread->UnwindStack();
155 }
156
157 std::string fatalMsg = process->GetFatalMessage() + unwindAsyncThread->tip;
158 if (!unwindAsyncThread->tip.empty()) {
159 if (ProcessDumper::GetInstance().IsCrash()) {
160 ReportCrashException(process->processInfo_.processName, process->processInfo_.pid,
161 process->processInfo_.uid, CrashExceptionCode::CRASH_UNWIND_ESTACK);
162 }
163 }
164 process->SetFatalMessage(fatalMsg);
165 Printer::PrintDumpHeader(request, process, unwinder);
166 Printer::PrintThreadHeaderByConfig(process->keyThread_, true);
167 Printer::PrintThreadBacktraceByConfig(unwThread, true);
168 if (ProcessDumper::GetInstance().IsCrash()) {
169 // Registers of unwThread has been changed, we should print regs from request context.
170 process->regs_ = DfxRegs::CreateFromUcontext(request->context);
171 Printer::PrintRegsByConfig(process->regs_);
172 }
173 return result;
174 }
175
UnwindOtherThread(std::shared_ptr<DfxProcess> process,std::shared_ptr<Unwinder> unwinder,pid_t vmPid)176 int DfxUnwindRemote::UnwindOtherThread(std::shared_ptr<DfxProcess> process, std::shared_ptr<Unwinder> unwinder,
177 pid_t vmPid)
178 {
179 if ((!DfxConfig::GetConfig().dumpOtherThreads) || (vmPid != 0 && !isVmProcAttach)) {
180 return 0;
181 }
182 unwinder->SetIsJitCrashFlag(false);
183 size_t index = 0;
184 int unwCnt = 0;
185 for (auto &thread : process->GetOtherThreads()) {
186 if ((index == 0) && ProcessDumper::GetInstance().IsCrash()) {
187 Printer::PrintOtherThreadHeaderByConfig();
188 }
189
190 if (isVmProcAttach || thread->Attach(PTRACE_ATTATCH_OTHER_THREAD_TIMEOUT)) {
191 Printer::PrintThreadHeaderByConfig(thread, false);
192 auto regs = thread->GetThreadRegs();
193 unwinder->SetRegs(regs);
194 bool withRegs = regs != nullptr;
195 pid_t tid = thread->threadInfo_.nsTid;
196 DFXLOG_DEBUG("%s, unwind tid(%d) start", __func__, tid);
197 if (isVmProcAttach && !withRegs) {
198 GetThreadKernelStack(thread);
199 Printer::PrintThreadBacktraceByConfig(thread, false);
200 thread->Detach();
201 continue;
202 }
203 auto pid = (vmPid != 0 && isVmProcAttach) ? vmPid : tid;
204 DFX_TRACE_START("OtherThreadUnwindRemote:%d", tid);
205 bool ret = unwinder->UnwindRemote(pid, withRegs, DfxConfig::GetConfig().maxFrameNums);
206 DFX_TRACE_FINISH();
207 thread->Detach();
208 DFX_TRACE_START("OtherThreadGetFrames:%d", tid);
209 thread->SetFrames(unwinder->GetFrames());
210 DFX_TRACE_FINISH();
211 #ifdef PARSE_LOCK_OWNER
212 LockParser::ParseLockInfo(unwinder, pid, tid);
213 #endif
214 if (ProcessDumper::GetInstance().IsCrash()) {
215 ReportUnwinderException(unwinder->GetLastErrorCode());
216 }
217 if (!ret) {
218 DFXLOG_WARN("%s, unwind tid(%d) finish ret(%d).", __func__, tid, ret);
219 } else {
220 unwCnt++;
221 }
222 Printer::PrintThreadBacktraceByConfig(thread, false);
223 }
224 index++;
225 }
226 return unwCnt;
227 }
228
InitTargetKeyThreadRegs(std::shared_ptr<ProcessDumpRequest> request,std::shared_ptr<DfxProcess> process)229 bool DfxUnwindRemote::InitTargetKeyThreadRegs(std::shared_ptr<ProcessDumpRequest> request,
230 std::shared_ptr<DfxProcess> process)
231 {
232 auto regs = DfxRegs::CreateFromUcontext(request->context);
233 if (regs == nullptr) {
234 return false;
235 }
236 process->keyThread_->SetThreadRegs(regs);
237 return true;
238 }
239
InitOtherThreadRegs(std::shared_ptr<DfxProcess> process)240 void DfxUnwindRemote::InitOtherThreadRegs(std::shared_ptr<DfxProcess> process)
241 {
242 if (!DfxConfig::GetConfig().dumpOtherThreads) {
243 return;
244 }
245
246 for (auto &thread : process->GetOtherThreads()) {
247 if (thread->Attach(PTRACE_ATTATCH_OTHER_THREAD_TIMEOUT)) {
248 thread->SetThreadRegs(DfxRegs::CreateRemoteRegs(thread->threadInfo_.nsTid));
249 }
250 }
251 }
252
InitProcessAllThreadRegs(std::shared_ptr<ProcessDumpRequest> request,std::shared_ptr<DfxProcess> process)253 bool DfxUnwindRemote::InitProcessAllThreadRegs(std::shared_ptr<ProcessDumpRequest> request,
254 std::shared_ptr<DfxProcess> process)
255 {
256 if (!InitTargetKeyThreadRegs(request, process)) {
257 DFXLOG_ERROR("%s", "get key thread regs fail");
258 return false;
259 }
260 InitOtherThreadRegs(process);
261 return true;
262 }
263 } // namespace HiviewDFX
264 } // namespace OHOS
265