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