TeaSpeakLibrary/src/misc/TraceUtils.cpp

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) {}
}