1/** 2 * Copyright (c) 2021-2022 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 29namespace panda { 30 31struct VmaEntry { 32 enum DebugInfoStatus { NOT_READ, VALID, BAD }; 33 34 // NOLINTNEXTLINE(modernize-pass-by-value) 35 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 53class Tokenizer { 54public: 55 // NOLINTNEXTLINE(modernize-pass-by-value) 56 explicit Tokenizer(const std::string &str) : str_(str), pos_(0) {} 57 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 75private: 76 std::string str_; 77 size_t pos_; 78}; 79 80class StackPrinter { 81public: 82 static StackPrinter &GetInstance() 83 { 84 static StackPrinter printer; 85 return printer; 86 } 87 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 101private: 102 explicit StackPrinter() = default; 103 ~StackPrinter() = default; 104 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 140 void PrintFrame(const std::string &function, const std::string &src_file, unsigned int line, std::ostream &out) 141 { 142 if (function.empty()) { 143 out << "??"; 144 } else { 145 Demangle(function, out); 146 } 147 out << "\n at "; 148 if (src_file.empty()) { 149 out << "??"; 150 } else { 151 out << src_file; 152 } 153 out << ":"; 154 if (line == 0) { 155 out << "??"; 156 } else { 157 out << line; 158 } 159 160 out << "\n"; 161 } 162 163 void PrintFrame(const std::string &function, uintptr_t offset, std::ostream &out) 164 { 165 std::ios_base::fmtflags f = out.flags(); 166 Demangle(function, out); 167 out << std::hex << "+0x" << offset << "\n"; 168 out.flags(f); 169 } 170 171 bool ReadSymbol(uintptr_t pc, std::string *function, uintptr_t *offset) 172 { 173 Dl_info info {}; 174 if (dladdr(reinterpret_cast<void *>(pc), &info) != 0 && info.dli_sname != nullptr) { 175 *function = info.dli_sname; 176 *offset = pc - reinterpret_cast<uintptr_t>(info.dli_saddr); 177 return true; 178 } 179 return false; 180 } 181 182 void Demangle(const std::string &function, std::ostream &out) 183 { 184 size_t length = 0; 185 int status = 0; 186 char *demangled_function = abi::__cxa_demangle(function.c_str(), nullptr, &length, &status); 187 if (status == 0) { 188 out << demangled_function; 189 free(demangled_function); // NOLINT(cppcoreguidelines-no-malloc) 190 } else { 191 out << function; 192 } 193 } 194 195 VmaEntry *FindVma(uintptr_t pc) 196 { 197 VmaEntry el(pc, pc, 0, ""); 198 auto it = std::upper_bound(vmas_.begin(), vmas_.end(), el, 199 [](const VmaEntry &e1, const VmaEntry &e2) { return e1.end_addr < e2.end_addr; }); 200 if (it != vmas_.end() && (it->start_addr <= pc && pc < it->end_addr)) { 201 return &(*it); 202 } 203 return nullptr; 204 } 205 206 bool ReadDebugInfo(VmaEntry *vma) 207 { 208 if (vma->status == VmaEntry::VALID) { 209 return true; 210 } 211 if (vma->status == VmaEntry::BAD) { 212 return false; 213 } 214 if (!vma->filename.empty() && vma->debug_info.ReadFromFile(vma->filename.c_str()) == DebugInfo::SUCCESS) { 215 vma->status = VmaEntry::VALID; 216 return true; 217 } 218 vma->status = VmaEntry::BAD; 219 return false; 220 } 221 222 void ScanVma() 223 { 224 static const int HEX_RADIX = 16; 225 static const size_t MODE_FIELD_LEN = 4; 226 static const size_t XMODE_POS = 2; 227 228 if (!vmas_.empty()) { 229 return; 230 } 231 232 std::stringstream fname; 233 fname << "/proc/self/maps"; 234 std::string filename = fname.str(); 235 std::ifstream maps(filename.c_str()); 236 237 while (maps) { 238 std::string line; 239 std::getline(maps, line); 240 Tokenizer tokenizer(line); 241 std::string start_addr = tokenizer.Next('-'); 242 std::string end_addr = tokenizer.Next(); 243 std::string rights = tokenizer.Next(); 244 if (rights.length() == MODE_FIELD_LEN && rights[XMODE_POS] == 'x') { 245 std::string offset = tokenizer.Next(); 246 tokenizer.Next(); 247 tokenizer.Next(); 248 std::string obj_filename = tokenizer.Next(); 249 vmas_.emplace_back(stoul(start_addr, nullptr, HEX_RADIX), stoul(end_addr, nullptr, HEX_RADIX), 250 stoul(offset, nullptr, HEX_RADIX), obj_filename); 251 } 252 } 253 } 254 255private: 256 std::vector<VmaEntry> vmas_; 257 os::memory::Mutex mutex_; 258}; 259 260class Buf { 261public: 262 Buf(uintptr_t *buf, size_t skip, size_t capacity) : buf_(buf), skip_(skip), size_(0), capacity_(capacity) {} 263 264 void Append(uintptr_t pc) 265 { 266 if (skip_ > 0) { 267 // Skip the element 268 --skip_; 269 return; 270 } 271 if (size_ >= capacity_) { 272 return; 273 } 274 buf_[size_++] = pc; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) 275 } 276 277 int Size() const 278 { 279 return size_; 280 } 281 282private: 283 uintptr_t *buf_; 284 size_t skip_; 285 size_t size_; 286 size_t capacity_; 287}; 288 289static _Unwind_Reason_Code FrameHandler(struct _Unwind_Context *ctx, [[maybe_unused]] void *arg) 290{ 291 Buf *buf = reinterpret_cast<Buf *>(arg); 292 uintptr_t pc = _Unwind_GetIP(ctx); 293 // _Unwind_GetIP returns 0 pc at the end of the stack. Ignore it 294 if (pc != 0) { 295 buf->Append(pc); 296 } 297 return _URC_NO_REASON; 298} 299 300std::vector<uintptr_t> GetStacktrace() 301{ 302 static constexpr size_t BUF_SIZE = 100; 303 static constexpr int SKIP_FRAMES = 2; // backtrace 304 std::vector<uintptr_t> buf; 305 buf.resize(BUF_SIZE); 306 Buf buf_wrapper(buf.data(), SKIP_FRAMES, buf.size()); 307 _Unwind_Reason_Code res = _Unwind_Backtrace(FrameHandler, &buf_wrapper); 308 if (res != _URC_END_OF_STACK || buf_wrapper.Size() < 0) { 309 return std::vector<uintptr_t>(); 310 } 311 312 buf.resize(buf_wrapper.Size()); 313 return buf; 314} 315 316std::ostream &PrintStack(const std::vector<uintptr_t> &stacktrace, std::ostream &out) 317{ 318 return StackPrinter::GetInstance().Print(stacktrace, out); 319} 320 321} // namespace panda 322