v2 / vlib / builtin / backtraces.c.v
116 lines · 111 sloc · 3.31 KB · da76f5588e022410b2c6e32749b54b9ce3a4ae91
Raw
1module builtin
2
3// print_backtrace shows a backtrace of the current call stack on stdout.
4pub fn print_backtrace() {
5 // At the time of backtrace_symbols_fd call, the C stack would look something like this:
6 // * print_backtrace_skipping_top_frames
7 // * print_backtrace itself
8 // * the rest of the backtrace frames
9 // => top 2 frames should be skipped, since they will not be informative to the developer
10 $if !no_backtrace ? {
11 $if freestanding {
12 println(bare_backtrace())
13 } $else $if tinyc {
14 C.tcc_backtrace(c'Backtrace')
15 } $else $if use_libbacktrace ? {
16 $if openbsd {
17 print_backtrace_skipping_top_frames(2)
18 } $else {
19 // NOTE: TCC doesn't have the unwind library
20 print_libbacktrace(1)
21 }
22 } $else {
23 print_backtrace_skipping_top_frames(2)
24 }
25 }
26}
27
28// demangle_v_symbol converts a C-mangled V symbol name to a human-readable V name.
29// For example: `veb__run_T_main__App_main__Context` => `veb.run[main.App, main.Context]`
30@[direct_array_access]
31fn demangle_v_symbol(cname string) string {
32 mut name := cname
33 // Strip builtin__ prefix:
34 if name.starts_with('builtin__') {
35 name = name[9..]
36 }
37 // Replace pointer type encoding:
38 name = name.replace('__ptr__', '&')
39 // Split base name from generic parameters on `_T_`:
40 // C mangling: `base_T_type1_type2` where each type is prefixed with `_`
41 // and types may contain `__` for module separators.
42 t_pos := name.index('_T_') or { -1 }
43 if t_pos >= 0 {
44 base := name[..t_pos].replace('__', '.')
45 generic_suffix := name[t_pos + 3..]
46 // Split on single `_` that is not part of `__` (double underscore).
47 params := split_generic_params(generic_suffix)
48 mut demangled_params := []string{cap: params.len}
49 for param in params {
50 demangled_params << param.replace('__', '.')
51 }
52 return base + '[' + demangled_params.join(', ') + ']'
53 }
54 // No generics - just replace module separator:
55 name = name.replace('__', '.')
56 // `main.main` is V's internal C mangling, in V it's just `main`:
57 if name == 'main.main' {
58 return 'main'
59 }
60 return name
61}
62
63// split_generic_params splits a generic suffix like `main__App_main__Context`
64// into individual type parameters [`main__App`, `main__Context`].
65// Splits on single `_` but not `__` (double underscore used for module separators).
66@[direct_array_access]
67fn split_generic_params(s string) []string {
68 mut params := []string{}
69 mut start := 0
70 mut i := 0
71 for i < s.len {
72 if s[i] == `_` {
73 if i + 1 < s.len && s[i + 1] == `_` {
74 // Part of `__` (module separator), skip both.
75 i += 2
76 } else {
77 // Single `_` is a generic param separator.
78 if i > start {
79 params << s[start..i]
80 }
81 i++
82 start = i
83 }
84 } else {
85 i++
86 }
87 }
88 if start < s.len {
89 params << s[start..]
90 }
91 return params
92}
93
94// demangle_backtrace_sym demangles a V symbol within a Linux-style backtrace fragment.
95// Linux format: `./executable(symbol_name+0x1a4) `
96fn demangle_backtrace_sym(s string) string {
97 paren_start := s.index('(') or { return s }
98 plus_pos := s.index_after_('+', paren_start)
99 if plus_pos < 0 {
100 return s
101 }
102 symbol := s[paren_start + 1..plus_pos]
103 if symbol.len == 0 {
104 return s
105 }
106 return s[..paren_start + 1] + demangle_v_symbol(symbol) + s[plus_pos..]
107}
108
109fn eprint_space_padding(output string, max_len int) {
110 padding_len := max_len - output.len
111 if padding_len > 0 {
112 for _ in 0 .. padding_len {
113 eprint(' ')
114 }
115 }
116}
117