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 }