diff --git a/mk/rt.mk b/mk/rt.mk index 5e5fd8dc986..f2ed226b266 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -28,6 +28,7 @@ RUNTIME_CS := rt/sync/timer.cpp \ rt/rust_gc.cpp \ rt/rust_abi.cpp \ rt/rust_cc.cpp \ + rt/rust_debug.cpp \ rt/memory_region.cpp \ rt/test/rust_test_harness.cpp \ rt/test/rust_test_runtime.cpp \ diff --git a/src/rt/rust_debug.cpp b/src/rt/rust_debug.cpp new file mode 100644 index 00000000000..697f9f13bbc --- /dev/null +++ b/src/rt/rust_debug.cpp @@ -0,0 +1,70 @@ +// Routines useful when debugging the Rust runtime. + +#include "rust_debug.h" +#include "rust_internal.h" + +#include +#include +#include +#include + +#if defined(__APPLE__) || defined(__linux__) +#define HAVE_BACKTRACE +#include +#elif defined(_WIN32) +#include +#endif + +namespace { + +debug::flag track_origins("RUST_TRACK_ORIGINS"); + +} // end anonymous namespace + +namespace debug { + +std::string +backtrace() { + void *call_stack[256]; + int n_frames = ::backtrace(call_stack, 256); + char **syms = backtrace_symbols(call_stack, n_frames); + + std::stringstream ss; + for (int i = 0; i < n_frames; i++) + ss << syms[i] << std::endl; + + free(syms); + + return ss.str(); +} + +void +maybe_track_origin(rust_task *task, void *ptr) { + if (!*track_origins) + return; + task->debug.origins[ptr] = backtrace(); +} + +void +maybe_untrack_origin(rust_task *task, void *ptr) { + if (!*track_origins) + return; + task->debug.origins.erase(ptr); +} + +// This function is intended to be called by the debugger. +void +dump_origin(rust_task *task, void *ptr) { + if (!*track_origins) { + std::cerr << "Try again with RUST_TRACK_ORIGINS=1." << std::endl; + } else if (task->debug.origins.find(ptr) == task->debug.origins.end()) { + std::cerr << "Pointer " << std::hex << (uintptr_t)ptr << + " does not have a tracked origin."; + } else { + std::cerr << "Origin of pointer " << std::hex << ":" << std::endl << + task->debug.origins[ptr]; + } +} + +} // end namespace debug + diff --git a/src/rt/rust_debug.h b/src/rt/rust_debug.h index f7c444324f7..da9838ba341 100644 --- a/src/rt/rust_debug.h +++ b/src/rt/rust_debug.h @@ -3,8 +3,12 @@ #ifndef RUST_DEBUG_H #define RUST_DEBUG_H +#include +#include #include +struct rust_task; + namespace debug { class flag { @@ -27,6 +31,19 @@ public: } }; +class task_debug_info { +public: + std::map origins; +}; + +std::string backtrace(); + +void maybe_track_origin(rust_task *task, void *ptr); +void maybe_untrack_origin(rust_task *task, void *ptr); + +// This function is intended to be called by the debugger. +void dump_origin(rust_task *task, void *ptr); + } // end namespace debug #endif diff --git a/src/rt/rust_internal.h b/src/rt/rust_internal.h index b53e0bfd2a9..8688cedc857 100644 --- a/src/rt/rust_internal.h +++ b/src/rt/rust_internal.h @@ -67,12 +67,6 @@ struct frame_glue_fns; typedef intptr_t rust_task_id; typedef intptr_t rust_port_id; -// Corresponds to the rust chan (currently _chan) type. -struct chan_handle { - rust_task_id task; - rust_port_id port; -}; - #ifndef __i386__ #error "Target CPU not supported." #endif diff --git a/src/rt/rust_kernel.h b/src/rt/rust_kernel.h index 774f7f4d0a4..8d59dabdcb9 100644 --- a/src/rt/rust_kernel.h +++ b/src/rt/rust_kernel.h @@ -2,6 +2,11 @@ #ifndef RUST_KERNEL_H #define RUST_KERNEL_H +#include "memory_region.h" +#include "rust_log.h" + +struct rust_scheduler; + /** * A global object shared by all thread domains. Most of the data structures * in this class are synchronized since they are accessed from multiple diff --git a/src/rt/rust_shape.h b/src/rt/rust_shape.h index 357786ae2ce..06e8571f589 100644 --- a/src/rt/rust_shape.h +++ b/src/rt/rust_shape.h @@ -16,11 +16,11 @@ #define ARENA_SIZE 256 -//#define DPRINT(fmt,...) fprintf(stderr, fmt, ##__VA_ARGS__) -//#define DPRINTCX(cx) shape::print::print_cx(cx) +#define DPRINT(fmt,...) fprintf(stderr, fmt, ##__VA_ARGS__) +#define DPRINTCX(cx) shape::print::print_cx(cx) -#define DPRINT(fmt,...) -#define DPRINTCX(cx) +//#define DPRINT(fmt,...) +//#define DPRINTCX(cx) namespace shape { @@ -526,6 +526,13 @@ public: const rust_shape_tables *in_tables = NULL) : ctxt(other, in_sp, in_params, in_tables) {} + print(rust_task *in_task, + bool in_align, + const uint8_t *in_sp, + const type_param *in_params, + const rust_shape_tables *in_tables) + : ctxt(in_task, in_align, in_sp, in_params, in_tables) {} + void walk_tag(tag_info &tinfo); void walk_struct(const uint8_t *end_sp); void walk_res(const rust_fn *dtor, unsigned n_params, diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index 047fbd2b58f..76b828af58f 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -10,8 +10,17 @@ #include "util/array_list.h" #include "context.h" +#include "rust_debug.h" +#include "rust_internal.h" +#include "rust_kernel.h" #include "rust_obstack.h" +// Corresponds to the rust chan (currently _chan) type. +struct chan_handle { + rust_task_id task; + rust_port_id port; +}; + struct rust_box; struct stk_seg { @@ -117,6 +126,8 @@ rust_task : public kernel_owned, rust_cond std::map local_allocs; + debug::task_debug_info debug; + // Only a pointer to 'name' is kept, so it must live as long as this task. rust_task(rust_scheduler *sched, rust_task_list *state, diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 758d5622b10..7a96a5ef7cb 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -67,10 +67,13 @@ upcall_malloc(rust_task *task, size_t nbytes, type_desc *td) { // TODO: Maybe use dladdr here to find a more useful name for the // type_desc. + // TODO: Implement RUST_TRACK_ORIGINS + void *p = task->malloc(nbytes, "tdesc", td); memset(p, '\0', nbytes); task->local_allocs[p] = td; + debug::maybe_track_origin(task, p); LOG(task, mem, "upcall malloc(%" PRIdPTR ", 0x%" PRIxPTR ") = 0x%" PRIxPTR, @@ -91,6 +94,8 @@ upcall_free(rust_task *task, void* ptr, uintptr_t is_gc) { (uintptr_t)ptr, is_gc); task->local_allocs.erase(ptr); + debug::maybe_untrack_origin(task, ptr); + task->free(ptr, (bool) is_gc); }