1 /*
2  * Copyright (c) 2022-2024 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 #include "dfx_unwind_async_thread.h"
16 
17 #include "crash_exception.h"
18 #include "dfx_config.h"
19 #include "dfx_log.h"
20 #include "dfx_memory.h"
21 #include "dfx_maps.h"
22 #include "dfx_regs.h"
23 #include "dfx_ring_buffer_wrapper.h"
24 #include "dfx_trace.h"
25 #ifdef PARSE_LOCK_OWNER
26 #include "lock_parser.h"
27 #endif
28 #include "process_dumper.h"
29 #include "printer.h"
30 #include "unique_stack_table.h"
31 
32 namespace OHOS {
33 namespace HiviewDFX {
UnwindStack(pid_t vmPid)34 bool DfxUnwindAsyncThread::UnwindStack(pid_t vmPid)
35 {
36     if (unwinder_ == nullptr || thread_ == nullptr) {
37         DFXLOG_ERROR("%s::thread or unwinder is not initialized.", __func__);
38         return false;
39     }
40     // 1: get crash stack
41     // unwinding with context passed by dump request, only for crash thread or target thread.
42     auto regs = thread_->GetThreadRegs();
43     unwinder_->SetRegs(regs);
44     pid_t tid = thread_->threadInfo_.nsTid;
45     DFXLOG_INFO("%s, unwind tid(%d) start.", __func__, tid);
46     auto tmpPid = vmPid != 0 ? vmPid : tid;
47     DFX_TRACE_START("KeyThreadUnwindRemote:%d", tid);
48     bool ret = unwinder_->UnwindRemote(tmpPid,
49                                        regs != nullptr,
50                                        DfxConfig::GetConfig().maxFrameNums);
51     DFX_TRACE_FINISH();
52     if (ProcessDumper::GetInstance().IsCrash()) {
53         ReportUnwinderException(unwinder_->GetLastErrorCode());
54         if (!ret) {
55             UnwindThreadFallback();
56         }
57         DFX_TRACE_START("KeyThreadGetFrames:%d", tid);
58         thread_->SetFrames(unwinder_->GetFrames());
59         DFX_TRACE_FINISH();
60         UnwindThreadByParseStackIfNeed();
61         // 2: get submitterStack
62         std::vector<DfxFrame> submmiterFrames;
63         GetSubmitterStack(submmiterFrames);
64         // 3: merge two stack
65         MergeStack(submmiterFrames);
66     } else {
67 #ifdef PARSE_LOCK_OWNER
68         DFX_TRACE_START("KeyThreadGetFrames:%d", tid);
69         thread_->SetFrames(unwinder_->GetFrames());
70         DFX_TRACE_FINISH();
71         LockParser::ParseLockInfo(unwinder_, tmpPid, thread_->threadInfo_.nsTid);
72 #else
73         thread_->Detach();
74         DFX_TRACE_START("KeyThreadGetFrames:%d", tid);
75         thread_->SetFrames(unwinder_->GetFrames());
76         DFX_TRACE_FINISH();
77 #endif
78     }
79 
80     DFXLOG_INFO("%s, unwind tid(%d) finish ret(%d).", __func__, tid, ret);
81     return ret;
82 }
83 
GetSubmitterStack(std::vector<DfxFrame> & submitterFrames)84 void DfxUnwindAsyncThread::GetSubmitterStack(std::vector<DfxFrame> &submitterFrames)
85 {
86     if (stackId_ == 0) {
87         return;
88     }
89     const std::shared_ptr<DfxMaps>& maps = unwinder_->GetMaps();
90     if (maps == nullptr) {
91         return;
92     }
93     std::vector<std::shared_ptr<DfxMap>> mapVec;
94     if (!maps->FindMapsByName("[anon:async_stack_table]", mapVec)) {
95         DFXLOG_ERROR("%s::Can not find map of async stack table", __func__);
96         return;
97     }
98     auto map = mapVec.front();
99     size_t size = map->end - map->begin;
100     auto tableData = std::make_shared<std::vector<uint8_t>>(size);
101     size_t byte = DfxMemory::ReadProcMemByPid(thread_->threadInfo_.nsTid, map->begin, tableData->data(), size);
102     if (byte != size) {
103         DFXLOG_ERROR("%s", "Failed to read unique_table from target");
104         return;
105     }
106     auto table = std::make_shared<UniqueStackTable>(tableData->data(), size, false);
107     std::vector<uintptr_t> pcs;
108     StackId id;
109     id.value = stackId_;
110     if (table->GetPcsByStackId(id, pcs)) {
111         unwinder_->GetFramesByPcs(submitterFrames, pcs);
112     } else {
113         DFXLOG_WARN("%s::Failed to get pcs", __func__);
114     }
115 }
116 
MergeStack(std::vector<DfxFrame> & submitterFrames)117 void DfxUnwindAsyncThread::MergeStack(std::vector<DfxFrame> &submitterFrames)
118 {
119     auto frames = thread_->GetFrames();
120     frames.insert(frames.end(), submitterFrames.begin(), submitterFrames.end());
121     thread_->SetFrames(frames);
122 }
123 
UnwindThreadFallback()124 void DfxUnwindAsyncThread::UnwindThreadFallback()
125 {
126 #ifndef __x86_64__
127     if (unwinder_->GetFrames().size() > 0) {
128         return;
129     }
130     // As we failed to init libunwind, just print pc and lr for first two frames
131     std::shared_ptr<DfxRegs> regs = thread_->GetThreadRegs();
132     if (regs == nullptr) {
133         DfxRingBufferWrapper::GetInstance().AppendMsg("RegisterInfo is not existed for crash process");
134         return;
135     }
136 
137     std::shared_ptr<DfxMaps> maps = unwinder_->GetMaps();
138     if (maps == nullptr) {
139         DfxRingBufferWrapper::GetInstance().AppendMsg("MapsInfo is not existed for crash process");
140         return;
141     }
142     std::shared_ptr<Unwinder> unwinder = unwinder_;
143 
144     auto createFrame = [maps, unwinder] (size_t index, uintptr_t pc, uintptr_t sp = 0) {
145         std::shared_ptr<DfxMap> map;
146         DfxFrame frame;
147         frame.pc = pc;
148         frame.sp = sp;
149         frame.index = index;
150         if (maps->FindMapByAddr(pc, map)) {
151             frame.relPc = map->GetRelPc(pc);
152             frame.mapName = map->name;
153         } else {
154             frame.relPc = pc;
155             frame.mapName = (index == 0 ? "Not mapped pc" : "Not mapped lr");
156         }
157         unwinder->AddFrame(frame);
158     };
159 
160     createFrame(0, regs->GetPc(), regs->GetSp());
161     createFrame(1, *(regs->GetReg(REG_LR)));
162 #endif
163 }
164 
UnwindThreadByParseStackIfNeed()165 void DfxUnwindAsyncThread::UnwindThreadByParseStackIfNeed()
166 {
167     if (thread_ == nullptr || unwinder_ == nullptr) {
168         return;
169     }
170     auto frames = thread_->GetFrames();
171     constexpr int minFramesNum = 3;
172     size_t initSize = frames.size();
173     if (initSize < minFramesNum || frames[minFramesNum - 1].mapName.find("Not mapped") != std::string::npos ||
174         frames[minFramesNum - 1].mapName.find("[Unknown]") != std::string::npos) {
175         bool needParseStack = true;
176         thread_->InitFaultStack(needParseStack);
177         auto faultStack = thread_->GetFaultStack();
178         if (faultStack == nullptr || !faultStack->ParseUnwindStack(unwinder_->GetMaps(), frames)) {
179             DFXLOG_ERROR("%s : Failed to parse unwind stack.", __func__);
180             return;
181         }
182         thread_->SetFrames(frames);
183         tip = StringPrintf(
184             " Failed to unwind stack, try to get unreliable call stack from #%02zu by reparsing thread stack.",
185             initSize);
186     }
187 }
188 }
189 }