backtraces: add source line numbers too on linux

* Add source line numbers to backtraces on linux.

* Fix -g (broken after token caching).

* reset the #line directives after all the v code is compiled

* cleanup p.cgen.line setting inside p.next() .

* Support windows filepaths like "C:\Users\travis\build\vlang\v\v.exe.tmp.c" inside generated #line directives.

* Try to diagnose better windows-gcc failing.

* Revert "Try to diagnose better windows-gcc failing."

* implement and use cescaped_path/1 .

* Use cescaped_path/1 consistently throughout compiler/ .
pull/2290/head
Delyan Angelov 2019-10-12 00:04:42 +03:00 committed by Alexander Medvednikov
parent c254c9842b
commit 2b087dbf95
7 changed files with 62 additions and 12 deletions

View File

@ -63,7 +63,9 @@ fn (g mut CGen) genln(s string) {
g.cur_line = '$g.cur_line $s'
if g.cur_line != '' {
if g.line_directives && g.cur_line.trim_space() != '' {
g.lines << '\n#line $g.line "$g.file"'
if g.file.len > 0 && g.line > 0 {
g.lines << '\n#line $g.line "$g.file"'
}
}
g.lines << g.cur_line
g.prev_line = g.cur_line

View File

@ -809,7 +809,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
if p.v.pref.is_debug && f.name == 'panic' && !p.is_js {
mod_name := p.mod.replace('_dot_', '.')
fn_name := p.cur_fn.name.replace('${p.mod}__', '')
file_path := p.file_path.replace('\\', '\\\\') // escape \
file_path := cescaped_path(p.file_path)
p.cgen.resetln(p.cgen.cur_line.replace(
'v_panic (',
'panic_debug ($p.scanner.line_nr, tos3("$file_path"), tos3("$mod_name"), tos2((byte *)"$fn_name"), '

View File

@ -73,8 +73,8 @@ fn (v &V) generate_hot_reload_code() {
mut vexe := os.args[0]
if os.user_os() == 'windows' {
vexe = vexe.replace('\\', '\\\\')
file = file.replace('\\', '\\\\')
vexe = cescaped_path(vexe)
file = cescaped_path(file)
}
mut msvc := ''

View File

@ -338,6 +338,7 @@ fn (v mut V) compile() {
if v.pref.build_mode == .build_module {
v.generate_vh()
}
// parse generated V code (str() methods etc)
mut vgen_parser := v.new_parser_from_string(v.vgen_buf.str(), 'vgen')
// free the string builder which held the generated methods
@ -383,6 +384,15 @@ fn (v mut V) generate_main() {
mut cgen := v.cgen
$if js { return }
///// After this point, the v files are compiled.
///// The rest is auto generated code, which will not have
///// different .v source file/line numbers.
lines_so_far := cgen.lines.join('\n').count('\n') + 5
cgen.genln('')
cgen.genln('////////////////// Reset the file/line numbers //////////')
cgen.lines << '#line $lines_so_far "${cescaped_path(os.realpath(cgen.out_path))}"'
cgen.genln('')
if v.pref.build_mode == .default_mode {
mut consts_init_body := cgen.consts_init.join_lines()
// vlib can't have `init_consts()`
@ -1042,3 +1052,7 @@ fn vhash() string {
C.snprintf(*char(buf), 50, '%s', C.V_COMMIT_HASH )
return tos_clone(buf)
}
fn cescaped_path(s string) string {
return s.replace('\\','\\\\')
}

View File

@ -100,7 +100,14 @@ fn (v mut V) new_parser_from_string(text string, id string) Parser {
return p
}
fn (v mut V) reset_cgen_file_line_parameters(){
v.cgen.line = 0
v.cgen.file = ''
v.cgen.line_directives = v.pref.is_debuggable
}
fn (v mut V) new_parser_from_file(path string) Parser {
v.reset_cgen_file_line_parameters()
//println('new_parser("$path")')
mut path_pcguard := ''
mut path_platform := '.v'
@ -124,7 +131,6 @@ fn (v mut V) new_parser_from_file(path string) Parser {
if p.pref.building_v {
p.scanner.should_print_relative_paths_on_error = true
}
v.cgen.file = path
p.scan_tokens()
//p.scanner.debug_tokens()
return p
@ -133,6 +139,7 @@ fn (v mut V) new_parser_from_file(path string) Parser {
// creates a new parser. most likely you will want to use
// `new_parser_file` or `new_parser_string` instead.
fn (v mut V) new_parser(scanner &Scanner, id string) Parser {
v.reset_cgen_file_line_parameters()
mut p := Parser {
id: id
scanner: scanner
@ -155,8 +162,6 @@ fn (v mut V) new_parser(scanner &Scanner, id string) Parser {
p.scanner.should_print_errors_in_color = false
p.scanner.should_print_relative_paths_on_error = true
}
v.cgen.line_directives = v.pref.is_debuggable
// v.cgen.file = path
return p
}
@ -195,6 +200,7 @@ fn (p mut Parser) next() {
p.tok = res.tok
p.lit = res.lit
p.scanner.line_nr = res.line_nr
p.cgen.line = res.line_nr
}
fn (p & Parser) peek() TokenKind {
@ -229,7 +235,10 @@ fn (p &Parser) log(s string) {
*/
}
fn (p mut Parser) parse(pass Pass) {
fn (p mut Parser) parse(pass Pass) {
p.cgen.line = 0
p.cgen.file = cescaped_path(os.realpath(p.file_path))
/////////////////////////////////////
p.pass = pass
p.token_idx = 0
p.next()
@ -2807,7 +2816,7 @@ fn (p mut Parser) string_expr() {
// No ${}, just return a simple string
if p.peek() != .dollar || is_raw {
p.fgen("'$str'")
f := if is_raw { str.replace('\\', '\\\\') } else { format_str(str) }
f := if is_raw { cescaped_path(str) } else { format_str(str) }
// `C.puts('hi')` => `puts("hi");`
/*
Calling a C function sometimes requires a call to a string method
@ -3849,7 +3858,7 @@ fn (p mut Parser) assert_statement() {
p.gen('bool $tmp = ')
p.check_types(p.bool_expression(), 'bool')
// TODO print "expected: got" for failed tests
filename := p.file_path.replace('\\', '\\\\')
filename := cescaped_path(p.file_path)
p.genln(';
\n

View File

@ -410,7 +410,7 @@ fn (s mut Scanner) scan() ScanRes {
// println( 'file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @FN)
// ... which is useful while debugging/tracing
if name == 'FN' { return scan_res(.str, s.fn_name) }
if name == 'FILE' { return scan_res(.str, os.realpath(s.file_path).replace('\\', '\\\\')) } // escape \
if name == 'FILE' { return scan_res(.str, cescaped_path(os.realpath(s.file_path))) }
if name == 'LINE' { return scan_res(.str, (s.line_nr+1).str()) }
if name == 'COLUMN' { return scan_res(.str, (s.current_column()).str()) }
if name == 'VHASH' { return scan_res(.str, vhash()) }

View File

@ -55,7 +55,32 @@ pub fn print_backtrace_skipping_top_frames(skipframes int) {
if C.backtrace_symbols_fd != 0 {
buffer := [100]byteptr
nr_ptrs := C.backtrace(*voidptr(buffer), 100)
C.backtrace_symbols_fd(*voidptr(&buffer[skipframes]), nr_ptrs-skipframes, 1)
nr_actual_frames := nr_ptrs-skipframes
mut sframes := []string
csymbols := *byteptr(C.backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames))
for i in 0..nr_actual_frames { sframes << tos2(csymbols[i]) }
for sframe in sframes {
executable := sframe.all_before('(')
addr := sframe.all_after('[').all_before(']')
cmd := 'addr2line -e $executable $addr'
// taken from os, to avoid depending on the os module inside builtin.v
f := byteptr(C.popen(cmd.str, 'r'))
if isnil(f) {
println(sframe) continue
}
buf := [1000]byte
mut output := ''
for C.fgets(buf, 1000, f) != 0 {
output += tos(buf, vstrlen(buf))
}
output = output.trim_space()+':'
if 0 != int(C.pclose(f)) {
println(sframe) continue
}
println( '${output:-45s} | $sframe')
}
//C.backtrace_symbols_fd(*voidptr(&buffer[skipframes]), nr_actual_frames, 1)
return
}else{
C.printf('backtrace_symbols_fd is missing, so printing backtraces is not available.\n')