From 39365b46e250162f278cb36aa148bc2a92b1b84a Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 15 May 2020 01:22:56 +0900 Subject: Merge pull request #3047 from mame/suppress-backtrace Add `--suppress-backtrace=num` option to limit the backtrace length --- doc/fiber.rdoc | 6 +++--- error.c | 1 + eval_error.c | 32 +++++++++++++++++++++++--------- internal/error.h | 1 + ruby.c | 7 +++++++ 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/doc/fiber.rdoc b/doc/fiber.rdoc index d3c19a0d14..4a489edd3a 100644 --- a/doc/fiber.rdoc +++ b/doc/fiber.rdoc @@ -88,13 +88,13 @@ context switching points. Fiber.new(blocking: false) do puts Fiber.current.blocking? # false - # May invoke `Thread.scheduler&.wait_readable`. + # May invoke `Thread.current.scheduler&.wait_readable`. io.read(...) - # May invoke `Thread.scheduler&.wait_writable`. + # May invoke `Thread.current.scheduler&.wait_writable`. io.write(...) - # Will invoke `Thread.scheduler&.wait_sleep`. + # Will invoke `Thread.current.scheduler&.wait_sleep`. sleep(n) end.resume diff --git a/error.c b/error.c index 4912869e43..31ffc32723 100644 --- a/error.c +++ b/error.c @@ -64,6 +64,7 @@ VALUE rb_iseqw_local_variables(VALUE iseqval); VALUE rb_iseqw_new(const rb_iseq_t *); int rb_str_end_with_asciichar(VALUE str, int c); +long rb_backtrace_length_limit = -1; VALUE rb_eEAGAIN; VALUE rb_eEWOULDBLOCK; VALUE rb_eEINPROGRESS; diff --git a/eval_error.c b/eval_error.c index 89e27afe56..e8a7243b96 100644 --- a/eval_error.c +++ b/eval_error.c @@ -233,29 +233,43 @@ print_backtrace(const VALUE eclass, const VALUE errat, const VALUE str, int reve if (!NIL_P(errat)) { long i; long len = RARRAY_LEN(errat); - int skip = eclass == rb_eSysStackError; const int threshold = 1000000000; int width = (len <= 1) ? INT_MIN : ((int)log10((double)(len > threshold ? ((len - 1) / threshold) : len - 1)) + (len < threshold ? 0 : 9) + 1); -#define TRACE_MAX (TRACE_HEAD+TRACE_TAIL+5) -#define TRACE_HEAD 8 -#define TRACE_TAIL 5 + long skip_start = -1, skip_len = 0; + + // skip for stackoverflow + if (eclass == rb_eSysStackError) { + long trace_head = 9; + long trace_tail = 4; + long trace_max = trace_head + trace_tail + 5; + if (len > trace_max) { + skip_start = trace_head; + skip_len = len - trace_max + 5; + } + } + + // skip for explicit limit + if (rb_backtrace_length_limit >= 0 && len > rb_backtrace_length_limit + 1) { + skip_start = rb_backtrace_length_limit + 1; + skip_len = len - rb_backtrace_length_limit; + } for (i = 1; i < len; i++) { + if (i == skip_start) { + write_warn_str(str, rb_sprintf("\t ... %ld levels...\n", skip_len)); + i += skip_len; + if (i >= len) break; + } VALUE line = RARRAY_AREF(errat, reverse ? len - i : i); if (RB_TYPE_P(line, T_STRING)) { VALUE bt = rb_str_new_cstr("\t"); if (reverse) rb_str_catf(bt, "%*ld: ", width, len - i); write_warn_str(str, rb_str_catf(bt, "from %"PRIsVALUE"\n", line)); } - if (skip && i == TRACE_HEAD && len > TRACE_MAX) { - write_warn_str(str, rb_sprintf("\t ... %ld levels...\n", - len - TRACE_HEAD - TRACE_TAIL)); - i = len - TRACE_TAIL; - } } } } diff --git a/internal/error.h b/internal/error.h index 06eb0c4b3e..ff60d0075d 100644 --- a/internal/error.h +++ b/internal/error.h @@ -46,6 +46,7 @@ typedef enum { RB_WARN_CATEGORY_EXPERIMENTAL, } rb_warning_category_t; +extern long rb_backtrace_length_limit; extern VALUE rb_eEAGAIN; extern VALUE rb_eEWOULDBLOCK; extern VALUE rb_eEINPROGRESS; diff --git a/ruby.c b/ruby.c index 901556fb86..ba515bf277 100644 --- a/ruby.c +++ b/ruby.c @@ -309,6 +309,7 @@ usage(const char *name, int help, int highlight, int columns) M("--verbose", "", "turn on verbose mode and disable script from stdin"), M("--version", "", "print the version number, then exit"), M("--help", "", "show this message, -h for short message"), + M("--backtrace-limit=num", "", "limit the maximum length of backtrace"), }; static const struct message dumps[] = { M("insns", "", "instruction sequences"), @@ -1423,6 +1424,12 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt) opt->dump |= DUMP_BIT(help); goto switch_end; } + else if (is_option_with_arg("backtrace-limit", Qfalse, Qfalse)) { + char *e; + long n = strtol(s, &e, 10); + if (errno == ERANGE || n < 0 || *e) rb_raise(rb_eRuntimeError, "wrong limit for backtrace length"); + rb_backtrace_length_limit = n; + } else { rb_raise(rb_eRuntimeError, "invalid option --%s (-h will show valid options)", s); -- cgit v1.2.3