Navigation Menu

Skip to content

Commit

Permalink
Added stack trace.
Browse files Browse the repository at this point in the history
  • Loading branch information
luckboy committed Aug 27, 2019
1 parent 01256b9 commit d353b73
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 19 deletions.
42 changes: 39 additions & 3 deletions include/letin/vm.hpp
Expand Up @@ -746,6 +746,38 @@ namespace letin

~InterruptibleFunctionAround();
};

class StackTraceElement
{
bool _M_has_native_fun;
std::size_t _M_fun;
std::unique_ptr<std::string> _M_fun_name;
std::uint32_t _M_instr;
public:
StackTraceElement() : _M_has_native_fun(false), _M_fun(0), _M_instr(0) {}

StackTraceElement(std::size_t fun, std::string *fun_name) :
_M_has_native_fun(true), _M_fun(fun), _M_fun_name(fun_name), _M_instr(0) {}

StackTraceElement(std::size_t fun, std::string *fun_name, std::uint32_t instr) :
_M_has_native_fun(false), _M_fun(fun), _M_fun_name(fun_name), _M_instr(instr) {}

StackTraceElement(const StackTraceElement &elem) :
_M_has_native_fun(elem._M_has_native_fun),
_M_fun(elem._M_fun),
_M_fun_name(elem._M_fun_name.get() != nullptr ? new std::string(*(elem._M_fun_name)) : nullptr),
_M_instr(elem._M_instr) {}

bool has_native_fun() const { return _M_has_native_fun; }

std::size_t fun() const { return _M_fun; }

const std::string *fun_name() const { return _M_fun_name.get(); }

std::string *fun_name() { return _M_fun_name.get(); }

std::uint32_t instr() const { return _M_instr; }
};

class VirtualMachine
{
Expand All @@ -768,10 +800,14 @@ namespace letin
bool load(const std::vector<std::string> &file_names, std::list<LoadingError> *errors = nullptr);

bool load(const char *file_name, std::list<LoadingError> *errors = nullptr);

virtual Thread start(std::size_t i, const std::vector<Value> &args, std::function<void (const ReturnValue &)> fun, bool is_force = true) = 0;

Thread start(const std::vector<Value> &args, std::function<void (const ReturnValue &)> fun, bool is_force = true) { return start(entry(), args, fun); }
virtual Thread start(std::size_t i, const std::vector<Value> &args, std::function<void (const ReturnValue &, const std::vector<StackTraceElement> *)> fun, bool is_force = true) = 0;

Thread start(const std::vector<Value> &args, std::function<void (const ReturnValue &, const std::vector<StackTraceElement> *)> fun, bool is_force = true) { return start(entry(), args, fun, is_force); }

Thread start(std::size_t i, const std::vector<Value> &args, std::function<void (const ReturnValue &)> fun, bool is_force = true) { return start(i, args, [fun](const ReturnValue &value, const std::vector<StackTraceElement> *stack_trace) { fun(value); }, is_force); }

Thread start(const std::vector<Value> &args, std::function<void (const ReturnValue &)> fun, bool is_force = true) { return start(entry(), args, fun, is_force); }

virtual Environment &env() = 0;

Expand Down
9 changes: 8 additions & 1 deletion vm/impl_env.cpp
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright (C) 2014-2015 Łukasz Szpakowski. *
* Copyright (C) 2014-2015, 2019 Łukasz Szpakowski. *
* *
* This software is licensed under the GNU Lesser General Public *
* License v3 or later. See the LICENSE file and the GPL file for *
Expand Down Expand Up @@ -61,6 +61,12 @@ namespace letin

FunctionInfo *ImplEnvironment::fun_infos()
{ return _M_fun_infos.get(); }

const map<size_t, string> &ImplEnvironment::fun_symbols() const
{ return _M_fun_symbols; }

const map<int, string> &ImplEnvironment::native_fun_symbols() const
{ return _M_native_fun_symbols; }

const list<MemoizationCache *> &ImplEnvironment::memo_caches() const
{ return _M_memo_caches; }
Expand All @@ -85,6 +91,7 @@ namespace letin
_M_var_count = 0;
_M_fun_infos = unique_ptr<FunctionInfo []>(nullptr);
_M_fun_indexes = unordered_map<string, size_t>();
_M_fun_symbols = map<size_t, string>();
_M_var_indexes = unordered_map<string, size_t>();
_M_memo_caches = list<MemoizationCache *>();
_M_data_list_to_free = list<unique_ptr<char []>>();
Expand Down
11 changes: 10 additions & 1 deletion vm/impl_env.hpp
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright (C) 2014-2015 Łukasz Szpakowski. *
* Copyright (C) 2014-2015, 2019 Łukasz Szpakowski. *
* *
* This software is licensed under the GNU Lesser General Public *
* License v3 or later. See the LICENSE file and the GPL file for *
Expand All @@ -10,6 +10,7 @@

#include <cstddef>
#include <list>
#include <map>
#include <memory>
#include <unordered_map>
#include <utility>
Expand All @@ -30,8 +31,10 @@ namespace letin
std::size_t _M_var_count;
std::unique_ptr<FunctionInfo []> _M_fun_infos;
std::unordered_map<std::string, std::size_t> _M_fun_indexes;
std::map<std::size_t, std::string> _M_fun_symbols;
std::unordered_map<std::string, std::size_t> _M_var_indexes;
std::unordered_map<std::string, int> _M_native_fun_indexes;
std::map<int, std::string> _M_native_fun_symbols;
std::list<MemoizationCache *> _M_memo_caches;
std::list<std::unique_ptr<char []>> _M_data_list_to_free;
public:
Expand Down Expand Up @@ -83,6 +86,10 @@ namespace letin

FunctionInfo *fun_infos();

const std::map<std::size_t, std::string> &fun_symbols() const;

const std::map<int, std::string> &native_fun_symbols() const;

const std::list<MemoizationCache *> &memo_caches() const;

void set_fun(std::size_t i, const Function &fun);
Expand All @@ -104,6 +111,7 @@ namespace letin
{
if(_M_fun_indexes.find(name) != _M_fun_indexes.end()) return false;
_M_fun_indexes.insert(std::make_pair(name, i));
_M_fun_symbols.insert(std::make_pair(i, name));
return true;
}

Expand All @@ -118,6 +126,7 @@ namespace letin
{
if(_M_native_fun_indexes.find(name) != _M_native_fun_indexes.end()) return false;
_M_native_fun_indexes.insert(std::make_pair(name, i));
_M_native_fun_symbols.insert(std::make_pair(i, name));
return true;
}

Expand Down
6 changes: 3 additions & 3 deletions vm/impl_vm_base.cpp
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright (C) 2014-2015 Łukasz Szpakowski. *
* Copyright (C) 2014-2015, 2019 Łukasz Szpakowski. *
* *
* This software is licensed under the GNU Lesser General Public *
* License v3 or later. See the LICENSE file and the GPL file for *
Expand Down Expand Up @@ -317,7 +317,7 @@ namespace letin
return true;
}

Thread ImplVirtualMachineBase::start(size_t i, const vector<Value> &args, function<void (const ReturnValue &)> fun, bool is_force)
Thread ImplVirtualMachineBase::start(size_t i, const vector<Value> &args, function<void (const ReturnValue &, const std::vector<StackTraceElement> *)> fun, bool is_force)
{
ThreadContext *context = new ThreadContext(_M_env);
Thread thread(context);
Expand All @@ -335,7 +335,7 @@ namespace letin
value = ReturnValue(0, 0.0, Reference(), ERROR_EXCEPTION);
}
lazy_value_mutex_sem.op(-1);
try { fun(value); } catch(...) {}
try { fun(value, nullptr); } catch(...) {}
_M_gc->delete_thread_context(thread2.context());
stop_thread_stop_cont();
if(_M_gc->must_stop_from_vm_thread() && _M_gc->thread_context_count() == 0) {
Expand Down
4 changes: 2 additions & 2 deletions vm/impl_vm_base.hpp
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright (C) 2014-2015 Łukasz Szpakowski. *
* Copyright (C) 2014-2015, 2019 Łukasz Szpakowski. *
* *
* This software is licensed under the GNU Lesser General Public *
* License v3 or later. See the LICENSE file and the GPL file for *
Expand Down Expand Up @@ -33,7 +33,7 @@ namespace letin
private:
bool load_prog(std::size_t i, Program *prog, std::size_t fun_offset, std::size_t var_offset, std::list<LoadingError> *errors, void *data_to_free);
public:
Thread start(std::size_t i, const std::vector<Value> &args, std::function<void (const ReturnValue &)> fun, bool is_force);
Thread start(std::size_t i, const std::vector<Value> &args, std::function<void (const ReturnValue &, const std::vector<StackTraceElement> *)> fun, bool is_force);
protected:
virtual ReturnValue start_in_thread(std::size_t i, const std::vector<Value> &args, ThreadContext &context, bool is_force) = 0;
public:
Expand Down
59 changes: 56 additions & 3 deletions vm/vm.cpp
Expand Up @@ -1219,7 +1219,9 @@ namespace letin
ThreadContext::ThreadContext(const VirtualMachineContext &context, size_t stack_size, size_t expr_stack_size) :
_M_funs(context.funs()), _M_fun_count(context.fun_count()),
_M_global_vars(context.vars()), _M_global_var_count(context.var_count()),
_M_fun_infos(context.fun_infos())
_M_fun_infos(context.fun_infos()),
_M_fun_symbols(context.fun_symbols()),
_M_native_fun_symbols(context.native_fun_symbols())
{
_M_gc = nullptr;
_M_native_fun_handler = nullptr;
Expand Down Expand Up @@ -1343,9 +1345,46 @@ namespace letin
atomic_thread_fence(memory_order_release);
}

void ThreadContext::set_error_without_try(int error, const Reference &r)
void ThreadContext::add_stack_trace_elems(std::size_t new_stack_elem_count, bool is_new_stack_trace)
{
try {
if(is_new_stack_trace) {
_M_stack_trace = unique_ptr<vector<StackTraceElement>>(new vector<StackTraceElement>());
} else {
if(_M_stack_trace.get() == nullptr)
_M_stack_trace = unique_ptr<vector<StackTraceElement>>(new vector<StackTraceElement>());
}
size_t fun = _M_regs.fp;
uint32_t instr = _M_regs.ip - 1;
uint32_t abp = _M_regs.abp;
uint32_t ac = _M_regs.ac;
while(true) {
_M_stack_trace->push_back(StackTraceElement(fun, fun_symbol(fun), instr));
if(abp + ac <= new_stack_elem_count) break;
uint32_t fbp = abp + ac;
if(fbp + 4 < _M_stack_size &&
_M_stack[fbp + 0].type() == VALUE_TYPE_PAIR &&
_M_stack[fbp + 1].type() == VALUE_TYPE_PAIR &&
_M_stack[fbp + 2].type() == VALUE_TYPE_INT &&
_M_stack[fbp + 3].type() == VALUE_TYPE_PAIR) {
abp = _M_stack[fbp + 0].raw().p.first;
ac = _M_stack[fbp + 0].raw().p.second;
instr = _M_stack[fbp + 1].raw().p.second;
fun = static_cast<size_t>(_M_stack[fbp + 2].raw().i >> 8);
} else {
_M_stack_trace = nullptr;
return;
}
}
} catch(bad_alloc &) {
_M_stack_trace = nullptr;
}
}

void ThreadContext::set_error_without_try(int error, const Reference &r, bool is_new_stack_trace)
{
try_lock_and_unlock_lazy_values(_M_regs.nfbp);
add_stack_trace_elems(_M_regs.nfbp, is_new_stack_trace);
_M_regs.abp = _M_regs.abp2 = _M_regs.sec = _M_regs.nfbp;
_M_regs.esec = _M_regs.evbp = _M_regs.enfbp;
_M_regs.ac = _M_regs.lvc = _M_regs.ac2 = _M_regs.evc = 0;
Expand All @@ -1357,12 +1396,13 @@ namespace letin
atomic_thread_fence(memory_order_release);
}

void ThreadContext::set_error(int error, const Reference &r)
void ThreadContext::set_error(int error, const Reference &r, bool is_new_stack_trace)
{
if(!_M_regs.try_flag || _M_regs.try_abp < _M_regs.nfbp) {
set_error_without_try(error, r);
} else {
try_lock_and_unlock_lazy_values(_M_regs.try_abp + _M_regs.try_ac);
add_stack_trace_elems(_M_regs.try_abp + _M_regs.try_ac, is_new_stack_trace);
_M_regs.abp = _M_regs.try_abp;
_M_regs.ac = _M_regs.try_ac;
_M_regs.rv = ReturnValue(0, 0.0, r, error);
Expand All @@ -1371,6 +1411,19 @@ namespace letin
}
}

bool ThreadContext::add_stack_trace_elem_for_native_fun(int native_fun)
{
try {
if(_M_stack_trace.get() == nullptr)
_M_stack_trace = unique_ptr<vector<StackTraceElement>>(new vector<StackTraceElement>());
_M_stack_trace->push_back(StackTraceElement(native_fun, native_fun_symbol(native_fun)));
return true;
} catch(bad_alloc &) {
_M_stack_trace = nullptr;
return false;
}
}

void ThreadContext::traverse_root_objects(function<void (Object *)> fun)
{
for(size_t i = 0; i < _M_regs.sec; i++) {
Expand Down
48 changes: 45 additions & 3 deletions vm/vm.hpp
Expand Up @@ -11,6 +11,7 @@
#include <cstddef>
#include <cstdint>
#include <functional>
#include <map>
#include <set>
#include <thread>
#include <unordered_map>
Expand Down Expand Up @@ -198,10 +199,14 @@ namespace letin
const Value *_M_global_vars;
std::size_t _M_global_var_count;
const FunctionInfo *_M_fun_infos;
const std::map<std::size_t, std::string> &_M_fun_symbols;
const std::map<int, std::string> &_M_native_fun_symbols;
RegisteredReference *_M_first_registered_r;
RegisteredReference *_M_last_registered_r;
std::mutex _M_interruptible_fun_mutex;
bool _M_interruptible_fun_flag;
std::unique_ptr<std::vector<StackTraceElement>> _M_stack_trace;
std::unique_ptr<std::vector<StackTraceElement>> _M_try_catch_stack_trace;
public:
ThreadContext(const VirtualMachineContext &vm_context, std::size_t stack_size = 32 * 1024, std::size_t expr_stack_size = 16 * 1024);

Expand Down Expand Up @@ -246,6 +251,20 @@ namespace letin

const FunctionInfo &fun_info(std::size_t i) const { return _M_fun_infos[i]; }

std::string *fun_symbol(std::size_t i) const
{
auto iter = _M_fun_symbols.find(i);
if(iter == _M_fun_symbols.end()) return nullptr;
return new std::string(iter->second);
}

std::string *native_fun_symbol(int i) const
{
auto iter = _M_native_fun_symbols.find(i);
if(iter == _M_native_fun_symbols.end()) return nullptr;
return new std::string(iter->second);
}

const RegisteredReference *first_registered_r() const { return _M_first_registered_r; }

RegisteredReference *&first_registered_r() { return _M_first_registered_r; }
Expand Down Expand Up @@ -311,11 +330,13 @@ namespace letin

ReturnValue invoke_native_fun(VirtualMachine *vm, int nfi, ArgumentList &args);
private:
void try_lock_and_unlock_lazy_values(std::size_t stack_elem_count);
void try_lock_and_unlock_lazy_values(std::size_t new_stack_elem_count);

void set_error_without_try(int error, const Reference &r);
void add_stack_trace_elems(std::size_t new_stack_elem_count, bool is_new_stack_trace);

void set_error_without_try(int error, const Reference &r, bool is_new_stack_trace = true);
public:
void set_error(int error, const Reference &r = Reference());
void set_error(int error, const Reference &r = Reference(), bool is_new_stack_trace = true);

ArgumentList pushed_args() const { return ArgumentList(_M_stack + _M_regs.abp2, _M_regs.ac2); }

Expand Down Expand Up @@ -525,6 +546,8 @@ namespace letin
return false;
}

bool add_stack_trace_elem_for_native_fun(int native_fun);

void traverse_root_objects(std::function<void (Object *)> fun);

void safely_set_gc_tmp_ptr_for_gc(void *ptr)
Expand Down Expand Up @@ -554,6 +577,21 @@ namespace letin

bool &interruptible_fun_flag() { return _M_interruptible_fun_flag; }

const std::vector<StackTraceElement> *stack_trace() const
{ return _M_stack_trace.get(); }

const std::vector<StackTraceElement> *try_catch_stack_trace() const
{ return _M_try_catch_stack_trace.get(); }

void move_stack_trace_to_try_catch_stack_trace()
{ _M_try_catch_stack_trace = std::move(_M_stack_trace); }

void move_try_catch_stack_trace_to_stack_trace()
{ _M_stack_trace = std::move(_M_try_catch_stack_trace); }

void reset_try_catch_stack_trace()
{ _M_try_catch_stack_trace = nullptr; }

void free_stack()
{
if(_M_stack != nullptr) {
Expand Down Expand Up @@ -586,6 +624,10 @@ namespace letin

virtual FunctionInfo *fun_infos() = 0;

virtual const std::map<std::size_t, std::string> &fun_symbols() const = 0;

virtual const std::map<int, std::string> &native_fun_symbols() const = 0;

virtual const std::list<MemoizationCache *> &memo_caches() const = 0;

void traverse_root_objects(std::function<void (Object *)> fun);
Expand Down

0 comments on commit d353b73

Please sign in to comment.