From c7752ce8d3b351d65b7cfd53a6cae287714e20e0 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Thu, 15 Apr 2021 20:26:51 +0300 Subject: [PATCH] v: add an -assert aborts/backtraces option to ease debugging --- cmd/v/help/build-c.txt | 10 ++++++++++ vlib/v/gen/c/assert.v | 20 ++++++++++++++------ vlib/v/pref/pref.v | 24 ++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/cmd/v/help/build-c.txt b/cmd/v/help/build-c.txt index a2c0052def..8973a6dc8b 100644 --- a/cmd/v/help/build-c.txt +++ b/cmd/v/help/build-c.txt @@ -205,3 +205,13 @@ see also `v help build`. Write all C flags into `file.txt`, one flag per line. If `file.txt` is `-`, then write the flags to stdout, one flag per line. + -assert aborts + Call abort() after an assertion failure. Debuggers usually + install signal handlers for SIGABRT, so your program will stop and you + will get a backtrace. If you are running your program outside of a + debugger, you will most likely get a core dump file. + + -assert backtraces + Call print_backtrace() after an assertion failure. Note that + backtraces are not implemented yet on all combinations of + platform/compiler. diff --git a/vlib/v/gen/c/assert.v b/vlib/v/gen/c/assert.v index 6d611b9413..2e1a12f921 100644 --- a/vlib/v/gen/c/assert.v +++ b/vlib/v/gen/c/assert.v @@ -33,9 +33,7 @@ fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) { g.writeln('\tg_test_fails++;') metaname_fail := g.gen_assert_metainfo(node) g.writeln('\tmain__cb_assertion_failed(&$metaname_fail);') - if 'abort_on_assert' in g.pref.compile_defines_all { - g.writeln('\tabort();') - } + g.gen_assert_postfailure_mode(node) g.writeln('\tlongjmp(g_jump_buffer, 1);') g.writeln('\t// TODO') g.writeln('\t// Maybe print all vars in a test function if it fails?') @@ -48,14 +46,24 @@ fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) { g.writeln(' {') metaname_panic := g.gen_assert_metainfo(node) g.writeln('\t__print_assert_failure(&$metaname_panic);') - if 'abort_on_assert' in g.pref.compile_defines_all { - g.writeln('\tabort();') - } + g.gen_assert_postfailure_mode(node) g.writeln('\tv_panic(_SLIT("Assertion failed..."));') g.writeln('}') } } +fn (mut g Gen) gen_assert_postfailure_mode(node ast.AssertStmt) { + match g.pref.assert_failure_mode { + .default {} + .aborts { + g.writeln('\tabort();') + } + .backtraces { + g.writeln('\tprint_backtrace();') + } + } +} + fn (mut g Gen) gen_assert_metainfo(node ast.AssertStmt) string { mod_path := cestring(g.file.path) fn_name := g.fn_decl.name diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 555931522b..055f3d84ed 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -17,6 +17,12 @@ pub enum BuildMode { build_module } +pub enum AssertFailureMode { + default + aborts + backtraces +} + pub enum GarbageCollectionMode { no_gc boehm_full // full garbage collection mode @@ -167,6 +173,7 @@ pub mut: is_help bool // -h, -help or --help was passed gc_mode GarbageCollectionMode = .no_gc // .no_gc, .boehm, .boehm_leak, ... is_cstrict bool // turn on more C warnings; slightly slower + assert_failure_mode AssertFailureMode // whether to call abort() or print_backtrace() after an assertion failure // checker settings: checker_match_exhaustive_cutoff_limit int = 10 } @@ -197,6 +204,23 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences res.arch = target_arch_kind res.build_options << '$arg $target_arch' } + '-assert' { + assert_mode := cmdline.option(current_args, '-assert', '') + match assert_mode { + 'aborts' { + res.assert_failure_mode = .aborts + } + 'backtraces' { + res.assert_failure_mode = .backtraces + } + else { + eprintln('unknown assert mode `-gc $assert_mode`, supported modes are:`') + eprintln(' `-assert aborts` .... calls abort() after assertion failure') + eprintln(' `-assert backtraces` .... calls print_backtrace() after assertion failure') + exit(1) + } + } + } '-show-timings' { res.show_timings = true }