119 lines
3.9 KiB
C++
119 lines
3.9 KiB
C++
#include <execinfo.h> // for backtrace
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <functional>
|
|
#include <src/log/LogUtils.h>
|
|
|
|
#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<void(StackTraceElement*)> 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<const StackTraceElement **>(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) {}
|
|
} |