1
0
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:
A. R. Shajii 2021-11-11 11:50:00 -05:00
parent 9653a1cba0
commit d69af8a301
9 changed files with 185 additions and 41 deletions

View File

@ -323,6 +323,7 @@ llvm_map_components_to_libnames(LLVM_LIBS
Remarks
ScalarOpts
Support
Symbolize
Target
TransformUtils
Vectorize

View File

@ -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);

View File

@ -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());
}

View File

@ -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());

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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"