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
16 #include "dfx_fault_stack.h"
17
18 #include <algorithm>
19 #include <csignal>
20 #include <sys/ptrace.h>
21 #include <sys/stat.h>
22
23 #include "dfx_config.h"
24 #include "dfx_elf.h"
25 #include "dfx_logger.h"
26 #include "dfx_ring_buffer_wrapper.h"
27
28 namespace OHOS {
29 namespace HiviewDFX {
30 namespace {
31 #if defined(__arm__)
32 constexpr uint64_t STEP = 4;
33 #define PRINT_FORMAT "%08x"
34 #elif defined(__aarch64__)
35 constexpr uint64_t STEP = 8;
36 #define PRINT_FORMAT "%016llx"
37 #else
38 constexpr uint64_t STEP = 8;
39 #define PRINT_FORMAT "%016llx"
40 #endif
41 }
ReadTargetMemory(uintptr_t addr,uintptr_t & value) const42 bool FaultStack::ReadTargetMemory(uintptr_t addr, uintptr_t &value) const
43 {
44 uintptr_t targetAddr = addr;
45 auto retAddr = reinterpret_cast<long*>(&value);
46 for (size_t i = 0; i < sizeof(uintptr_t) / sizeof(long); i++) {
47 *retAddr = ptrace(PTRACE_PEEKTEXT, tid_, reinterpret_cast<void*>(targetAddr), nullptr);
48 if (*retAddr == -1) {
49 if (errno != prevErrno_) {
50 DFXLOG_ERROR("read target mem by ptrace failed, errno(%s).", strerror(errno));
51 prevErrno_ = errno;
52 }
53 return false;
54 }
55 targetAddr += sizeof(long);
56 retAddr += 1;
57 }
58
59 return true;
60 }
61
AdjustAndCreateMemoryBlock(size_t index,uintptr_t prevSp,uintptr_t prevEndAddr,uintptr_t size)62 uintptr_t FaultStack::AdjustAndCreateMemoryBlock(size_t index, uintptr_t prevSp, uintptr_t prevEndAddr, uintptr_t size)
63 {
64 uintptr_t lowAddrLength = DfxConfig::GetConfig().lowAddressStep;
65 uintptr_t startAddr = prevSp - lowAddrLength * STEP;
66 if (prevEndAddr != 0 && startAddr <= prevEndAddr) {
67 startAddr = prevEndAddr;
68 } else {
69 size += lowAddrLength;
70 }
71
72 if (size == 0 || index == 0) {
73 return prevEndAddr;
74 }
75
76 std::string name = "sp" + std::to_string(index - 1);
77 auto block = CreateMemoryBlock(startAddr, prevSp, size, name);
78 blocks_.push_back(block);
79 return startAddr + size * STEP;
80 }
81
CollectStackInfo(const std::vector<DfxFrame> & frames,bool needParseStack)82 bool FaultStack::CollectStackInfo(const std::vector<DfxFrame>& frames, bool needParseStack)
83 {
84 if (frames.empty()) {
85 DFXLOG_WARN("%s", "null frames.");
86 return false;
87 }
88
89 size_t index = 1;
90 uintptr_t minAddr = 4096;
91 uintptr_t size = 0;
92 uintptr_t prevSp = 0;
93 uintptr_t prevEndAddr = 0;
94 uintptr_t highAddrLength = DfxConfig::GetConfig().highAddressStep;
95 if (needParseStack) {
96 highAddrLength = 8192; // 8192 : 32k / STEP
97 }
98
99 auto firstFrame = frames.at(0);
100 prevSp = static_cast<uintptr_t>(firstFrame.sp);
101 constexpr size_t MAX_FAULT_STACK_SZ = 4;
102 for (index = 1; index < frames.size(); index++) {
103 if (index > MAX_FAULT_STACK_SZ) {
104 break;
105 }
106
107 auto frame = frames.at(index);
108 uintptr_t curSp = static_cast<uintptr_t>(frame.sp);
109
110 size = 0;
111 if (curSp > prevSp) {
112 size = std::min(highAddrLength, static_cast<uintptr_t>(((curSp - prevSp) / STEP) - 1));
113 } else {
114 break;
115 }
116
117 prevEndAddr = AdjustAndCreateMemoryBlock(index, prevSp, prevEndAddr, size);
118 prevSp = curSp;
119 }
120
121 if ((blocks_.size() < MAX_FAULT_STACK_SZ) && (prevSp > minAddr)) {
122 size = highAddrLength;
123 prevEndAddr = AdjustAndCreateMemoryBlock(index, prevSp, prevEndAddr, size);
124 }
125
126 const uintptr_t minCorruptedStackSz = 1024;
127 CreateBlockForCorruptedStack(frames, prevEndAddr, minCorruptedStackSz);
128 return true;
129 }
130
CreateBlockForCorruptedStack(const std::vector<DfxFrame> & frames,uintptr_t prevEndAddr,uintptr_t size)131 bool FaultStack::CreateBlockForCorruptedStack(const std::vector<DfxFrame>& frames, uintptr_t prevEndAddr,
132 uintptr_t size)
133 {
134 const auto& frame = frames.back();
135 // stack trace should end with libc or ffrt or */bin/*
136 if (frame.mapName.find("ld-musl") != std::string::npos ||
137 frame.mapName.find("ffrt") != std::string::npos ||
138 frame.mapName.find("bin") != std::string::npos) {
139 return false;
140 }
141
142 AdjustAndCreateMemoryBlock(frame.index, frame.sp, prevEndAddr, size);
143 return true;
144 }
145
PrintMemoryBlock(const MemoryBlockInfo & info,uintptr_t stackStartAddr) const146 uintptr_t FaultStack::PrintMemoryBlock(const MemoryBlockInfo& info, uintptr_t stackStartAddr) const
147 {
148 uintptr_t targetAddr = info.startAddr;
149 for (uint64_t i = 0; i < static_cast<uint64_t>(info.content.size()); i++) {
150 if (targetAddr == info.nameAddr) {
151 DfxRingBufferWrapper::GetInstance().AppendBuf("%s:" PRINT_FORMAT " " PRINT_FORMAT "\n",
152 info.name.c_str(),
153 targetAddr,
154 info.content.at(i));
155 } else {
156 DfxRingBufferWrapper::GetInstance().AppendBuf(" " PRINT_FORMAT " " PRINT_FORMAT "\n",
157 targetAddr,
158 info.content.at(i));
159 }
160 if (targetAddr - stackStartAddr > STEP * DfxConfig::GetConfig().highAddressStep) {
161 break;
162 }
163 targetAddr += STEP;
164 }
165 return targetAddr;
166 }
167
Print() const168 void FaultStack::Print() const
169 {
170 PrintRegisterMemory();
171
172 if (blocks_.empty()) {
173 return;
174 }
175
176 DfxRingBufferWrapper::GetInstance().AppendMsg("FaultStack:\n");
177 uintptr_t end = 0;
178 uintptr_t stackStartAddr = blocks_.at(0).startAddr;
179 for (const auto& block : blocks_) {
180 if (end != 0 && end < block.startAddr) {
181 DfxRingBufferWrapper::GetInstance().AppendMsg(" ...\n");
182 }
183 end = PrintMemoryBlock(block, stackStartAddr);
184 }
185 }
186
CreateMemoryBlock(uintptr_t addr,uintptr_t offset,uintptr_t size,const std::string & name) const187 MemoryBlockInfo FaultStack::CreateMemoryBlock(
188 uintptr_t addr,
189 uintptr_t offset,
190 uintptr_t size,
191 const std::string& name) const
192 {
193 MemoryBlockInfo info;
194 info.startAddr = addr;
195 info.nameAddr = offset;
196 info.size = size;
197 info.name = name;
198 uintptr_t targetAddr = addr;
199 for (uint64_t i = 0; i < static_cast<uint64_t>(size); i++) {
200 uintptr_t value = 0;
201 if (ReadTargetMemory(targetAddr, value)) {
202 info.content.push_back(value);
203 } else {
204 info.content.push_back(-1);
205 }
206 targetAddr += STEP;
207 }
208 return info;
209 }
210
CollectRegistersBlock(std::shared_ptr<DfxRegs> regs,std::shared_ptr<DfxMaps> maps)211 void FaultStack::CollectRegistersBlock(std::shared_ptr<DfxRegs> regs, std::shared_ptr<DfxMaps> maps)
212 {
213 if (regs == nullptr || maps == nullptr) {
214 DFXLOG_ERROR("%s : regs or maps is null.", __func__);
215 return;
216 }
217
218 auto regsData = regs->GetRegsData();
219 int index = 0;
220 for (auto data : regsData) {
221 index++;
222 std::shared_ptr<DfxMap> map;
223 if (!maps->FindMapByAddr(data, map)) {
224 continue;
225 }
226
227 if ((map->prots & PROT_READ) == 0) {
228 continue;
229 }
230
231 std::string name = regs->GetSpecialRegsNameByIndex(index - 1);
232 if (name.empty()) {
233 #if defined(__arm__)
234 #define NAME_PREFIX "r"
235 #elif defined(__aarch64__)
236 #define NAME_PREFIX "x"
237 #else
238 #define NAME_PREFIX "x"
239 #endif
240 name = NAME_PREFIX + std::to_string(index - 1);
241 }
242
243 constexpr size_t SIZE = sizeof(uintptr_t);
244 constexpr int COUNT = 32;
245 constexpr int FORWARD_SZ = 2;
246 auto mapName = map->name;
247 if (!mapName.empty()) {
248 name.append("(" + mapName + ")");
249 }
250
251 data = data & ~(SIZE - 1);
252 data -= (FORWARD_SZ * SIZE);
253 auto block = CreateMemoryBlock(data, 0, COUNT, name);
254 registerBlocks_.push_back(block);
255 }
256 }
257
PrintRegisterMemory() const258 void FaultStack::PrintRegisterMemory() const
259 {
260 if (registerBlocks_.empty()) {
261 return;
262 }
263
264 DfxRingBufferWrapper::GetInstance().AppendMsg("Memory near registers:\n");
265 for (const auto& block : registerBlocks_) {
266 uintptr_t targetAddr = block.startAddr;
267 DfxRingBufferWrapper::GetInstance().AppendMsg(block.name + ":\n");
268 for (size_t i = 0; i < block.content.size(); i++) {
269 DfxRingBufferWrapper::GetInstance().AppendBuf(" " PRINT_FORMAT " " PRINT_FORMAT "\n",
270 targetAddr,
271 block.content.at(i));
272 targetAddr += STEP;
273 }
274 }
275 }
276
ParseUnwindStack(std::shared_ptr<DfxMaps> maps,std::vector<DfxFrame> & frames)277 bool FaultStack::ParseUnwindStack(std::shared_ptr<DfxMaps> maps, std::vector<DfxFrame>& frames)
278 {
279 if (maps == nullptr) {
280 DFXLOG_ERROR("%s : maps is null.", __func__);
281 return false;
282 }
283 size_t index = frames.size();
284 for (const auto& block : blocks_) {
285 std::shared_ptr<DfxMap> map;
286 for (size_t i = 0; i < block.content.size(); i++) {
287 if (!maps->FindMapByAddr(block.content[i], map) ||
288 (map->prots & PROT_EXEC) == 0) {
289 continue;
290 }
291 DfxFrame frame;
292 frame.index = index;
293 frame.pc = block.content[i];
294 frame.map = map;
295 frame.mapName = map->name;
296 int64_t loadBias = 0;
297 struct stat st;
298 if (stat(map->name.c_str(), &st) == 0 && (st.st_mode & S_IFREG)) {
299 auto elf = DfxElf::Create(frame.mapName);
300 if (elf == nullptr || !elf->IsValid()) {
301 DFXLOG_ERROR("%s : Failed to create DfxElf, elf path(%s).", __func__, frame.mapName.c_str());
302 return false;
303 }
304 loadBias = elf->GetLoadBias();
305 frame.buildId = elf->GetBuildId();
306 } else {
307 DFXLOG_WARN("%s : mapName(%s) is not file.", __func__, frame.mapName.c_str());
308 }
309
310 frame.relPc = frame.pc - map->begin + map->offset + static_cast<uint64_t>(loadBias);
311 frames.emplace_back(frame);
312 constexpr int MAX_VALID_ADDRESS_NUM = 32;
313 if (++index >= MAX_VALID_ADDRESS_NUM) {
314 return true;
315 }
316 }
317 }
318 return true;
319 }
320 } // namespace HiviewDFX
321 } // namespace OHOS
322