report bug with machine regisiters

* error.c (rb_bug_context): new function to report bug with
  context.
* vm_dump.c (rb_vm_bugreport): accepts `ucontext_t` argument to
  dump machine regisiters.  based on [GH-584].
* signal.c (sigbus, sigsegv): dump machine regisiters if available.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@46106 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nobu 2014-05-25 03:46:55 +00:00
parent c1b5b93b3f
commit 82f4c4d4d7
6 changed files with 238 additions and 42 deletions

View File

@ -1,3 +1,13 @@
Sun May 25 12:46:47 2014 Nobuyoshi Nakada <nobu@ruby-lang.org>
* error.c (rb_bug_context): new function to report bug with
context.
* vm_dump.c (rb_vm_bugreport): accepts `ucontext_t` argument to
dump machine regisiters. based on [GH-584].
* signal.c (sigbus, sigsegv): dump machine regisiters if available.
Sun May 25 12:32:42 2014 Tanaka Akira <akr@fsij.org>
* test/lib/minitest/unit.rb: Sort leaked threads and tempfiles.

118
error.c
View File

@ -292,41 +292,78 @@ rb_bug_reporter_add(void (*func)(FILE *, void *), void *data)
return 1;
}
static void
report_bug(const char *file, int line, const char *fmt, va_list args)
/* SIGSEGV handler might have a very small stack. Thus we need to use it carefully. */
#define REPORT_BUG_BUFSIZ 256
static FILE *
bug_report_file(const char *file, int line)
{
/* SIGSEGV handler might have a very small stack. Thus we need to use it carefully. */
char buf[256];
char buf[REPORT_BUG_BUFSIZ];
FILE *out = stderr;
int len = err_position_0(buf, 256, file, line);
int len = err_position_0(buf, sizeof(buf), file, line);
if ((ssize_t)fwrite(buf, 1, len, out) == (ssize_t)len ||
(ssize_t)fwrite(buf, 1, len, (out = stdout)) == (ssize_t)len) {
fputs("[BUG] ", out);
vsnprintf(buf, 256, fmt, args);
fputs(buf, out);
snprintf(buf, 256, "\n%s\n\n", ruby_description);
fputs(buf, out);
rb_vm_bugreport();
/* call additional bug reporters */
{
int i;
for (i=0; i<bug_reporters_size; i++) {
struct bug_reporters *reporter = &bug_reporters[i];
(*reporter->func)(out, reporter->data);
}
}
fprintf(out, REPORTBUG_MSG);
return out;
}
return NULL;
}
static void
bug_report_begin(FILE *out, const char *fmt, va_list args)
{
char buf[REPORT_BUG_BUFSIZ];
fputs("[BUG] ", out);
vsnprintf(buf, sizeof(buf), fmt, args);
fputs(buf, out);
snprintf(buf, sizeof(buf), "\n%s\n\n", ruby_description);
fputs(buf, out);
}
#define bug_report_begin(out, fmt) do { \
va_list args; \
va_start(args, fmt); \
bug_report_begin(out, fmt, args); \
va_end(args); \
} while (0)
static void
bug_report_end(FILE *out)
{
/* call additional bug reporters */
{
int i;
for (i=0; i<bug_reporters_size; i++) {
struct bug_reporters *reporter = &bug_reporters[i];
(*reporter->func)(out, reporter->data);
}
}
fprintf(out, REPORTBUG_MSG);
}
#define report_bug(file, line, fmt, ctx) do { \
FILE *out = bug_report_file(file, line); \
if (out) { \
bug_report_begin(out, fmt); \
rb_vm_bugreport(ctx); \
bug_report_end(out); \
} \
} while (0) \
NORETURN(static void die(void));
static void
die(void)
{
#if defined(_WIN32) && defined(RUBY_MSVCRT_VERSION) && RUBY_MSVCRT_VERSION >= 80
_set_abort_behavior( 0, _CALL_REPORTFAULT);
#endif
abort();
}
void
rb_bug(const char *fmt, ...)
{
va_list args;
const char *file = NULL;
int line = 0;
@ -335,17 +372,28 @@ rb_bug(const char *fmt, ...)
line = rb_sourceline();
}
va_start(args, fmt);
report_bug(file, line, fmt, args);
va_end(args);
report_bug(file, line, fmt, NULL);
#if defined(_WIN32) && defined(RUBY_MSVCRT_VERSION) && RUBY_MSVCRT_VERSION >= 80
_set_abort_behavior( 0, _CALL_REPORTFAULT);
#endif
abort();
die();
}
void
rb_bug_context(const void *ctx, const char *fmt, ...)
{
const char *file = NULL;
int line = 0;
if (GET_THREAD()) {
file = rb_sourcefile();
line = rb_sourceline();
}
report_bug(file, line, fmt, ctx);
die();
}
void
rb_bug_errno(const char *mesg, int errno_arg)
{
@ -394,11 +442,7 @@ rb_async_bug_errno(const char *mesg, int errno_arg)
void
rb_compile_bug(const char *file, int line, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
report_bug(file, line, fmt, args);
va_end(args);
report_bug(file, line, fmt, NULL);
abort();
}

View File

@ -507,9 +507,11 @@ typedef RETSIGTYPE (*sighandler_t)(int);
#ifdef USE_SIGALTSTACK
typedef void ruby_sigaction_t(int, siginfo_t*, void*);
#define SIGINFO_ARG , siginfo_t *info, void *ctx
#define SIGINFO_CTX ctx
#else
typedef RETSIGTYPE ruby_sigaction_t(int);
#define SIGINFO_ARG
#define SIGINFO_CTX 0
#endif
#ifdef USE_SIGALTSTACK
@ -776,7 +778,7 @@ sigbus(int sig SIGINFO_ARG)
#if defined __APPLE__
CHECK_STACK_OVERFLOW();
#endif
rb_bug("Bus Error" MESSAGE_FAULT_ADDRESS);
rb_bug_context(SIGINFO_CTX, "Bus Error" MESSAGE_FAULT_ADDRESS);
}
#endif
@ -813,7 +815,7 @@ sigsegv(int sig SIGINFO_ARG)
segv_received = 1;
ruby_disable_gc_stress = 1;
rb_bug("Segmentation fault" MESSAGE_FAULT_ADDRESS);
rb_bug_context(SIGINFO_CTX, "Segmentation fault" MESSAGE_FAULT_ADDRESS);
}
#endif

2
vm.c
View File

@ -2376,7 +2376,7 @@ extern VALUE *rb_gc_register_stack_start;
static VALUE
sdr(void)
{
rb_vm_bugreport();
rb_vm_bugreport(NULL);
return Qnil;
}

View File

@ -836,7 +836,8 @@ extern void rb_vmdebug_debug_print_post(rb_thread_t *th, rb_control_frame_t *cfp
#define SDR() rb_vmdebug_stack_dump_raw(GET_THREAD(), GET_THREAD()->cfp)
#define SDR2(cfp) rb_vmdebug_stack_dump_raw(GET_THREAD(), (cfp))
void rb_vm_bugreport(void);
void rb_vm_bugreport(const void *);
NORETURN(void rb_bug_context(const void *, const char *fmt, ...));
/* functions about thread/vm execution */
RUBY_SYMBOL_EXPORT_BEGIN

141
vm_dump.c
View File

@ -789,8 +789,145 @@ procstat_vm(struct procstat *procstat, struct kinfo_proc *kipp)
}
#endif
#if defined __linux__
# if defined __x86_64__ || defined __i386__
# define HAVE_PRINT_MACHINE_REGISTERS 1
# endif
#elif defined __APPLE__
# if defined __x86_64__ || defined __i386__
# define HAVE_PRINT_MACHINE_REGISTERS 1
# endif
#endif
#ifdef HAVE_PRINT_MACHINE_REGISTERS
static int
print_machine_register(size_t reg, const char *reg_name, int col_count, int max_col)
{
int ret;
char buf[64];
#ifdef __LP64__
ret = snprintf(buf, sizeof(buf), " %3.3s: 0x%016zx", reg_name, reg);
#else
ret = snprintf(buf, sizeof(buf), " %3.3s: 0x%08zx", reg_name, reg);
#endif
if (col_count + ret > max_col) {
fputs("\n", stderr);
col_count = 0;
}
col_count += ret;
fputs(buf, stderr);
return col_count;
}
# ifdef __linux__
# define dump_machine_register(reg) (col_count = print_machine_register(mctx->gregs[REG_##reg], #reg, col_count, 80))
# elif defined __APPLE__
# define dump_machine_register(reg) (col_count = print_machine_register(mctx->__ss.__##reg, #reg, col_count, 80))
# endif
static void
rb_dump_machine_register(const ucontext_t *ctx)
{
int col_count = 0;
if (!ctx) return;
fprintf(stderr, "-- Machine register context "
"------------------------------------------------\n");
# if defined __linux__
{
const mcontext_t *const mctx = &ctx->uc_mcontext;
# if defined __x86_64__
dump_machine_register(RIP);
dump_machine_register(RBP);
dump_machine_register(RSP);
dump_machine_register(RAX);
dump_machine_register(RBX);
dump_machine_register(RCX);
dump_machine_register(RDX);
dump_machine_register(RDI);
dump_machine_register(RSI);
dump_machine_register(R8);
dump_machine_register(R9);
dump_machine_register(R10);
dump_machine_register(R11);
dump_machine_register(R12);
dump_machine_register(R13);
dump_machine_register(R14);
dump_machine_register(R15);
dump_machine_register(EFL);
# elif defined __i386__
dump_machine_register(GS);
dump_machine_register(FS);
dump_machine_register(ES);
dump_machine_register(DS);
dump_machine_register(EDI);
dump_machine_register(ESI);
dump_machine_register(EBP);
dump_machine_register(ESP);
dump_machine_register(EBX);
dump_machine_register(EDX);
dump_machine_register(ECX);
dump_machine_register(EAX);
dump_machine_register(TRAPNO);
dump_machine_register(ERR);
dump_machine_register(EIP);
dump_machine_register(CS);
dump_machine_register(EFL);
dump_machine_register(UESP);
dump_machine_register(SS);
# endif
}
# elif defined __APPLE__
{
const mcontext_t mctx = ctx->uc_mcontext;
# if defined __x86_64__
dump_machine_register(rax);
dump_machine_register(rbx);
dump_machine_register(rcx);
dump_machine_register(rdx);
dump_machine_register(rdi);
dump_machine_register(rsi);
dump_machine_register(rbp);
dump_machine_register(rsp);
dump_machine_register(r8);
dump_machine_register(r9);
dump_machine_register(r10);
dump_machine_register(r11);
dump_machine_register(r12);
dump_machine_register(r13);
dump_machine_register(r14);
dump_machine_register(r15);
dump_machine_register(rip);
dump_machine_register(rflags);
# elif defined __i386__
dump_machine_register(eax);
dump_machine_register(ebx);
dump_machine_register(ecx);
dump_machine_register(edx);
dump_machine_register(edi);
dump_machine_register(esi);
dump_machine_register(ebp);
dump_machine_register(esp);
dump_machine_register(ss);
dump_machine_register(eflags);
dump_machine_register(eip);
dump_machine_register(cs);
dump_machine_register(ds);
dump_machine_register(es);
dump_machine_register(fs);
dump_machine_register(gs);
# endif
}
# endif
fprintf(stderr, "\n\n");
}
#else
# define rb_dump_machine_register(ctx) ((void)0)
#endif /* HAVE_PRINT_MACHINE_REGISTERS */
void
rb_vm_bugreport(void)
rb_vm_bugreport(const void *ctx)
{
#ifdef __linux__
# define PROC_MAPS_NAME "/proc/self/maps"
@ -820,6 +957,8 @@ rb_vm_bugreport(void)
fputs("\n", stderr);
}
rb_dump_machine_register(ctx);
#if HAVE_BACKTRACE || defined(_WIN32)
fprintf(stderr, "-- C level backtrace information "
"-------------------------------------------\n");