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