• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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