@@ -29,19 +29,25 @@ static int jl_unw_step(bt_cursor_t *cursor, uintptr_t *ip, uintptr_t *sp, uintpt
29
29
30
30
// Record backtrace entries into bt_data by stepping cursor with jl_unw_step
31
31
// until the outermost frame is encountered or the buffer bt_data is (close to)
32
- // full. Native instruction pointers are adjusted to point to the address of
33
- // the call instruction.
32
+ // full. Returned instruction pointers are adjusted to point to the address of
33
+ // the call instruction. The first `skip` frames are not included in `bt_data`.
34
34
//
35
35
// `maxsize` is the size of the buffer `bt_data` (and `sp` if non-NULL). It
36
36
// must be at least JL_BT_MAX_ENTRY_SIZE to accommodate extended backtrace
37
37
// entries. If `sp != NULL`, the stack pointer corresponding `bt_data[i]` is
38
38
// stored in `sp[i]`.
39
39
//
40
+ // Flag `add_interp_frames==1` should be set to record an extended backtrace
41
+ // entries in `bt_data` for each julia interpreter frame.
42
+ //
43
+ // Flag `from_signal_handler==1` should be set if the cursor was obtained by
44
+ // asynchronously interrupting the code.
45
+ //
40
46
// jl_unw_stepn will return 1 if there are more frames to come. The number of
41
- // elements of bt_data (and sp if non-NULL) which were used are returned in
42
- // bt_size.
47
+ // elements written to bt_data (and sp if non-NULL) are returned in bt_size.
43
48
int jl_unw_stepn (bt_cursor_t * cursor , uintptr_t * bt_data , size_t * bt_size ,
44
- uintptr_t * sp , size_t maxsize , int add_interp_frames ) JL_NOTSAFEPOINT
49
+ uintptr_t * sp , size_t maxsize , int skip , int add_interp_frames ,
50
+ int from_signal_handler ) JL_NOTSAFEPOINT
45
51
{
46
52
jl_ptls_t ptls = jl_get_ptls_states ();
47
53
volatile size_t n = 0 ;
@@ -52,50 +58,65 @@ int jl_unw_stepn(bt_cursor_t *cursor, uintptr_t *bt_data, size_t *bt_size,
52
58
#if defined(_OS_WINDOWS_ ) && !defined(_CPU_X86_64_ )
53
59
assert (!jl_in_stackwalk );
54
60
jl_in_stackwalk = 1 ;
61
+ if (!from_signal_handler ) {
62
+ // Workaround 32-bit windows bug missing top frame
63
+ // See for example https://bugs.chromium.org/p/crashpad/issues/detail?id=53
64
+ skip -- ;
65
+ }
55
66
#endif
56
67
#if !defined(_OS_WINDOWS_ )
57
68
jl_jmp_buf * old_buf = ptls -> safe_restore ;
58
69
jl_jmp_buf buf ;
59
70
if (!jl_setjmp (buf , 0 )) {
60
71
ptls -> safe_restore = & buf ;
61
72
#endif
62
- while (1 ) {
73
+ int have_more_frames = 1 ;
74
+ while (have_more_frames ) {
63
75
if (n + JL_BT_MAX_ENTRY_SIZE > maxsize ) {
64
76
// Postpone advancing the cursor: may need more space
65
77
need_more_space = 1 ;
66
78
break ;
67
79
}
68
- int have_more_frames = jl_unw_step (cursor , & return_ip , & thesp , & thefp );
80
+ have_more_frames = jl_unw_step (cursor , & return_ip , & thesp , & thefp );
81
+ if (skip > 0 ) {
82
+ skip -- ;
83
+ continue ;
84
+ }
69
85
if (sp )
70
86
sp [n ] = thesp ;
71
- // ARM instruction pointer encoding uses the low bit as a flag for
72
- // thumb mode, which must be cleared before further use. (Note not
73
- // needed for ARM AArch64.) See
74
- // https://github.com/libunwind/libunwind/pull/131
75
- #ifdef _CPU_ARM_
76
- return_ip &= ~(uintptr_t )0x1 ;
77
- #endif
78
87
// For the purposes of looking up debug info for functions, we want
79
88
// to harvest addresses for the *call* instruction `call_ip` during
80
89
// stack walking. However, this information isn't directly
81
90
// available. Instead, the stack walk discovers the address
82
91
// `return_ip` which would be *returned to* as the stack is
83
92
// unwound.
84
93
//
85
- // To infer `call_ip` in full generality we would need to
86
- // understand each platform ABI instruction pointer encoding and
87
- // calling conventions, noting that these may vary per stack frame.
88
- // (For example signal frames on linux x86_64 have `call_ip ==
89
- // return_ip`.)
90
- //
91
- // However for our current purposes it seems sufficient to assume
92
- // that `call_ip = return_ip-1`. See also:
94
+ // To infer `call_ip` in full generality we need to understand each
95
+ // platform ABI instruction pointer encoding and calling
96
+ // conventions, noting that the latter may vary per stack frame.
93
97
//
98
+ // See also:
94
99
// * The LLVM unwinder functions step() and setInfoBasedOnIPRegister()
95
100
// https://github.com/llvm/llvm-project/blob/master/libunwind/src/UnwindCursor.hpp
96
101
// * The way that libunwind handles it in `unw_get_proc_name`:
97
102
// https://lists.nongnu.org/archive/html/libunwind-devel/2014-06/msg00025.html
98
- uintptr_t call_ip = return_ip - 1 ;
103
+ uintptr_t call_ip = return_ip ;
104
+ // ARM instruction pointer encoding uses the low bit as a flag for
105
+ // thumb mode, which must be cleared before further use. (Note not
106
+ // needed for ARM AArch64.) See
107
+ // https://github.com/libunwind/libunwind/pull/131
108
+ #ifdef _CPU_ARM_
109
+ call_ip &= ~(uintptr_t )0x1 ;
110
+ #endif
111
+ // Now there's two main cases to adjust for:
112
+ // * Normal stack frames where compilers emit a `call` instruction
113
+ // which we can get from the return address via `call_ip = return_ip - 1`.
114
+ // * Code which was interrupted asynchronously (eg, via a signal)
115
+ // is expected to have `call_ip == return_ip`.
116
+ if (n != 0 || !from_signal_handler ) {
117
+ // normal frame
118
+ call_ip -= 1 ;
119
+ }
99
120
if (call_ip == JL_BT_INTERP_FRAME ) {
100
121
// Never leave special marker in the bt data as it can corrupt the GC.
101
122
call_ip = 0 ;
@@ -109,8 +130,6 @@ int jl_unw_stepn(bt_cursor_t *cursor, uintptr_t *bt_data, size_t *bt_size,
109
130
* bt_entry = call_ip ;
110
131
n ++ ;
111
132
}
112
- if (!have_more_frames )
113
- break ;
114
133
}
115
134
#if !defined(_OS_WINDOWS_ )
116
135
}
@@ -130,27 +149,45 @@ int jl_unw_stepn(bt_cursor_t *cursor, uintptr_t *bt_data, size_t *bt_size,
130
149
return need_more_space ;
131
150
}
132
151
133
- size_t rec_backtrace_ctx (uintptr_t * bt_data , size_t maxsize ,
134
- bt_context_t * context , int add_interp_frames )
152
+ NOINLINE size_t rec_backtrace_ctx (uintptr_t * bt_data , size_t maxsize ,
153
+ bt_context_t * context , int add_interp_frames ) JL_NOTSAFEPOINT
135
154
{
136
- size_t bt_size = 0 ;
137
155
bt_cursor_t cursor ;
138
156
if (!jl_unw_init (& cursor , context ))
139
157
return 0 ;
140
- jl_unw_stepn (& cursor , bt_data , & bt_size , NULL , maxsize , add_interp_frames );
158
+ size_t bt_size = 0 ;
159
+ jl_unw_stepn (& cursor , bt_data , & bt_size , NULL , maxsize , 0 , add_interp_frames , 1 );
141
160
return bt_size ;
142
161
}
143
162
144
- size_t rec_backtrace (uintptr_t * bt_data , size_t maxsize )
163
+ // Record backtrace into buffer `bt_data`, using a maximum of `maxsize`
164
+ // elements, and returning the number of elements written.
165
+ //
166
+ // The first `skip` frames are omitted, in addition to omitting the frame from
167
+ // `rec_backtrace` itself.
168
+ NOINLINE size_t rec_backtrace (uintptr_t * bt_data , size_t maxsize , int skip )
145
169
{
146
170
bt_context_t context ;
147
171
memset (& context , 0 , sizeof (context ));
148
172
jl_unw_get (& context );
149
- return rec_backtrace_ctx (bt_data , maxsize , & context , 1 );
173
+ bt_cursor_t cursor ;
174
+ if (!jl_unw_init (& cursor , & context ))
175
+ return 0 ;
176
+ size_t bt_size = 0 ;
177
+ jl_unw_stepn (& cursor , bt_data , & bt_size , NULL , maxsize , skip + 1 , 1 , 0 );
178
+ return bt_size ;
150
179
}
151
180
152
181
static jl_value_t * array_ptr_void_type JL_ALWAYS_LEAFTYPE = NULL ;
153
- JL_DLLEXPORT jl_value_t * jl_backtrace_from_here (int returnsp )
182
+ // Return backtrace information as an svec of (bt1, bt2, [sp])
183
+ //
184
+ // The stack pointers `sp` are returned only when `returnsp` evaluates to true.
185
+ // bt1 contains raw backtrace entries, while bt2 exists to root any julia
186
+ // objects associated with the entries in bt1.
187
+ //
188
+ // The frame from jl_backtrace_from_here will be skipped; set `skip > 0` to
189
+ // skip additional native frames from the start of the backtrace.
190
+ JL_DLLEXPORT jl_value_t * jl_backtrace_from_here (int returnsp , int skip )
154
191
{
155
192
jl_array_t * ip = NULL ;
156
193
jl_array_t * sp = NULL ;
@@ -168,25 +205,26 @@ JL_DLLEXPORT jl_value_t *jl_backtrace_from_here(int returnsp)
168
205
memset (& context , 0 , sizeof (context ));
169
206
jl_unw_get (& context );
170
207
if (jl_unw_init (& cursor , & context )) {
208
+ // Skip frame for jl_backtrace_from_here itself
209
+ skip += 1 ;
171
210
size_t offset = 0 ;
172
- while (1 ) {
211
+ int have_more_frames = 1 ;
212
+ while (have_more_frames ) {
173
213
jl_array_grow_end (ip , maxincr );
174
214
uintptr_t * sp_ptr = NULL ;
175
215
if (returnsp ) {
176
216
sp_ptr = (uintptr_t * )jl_array_data (sp ) + offset ;
177
217
jl_array_grow_end (sp , maxincr );
178
218
}
179
219
size_t size_incr = 0 ;
180
- int need_more_space = jl_unw_stepn (& cursor , (uintptr_t * )jl_array_data (ip ) + offset ,
181
- & size_incr , sp_ptr , maxincr , 1 );
220
+ have_more_frames = jl_unw_stepn (& cursor , (uintptr_t * )jl_array_data (ip ) + offset ,
221
+ & size_incr , sp_ptr , maxincr , skip , 1 , 0 );
222
+ skip = 0 ;
182
223
offset += size_incr ;
183
- if (!need_more_space ) {
184
- jl_array_del_end (ip , jl_array_len (ip ) - offset );
185
- if (returnsp )
186
- jl_array_del_end (sp , jl_array_len (sp ) - offset );
187
- break ;
188
- }
189
224
}
225
+ jl_array_del_end (ip , jl_array_len (ip ) - offset );
226
+ if (returnsp )
227
+ jl_array_del_end (sp , jl_array_len (sp ) - offset );
190
228
191
229
size_t n = 0 ;
192
230
while (n < jl_array_len (ip )) {
@@ -480,18 +518,14 @@ static int jl_unw_step(bt_cursor_t *cursor, uintptr_t *ip, uintptr_t *sp, uintpt
480
518
}
481
519
482
520
#ifdef LIBOSXUNWIND
483
- int jl_unw_init_dwarf (bt_cursor_t * cursor , bt_context_t * uc )
484
- {
485
- return unw_init_local_dwarf (cursor , uc ) != 0 ;
486
- }
487
- size_t rec_backtrace_ctx_dwarf (uintptr_t * bt_data , size_t maxsize ,
488
- bt_context_t * context , int add_interp_frames )
521
+ NOINLINE size_t rec_backtrace_ctx_dwarf (uintptr_t * bt_data , size_t maxsize ,
522
+ bt_context_t * context , int add_interp_frames )
489
523
{
490
524
size_t bt_size = 0 ;
491
525
bt_cursor_t cursor ;
492
- if (! jl_unw_init_dwarf (& cursor , context ))
526
+ if (unw_init_local_dwarf (& cursor , context ) != UNW_ESUCCESS )
493
527
return 0 ;
494
- jl_unw_stepn (& cursor , bt_data , & bt_size , NULL , maxsize , add_interp_frames );
528
+ jl_unw_stepn (& cursor , bt_data , & bt_size , NULL , maxsize , 0 , add_interp_frames , 1 );
495
529
return bt_size ;
496
530
}
497
531
#endif
0 commit comments