Dump backtraces to an arbitrary stream

This commit is contained in:
Nobuyoshi Nakada 2023-08-01 03:04:42 +09:00
parent acd44902b9
commit ac244938e8
9 changed files with 265 additions and 236 deletions

View File

@ -144,7 +144,7 @@ void *alloca();
#define DW_LNE_define_file 0x03 #define DW_LNE_define_file 0x03
#define DW_LNE_set_discriminator 0x04 /* DWARF4 */ #define DW_LNE_set_discriminator 0x04 /* DWARF4 */
#define kprintf(...) fprintf(stderr, "" __VA_ARGS__) #define kprintf(...) fprintf(errout, "" __VA_ARGS__)
typedef struct line_info { typedef struct line_info {
const char *dirname; const char *dirname;
@ -254,7 +254,7 @@ sleb128(const char **p)
} }
static const char * static const char *
get_nth_dirname(unsigned long dir, const char *p) get_nth_dirname(unsigned long dir, const char *p, FILE *errout)
{ {
if (!dir--) { if (!dir--) {
return ""; return "";
@ -271,10 +271,14 @@ get_nth_dirname(unsigned long dir, const char *p)
return p; return p;
} }
static const char *parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t *obj, const char **out_path, uint64_t *out_directory_index); static const char *parse_ver5_debug_line_header(
const char *p, int idx, uint8_t format,
obj_info_t *obj, const char **out_path,
uint64_t *out_directory_index, FILE *errout);
static void static void
fill_filename(int file, uint8_t format, uint16_t version, const char *include_directories, const char *filenames, line_info_t *line, obj_info_t *obj) fill_filename(int file, uint8_t format, uint16_t version, const char *include_directories,
const char *filenames, line_info_t *line, obj_info_t *obj, FILE *errout)
{ {
int i; int i;
const char *p = filenames; const char *p = filenames;
@ -283,9 +287,9 @@ fill_filename(int file, uint8_t format, uint16_t version, const char *include_di
if (version >= 5) { if (version >= 5) {
const char *path; const char *path;
uint64_t directory_index = -1; uint64_t directory_index = -1;
parse_ver5_debug_line_header(filenames, file, format, obj, &path, &directory_index); parse_ver5_debug_line_header(filenames, file, format, obj, &path, &directory_index, errout);
line->filename = path; line->filename = path;
parse_ver5_debug_line_header(include_directories, (int)directory_index, format, obj, &path, NULL); parse_ver5_debug_line_header(include_directories, (int)directory_index, format, obj, &path, NULL, errout);
line->dirname = path; line->dirname = path;
} }
else { else {
@ -307,7 +311,7 @@ fill_filename(int file, uint8_t format, uint16_t version, const char *include_di
if (i == file) { if (i == file) {
line->filename = filename; line->filename = filename;
line->dirname = get_nth_dirname(dir, include_directories); line->dirname = get_nth_dirname(dir, include_directories, errout);
} }
} }
} }
@ -316,7 +320,7 @@ fill_filename(int file, uint8_t format, uint16_t version, const char *include_di
static void static void
fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line, fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line,
uint8_t format, uint16_t version, const char *include_directories, const char *filenames, uint8_t format, uint16_t version, const char *include_directories, const char *filenames,
obj_info_t *obj, line_info_t *lines, int offset) obj_info_t *obj, line_info_t *lines, int offset, FILE *errout)
{ {
int i; int i;
addr += obj->base_addr - obj->vmaddr; addr += obj->base_addr - obj->vmaddr;
@ -325,7 +329,7 @@ fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line,
/* We assume one line code doesn't result >100 bytes of native code. /* We assume one line code doesn't result >100 bytes of native code.
We may want more reliable way eventually... */ We may want more reliable way eventually... */
if (addr < a && a < addr + 100) { if (addr < a && a < addr + 100) {
fill_filename(file, format, version, include_directories, filenames, &lines[i], obj); fill_filename(file, format, version, include_directories, filenames, &lines[i], obj, errout);
lines[i].line = line; lines[i].line = line;
} }
} }
@ -350,7 +354,7 @@ struct LineNumberProgramHeader {
}; };
static int static int
parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgramHeader *header) parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgramHeader *header, FILE *errout)
{ {
const char *p = *pp; const char *p = *pp;
header->unit_length = *(uint32_t *)p; header->unit_length = *(uint32_t *)p;
@ -396,7 +400,7 @@ parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgr
if (header->version >= 5) { if (header->version >= 5) {
header->include_directories = p; header->include_directories = p;
p = parse_ver5_debug_line_header(p, -1, header->format, obj, NULL, NULL); p = parse_ver5_debug_line_header(p, -1, header->format, obj, NULL, NULL, errout);
header->filenames = p; header->filenames = p;
} }
else { else {
@ -423,7 +427,7 @@ parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgr
static int static int
parse_debug_line_cu(int num_traces, void **traces, const char **debug_line, parse_debug_line_cu(int num_traces, void **traces, const char **debug_line,
obj_info_t *obj, line_info_t *lines, int offset) obj_info_t *obj, line_info_t *lines, int offset, FILE *errout)
{ {
const char *p = (const char *)*debug_line; const char *p = (const char *)*debug_line;
struct LineNumberProgramHeader header; struct LineNumberProgramHeader header;
@ -440,7 +444,7 @@ parse_debug_line_cu(int num_traces, void **traces, const char **debug_line,
/* int epilogue_begin = 0; */ /* int epilogue_begin = 0; */
/* unsigned int isa = 0; */ /* unsigned int isa = 0; */
if (parse_debug_line_header(obj, &p, &header)) if (parse_debug_line_header(obj, &p, &header, errout))
return -1; return -1;
is_stmt = header.default_is_stmt; is_stmt = header.default_is_stmt;
@ -451,7 +455,7 @@ parse_debug_line_cu(int num_traces, void **traces, const char **debug_line,
header.version, \ header.version, \
header.include_directories, \ header.include_directories, \
header.filenames, \ header.filenames, \
obj, lines, offset); \ obj, lines, offset, errout); \
/*basic_block = prologue_end = epilogue_begin = 0;*/ \ /*basic_block = prologue_end = epilogue_begin = 0;*/ \
} while (0) } while (0)
@ -551,11 +555,11 @@ parse_debug_line_cu(int num_traces, void **traces, const char **debug_line,
static int static int
parse_debug_line(int num_traces, void **traces, parse_debug_line(int num_traces, void **traces,
const char *debug_line, unsigned long size, const char *debug_line, unsigned long size,
obj_info_t *obj, line_info_t *lines, int offset) obj_info_t *obj, line_info_t *lines, int offset, FILE *errout)
{ {
const char *debug_line_end = debug_line + size; const char *debug_line_end = debug_line + size;
while (debug_line < debug_line_end) { while (debug_line < debug_line_end) {
if (parse_debug_line_cu(num_traces, traces, &debug_line, obj, lines, offset)) if (parse_debug_line_cu(num_traces, traces, &debug_line, obj, lines, offset, errout))
return -1; return -1;
} }
if (debug_line != debug_line_end) { if (debug_line != debug_line_end) {
@ -568,7 +572,7 @@ parse_debug_line(int num_traces, void **traces,
/* read file and fill lines */ /* read file and fill lines */
static uintptr_t static uintptr_t
fill_lines(int num_traces, void **traces, int check_debuglink, fill_lines(int num_traces, void **traces, int check_debuglink,
obj_info_t **objp, line_info_t *lines, int offset); obj_info_t **objp, line_info_t *lines, int offset, FILE *errout);
static void static void
append_obj(obj_info_t **objp) append_obj(obj_info_t **objp)
@ -596,7 +600,7 @@ append_obj(obj_info_t **objp)
// check the path pattern of "/usr/lib/debug/usr/bin/ruby.debug" // check the path pattern of "/usr/lib/debug/usr/bin/ruby.debug"
static void static void
follow_debuglink(const char *debuglink, int num_traces, void **traces, follow_debuglink(const char *debuglink, int num_traces, void **traces,
obj_info_t **objp, line_info_t *lines, int offset) obj_info_t **objp, line_info_t *lines, int offset, FILE *errout)
{ {
static const char global_debug_dir[] = "/usr/lib/debug"; static const char global_debug_dir[] = "/usr/lib/debug";
const size_t global_debug_dir_len = sizeof(global_debug_dir) - 1; const size_t global_debug_dir_len = sizeof(global_debug_dir) - 1;
@ -622,13 +626,13 @@ follow_debuglink(const char *debuglink, int num_traces, void **traces,
o2 = *objp; o2 = *objp;
o2->base_addr = o1->base_addr; o2->base_addr = o1->base_addr;
o2->path = o1->path; o2->path = o1->path;
fill_lines(num_traces, traces, 0, objp, lines, offset); fill_lines(num_traces, traces, 0, objp, lines, offset, errout);
} }
// check the path pattern of "/usr/lib/debug/.build-id/ab/cdef1234.debug" // check the path pattern of "/usr/lib/debug/.build-id/ab/cdef1234.debug"
static void static void
follow_debuglink_build_id(const char *build_id, size_t build_id_size, int num_traces, void **traces, follow_debuglink_build_id(const char *build_id, size_t build_id_size, int num_traces, void **traces,
obj_info_t **objp, line_info_t *lines, int offset) obj_info_t **objp, line_info_t *lines, int offset, FILE *errout)
{ {
static const char global_debug_dir[] = "/usr/lib/debug/.build-id/"; static const char global_debug_dir[] = "/usr/lib/debug/.build-id/";
const size_t global_debug_dir_len = sizeof(global_debug_dir) - 1; const size_t global_debug_dir_len = sizeof(global_debug_dir) - 1;
@ -653,7 +657,7 @@ follow_debuglink_build_id(const char *build_id, size_t build_id_size, int num_tr
o2 = *objp; o2 = *objp;
o2->base_addr = o1->base_addr; o2->base_addr = o1->base_addr;
o2->path = o1->path; o2->path = o1->path;
fill_lines(num_traces, traces, 0, objp, lines, offset); fill_lines(num_traces, traces, 0, objp, lines, offset, errout);
} }
#endif #endif
@ -1079,13 +1083,13 @@ di_read_debug_abbrev_cu(DebugInfoReader *reader)
} }
static int static int
di_read_debug_line_cu(DebugInfoReader *reader) di_read_debug_line_cu(DebugInfoReader *reader, FILE *errout)
{ {
const char *p; const char *p;
struct LineNumberProgramHeader header; struct LineNumberProgramHeader header;
p = (const char *)reader->debug_line_cu_end; p = (const char *)reader->debug_line_cu_end;
if (parse_debug_line_header(reader->obj, &p, &header)) if (parse_debug_line_header(reader->obj, &p, &header, errout))
return -1; return -1;
reader->debug_line_cu_end = (char *)header.cu_end; reader->debug_line_cu_end = (char *)header.cu_end;
@ -1186,7 +1190,7 @@ debug_info_reader_read_addr_value_member(DebugInfoReader *reader, DebugInfoValue
static bool static bool
debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoValue *v) debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoValue *v, FILE *errout)
{ {
switch (form) { switch (form) {
case DW_FORM_addr: case DW_FORM_addr:
@ -1369,7 +1373,7 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa
/* find abbrev in current compilation unit */ /* find abbrev in current compilation unit */
static const char * static const char *
di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number) di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number, FILE *errout)
{ {
const char *p; const char *p;
if (abbrev_number < ABBREV_TABLE_SIZE) { if (abbrev_number < ABBREV_TABLE_SIZE) {
@ -1394,7 +1398,7 @@ di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number)
#if 0 #if 0
static void static void
hexdump0(const unsigned char *p, size_t n) hexdump0(const unsigned char *p, size_t n, FILE *errout)
{ {
size_t i; size_t i;
kprintf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); kprintf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
@ -1415,10 +1419,10 @@ hexdump0(const unsigned char *p, size_t n)
kprintf("\n"); kprintf("\n");
} }
} }
#define hexdump(p,n) hexdump0((const unsigned char *)p, n) #define hexdump(p,n,e) hexdump0((const unsigned char *)p, n, e)
static void static void
div_inspect(DebugInfoValue *v) div_inspect(DebugInfoValue *v, FILE *errout)
{ {
switch (v->type) { switch (v->type) {
case VAL_uint: case VAL_uint:
@ -1432,14 +1436,14 @@ div_inspect(DebugInfoValue *v)
break; break;
case VAL_data: case VAL_data:
kprintf("%d: type:%d size:%" PRIxSIZE " v:\n",__LINE__,v->type,v->size); kprintf("%d: type:%d size:%" PRIxSIZE " v:\n",__LINE__,v->type,v->size);
hexdump(v->as.ptr, 16); hexdump(v->as.ptr, 16, errout);
break; break;
} }
} }
#endif #endif
static DIE * static DIE *
di_read_die(DebugInfoReader *reader, DIE *die) di_read_die(DebugInfoReader *reader, DIE *die, FILE *errout)
{ {
uint64_t abbrev_number = uleb128(&reader->p); uint64_t abbrev_number = uleb128(&reader->p);
if (abbrev_number == 0) { if (abbrev_number == 0) {
@ -1447,7 +1451,7 @@ di_read_die(DebugInfoReader *reader, DIE *die)
return NULL; return NULL;
} }
if (!(reader->q = di_find_abbrev(reader, abbrev_number))) return NULL; if (!(reader->q = di_find_abbrev(reader, abbrev_number, errout))) return NULL;
die->pos = reader->p - reader->obj->debug_info.ptr - 1; die->pos = reader->p - reader->obj->debug_info.ptr - 1;
die->tag = (int)uleb128(&reader->q); /* tag */ die->tag = (int)uleb128(&reader->q); /* tag */
@ -1459,26 +1463,26 @@ di_read_die(DebugInfoReader *reader, DIE *die)
} }
static DebugInfoValue * static DebugInfoValue *
di_read_record(DebugInfoReader *reader, DebugInfoValue *vp) di_read_record(DebugInfoReader *reader, DebugInfoValue *vp, FILE *errout)
{ {
uint64_t at = uleb128(&reader->q); uint64_t at = uleb128(&reader->q);
uint64_t form = uleb128(&reader->q); uint64_t form = uleb128(&reader->q);
if (!at || !form) return NULL; if (!at || !form) return NULL;
vp->at = at; vp->at = at;
vp->form = form; vp->form = form;
if (!debug_info_reader_read_value(reader, form, vp)) return NULL; if (!debug_info_reader_read_value(reader, form, vp, errout)) return NULL;
return vp; return vp;
} }
static bool static bool
di_skip_records(DebugInfoReader *reader) di_skip_records(DebugInfoReader *reader, FILE *errout)
{ {
for (;;) { for (;;) {
DebugInfoValue v = {{0}}; DebugInfoValue v = {{0}};
uint64_t at = uleb128(&reader->q); uint64_t at = uleb128(&reader->q);
uint64_t form = uleb128(&reader->q); uint64_t form = uleb128(&reader->q);
if (!at || !form) return true; if (!at || !form) return true;
if (!debug_info_reader_read_value(reader, form, &v)) return false; if (!debug_info_reader_read_value(reader, form, &v, errout)) return false;
} }
} }
@ -1491,7 +1495,8 @@ typedef struct addr_header {
} addr_header_t; } addr_header_t;
static bool static bool
addr_header_init(obj_info_t *obj, addr_header_t *header) { addr_header_init(obj_info_t *obj, addr_header_t *header, FILE *errout)
{
const char *p = obj->debug_addr.ptr; const char *p = obj->debug_addr.ptr;
header->ptr = p; header->ptr = p;
@ -1536,7 +1541,8 @@ typedef struct rnglists_header {
} rnglists_header_t; } rnglists_header_t;
static bool static bool
rnglists_header_init(obj_info_t *obj, rnglists_header_t *header) { rnglists_header_init(obj_info_t *obj, rnglists_header_t *header, FILE *errout)
{
const char *p = obj->debug_rnglists.ptr; const char *p = obj->debug_rnglists.ptr;
if (!p) return true; if (!p) return true;
@ -1603,7 +1609,7 @@ ranges_set(ranges_t *ptr, DebugInfoValue *v, addr_header_t *addr_header, uint64_
} }
static uint64_t static uint64_t
read_dw_form_addr(DebugInfoReader *reader, const char **ptr) read_dw_form_addr(DebugInfoReader *reader, const char **ptr, FILE *errout)
{ {
const char *p = *ptr; const char *p = *ptr;
*ptr = p + reader->address_size; *ptr = p + reader->address_size;
@ -1615,7 +1621,7 @@ read_dw_form_addr(DebugInfoReader *reader, const char **ptr)
} }
static uintptr_t static uintptr_t
ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_header_t *rnglists_header) ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_header_t *rnglists_header, FILE *errout)
{ {
if (ptr->high_pc_set) { if (ptr->high_pc_set) {
if (ptr->ranges_set || !ptr->low_pc_set) { if (ptr->ranges_set || !ptr->low_pc_set) {
@ -1668,15 +1674,15 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_h
to = (uintptr_t)base + uleb128(&p); to = (uintptr_t)base + uleb128(&p);
break; break;
case DW_RLE_base_address: case DW_RLE_base_address:
base = read_dw_form_addr(reader, &p); base = read_dw_form_addr(reader, &p, errout);
base_valid = true; base_valid = true;
break; break;
case DW_RLE_start_end: case DW_RLE_start_end:
from = (uintptr_t)read_dw_form_addr(reader, &p); from = (uintptr_t)read_dw_form_addr(reader, &p, errout);
to = (uintptr_t)read_dw_form_addr(reader, &p); to = (uintptr_t)read_dw_form_addr(reader, &p, errout);
break; break;
case DW_RLE_start_length: case DW_RLE_start_length:
from = (uintptr_t)read_dw_form_addr(reader, &p); from = (uintptr_t)read_dw_form_addr(reader, &p, errout);
to = from + uleb128(&p); to = from + uleb128(&p);
break; break;
} }
@ -1710,7 +1716,7 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_h
#if 0 #if 0
static void static void
ranges_inspect(DebugInfoReader *reader, ranges_t *ptr) ranges_inspect(DebugInfoReader *reader, ranges_t *ptr, FILE *errout)
{ {
if (ptr->high_pc_set) { if (ptr->high_pc_set) {
if (ptr->ranges_set || !ptr->low_pc_set) { if (ptr->ranges_set || !ptr->low_pc_set) {
@ -1740,7 +1746,7 @@ ranges_inspect(DebugInfoReader *reader, ranges_t *ptr)
#endif #endif
static int static int
di_read_cu(DebugInfoReader *reader) di_read_cu(DebugInfoReader *reader, FILE *errout)
{ {
uint64_t unit_length; uint64_t unit_length;
uint16_t version; uint16_t version;
@ -1775,15 +1781,15 @@ di_read_cu(DebugInfoReader *reader)
reader->level = 0; reader->level = 0;
di_read_debug_abbrev_cu(reader); di_read_debug_abbrev_cu(reader);
if (di_read_debug_line_cu(reader)) return -1; if (di_read_debug_line_cu(reader, errout)) return -1;
do { do {
DIE die; DIE die;
if (!di_read_die(reader, &die)) continue; if (!di_read_die(reader, &die, errout)) continue;
if (die.tag != DW_TAG_compile_unit) { if (die.tag != DW_TAG_compile_unit) {
if (!di_skip_records(reader)) return -1; if (!di_skip_records(reader, errout)) return -1;
break; break;
} }
@ -1795,7 +1801,7 @@ di_read_cu(DebugInfoReader *reader)
/* enumerate abbrev */ /* enumerate abbrev */
for (;;) { for (;;) {
DebugInfoValue v = {{0}}; DebugInfoValue v = {{0}};
if (!di_read_record(reader, &v)) break; if (!di_read_record(reader, &v, errout)) break;
switch (v.at) { switch (v.at) {
case DW_AT_low_pc: case DW_AT_low_pc:
// clang may output DW_AT_addr_base after DW_AT_low_pc. // clang may output DW_AT_addr_base after DW_AT_low_pc.
@ -1821,7 +1827,7 @@ di_read_cu(DebugInfoReader *reader)
case VAL_addr: case VAL_addr:
{ {
addr_header_t header = {0}; addr_header_t header = {0};
if (!addr_header_init(reader->obj, &header)) return -1; if (!addr_header_init(reader->obj, &header, errout)) return -1;
reader->current_low_pc = read_addr(&header, reader->current_addr_base, low_pc.as.addr_idx); reader->current_low_pc = read_addr(&header, reader->current_addr_base, low_pc.as.addr_idx);
} }
break; break;
@ -1832,7 +1838,7 @@ di_read_cu(DebugInfoReader *reader)
} }
static void static void
read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_origin, line_info_t *line) read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_origin, line_info_t *line, FILE *errout)
{ {
const char *p = reader->p; const char *p = reader->p;
const char *q = reader->q; const char *q = reader->q;
@ -1857,12 +1863,12 @@ read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_o
default: default:
goto finish; goto finish;
} }
if (!di_read_die(reader, &die)) goto finish; if (!di_read_die(reader, &die, errout)) goto finish;
/* enumerate abbrev */ /* enumerate abbrev */
for (;;) { for (;;) {
DebugInfoValue v = {{0}}; DebugInfoValue v = {{0}};
if (!di_read_record(reader, &v)) break; if (!di_read_record(reader, &v, errout)) break;
switch (v.at) { switch (v.at) {
case DW_AT_name: case DW_AT_name:
line->sname = get_cstr_value(&v); line->sname = get_cstr_value(&v);
@ -1878,25 +1884,26 @@ read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_o
static bool static bool
debug_info_read(DebugInfoReader *reader, int num_traces, void **traces, debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
line_info_t *lines, int offset) { line_info_t *lines, int offset, FILE *errout)
{
addr_header_t addr_header = {0}; addr_header_t addr_header = {0};
if (!addr_header_init(reader->obj, &addr_header)) return false; if (!addr_header_init(reader->obj, &addr_header, errout)) return false;
rnglists_header_t rnglists_header = {0}; rnglists_header_t rnglists_header = {0};
if (!rnglists_header_init(reader->obj, &rnglists_header)) return false; if (!rnglists_header_init(reader->obj, &rnglists_header, errout)) return false;
while (reader->p < reader->cu_end) { while (reader->p < reader->cu_end) {
DIE die; DIE die;
ranges_t ranges = {0}; ranges_t ranges = {0};
line_info_t line = {0}; line_info_t line = {0};
if (!di_read_die(reader, &die)) continue; if (!di_read_die(reader, &die, errout)) continue;
/* kprintf("%d:%tx: <%d>\n",__LINE__,die.pos,reader->level,die.tag); */ /* kprintf("%d:%tx: <%d>\n",__LINE__,die.pos,reader->level,die.tag); */
if (die.tag != DW_TAG_subprogram && die.tag != DW_TAG_inlined_subroutine) { if (die.tag != DW_TAG_subprogram && die.tag != DW_TAG_inlined_subroutine) {
skip_die: skip_die:
if (!di_skip_records(reader)) return false; if (!di_skip_records(reader, errout)) return false;
continue; continue;
} }
@ -1904,15 +1911,15 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
for (;;) { for (;;) {
DebugInfoValue v = {{0}}; DebugInfoValue v = {{0}};
/* ptrdiff_t pos = reader->p - reader->p0; */ /* ptrdiff_t pos = reader->p - reader->p0; */
if (!di_read_record(reader, &v)) break; if (!di_read_record(reader, &v, errout)) break;
/* kprintf("\n%d:%tx: AT:%lx FORM:%lx\n",__LINE__,pos,v.at,v.form); */ /* kprintf("\n%d:%tx: AT:%lx FORM:%lx\n",__LINE__,pos,v.at,v.form); */
/* div_inspect(&v); */ /* div_inspect(&v, errout); */
switch (v.at) { switch (v.at) {
case DW_AT_name: case DW_AT_name:
line.sname = get_cstr_value(&v); line.sname = get_cstr_value(&v);
break; break;
case DW_AT_call_file: case DW_AT_call_file:
fill_filename((int)v.as.uint64, reader->debug_line_format, reader->debug_line_version, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj); fill_filename((int)v.as.uint64, reader->debug_line_format, reader->debug_line_version, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj, errout);
break; break;
case DW_AT_call_line: case DW_AT_call_line:
line.line = (int)v.as.uint64; line.line = (int)v.as.uint64;
@ -1928,16 +1935,16 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
/* 1 or 3 */ /* 1 or 3 */
break; /* goto skip_die; */ break; /* goto skip_die; */
case DW_AT_abstract_origin: case DW_AT_abstract_origin:
read_abstract_origin(reader, v.form, v.as.uint64, &line); read_abstract_origin(reader, v.form, v.as.uint64, &line, errout);
break; /* goto skip_die; */ break; /* goto skip_die; */
} }
} }
/* ranges_inspect(reader, &ranges); */ /* ranges_inspect(reader, &ranges, errout); */
/* kprintf("%d:%tx: %x ",__LINE__,diepos,die.tag); */ /* kprintf("%d:%tx: %x ",__LINE__,diepos,die.tag); */
for (int i=offset; i < num_traces; i++) { for (int i=offset; i < num_traces; i++) {
uintptr_t addr = (uintptr_t)traces[i]; uintptr_t addr = (uintptr_t)traces[i];
uintptr_t offset = addr - reader->obj->base_addr + reader->obj->vmaddr; uintptr_t offset = addr - reader->obj->base_addr + reader->obj->vmaddr;
uintptr_t saddr = ranges_include(reader, &ranges, offset, &rnglists_header); uintptr_t saddr = ranges_include(reader, &ranges, offset, &rnglists_header, errout);
if (saddr == UINTPTR_MAX) return false; if (saddr == UINTPTR_MAX) return false;
if (saddr) { if (saddr) {
/* kprintf("%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */ /* kprintf("%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */
@ -1976,7 +1983,10 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
// //
// It records DW_LNCT_path and DW_LNCT_directory_index at the index "idx". // It records DW_LNCT_path and DW_LNCT_directory_index at the index "idx".
static const char * static const char *
parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t *obj, const char **out_path, uint64_t *out_directory_index) { parse_ver5_debug_line_header(const char *p, int idx, uint8_t format,
obj_info_t *obj, const char **out_path,
uint64_t *out_directory_index, FILE *errout)
{
int i, j; int i, j;
int entry_format_count = *(uint8_t *)p++; int entry_format_count = *(uint8_t *)p++;
const char *entry_format = p; const char *entry_format = p;
@ -1996,7 +2006,7 @@ parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t
DebugInfoValue v = {{0}}; DebugInfoValue v = {{0}};
unsigned long dw_lnct = uleb128(&format); unsigned long dw_lnct = uleb128(&format);
unsigned long dw_form = uleb128(&format); unsigned long dw_form = uleb128(&format);
if (!debug_info_reader_read_value(&reader, dw_form, &v)) return 0; if (!debug_info_reader_read_value(&reader, dw_form, &v, errout)) return 0;
if (dw_lnct == 1 /* DW_LNCT_path */ && v.type == VAL_cstr && out_path) if (dw_lnct == 1 /* DW_LNCT_path */ && v.type == VAL_cstr && out_path)
*out_path = v.as.ptr + v.off; *out_path = v.as.ptr + v.off;
if (dw_lnct == 2 /* DW_LNCT_directory_index */ && v.type == VAL_uint && out_directory_index) if (dw_lnct == 2 /* DW_LNCT_directory_index */ && v.type == VAL_uint && out_directory_index)
@ -2041,7 +2051,7 @@ fail:
/* read file and fill lines */ /* read file and fill lines */
static uintptr_t static uintptr_t
fill_lines(int num_traces, void **traces, int check_debuglink, fill_lines(int num_traces, void **traces, int check_debuglink,
obj_info_t **objp, line_info_t *lines, int offset) obj_info_t **objp, line_info_t *lines, int offset, FILE *errout)
{ {
int i, j; int i, j;
char *shstr; char *shstr;
@ -2202,8 +2212,8 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
i = 0; i = 0;
while (reader.p < reader.pend) { while (reader.p < reader.pend) {
/* kprintf("%d:%tx: CU[%d]\n", __LINE__, reader.p - reader.obj->debug_info.ptr, i++); */ /* kprintf("%d:%tx: CU[%d]\n", __LINE__, reader.p - reader.obj->debug_info.ptr, i++); */
if (di_read_cu(&reader)) goto use_symtab; if (di_read_cu(&reader, errout)) goto use_symtab;
if (!debug_info_read(&reader, num_traces, traces, lines, offset)) if (!debug_info_read(&reader, num_traces, traces, lines, offset, errout))
goto use_symtab; goto use_symtab;
} }
} }
@ -2244,14 +2254,14 @@ use_symtab:
if (gnu_debuglink_shdr && check_debuglink) { if (gnu_debuglink_shdr && check_debuglink) {
follow_debuglink(file + gnu_debuglink_shdr->sh_offset, follow_debuglink(file + gnu_debuglink_shdr->sh_offset,
num_traces, traces, num_traces, traces,
objp, lines, offset); objp, lines, offset, errout);
} }
if (note_gnu_build_id && check_debuglink) { if (note_gnu_build_id && check_debuglink) {
ElfW(Nhdr) *nhdr = (ElfW(Nhdr)*) (file + note_gnu_build_id->sh_offset); ElfW(Nhdr) *nhdr = (ElfW(Nhdr)*) (file + note_gnu_build_id->sh_offset);
const char *build_id = (char *)(nhdr + 1) + nhdr->n_namesz; const char *build_id = (char *)(nhdr + 1) + nhdr->n_namesz;
follow_debuglink_build_id(build_id, nhdr->n_descsz, follow_debuglink_build_id(build_id, nhdr->n_descsz,
num_traces, traces, num_traces, traces,
objp, lines, offset); objp, lines, offset, errout);
} }
goto finish; goto finish;
} }
@ -2259,7 +2269,7 @@ use_symtab:
if (parse_debug_line(num_traces, traces, if (parse_debug_line(num_traces, traces,
obj->debug_line.ptr, obj->debug_line.ptr,
obj->debug_line.size, obj->debug_line.size,
obj, lines, offset) == -1) obj, lines, offset, errout) == -1)
goto fail; goto fail;
finish: finish:
@ -2271,7 +2281,7 @@ fail:
/* read file and fill lines */ /* read file and fill lines */
static uintptr_t static uintptr_t
fill_lines(int num_traces, void **traces, int check_debuglink, fill_lines(int num_traces, void **traces, int check_debuglink,
obj_info_t **objp, line_info_t *lines, int offset) obj_info_t **objp, line_info_t *lines, int offset, FILE *errout)
{ {
# ifdef __LP64__ # ifdef __LP64__
# define LP(x) x##_64 # define LP(x) x##_64
@ -2472,8 +2482,8 @@ found_mach_header:
DebugInfoReader reader; DebugInfoReader reader;
debug_info_reader_init(&reader, obj); debug_info_reader_init(&reader, obj);
while (reader.p < reader.pend) { while (reader.p < reader.pend) {
if (di_read_cu(&reader)) goto fail; if (di_read_cu(&reader, errout)) goto fail;
if (!debug_info_read(&reader, num_traces, traces, lines, offset)) if (!debug_info_read(&reader, num_traces, traces, lines, offset, errout))
goto fail; goto fail;
} }
} }
@ -2481,7 +2491,7 @@ found_mach_header:
if (parse_debug_line(num_traces, traces, if (parse_debug_line(num_traces, traces,
obj->debug_line.ptr, obj->debug_line.ptr,
obj->debug_line.size, obj->debug_line.size,
obj, lines, offset) == -1) obj, lines, offset, errout) == -1)
goto fail; goto fail;
return dladdr_fbase; return dladdr_fbase;
@ -2542,7 +2552,7 @@ main_exe_path(void)
#endif #endif
static void static void
print_line0(line_info_t *line, void *address) print_line0(line_info_t *line, void *address, FILE *errout)
{ {
uintptr_t addr = (uintptr_t)address; uintptr_t addr = (uintptr_t)address;
uintptr_t d = addr - line->saddr; uintptr_t d = addr - line->saddr;
@ -2583,16 +2593,16 @@ print_line0(line_info_t *line, void *address)
} }
static void static void
print_line(line_info_t *line, void *address) print_line(line_info_t *line, void *address, FILE *errout)
{ {
print_line0(line, address); print_line0(line, address, errout);
if (line->next) { if (line->next) {
print_line(line->next, NULL); print_line(line->next, NULL, errout);
} }
} }
void void
rb_dump_backtrace_with_lines(int num_traces, void **traces) rb_dump_backtrace_with_lines(int num_traces, void **traces, FILE *errout)
{ {
int i; int i;
/* async-signal unsafe */ /* async-signal unsafe */
@ -2611,7 +2621,7 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces)
memcpy(main_path, binary_filename, len+1); memcpy(main_path, binary_filename, len+1);
append_obj(&obj); append_obj(&obj);
obj->path = main_path; obj->path = main_path;
addr = fill_lines(num_traces, traces, 1, &obj, lines, -1); addr = fill_lines(num_traces, traces, 1, &obj, lines, -1, errout);
if (addr != (uintptr_t)-1) { if (addr != (uintptr_t)-1) {
dladdr_fbases[0] = (void *)addr; dladdr_fbases[0] = (void *)addr;
} }
@ -2648,7 +2658,7 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces)
lines[i].saddr = (uintptr_t)info.dli_saddr; lines[i].saddr = (uintptr_t)info.dli_saddr;
} }
strlcpy(binary_filename, path, PATH_MAX); strlcpy(binary_filename, path, PATH_MAX);
if (fill_lines(num_traces, traces, 1, &obj, lines, i) == (uintptr_t)-1) if (fill_lines(num_traces, traces, 1, &obj, lines, i, errout) == (uintptr_t)-1)
break; break;
} }
next_line: next_line:
@ -2657,7 +2667,7 @@ next_line:
/* output */ /* output */
for (i = 0; i < num_traces; i++) { for (i = 0; i < num_traces; i++) {
print_line(&lines[i], traces[i]); print_line(&lines[i], traces[i], errout);
/* FreeBSD's backtrace may show _start and so on */ /* FreeBSD's backtrace may show _start and so on */
if (lines[i].sname && strcmp("main", lines[i].sname) == 0) if (lines[i].sname && strcmp("main", lines[i].sname) == 0)

View File

@ -12,8 +12,10 @@
#if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)) #if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H))
#include <stdio.h>
void void
rb_dump_backtrace_with_lines(int num_traces, void **traces); rb_dump_backtrace_with_lines(int num_traces, void **traces, FILE *errout);
#endif /* USE_ELF */ #endif /* USE_ELF */

View File

@ -757,7 +757,7 @@ bug_report_end(FILE *out)
FILE *out = bug_report_file(file, line); \ FILE *out = bug_report_file(file, line); \
if (out) { \ if (out) { \
bug_report_begin(out, fmt); \ bug_report_begin(out, fmt); \
rb_vm_bugreport(ctx); \ rb_vm_bugreport(ctx, out); \
bug_report_end(out); \ bug_report_end(out); \
} \ } \
} while (0) \ } while (0) \
@ -766,7 +766,7 @@ bug_report_end(FILE *out)
FILE *out = bug_report_file(file, line); \ FILE *out = bug_report_file(file, line); \
if (out) { \ if (out) { \
bug_report_begin_valist(out, fmt, args); \ bug_report_begin_valist(out, fmt, args); \
rb_vm_bugreport(ctx); \ rb_vm_bugreport(ctx, out); \
bug_report_end(out); \ bug_report_end(out); \
} \ } \
} while (0) \ } while (0) \
@ -881,7 +881,7 @@ rb_assert_failure(const char *file, int line, const char *name, const char *expr
if (name) fprintf(out, "%s:", name); if (name) fprintf(out, "%s:", name);
fprintf(out, "%s\n%s\n\n", expr, rb_dynamic_description); fprintf(out, "%s\n%s\n\n", expr, rb_dynamic_description);
preface_dump(out); preface_dump(out);
rb_vm_bugreport(NULL); rb_vm_bugreport(NULL, out);
bug_report_end(out); bug_report_end(out);
die(); die();
} }
@ -3256,7 +3256,7 @@ rb_fatal(const char *fmt, ...)
/* The thread has no GVL. Object allocation impossible (cant run GC), /* The thread has no GVL. Object allocation impossible (cant run GC),
* thus no message can be printed out. */ * thus no message can be printed out. */
fprintf(stderr, "[FATAL] rb_fatal() outside of GVL\n"); fprintf(stderr, "[FATAL] rb_fatal() outside of GVL\n");
rb_print_backtrace(); rb_print_backtrace(stderr);
die(); die();
} }

4
gc.c
View File

@ -7798,7 +7798,7 @@ check_children_i(const VALUE child, void *ptr)
if (check_rvalue_consistency_force(child, FALSE) != 0) { if (check_rvalue_consistency_force(child, FALSE) != 0) {
fprintf(stderr, "check_children_i: %s has error (referenced from %s)", fprintf(stderr, "check_children_i: %s has error (referenced from %s)",
obj_info(child), obj_info(data->parent)); obj_info(child), obj_info(data->parent));
rb_print_backtrace(); /* C backtrace will help to debug */ rb_print_backtrace(stderr); /* C backtrace will help to debug */
data->err_count++; data->err_count++;
} }
@ -9118,7 +9118,7 @@ rb_gc_register_address(VALUE *addr)
if (0 && !SPECIAL_CONST_P(obj)) { if (0 && !SPECIAL_CONST_P(obj)) {
rb_warn("Object is assigned to registering address already: %"PRIsVALUE, rb_warn("Object is assigned to registering address already: %"PRIsVALUE,
rb_obj_class(obj)); rb_obj_class(obj));
rb_print_backtrace(); rb_print_backtrace(stderr);
} }
} }

View File

@ -95,7 +95,7 @@ int rb_ec_obj_respond_to(struct rb_execution_context_struct *ec, VALUE obj, ID i
void rb_clear_constant_cache(void); void rb_clear_constant_cache(void);
/* vm_dump.c */ /* vm_dump.c */
void rb_print_backtrace(void); void rb_print_backtrace(FILE *);
/* vm_backtrace.c */ /* vm_backtrace.c */
VALUE rb_vm_thread_backtrace(int argc, const VALUE *argv, VALUE thval); VALUE rb_vm_thread_backtrace(int argc, const VALUE *argv, VALUE thval);
@ -103,7 +103,7 @@ VALUE rb_vm_thread_backtrace_locations(int argc, const VALUE *argv, VALUE thval)
VALUE rb_vm_backtrace(int argc, const VALUE * argv, struct rb_execution_context_struct * ec); VALUE rb_vm_backtrace(int argc, const VALUE * argv, struct rb_execution_context_struct * ec);
VALUE rb_vm_backtrace_locations(int argc, const VALUE * argv, struct rb_execution_context_struct * ec); VALUE rb_vm_backtrace_locations(int argc, const VALUE * argv, struct rb_execution_context_struct * ec);
VALUE rb_make_backtrace(void); VALUE rb_make_backtrace(void);
void rb_backtrace_print_as_bugreport(void); void rb_backtrace_print_as_bugreport(FILE*);
int rb_backtrace_p(VALUE obj); int rb_backtrace_p(VALUE obj);
VALUE rb_backtrace_to_str_ary(VALUE obj); VALUE rb_backtrace_to_str_ary(VALUE obj);
VALUE rb_backtrace_to_location_ary(VALUE obj); VALUE rb_backtrace_to_location_ary(VALUE obj);

2
vm.c
View File

@ -3542,7 +3542,7 @@ extern size_t rb_gc_stack_maxsize;
static VALUE static VALUE
sdr(VALUE self) sdr(VALUE self)
{ {
rb_vm_bugreport(NULL); rb_vm_bugreport(NULL, stderr);
return Qnil; return Qnil;
} }

View File

@ -71,7 +71,7 @@ calc_pos(const rb_iseq_t *iseq, const VALUE *pc, int *lineno, int *node_id)
#if VMDEBUG && defined(HAVE_BUILTIN___BUILTIN_TRAP) #if VMDEBUG && defined(HAVE_BUILTIN___BUILTIN_TRAP)
else { else {
/* SDR() is not possible; that causes infinite loop. */ /* SDR() is not possible; that causes infinite loop. */
rb_print_backtrace(); rb_print_backtrace(stderr);
__builtin_trap(); __builtin_trap();
} }
#endif #endif
@ -1003,31 +1003,38 @@ vm_backtrace_print(FILE *fp)
&arg); &arg);
} }
struct oldbt_bugreport_arg {
FILE *fp;
int count;
};
static void static void
oldbt_bugreport(void *arg, VALUE file, int line, VALUE method) oldbt_bugreport(void *arg, VALUE file, int line, VALUE method)
{ {
struct oldbt_bugreport_arg *p = arg;
FILE *fp = p->fp;
const char *filename = NIL_P(file) ? "ruby" : RSTRING_PTR(file); const char *filename = NIL_P(file) ? "ruby" : RSTRING_PTR(file);
if (!*(int *)arg) { if (!p->count) {
fprintf(stderr, "-- Ruby level backtrace information " fprintf(fp, "-- Ruby level backtrace information "
"----------------------------------------\n"); "----------------------------------------\n");
*(int *)arg = 1; p->count = 1;
} }
if (NIL_P(method)) { if (NIL_P(method)) {
fprintf(stderr, "%s:%d:in unknown method\n", filename, line); fprintf(fp, "%s:%d:in unknown method\n", filename, line);
} }
else { else {
fprintf(stderr, "%s:%d:in `%s'\n", filename, line, RSTRING_PTR(method)); fprintf(fp, "%s:%d:in `%s'\n", filename, line, RSTRING_PTR(method));
} }
} }
void void
rb_backtrace_print_as_bugreport(void) rb_backtrace_print_as_bugreport(FILE *fp)
{ {
struct oldbt_arg arg; struct oldbt_arg arg;
int i = 0; struct oldbt_bugreport_arg barg = {fp, 0};
arg.func = oldbt_bugreport; arg.func = oldbt_bugreport;
arg.data = (int *)&i; arg.data = &barg;
backtrace_each(GET_EC(), backtrace_each(GET_EC(),
oldbt_init, oldbt_init,

View File

@ -1678,13 +1678,13 @@ VALUE rb_proc_alloc(VALUE klass);
VALUE rb_proc_dup(VALUE self); VALUE rb_proc_dup(VALUE self);
/* for debug */ /* for debug */
extern void rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp); extern void rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *);
extern void rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE *_pc); extern void rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE *_pc, FILE *);
extern void rb_vmdebug_debug_print_post(const rb_execution_context_t *ec, const rb_control_frame_t *cfp); extern void rb_vmdebug_debug_print_post(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *);
#define SDR() rb_vmdebug_stack_dump_raw(GET_EC(), GET_EC()->cfp) #define SDR() rb_vmdebug_stack_dump_raw(GET_EC(), GET_EC()->cfp, stderr)
#define SDR2(cfp) rb_vmdebug_stack_dump_raw(GET_EC(), (cfp)) #define SDR2(cfp) rb_vmdebug_stack_dump_raw(GET_EC(), (cfp), stderr)
void rb_vm_bugreport(const void *); void rb_vm_bugreport(const void *, FILE *);
typedef void (*ruby_sighandler_t)(int); typedef void (*ruby_sighandler_t)(int);
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 4, 5) RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 4, 5)
NORETURN(void rb_bug_for_fatal_signal(ruby_sighandler_t default_sighandler, int sig, const void *, const char *fmt, ...)); NORETURN(void rb_bug_for_fatal_signal(ruby_sighandler_t default_sighandler, int sig, const void *, const char *fmt, ...));

264
vm_dump.c
View File

@ -47,7 +47,7 @@ const char *rb_method_type_name(rb_method_type_t type);
int ruby_on_ci; int ruby_on_ci;
static void static void
control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *cfp) control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *errout)
{ {
ptrdiff_t pc = -1; ptrdiff_t pc = -1;
ptrdiff_t ep = cfp->ep - ec->vm_stack; ptrdiff_t ep = cfp->ep - ec->vm_stack;
@ -140,30 +140,30 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c
line = -1; line = -1;
} }
fprintf(stderr, "c:%04"PRIdPTRDIFF" ", fprintf(errout, "c:%04"PRIdPTRDIFF" ",
((rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size) - cfp)); ((rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size) - cfp));
if (pc == -1) { if (pc == -1) {
fprintf(stderr, "p:---- "); fprintf(errout, "p:---- ");
} }
else { else {
fprintf(stderr, "p:%04"PRIdPTRDIFF" ", pc); fprintf(errout, "p:%04"PRIdPTRDIFF" ", pc);
} }
fprintf(stderr, "s:%04"PRIdPTRDIFF" ", cfp->sp - ec->vm_stack); fprintf(errout, "s:%04"PRIdPTRDIFF" ", cfp->sp - ec->vm_stack);
fprintf(stderr, ep_in_heap == ' ' ? "e:%06"PRIdPTRDIFF" " : "E:%06"PRIxPTRDIFF" ", ep % 10000); fprintf(errout, ep_in_heap == ' ' ? "e:%06"PRIdPTRDIFF" " : "E:%06"PRIxPTRDIFF" ", ep % 10000);
fprintf(stderr, "%-6s", magic); fprintf(errout, "%-6s", magic);
if (line) { if (line) {
fprintf(stderr, " %s", posbuf); fprintf(errout, " %s", posbuf);
} }
if (VM_FRAME_FINISHED_P(cfp)) { if (VM_FRAME_FINISHED_P(cfp)) {
fprintf(stderr, " [FINISH]"); fprintf(errout, " [FINISH]");
} }
if (0) { if (0) {
fprintf(stderr, " \t"); fprintf(errout, " \t");
fprintf(stderr, "iseq: %-24s ", iseq_name); fprintf(errout, "iseq: %-24s ", iseq_name);
fprintf(stderr, "self: %-24s ", selfstr); fprintf(errout, "self: %-24s ", selfstr);
fprintf(stderr, "%-1s ", biseq_name); fprintf(errout, "%-1s ", biseq_name);
} }
fprintf(stderr, "\n"); fprintf(errout, "\n");
// additional information for CI machines // additional information for CI machines
if (ruby_on_ci) { if (ruby_on_ci) {
@ -171,26 +171,26 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c
if (me) { if (me) {
if (IMEMO_TYPE_P(me, imemo_ment)) { if (IMEMO_TYPE_P(me, imemo_ment)) {
fprintf(stderr, " me:\n"); fprintf(errout, " me:\n");
fprintf(stderr, " called_id: %s, type: %s\n", rb_id2name(me->called_id), rb_method_type_name(me->def->type)); fprintf(errout, " called_id: %s, type: %s\n", rb_id2name(me->called_id), rb_method_type_name(me->def->type));
fprintf(stderr, " owner class: %s\n", rb_raw_obj_info(buff, 0x100, me->owner)); fprintf(errout, " owner class: %s\n", rb_raw_obj_info(buff, 0x100, me->owner));
if (me->owner != me->defined_class) { if (me->owner != me->defined_class) {
fprintf(stderr, " defined_class: %s\n", rb_raw_obj_info(buff, 0x100, me->defined_class)); fprintf(errout, " defined_class: %s\n", rb_raw_obj_info(buff, 0x100, me->defined_class));
} }
} }
else { else {
fprintf(stderr, " me is corrupted (%s)\n", rb_raw_obj_info(buff, 0x100, (VALUE)me)); fprintf(errout, " me is corrupted (%s)\n", rb_raw_obj_info(buff, 0x100, (VALUE)me));
} }
} }
fprintf(stderr, " self: %s\n", rb_raw_obj_info(buff, 0x100, cfp->self)); fprintf(errout, " self: %s\n", rb_raw_obj_info(buff, 0x100, cfp->self));
if (iseq) { if (iseq) {
if (ISEQ_BODY(iseq)->local_table_size > 0) { if (ISEQ_BODY(iseq)->local_table_size > 0) {
fprintf(stderr, " lvars:\n"); fprintf(errout, " lvars:\n");
for (unsigned int i=0; i<ISEQ_BODY(iseq)->local_table_size; i++) { for (unsigned int i=0; i<ISEQ_BODY(iseq)->local_table_size; i++) {
const VALUE *argv = cfp->ep - ISEQ_BODY(cfp->iseq)->local_table_size - VM_ENV_DATA_SIZE + 1; const VALUE *argv = cfp->ep - ISEQ_BODY(cfp->iseq)->local_table_size - VM_ENV_DATA_SIZE + 1;
fprintf(stderr, " %s: %s\n", fprintf(errout, " %s: %s\n",
rb_id2name(ISEQ_BODY(iseq)->local_table[i]), rb_id2name(ISEQ_BODY(iseq)->local_table[i]),
rb_raw_obj_info(buff, 0x100, argv[i])); rb_raw_obj_info(buff, 0x100, argv[i]));
} }
@ -200,83 +200,83 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c
} }
void void
rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp) rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *errout)
{ {
#if 0 #if 0
VALUE *sp = cfp->sp; VALUE *sp = cfp->sp;
const VALUE *ep = cfp->ep; const VALUE *ep = cfp->ep;
VALUE *p, *st, *t; VALUE *p, *st, *t;
fprintf(stderr, "-- stack frame ------------\n"); fprintf(errout, "-- stack frame ------------\n");
for (p = st = ec->vm_stack; p < sp; p++) { for (p = st = ec->vm_stack; p < sp; p++) {
fprintf(stderr, "%04ld (%p): %08"PRIxVALUE, (long)(p - st), p, *p); fprintf(errout, "%04ld (%p): %08"PRIxVALUE, (long)(p - st), p, *p);
t = (VALUE *)*p; t = (VALUE *)*p;
if (ec->vm_stack <= t && t < sp) { if (ec->vm_stack <= t && t < sp) {
fprintf(stderr, " (= %ld)", (long)((VALUE *)GC_GUARDED_PTR_REF((VALUE)t) - ec->vm_stack)); fprintf(errout, " (= %ld)", (long)((VALUE *)GC_GUARDED_PTR_REF((VALUE)t) - ec->vm_stack));
} }
if (p == ep) if (p == ep)
fprintf(stderr, " <- ep"); fprintf(errout, " <- ep");
fprintf(stderr, "\n"); fprintf(errout, "\n");
} }
#endif #endif
fprintf(stderr, "-- Control frame information " fprintf(errout, "-- Control frame information "
"-----------------------------------------------\n"); "-----------------------------------------------\n");
while ((void *)cfp < (void *)(ec->vm_stack + ec->vm_stack_size)) { while ((void *)cfp < (void *)(ec->vm_stack + ec->vm_stack_size)) {
control_frame_dump(ec, cfp); control_frame_dump(ec, cfp, errout);
cfp++; cfp++;
} }
fprintf(stderr, "\n"); fprintf(errout, "\n");
} }
void void
rb_vmdebug_stack_dump_raw_current(void) rb_vmdebug_stack_dump_raw_current(void)
{ {
const rb_execution_context_t *ec = GET_EC(); const rb_execution_context_t *ec = GET_EC();
rb_vmdebug_stack_dump_raw(ec, ec->cfp); rb_vmdebug_stack_dump_raw(ec, ec->cfp, stderr);
} }
void void
rb_vmdebug_env_dump_raw(const rb_env_t *env, const VALUE *ep) rb_vmdebug_env_dump_raw(const rb_env_t *env, const VALUE *ep, FILE *errout)
{ {
unsigned int i; unsigned int i;
fprintf(stderr, "-- env --------------------\n"); fprintf(errout, "-- env --------------------\n");
while (env) { while (env) {
fprintf(stderr, "--\n"); fprintf(errout, "--\n");
for (i = 0; i < env->env_size; i++) { for (i = 0; i < env->env_size; i++) {
fprintf(stderr, "%04d: %08"PRIxVALUE" (%p)", i, env->env[i], (void *)&env->env[i]); fprintf(errout, "%04d: %08"PRIxVALUE" (%p)", i, env->env[i], (void *)&env->env[i]);
if (&env->env[i] == ep) fprintf(stderr, " <- ep"); if (&env->env[i] == ep) fprintf(errout, " <- ep");
fprintf(stderr, "\n"); fprintf(errout, "\n");
} }
env = rb_vm_env_prev_env(env); env = rb_vm_env_prev_env(env);
} }
fprintf(stderr, "---------------------------\n"); fprintf(errout, "---------------------------\n");
} }
void void
rb_vmdebug_proc_dump_raw(rb_proc_t *proc) rb_vmdebug_proc_dump_raw(rb_proc_t *proc, FILE *errout)
{ {
const rb_env_t *env; const rb_env_t *env;
char *selfstr; char *selfstr;
VALUE val = rb_inspect(vm_block_self(&proc->block)); VALUE val = rb_inspect(vm_block_self(&proc->block));
selfstr = StringValueCStr(val); selfstr = StringValueCStr(val);
fprintf(stderr, "-- proc -------------------\n"); fprintf(errout, "-- proc -------------------\n");
fprintf(stderr, "self: %s\n", selfstr); fprintf(errout, "self: %s\n", selfstr);
env = VM_ENV_ENVVAL_PTR(vm_block_ep(&proc->block)); env = VM_ENV_ENVVAL_PTR(vm_block_ep(&proc->block));
rb_vmdebug_env_dump_raw(env, vm_block_ep(&proc->block)); rb_vmdebug_env_dump_raw(env, vm_block_ep(&proc->block), errout);
} }
void void
rb_vmdebug_stack_dump_th(VALUE thval) rb_vmdebug_stack_dump_th(VALUE thval, FILE *errout)
{ {
rb_thread_t *target_th = rb_thread_ptr(thval); rb_thread_t *target_th = rb_thread_ptr(thval);
rb_vmdebug_stack_dump_raw(target_th->ec, target_th->ec->cfp); rb_vmdebug_stack_dump_raw(target_th->ec, target_th->ec->cfp, errout);
} }
#if VMDEBUG > 2 #if VMDEBUG > 2
@ -295,7 +295,7 @@ vm_base_ptr(const rb_control_frame_t *cfp)
} }
static void static void
vm_stack_dump_each(const rb_execution_context_t *ec, const rb_control_frame_t *cfp) vm_stack_dump_each(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *errout)
{ {
int i, argc = 0, local_table_size = 0; int i, argc = 0, local_table_size = 0;
VALUE rstr; VALUE rstr;
@ -321,17 +321,17 @@ vm_stack_dump_each(const rb_execution_context_t *ec, const rb_control_frame_t *c
{ {
const VALUE *ptr = ep - local_table_size; const VALUE *ptr = ep - local_table_size;
control_frame_dump(ec, cfp); control_frame_dump(ec, cfp, errout);
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
rstr = rb_inspect(*ptr); rstr = rb_inspect(*ptr);
fprintf(stderr, " arg %2d: %8s (%p)\n", i, StringValueCStr(rstr), fprintf(errout, " arg %2d: %8s (%p)\n", i, StringValueCStr(rstr),
(void *)ptr++); (void *)ptr++);
} }
for (; i < local_table_size - 1; i++) { for (; i < local_table_size - 1; i++) {
rstr = rb_inspect(*ptr); rstr = rb_inspect(*ptr);
fprintf(stderr, " local %2d: %8s (%p)\n", i, StringValueCStr(rstr), fprintf(errout, " local %2d: %8s (%p)\n", i, StringValueCStr(rstr),
(void *)ptr++); (void *)ptr++);
} }
ptr = vm_base_ptr(cfp); ptr = vm_base_ptr(cfp);
@ -347,13 +347,13 @@ vm_stack_dump_each(const rb_execution_context_t *ec, const rb_control_frame_t *c
rstr = rb_inspect(*ptr); rstr = rb_inspect(*ptr);
break; break;
} }
fprintf(stderr, " stack %2d: %8s (%"PRIdPTRDIFF")\n", i, StringValueCStr(rstr), fprintf(errout, " stack %2d: %8s (%"PRIdPTRDIFF")\n", i, StringValueCStr(rstr),
(ptr - ec->vm_stack)); (ptr - ec->vm_stack));
} }
} }
else if (VM_FRAME_FINISHED_P(cfp)) { else if (VM_FRAME_FINISHED_P(cfp)) {
if (ec->vm_stack + ec->vm_stack_size > (VALUE *)(cfp + 1)) { if (ec->vm_stack + ec->vm_stack_size > (VALUE *)(cfp + 1)) {
vm_stack_dump_each(ec, cfp + 1); vm_stack_dump_each(ec, cfp + 1, errout);
} }
else { else {
/* SDR(); */ /* SDR(); */
@ -366,7 +366,7 @@ vm_stack_dump_each(const rb_execution_context_t *ec, const rb_control_frame_t *c
#endif #endif
void void
rb_vmdebug_debug_print_register(const rb_execution_context_t *ec) rb_vmdebug_debug_print_register(const rb_execution_context_t *ec, FILE *errout)
{ {
rb_control_frame_t *cfp = ec->cfp; rb_control_frame_t *cfp = ec->cfp;
ptrdiff_t pc = -1; ptrdiff_t pc = -1;
@ -382,18 +382,18 @@ rb_vmdebug_debug_print_register(const rb_execution_context_t *ec)
} }
cfpi = ((rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size)) - cfp; cfpi = ((rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size)) - cfp;
fprintf(stderr, " [PC] %04"PRIdPTRDIFF", [SP] %04"PRIdPTRDIFF", [EP] %04"PRIdPTRDIFF", [CFP] %04"PRIdPTRDIFF"\n", fprintf(errout, " [PC] %04"PRIdPTRDIFF", [SP] %04"PRIdPTRDIFF", [EP] %04"PRIdPTRDIFF", [CFP] %04"PRIdPTRDIFF"\n",
pc, (cfp->sp - ec->vm_stack), ep, cfpi); pc, (cfp->sp - ec->vm_stack), ep, cfpi);
} }
void void
rb_vmdebug_thread_dump_regs(VALUE thval) rb_vmdebug_thread_dump_regs(VALUE thval, FILE *errout)
{ {
rb_vmdebug_debug_print_register(rb_thread_ptr(thval)->ec); rb_vmdebug_debug_print_register(rb_thread_ptr(thval)->ec, errout);
} }
void void
rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE *_pc) rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE *_pc, FILE *errout)
{ {
const rb_iseq_t *iseq = cfp->iseq; const rb_iseq_t *iseq = cfp->iseq;
@ -416,27 +416,27 @@ rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_fr
} }
#if VMDEBUG > 3 #if VMDEBUG > 3
fprintf(stderr, " (1)"); fprintf(errout, " (1)");
rb_vmdebug_debug_print_register(ec); rb_vmdebug_debug_print_register(errout, ec);
#endif #endif
} }
void void
rb_vmdebug_debug_print_post(const rb_execution_context_t *ec, const rb_control_frame_t *cfp) rb_vmdebug_debug_print_post(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *errout)
{ {
#if VMDEBUG > 9 #if VMDEBUG > 9
SDR2(cfp); rb_vmdebug_stack_dump_raw(ec, cfp, errout);
#endif #endif
#if VMDEBUG > 3 #if VMDEBUG > 3
fprintf(stderr, " (2)"); fprintf(errout, " (2)");
rb_vmdebug_debug_print_register(ec); rb_vmdebug_debug_print_register(errout, ec);
#endif #endif
/* stack_dump_raw(ec, cfp); */ /* stack_dump_raw(ec, cfp); */
#if VMDEBUG > 2 #if VMDEBUG > 2
/* stack_dump_thobj(ec); */ /* stack_dump_thobj(ec); */
vm_stack_dump_each(ec, ec->cfp); vm_stack_dump_each(ec, ec->cfp, errout);
printf printf
("--------------------------------------------------------------\n"); ("--------------------------------------------------------------\n");
@ -444,14 +444,14 @@ rb_vmdebug_debug_print_post(const rb_execution_context_t *ec, const rb_control_f
} }
VALUE VALUE
rb_vmdebug_thread_dump_state(VALUE self) rb_vmdebug_thread_dump_state(FILE *errout, VALUE self)
{ {
rb_thread_t *th = rb_thread_ptr(self); rb_thread_t *th = rb_thread_ptr(self);
rb_control_frame_t *cfp = th->ec->cfp; rb_control_frame_t *cfp = th->ec->cfp;
fprintf(stderr, "Thread state dump:\n"); fprintf(errout, "Thread state dump:\n");
fprintf(stderr, "pc : %p, sp : %p\n", (void *)cfp->pc, (void *)cfp->sp); fprintf(errout, "pc : %p, sp : %p\n", (void *)cfp->pc, (void *)cfp->sp);
fprintf(stderr, "cfp: %p, ep : %p\n", (void *)cfp, (void *)cfp->ep); fprintf(errout, "cfp: %p, ep : %p\n", (void *)cfp, (void *)cfp->ep);
return Qnil; return Qnil;
} }
@ -664,6 +664,11 @@ typedef void *PGET_MODULE_BASE_ROUTINE64;
typedef void *PTRANSLATE_ADDRESS_ROUTINE64; typedef void *PTRANSLATE_ADDRESS_ROUTINE64;
# endif # endif
struct dump_thead_arg {
DWORD tid;
FILE *errout;
};
static void static void
dump_thread(void *arg) dump_thread(void *arg)
{ {
@ -675,7 +680,8 @@ dump_thread(void *arg)
BOOL (WINAPI *pSymFromAddr)(HANDLE, DWORD64, DWORD64 *, SYMBOL_INFO *); BOOL (WINAPI *pSymFromAddr)(HANDLE, DWORD64, DWORD64 *, SYMBOL_INFO *);
BOOL (WINAPI *pSymGetLineFromAddr64)(HANDLE, DWORD64, DWORD *, IMAGEHLP_LINE64 *); BOOL (WINAPI *pSymGetLineFromAddr64)(HANDLE, DWORD64, DWORD *, IMAGEHLP_LINE64 *);
HANDLE (WINAPI *pOpenThread)(DWORD, BOOL, DWORD); HANDLE (WINAPI *pOpenThread)(DWORD, BOOL, DWORD);
DWORD tid = *(DWORD *)arg; DWORD tid = ((struct dump_thead_arg *)arg)->tid;
FILE *errout = ((struct dump_thead_arg *)arg)->errout;
HANDLE ph; HANDLE ph;
HANDLE th; HANDLE th;
@ -740,16 +746,16 @@ dump_thread(void *arg)
info->MaxNameLen = MAX_SYM_NAME; info->MaxNameLen = MAX_SYM_NAME;
if (pSymFromAddr(ph, addr, &displacement, info)) { if (pSymFromAddr(ph, addr, &displacement, info)) {
if (GetModuleFileName((HANDLE)(uintptr_t)pSymGetModuleBase64(ph, addr), libpath, sizeof(libpath))) if (GetModuleFileName((HANDLE)(uintptr_t)pSymGetModuleBase64(ph, addr), libpath, sizeof(libpath)))
fprintf(stderr, "%s", libpath); fprintf(errout, "%s", libpath);
fprintf(stderr, "(%s+0x%"PRI_64_PREFIX"x)", fprintf(errout, "(%s+0x%"PRI_64_PREFIX"x)",
info->Name, displacement); info->Name, displacement);
} }
fprintf(stderr, " [0x%p]", (void *)(VALUE)addr); fprintf(errout, " [0x%p]", (void *)(VALUE)addr);
memset(&line, 0, sizeof(line)); memset(&line, 0, sizeof(line));
line.SizeOfStruct = sizeof(line); line.SizeOfStruct = sizeof(line);
if (pSymGetLineFromAddr64(ph, addr, &tmp, &line)) if (pSymGetLineFromAddr64(ph, addr, &tmp, &line))
fprintf(stderr, " %s:%lu", line.FileName, line.LineNumber); fprintf(errout, " %s:%lu", line.FileName, line.LineNumber);
fprintf(stderr, "\n"); fprintf(errout, "\n");
} }
} }
@ -764,27 +770,30 @@ dump_thread(void *arg)
#endif #endif
void void
rb_print_backtrace(void) rb_print_backtrace(FILE *errout)
{ {
#if USE_BACKTRACE #if USE_BACKTRACE
#define MAX_NATIVE_TRACE 1024 #define MAX_NATIVE_TRACE 1024
static void *trace[MAX_NATIVE_TRACE]; static void *trace[MAX_NATIVE_TRACE];
int n = (int)backtrace(trace, MAX_NATIVE_TRACE); int n = (int)backtrace(trace, MAX_NATIVE_TRACE);
#if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)) && defined(HAVE_DLADDR) && !defined(__sparc) #if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)) && defined(HAVE_DLADDR) && !defined(__sparc)
rb_dump_backtrace_with_lines(n, trace); rb_dump_backtrace_with_lines(n, trace, errout);
#else #else
char **syms = backtrace_symbols(trace, n); char **syms = backtrace_symbols(trace, n);
if (syms) { if (syms) {
int i; int i;
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
fprintf(stderr, "%s\n", syms[i]); fprintf(errout, "%s\n", syms[i]);
} }
free(syms); free(syms);
} }
#endif #endif
#elif defined(_WIN32) #elif defined(_WIN32)
DWORD tid = GetCurrentThreadId(); struct dump_thead_arg arg = {
HANDLE th = (HANDLE)_beginthread(dump_thread, 0, &tid); .tid = GetCurrentThreadId(),
.errout = errout,
};
HANDLE th = (HANDLE)_beginthread(dump_thread, 0, &arg);
if (th != (HANDLE)-1) if (th != (HANDLE)-1)
WaitForSingleObject(th, INFINITE); WaitForSingleObject(th, INFINITE);
#endif #endif
@ -796,21 +805,21 @@ rb_print_backtrace(void)
#if defined __linux__ #if defined __linux__
# if defined(__x86_64__) || defined(__i386__) # if defined(__x86_64__) || defined(__i386__)
# define dump_machine_register(reg) (col_count = print_machine_register(mctx->gregs[REG_##reg], #reg, col_count, 80)) # define dump_machine_register(reg) (col_count = print_machine_register(errout, mctx->gregs[REG_##reg], #reg, col_count, 80))
# elif defined(__aarch64__) || defined(__arm__) || defined(__riscv) || defined(__loongarch64) # elif defined(__aarch64__) || defined(__arm__) || defined(__riscv) || defined(__loongarch64)
# define dump_machine_register(reg, regstr) (col_count = print_machine_register(reg, regstr, col_count, 80)) # define dump_machine_register(reg, regstr) (col_count = print_machine_register(errout, reg, regstr, col_count, 80))
# endif # endif
#elif defined __APPLE__ #elif defined __APPLE__
# if defined(__aarch64__) # if defined(__aarch64__)
# define dump_machine_register(reg, regstr) (col_count = print_machine_register(mctx->MCTX_SS_REG(reg), regstr, col_count, 80)) # define dump_machine_register(reg, regstr) (col_count = print_machine_register(errout, mctx->MCTX_SS_REG(reg), regstr, col_count, 80))
# else # else
# define dump_machine_register(reg) (col_count = print_machine_register(mctx->MCTX_SS_REG(reg), #reg, col_count, 80)) # define dump_machine_register(reg) (col_count = print_machine_register(errout, mctx->MCTX_SS_REG(reg), #reg, col_count, 80))
# endif # endif
#endif #endif
#ifdef dump_machine_register #ifdef dump_machine_register
static int static int
print_machine_register(size_t reg, const char *reg_name, int col_count, int max_col) print_machine_register(FILE *errout, size_t reg, const char *reg_name, int col_count, int max_col)
{ {
int ret; int ret;
char buf[64]; char buf[64];
@ -818,21 +827,21 @@ print_machine_register(size_t reg, const char *reg_name, int col_count, int max_
ret = snprintf(buf, sizeof(buf), " %3.3s: 0x%.*" PRIxSIZE, reg_name, size_width, reg); ret = snprintf(buf, sizeof(buf), " %3.3s: 0x%.*" PRIxSIZE, reg_name, size_width, reg);
if (col_count + ret > max_col) { if (col_count + ret > max_col) {
fputs("\n", stderr); fputs("\n", errout);
col_count = 0; col_count = 0;
} }
col_count += ret; col_count += ret;
fputs(buf, stderr); fputs(buf, errout);
return col_count; return col_count;
} }
static void static void
rb_dump_machine_register(const ucontext_t *ctx) rb_dump_machine_register(FILE *errout, const ucontext_t *ctx)
{ {
int col_count = 0; int col_count = 0;
if (!ctx) return; if (!ctx) return;
fprintf(stderr, "-- Machine register context " fprintf(errout, "-- Machine register context "
"------------------------------------------------\n"); "------------------------------------------------\n");
# if defined __linux__ # if defined __linux__
@ -1025,14 +1034,14 @@ rb_dump_machine_register(const ucontext_t *ctx)
# endif # endif
} }
# endif # endif
fprintf(stderr, "\n\n"); fprintf(errout, "\n\n");
} }
#else #else
# define rb_dump_machine_register(ctx) ((void)0) # define rb_dump_machine_register(errout, ctx) ((void)0)
#endif /* dump_machine_register */ #endif /* dump_machine_register */
void void
rb_vm_bugreport(const void *ctx) rb_vm_bugreport(const void *ctx, FILE *errout)
{ {
const char *cmd = getenv("RUBY_ON_BUG"); const char *cmd = getenv("RUBY_ON_BUG");
if (cmd) { if (cmd) {
@ -1049,7 +1058,7 @@ rb_vm_bugreport(const void *ctx)
{ {
static bool crashing = false; static bool crashing = false;
if (crashing) { if (crashing) {
fprintf(stderr, "Crashed while printing bug report\n"); fprintf(errout, "Crashed while printing bug report\n");
return; return;
} }
crashing = true; crashing = true;
@ -1067,32 +1076,32 @@ rb_vm_bugreport(const void *ctx)
const rb_execution_context_t *ec = rb_current_execution_context(false); const rb_execution_context_t *ec = rb_current_execution_context(false);
if (vm && ec) { if (vm && ec) {
SDR(); rb_vmdebug_stack_dump_raw(ec, ec->cfp, errout);
rb_backtrace_print_as_bugreport(); rb_backtrace_print_as_bugreport(errout);
fputs("\n", stderr); fputs("\n", errout);
// If we get here, hopefully things are intact enough that // If we get here, hopefully things are intact enough that
// we can read these two numbers. It is an estimate because // we can read these two numbers. It is an estimate because
// we are reading without synchronization. // we are reading without synchronization.
fprintf(stderr, "-- Threading information " fprintf(errout, "-- Threading information "
"---------------------------------------------------\n"); "---------------------------------------------------\n");
fprintf(stderr, "Total ractor count: %u\n", vm->ractor.cnt); fprintf(errout, "Total ractor count: %u\n", vm->ractor.cnt);
fprintf(stderr, "Ruby thread count for this ractor: %u\n", rb_ec_ractor_ptr(ec)->threads.cnt); fprintf(errout, "Ruby thread count for this ractor: %u\n", rb_ec_ractor_ptr(ec)->threads.cnt);
fputs("\n", stderr); fputs("\n", errout);
} }
rb_dump_machine_register(ctx); rb_dump_machine_register(errout, ctx);
#if USE_BACKTRACE || defined(_WIN32) #if USE_BACKTRACE || defined(_WIN32)
fprintf(stderr, "-- C level backtrace information " fprintf(errout, "-- C level backtrace information "
"-------------------------------------------\n"); "-------------------------------------------\n");
rb_print_backtrace(); rb_print_backtrace(errout);
fprintf(stderr, "\n"); fprintf(errout, "\n");
#endif /* USE_BACKTRACE */ #endif /* USE_BACKTRACE */
if (other_runtime_info || vm) { if (other_runtime_info || vm) {
fprintf(stderr, "-- Other runtime information " fprintf(errout, "-- Other runtime information "
"-----------------------------------------------\n\n"); "-----------------------------------------------\n\n");
} }
if (vm && !rb_during_gc()) { if (vm && !rb_during_gc()) {
@ -1105,16 +1114,16 @@ rb_vm_bugreport(const void *ctx)
name = vm->progname; name = vm->progname;
if (name) { if (name) {
fprintf(stderr, "* Loaded script: %.*s\n", fprintf(errout, "* Loaded script: %.*s\n",
LIMITED_NAME_LENGTH(name), RSTRING_PTR(name)); LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
fprintf(stderr, "\n"); fprintf(errout, "\n");
} }
if (vm->loaded_features) { if (vm->loaded_features) {
fprintf(stderr, "* Loaded features:\n\n"); fprintf(errout, "* Loaded features:\n\n");
for (i=0; i<RARRAY_LEN(vm->loaded_features); i++) { for (i=0; i<RARRAY_LEN(vm->loaded_features); i++) {
name = RARRAY_AREF(vm->loaded_features, i); name = RARRAY_AREF(vm->loaded_features, i);
if (RB_TYPE_P(name, T_STRING)) { if (RB_TYPE_P(name, T_STRING)) {
fprintf(stderr, " %4d %.*s\n", i, fprintf(errout, " %4d %.*s\n", i,
LIMITED_NAME_LENGTH(name), RSTRING_PTR(name)); LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
} }
else if (RB_TYPE_P(name, T_CLASS) || RB_TYPE_P(name, T_MODULE)) { else if (RB_TYPE_P(name, T_CLASS) || RB_TYPE_P(name, T_MODULE)) {
@ -1122,26 +1131,26 @@ rb_vm_bugreport(const void *ctx)
"class" : "module"; "class" : "module";
name = rb_search_class_path(rb_class_real(name)); name = rb_search_class_path(rb_class_real(name));
if (!RB_TYPE_P(name, T_STRING)) { if (!RB_TYPE_P(name, T_STRING)) {
fprintf(stderr, " %4d %s:<unnamed>\n", i, type); fprintf(errout, " %4d %s:<unnamed>\n", i, type);
continue; continue;
} }
fprintf(stderr, " %4d %s:%.*s\n", i, type, fprintf(errout, " %4d %s:%.*s\n", i, type,
LIMITED_NAME_LENGTH(name), RSTRING_PTR(name)); LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
} }
else { else {
VALUE klass = rb_search_class_path(rb_obj_class(name)); VALUE klass = rb_search_class_path(rb_obj_class(name));
if (!RB_TYPE_P(klass, T_STRING)) { if (!RB_TYPE_P(klass, T_STRING)) {
fprintf(stderr, " %4d #<%p:%p>\n", i, fprintf(errout, " %4d #<%p:%p>\n", i,
(void *)CLASS_OF(name), (void *)name); (void *)CLASS_OF(name), (void *)name);
continue; continue;
} }
fprintf(stderr, " %4d #<%.*s:%p>\n", i, fprintf(errout, " %4d #<%.*s:%p>\n", i,
LIMITED_NAME_LENGTH(klass), RSTRING_PTR(klass), LIMITED_NAME_LENGTH(klass), RSTRING_PTR(klass),
(void *)name); (void *)name);
} }
} }
} }
fprintf(stderr, "\n"); fprintf(errout, "\n");
} }
{ {
@ -1149,17 +1158,17 @@ rb_vm_bugreport(const void *ctx)
{ {
FILE *fp = fopen(PROC_MAPS_NAME, "r"); FILE *fp = fopen(PROC_MAPS_NAME, "r");
if (fp) { if (fp) {
fprintf(stderr, "* Process memory map:\n\n"); fprintf(errout, "* Process memory map:\n\n");
while (!feof(fp)) { while (!feof(fp)) {
char buff[0x100]; char buff[0x100];
size_t rn = fread(buff, 1, 0x100, fp); size_t rn = fread(buff, 1, 0x100, fp);
if (fwrite(buff, 1, rn, stderr) != rn) if (fwrite(buff, 1, rn, errout) != rn)
break; break;
} }
fclose(fp); fclose(fp);
fprintf(stderr, "\n\n"); fprintf(errout, "\n\n");
} }
} }
#endif /* __linux__ */ #endif /* __linux__ */
@ -1173,14 +1182,14 @@ rb_vm_bugreport(const void *ctx)
mib[2] = KERN_PROC_PID; mib[2] = KERN_PROC_PID;
mib[3] = getpid(); mib[3] = getpid();
if (sysctl(mib, MIB_KERN_PROC_PID_LEN, &kp, &len, NULL, 0) == -1) { if (sysctl(mib, MIB_KERN_PROC_PID_LEN, &kp, &len, NULL, 0) == -1) {
perror("sysctl"); fprintf(errout, "sysctl: %s\n", strerror(errno));
} }
else { else {
struct procstat *prstat = procstat_open_sysctl(); struct procstat *prstat = procstat_open_sysctl();
fprintf(stderr, "* Process memory map:\n\n"); fprintf(errout, "* Process memory map:\n\n");
procstat_vm(prstat, &kp); procstat_vm(prstat, &kp);
procstat_close(prstat); procstat_close(prstat);
fprintf(stderr, "\n"); fprintf(errout, "\n");
} }
#endif /* __FreeBSD__ */ #endif /* __FreeBSD__ */
#ifdef __APPLE__ #ifdef __APPLE__
@ -1190,7 +1199,7 @@ rb_vm_bugreport(const void *ctx)
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT; mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT;
natural_t depth = 0; natural_t depth = 0;
fprintf(stderr, "* Process memory map:\n\n"); fprintf(errout, "* Process memory map:\n\n");
while (1) { while (1) {
if (vm_region_recurse(mach_task_self(), &addr, &size, &depth, if (vm_region_recurse(mach_task_self(), &addr, &size, &depth,
(vm_region_recurse_info_t)&map, &count) != KERN_SUCCESS) { (vm_region_recurse_info_t)&map, &count) != KERN_SUCCESS) {
@ -1202,17 +1211,17 @@ rb_vm_bugreport(const void *ctx)
depth++; depth++;
} }
else { else {
fprintf(stderr, "%lx-%lx %s%s%s", addr, (addr+size), fprintf(errout, "%lx-%lx %s%s%s", addr, (addr+size),
((map.protection & VM_PROT_READ) != 0 ? "r" : "-"), ((map.protection & VM_PROT_READ) != 0 ? "r" : "-"),
((map.protection & VM_PROT_WRITE) != 0 ? "w" : "-"), ((map.protection & VM_PROT_WRITE) != 0 ? "w" : "-"),
((map.protection & VM_PROT_EXECUTE) != 0 ? "x" : "-")); ((map.protection & VM_PROT_EXECUTE) != 0 ? "x" : "-"));
#ifdef HAVE_LIBPROC_H #ifdef HAVE_LIBPROC_H
char buff[PATH_MAX]; char buff[PATH_MAX];
if (proc_regionfilename(getpid(), addr, buff, sizeof(buff)) > 0) { if (proc_regionfilename(getpid(), addr, buff, sizeof(buff)) > 0) {
fprintf(stderr, " %s", buff); fprintf(errout, " %s", buff);
} }
#endif #endif
fprintf(stderr, "\n"); fprintf(errout, "\n");
} }
addr += size; addr += size;
@ -1227,14 +1236,15 @@ rb_vmdebug_stack_dump_all_threads(void)
{ {
rb_thread_t *th = NULL; rb_thread_t *th = NULL;
rb_ractor_t *r = GET_RACTOR(); rb_ractor_t *r = GET_RACTOR();
FILE *errout = stderr;
// TODO: now it only shows current ractor // TODO: now it only shows current ractor
ccan_list_for_each(&r->threads.set, th, lt_node) { ccan_list_for_each(&r->threads.set, th, lt_node) {
#ifdef NON_SCALAR_THREAD_ID #ifdef NON_SCALAR_THREAD_ID
fprintf(stderr, "th: %p, native_id: N/A\n", th); fprintf(errout, "th: %p, native_id: N/A\n", th);
#else #else
fprintf(stderr, "th: %p, native_id: %p\n", (void *)th, (void *)(uintptr_t)th->nt->thread_id); fprintf(errout, "th: %p, native_id: %p\n", (void *)th, (void *)(uintptr_t)th->nt->thread_id);
#endif #endif
rb_vmdebug_stack_dump_raw(th->ec, th->ec->cfp); rb_vmdebug_stack_dump_raw(th->ec, th->ec->cfp, errout);
} }
} }