• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 
25 #include <cxxabi.h>
26 #include <dlfcn.h>
27 #include "debug_info.h"
28 
29 namespace panda {
30 
31 struct VmaEntry {
32     enum DebugInfoStatus { NOT_READ, VALID, BAD };
33 
34     // NOLINTNEXTLINE(modernize-pass-by-value)
VmaEntrypanda::VmaEntry35     VmaEntry(uintptr_t param_start_addr, uintptr_t param_end_addr, uintptr_t param_offset, const std::string &fname)
36         : start_addr(param_start_addr), end_addr(param_end_addr), offset(param_offset), filename(fname)
37     {
38     }
39 
40     ~VmaEntry() = default;
41 
42     uintptr_t start_addr;               // NOLINT(misc-non-private-member-variables-in-classes)
43     uintptr_t end_addr;                 // NOLINT(misc-non-private-member-variables-in-classes)
44     uintptr_t offset;                   // NOLINT(misc-non-private-member-variables-in-classes)
45     std::string filename;               // NOLINT(misc-non-private-member-variables-in-classes)
46     DebugInfoStatus status {NOT_READ};  // NOLINT(misc-non-private-member-variables-in-classes)
47     DebugInfo debug_info;               // NOLINT(misc-non-private-member-variables-in-classes)
48 
49     DEFAULT_MOVE_SEMANTIC(VmaEntry);
50     NO_COPY_SEMANTIC(VmaEntry);
51 };
52 
53 class Tokenizer {
54 public:
55     // NOLINTNEXTLINE(modernize-pass-by-value)
Tokenizer(const std::string & str)56     explicit Tokenizer(const std::string &str) : str_(str), pos_(0) {}
57 
Next(char delim=' ')58     std::string Next(char delim = ' ')
59     {
60         while (pos_ < str_.length() && str_[pos_] == ' ') {
61             ++pos_;
62         }
63         size_t pos = str_.find(delim, pos_);
64         std::string token;
65         if (pos == std::string::npos) {
66             token = str_.substr(pos_);
67             pos_ = str_.length();
68         } else {
69             token = str_.substr(pos_, pos - pos_);
70             pos_ = pos + 1;  // skip delimiter
71         }
72         return token;
73     }
74 
75 private:
76     std::string str_;
77     size_t pos_;
78 };
79 
80 class StackPrinter {
81 public:
GetInstance()82     static StackPrinter &GetInstance()
83     {
84         static StackPrinter printer;
85         return printer;
86     }
87 
Print(const std::vector<uintptr_t> & stacktrace,std::ostream & out)88     std::ostream &Print(const std::vector<uintptr_t> &stacktrace, std::ostream &out)
89     {
90         os::memory::LockHolder lock(mutex_);
91         ScanVma();
92         for (size_t frame_num = 0; frame_num < stacktrace.size(); ++frame_num) {
93             PrintFrame(frame_num, stacktrace[frame_num], out);
94         }
95         return out;
96     }
97 
98     NO_MOVE_SEMANTIC(StackPrinter);
99     NO_COPY_SEMANTIC(StackPrinter);
100 
101 private:
102     explicit StackPrinter() = default;
103     ~StackPrinter() = default;
104 
PrintFrame(size_t frame_num,uintptr_t pc,std::ostream & out)105     void PrintFrame(size_t frame_num, uintptr_t pc, std::ostream &out)
106     {
107         std::ios_base::fmtflags f = out.flags();
108         auto w = out.width();
109         out << "#" << std::setw(2U) << std::left << frame_num << ": 0x" << std::hex << pc << " ";
110         out.flags(f);
111         out.width(w);
112 
113         VmaEntry *vma = FindVma(pc);
114         if (vma == nullptr) {
115             vmas_.clear();
116             ScanVma();
117             vma = FindVma(pc);
118         }
119         if (vma != nullptr) {
120             uintptr_t pc_offset = pc - vma->start_addr + vma->offset;
121             // pc points to the instruction after the call
122             // Decrement pc to get source line number pointing to the function call
123             --pc_offset;
124             std::string function;
125             std::string src_file;
126             unsigned int line = 0;
127             if (ReadDebugInfo(vma) && vma->debug_info.GetSrcLocation(pc_offset, &function, &src_file, &line)) {
128                 PrintFrame(function, src_file, line, out);
129                 return;
130             }
131             uintptr_t offset = 0;
132             if (ReadSymbol(pc, &function, &offset)) {
133                 PrintFrame(function, offset, out);
134                 return;
135             }
136         }
137         out << "??:??\n";
138     }
139 
PrintFrame(const std::string & function,const std::string & src_file,unsigned int line,std::ostream & out) const140     void PrintFrame(const std::string &function, const std::string &src_file, unsigned int line,
141                     std::ostream &out) const
142     {
143         if (function.empty()) {
144             out << "??";
145         } else {
146             Demangle(function, out);
147         }
148         out << "\n     at ";
149         if (src_file.empty()) {
150             out << "??";
151         } else {
152             out << src_file;
153         }
154         out << ":";
155         if (line == 0) {
156             out << "??";
157         } else {
158             out << line;
159         }
160 
161         out << "\n";
162     }
163 
PrintFrame(const std::string & function,uintptr_t offset,std::ostream & out) const164     void PrintFrame(const std::string &function, uintptr_t offset, std::ostream &out) const
165     {
166         std::ios_base::fmtflags f = out.flags();
167         Demangle(function, out);
168         out << std::hex << "+0x" << offset << "\n";
169         out.flags(f);
170     }
171 
ReadSymbol(uintptr_t pc,std::string * function,uintptr_t * offset) const172     bool ReadSymbol(uintptr_t pc, std::string *function, uintptr_t *offset) const
173     {
174         Dl_info info {};
175         if (dladdr(reinterpret_cast<void *>(pc), &info) != 0 && info.dli_sname != nullptr) {
176             *function = info.dli_sname;
177             *offset = pc - reinterpret_cast<uintptr_t>(info.dli_saddr);
178             return true;
179         }
180         return false;
181     }
182 
Demangle(const std::string & function,std::ostream & out) const183     void Demangle(const std::string &function, std::ostream &out) const
184     {
185         size_t length = 0;
186         int status = 0;
187         char *demangled_function = abi::__cxa_demangle(function.c_str(), nullptr, &length, &status);
188         if (status == 0) {
189             out << demangled_function;
190             free(demangled_function);  // NOLINT(cppcoreguidelines-no-malloc)
191         } else {
192             out << function;
193         }
194     }
195 
FindVma(uintptr_t pc)196     VmaEntry *FindVma(uintptr_t pc)
197     {
198         VmaEntry el(pc, pc, 0, "");
199         auto it = std::upper_bound(vmas_.begin(), vmas_.end(), el,
200                                    [](const VmaEntry &e1, const VmaEntry &e2) { return e1.end_addr < e2.end_addr; });
201         if (it != vmas_.end() && (it->start_addr <= pc && pc < it->end_addr)) {
202             return &(*it);
203         }
204         return nullptr;
205     }
206 
ReadDebugInfo(VmaEntry * vma) const207     bool ReadDebugInfo(VmaEntry *vma) const
208     {
209         if (vma->status == VmaEntry::VALID) {
210             return true;
211         }
212         if (vma->status == VmaEntry::BAD) {
213             return false;
214         }
215         if (!vma->filename.empty() && vma->debug_info.ReadFromFile(vma->filename.c_str()) == DebugInfo::SUCCESS) {
216             vma->status = VmaEntry::VALID;
217             return true;
218         }
219         vma->status = VmaEntry::BAD;
220         return false;
221     }
222 
ScanVma()223     void ScanVma()
224     {
225         static const int HEX_RADIX = 16;
226         static const size_t MODE_FIELD_LEN = 4;
227         static const size_t XMODE_POS = 2;
228 
229         if (!vmas_.empty()) {
230             return;
231         }
232 
233         std::stringstream fname;
234         fname << "/proc/self/maps";
235         std::string filename = fname.str();
236         std::ifstream maps(filename.c_str());
237 
238         while (maps) {
239             std::string line;
240             std::getline(maps, line);
241             Tokenizer tokenizer(line);
242             std::string start_addr = tokenizer.Next('-');
243             std::string end_addr = tokenizer.Next();
244             std::string rights = tokenizer.Next();
245             if (rights.length() == MODE_FIELD_LEN && rights[XMODE_POS] == 'x') {
246                 std::string offset = tokenizer.Next();
247                 tokenizer.Next();
248                 tokenizer.Next();
249                 std::string obj_filename = tokenizer.Next();
250                 vmas_.emplace_back(stoul(start_addr, nullptr, HEX_RADIX), stoul(end_addr, nullptr, HEX_RADIX),
251                                    stoul(offset, nullptr, HEX_RADIX), obj_filename);
252             }
253         }
254     }
255 
256 private:
257     std::vector<VmaEntry> vmas_;
258     os::memory::Mutex mutex_;
259 };
260 
261 class Buf {
262 public:
Buf(uintptr_t * buf,size_t skip,size_t capacity)263     Buf(uintptr_t *buf, size_t skip, size_t capacity) : buf_(buf), skip_(skip), size_(0), capacity_(capacity) {}
264     ~Buf() = default;
265     DEFAULT_MOVE_SEMANTIC(Buf);
266     DEFAULT_COPY_SEMANTIC(Buf);
Append(uintptr_t pc)267     void Append(uintptr_t pc)
268     {
269         if (skip_ > 0) {
270             // Skip the element
271             --skip_;
272             return;
273         }
274         if (size_ >= capacity_) {
275             return;
276         }
277         buf_[size_++] = pc;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
278     }
279 
Size() const280     int Size() const
281     {
282         return size_;
283     }
284 
285 private:
286     uintptr_t *buf_;
287     size_t skip_;
288     size_t size_;
289     size_t capacity_;
290 };
291 
FrameHandler(struct _Unwind_Context * ctx,void * arg)292 static _Unwind_Reason_Code FrameHandler(struct _Unwind_Context *ctx, [[maybe_unused]] void *arg)
293 {
294     Buf *buf = reinterpret_cast<Buf *>(arg);
295     uintptr_t pc = _Unwind_GetIP(ctx);
296     // _Unwind_GetIP returns 0 pc at the end of the stack. Ignore it
297     if (pc != 0) {
298         buf->Append(pc);
299     }
300     return _URC_NO_REASON;
301 }
302 
GetStacktrace()303 std::vector<uintptr_t> GetStacktrace()
304 {
305     static constexpr size_t BUF_SIZE = 100;
306     static constexpr int SKIP_FRAMES = 2;  // backtrace
307     std::vector<uintptr_t> buf;
308     buf.resize(BUF_SIZE);
309     Buf buf_wrapper(buf.data(), SKIP_FRAMES, buf.size());
310     _Unwind_Reason_Code res = _Unwind_Backtrace(FrameHandler, &buf_wrapper);
311     if (res != _URC_END_OF_STACK || buf_wrapper.Size() < 0) {
312         return std::vector<uintptr_t>();
313     }
314 
315     buf.resize(buf_wrapper.Size());
316     return buf;
317 }
318 
PrintStack(const std::vector<uintptr_t> & stacktrace,std::ostream & out)319 std::ostream &PrintStack(const std::vector<uintptr_t> &stacktrace, std::ostream &out)
320 {
321     return StackPrinter::GetInstance().Print(stacktrace, out);
322 }
323 
324 }  // namespace panda
325