1 /*
2 * Copyright (c) 2021-2025 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 <fstream>
16 #include <sstream>
17 #include <iomanip>
18 #include <memory>
19 #include <algorithm>
20 #include <unwind.h>
21
22 #include "stacktrace.h"
23 #include "os/mutex.h"
24 #include "utils/string_helpers.h"
25
26 #include <cxxabi.h>
27 #include <dlfcn.h>
28 #include <link.h>
29 #include "debug_info.h"
30
31 namespace ark {
32
33 struct VmaEntry {
34 enum DebugInfoStatus { NOT_READ, VALID, BAD };
35
36 // NOLINTNEXTLINE(modernize-pass-by-value)
VmaEntryark::VmaEntry37 VmaEntry(uintptr_t paramStartAddr, uintptr_t paramEndAddr, uintptr_t paramBaseAddr, const std::string &fname)
38 : startAddr(paramStartAddr), endAddr(paramEndAddr), baseAddr(paramBaseAddr), filename(fname)
39 {
40 }
41
42 ~VmaEntry() = default;
43
44 uintptr_t startAddr; // NOLINT(misc-non-private-member-variables-in-classes)
45 uintptr_t endAddr; // NOLINT(misc-non-private-member-variables-in-classes)
46 uintptr_t baseAddr; // NOLINT(misc-non-private-member-variables-in-classes)
47 std::string filename; // NOLINT(misc-non-private-member-variables-in-classes)
48 DebugInfoStatus status {NOT_READ}; // NOLINT(misc-non-private-member-variables-in-classes)
49 DebugInfo debugInfo; // NOLINT(misc-non-private-member-variables-in-classes)
50
51 DEFAULT_MOVE_SEMANTIC(VmaEntry);
52 NO_COPY_SEMANTIC(VmaEntry);
53 };
54
55 class StackPrinter {
56 public:
GetInstance()57 static StackPrinter &GetInstance()
58 {
59 static StackPrinter printer;
60 return printer;
61 }
62
Print(const std::vector<uintptr_t> & stacktrace,std::ostream & out)63 std::ostream &Print(const std::vector<uintptr_t> &stacktrace, std::ostream &out)
64 {
65 os::memory::LockHolder lock(mutex_);
66 ScanVma();
67 for (size_t frameNum = 0; frameNum < stacktrace.size(); ++frameNum) {
68 PrintFrame(frameNum, stacktrace[frameNum], out);
69 }
70 return out;
71 }
72
73 NO_MOVE_SEMANTIC(StackPrinter);
74 NO_COPY_SEMANTIC(StackPrinter);
75
76 private:
77 explicit StackPrinter() = default;
78 ~StackPrinter() = default;
79
PrintFrame(size_t frameNum,uintptr_t pc,std::ostream & out)80 void PrintFrame(size_t frameNum, uintptr_t pc, std::ostream &out)
81 {
82 std::ios_base::fmtflags f = out.flags();
83 auto w = out.width();
84 out << "#" << std::setw(2U) << std::left << frameNum << ": 0x" << std::hex << pc << " ";
85 out.flags(f);
86 out.width(w);
87
88 VmaEntry *vma = FindVma(pc);
89 if (vma == nullptr) {
90 vmas_.clear();
91 ScanVma();
92 vma = FindVma(pc);
93 }
94 if (vma != nullptr) {
95 uintptr_t pcOffset = pc - vma->baseAddr;
96 // pc points to the instruction after the call
97 // Decrement pc to get source line number pointing to the function call
98 --pcOffset;
99 std::string function;
100 std::string srcFile;
101 unsigned int line = 0;
102 if (ReadDebugInfo(vma) && vma->debugInfo.GetSrcLocation(pcOffset, &function, &srcFile, &line)) {
103 PrintFrame(function, srcFile, line, out);
104 return;
105 }
106 uintptr_t offset = 0;
107 if (ReadSymbol(pc, &function, &offset)) {
108 PrintFrame(function, offset, out);
109 return;
110 }
111 }
112 out << "??:??\n";
113 }
114
PrintFrame(const std::string & function,const std::string & srcFile,unsigned int line,std::ostream & out)115 void PrintFrame(const std::string &function, const std::string &srcFile, unsigned int line, std::ostream &out)
116 {
117 if (function.empty()) {
118 out << "??";
119 } else {
120 Demangle(function, out);
121 }
122 out << "\n at ";
123 if (srcFile.empty()) {
124 out << "??";
125 } else {
126 out << srcFile;
127 }
128 out << ":";
129 if (line == 0) {
130 out << "??";
131 } else {
132 out << line;
133 }
134
135 out << "\n";
136 }
137
PrintFrame(const std::string & function,uintptr_t offset,std::ostream & out)138 void PrintFrame(const std::string &function, uintptr_t offset, std::ostream &out)
139 {
140 std::ios_base::fmtflags f = out.flags();
141 Demangle(function, out);
142 out << std::hex << "+0x" << offset << "\n";
143 out.flags(f);
144 }
145
ReadSymbol(uintptr_t pc,std::string * function,uintptr_t * offset)146 bool ReadSymbol(uintptr_t pc, std::string *function, uintptr_t *offset)
147 {
148 Dl_info info {};
149 if (dladdr(reinterpret_cast<void *>(pc), &info) != 0 && info.dli_sname != nullptr) {
150 *function = info.dli_sname;
151 *offset = pc - reinterpret_cast<uintptr_t>(info.dli_saddr);
152 return true;
153 }
154 return false;
155 }
156
Demangle(const std::string & function,std::ostream & out)157 void Demangle(const std::string &function, std::ostream &out)
158 {
159 size_t length = 0;
160 int status = 0;
161 char *demangledFunction = abi::__cxa_demangle(function.c_str(), nullptr, &length, &status);
162 if (status == 0) {
163 out << demangledFunction;
164 free(demangledFunction); // NOLINT(cppcoreguidelines-no-malloc)
165 } else {
166 out << function;
167 }
168 }
169
FindVma(uintptr_t pc)170 VmaEntry *FindVma(uintptr_t pc)
171 {
172 VmaEntry el(pc, pc, 0, "");
173 auto it = std::upper_bound(vmas_.begin(), vmas_.end(), el,
174 [](const VmaEntry &e1, const VmaEntry &e2) { return e1.endAddr < e2.endAddr; });
175 if (it != vmas_.end() && (it->startAddr <= pc && pc < it->endAddr)) {
176 return &(*it);
177 }
178 return nullptr;
179 }
180
ReadDebugInfo(VmaEntry * vma)181 bool ReadDebugInfo(VmaEntry *vma)
182 {
183 if (vma->status == VmaEntry::VALID) {
184 return true;
185 }
186 if (vma->status == VmaEntry::BAD) {
187 return false;
188 }
189 if (!vma->filename.empty() && vma->debugInfo.ReadFromFile(vma->filename.c_str()) == DebugInfo::SUCCESS) {
190 vma->status = VmaEntry::VALID;
191 return true;
192 }
193 vma->status = VmaEntry::BAD;
194 return false;
195 }
196
HandleLibrary(dl_phdr_info * info,size_t size,void * data)197 static int HandleLibrary(dl_phdr_info *info, [[maybe_unused]] size_t size, void *data)
198 {
199 auto *vmas = reinterpret_cast<std::vector<VmaEntry> *>(data);
200 Span<const ElfW(Phdr)> phdrs(info->dlpi_phdr, info->dlpi_phnum);
201 for (const ElfW(Phdr) & phdr : phdrs) {
202 // NOLINTNEXTLINE(hicpp-signed-bitwise)
203 if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X) != 0) {
204 uintptr_t startAddr = info->dlpi_addr + phdr.p_vaddr;
205 uintptr_t endAddr = startAddr + phdr.p_memsz;
206 vmas->emplace_back(startAddr, endAddr, info->dlpi_addr, info->dlpi_name);
207 }
208 }
209 return 0;
210 }
211
ScanVma()212 void ScanVma()
213 {
214 dl_iterate_phdr(HandleLibrary, &vmas_);
215 std::sort(vmas_.begin(), vmas_.end(),
216 [](const VmaEntry &e1, const VmaEntry &e2) { return e1.startAddr < e2.startAddr; });
217 }
218
219 private:
220 std::vector<VmaEntry> vmas_;
221 os::memory::Mutex mutex_;
222 };
223
224 class Buf {
225 public:
Buf(uintptr_t * buf,size_t skip,size_t capacity)226 Buf(uintptr_t *buf, size_t skip, size_t capacity) : buf_(buf), skip_(skip), capacity_(capacity) {}
227
Append(uintptr_t pc)228 void Append(uintptr_t pc)
229 {
230 if (skip_ > 0) {
231 // Skip the element
232 --skip_;
233 return;
234 }
235 if (size_ >= capacity_) {
236 return;
237 }
238 buf_[size_++] = pc; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
239 }
240
Size() const241 int Size() const
242 {
243 return static_cast<int>(size_);
244 }
245
246 private:
247 uintptr_t *buf_;
248 size_t skip_;
249 size_t size_ {0};
250 size_t capacity_;
251 };
252
FrameHandler(struct _Unwind_Context * ctx,void * arg)253 static _Unwind_Reason_Code FrameHandler(struct _Unwind_Context *ctx, [[maybe_unused]] void *arg)
254 {
255 Buf *buf = reinterpret_cast<Buf *>(arg);
256 uintptr_t pc = _Unwind_GetIP(ctx);
257 // _Unwind_GetIP returns 0 pc at the end of the stack. Ignore it
258 if (pc != 0) {
259 buf->Append(pc);
260 }
261 return _URC_NO_REASON;
262 }
263
GetStacktrace(uintptr_t * buf,size_t size)264 static size_t GetStacktrace(uintptr_t *buf, size_t size)
265 {
266 static constexpr int SKIP_FRAMES = 2; // backtrace
267 Buf bufWrapper(buf, SKIP_FRAMES, size);
268 _Unwind_Reason_Code res = _Unwind_Backtrace(FrameHandler, &bufWrapper);
269 if (res != _URC_END_OF_STACK || bufWrapper.Size() < 0) {
270 return 0;
271 }
272
273 return bufWrapper.Size();
274 }
275
GetStacktrace()276 std::vector<uintptr_t> GetStacktrace()
277 {
278 static constexpr size_t BUF_SIZE = 100;
279 std::vector<uintptr_t> buf;
280 buf.resize(BUF_SIZE);
281 size_t size = GetStacktrace(buf.data(), buf.size());
282 buf.resize(size);
283 return buf;
284 }
285
PrintStack(const std::vector<uintptr_t> & stacktrace,std::ostream & out)286 std::ostream &PrintStack(const std::vector<uintptr_t> &stacktrace, std::ostream &out)
287 {
288 return StackPrinter::GetInstance().Print(stacktrace, out);
289 }
290
291 } // namespace ark
292