mirror of
https://github.com/exaloop/codon.git
synced 2025-06-03 15:03:52 +08:00
Support run-mode backtraces
This commit is contained in:
parent
9653a1cba0
commit
d69af8a301
@ -323,6 +323,7 @@ llvm_map_components_to_libnames(LLVM_LIBS
|
||||
Remarks
|
||||
ScalarOpts
|
||||
Support
|
||||
Symbolize
|
||||
Target
|
||||
TransformUtils
|
||||
Vectorize
|
||||
|
@ -79,7 +79,8 @@ int docMode(const std::vector<const char *> &args, const std::string &argv0) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
std::unique_ptr<codon::Compiler> processSource(const std::vector<const char *> &args) {
|
||||
std::unique_ptr<codon::Compiler> processSource(const std::vector<const char *> &args,
|
||||
bool standalone) {
|
||||
llvm::cl::opt<std::string> input(llvm::cl::Positional, llvm::cl::desc("<input file>"),
|
||||
llvm::cl::init("-"));
|
||||
llvm::cl::opt<OptMode> optMode(
|
||||
@ -133,6 +134,7 @@ std::unique_ptr<codon::Compiler> processSource(const std::vector<const char *> &
|
||||
const bool isDebug = (optMode == OptMode::Debug);
|
||||
std::vector<std::string> disabledOptsVec(disabledOpts);
|
||||
auto compiler = std::make_unique<codon::Compiler>(args[0], isDebug, disabledOptsVec);
|
||||
compiler->getLLVMVisitor()->setStandalone(standalone);
|
||||
|
||||
// load plugins
|
||||
for (const auto &plugin : plugins) {
|
||||
@ -168,7 +170,7 @@ int runMode(const std::vector<const char *> &args) {
|
||||
"l", llvm::cl::desc("Load and link the specified library"));
|
||||
llvm::cl::list<std::string> seqArgs(llvm::cl::ConsumeAfter,
|
||||
llvm::cl::desc("<program arguments>..."));
|
||||
auto compiler = processSource(args);
|
||||
auto compiler = processSource(args, /*standalone=*/false);
|
||||
if (!compiler)
|
||||
return EXIT_FAILURE;
|
||||
std::vector<std::string> libsVec(libs);
|
||||
@ -229,7 +231,7 @@ int buildMode(const std::vector<const char *> &args) {
|
||||
"Write compiled output to specified file. Supported extensions: "
|
||||
"none (executable), .o (object file), .ll (LLVM IR), .bc (LLVM bitcode)"));
|
||||
|
||||
auto compiler = processSource(args);
|
||||
auto compiler = processSource(args, /*standalone=*/true);
|
||||
if (!compiler)
|
||||
return EXIT_FAILURE;
|
||||
std::vector<std::string> libsVec(libs);
|
||||
|
@ -19,9 +19,10 @@ Compiler::Compiler(const std::string &argv0, bool debug,
|
||||
module(std::make_unique<ir::Module>()),
|
||||
pm(std::make_unique<ir::transform::PassManager>(debug && !isTest,
|
||||
disabledPasses)),
|
||||
llvisitor(std::make_unique<ir::LLVMVisitor>(debug)) {
|
||||
llvisitor(std::make_unique<ir::LLVMVisitor>()) {
|
||||
cache->module = module.get();
|
||||
module->setCache(cache.get());
|
||||
llvisitor->setDebug(debug);
|
||||
llvisitor->setPluginManager(plm.get());
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ llvm::Expected<std::string> JIT::run(const ir::Func *input,
|
||||
try {
|
||||
CaptureOutput(buffer.rdbuf());
|
||||
(*repl)();
|
||||
} catch (const seq_jit_error &err) {
|
||||
} catch (const JITError &err) {
|
||||
return llvm::make_error<error::RuntimeErrorInfo>(err.getOutput(), err.getType(),
|
||||
err.what(), err.getFile(),
|
||||
err.getLine(), err.getCol());
|
||||
|
@ -7,10 +7,14 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
|
||||
struct BacktraceFrame {
|
||||
char *function;
|
||||
char *filename;
|
||||
@ -42,6 +46,8 @@ struct Backtrace {
|
||||
frames[count++] = {functionDup, filenameDup, pc, lineno};
|
||||
}
|
||||
|
||||
void push_back(uintptr_t pc) { push_back("<invalid>", "<invalid>", pc, 0); }
|
||||
|
||||
void free() {
|
||||
for (auto i = 0; i < count; i++) {
|
||||
auto *frame = &frames[i];
|
||||
@ -55,7 +61,7 @@ struct Backtrace {
|
||||
};
|
||||
|
||||
void seq_backtrace_error_callback(void *data, const char *msg, int errnum) {
|
||||
// nothing to do
|
||||
// printf("seq_backtrace_error_callback: %s (errnum = %d)\n", msg, errnum);
|
||||
}
|
||||
|
||||
int seq_backtrace_full_callback(void *data, uintptr_t pc, const char *filename,
|
||||
@ -140,6 +146,7 @@ static void seq_delete_unwind_exc(_Unwind_Reason_Code reason,
|
||||
}
|
||||
|
||||
static struct backtrace_state *state = nullptr;
|
||||
static std::mutex stateLock;
|
||||
|
||||
SEQ_FUNC void *seq_alloc_exc(int type, void *obj) {
|
||||
const size_t size = sizeof(OurException);
|
||||
@ -152,11 +159,33 @@ SEQ_FUNC void *seq_alloc_exc(int type, void *obj) {
|
||||
if (seq_flags & SEQ_FLAG_DEBUG) {
|
||||
e->bt.frames = nullptr;
|
||||
e->bt.count = 0;
|
||||
|
||||
if (seq_flags & SEQ_FLAG_STANDALONE) {
|
||||
if (!state) {
|
||||
stateLock.lock();
|
||||
if (!state)
|
||||
state = backtrace_create_state(/*filename=*/nullptr, /*threaded=*/0,
|
||||
state =
|
||||
backtrace_create_state(/*filename=*/nullptr, /*threaded=*/1,
|
||||
seq_backtrace_error_callback, /*data=*/nullptr);
|
||||
stateLock.unlock();
|
||||
}
|
||||
backtrace_full(state, /*skip=*/1, seq_backtrace_full_callback,
|
||||
seq_backtrace_error_callback, &e->bt);
|
||||
} else {
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t context;
|
||||
|
||||
unw_getcontext(&context);
|
||||
unw_init_local(&cursor, &context);
|
||||
|
||||
while (unw_step(&cursor) > 0) {
|
||||
unw_word_t pc;
|
||||
unw_get_reg(&cursor, UNW_REG_IP, &pc);
|
||||
if (pc == 0)
|
||||
break;
|
||||
e->bt.push_back(pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
return &(e->unwindException);
|
||||
}
|
||||
@ -209,7 +238,7 @@ SEQ_FUNC void seq_terminate(void *exc) {
|
||||
}
|
||||
buf << "\n";
|
||||
|
||||
if (seq_flags & SEQ_FLAG_DEBUG) {
|
||||
if ((seq_flags & SEQ_FLAG_DEBUG) && (seq_flags & SEQ_FLAG_STANDALONE)) {
|
||||
auto *bt = &base->bt;
|
||||
if (bt->count > 0) {
|
||||
buf << "\n\033[1mBacktrace:\033[0m\n";
|
||||
@ -223,14 +252,23 @@ SEQ_FUNC void seq_terminate(void *exc) {
|
||||
}
|
||||
|
||||
auto output = buf.str();
|
||||
if (seq_flags & SEQ_FLAG_JIT) {
|
||||
if (seq_flags & SEQ_FLAG_STANDALONE) {
|
||||
fwrite(output.data(), 1, output.size(), stderr);
|
||||
abort();
|
||||
} else {
|
||||
auto *bt = &base->bt;
|
||||
std::string msg(hdr->msg.str, hdr->msg.len);
|
||||
std::string file(hdr->file.str, hdr->file.len);
|
||||
std::string type(hdr->type.str, hdr->type.len);
|
||||
throw seq_jit_error(output, msg, type, file, (int)hdr->line, (int)hdr->col);
|
||||
} else {
|
||||
fwrite(output.data(), 1, output.size(), stderr);
|
||||
abort();
|
||||
|
||||
std::vector<uintptr_t> backtrace;
|
||||
if (seq_flags & SEQ_FLAG_DEBUG) {
|
||||
for (unsigned i = 0; i < bt->count; i++) {
|
||||
backtrace.push_back(bt->frames[i].pc);
|
||||
}
|
||||
}
|
||||
throw codon::JITError(output, msg, type, file, (int)hdr->line, (int)hdr->col,
|
||||
backtrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,19 +7,16 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unwind.h>
|
||||
#include <vector>
|
||||
|
||||
#define SEQ_FLAG_DEBUG (1 << 0)
|
||||
#define SEQ_FLAG_JIT (1 << 1)
|
||||
#define SEQ_FLAG_DEBUG (1 << 0) // compiled/running in debug mode
|
||||
#define SEQ_FLAG_JIT (1 << 1) // compiled/running in JIT mode
|
||||
#define SEQ_FLAG_STANDALONE (1 << 2) // compiled as a standalone object/binary
|
||||
|
||||
#define SEQ_FUNC extern "C"
|
||||
|
||||
typedef int64_t seq_int_t;
|
||||
|
||||
struct seq_t {
|
||||
seq_int_t len;
|
||||
char *seq;
|
||||
};
|
||||
|
||||
struct seq_str_t {
|
||||
seq_int_t len;
|
||||
char *str;
|
||||
@ -80,24 +77,28 @@ SEQ_FUNC void *seq_rlock_new();
|
||||
SEQ_FUNC bool seq_rlock_acquire(void *lock, bool block, double timeout);
|
||||
SEQ_FUNC void seq_rlock_release(void *lock);
|
||||
|
||||
class seq_jit_error : public std::runtime_error {
|
||||
namespace codon {
|
||||
class JITError : public std::runtime_error {
|
||||
private:
|
||||
std::string output;
|
||||
std::string type;
|
||||
std::string file;
|
||||
int line;
|
||||
int col;
|
||||
std::vector<uintptr_t> backtrace;
|
||||
|
||||
public:
|
||||
explicit seq_jit_error(const std::string &output, const std::string &what,
|
||||
const std::string &type, const std::string &file, int line,
|
||||
int col)
|
||||
JITError(const std::string &output, const std::string &what, const std::string &type,
|
||||
const std::string &file, int line, int col,
|
||||
std::vector<uintptr_t> backtrace = {})
|
||||
: std::runtime_error(what), output(output), type(type), file(file), line(line),
|
||||
col(col) {}
|
||||
col(col), backtrace(std::move(backtrace)) {}
|
||||
|
||||
std::string getOutput() const { return output; }
|
||||
std::string getType() const { return type; }
|
||||
std::string getFile() const { return file; }
|
||||
int getLine() const { return line; }
|
||||
int getCol() const { return col; }
|
||||
std::vector<uintptr_t> getBacktrace() const { return backtrace; }
|
||||
};
|
||||
} // namespace codon
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "llvisitor.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
@ -27,11 +29,11 @@ llvm::DIFile *LLVMVisitor::DebugInfo::getFile(const std::string &path) {
|
||||
return builder->createFile(filename, directory);
|
||||
}
|
||||
|
||||
LLVMVisitor::LLVMVisitor(bool debug, bool jit, const std::string &flags)
|
||||
LLVMVisitor::LLVMVisitor()
|
||||
: util::ConstVisitor(), context(std::make_unique<llvm::LLVMContext>()), M(),
|
||||
B(std::make_unique<llvm::IRBuilder<>>(*context)), func(nullptr), block(nullptr),
|
||||
value(nullptr), vars(), funcs(), coro(), loops(), trycatch(),
|
||||
db(debug, jit, flags), plugins(nullptr) {
|
||||
value(nullptr), vars(), funcs(), coro(), loops(), trycatch(), db(),
|
||||
plugins(nullptr) {
|
||||
llvm::InitializeAllTargets();
|
||||
llvm::InitializeAllTargetMCs();
|
||||
llvm::InitializeAllAsmPrinters();
|
||||
@ -376,6 +378,63 @@ void LLVMVisitor::compile(const std::string &filename,
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
class DebugInfoListener : public llvm::JITEventListener {
|
||||
public:
|
||||
std::pair<std::unique_ptr<llvm::object::ObjectFile>,
|
||||
std::unique_ptr<llvm::MemoryBuffer>>
|
||||
saved;
|
||||
intptr_t start = 0;
|
||||
|
||||
void notifyObjectLoaded(ObjectKey key, const llvm::object::ObjectFile &obj,
|
||||
const llvm::RuntimeDyld::LoadedObjectInfo &L) override {
|
||||
start = L.getSectionLoadAddress(*obj.section_begin());
|
||||
saved = L.getObjectForDebug(obj).takeBinary();
|
||||
if (!saved.first) {
|
||||
auto buf = llvm::MemoryBuffer::getMemBufferCopy(obj.getData(), obj.getFileName());
|
||||
auto newObj = llvm::cantFail(
|
||||
llvm::object::ObjectFile::createObjectFile(buf->getMemBufferRef()));
|
||||
saved = std::make_pair(std::move(newObj), std::move(buf));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void LLVMVisitor::run(const std::vector<std::string> &args,
|
||||
const std::vector<std::string> &libs, const char *const *envp) {
|
||||
runLLVMPipeline();
|
||||
@ -384,14 +443,48 @@ void LLVMVisitor::run(const std::vector<std::string> &args,
|
||||
EB.setMCJITMemoryManager(std::make_unique<BoehmGCMemoryManager>());
|
||||
llvm::ExecutionEngine *eng = EB.create();
|
||||
|
||||
std::string err;
|
||||
auto dbListener = std::unique_ptr<DebugInfoListener>();
|
||||
if (db.debug) {
|
||||
dbListener = std::make_unique<DebugInfoListener>();
|
||||
eng->RegisterJITEventListener(dbListener.get());
|
||||
}
|
||||
|
||||
for (auto &lib : libs) {
|
||||
std::string err;
|
||||
if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(lib.c_str(), &err)) {
|
||||
compilationError(err);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
eng->runFunctionAsMain(main, args, envp);
|
||||
} catch (const JITError &e) {
|
||||
fmt::print(stderr, "{}", e.getOutput());
|
||||
if (db.debug) {
|
||||
llvm::symbolize::LLVMSymbolizer sym;
|
||||
auto invalid = [](const std::string &name) { return name == "<invalid>"; };
|
||||
|
||||
fmt::print(stderr, "\n\033[1mBacktrace:\033[0m\n");
|
||||
auto base = dbListener->start;
|
||||
for (auto pc : e.getBacktrace()) {
|
||||
auto src = sym.symbolizeCode(
|
||||
*dbListener->saved.first,
|
||||
{pc - base, llvm::object::SectionedAddress::UndefSection});
|
||||
if (auto err = src.takeError())
|
||||
break;
|
||||
if (invalid(src->FunctionName) || invalid(src->FileName))
|
||||
continue;
|
||||
|
||||
auto func = unmangleFunc(src->FunctionName);
|
||||
auto file = src->FileName;
|
||||
auto line = src->Line;
|
||||
auto col = src->Column;
|
||||
fmt::print(stderr, " [\033[33m0x{:016x}\033[0m] \033[32m{}\033[0m {}:{}:{}\n",
|
||||
pc, func, file, line, col);
|
||||
}
|
||||
}
|
||||
std::abort();
|
||||
}
|
||||
delete eng;
|
||||
}
|
||||
|
||||
@ -625,7 +718,8 @@ void LLVMVisitor::visit(const Module *x) {
|
||||
llvm::Value *argStorage = getVar(x->getArgVar());
|
||||
seqassert(argStorage, "argument storage missing");
|
||||
B->CreateStore(arr, argStorage);
|
||||
const int flags = (db.debug ? SEQ_FLAG_DEBUG : 0) | (db.jit ? SEQ_FLAG_JIT : 0);
|
||||
const int flags = (db.debug ? SEQ_FLAG_DEBUG : 0) | (db.jit ? SEQ_FLAG_JIT : 0) |
|
||||
(db.standalone ? SEQ_FLAG_STANDALONE : 0);
|
||||
B->CreateCall(initFunc, B->getInt32(flags));
|
||||
|
||||
// Put the entire program in a new function
|
||||
|
@ -98,11 +98,14 @@ private:
|
||||
bool debug;
|
||||
/// Whether we are compiling in JIT mode
|
||||
bool jit;
|
||||
/// Whether we are compiling a standalone object/executable
|
||||
bool standalone;
|
||||
/// Program command-line flags
|
||||
std::string flags;
|
||||
|
||||
DebugInfo(bool debug, bool jit, const std::string &flags)
|
||||
: builder(), unit(nullptr), debug(debug), jit(jit), flags(flags) {}
|
||||
DebugInfo()
|
||||
: builder(), unit(nullptr), debug(false), jit(false), standalone(false),
|
||||
flags() {}
|
||||
|
||||
llvm::DIFile *getFile(const std::string &path);
|
||||
|
||||
@ -223,11 +226,7 @@ public:
|
||||
}
|
||||
|
||||
/// Constructs an LLVM visitor.
|
||||
/// @param debug whether to compile in debug mode
|
||||
/// @param jit whether to compile in JIT mode
|
||||
/// @param flags command-line flags to be included in debug info
|
||||
explicit LLVMVisitor(bool debug = false, bool jit = false,
|
||||
const std::string &flags = "");
|
||||
LLVMVisitor();
|
||||
|
||||
/// @return true if in debug mode, false otherwise
|
||||
bool getDebug() const { return db.debug; }
|
||||
@ -241,6 +240,12 @@ public:
|
||||
/// @param j true if JIT mode
|
||||
void setJIT(bool j = true) { db.jit = j; }
|
||||
|
||||
/// @return true if in standalone mode, false otherwise
|
||||
bool getStandalone() const { return db.standalone; }
|
||||
/// Sets standalone status.
|
||||
/// @param s true if standalone
|
||||
void setStandalone(bool s = true) { db.standalone = s; }
|
||||
|
||||
/// @return program flags
|
||||
std::string getFlags() const { return db.flags; }
|
||||
/// Sets program flags.
|
||||
|
@ -12,8 +12,10 @@
|
||||
#include "llvm/Bitcode/BitcodeWriter.h"
|
||||
#include "llvm/CodeGen/MachineModuleInfo.h"
|
||||
#include "llvm/CodeGen/TargetPassConfig.h"
|
||||
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
|
||||
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||
#include "llvm/ExecutionEngine/GenericValue.h"
|
||||
#include "llvm/ExecutionEngine/JITEventListener.h"
|
||||
#include "llvm/ExecutionEngine/JITSymbol.h"
|
||||
#include "llvm/ExecutionEngine/MCJIT.h"
|
||||
#include "llvm/ExecutionEngine/RuntimeDyld.h"
|
||||
|
Loading…
x
Reference in New Issue
Block a user