Skip to content

Commit d34ef7e

Browse files
authored
[Mono]: Additional support for signal async safe stack-walks. (#113645)
* [Mono]: Fix additional stack-walks to be async safe. Mono's stack-walker can be signal/async safe when needed, making sure it won't allocate additional memory or taking internal runtime locks. It is however up to the caller of the stack walking API's to decide if it should be signal and/or async safe. Currently this is controlled using two different flags, the MONO_UNWIND_SIGNAL_SAFE as well as mono_thread_info_is_async_context. This is problematic since callers wants signal safe stack-walking but since not both are set, it will not behave fully signal safe. dotnet/android#9365 hit a couple of scenarios described here: dotnet/android#9365 (comment) that ends up deadlocking due to the fact that they did stack-walking from within a signal handler and deadlocked or dumping stack on suspended thread holding runtime loader lock, but without making the stack-walk async safe. Fix makes sure that calls to stack-walk API's can be made signal and/or async safe and that identified areas uses the correct set of flags given state of threads when stack-walking. * Add signal async safe stack unwind option. * Assert that walk_stack_full_llvm_only is only called in llvm only mode. * Correct some bool usage. * Make signal safe unwind option, signal async safe. Mono's current MONO_UNWIND_SIGNAL_SAFE was not fully signal safe since it was not async safe, that could lead to taking loader lock. This will fix MONO_UNWIND_SIGNAL_SAFE to be signal asycn safe, it will also change current use of MONO_UNWIND_SIGNAL_SAFE to MONO_UNWIND_NONE since they where equal before this fix, meaning old calls using MONO_UNWIND_SIGNAL_SAFE will behave identical using MONO_UNWIND_NONE so no regression.
1 parent f285289 commit d34ef7e

File tree

5 files changed

+113
-91
lines changed

5 files changed

+113
-91
lines changed

src/mono/mono/eventpipe/ep-rt-mono-runtime-provider.c

+14-18
Original file line numberDiff line numberDiff line change
@@ -1184,31 +1184,27 @@ ep_rt_mono_walk_managed_stack_for_thread (
11841184
stack_walk_data.safe_point_frame = false;
11851185
stack_walk_data.runtime_invoke_frame = false;
11861186

1187-
bool restore_async_context = FALSE;
1188-
bool prevent_profiler_event_recursion = FALSE;
1187+
bool prevent_profiler_event_recursion = false;
1188+
MonoUnwindOptions unwind_options = MONO_UNWIND_NONE;
11891189
EventPipeMonoThreadData *thread_data = ep_rt_mono_thread_data_get_or_create ();
11901190
if (thread_data) {
11911191
prevent_profiler_event_recursion = thread_data->prevent_profiler_event_recursion;
1192-
if (prevent_profiler_event_recursion && !mono_thread_info_is_async_context ()) {
1192+
if (prevent_profiler_event_recursion) {
11931193
// Running stackwalk in async context mode is currently the only way to prevent
11941194
// unwinder to NOT load additional classes during stackwalk, making it signal unsafe and
11951195
// potential triggering uncontrolled recursion in profiler class loading event.
1196-
mono_thread_info_set_is_async_context (TRUE);
1197-
restore_async_context = TRUE;
1196+
unwind_options = MONO_UNWIND_SIGNAL_SAFE;
11981197
}
1199-
thread_data->prevent_profiler_event_recursion = TRUE;
1198+
thread_data->prevent_profiler_event_recursion = true;
12001199
}
12011200

12021201
if (thread == ep_rt_thread_get_handle () && mono_get_eh_callbacks ()->mono_walk_stack_with_ctx)
1203-
mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (walk_managed_stack_for_thread_callback, NULL, MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data);
1202+
mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (walk_managed_stack_for_thread_callback, NULL, unwind_options, &stack_walk_data);
12041203
else if (mono_get_eh_callbacks ()->mono_walk_stack_with_state)
1205-
mono_get_eh_callbacks ()->mono_walk_stack_with_state (walk_managed_stack_for_thread_callback, mono_thread_info_get_suspend_state (thread), MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data);
1204+
mono_get_eh_callbacks ()->mono_walk_stack_with_state (walk_managed_stack_for_thread_callback, mono_thread_info_get_suspend_state (thread), unwind_options, &stack_walk_data);
12061205

1207-
if (thread_data) {
1208-
if (restore_async_context)
1209-
mono_thread_info_set_is_async_context (FALSE);
1206+
if (thread_data)
12101207
thread_data->prevent_profiler_event_recursion = prevent_profiler_event_recursion;
1211-
}
12121208

12131209
return true;
12141210
}
@@ -1236,10 +1232,10 @@ ep_rt_mono_sample_profiler_write_sampling_event_for_threads (
12361232

12371233
mono_stop_world (MONO_THREAD_INFO_FLAGS_NO_GC);
12381234

1239-
bool restore_async_context = FALSE;
1235+
bool restore_async_context = false;
12401236
if (!mono_thread_info_is_async_context ()) {
12411237
mono_thread_info_set_is_async_context (TRUE);
1242-
restore_async_context = TRUE;
1238+
restore_async_context = true;
12431239
}
12441240

12451241
// Record all info needed in sample events while runtime is suspended, must be async safe.
@@ -1361,7 +1357,7 @@ method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *c
13611357
data->stack_walk_data.runtime_invoke_frame = false;
13621358
ep_stack_contents_reset (&data->stack_contents);
13631359

1364-
mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (sample_profiler_walk_managed_stack_for_thread_callback, &ctx->context, MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data);
1360+
mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (sample_profiler_walk_managed_stack_for_thread_callback, &ctx->context, MONO_UNWIND_NONE, &stack_walk_data);
13651361
if (data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && (data->stack_walk_data.safe_point_frame || data->stack_walk_data.runtime_invoke_frame)) {
13661362
data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED;
13671363
}
@@ -2504,7 +2500,7 @@ write_event_exception_thrown (MonoObject *obj)
25042500
exception_message = g_strdup ("");
25052501

25062502
if (mono_get_eh_callbacks ()->mono_walk_stack_with_ctx)
2507-
mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (get_exception_ip_func, NULL, MONO_UNWIND_SIGNAL_SAFE, (void *)&ip);
2503+
mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (get_exception_ip_func, NULL, MONO_UNWIND_NONE, (void *)&ip);
25082504

25092505
type_name = mono_type_get_name_full (m_class_get_byval_arg (mono_object_class (obj)), MONO_TYPE_NAME_FORMAT_IL);
25102506

@@ -3024,13 +3020,13 @@ class_loading_callback (
30243020
MonoProfiler *prof,
30253021
MonoClass *klass)
30263022
{
3027-
bool prevent_profiler_event_recursion = FALSE;
3023+
bool prevent_profiler_event_recursion = false;
30283024
EventPipeMonoThreadData *thread_data = ep_rt_mono_thread_data_get_or_create ();
30293025
if (thread_data) {
30303026
// Prevent additional class loading to happen recursively as part of fire TypeLoadStart event.
30313027
// Additional class loading can happen as part of capturing callstack for TypeLoadStart event.
30323028
prevent_profiler_event_recursion = thread_data->prevent_profiler_event_recursion;
3033-
thread_data->prevent_profiler_event_recursion = TRUE;
3029+
thread_data->prevent_profiler_event_recursion = true;
30343030
}
30353031

30363032
write_event_type_load_start (m_class_get_byval_arg (klass));

src/mono/mono/metadata/threads.c

+2-10
Original file line numberDiff line numberDiff line change
@@ -2941,20 +2941,13 @@ collect_frame (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
29412941
return FALSE;
29422942
}
29432943

2944-
/* This needs to be async safe */
29452944
static SuspendThreadResult
29462945
get_thread_dump (MonoThreadInfo *info, gpointer ud)
29472946
{
29482947
ThreadDumpUserData *user_data = (ThreadDumpUserData *)ud;
29492948
MonoInternalThread *thread = user_data->thread;
29502949

2951-
#if 0
2952-
/* This no longer works with remote unwinding */
2953-
g_string_append_printf (text, " tid=0x%p this=0x%p ", (gpointer)(gsize)thread->tid, thread);
2954-
mono_thread_internal_describe (thread, text);
2955-
g_string_append (text, "\n");
2956-
#endif
2957-
2950+
/* This needs to be async safe */
29582951
if (thread == mono_thread_internal_current ())
29592952
mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (collect_frame, NULL, MONO_UNWIND_SIGNAL_SAFE, ud);
29602953
else
@@ -4107,9 +4100,8 @@ mono_thread_info_get_last_managed (MonoThreadInfo *info)
41074100
* The suspended thread might be holding runtime locks. Make sure we don't try taking
41084101
* any runtime locks while unwinding.
41094102
*/
4110-
mono_thread_info_set_is_async_context (TRUE);
41114103
mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
4112-
mono_thread_info_set_is_async_context (FALSE);
4104+
41134105
return ji;
41144106
}
41154107

src/mono/mono/mini/mini-exceptions.c

+89-60
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ static MonoFtnPtrEHCallback ftnptr_eh_callback;
126126
*/
127127
int mono_llvmonly_do_unwind_flag;
128128

129-
static void mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data, gboolean crash_context);
129+
static void mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data);
130130
static void mono_raise_exception_with_ctx (MonoException *exc, MonoContext *ctx);
131131
static void mono_runtime_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data);
132132
static gboolean mono_current_thread_has_handle_block_guard (void);
@@ -160,7 +160,7 @@ static gpointer
160160
mono_thread_get_managed_sp (void)
161161
{
162162
gpointer addr = NULL;
163-
mono_walk_stack (first_managed, MONO_UNWIND_SIGNAL_SAFE, &addr);
163+
mono_walk_stack (first_managed, MONO_UNWIND_NONE, &addr);
164164
return addr;
165165
}
166166

@@ -1224,7 +1224,7 @@ mono_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnw
12241224
start_ctx = &extra_ctx;
12251225
}
12261226

1227-
mono_walk_stack_full (func, start_ctx, thread->jit_data, mono_get_lmf (), unwind_options, user_data, FALSE);
1227+
mono_walk_stack_full (func, start_ctx, thread->jit_data, mono_get_lmf (), unwind_options, user_data);
12281228
}
12291229

12301230
/**
@@ -1241,8 +1241,9 @@ void
12411241
mono_walk_stack_with_state (MonoJitStackWalk func, MonoThreadUnwindState *state, MonoUnwindOptions unwind_options, void *user_data)
12421242
{
12431243
MonoThreadUnwindState extra_state;
1244+
12441245
if (!state) {
1245-
g_assert (!mono_thread_info_is_async_context ());
1246+
g_assert (!(unwind_options & MONO_UNWIND_SIGNAL_SAFE) && !mono_thread_info_is_async_context ());
12461247
if (!mono_thread_state_init_from_current (&extra_state))
12471248
return;
12481249
state = &extra_state;
@@ -1258,7 +1259,7 @@ mono_walk_stack_with_state (MonoJitStackWalk func, MonoThreadUnwindState *state,
12581259
&state->ctx,
12591260
(MonoJitTlsData *)state->unwind_data [MONO_UNWIND_DATA_JIT_TLS],
12601261
(MonoLMF *)state->unwind_data [MONO_UNWIND_DATA_LMF],
1261-
unwind_options, user_data, FALSE);
1262+
unwind_options, user_data);
12621263
}
12631264

12641265
void
@@ -1270,22 +1271,49 @@ mono_walk_stack (MonoJitStackWalk func, MonoUnwindOptions options, void *user_da
12701271
mono_walk_stack_with_state (func, &state, options, user_data);
12711272
}
12721273

1273-
/**
1274-
* mono_walk_stack_full:
1275-
* \param func callback to call for each stack frame
1276-
* \param unwind_options what extra information the unwinder should gather
1277-
* \param start_ctx starting state of the stack walk, can be NULL.
1278-
* \param thread the thread whose stack to walk, can be NULL to use the current thread
1279-
* \param lmf the LMF of \p thread, can be NULL to use the LMF of the current thread
1280-
* \param user_data data passed to the callback
1281-
* \param crash_context tells us that we're in a context where it's not safe to lock or allocate
1282-
* This function walks the stack of a thread, starting from the state
1283-
* represented by \p start_ctx. For each frame the callback
1284-
* function is called with the relevant info. The walk ends when no more
1285-
* managed stack frames are found or when the callback returns a TRUE value.
1286-
*/
1274+
#ifndef TARGET_WASM
12871275
static void
1288-
mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data, gboolean crash_context)
1276+
walk_stack_full_llvm_only(MonoJitStackWalk func, MonoContext* start_ctx, MonoJitTlsData* jit_tls, MonoLMF* lmf, MonoUnwindOptions unwind_options, gpointer user_data)
1277+
{
1278+
GSList *l, *ips;
1279+
StackFrameInfo frame;
1280+
1281+
g_assert(mono_llvm_only);
1282+
1283+
memset (&frame, 0, sizeof (StackFrameInfo));
1284+
1285+
// TODO: Fix async support.
1286+
if (mono_thread_info_is_async_context ())
1287+
return;
1288+
1289+
ips = get_unwind_backtrace ();
1290+
for (l = ips; l; l = l->next) {
1291+
guint8 *ip = (guint8*)l->data;
1292+
memset (&frame, 0, sizeof (StackFrameInfo));
1293+
frame.ji = mini_jit_info_table_find (ip);
1294+
if (!frame.ji || frame.ji->is_trampoline)
1295+
continue;
1296+
frame.type = FRAME_TYPE_MANAGED;
1297+
frame.method = jinfo_get_method (frame.ji);
1298+
// FIXME: Cannot lookup the actual method
1299+
frame.actual_method = frame.method;
1300+
if (frame.type == FRAME_TYPE_MANAGED) {
1301+
if (!frame.method->wrapper_type || frame.method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
1302+
frame.managed = TRUE;
1303+
}
1304+
frame.native_offset = GPTRDIFF_TO_INT (ip - (guint8*)frame.ji->code_start);
1305+
frame.il_offset = -1;
1306+
1307+
if (func (&frame, NULL, user_data))
1308+
break;
1309+
}
1310+
g_slist_free (ips);
1311+
return;
1312+
}
1313+
#endif
1314+
1315+
static void
1316+
walk_stack_full(MonoJitStackWalk func, MonoContext* start_ctx, MonoJitTlsData* jit_tls, MonoLMF* lmf, MonoUnwindOptions unwind_options, gpointer user_data)
12891317
{
12901318
gint il_offset;
12911319
MonoContext ctx, new_ctx;
@@ -1299,39 +1327,6 @@ mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsD
12991327

13001328
memset (&frame, 0, sizeof (StackFrameInfo));
13011329

1302-
#ifndef TARGET_WASM
1303-
if (mono_llvm_only) {
1304-
GSList *l, *ips;
1305-
1306-
if (async)
1307-
return;
1308-
1309-
ips = get_unwind_backtrace ();
1310-
for (l = ips; l; l = l->next) {
1311-
guint8 *ip = (guint8*)l->data;
1312-
memset (&frame, 0, sizeof (StackFrameInfo));
1313-
frame.ji = mini_jit_info_table_find (ip);
1314-
if (!frame.ji || frame.ji->is_trampoline)
1315-
continue;
1316-
frame.type = FRAME_TYPE_MANAGED;
1317-
frame.method = jinfo_get_method (frame.ji);
1318-
// FIXME: Cannot lookup the actual method
1319-
frame.actual_method = frame.method;
1320-
if (frame.type == FRAME_TYPE_MANAGED) {
1321-
if (!frame.method->wrapper_type || frame.method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
1322-
frame.managed = TRUE;
1323-
}
1324-
frame.native_offset = GPTRDIFF_TO_INT (ip - (guint8*)frame.ji->code_start);
1325-
frame.il_offset = -1;
1326-
1327-
if (func (&frame, NULL, user_data))
1328-
break;
1329-
}
1330-
g_slist_free (ips);
1331-
return;
1332-
}
1333-
#endif
1334-
13351330
if (!start_ctx) {
13361331
g_warning ("start_ctx required for stack walk");
13371332
return;
@@ -1372,15 +1367,15 @@ mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsD
13721367
MonoDebugSourceLocation *source = NULL;
13731368

13741369
// Don't do this when we can be in a signal handler
1375-
if (!crash_context)
1370+
if (!async)
13761371
source = mono_debug_lookup_source_location (jinfo_get_method (frame.ji), frame.native_offset, NULL);
13771372
if (source) {
13781373
il_offset = source->il_offset;
13791374
} else {
13801375
MonoSeqPointInfo *seq_points = NULL;
13811376

13821377
// It's more reliable to look into the global cache if possible
1383-
if (crash_context)
1378+
if (async)
13841379
seq_points = (MonoSeqPointInfo *) frame.ji->seq_points;
13851380
else
13861381
seq_points = mono_get_seq_points (jinfo_get_method (frame.ji));
@@ -1422,6 +1417,41 @@ mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsD
14221417
}
14231418
}
14241419

1420+
/**
1421+
* mono_walk_stack_full:
1422+
* \param func callback to call for each stack frame
1423+
* \param unwind_options what extra information the unwinder should gather
1424+
* \param start_ctx starting state of the stack walk, can be NULL.
1425+
* \param thread the thread whose stack to walk, can be NULL to use the current thread
1426+
* \param lmf the LMF of \p thread, can be NULL to use the LMF of the current thread
1427+
* \param user_data data passed to the callback
1428+
* This function walks the stack of a thread, starting from the state
1429+
* represented by \p start_ctx. For each frame the callback
1430+
* function is called with the relevant info. The walk ends when no more
1431+
* managed stack frames are found or when the callback returns a TRUE value.
1432+
*/
1433+
static void
1434+
mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data)
1435+
{
1436+
gboolean restore_async_context = FALSE;
1437+
if ((unwind_options & MONO_UNWIND_SIGNAL_SAFE) && !mono_thread_info_is_async_context ()) {
1438+
mono_thread_info_set_is_async_context (TRUE);
1439+
restore_async_context = TRUE;
1440+
}
1441+
1442+
#ifndef TARGET_WASM
1443+
if (mono_llvm_only)
1444+
walk_stack_full_llvm_only (func, start_ctx, jit_tls, lmf, unwind_options, user_data);
1445+
else
1446+
walk_stack_full (func, start_ctx, jit_tls, lmf, unwind_options, user_data);
1447+
#else
1448+
walk_stack_full (func, start_ctx, jit_tls, lmf, unwind_options, user_data);
1449+
#endif
1450+
1451+
if (restore_async_context)
1452+
mono_thread_info_set_is_async_context (FALSE);
1453+
}
1454+
14251455
MonoBoolean
14261456
mono_get_frame_info (gint32 skip,
14271457
MonoMethod **out_method,
@@ -2976,7 +3006,8 @@ mono_handle_native_crash (const char *signal, MonoContext *mctx, MONO_SIG_HANDLE
29763006
g_async_safe_printf ("\tManaged Stacktrace:\n");
29773007
g_async_safe_printf ("=================================================================\n");
29783008

2979-
mono_walk_stack_full (print_stack_frame_signal_safe, mctx, jit_tls, mono_get_lmf (), MONO_UNWIND_LOOKUP_IL_OFFSET, NULL, TRUE);
3009+
mono_walk_stack_full (print_stack_frame_signal_safe, mctx, jit_tls, mono_get_lmf (), MONO_UNWIND_LOOKUP_IL_OFFSET | MONO_UNWIND_SIGNAL_SAFE, NULL);
3010+
29803011
g_async_safe_printf ("=================================================================\n");
29813012
}
29823013

@@ -3146,15 +3177,13 @@ mono_install_handler_block_guard (MonoThreadUnwindState *ctx)
31463177
MonoJitTlsData *jit_tls = (MonoJitTlsData *)ctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS];
31473178

31483179
/* Guard against a null MonoJitTlsData. This can happens if the thread receives the
3149-
* interrupt signal before the JIT has time to initialize its TLS data for the given thread.
3180+
* interrupt signal before the JIT has time to initialize its TLS data for the given thread.
31503181
*/
31513182
if (!jit_tls || jit_tls->handler_block)
31523183
return FALSE;
31533184

31543185
/* Do an async safe stack walk */
3155-
mono_thread_info_set_is_async_context (TRUE);
3156-
mono_walk_stack_with_state (find_last_handler_block, ctx, MONO_UNWIND_NONE, &data);
3157-
mono_thread_info_set_is_async_context (FALSE);
3186+
mono_walk_stack_with_state (find_last_handler_block, ctx, MONO_UNWIND_SIGNAL_SAFE, &data);
31583187

31593188
if (!data.ji)
31603189
return FALSE;

src/mono/mono/mini/mini-posix.c

+7-2
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,16 @@ MONO_SIG_HANDLER_FUNC (static, profiler_signal_handler)
291291

292292
int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
293293

294-
mono_thread_info_set_is_async_context (TRUE);
294+
gboolean restore_async_context = FALSE;
295+
if (!mono_thread_info_is_async_context ()) {
296+
mono_thread_info_set_is_async_context (TRUE);
297+
restore_async_context = TRUE;
298+
}
295299

296300
MONO_PROFILER_RAISE (sample_hit, ((const mono_byte*)mono_arch_ip_from_context (ctx), ctx));
297301

298-
mono_thread_info_set_is_async_context (FALSE);
302+
if (restore_async_context)
303+
mono_thread_info_set_is_async_context (FALSE);
299304

300305
mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
301306

src/mono/mono/utils/mono-stack-unwinding.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ typedef enum {
4848
* callback.
4949
*/
5050
MONO_UNWIND_REG_LOCATIONS = 0x4,
51+
MONO_UNWIND_SIGNAL_SAFE = 0x8,
5152
MONO_UNWIND_DEFAULT = MONO_UNWIND_LOOKUP_ACTUAL_METHOD,
52-
MONO_UNWIND_SIGNAL_SAFE = MONO_UNWIND_NONE,
5353
MONO_UNWIND_LOOKUP_ALL = MONO_UNWIND_LOOKUP_IL_OFFSET | MONO_UNWIND_LOOKUP_ACTUAL_METHOD,
5454
} MonoUnwindOptions;
5555

0 commit comments

Comments
 (0)