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
							parent
							
								
									c254c9842b
								
							
						
					
					
						commit
						2b087dbf95
					
				| 
						 | 
					@ -63,8 +63,10 @@ fn (g mut CGen) genln(s string) {
 | 
				
			||||||
	g.cur_line = '$g.cur_line $s'
 | 
						g.cur_line = '$g.cur_line $s'
 | 
				
			||||||
	if g.cur_line != '' {
 | 
						if g.cur_line != '' {
 | 
				
			||||||
		if g.line_directives && g.cur_line.trim_space() != '' {
 | 
							if g.line_directives && g.cur_line.trim_space() != '' {
 | 
				
			||||||
 | 
								if g.file.len > 0 && g.line > 0 {
 | 
				
			||||||
				g.lines << '\n#line $g.line "$g.file"'
 | 
									g.lines << '\n#line $g.line "$g.file"'
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		g.lines << g.cur_line
 | 
							g.lines << g.cur_line
 | 
				
			||||||
		g.prev_line = g.cur_line
 | 
							g.prev_line = g.cur_line
 | 
				
			||||||
		g.cur_line = ''
 | 
							g.cur_line = ''
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 {
 | 
						if p.v.pref.is_debug && f.name == 'panic' && !p.is_js {
 | 
				
			||||||
		mod_name := p.mod.replace('_dot_', '.')
 | 
							mod_name := p.mod.replace('_dot_', '.')
 | 
				
			||||||
		fn_name := p.cur_fn.name.replace('${p.mod}__', '')
 | 
							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(
 | 
							p.cgen.resetln(p.cgen.cur_line.replace(
 | 
				
			||||||
			'v_panic (',
 | 
								'v_panic (',
 | 
				
			||||||
			'panic_debug ($p.scanner.line_nr, tos3("$file_path"), tos3("$mod_name"), tos2((byte *)"$fn_name"), '
 | 
								'panic_debug ($p.scanner.line_nr, tos3("$file_path"), tos3("$mod_name"), tos2((byte *)"$fn_name"), '
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,8 +73,8 @@ fn (v &V) generate_hot_reload_code() {
 | 
				
			||||||
		mut vexe := os.args[0]
 | 
							mut vexe := os.args[0]
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		if os.user_os() == 'windows' {
 | 
							if os.user_os() == 'windows' {
 | 
				
			||||||
			vexe = vexe.replace('\\', '\\\\')
 | 
								vexe = cescaped_path(vexe)
 | 
				
			||||||
			file = file.replace('\\', '\\\\')
 | 
								file = cescaped_path(file)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		mut msvc := ''
 | 
							mut msvc := ''
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -338,6 +338,7 @@ fn (v mut V) compile() {
 | 
				
			||||||
	if v.pref.build_mode == .build_module {
 | 
						if v.pref.build_mode == .build_module {
 | 
				
			||||||
		v.generate_vh()
 | 
							v.generate_vh()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// parse generated V code (str() methods etc)
 | 
						// parse generated V code (str() methods etc)
 | 
				
			||||||
	mut vgen_parser := v.new_parser_from_string(v.vgen_buf.str(), 'vgen')
 | 
						mut vgen_parser := v.new_parser_from_string(v.vgen_buf.str(), 'vgen')
 | 
				
			||||||
	// free the string builder which held the generated methods
 | 
						// free the string builder which held the generated methods
 | 
				
			||||||
| 
						 | 
					@ -383,6 +384,15 @@ fn (v mut V) generate_main() {
 | 
				
			||||||
	mut cgen := v.cgen
 | 
						mut cgen := v.cgen
 | 
				
			||||||
	$if js { return }
 | 
						$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 {
 | 
						if v.pref.build_mode == .default_mode {
 | 
				
			||||||
		mut consts_init_body := cgen.consts_init.join_lines()
 | 
							mut consts_init_body := cgen.consts_init.join_lines()
 | 
				
			||||||
		// vlib can't have `init_consts()`
 | 
							// vlib can't have `init_consts()`
 | 
				
			||||||
| 
						 | 
					@ -1042,3 +1052,7 @@ fn vhash() string {
 | 
				
			||||||
	C.snprintf(*char(buf), 50, '%s', C.V_COMMIT_HASH )
 | 
						C.snprintf(*char(buf), 50, '%s', C.V_COMMIT_HASH )
 | 
				
			||||||
	return tos_clone(buf)
 | 
						return tos_clone(buf)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn cescaped_path(s string) string {
 | 
				
			||||||
 | 
					  return s.replace('\\','\\\\')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,7 +100,14 @@ fn (v mut V) new_parser_from_string(text string, id string) Parser {
 | 
				
			||||||
	return p
 | 
						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 {
 | 
					fn (v mut V) new_parser_from_file(path string) Parser {
 | 
				
			||||||
 | 
						v.reset_cgen_file_line_parameters()
 | 
				
			||||||
	//println('new_parser("$path")')
 | 
						//println('new_parser("$path")')
 | 
				
			||||||
	mut path_pcguard := ''
 | 
						mut path_pcguard := ''
 | 
				
			||||||
	mut path_platform := '.v'
 | 
						mut path_platform := '.v'
 | 
				
			||||||
| 
						 | 
					@ -124,7 +131,6 @@ fn (v mut V) new_parser_from_file(path string) Parser {
 | 
				
			||||||
	if p.pref.building_v {
 | 
						if p.pref.building_v {
 | 
				
			||||||
		p.scanner.should_print_relative_paths_on_error = true
 | 
							p.scanner.should_print_relative_paths_on_error = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	v.cgen.file = path
 | 
					 | 
				
			||||||
	p.scan_tokens()
 | 
						p.scan_tokens()
 | 
				
			||||||
	//p.scanner.debug_tokens()
 | 
						//p.scanner.debug_tokens()
 | 
				
			||||||
	return p
 | 
						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
 | 
					// creates a new parser. most likely you will want to use
 | 
				
			||||||
// `new_parser_file` or `new_parser_string` instead.
 | 
					// `new_parser_file` or `new_parser_string` instead.
 | 
				
			||||||
fn (v mut V) new_parser(scanner &Scanner, id string) Parser {
 | 
					fn (v mut V) new_parser(scanner &Scanner, id string) Parser {
 | 
				
			||||||
 | 
						v.reset_cgen_file_line_parameters()
 | 
				
			||||||
	mut p := Parser {
 | 
						mut p := Parser {
 | 
				
			||||||
		id: id
 | 
							id: id
 | 
				
			||||||
		scanner: scanner
 | 
							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_errors_in_color = false
 | 
				
			||||||
		p.scanner.should_print_relative_paths_on_error = true
 | 
							p.scanner.should_print_relative_paths_on_error = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	v.cgen.line_directives = v.pref.is_debuggable
 | 
					 | 
				
			||||||
	// v.cgen.file = path
 | 
					 | 
				
			||||||
	return p
 | 
						return p
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -195,6 +200,7 @@ fn (p mut Parser) next() {
 | 
				
			||||||
	 p.tok = res.tok
 | 
						 p.tok = res.tok
 | 
				
			||||||
	 p.lit = res.lit
 | 
						 p.lit = res.lit
 | 
				
			||||||
	 p.scanner.line_nr = res.line_nr
 | 
						 p.scanner.line_nr = res.line_nr
 | 
				
			||||||
 | 
						 p.cgen.line = res.line_nr
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn (p & Parser) peek() TokenKind {
 | 
					fn (p & Parser) peek() TokenKind {
 | 
				
			||||||
| 
						 | 
					@ -230,6 +236,9 @@ 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.pass = pass
 | 
				
			||||||
	p.token_idx = 0
 | 
						p.token_idx = 0
 | 
				
			||||||
	p.next()
 | 
						p.next()
 | 
				
			||||||
| 
						 | 
					@ -2807,7 +2816,7 @@ fn (p mut Parser) string_expr() {
 | 
				
			||||||
	// No ${}, just return a simple string
 | 
						// No ${}, just return a simple string
 | 
				
			||||||
	if p.peek() != .dollar || is_raw {
 | 
						if p.peek() != .dollar || is_raw {
 | 
				
			||||||
		p.fgen("'$str'")
 | 
							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");`
 | 
							// `C.puts('hi')` => `puts("hi");`
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		Calling a C function sometimes requires a call to a string method
 | 
							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.gen('bool $tmp = ')
 | 
				
			||||||
	p.check_types(p.bool_expression(), 'bool')
 | 
						p.check_types(p.bool_expression(), 'bool')
 | 
				
			||||||
	// TODO print "expected:  got" for failed tests
 | 
						// TODO print "expected:  got" for failed tests
 | 
				
			||||||
	filename := p.file_path.replace('\\', '\\\\')
 | 
						filename := cescaped_path(p.file_path)
 | 
				
			||||||
	p.genln(';
 | 
						p.genln(';
 | 
				
			||||||
\n
 | 
					\n
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -410,7 +410,7 @@ fn (s mut Scanner) scan() ScanRes {
 | 
				
			||||||
		// println( 'file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @FN)
 | 
							// println( 'file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @FN)
 | 
				
			||||||
		// ... which is useful while debugging/tracing
 | 
							// ... which is useful while debugging/tracing
 | 
				
			||||||
		if name == 'FN' { return scan_res(.str, s.fn_name) }
 | 
							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 == 'LINE' { return scan_res(.str, (s.line_nr+1).str()) }
 | 
				
			||||||
		if name == 'COLUMN' { return scan_res(.str, (s.current_column()).str()) }
 | 
							if name == 'COLUMN' { return scan_res(.str, (s.current_column()).str()) }
 | 
				
			||||||
		if name == 'VHASH' { return scan_res(.str, vhash()) }
 | 
							if name == 'VHASH' { return scan_res(.str, vhash()) }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,7 +55,32 @@ pub fn print_backtrace_skipping_top_frames(skipframes int) {
 | 
				
			||||||
			if C.backtrace_symbols_fd != 0 {
 | 
								if C.backtrace_symbols_fd != 0 {
 | 
				
			||||||
				buffer := [100]byteptr
 | 
									buffer := [100]byteptr
 | 
				
			||||||
				nr_ptrs := C.backtrace(*voidptr(buffer), 100)
 | 
									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
 | 
									return
 | 
				
			||||||
			}else{
 | 
								}else{
 | 
				
			||||||
				C.printf('backtrace_symbols_fd is missing, so printing backtraces is not available.\n')
 | 
									C.printf('backtrace_symbols_fd is missing, so printing backtraces is not available.\n')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue