From f33697aa87832f085c2994828fc1f64587a5a651 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 6 May 2020 06:24:47 -0500 Subject: [PATCH] jl_debug_method_invalidation: return a list of instances, signatures, and tags Rather than print debug info, this stores the relevant entities to an array. This facilitates easier analysis of the causes, for example making it possible to automatically walk the hierarchy of related code objects (methods, functions, signature types), perform type-intersection, etc. Analysis code will live outside Julia proper (SnoopCompile and MethodAnalysis are two early "consumers"). --- src/dump.c | 13 +++++--- src/gf.c | 96 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 83 insertions(+), 26 deletions(-) diff --git a/src/dump.c b/src/dump.c index 0e96c025ffbde..5cef3e08c54ab 100644 --- a/src/dump.c +++ b/src/dump.c @@ -2294,7 +2294,7 @@ static void jl_insert_methods(jl_array_t *list) } } -extern int jl_debug_method_invalidation; +extern jl_array_t *_jl_debug_method_invalidation JL_GLOBALLY_ROOTED; // verify that these edges intersect with the same methods as before static void jl_verify_edges(jl_array_t *targets, jl_array_t **pvalids) @@ -2348,7 +2348,8 @@ static void jl_insert_backedges(jl_array_t *list, jl_array_t *targets) // map(enable, ((list[i] => targets[list[i + 1] .* 2]) for i in 1:2:length(list) if all(valids[list[i + 1]]))) size_t i, l = jl_array_len(list); jl_array_t *valids = NULL; - JL_GC_PUSH1(&valids); + jl_value_t *loctag = NULL; + JL_GC_PUSH2(&valids, &loctag); jl_verify_edges(targets, &valids); for (i = 0; i < l; i += 2) { jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(list, i); @@ -2386,9 +2387,11 @@ static void jl_insert_backedges(jl_array_t *list, jl_array_t *targets) } } else { - if (jl_debug_method_invalidation) { - jl_static_show(JL_STDOUT, (jl_value_t*)caller); - jl_uv_puts(JL_STDOUT, "<<<\n", 4); + if (_jl_debug_method_invalidation) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)caller); + loctag = jl_cstr_to_string("insert_backedges"); + jl_gc_wb(_jl_debug_method_invalidation, loctag); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); } } } diff --git a/src/gf.c b/src/gf.c index 183a6c74d7187..f2e883a07ab2c 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1485,17 +1485,33 @@ static void update_max_args(jl_methtable_t *mt, jl_value_t *type) mt->max_args = na; } -JL_DLLEXPORT int jl_debug_method_invalidation = 0; +jl_array_t *_jl_debug_method_invalidation JL_GLOBALLY_ROOTED = NULL; +JL_DLLEXPORT jl_value_t *jl_debug_method_invalidation(int state) +{ + /* After calling with `state = 1`, caller is responsible for + holding a reference to the returned array until this is called + again with `state = 0`. */ + if (state) { + if (_jl_debug_method_invalidation) + return (jl_value_t*) _jl_debug_method_invalidation; + _jl_debug_method_invalidation = jl_alloc_array_1d(jl_array_any_type, 0); + return (jl_value_t*) _jl_debug_method_invalidation; + } + _jl_debug_method_invalidation = NULL; + return jl_nothing; +} // recursively invalidate cached methods that had an edge to a replaced method static void invalidate_method_instance(jl_method_instance_t *replaced, size_t max_world, int depth) { - if (jl_debug_method_invalidation) { - int d0 = depth; - while (d0-- > 0) - jl_uv_puts(JL_STDOUT, " ", 1); - jl_static_show(JL_STDOUT, (jl_value_t*)replaced); - jl_uv_puts(JL_STDOUT, "\n", 1); + if (_jl_debug_method_invalidation) { + jl_value_t *boxeddepth = NULL; + JL_GC_PUSH1(&boxeddepth); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)replaced); + boxeddepth = jl_box_int32(depth); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, boxeddepth); + jl_gc_wb(_jl_debug_method_invalidation, boxeddepth); + JL_GC_POP(); } if (!jl_is_method(replaced->def.method)) return; // shouldn't happen, but better to be safe @@ -1622,10 +1638,14 @@ static int invalidate_mt_cache(jl_typemap_entry_t *oldentry, void *closure0) } } if (intersects) { - if (jl_debug_method_invalidation) { - jl_uv_puts(JL_STDOUT, "-- ", 3); - jl_static_show(JL_STDOUT, (jl_value_t*)mi); - jl_uv_puts(JL_STDOUT, "\n", 1); + if (_jl_debug_method_invalidation) { + jl_value_t *loctag = NULL; + JL_GC_PUSH1(&loctag); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); + loctag = jl_cstr_to_string("invalidate_mt_cache"); + jl_gc_wb(_jl_debug_method_invalidation, loctag); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + JL_GC_POP(); } oldentry->max_world = env->max_world; } @@ -1673,12 +1693,33 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho jl_typemap_visitor(mt->cache, invalidate_mt_cache, (void*)&mt_cache_env); // Invalidate the backedges jl_svec_t *specializations = methodentry->func.method->specializations; + int invalidated = 0; + jl_value_t *loctag = NULL; + JL_GC_PUSH1(&loctag); size_t i, l = jl_svec_len(specializations); for (i = 0; i < l; i++) { jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, i); - if (mi) - invalidate_backedges(mi, methodentry->max_world); + if (mi) { + invalidated = 1; + if (invalidate_backedges(mi, methodentry->max_world)) + if (_jl_debug_method_invalidation) { + if (!loctag) { + loctag = jl_cstr_to_string("jl_method_table_disable"); + jl_gc_wb(_jl_debug_method_invalidation, loctag); + } + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + } + } + } + if (invalidated && _jl_debug_method_invalidation) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)method); + if (!loctag) { + loctag = jl_cstr_to_string("jl_method_table_disable"); + jl_gc_wb(_jl_debug_method_invalidation, loctag); + } + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); } + JL_GC_POP(); JL_UNLOCK(&mt->writelock); } @@ -1693,7 +1734,8 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method method->primary_world = ++jl_world_counter; size_t max_world = method->primary_world - 1; int invalidated = 0; - JL_GC_PUSH1(&oldvalue); + jl_value_t *loctag = NULL; // debug info for invalidation + JL_GC_PUSH2(&oldvalue, &loctag); JL_LOCK(&mt->writelock); // first delete the existing entry (we'll disable it later) struct jl_typemap_assoc search = {(jl_value_t*)type, method->primary_world, NULL, 0, ~(size_t)0}; @@ -1722,6 +1764,8 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method jl_method_instance_t *backedge = (jl_method_instance_t*)backedges[i]; invalidate_method_instance(backedge, max_world, 0); invalidated = 1; + if (_jl_debug_method_invalidation) + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)backedgetyp); } else { backedges[ins++] = backedges[i - 1]; @@ -1767,17 +1811,27 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method for (i = 0; i < l; i++) { jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, i); if (mi != NULL && !jl_has_empty_intersection(type, (jl_value_t*)mi->specTypes)) - if (invalidate_backedges(mi, max_world)) + if (invalidate_backedges(mi, max_world)) { invalidated = 1; + if (_jl_debug_method_invalidation) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); + if (!loctag) { + loctag = jl_cstr_to_string("jl_method_table_insert"); + jl_gc_wb(_jl_debug_method_invalidation, loctag); + } + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + } + } } } } - if (invalidated && jl_debug_method_invalidation) { - jl_uv_puts(JL_STDOUT, ">> ", 3); - jl_static_show(JL_STDOUT, (jl_value_t*)method); - jl_uv_puts(JL_STDOUT, " ", 1); - jl_static_show(JL_STDOUT, (jl_value_t*)type); - jl_uv_puts(JL_STDOUT, "\n", 1); + if (invalidated && _jl_debug_method_invalidation) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)method); + if (!loctag) { + loctag = jl_cstr_to_string("jl_method_table_insert"); + jl_gc_wb(_jl_debug_method_invalidation, loctag); + } + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); } update_max_args(mt, type); JL_UNLOCK(&mt->writelock);