#include // for backtrace #include #include #include #include #include "TraceUtils.h" #include "../../../music/providers/shared/pstream.h" #define READ_BUFFER_SIZE 128 namespace TraceUtils { static bool addr2line_present = true; inline std::string addr2lineInfo(StackTraceElement *element) { if(!addr2line_present) return "??\n??:0"; char buffer[READ_BUFFER_SIZE]; sprintf(buffer, "addr2line -Cif -e %s %p", element->file.c_str(), element->offset); //last parameter is the name of this app redi::pstream stream(buffer, redi::pstream::pstdout | redi::pstream::pstderr); std::string result; std::string error; do { auto read = stream.err().readsome(buffer, READ_BUFFER_SIZE); if(read > 0) error += string(buffer, read); read = stream.out().readsome(buffer, READ_BUFFER_SIZE); if(read > 0) result += string(buffer, read); } while(stream.good()); if(!error.empty()) { while(!error.empty() && (error.back() == '\n' || error.back() == '\r')) error = error.substr(0, error.length() - 1); logError("Could not resolve symbols. (Error: " + error + ")"); addr2line_present = false; return "??\n??:0"; } return result; } void StackTrace::printStackTrace() { printStackTrace([](StackTraceElement* e) { cerr << " at "+e->getFunctionName()+"( " + e->getSourceFile() + ":" + to_string(e->getSourceLine()) + ")" << endl; }); } void StackTrace::printStackTrace(std::function writeMessage) { for (int i = 0; i < stackSize; i++) { writeMessage((StackTraceElement*) elements[i]); } } StackTrace backTrace(int size) { int backtraceLength; void *buffer[BT_BUF_SIZE]; char **symbols; backtraceLength = backtrace(buffer, BT_BUF_SIZE); symbols = backtrace_symbols(buffer, backtraceLength); if (symbols == nullptr) { perror("backtrace_symbols"); exit(EXIT_FAILURE); } StackTrace out(backtraceLength); for (int i = 0; i < backtraceLength; i++) { auto sym = std::string(symbols[i]); string file = "undefined"; if (sym.find_first_of('(') != std::string::npos) file = sym.substr(0, sym.find_first_of('(')); out.elements[i] = new StackTraceElement{i, buffer[i], file, sym}; } free(symbols); return out; } StackTrace::StackTrace(int size) : stackSize(size), elements(static_cast(malloc(size * sizeof(void *)))) {} StackTrace::~StackTrace() { for (int i = 0; i < this->stackSize; i++) if (this->elements[i]) delete this->elements[i]; free(this->elements); } void StackTraceElement::loadSymbols() { if (this->symbolLoadState == 0) { auto strInfo = addr2lineInfo(this); this->fnName = strInfo.substr(0, strInfo.find_first_of('\n')); auto srcInfo = strInfo.substr(strInfo.find_first_of('\n') + 1); this->srcFile = srcInfo.substr(0, srcInfo.find_first_of(':')); this->srcLine = atoi(srcInfo.substr(srcInfo.find_first_of(':') + 1).c_str()); this->symbolLoadState = 1; } } string StackTraceElement::getFunctionName() { loadSymbols(); return this->fnName; } string StackTraceElement::getSourceFile() { loadSymbols(); return this->srcFile; } int StackTraceElement::getSourceLine() { loadSymbols(); return this->srcLine; } StackTraceElement::StackTraceElement(int elementIndex, void *offset, string file, string symbol) : elementIndex(elementIndex), offset(offset), file(file), symbol(symbol) {} }