Commit acc52c0e authored by Michael Arntzenius's avatar Michael Arntzenius

implement custom C++ unwinder

parent 1bac7510
# Using exceptions safely in Pyston
In addition to following general best practices for writing exception-safe C++, when writing Pyston there are a few special rules (because it has a custom unwinder):
1. **Only throw `ExcInfo` values.** All Pyston exceptions are of type `ExcInfo`, which represents a Python exception. In fact, usually you should never `throw`; instead, call `raiseRaw`, `raiseExc`, `raise3`, or similar.
2. **Always catch by value.** That is, always write:
```c++
try { ... } catch (ExcInfo e) { ... } // Do this!
```
And **never** write:
```c++
try { ... } catch (ExcInfo& e) { ... } // DO NOT DO THIS!
```
The reason for this has to do with the way exceptions are stored in thread-local storage in Pyston; see `docs/UNWINDING.md` for the gory details.
3. **Never rethrow with bare `throw;`.** Instead, write `throw e;`, where `e` is the exception you caught previously.
4. **Never invoke the GC from a destructor.** The GC is not currently aware of the place the exception-currently-being-unwound is stored. Invoking the GC from a destructor might collect the exception, producing a use-after-free bug!
5. **Never throw an exception inside a destructor.** This is a general rule in C++ anyways, but worth reiterating here. In fact, don't even invoke code that *throws an exception but handles it*! This, again, has to do with the way exceptions are stored.
6. **Don't throw exceptions inside signal handlers.** It should be okay if you throw an exception and *always* catch it inside the handler, but I haven't tested this. In theory the exception should just unwind through the signal frame, and libunwind will take care of resetting the signal mask. However, as this codepath hasn't been tested, it's best avoided.
Most of these restrictions could be eliminated in principle. See `docs/UNWINDING.md` for the gory details.
This diff is collapsed.
......@@ -84,6 +84,7 @@ add_library(PYSTON_OBJECTS OBJECT ${OPTIONAL_SRCS}
runtime/code.cpp
runtime/complex.cpp
runtime/ctxswitching.S
runtime/cxx_unwind.cpp
runtime/descr.cpp
runtime/dict.cpp
runtime/file.cpp
......
......@@ -213,7 +213,7 @@ extern "C" Box* next(Box* iterator, Box* _default) {
} catch (ExcInfo e) {
if (_default && e.matches(StopIteration))
return _default;
throw;
throw e;
}
}
......
This diff is collapsed.
......@@ -42,7 +42,7 @@ static void propertyDocCopy(BoxedProperty* prop, Box* fget) {
get_doc = getattrInternal(fget, "__doc__", NULL);
} catch (ExcInfo e) {
if (!e.matches(Exception)) {
throw;
throw e;
}
get_doc = NULL;
}
......
......@@ -4968,7 +4968,7 @@ extern "C" Box* boxedLocalsGet(Box* boxedLocals, const char* attr, Box* globals)
// If it throws a KeyError, then the variable doesn't exist so move on
// and check the globals (below); otherwise, just propogate the exception.
if (!isSubclass(e.value->cls, KeyError)) {
throw;
throw e;
}
}
}
......
......@@ -45,55 +45,6 @@ void showBacktrace() {
}
}
// Currently-unused libunwind-based unwinding:
void unwindExc(Box* exc_obj) __attribute__((noreturn));
void unwindExc(Box* exc_obj) {
unw_cursor_t cursor;
unw_context_t uc;
unw_word_t ip, sp;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
int code;
unw_proc_info_t pip;
while (unw_step(&cursor) > 0) {
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
printf("ip = %lx, sp = %lx\n", (long)ip, (long)sp);
code = unw_get_proc_info(&cursor, &pip);
RELEASE_ASSERT(code == 0, "");
// printf("%lx %lx %lx %lx %lx %lx %d %d %p\n", pip.start_ip, pip.end_ip, pip.lsda, pip.handler, pip.gp,
// pip.flags, pip.format, pip.unwind_info_size, pip.unwind_info);
assert((pip.lsda == 0) == (pip.handler == 0));
assert(pip.flags == 0);
if (pip.handler == 0) {
if (VERBOSITY())
printf("Skipping frame without handler\n");
continue;
}
printf("%lx %lx %lx\n", pip.lsda, pip.handler, pip.flags);
// assert(pip.handler == (uintptr_t)__gxx_personality_v0 || pip.handler == (uintptr_t)__py_personality_v0);
// auto handler_fn = (int (*)(int, int, uint64_t, void*, void*))pip.handler;
////handler_fn(1, 1 /* _UA_SEARCH_PHASE */, 0 /* exc_class */, NULL, NULL);
// handler_fn(2, 2 /* _UA_SEARCH_PHASE */, 0 /* exc_class */, NULL, NULL);
unw_set_reg(&cursor, UNW_REG_IP, 1);
// TODO testing:
// unw_resume(&cursor);
}
abort();
}
void raiseRaw(const ExcInfo& e) __attribute__((__noreturn__));
void raiseRaw(const ExcInfo& e) {
STAT_TIMER(t0, "us_timer_raiseraw");
......@@ -105,11 +56,17 @@ void raiseRaw(const ExcInfo& e) {
assert(gc::isValidGCObject(e.value));
assert(gc::isValidGCObject(e.traceback));
// Using libgcc:
throw e;
if (VERBOSITY("stacktrace")) {
try {
std::string st = str(e.type)->s.str();
std::string sv = str(e.value)->s.str();
printf("---- raiseRaw() called with %s: %s\n", st.c_str(), sv.c_str());
} catch (ExcInfo e) {
printf("---- raiseRaw() called and WTFed\n");
}
}
// Using libunwind
// unwindExc(exc_obj);
throw e;
}
void raiseExc(Box* exc_obj) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment