mirror of
https://github.com/exaloop/codon.git
synced 2025-06-03 15:03:52 +08:00
Refactor backtraces
This commit is contained in:
parent
e76b756226
commit
8dc2c45e88
@ -1,13 +1,57 @@
|
|||||||
#include "debug_listener.h"
|
#include "debug_listener.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "codon/runtime/lib.h"
|
||||||
|
|
||||||
namespace codon {
|
namespace codon {
|
||||||
|
namespace {
|
||||||
|
std::string unmangleType(llvm::StringRef s) {
|
||||||
|
auto p = s.rsplit('.');
|
||||||
|
return (p.second.empty() ? p.first : p.second).str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string unmangleFunc(llvm::StringRef s) {
|
||||||
|
// separate type and function
|
||||||
|
auto p = s.split(':');
|
||||||
|
llvm::StringRef func = s;
|
||||||
|
std::string type;
|
||||||
|
if (!p.second.empty()) {
|
||||||
|
type = unmangleType(p.first);
|
||||||
|
func = p.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim off ".<id>"
|
||||||
|
p = func.rsplit('.');
|
||||||
|
if (!p.second.empty() && p.second.find_if([](char c) { return !std::isdigit(c); }) ==
|
||||||
|
llvm::StringRef::npos)
|
||||||
|
func = p.first;
|
||||||
|
|
||||||
|
// trim off generics
|
||||||
|
func = func.split('[').first;
|
||||||
|
|
||||||
|
// trim off qualified name
|
||||||
|
p = func.rsplit('.');
|
||||||
|
if (!p.second.empty())
|
||||||
|
func = p.second;
|
||||||
|
|
||||||
|
if (!type.empty())
|
||||||
|
return type + "." + func.str();
|
||||||
|
else
|
||||||
|
return func.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string simplifyFile(llvm::StringRef s) {
|
||||||
|
auto p = s.rsplit('/');
|
||||||
|
return (p.second.empty() ? p.first : p.second).str();
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void DebugListener::notifyObjectLoaded(ObjectKey key,
|
void DebugListener::notifyObjectLoaded(ObjectKey key,
|
||||||
const llvm::object::ObjectFile &obj,
|
const llvm::object::ObjectFile &obj,
|
||||||
const llvm::RuntimeDyld::LoadedObjectInfo &L) {
|
const llvm::RuntimeDyld::LoadedObjectInfo &L) {
|
||||||
intptr_t start = 0, stop = 0;
|
uintptr_t start = 0, stop = 0;
|
||||||
for (const auto &sec : obj.sections()) {
|
for (const auto &sec : obj.sections()) {
|
||||||
if (sec.isText()) {
|
if (sec.isText()) {
|
||||||
start = L.getSectionLoadAddress(sec);
|
start = L.getSectionLoadAddress(sec);
|
||||||
@ -25,15 +69,33 @@ void DebugListener::notifyFreeingObject(ObjectKey key) {
|
|||||||
objects.end());
|
objects.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Expected<llvm::DILineInfo> DebugListener::symbolize(intptr_t pc) {
|
llvm::Expected<llvm::DILineInfo> DebugListener::symbolize(uintptr_t pc) {
|
||||||
for (const auto &o : objects) {
|
for (const auto &o : objects) {
|
||||||
if (o.contains(pc)) {
|
if (o.contains(pc)) {
|
||||||
return sym.symbolizeCode(o.getObject(),
|
return sym.symbolizeCode(
|
||||||
{static_cast<uint64_t>(pc - o.getStart()),
|
o.getObject(),
|
||||||
llvm::object::SectionedAddress::UndefSection});
|
{pc - o.getStart(), llvm::object::SectionedAddress::UndefSection});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return llvm::DILineInfo();
|
return llvm::DILineInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string DebugListener::getPrettyBacktrace(const std::vector<uintptr_t> &backtrace) {
|
||||||
|
auto invalid = [](const std::string &name) { return name == "<invalid>"; };
|
||||||
|
std::ostringstream buf;
|
||||||
|
buf << "\033[1mBacktrace:\033[0m\n";
|
||||||
|
for (auto pc : backtrace) {
|
||||||
|
auto src = symbolize(pc);
|
||||||
|
if (auto err = src.takeError())
|
||||||
|
break;
|
||||||
|
if (invalid(src->FunctionName) || invalid(src->FileName))
|
||||||
|
continue;
|
||||||
|
buf << " "
|
||||||
|
<< makeBacktraceFrameString(pc, unmangleFunc(src->FunctionName),
|
||||||
|
simplifyFile(src->FileName), src->Line, src->Column)
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
return buf.str();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace codon
|
} // namespace codon
|
||||||
|
@ -13,19 +13,19 @@ public:
|
|||||||
private:
|
private:
|
||||||
ObjectKey key;
|
ObjectKey key;
|
||||||
const llvm::object::ObjectFile *object;
|
const llvm::object::ObjectFile *object;
|
||||||
intptr_t start;
|
uintptr_t start;
|
||||||
intptr_t stop;
|
uintptr_t stop;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ObjectInfo(ObjectKey key, const llvm::object::ObjectFile *object, intptr_t start,
|
ObjectInfo(ObjectKey key, const llvm::object::ObjectFile *object, uintptr_t start,
|
||||||
intptr_t stop)
|
uintptr_t stop)
|
||||||
: key(key), object(object), start(start), stop(stop) {}
|
: key(key), object(object), start(start), stop(stop) {}
|
||||||
|
|
||||||
ObjectKey getKey() const { return key; }
|
ObjectKey getKey() const { return key; }
|
||||||
const llvm::object::ObjectFile &getObject() const { return *object; }
|
const llvm::object::ObjectFile &getObject() const { return *object; }
|
||||||
intptr_t getStart() const { return start; }
|
uintptr_t getStart() const { return start; }
|
||||||
intptr_t getStop() const { return stop; }
|
uintptr_t getStop() const { return stop; }
|
||||||
bool contains(intptr_t pc) const { return start <= pc && pc < stop; }
|
bool contains(uintptr_t pc) const { return start <= pc && pc < stop; }
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -39,7 +39,8 @@ private:
|
|||||||
public:
|
public:
|
||||||
DebugListener() : llvm::JITEventListener(), sym(), objects() {}
|
DebugListener() : llvm::JITEventListener(), sym(), objects() {}
|
||||||
|
|
||||||
llvm::Expected<llvm::DILineInfo> symbolize(intptr_t pc);
|
llvm::Expected<llvm::DILineInfo> symbolize(uintptr_t pc);
|
||||||
|
std::string getPrettyBacktrace(const std::vector<uintptr_t> &backtrace);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace codon
|
} // namespace codon
|
||||||
|
@ -103,42 +103,6 @@ llvm::Expected<std::string> JIT::run(const ir::Func *input,
|
|||||||
return buffer.str();
|
return buffer.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<ir::Func *, std::vector<ir::Var *>>
|
|
||||||
JIT::transformSimplified(const ast::StmtPtr &simplified) {
|
|
||||||
auto *cache = compiler->getCache();
|
|
||||||
|
|
||||||
// LOG("-- {}", simplified->toString(1));
|
|
||||||
|
|
||||||
auto typechecked = ast::TypecheckVisitor::apply(cache, simplified);
|
|
||||||
std::vector<std::string> globalNames;
|
|
||||||
for (auto &g : cache->globals) {
|
|
||||||
if (!g.second)
|
|
||||||
globalNames.push_back(g.first);
|
|
||||||
}
|
|
||||||
// add newly realized functions
|
|
||||||
std::vector<ast::StmtPtr> v;
|
|
||||||
std::vector<ir::Func **> frs;
|
|
||||||
v.push_back(typechecked);
|
|
||||||
for (auto &p : cache->pendingRealizations) {
|
|
||||||
v.push_back(cache->functions[p.first].ast);
|
|
||||||
frs.push_back(&cache->functions[p.first].realizations[p.second]->ir);
|
|
||||||
}
|
|
||||||
auto func =
|
|
||||||
ast::TranslateVisitor::apply(cache, std::make_shared<ast::SuiteStmt>(v, false));
|
|
||||||
cache->jitCell++;
|
|
||||||
|
|
||||||
std::vector<ir::Var *> globalVars;
|
|
||||||
for (auto &g : globalNames) {
|
|
||||||
seqassert(cache->globals[g], "JIT global {} not set", g);
|
|
||||||
globalVars.push_back(cache->globals[g]);
|
|
||||||
}
|
|
||||||
for (auto &i : frs) {
|
|
||||||
seqassert(*i, "JIT fn not set");
|
|
||||||
globalVars.push_back(*i);
|
|
||||||
}
|
|
||||||
return {func, globalVars};
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::Expected<std::string> JIT::exec(const std::string &code) {
|
llvm::Expected<std::string> JIT::exec(const std::string &code) {
|
||||||
auto *cache = compiler->getCache();
|
auto *cache = compiler->getCache();
|
||||||
ast::StmtPtr node = ast::parseCode(cache, JIT_FILENAME, code, /*startLine=*/0);
|
ast::StmtPtr node = ast::parseCode(cache, JIT_FILENAME, code, /*startLine=*/0);
|
||||||
@ -155,8 +119,35 @@ llvm::Expected<std::string> JIT::exec(const std::string &code) {
|
|||||||
simplified->stmts.push_back(s);
|
simplified->stmts.push_back(s);
|
||||||
// TODO: unroll on errors...
|
// TODO: unroll on errors...
|
||||||
|
|
||||||
auto p = transformSimplified(simplified);
|
auto *cache = compiler->getCache();
|
||||||
return run(p.first, p.second);
|
auto typechecked = ast::TypecheckVisitor::apply(cache, simplified);
|
||||||
|
std::vector<std::string> globalNames;
|
||||||
|
for (auto &g : cache->globals) {
|
||||||
|
if (!g.second)
|
||||||
|
globalNames.push_back(g.first);
|
||||||
|
}
|
||||||
|
// add newly realized functions
|
||||||
|
std::vector<ast::StmtPtr> v;
|
||||||
|
std::vector<ir::Func **> frs;
|
||||||
|
v.push_back(typechecked);
|
||||||
|
for (auto &p : cache->pendingRealizations) {
|
||||||
|
v.push_back(cache->functions[p.first].ast);
|
||||||
|
frs.push_back(&cache->functions[p.first].realizations[p.second]->ir);
|
||||||
|
}
|
||||||
|
auto func =
|
||||||
|
ast::TranslateVisitor::apply(cache, std::make_shared<ast::SuiteStmt>(v, false));
|
||||||
|
cache->jitCell++;
|
||||||
|
|
||||||
|
std::vector<ir::Var *> globalVars;
|
||||||
|
for (auto &g : globalNames) {
|
||||||
|
seqassert(cache->globals[g], "JIT global {} not set", g);
|
||||||
|
globalVars.push_back(cache->globals[g]);
|
||||||
|
}
|
||||||
|
for (auto &i : frs) {
|
||||||
|
seqassert(*i, "JIT fn not set");
|
||||||
|
globalVars.push_back(*i);
|
||||||
|
}
|
||||||
|
return run(func, globalVars);
|
||||||
} catch (const exc::ParserException &e) {
|
} catch (const exc::ParserException &e) {
|
||||||
return llvm::make_error<error::ParserErrorInfo>(e);
|
return llvm::make_error<error::ParserErrorInfo>(e);
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,14 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit JIT(const std::string &argv0);
|
explicit JIT(const std::string &argv0);
|
||||||
|
|
||||||
|
Compiler *getCompiler() const { return compiler.get(); }
|
||||||
|
Engine *getEngine() const { return engine.get(); }
|
||||||
|
|
||||||
llvm::Error init();
|
llvm::Error init();
|
||||||
llvm::Expected<std::string> run(const ir::Func *input,
|
llvm::Expected<std::string> run(const ir::Func *input,
|
||||||
const std::vector<ir::Var *> &newGlobals = {});
|
const std::vector<ir::Var *> &newGlobals = {});
|
||||||
llvm::Expected<std::string> exec(const std::string &code);
|
llvm::Expected<std::string> exec(const std::string &code);
|
||||||
|
|
||||||
private:
|
|
||||||
std::pair<ir::Func *, std::vector<ir::Var *>>
|
|
||||||
transformSimplified(const ast::StmtPtr &simplified);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace jit
|
} // namespace jit
|
||||||
|
@ -236,9 +236,11 @@ SEQ_FUNC void seq_terminate(void *exc) {
|
|||||||
buf << "\n\033[1mBacktrace:\033[0m\n";
|
buf << "\n\033[1mBacktrace:\033[0m\n";
|
||||||
for (unsigned i = 0; i < bt->count; i++) {
|
for (unsigned i = 0; i < bt->count; i++) {
|
||||||
auto *frame = &bt->frames[i];
|
auto *frame = &bt->frames[i];
|
||||||
buf << " [\033[33m0x" << std::hex << frame->pc << std::dec
|
buf << " "
|
||||||
<< "\033[0m] \033[32m" << frame->function << "\033[0m " << frame->filename
|
<< codon::makeBacktraceFrameString(frame->pc, std::string(frame->function),
|
||||||
<< ":" << frame->lineno << "\n";
|
std::string(frame->filename),
|
||||||
|
frame->lineno)
|
||||||
|
<< "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -587,3 +589,23 @@ SEQ_FUNC int64_t seq_exc_offset() {
|
|||||||
SEQ_FUNC uint64_t seq_exc_class() {
|
SEQ_FUNC uint64_t seq_exc_class() {
|
||||||
return genClass(ourBaseExcpClassChars, sizeof(ourBaseExcpClassChars));
|
return genClass(ourBaseExcpClassChars, sizeof(ourBaseExcpClassChars));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string codon::makeBacktraceFrameString(uintptr_t pc, const std::string &func,
|
||||||
|
const std::string &file, int line,
|
||||||
|
int col) {
|
||||||
|
std::ostringstream buf;
|
||||||
|
buf << "[\033[33m0x" << std::hex << pc << std::dec << "\033[0m]";
|
||||||
|
if (!func.empty()) {
|
||||||
|
buf << " \033[32m" << func << "\033[0m";
|
||||||
|
if (!file.empty()) {
|
||||||
|
buf << " at \033[36m" << file << "\033[0m";
|
||||||
|
if (line != 0) {
|
||||||
|
buf << ":\033[33m" << line << "\033[0m";
|
||||||
|
if (col != 0) {
|
||||||
|
buf << ":\033[33m" << col << "\033[0m";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.str();
|
||||||
|
}
|
||||||
|
@ -101,4 +101,8 @@ public:
|
|||||||
int getCol() const { return col; }
|
int getCol() const { return col; }
|
||||||
std::vector<uintptr_t> getBacktrace() const { return backtrace; }
|
std::vector<uintptr_t> getBacktrace() const { return backtrace; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string makeBacktraceFrameString(uintptr_t pc, const std::string &func = "",
|
||||||
|
const std::string &file = "", int line = 0,
|
||||||
|
int col = 0);
|
||||||
} // namespace codon
|
} // namespace codon
|
||||||
|
@ -379,48 +379,6 @@ void LLVMVisitor::compile(const std::string &filename,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::string unmangleType(llvm::StringRef s) {
|
|
||||||
auto p = s.rsplit('.');
|
|
||||||
return (p.second.empty() ? p.first : p.second).str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string unmangleFunc(llvm::StringRef s) {
|
|
||||||
// separate type and function
|
|
||||||
auto p = s.split(':');
|
|
||||||
llvm::StringRef func = s;
|
|
||||||
std::string type;
|
|
||||||
if (!p.second.empty()) {
|
|
||||||
type = unmangleType(p.first);
|
|
||||||
func = p.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
// trim off ".<id>"
|
|
||||||
p = func.rsplit('.');
|
|
||||||
if (!p.second.empty() && p.second.find_if([](char c) { return !std::isdigit(c); }) ==
|
|
||||||
llvm::StringRef::npos)
|
|
||||||
func = p.first;
|
|
||||||
|
|
||||||
// trim off generics
|
|
||||||
func = func.split('[').first;
|
|
||||||
|
|
||||||
// trim off qualified name
|
|
||||||
p = func.rsplit('.');
|
|
||||||
if (!p.second.empty())
|
|
||||||
func = p.second;
|
|
||||||
|
|
||||||
if (!type.empty())
|
|
||||||
return type + "." + func.str();
|
|
||||||
else
|
|
||||||
return func.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string simplifyFile(llvm::StringRef s) {
|
|
||||||
auto p = s.rsplit('/');
|
|
||||||
return (p.second.empty() ? p.first : p.second).str();
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void LLVMVisitor::run(const std::vector<std::string> &args,
|
void LLVMVisitor::run(const std::vector<std::string> &args,
|
||||||
const std::vector<std::string> &libs, const char *const *envp) {
|
const std::vector<std::string> &libs, const char *const *envp) {
|
||||||
runLLVMPipeline();
|
runLLVMPipeline();
|
||||||
@ -446,28 +404,8 @@ void LLVMVisitor::run(const std::vector<std::string> &args,
|
|||||||
eng->runFunctionAsMain(main, args, envp);
|
eng->runFunctionAsMain(main, args, envp);
|
||||||
} catch (const JITError &e) {
|
} catch (const JITError &e) {
|
||||||
fmt::print(stderr, "{}", e.getOutput());
|
fmt::print(stderr, "{}", e.getOutput());
|
||||||
if (db.debug) {
|
if (db.debug)
|
||||||
llvm::symbolize::LLVMSymbolizer sym;
|
fmt::print(stderr, "\n{}", dbListener->getPrettyBacktrace(e.getBacktrace()));
|
||||||
auto invalid = [](const std::string &name) { return name == "<invalid>"; };
|
|
||||||
|
|
||||||
fmt::print(stderr, "\n\033[1mBacktrace:\033[0m\n");
|
|
||||||
for (auto pc : e.getBacktrace()) {
|
|
||||||
auto src = dbListener->symbolize(pc);
|
|
||||||
if (auto err = src.takeError())
|
|
||||||
break;
|
|
||||||
if (invalid(src->FunctionName) || invalid(src->FileName))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto func = unmangleFunc(src->FunctionName);
|
|
||||||
auto file = simplifyFile(src->FileName);
|
|
||||||
auto line = src->Line;
|
|
||||||
auto col = src->Column;
|
|
||||||
fmt::print(stderr,
|
|
||||||
" [\033[33m0x{:016x}\033[0m] \033[32m{}\033[0m at "
|
|
||||||
"\033[36m{}\033[0m:\033[33m{}\033[0m:\033[33m{}\033[0m\n",
|
|
||||||
pc, func, file, line, col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
delete eng;
|
delete eng;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user