From 129ca06a00269c4e6fb965b2f942f3628d02d702 Mon Sep 17 00:00:00 2001 From: Rodrigo Kumpera Date: Thu, 13 Jul 2017 16:25:16 -0700 Subject: [PATCH 1/8] [profiler] Rework GC roots reporting. This change re-implement GC roots reporting in a way that allows users to correctly track the source of a GC root. The new design is based on emitting GC root information upfront and only report addresses during heap dumps. When a GC root is registered we emit an event that is an address range, the root kind, a key and a text description. A decoder can use this information to match a reported root address with all registration addresses to figure out what they mean. A GC Root key is used to further its meaning. For example, if kind is thread, key is a tid, if kind is static variables, key is the class pointer and so on. The build of the change is introducing this key argument across all root registration code and rework our root reporting code to encode their addresses. Some roots use pseudo-addresses when they are not registrable. This is the case of thread stacks/registers and the finalizer queue. Finally, root reporting was changed to happen only once per collection and at the end, leading to correct and useful data being produced. --- .../Mono.Profiler.Log/LogEnums.cs | 21 ++ .../Mono.Profiler.Log/LogEventVisitor.cs | 8 + .../Mono.Profiler.Log/LogEvents.cs | 26 +- .../Mono.Profiler.Log/LogProcessor.cs | 29 +- mono/metadata/boehm-gc.c | 20 +- mono/metadata/custom-attrs.c | 2 +- mono/metadata/domain.c | 34 +- mono/metadata/gc-internals.h | 16 +- mono/metadata/handle.c | 2 +- mono/metadata/marshal.c | 2 +- mono/metadata/mono-conc-hash.c | 6 +- mono/metadata/mono-gc.h | 4 +- mono/metadata/mono-hash.c | 10 +- mono/metadata/mono-ptr-array.h | 4 +- mono/metadata/null-gc.c | 11 +- mono/metadata/object.c | 10 +- mono/metadata/profiler-events.h | 15 +- mono/metadata/profiler-private.h | 10 +- mono/metadata/profiler.c | 13 + mono/metadata/profiler.h | 23 +- mono/metadata/sgen-client-mono.h | 28 +- mono/metadata/sgen-mono.c | 338 +++++++++++++----- mono/metadata/sre-save.c | 2 +- mono/metadata/threadpool-io.c | 2 +- mono/metadata/threads.c | 38 +- mono/mini/debugger-agent.c | 2 +- mono/mini/tasklets.c | 2 +- mono/profiler/log.c | 55 ++- mono/profiler/log.h | 27 +- mono/profiler/mprof-report.c | 92 ++++- mono/sgen/sgen-client.h | 19 +- mono/sgen/sgen-descriptor.h | 1 + mono/sgen/sgen-gc.c | 27 +- mono/sgen/sgen-gc.h | 3 +- mono/sgen/sgen-gchandles.c | 31 +- mono/sgen/sgen-marksweep.c | 1 + mono/sgen/sgen-pinning.c | 2 + 37 files changed, 686 insertions(+), 250 deletions(-) diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEnums.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEnums.cs index 0dbc72eb003e8..4bdacf31eae24 100644 --- a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEnums.cs +++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEnums.cs @@ -55,6 +55,8 @@ enum LogEventType { HeapEnd = 1 << 4, HeapObject = 2 << 4, HeapRoots = 3 << 4, + HeapRootRegister = 4 << 4, + HeapRootUnregister = 5 << 4, SampleHit = 0 << 4, SampleUnmanagedSymbol = 1 << 4, @@ -211,4 +213,23 @@ public enum LogHeapshotMode { Milliseconds = 3, Collections = 4, } + + // mono/metadata/mono-gc.h : MonoGCRootSource + public enum LogHeapRootSource { + External = 0, + Stack = 1, + FinalizerQueue = 2, + StaticVariable = 3, + ThreadLocalVariable = 4, + ContextLocalVariable = 5, + GCHandle = 6, + JIT = 7, + Threading = 8, + AppDomain = 9, + Reflection = 10, + Marshal = 11, + ThreadPool = 12, + Debugger = 13, + RuntimeHandle = 14, + } } diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEventVisitor.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEventVisitor.cs index 129a1284bf3e8..e205057dfa28c 100644 --- a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEventVisitor.cs +++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEventVisitor.cs @@ -94,6 +94,14 @@ public virtual void Visit (HeapRootsEvent ev) { } + public virtual void Visit (HeapRootRegisterEvent ev) + { + } + + public virtual void Visit (HeapRootUnregisterEvent ev) + { + } + public virtual void Visit (GCEvent ev) { } diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs index 8bdd5d7ab59b9..fe05735c93c61 100644 --- a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs +++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs @@ -252,16 +252,34 @@ public sealed class HeapRootsEvent : LogEvent { public struct HeapRoot { + public long AddressPointer { get; internal set; } + public long ObjectPointer { get; internal set; } + } - public LogHeapRootAttributes Attributes { get; internal set; } + public IReadOnlyList Roots { get; internal set; } - public long ExtraInfo { get; internal set; } + internal override void Accept (LogEventVisitor visitor) + { + visitor.Visit (this); } + } - public long MaxGenerationCollectionCount { get; internal set; } + public sealed class HeapRootRegisterEvent : LogEvent { + public long Start { get; internal set; } + public long Size { get; internal set; } + public LogHeapRootSource Kind { get; internal set; } + public long Key { get; internal set; } + public string Message { get; internal set; } - public IReadOnlyList Roots { get; internal set; } + internal override void Accept (LogEventVisitor visitor) + { + visitor.Visit (this); + } + } + + public sealed class HeapRootUnregisterEvent : LogEvent { + public long Start { get; internal set; } internal override void Accept (LogEventVisitor visitor) { diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs index 2b205e13318b0..e6cf6852b73c4 100644 --- a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs +++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs @@ -354,18 +354,13 @@ LogEvent ReadEvent () return ev; } case LogEventType.HeapRoots: { - // TODO: This entire event makes no sense. - var ev = new HeapRootsEvent (); var list = new HeapRootsEvent.HeapRoot [(int) Reader.ReadULeb128 ()]; - ev.MaxGenerationCollectionCount = (long) Reader.ReadULeb128 (); - for (var i = 0; i < list.Length; i++) { list [i] = new HeapRootsEvent.HeapRoot { - ObjectPointer = ReadObject (), - Attributes = (LogHeapRootAttributes) Reader.ReadByte (), - ExtraInfo = (long) Reader.ReadULeb128 (), + AddressPointer = ReadPointer (), + ObjectPointer = ReadObject () }; } @@ -373,6 +368,24 @@ LogEvent ReadEvent () return ev; } + case LogEventType.HeapRootRegister: { + var ev = new HeapRootRegisterEvent () { + Start = ReadPointer (), + Size = (long)Reader.ReadULeb128 (), + Kind = (LogHeapRootSource)Reader.ReadByte (), + Key = ReadPointer (), + Message = Reader.ReadCString () + }; + + return ev; + } + case LogEventType.HeapRootUnregister: { + var ev = new HeapRootUnregisterEvent () { + Start = ReadPointer () + }; + + return ev; + } default: throw new LogException ($"Invalid extended event type ({extType})."); } @@ -547,7 +560,7 @@ long ReadPointer () long ReadObject () { - return Reader.ReadSLeb128 () + _bufferHeader.ObjectBase << 3; + return (Reader.ReadSLeb128 () + _bufferHeader.ObjectBase) << 3; } long ReadMethod () diff --git a/mono/metadata/boehm-gc.c b/mono/metadata/boehm-gc.c index bd853b21eb9b4..b4ed69d8ae5c4 100644 --- a/mono/metadata/boehm-gc.c +++ b/mono/metadata/boehm-gc.c @@ -544,21 +544,21 @@ register_root (gpointer arg) } int -mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg) +mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg) { RootData root_data; root_data.start = start; /* Boehm root processing requires one byte past end of region to be scanned */ root_data.end = start + size + 1; GC_call_with_alloc_lock (register_root, &root_data); - + MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte*)start, size, source, key, msg)); return TRUE; } int -mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg) +mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg) { - return mono_gc_register_root (start, size, descr, source, msg); + return mono_gc_register_root (start, size, descr, source, key, msg); } static gpointer @@ -573,6 +573,7 @@ void mono_gc_deregister_root (char* addr) { GC_call_with_alloc_lock (deregister_root, addr); + MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte*)addr)); } static void @@ -668,14 +669,17 @@ mono_gc_make_root_descr_all_refs (int numbits) } void* -mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg) +mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg) { - return GC_MALLOC_UNCOLLECTABLE (size); + void *start = GC_MALLOC_UNCOLLECTABLE (size); + MONO_PROFILER_RAISE (gc_root_register, (start, size, source, key, msg)); + return start; } void mono_gc_free_fixed (void* addr) { + MONO_PROFILER_RAISE (gc_root_unregister, (addr)); GC_FREE (addr); } @@ -1634,7 +1638,7 @@ handle_data_alloc_entries (HandleData *handles) handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size); handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size); } else { - handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table"); + handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "gc handles table"); } handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT); } @@ -1701,7 +1705,7 @@ handle_data_grow (HandleData *handles, gboolean track) handles->domain_ids = domain_ids; } else { gpointer *entries; - entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table"); + entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "gc handles table"); mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size); mono_gc_free_fixed (handles->entries); handles->entries = entries; diff --git a/mono/metadata/custom-attrs.c b/mono/metadata/custom-attrs.c index 0fecdbd66ce74..fddf243320cec 100644 --- a/mono/metadata/custom-attrs.c +++ b/mono/metadata/custom-attrs.c @@ -619,7 +619,7 @@ create_custom_attr (MonoImage *image, MonoMethod *method, const guchar *data, gu memset (params, 0, sizeof (void*) * sig->param_count); } else { /* Allocate using GC so it gets GC tracking */ - params = (void **)mono_gc_alloc_fixed (sig->param_count * sizeof (void*), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_REFLECTION, "custom attribute parameters"); + params = (void **)mono_gc_alloc_fixed (sig->param_count * sizeof (void*), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_REFLECTION, NULL, "custom attribute parameters"); } /* skip prolog */ diff --git a/mono/metadata/domain.c b/mono/metadata/domain.c index 2a97148f212c4..25a6a89c8eb2a 100644 --- a/mono/metadata/domain.c +++ b/mono/metadata/domain.c @@ -292,6 +292,24 @@ mono_ptrarray_hash (gpointer *s) return hash; } +//g_malloc on sgen and mono_gc_alloc_fixed on boehm +static void* +gc_alloc_fixed_non_heap (size_t size) +{ + if (mono_gc_is_moving ()) + return g_malloc0 (size); + else + return mono_gc_alloc_fixed (appdomain_list_size * sizeof (void*), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, NULL, "Misc AppDomain Memory"); +} + +static void +gc_free_fixed_no_heap (void *ptr) +{ + if (mono_gc_is_moving ()) + return g_free (ptr); + else + return mono_gc_free_fixed (ptr); +} /* * Allocate an id for domain and set domain->domain_id. * LOCKING: must be called while holding appdomains_mutex. @@ -306,7 +324,8 @@ domain_id_alloc (MonoDomain *domain) int id = -1, i; if (!appdomains_list) { appdomain_list_size = 2; - appdomains_list = (MonoDomain **)mono_gc_alloc_fixed (appdomain_list_size * sizeof (void*), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, "domains list"); + appdomains_list = (MonoDomain **)gc_alloc_fixed_non_heap (appdomain_list_size * sizeof (void*)); + } for (i = appdomain_next; i < appdomain_list_size; ++i) { if (!appdomains_list [i]) { @@ -328,9 +347,9 @@ domain_id_alloc (MonoDomain *domain) if (new_size >= (1 << 16)) g_assert_not_reached (); id = appdomain_list_size; - new_list = (MonoDomain **)mono_gc_alloc_fixed (new_size * sizeof (void*), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, "domains list"); + new_list = (MonoDomain **)gc_alloc_fixed_non_heap (new_size * sizeof (void*)); memcpy (new_list, appdomains_list, appdomain_list_size * sizeof (void*)); - mono_gc_free_fixed (appdomains_list); + gc_free_fixed_no_heap (appdomains_list); appdomains_list = new_list; appdomain_list_size = new_size; } @@ -387,10 +406,9 @@ mono_domain_create (void) mono_appdomains_unlock (); #ifdef HAVE_BOEHM_GC - domain = (MonoDomain *)mono_gc_alloc_fixed (sizeof (MonoDomain), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, "domain object"); + domain = (MonoDomain *)mono_gc_alloc_fixed (sizeof (MonoDomain), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, NULL, "domain object"); #else - domain = (MonoDomain *)mono_gc_alloc_fixed (sizeof (MonoDomain), domain_gc_desc, MONO_ROOT_SOURCE_DOMAIN, "domain object"); - mono_gc_register_root ((char*)&(domain->MONO_DOMAIN_FIRST_GC_TRACKED), G_STRUCT_OFFSET (MonoDomain, MONO_DOMAIN_LAST_GC_TRACKED) - G_STRUCT_OFFSET (MonoDomain, MONO_DOMAIN_FIRST_GC_TRACKED), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, "misc domain fields"); + domain = (MonoDomain *)mono_gc_alloc_fixed (sizeof (MonoDomain), domain_gc_desc, MONO_ROOT_SOURCE_DOMAIN, NULL, "domain object"); #endif domain->shadow_serial = shadow_serial; domain->domain = NULL; @@ -952,7 +970,7 @@ mono_domain_foreach (MonoDomainFunc func, gpointer user_data) */ mono_appdomains_lock (); size = appdomain_list_size; - copy = (MonoDomain **)mono_gc_alloc_fixed (appdomain_list_size * sizeof (void*), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, "temporary domains list"); + copy = (MonoDomain **)gc_alloc_fixed_non_heap (appdomain_list_size * sizeof (void*)); memcpy (copy, appdomains_list, appdomain_list_size * sizeof (void*)); mono_appdomains_unlock (); @@ -961,7 +979,7 @@ mono_domain_foreach (MonoDomainFunc func, gpointer user_data) func (copy [i], user_data); } - mono_gc_free_fixed (copy); + gc_free_fixed_no_heap (copy); } /* FIXME: maybe we should integrate this with mono_assembly_open? */ diff --git a/mono/metadata/gc-internals.h b/mono/metadata/gc-internals.h index 76e73073dcca8..52d7c1d3b4d96 100644 --- a/mono/metadata/gc-internals.h +++ b/mono/metadata/gc-internals.h @@ -22,7 +22,7 @@ #define mono_domain_finalizers_unlock(domain) mono_os_mutex_unlock (&(domain)->finalizable_objects_hash_lock); /* Register a memory area as a conservatively scanned GC root */ -#define MONO_GC_REGISTER_ROOT_PINNING(x,src,msg) mono_gc_register_root ((char*)&(x), sizeof(x), MONO_GC_DESCRIPTOR_NULL, (src), (msg)) +#define MONO_GC_REGISTER_ROOT_PINNING(x,src,key,msg) mono_gc_register_root ((char*)&(x), sizeof(x), MONO_GC_DESCRIPTOR_NULL, (src), (key), (msg)) #define MONO_GC_UNREGISTER_ROOT(x) mono_gc_deregister_root ((char*)&(x)) @@ -34,18 +34,18 @@ #define MONO_GC_ROOT_DESCR_FOR_FIXED(n) (mono_gc_is_moving () ? mono_gc_make_root_descr_all_refs (0) : MONO_GC_DESCRIPTOR_NULL) /* Register a memory location holding a single object reference as a GC root */ -#define MONO_GC_REGISTER_ROOT_SINGLE(x,src,msg) do { \ +#define MONO_GC_REGISTER_ROOT_SINGLE(x,src,key,msg) do { \ g_assert (sizeof (x) == sizeof (MonoObject*)); \ - mono_gc_register_root ((char*)&(x), sizeof(MonoObject*), mono_gc_make_root_descr_all_refs (1), (src), (msg)); \ + mono_gc_register_root ((char*)&(x), sizeof(MonoObject*), mono_gc_make_root_descr_all_refs (1), (src), (key),(msg)); \ } while (0) /* * This is used for fields which point to objects which are kept alive by other references * when using Boehm. */ -#define MONO_GC_REGISTER_ROOT_IF_MOVING(x,src,msg) do { \ +#define MONO_GC_REGISTER_ROOT_IF_MOVING(x,src,key,msg) do { \ if (mono_gc_is_moving ()) \ - MONO_GC_REGISTER_ROOT_SINGLE(x,src,msg); \ + MONO_GC_REGISTER_ROOT_SINGLE(x,src,key,msg); \ } while (0) #define MONO_GC_UNREGISTER_ROOT_IF_MOVING(x) do { \ @@ -122,7 +122,7 @@ gboolean mono_gc_user_markers_supported (void); * size bytes will be available from the returned address (ie, descr * must not be stored in the returned memory) */ -void* mono_gc_alloc_fixed (size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg); +void* mono_gc_alloc_fixed (size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg); void mono_gc_free_fixed (void* addr); /* make sure the gchandle was allocated for an object in domain */ @@ -141,7 +141,7 @@ MonoGCDescriptor mono_gc_make_descr_for_string (gsize *bitmap, int numbits); void mono_gc_register_for_finalization (MonoObject *obj, void *user_data); void mono_gc_add_memory_pressure (gint64 value); -MONO_API int mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg); +MONO_API int mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg); void mono_gc_deregister_root (char* addr); void mono_gc_finalize_domain (MonoDomain *domain); void mono_gc_run_finalize (void *obj, void *data); @@ -160,7 +160,7 @@ void mono_gc_suspend_finalizers (void); * FIXME: Add an API for clearing remset entries if a root with a user defined * mark routine is deleted. */ -int mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg); +int mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg); void mono_gc_wbarrier_set_root (gpointer ptr, MonoObject *value); diff --git a/mono/metadata/handle.c b/mono/metadata/handle.c index 5e32a478a3821..646066526347c 100644 --- a/mono/metadata/handle.c +++ b/mono/metadata/handle.c @@ -73,7 +73,7 @@ Combine: MonoDefaults, GENERATE_GET_CLASS_WITH_CACHE, TYPED_HANDLE_DECL and frie static HandleStack* new_handle_stack (void) { - return (HandleStack *)mono_gc_alloc_fixed (sizeof (HandleStack), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_HANDLE, "Thread Handle Stack"); + return (HandleStack *)mono_gc_alloc_fixed (sizeof (HandleStack), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_HANDLE, MONO_UINT_TO_NATIVE_THREAD_ID (mono_native_thread_id_get ()), "Thread Handle Stack"); } static void diff --git a/mono/metadata/marshal.c b/mono/metadata/marshal.c index 9403e54a149ad..6e21c3b0e5173 100644 --- a/mono/metadata/marshal.c +++ b/mono/metadata/marshal.c @@ -4029,7 +4029,7 @@ emit_invoke_call (MonoMethodBuilder *mb, MonoMethod *method, /* to make it work with our special string constructors */ if (!string_dummy) { MonoError error; - MONO_GC_REGISTER_ROOT_SINGLE (string_dummy, MONO_ROOT_SOURCE_MARSHAL, "dummy marshal string"); + MONO_GC_REGISTER_ROOT_SINGLE (string_dummy, MONO_ROOT_SOURCE_MARSHAL, NULL, "dummy marshal string"); string_dummy = mono_string_new_checked (mono_get_root_domain (), "dummy", &error); mono_error_assert_ok (&error); } diff --git a/mono/metadata/mono-conc-hash.c b/mono/metadata/mono-conc-hash.c index c0bdb0219e814..dc11acb530c8b 100644 --- a/mono/metadata/mono-conc-hash.c +++ b/mono/metadata/mono-conc-hash.c @@ -58,7 +58,7 @@ conc_table_new (MonoConcGHashTable *hash, int size) #ifdef HAVE_SGEN_GC conc_table *table = mg_new0 (conc_table, 1); #else - conc_table *table = mono_gc_alloc_fixed (sizeof (conc_table), MONO_GC_ROOT_DESCR_FOR_FIXED (sizeof (conc_table)), hash->source, hash->msg); + conc_table *table = mono_gc_alloc_fixed (sizeof (conc_table), MONO_GC_ROOT_DESCR_FOR_FIXED (sizeof (conc_table)), hash->source, NULL, hash->msg); #endif table->keys = mg_new0 (void*, size); @@ -68,9 +68,9 @@ conc_table_new (MonoConcGHashTable *hash, int size) #ifdef HAVE_SGEN_GC if (hash->gc_type & MONO_HASH_KEY_GC) - mono_gc_register_root_wbarrier ((char*)table->keys, sizeof (MonoObject*) * size, mono_gc_make_vector_descr (), hash->source, hash->msg); + mono_gc_register_root_wbarrier ((char*)table->keys, sizeof (MonoObject*) * size, mono_gc_make_vector_descr (), hash->source, NULL, hash->msg); if (hash->gc_type & MONO_HASH_VALUE_GC) - mono_gc_register_root_wbarrier ((char*)table->values, sizeof (MonoObject*) * size, mono_gc_make_vector_descr (), hash->source, hash->msg); + mono_gc_register_root_wbarrier ((char*)table->values, sizeof (MonoObject*) * size, mono_gc_make_vector_descr (), hash->source, NULL, hash->msg); #endif return table; diff --git a/mono/metadata/mono-gc.h b/mono/metadata/mono-gc.h index bd1262a67b8b6..00fb123255071 100644 --- a/mono/metadata/mono-gc.h +++ b/mono/metadata/mono-gc.h @@ -11,7 +11,9 @@ MONO_BEGIN_DECLS typedef int (*MonoGCReferences) (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, MonoObject **refs, uintptr_t *offsets, void *data); - +/* + * This enum is used by the profiler API when reporting root registration. + */ typedef enum { // Roots external to Mono. Embedders may only use this value. MONO_ROOT_SOURCE_EXTERNAL = 0, diff --git a/mono/metadata/mono-hash.c b/mono/metadata/mono-hash.c index 1b558370d02c1..ae10dfaf59613 100644 --- a/mono/metadata/mono-hash.c +++ b/mono/metadata/mono-hash.c @@ -155,7 +155,7 @@ mono_g_hash_table_new_type (GHashFunc hash_func, GEqualFunc key_equal_func, Mono #ifdef HAVE_SGEN_GC hash = mg_new0 (MonoGHashTable, 1); #else - hash = mono_gc_alloc_fixed (sizeof (MonoGHashTable), MONO_GC_ROOT_DESCR_FOR_FIXED (sizeof (MonoGHashTable)), source, msg); + hash = mono_gc_alloc_fixed (sizeof (MonoGHashTable), MONO_GC_ROOT_DESCR_FOR_FIXED (sizeof (MonoGHashTable)), source, NULL, msg); #endif hash->hash_func = hash_func; @@ -174,9 +174,9 @@ mono_g_hash_table_new_type (GHashFunc hash_func, GEqualFunc key_equal_func, Mono #ifdef HAVE_SGEN_GC if (hash->gc_type & MONO_HASH_KEY_GC) - mono_gc_register_root_wbarrier ((char*)hash->keys, sizeof (MonoObject*) * hash->table_size, mono_gc_make_vector_descr (), hash->source, hash->msg); + mono_gc_register_root_wbarrier ((char*)hash->keys, sizeof (MonoObject*) * hash->table_size, mono_gc_make_vector_descr (), hash->source, NULL, hash->msg); if (hash->gc_type & MONO_HASH_VALUE_GC) - mono_gc_register_root_wbarrier ((char*)hash->values, sizeof (MonoObject*) * hash->table_size, mono_gc_make_vector_descr (), hash->source, hash->msg); + mono_gc_register_root_wbarrier ((char*)hash->values, sizeof (MonoObject*) * hash->table_size, mono_gc_make_vector_descr (), hash->source, NULL, hash->msg); #endif return hash; @@ -235,9 +235,9 @@ rehash (MonoGHashTable *hash) #ifdef HAVE_SGEN_GC if (hash->gc_type & MONO_HASH_KEY_GC) - mono_gc_register_root_wbarrier ((char*)data.keys, sizeof (MonoObject*) * data.new_size, mono_gc_make_vector_descr (), hash->source, hash->msg); + mono_gc_register_root_wbarrier ((char*)data.keys, sizeof (MonoObject*) * data.new_size, mono_gc_make_vector_descr (), hash->source, NULL, hash->msg); if (hash->gc_type & MONO_HASH_VALUE_GC) - mono_gc_register_root_wbarrier ((char*)data.values, sizeof (MonoObject*) * data.new_size, mono_gc_make_vector_descr (), hash->source, hash->msg); + mono_gc_register_root_wbarrier ((char*)data.values, sizeof (MonoObject*) * data.new_size, mono_gc_make_vector_descr (), hash->source, NULL, hash->msg); #endif if (!mono_threads_is_coop_enabled ()) { diff --git a/mono/metadata/mono-ptr-array.h b/mono/metadata/mono-ptr-array.h index 61589f3bbbdec..a2ceca61a6199 100644 --- a/mono/metadata/mono-ptr-array.h +++ b/mono/metadata/mono-ptr-array.h @@ -36,7 +36,7 @@ typedef struct { (ARRAY).source = SOURCE; \ (ARRAY).msg = MSG; \ (ARRAY).data = INITIAL_SIZE > MONO_PTR_ARRAY_MAX_ON_STACK \ - ? (void **)mono_gc_alloc_fixed (sizeof (void*) * INITIAL_SIZE, mono_gc_make_root_descr_all_refs (INITIAL_SIZE), SOURCE, MSG) \ + ? (void **)mono_gc_alloc_fixed (sizeof (void*) * INITIAL_SIZE, mono_gc_make_root_descr_all_refs (INITIAL_SIZE), SOURCE, NULL, MSG) \ : g_newa (void*, MONO_PTR_ARRAY_MAX_ON_STACK); \ } while (0) @@ -47,7 +47,7 @@ typedef struct { #define mono_ptr_array_append(ARRAY, VALUE) do { \ if ((ARRAY).size >= (ARRAY).capacity) {\ - void **__tmp = (void **)mono_gc_alloc_fixed (sizeof (void*) * (ARRAY).capacity * 2, mono_gc_make_root_descr_all_refs ((ARRAY).capacity * 2), (ARRAY).source, (ARRAY).msg); \ + void **__tmp = (void **)mono_gc_alloc_fixed (sizeof (void*) * (ARRAY).capacity * 2, mono_gc_make_root_descr_all_refs ((ARRAY).capacity * 2), (ARRAY).source, NULL, (ARRAY).msg); \ mono_gc_memmove_aligned ((void *)__tmp, (ARRAY).data, (ARRAY).capacity * sizeof (void*)); \ if ((ARRAY).capacity > MONO_PTR_ARRAY_MAX_ON_STACK) \ mono_gc_free_fixed ((ARRAY).data); \ diff --git a/mono/metadata/null-gc.c b/mono/metadata/null-gc.c index d791744dd0583..b8f68ced05bec 100644 --- a/mono/metadata/null-gc.c +++ b/mono/metadata/null-gc.c @@ -110,11 +110,18 @@ mono_object_is_alive (MonoObject* o) } int -mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg) +mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg) { return TRUE; } +int +mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg) +{ + return TRUE; +} + + void mono_gc_deregister_root (char* addr) { @@ -157,7 +164,7 @@ mono_gc_make_root_descr_all_refs (int numbits) } void* -mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg) +mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg) { return g_malloc0 (size); } diff --git a/mono/metadata/object.c b/mono/metadata/object.c index 5744f45d3c42c..d94cc7692c22b 100644 --- a/mono/metadata/object.c +++ b/mono/metadata/object.c @@ -214,7 +214,8 @@ mono_thread_set_main (MonoThread *thread) static gboolean registered = FALSE; if (!registered) { - MONO_GC_REGISTER_ROOT_SINGLE (main_thread, MONO_ROOT_SOURCE_THREADING, "main thread object"); + void *key = thread->internal_thread ? MONO_UINT_TO_NATIVE_THREAD_ID (thread->internal_thread->tid) : NULL; + MONO_GC_REGISTER_ROOT_SINGLE (main_thread, MONO_ROOT_SOURCE_THREADING, key, "main thread object"); registered = TRUE; } @@ -1967,7 +1968,8 @@ mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *klass, MonoErro bitmap = compute_class_bitmap (klass, default_bitmap, sizeof (default_bitmap) * 8, 0, &max_set, TRUE); /*g_print ("bitmap 0x%x for %s.%s (size: %d)\n", bitmap [0], klass->name_space, klass->name, class_size);*/ statics_gc_descr = mono_gc_make_descr_from_bitmap (bitmap, max_set + 1); - vt->vtable [klass->vtable_size] = mono_gc_alloc_fixed (class_size, statics_gc_descr, MONO_ROOT_SOURCE_STATIC, "managed static variables"); + vt->vtable [klass->vtable_size] = mono_gc_alloc_fixed (class_size, statics_gc_descr, MONO_ROOT_SOURCE_STATIC, klass, "managed static variables"); + if (bitmap != default_bitmap) g_free (bitmap); } else { @@ -2094,7 +2096,7 @@ mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *klass, MonoErro /* This is unregistered in unregister_vtable_reflection_type() in domain.c. */ - MONO_GC_REGISTER_ROOT_IF_MOVING(vt->type, MONO_ROOT_SOURCE_REFLECTION, "vtable reflection type"); + MONO_GC_REGISTER_ROOT_IF_MOVING (vt->type, MONO_ROOT_SOURCE_REFLECTION, klass, "vtable reflection type"); } mono_vtable_set_is_remote (vt, mono_class_is_contextbound (klass)); @@ -2153,7 +2155,7 @@ mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *klass, MonoErro /* This is unregistered in unregister_vtable_reflection_type() in domain.c. */ - MONO_GC_REGISTER_ROOT_IF_MOVING(vt->type, MONO_ROOT_SOURCE_REFLECTION, "vtable reflection type"); + MONO_GC_REGISTER_ROOT_IF_MOVING(vt->type, MONO_ROOT_SOURCE_REFLECTION, klass, "vtable reflection type"); } mono_domain_unlock (domain); diff --git a/mono/metadata/profiler-events.h b/mono/metadata/profiler-events.h index 7b85f00fb0c1a..7c3995f9fe8e6 100644 --- a/mono/metadata/profiler-events.h +++ b/mono/metadata/profiler-events.h @@ -74,18 +74,9 @@ MONO_PROFILER_EVENT_0(gc_finalizing, GCFinalizing) MONO_PROFILER_EVENT_0(gc_finalized, GCFinalized) MONO_PROFILER_EVENT_1(gc_finalizing_object, GCFinalizingObject, MonoObject *, object) MONO_PROFILER_EVENT_1(gc_finalized_object, GCFinalizedObject, MonoObject *, object) - -/* - * This callback provides very low quality data and doesn't really match how - * roots are actually handled in the runtime. It will be replaced with a more - * sensible callback in the future. **This will be a breaking change.** - * - * In the meantime, you must define MONO_PROFILER_UNSTABLE_GC_ROOTS to be able - * to use this interface. - */ -#ifdef MONO_PROFILER_UNSTABLE_GC_ROOTS -MONO_PROFILER_EVENT_4(gc_roots, GCRoots, MonoObject *const *, roots, const MonoProfilerGCRootType *, types, const uintptr_t *, extra, uint64_t, count) -#endif +MONO_PROFILER_EVENT_5(gc_root_register, RootRegister, const mono_byte *, start, uintptr_t, size, MonoGCRootSource, kind, const void *, key, const char *, msg) +MONO_PROFILER_EVENT_1(gc_root_unregister, RootUnregister, const mono_byte *, start) +MONO_PROFILER_EVENT_3(gc_roots, GcRoots, uint64_t, count, const mono_byte* const *, addresses, const MonoObject* const *, objects) MONO_PROFILER_EVENT_1(monitor_contention, MonitorContention, MonoObject *, object) MONO_PROFILER_EVENT_1(monitor_failed, MonitorFailed, MonoObject *, object) diff --git a/mono/metadata/profiler-private.h b/mono/metadata/profiler-private.h index e087be07eb604..0a35cb74f4d81 100644 --- a/mono/metadata/profiler-private.h +++ b/mono/metadata/profiler-private.h @@ -7,7 +7,6 @@ #ifndef __MONO_PROFILER_PRIVATE_H__ #define __MONO_PROFILER_PRIVATE_H__ -#define MONO_PROFILER_UNSTABLE_GC_ROOTS #include #include #include @@ -31,12 +30,15 @@ struct _MonoProfilerDesc { _MONO_PROFILER_EVENT(name) #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name) #include #undef MONO_PROFILER_EVENT_0 #undef MONO_PROFILER_EVENT_1 #undef MONO_PROFILER_EVENT_2 #undef MONO_PROFILER_EVENT_3 #undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 #undef _MONO_PROFILER_EVENT }; @@ -64,12 +66,15 @@ typedef struct { _MONO_PROFILER_EVENT(name) #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name) #include #undef MONO_PROFILER_EVENT_0 #undef MONO_PROFILER_EVENT_1 #undef MONO_PROFILER_EVENT_2 #undef MONO_PROFILER_EVENT_3 #undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 #undef _MONO_PROFILER_EVENT } MonoProfilerState; @@ -119,12 +124,15 @@ mono_profiler_allocations_enabled (void) _MONO_PROFILER_EVENT(name, arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name) #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ _MONO_PROFILER_EVENT(name, arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name, arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name, arg5_type arg5_name) #include #undef MONO_PROFILER_EVENT_0 #undef MONO_PROFILER_EVENT_1 #undef MONO_PROFILER_EVENT_2 #undef MONO_PROFILER_EVENT_3 #undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 #undef _MONO_PROFILER_EVENT // These are the macros the rest of the runtime should use. diff --git a/mono/metadata/profiler.c b/mono/metadata/profiler.c index 536749ce254f7..afb8372b4dfda 100644 --- a/mono/metadata/profiler.c +++ b/mono/metadata/profiler.c @@ -426,12 +426,15 @@ mono_profiler_cleanup (void) _MONO_PROFILER_EVENT(name) #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name) #include #undef MONO_PROFILER_EVENT_0 #undef MONO_PROFILER_EVENT_1 #undef MONO_PROFILER_EVENT_2 #undef MONO_PROFILER_EVENT_3 #undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 #undef _MONO_PROFILER_EVENT } @@ -447,12 +450,15 @@ mono_profiler_cleanup (void) _MONO_PROFILER_EVENT(name, type) #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name, type) #include #undef MONO_PROFILER_EVENT_0 #undef MONO_PROFILER_EVENT_1 #undef MONO_PROFILER_EVENT_2 #undef MONO_PROFILER_EVENT_3 #undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 #undef _MONO_PROFILER_EVENT } @@ -497,12 +503,15 @@ update_callback (volatile gpointer *location, gpointer new_, volatile gint32 *co _MONO_PROFILER_EVENT(name, type) #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name, type) #include #undef MONO_PROFILER_EVENT_0 #undef MONO_PROFILER_EVENT_1 #undef MONO_PROFILER_EVENT_2 #undef MONO_PROFILER_EVENT_3 #undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 #undef _MONO_PROFILER_EVENT #define _MONO_PROFILER_EVENT(name, type, params, args) \ @@ -525,10 +534,14 @@ update_callback (volatile gpointer *location, gpointer new_, volatile gint32 *co _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name), (h->prof, arg1_name, arg2_name, arg3_name)) #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name), (h->prof, arg1_name, arg2_name, arg3_name, arg4_name)) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name, arg5_type arg5_name), (h->prof, arg1_name, arg2_name, arg3_name, arg4_name, arg5_name)) + #include #undef MONO_PROFILER_EVENT_0 #undef MONO_PROFILER_EVENT_1 #undef MONO_PROFILER_EVENT_2 #undef MONO_PROFILER_EVENT_3 #undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 #undef _MONO_PROFILER_EVENT diff --git a/mono/metadata/profiler.h b/mono/metadata/profiler.h index 6dd1ea336335a..b86db66322baa 100644 --- a/mono/metadata/profiler.h +++ b/mono/metadata/profiler.h @@ -196,23 +196,6 @@ typedef MonoProfilerCallInstrumentationFlags (*MonoProfilerCallInstrumentationFi */ MONO_API void mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handle, MonoProfilerCallInstrumentationFilterCallback cb); -#ifdef MONO_PROFILER_UNSTABLE_GC_ROOTS -typedef enum { - /* Upper 2 bytes. */ - MONO_PROFILER_GC_ROOT_PINNING = 1 << 8, - MONO_PROFILER_GC_ROOT_WEAKREF = 2 << 8, - MONO_PROFILER_GC_ROOT_INTERIOR = 4 << 8, - - /* Lower 2 bytes (flags). */ - MONO_PROFILER_GC_ROOT_STACK = 1 << 0, - MONO_PROFILER_GC_ROOT_FINALIZER = 1 << 1, - MONO_PROFILER_GC_ROOT_HANDLE = 1 << 2, - MONO_PROFILER_GC_ROOT_OTHER = 1 << 3, - MONO_PROFILER_GC_ROOT_MISC = 1 << 4, - - MONO_PROFILER_GC_ROOT_TYPEMASK = 0xff, -} MonoProfilerGCRootType; -#endif typedef enum { /* data = MonoMethod *method */ @@ -281,12 +264,15 @@ typedef enum { _MONO_PROFILER_EVENT(type, MonoProfiler *prof, arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name) #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ _MONO_PROFILER_EVENT(type, MonoProfiler *prof, arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(type, MonoProfiler *prof, arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name, arg5_type arg5_name) #include #undef MONO_PROFILER_EVENT_0 #undef MONO_PROFILER_EVENT_1 #undef MONO_PROFILER_EVENT_2 #undef MONO_PROFILER_EVENT_3 #undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 #undef _MONO_PROFILER_EVENT #define _MONO_PROFILER_EVENT(name, type) \ @@ -301,12 +287,15 @@ typedef enum { _MONO_PROFILER_EVENT(name, type) #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_5(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name, arg5_type, arg5_name) \ + _MONO_PROFILER_EVENT(name, type) #include #undef MONO_PROFILER_EVENT_0 #undef MONO_PROFILER_EVENT_1 #undef MONO_PROFILER_EVENT_2 #undef MONO_PROFILER_EVENT_3 #undef MONO_PROFILER_EVENT_4 +#undef MONO_PROFILER_EVENT_5 #undef _MONO_PROFILER_EVENT MONO_END_DECLS diff --git a/mono/metadata/sgen-client-mono.h b/mono/metadata/sgen-client-mono.h index e3cae00e7e605..7e9228f0ab1cd 100644 --- a/mono/metadata/sgen-client-mono.h +++ b/mono/metadata/sgen-client-mono.h @@ -288,20 +288,6 @@ sgen_client_binary_protocol_collection_requested (int generation, size_t request MONO_GC_REQUESTED (generation, requested_size, force); } -static void G_GNUC_UNUSED -sgen_client_binary_protocol_collection_begin (int minor_gc_count, int generation) -{ - MONO_GC_BEGIN (generation); - - MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_START, generation)); - -#ifndef DISABLE_PERFCOUNTERS - if (generation == GENERATION_NURSERY) - mono_perfcounters->gc_collections0++; - else - mono_perfcounters->gc_collections1++; -#endif -} static void G_GNUC_UNUSED sgen_client_binary_protocol_collection_end (int minor_gc_count, int generation, long long num_objects_scanned, long long num_unique_objects_scanned) @@ -686,6 +672,20 @@ sgen_client_binary_protocol_pin_stats (int objects_pinned_in_nursery, size_t byt { } +static void G_GNUC_UNUSED +sgen_client_root_registered (char *start, size_t size, int source, void *key, const char *msg) +{ + MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte*)start, size, source, key, msg)); +} + + +static void G_GNUC_UNUSED +sgen_client_root_deregistered (char *start) +{ + MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte*)start)); +} + + #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = (SgenThreadInfo*)mono_tls_get_sgen_thread_info () #define IN_CRITICAL_REGION (__thread_info__->client_info.in_critical_region) diff --git a/mono/metadata/sgen-mono.c b/mono/metadata/sgen-mono.c index 93bdb1fb5c7e8..a52a2241586c5 100644 --- a/mono/metadata/sgen-mono.c +++ b/mono/metadata/sgen-mono.c @@ -978,13 +978,13 @@ mono_gc_alloc_mature (MonoVTable *vtable, size_t size) * mono_gc_alloc_fixed: */ void* -mono_gc_alloc_fixed (size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg) +mono_gc_alloc_fixed (size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg) { /* FIXME: do a single allocation */ void *res = g_calloc (1, size); if (!res) return NULL; - if (!mono_gc_register_root ((char *)res, size, descr, source, msg)) { + if (!mono_gc_register_root ((char *)res, size, descr, source, key, msg)) { g_free (res); res = NULL; } @@ -1874,11 +1874,13 @@ mono_gc_set_string_length (MonoString *str, gint32 new_length) */ #define GC_ROOT_NUM 32 +#define SPECIAL_ADDRESS_FIN_QUEUE ((void*)1) +#define SPECIAL_ADDRESS_CRIT_FIN_QUEUE ((void*)2) + typedef struct { int count; /* must be the first field */ + void *addresses [GC_ROOT_NUM]; void *objects [GC_ROOT_NUM]; - int root_types [GC_ROOT_NUM]; - uintptr_t extra_info [GC_ROOT_NUM]; } GCRootReport; static void @@ -1886,63 +1888,34 @@ notify_gc_roots (GCRootReport *report) { if (!report->count) return; - MONO_PROFILER_RAISE (gc_roots, ((MonoObject **) report->objects, (MonoProfilerGCRootType *) report->root_types, report->extra_info, report->count)); + MONO_PROFILER_RAISE (gc_roots, (report->count, (const mono_byte *const *)report->addresses, (const MonoObject *const *)report->objects)); report->count = 0; } static void -add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info) +report_gc_root (GCRootReport *report, void *address, void *object) { if (report->count == GC_ROOT_NUM) notify_gc_roots (report); + report->addresses [report->count] = address; report->objects [report->count] = object; - report->root_types [report->count] = rtype; - report->extra_info [report->count++] = (uintptr_t)SGEN_LOAD_VTABLE (object)->klass; -} - -void -sgen_client_nursery_objects_pinned (void **definitely_pinned, int count) -{ - if (MONO_PROFILER_ENABLED (gc_roots)) { - GCRootReport report; - int idx; - report.count = 0; - for (idx = 0; idx < count; ++idx) - add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILER_GC_ROOT_PINNING | MONO_PROFILER_GC_ROOT_MISC, 0); - notify_gc_roots (&report); - } -} - -static void -report_finalizer_roots_from_queue (SgenPointerQueue *queue) -{ - GCRootReport report; - size_t i; - - report.count = 0; - for (i = 0; i < queue->next_slot; ++i) { - void *obj = queue->data [i]; - if (!obj) - continue; - add_profile_gc_root (&report, obj, MONO_PROFILER_GC_ROOT_FINALIZER, 0); - } - notify_gc_roots (&report); + report->count++; } static void -report_finalizer_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue) +single_arg_report_root (MonoObject **obj, void *gc_data) { - report_finalizer_roots_from_queue (fin_ready_queue); - report_finalizer_roots_from_queue (critical_fin_queue); + GCRootReport *report = gc_data; + if (*obj) + report_gc_root (report, obj, *obj); } -static GCRootReport *root_report; - static void -single_arg_report_root (MonoObject **obj, void *gc_data) +two_args_report_root (void *address, MonoObject *obj, void *gc_data) { - if (*obj) - add_profile_gc_root (root_report, *obj, MONO_PROFILER_GC_ROOT_OTHER, 0); + GCRootReport *report = gc_data; + if (obj) + report_gc_root (report, address, obj); } static void @@ -1952,9 +1925,8 @@ precisely_report_roots_from (GCRootReport *report, void** start_root, void** end case ROOT_DESC_BITMAP: desc >>= ROOT_DESC_TYPE_SHIFT; while (desc) { - if ((desc & 1) && *start_root) { - add_profile_gc_root (report, *start_root, MONO_PROFILER_GC_ROOT_OTHER, 0); - } + if ((desc & 1) && *start_root) + report_gc_root (report, start_root, *start_root); desc >>= 1; start_root++; } @@ -1968,9 +1940,8 @@ precisely_report_roots_from (GCRootReport *report, void** start_root, void** end gsize bmap = *bitmap_data++; void **objptr = start_run; while (bmap) { - if ((bmap & 1) && *objptr) { - add_profile_gc_root (report, *objptr, MONO_PROFILER_GC_ROOT_OTHER, 0); - } + if ((bmap & 1) && *objptr) + report_gc_root (report, objptr, *objptr); bmap >>= 1; ++objptr; } @@ -1983,14 +1954,17 @@ precisely_report_roots_from (GCRootReport *report, void** start_root, void** end for (p = start_root; p < end_root; p++) { if (*p) - add_profile_gc_root (report, *p, MONO_PROFILER_GC_ROOT_OTHER, 0); + report_gc_root (report, p, *p); } break; } case ROOT_DESC_USER: { MonoGCRootMarkFunc marker = (MonoGCRootMarkFunc)sgen_get_user_descriptor_func (desc); - root_report = report; - marker ((MonoObject**)start_root, single_arg_report_root, NULL); + + if ((void*)marker == (void*)sgen_mark_normal_gc_handles) + sgen_gc_handles_report_roots (two_args_report_root, report); + else + marker ((MonoObject**)start_root, single_arg_report_root, report); break; } case ROOT_DESC_RUN_LEN: @@ -2001,15 +1975,161 @@ precisely_report_roots_from (GCRootReport *report, void** start_root, void** end } static void -report_registered_roots_by_type (int root_type) +report_pinning_roots (GCRootReport *report, void **start, void **end) +{ + while (start < end) { + mword addr = (mword)*start; + addr &= ~(SGEN_ALLOC_ALIGN - 1); + if (addr) + report_gc_root (report, start, (void*)addr); + + start++; + } +} + +static SgenPointerQueue pinned_objects = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_MOVED_OBJECT); +static mword lower_bound, upper_bound; + +static GCObject* +find_pinned_obj (char *addr) +{ + size_t idx = sgen_pointer_queue_search (&pinned_objects, addr); + + if (idx != pinned_objects.next_slot) { + if (pinned_objects.data [idx] == addr) + return pinned_objects.data [idx]; + if (idx == 0) + return NULL; + } + + GCObject *obj = pinned_objects.data [idx - 1]; + if (addr > (char*)obj && addr < ((char*)obj + sgen_safe_object_get_size (obj))) + return obj; + return NULL; +} + + +/* + * We pass @root_report_address so register are properly accounted towards their thread +*/ +static void +report_conservative_roots (GCRootReport *report, char *root_report_address, void **start, void **end) +{ + while (start < end) { + mword addr = (mword)*start; + addr &= ~(SGEN_ALLOC_ALIGN - 1); + + if (addr < lower_bound || addr > upper_bound) { + ++start; + continue; + } + + GCObject *obj = find_pinned_obj ((char*)addr); + if (obj) + report_gc_root (report, root_report_address, obj); + start++; + } +} + +static void +report_stack_roots (void) +{ + GCRootReport report = {0}; + FOREACH_THREAD (info) { + void *aligned_stack_start; + + if (info->client_info.skip) { + continue; + } else if (info->client_info.gc_disabled) { + continue; + } else if (!mono_thread_info_is_live (info)) { + continue; + } else if (!info->client_info.stack_start) { + continue; + } + + g_assert (info->client_info.stack_start); + g_assert (info->client_info.info.stack_end); + + aligned_stack_start = (void*)(mword) ALIGN_TO ((mword)info->client_info.stack_start, SIZEOF_VOID_P); +#ifdef HOST_WIN32 + /* Windows uses a guard page before the committed stack memory pages to detect when the + stack needs to be grown. If we suspend a thread just after a function prolog has + decremented the stack pointer to point into the guard page but before the thread has + been able to read or write to that page, starting the stack scan at aligned_stack_start + will raise a STATUS_GUARD_PAGE_VIOLATION and the process will crash. This code uses + VirtualQuery() to determine whether stack_start points into the guard page and then + updates aligned_stack_start to point at the next non-guard page. */ + MEMORY_BASIC_INFORMATION mem_info; + SIZE_T result = VirtualQuery (info->client_info.stack_start, &mem_info, sizeof(mem_info)); + g_assert (result != 0); + if (mem_info.Protect & PAGE_GUARD) { + aligned_stack_start = ((char*) mem_info.BaseAddress) + mem_info.RegionSize; + } +#endif + + g_assert (info->client_info.suspend_done); + + //TODO report handle stack + report_conservative_roots (&report, aligned_stack_start, (void **)aligned_stack_start, (void **)info->client_info.info.stack_end); + report_conservative_roots (&report, aligned_stack_start, (void**)&info->client_info.ctx, (void**)(&info->client_info.ctx + 1)); + + } FOREACH_THREAD_END + + notify_gc_roots (&report); +} + +static void +report_pin_queue (void) +{ + lower_bound = SIZE_MAX; + upper_bound = 0; + + //sort the addresses + sgen_pointer_queue_sort_uniq (&pinned_objects); + + for (int i = 0; i < pinned_objects.next_slot; ++i) { + GCObject *obj = pinned_objects.data [i]; + ssize_t size = sgen_safe_object_get_size (obj); + + ssize_t addr = (ssize_t)obj; + lower_bound = MIN (lower_bound, addr); + upper_bound = MAX (upper_bound, addr + size); + } + + report_stack_roots (); + sgen_pointer_queue_clear (&pinned_objects); +} + +static void +report_finalizer_roots_from_queue (SgenPointerQueue *queue, void* queue_address) { GCRootReport report; + size_t i; + + report.count = 0; + for (i = 0; i < queue->next_slot; ++i) { + void *obj = queue->data [i]; + if (!obj) + continue; + report_gc_root (&report, queue_address, obj); + } + notify_gc_roots (&report); +} + +static void +report_registered_roots_by_type (int root_type) +{ + GCRootReport report = { 0 }; void **start_root; RootRecord *root; report.count = 0; SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], void **, start_root, RootRecord *, root) { - SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc); - precisely_report_roots_from (&report, start_root, (void**)root->end_root, root->root_desc); + SGEN_LOG (6, "Profiler root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc); + if (root_type == ROOT_TYPE_PINNED) + report_pinning_roots (&report, start_root, (void**)root->end_root); + else + precisely_report_roots_from (&report, start_root, (void**)root->end_root, root->root_desc); } SGEN_HASH_TABLE_FOREACH_END; notify_gc_roots (&report); } @@ -2017,52 +2137,83 @@ report_registered_roots_by_type (int root_type) static void report_registered_roots (void) { - report_registered_roots_by_type (ROOT_TYPE_NORMAL); - report_registered_roots_by_type (ROOT_TYPE_WBARRIER); + for (int i = 0; i < ROOT_TYPE_NUM; ++i) + report_registered_roots_by_type (i); +} + +static void +sgen_report_all_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue) +{ + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; + + report_registered_roots (); + report_pin_queue (); + report_finalizer_roots_from_queue (fin_ready_queue, SPECIAL_ADDRESS_FIN_QUEUE); + report_finalizer_roots_from_queue (critical_fin_queue, SPECIAL_ADDRESS_CRIT_FIN_QUEUE); } void -sgen_client_collecting_minor (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue) +sgen_client_pinning_start (void) { - if (MONO_PROFILER_ENABLED (gc_roots)) - report_registered_roots (); + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; - if (MONO_PROFILER_ENABLED (gc_roots)) - report_finalizer_roots (fin_ready_queue, critical_fin_queue); + sgen_pointer_queue_clear (&pinned_objects); } -static GCRootReport major_root_report; -static gboolean profile_roots; +void +sgen_client_pinning_end (void) +{ + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; +} void -sgen_client_collecting_major_1 (void) +sgen_client_nursery_objects_pinned (void **definitely_pinned, int count) { - profile_roots = MONO_PROFILER_ENABLED (gc_roots); - memset (&major_root_report, 0, sizeof (GCRootReport)); + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; + + for (int i = 0; i < count; ++i) + sgen_pointer_queue_add (&pinned_objects, definitely_pinned [i]); } void sgen_client_pinned_los_object (GCObject *obj) { - if (profile_roots) - add_profile_gc_root (&major_root_report, (char*)obj, MONO_PROFILER_GC_ROOT_PINNING | MONO_PROFILER_GC_ROOT_MISC, 0); + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; + + sgen_pointer_queue_add (&pinned_objects, obj); } void -sgen_client_collecting_major_2 (void) +sgen_client_pinned_cemented_object (GCObject *obj) { - if (profile_roots) - notify_gc_roots (&major_root_report); + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; +} + +void +sgen_client_pinned_major_heap_object (GCObject *obj) +{ + if (!MONO_PROFILER_ENABLED (gc_roots)) + return; - if (MONO_PROFILER_ENABLED (gc_roots)) - report_registered_roots (); + sgen_pointer_queue_add (&pinned_objects, obj); } void -sgen_client_collecting_major_3 (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue) +sgen_client_collecting_minor_report_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue) { - if (MONO_PROFILER_ENABLED (gc_roots)) - report_finalizer_roots (fin_ready_queue, critical_fin_queue); + sgen_report_all_roots (fin_ready_queue, critical_fin_queue); +} + +void +sgen_client_collecting_major_report_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue) +{ + sgen_report_all_roots (fin_ready_queue, critical_fin_queue); } #define MOVED_OBJECTS_NUM 64 @@ -2486,15 +2637,15 @@ mono_gc_set_stack_end (void *stack_end) */ int -mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg) +mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg) { - return sgen_register_root (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED, source, msg); + return sgen_register_root (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED, source, key, msg); } int -mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg) +mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg) { - return sgen_register_root (start, size, descr, ROOT_TYPE_WBARRIER, source, msg); + return sgen_register_root (start, size, descr, ROOT_TYPE_WBARRIER, source, key, msg); } void @@ -3012,4 +3163,25 @@ mono_gc_is_null (void) return FALSE; } +static gboolean pseudo_roots_registered; +void +sgen_client_binary_protocol_collection_begin (int minor_gc_count, int generation) +{ + MONO_GC_BEGIN (generation); + + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_START, generation)); + + if (!pseudo_roots_registered) { + pseudo_roots_registered = TRUE; + MONO_PROFILER_RAISE (gc_root_register, (SPECIAL_ADDRESS_FIN_QUEUE, 1, MONO_ROOT_SOURCE_FINALIZER_QUEUE, NULL, "finalizer queue")); + MONO_PROFILER_RAISE (gc_root_register, (SPECIAL_ADDRESS_CRIT_FIN_QUEUE, 1, MONO_ROOT_SOURCE_FINALIZER_QUEUE, NULL, "critical finalizer queue")); + } + +#ifndef DISABLE_PERFCOUNTERS + if (generation == GENERATION_NURSERY) + mono_perfcounters->gc_collections0++; + else + mono_perfcounters->gc_collections1++; +#endif +} #endif diff --git a/mono/metadata/sre-save.c b/mono/metadata/sre-save.c index d36381357caaa..4b0f54106ea51 100644 --- a/mono/metadata/sre-save.c +++ b/mono/metadata/sre-save.c @@ -966,7 +966,7 @@ mono_image_get_generic_param_info (MonoReflectionGenericParam *gparam, guint32 o entry = g_new0 (GenericParamTableEntry, 1); entry->owner = owner; /* FIXME: track where gen_params should be freed and remove the GC root as well */ - MONO_GC_REGISTER_ROOT_IF_MOVING (entry->gparam, MONO_ROOT_SOURCE_REFLECTION, "reflection generic parameter"); + MONO_GC_REGISTER_ROOT_IF_MOVING (entry->gparam, MONO_ROOT_SOURCE_REFLECTION, NULL, "reflection generic parameter"); entry->gparam = gparam; g_ptr_array_add (assembly->gen_params, entry); diff --git a/mono/metadata/threadpool-io.c b/mono/metadata/threadpool-io.c index a40ed6de5ea1e..077b47c7c95f6 100644 --- a/mono/metadata/threadpool-io.c +++ b/mono/metadata/threadpool-io.c @@ -541,7 +541,7 @@ initialize (void) mono_coop_mutex_init (&threadpool_io->updates_lock); mono_coop_cond_init (&threadpool_io->updates_cond); - mono_gc_register_root ((char *)&threadpool_io->updates [0], sizeof (threadpool_io->updates), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_THREAD_POOL, "i/o thread pool updates list"); + mono_gc_register_root ((char *)&threadpool_io->updates [0], sizeof (threadpool_io->updates), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_THREAD_POOL, 0, "i/o thread pool updates list"); threadpool_io->updates_size = 0; diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index e0a7aee8edc4b..6fe6e59ae26c3 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -559,7 +559,7 @@ create_internal_thread_object (void) thread->managed_id = get_next_managed_thread_id (); if (mono_gc_is_moving ()) { thread->thread_pinning_ref = thread; - MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref, MONO_ROOT_SOURCE_THREADING, "thread pinning reference"); + MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref, MONO_ROOT_SOURCE_THREADING, 0, "thread pinning reference"); } thread->priority = MONO_THREAD_PRIORITY_NORMAL; @@ -646,7 +646,7 @@ mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPrior } static void -mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal); +mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, void *alloc_key, gboolean threadlocal); static gboolean mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain) @@ -714,7 +714,7 @@ mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean if (thread_static_info.offset || thread_static_info.idx > 0) { /* get the current allocated size */ guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (thread_static_info.idx, thread_static_info.offset, 0); - mono_alloc_static_data (&internal->static_data, offset, TRUE); + mono_alloc_static_data (&internal->static_data, offset, MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), TRUE); } mono_threads_unlock (); @@ -749,6 +749,20 @@ typedef struct { MonoCoopSem registered; } StartInfo; +static void +fire_attach_profiler_events (MonoNativeThreadId tid) +{ + MONO_PROFILER_RAISE (thread_started, ((uintptr_t)tid)); + + MonoThreadInfo *info = mono_thread_info_current (); + MONO_PROFILER_RAISE (gc_root_register, ( + info->stack_start_limit, + (char*)info->stack_end - (char*)info->stack_start_limit, + MONO_ROOT_SOURCE_STACK, + (void*)tid, + "thread stack")); +} + static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack_ptr) { MonoError error; @@ -824,7 +838,7 @@ static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack * to lock the thread, and the lock is held by thread_start () which waits for * start_notify. */ - MONO_PROFILER_RAISE (thread_started, (tid)); + fire_attach_profiler_events ((MonoNativeThreadId)tid); /* if the name was set before starting, we didn't invoke the profiler callback */ if (internal->name) { @@ -1124,7 +1138,7 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach) /* Can happen when we attach the profiler helper thread in order to heapshot. */ if (!mono_thread_info_current ()->tools_thread) - MONO_PROFILER_RAISE (thread_started, (MONO_NATIVE_THREAD_ID_TO_UINT (tid))); + fire_attach_profiler_events (tid); return thread; } @@ -1234,8 +1248,10 @@ mono_thread_detach_internal (MonoInternalThread *thread) mono_release_type_locks (thread); /* Can happen when we attach the profiler helper thread in order to heapshot. */ - if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread) + if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread) { MONO_PROFILER_RAISE (thread_stopped, (thread->tid)); + MONO_PROFILER_RAISE (gc_root_unregister, (((MonoThreadInfo*)thread->thread_info)->stack_start_limit)); + } mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1); @@ -4029,7 +4045,7 @@ mark_ctx_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data) * Allocate memory blocks for storing threads or context static data */ static void -mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal) +mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, void *alloc_key, gboolean threadlocal) { guint idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index); int i; @@ -4049,6 +4065,7 @@ mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean thr static_data = (void **)mono_gc_alloc_fixed (static_data_size [0], threadlocal ? tls_desc : ctx_desc, threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC, + alloc_key, threadlocal ? "managed thread-static variables" : "managed context-static variables"); *static_data_ptr = static_data; static_data [0] = static_data; @@ -4063,6 +4080,7 @@ mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean thr else static_data [i] = mono_gc_alloc_fixed (static_data_size [i], MONO_GC_DESCRIPTOR_NULL, threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC, + alloc_key, threadlocal ? "managed thread-static variables" : "managed context-static variables"); } } @@ -4141,7 +4159,7 @@ context_adjust_static_data (MonoAppContext *ctx) { if (context_static_info.offset || context_static_info.idx > 0) { guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0); - mono_alloc_static_data (&ctx->static_data, offset, FALSE); + mono_alloc_static_data (&ctx->static_data, offset, ctx, FALSE); ctx->data->static_data = ctx->static_data; } } @@ -4155,7 +4173,7 @@ alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user) MonoInternalThread *thread = (MonoInternalThread *)value; guint32 offset = GPOINTER_TO_UINT (user); - mono_alloc_static_data (&(thread->static_data), offset, TRUE); + mono_alloc_static_data (&(thread->static_data), offset, MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid), TRUE); } /* @@ -4170,7 +4188,7 @@ alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user) return; guint32 offset = GPOINTER_TO_UINT (user); - mono_alloc_static_data (&ctx->static_data, offset, FALSE); + mono_alloc_static_data (&ctx->static_data, offset, ctx, FALSE); ctx->data->static_data = ctx->static_data; } diff --git a/mono/mini/debugger-agent.c b/mono/mini/debugger-agent.c index 102158e4fdab6..7328b8ba941dc 100644 --- a/mono/mini/debugger-agent.c +++ b/mono/mini/debugger-agent.c @@ -3910,7 +3910,7 @@ thread_startup (MonoProfiler *prof, uintptr_t tid) g_assert (!tls); // FIXME: Free this somewhere tls = g_new0 (DebuggerTlsData, 1); - MONO_GC_REGISTER_ROOT_SINGLE (tls->thread, MONO_ROOT_SOURCE_DEBUGGER, "debugger thread reference"); + MONO_GC_REGISTER_ROOT_SINGLE (tls->thread, MONO_ROOT_SOURCE_DEBUGGER, NULL, "debugger thread reference"); tls->thread = thread; mono_native_tls_set_value (debugger_tls_id, tls); diff --git a/mono/mini/tasklets.c b/mono/mini/tasklets.c index e767f63125a69..8e50c610bff02 100644 --- a/mono/mini/tasklets.c +++ b/mono/mini/tasklets.c @@ -112,7 +112,7 @@ continuation_store (MonoContinuation *cont, int state, MonoException **e) mono_gc_free_fixed (cont->saved_stack); cont->stack_used_size = num_bytes; cont->stack_alloc_size = num_bytes * 1.1; - cont->saved_stack = mono_gc_alloc_fixed (cont->stack_alloc_size, NULL, MONO_ROOT_SOURCE_THREADING, "saved tasklet stack"); + cont->saved_stack = mono_gc_alloc_fixed (cont->stack_alloc_size, NULL, MONO_ROOT_SOURCE_THREADING, NULL, "saved tasklet stack"); tasklets_unlock (); } memcpy (cont->saved_stack, cont->return_sp, num_bytes); diff --git a/mono/profiler/log.c b/mono/profiler/log.c index 847ee26184cb9..167488aa754a8 100644 --- a/mono/profiler/log.c +++ b/mono/profiler/log.c @@ -1200,32 +1200,64 @@ gc_reference (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, } static void -gc_roots (MonoProfiler *prof, MonoObject *const *objects, const MonoProfilerGCRootType *root_types, const uintptr_t *extra_info, uint64_t num) +gc_roots (MonoProfiler *prof, uint64_t num, const mono_byte *const *addresses, const MonoObject* const *objects) { ENTER_LOG (&heap_roots_ctr, logbuffer, EVENT_SIZE /* event */ + LEB128_SIZE /* num */ + - LEB128_SIZE /* collections */ + num * ( - LEB128_SIZE /* object */ + - LEB128_SIZE /* root type */ + - LEB128_SIZE /* extra info */ + LEB128_SIZE /* address */ + + LEB128_SIZE /* object */ ) ); emit_event (logbuffer, TYPE_HEAP_ROOT | TYPE_HEAP); emit_value (logbuffer, num); - emit_value (logbuffer, mono_gc_collection_count (mono_gc_max_generation ())); for (int i = 0; i < num; ++i) { - emit_obj (logbuffer, objects [i]); - emit_byte (logbuffer, root_types [i]); - emit_value (logbuffer, extra_info [i]); + emit_ptr (logbuffer, addresses [i]); + emit_obj (logbuffer, (void*)objects [i]); } EXIT_LOG; } +static void +gc_root_register (MonoProfiler *prof, const mono_byte *start, size_t size, MonoGCRootSource kind, const void *key, const char *msg) +{ + int msg_len = msg ? strlen (msg) + 1 : 0; + ENTER_LOG (&heap_roots_ctr, logbuffer, + EVENT_SIZE /* event */ + + LEB128_SIZE /* start */ + + LEB128_SIZE /* size */ + + BYTE_SIZE /* kind */ + + LEB128_SIZE /* key */ + + msg_len /*msg */ + ); + + emit_event (logbuffer, TYPE_HEAP_ROOT_REGISTER | TYPE_HEAP); + emit_ptr (logbuffer, start); + emit_uvalue (logbuffer, size); + emit_byte (logbuffer, kind); + emit_ptr (logbuffer, key); + emit_string (logbuffer, msg, msg_len); + + EXIT_LOG; +} + +static void +gc_root_deregister (MonoProfiler *prof, const mono_byte *start) +{ + ENTER_LOG (&heap_roots_ctr, logbuffer, + EVENT_SIZE /* event */ + + LEB128_SIZE /* start */ + ); + + emit_event (logbuffer, TYPE_HEAP_ROOT_UNREGISTER | TYPE_HEAP); + emit_ptr (logbuffer, start); + + EXIT_LOG; +} static void trigger_on_demand_heapshot (void) @@ -4725,8 +4757,11 @@ mono_profiler_init (const char *desc) if (ENABLED (PROFLOG_GC_MOVE_EVENTS)) mono_profiler_set_gc_moves_callback (handle, gc_moves); - if (ENABLED (PROFLOG_GC_ROOT_EVENTS)) + if (ENABLED (PROFLOG_GC_ROOT_EVENTS)) { mono_profiler_set_gc_roots_callback (handle, gc_roots); + mono_profiler_set_gc_root_register_callback (handle, gc_root_register); + mono_profiler_set_gc_root_unregister_callback (handle, gc_root_deregister); + } if (ENABLED (PROFLOG_GC_HANDLE_EVENTS)) { mono_profiler_set_gc_handle_created_callback (handle, gc_handle_created); diff --git a/mono/profiler/log.h b/mono/profiler/log.h index d8182d90ee27d..052eff1d62c21 100644 --- a/mono/profiler/log.h +++ b/mono/profiler/log.h @@ -2,7 +2,6 @@ #define __MONO_PROFLOG_H__ #include -#define MONO_PROFILER_UNSTABLE_GC_ROOTS #include #define BUF_ID 0x4D504C01 @@ -16,6 +15,10 @@ * version 1.0: removed sysid field from header * added args, arch, os fields to header * + * Version 2.0: Reworked argument parsing, defaults now are a lot more saner + * Events/feature should be explicitly enabled as by default they won't be emited (use legacy option to get closer) + * Heap events (moves, roots, objects) are now all emitted around HEAD_START / HEAD_END events to make it easy to isolate a heapshot + * End-of-GC sync points are now emitted with the world stopped, eliminating the window it races with the mutator. * Changes in data versions: * version 2: added offsets in heap walk * version 3: added GC roots @@ -70,6 +73,8 @@ class unload events no longer exist (they were never emitted) removed type field from TYPE_SAMPLE_HIT removed MONO_GC_EVENT_{MARK,RECLAIM}_{START,END} + GC Roots events now have a different, and saner, format. + added gc root register/unregister events to be used to source gc root information */ /* @@ -248,7 +253,7 @@ * * type heap format * type: TYPE_HEAP - * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT, TYPE_HEAP_ROOT + * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT, TYPE_HEAP_ROOT, TYPE_HEAP_ROOT_REGISTER, TYPE_HEAP_ROOT_UNREGISTER * if exinfo == TYPE_HEAP_OBJECT * [object: sleb128] the object as a difference from obj_base * [class: sleb128] the object MonoClass* as a difference from ptr_base @@ -263,11 +268,17 @@ * provide additional referenced objects. * if exinfo == TYPE_HEAP_ROOT * [num_roots: uleb128] number of root references - * [num_gc: uleb128] number of major gcs - * [object: sleb128] the object as a difference from obj_base - * [root_type: byte] the root_type: MonoProfileGCRootType (profiler.h) - * [extra_info: uleb128] the extra_info value - * object, root_type and extra_info are repeated num_roots times + * num_roots runs of the following: + * [address: sleb128] the root address as a difference from ptr_base + * [object: sleb128] the object address as a difference from obj_base + * if exinfo == TYPE_HEAP_ROOT_REGISTER + * [start:sleb128] start address as a different from ptr_base + * [size:uleb] size of the root region + * [kind:byte] type of the buffer + * [key:sleb] root key, meaning dependent on type, value as a different from ptr_base + * [desc:string] description of the root, as null terminated string + * if exinfo == TYPE_HEAP_ROOT_UNREGISTER + * [start:sleb128] start address as a different from ptr_base * * type sample format * type: TYPE_SAMPLE @@ -374,6 +385,8 @@ enum { TYPE_HEAP_END = 1 << 4, TYPE_HEAP_OBJECT = 2 << 4, TYPE_HEAP_ROOT = 3 << 4, + TYPE_HEAP_ROOT_REGISTER = 4 << 4, + TYPE_HEAP_ROOT_UNREGISTER = 5 << 4, /* extended type for TYPE_METADATA */ TYPE_END_LOAD = 2 << 4, TYPE_END_UNLOAD = 4 << 4, diff --git a/mono/profiler/mprof-report.c b/mono/profiler/mprof-report.c index 4251a164ff566..679c591773dd7 100644 --- a/mono/profiler/mprof-report.c +++ b/mono/profiler/mprof-report.c @@ -76,6 +76,23 @@ #define HASH_SIZE 9371 #define SMALL_HASH_SIZE 31 +/* Version < 14 root type enum */ +typedef enum { + /* Upper 2 bytes. */ + MONO_PROFILER_GC_ROOT_PINNING = 1 << 8, + MONO_PROFILER_GC_ROOT_WEAKREF = 2 << 8, + MONO_PROFILER_GC_ROOT_INTERIOR = 4 << 8, + + /* Lower 2 bytes (flags). */ + MONO_PROFILER_GC_ROOT_STACK = 1 << 0, + MONO_PROFILER_GC_ROOT_FINALIZER = 1 << 1, + MONO_PROFILER_GC_ROOT_HANDLE = 1 << 2, + MONO_PROFILER_GC_ROOT_OTHER = 1 << 3, + MONO_PROFILER_GC_ROOT_MISC = 1 << 4, + + MONO_PROFILER_GC_ROOT_TYPEMASK = 0xff, +} MonoProfilerGCRootType; + static int debug = 0; static int collect_traces = 0; static int show_traces = 0; @@ -2691,29 +2708,68 @@ decode_buffer (ProfContext *ctx) fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num); } else if (subtype == TYPE_HEAP_ROOT) { uintptr_t num; - if (ctx->data_version > 12) { + if (ctx->data_version > 13) { + int i; uint64_t tdiff = decode_uleb128 (p + 1, &p); LOG_TIME (time_base, tdiff); time_base += tdiff; num = decode_uleb128 (p, &p); - } else - num = decode_uleb128 (p + 1, &p); - uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p); - int i; - for (i = 0; i < num; ++i) { - intptr_t objdiff = decode_sleb128 (p, &p); - int root_type; - if (ctx->data_version > 12) - root_type = *p++; - else - root_type = decode_uleb128 (p, &p); - /* we just discard the extra info for now */ - uintptr_t extra_info = decode_uleb128 (p, &p); - if (debug) - fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type)); - if (collect_traces) - thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info); + for (i = 0; i < num; ++i) { + intptr_t ptrdiff = decode_sleb128 (p, &p); + intptr_t objdiff = decode_sleb128 (p, &p); + + if (debug) + fprintf (outfile, "object %p at address %zx\n", (void*)OBJ_ADDR (objdiff), ptr_base + ptrdiff); + if (collect_traces) + thread_add_root (thread, OBJ_ADDR (objdiff), MONO_PROFILER_GC_ROOT_MISC, 0); + } + } else { + if (ctx->data_version > 12) { + uint64_t tdiff = decode_uleb128 (p + 1, &p); + LOG_TIME (time_base, tdiff); + time_base += tdiff; + num = decode_uleb128 (p, &p); + } else + num = decode_uleb128 (p + 1, &p); + uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p); + int i; + for (i = 0; i < num; ++i) { + intptr_t objdiff = decode_sleb128 (p, &p); + int root_type; + if (ctx->data_version > 12) + root_type = *p++; + else + root_type = decode_uleb128 (p, &p); + /* we just discard the extra info for now */ + uintptr_t extra_info = decode_uleb128 (p, &p); + if (debug) + fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type)); + if (collect_traces) + thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info); + } } + } else if (subtype == TYPE_HEAP_ROOT_REGISTER) { + uint64_t tdiff = decode_uleb128 (p + 1, &p); + LOG_TIME (time_base, tdiff); + time_base += tdiff; + + uint64_t ptrdiff = decode_uleb128 (p, &p); + uint64_t size = decode_uleb128 (p, &p); + int type = *p++; + uint64_t keydiff = decode_uleb128 (p, &p); + char *desc = (char*)p; + while (*p++); + + if (debug) + fprintf (outfile, "root register address %llx size %lld type %d key %llx description %s\n", ptr_base + ptrdiff, size, type, ptr_base + keydiff, desc); + } else if (subtype == TYPE_HEAP_ROOT_UNREGISTER) { + uint64_t tdiff = decode_uleb128 (p + 1, &p); + LOG_TIME (time_base, tdiff); + time_base += tdiff; + uint64_t ptrdiff = decode_uleb128 (p, &p); + + if (debug) + fprintf (outfile, "root %llx was unregisted\n", ptr_base + ptrdiff); } else if (subtype == TYPE_HEAP_END) { uint64_t tdiff = decode_uleb128 (p + 1, &p); LOG_TIME (time_base, tdiff); diff --git a/mono/sgen/sgen-client.h b/mono/sgen/sgen-client.h index a492ad74a977d..5dbfa360ffade 100644 --- a/mono/sgen/sgen-client.h +++ b/mono/sgen/sgen-client.h @@ -105,20 +105,31 @@ void sgen_client_nursery_objects_pinned (void **definitely_pinned, int count); /* * Called at a semi-random point during minor collections. No action is necessary. */ -void sgen_client_collecting_minor (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue); +void sgen_client_collecting_minor_report_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue); /* * Called at semi-random points during major collections. No action is necessary. */ -void sgen_client_collecting_major_1 (void); -void sgen_client_collecting_major_2 (void); -void sgen_client_collecting_major_3 (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue); +void sgen_client_collecting_major_report_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue); /* * Called after a LOS object has been pinned. No action is necessary. */ void sgen_client_pinned_los_object (GCObject *obj); +/* + * Called for each cemented obj + */ +void sgen_client_pinned_cemented_object (GCObject *obj); + +/* + * Called for each major heap obj pinned + */ +void sgen_client_pinned_major_heap_object (GCObject *obj); + +void sgen_client_pinning_start (void); +void sgen_client_pinning_end (void); + /* * Called for every degraded allocation. No action is necessary. */ diff --git a/mono/sgen/sgen-descriptor.h b/mono/sgen/sgen-descriptor.h index 2c7f19b18c46a..f10ebf6a5104a 100644 --- a/mono/sgen/sgen-descriptor.h +++ b/mono/sgen/sgen-descriptor.h @@ -122,6 +122,7 @@ enum { }; typedef void (*SgenUserMarkFunc) (GCObject **addr, void *gc_data); +typedef void (*SgenUserReportRootFunc) (void *addr, GCObject *obj, void *gc_data); typedef void (*SgenUserRootMarkFunc) (void *addr, SgenUserMarkFunc mark_func, void *gc_data); SgenDescriptor sgen_make_user_root_descriptor (SgenUserRootMarkFunc marker); diff --git a/mono/sgen/sgen-gc.c b/mono/sgen/sgen-gc.c index 8f6ee8aa833b6..1a737949e2cd5 100644 --- a/mono/sgen/sgen-gc.c +++ b/mono/sgen/sgen-gc.c @@ -677,6 +677,7 @@ pin_objects_from_nursery_pin_queue (gboolean do_scan_objects, ScanCopyContext ct * Finally - pin the object! */ desc = sgen_obj_get_descriptor_safe (obj_to_pin); + if (do_scan_objects) { scan_func (obj_to_pin, desc, queue); } else { @@ -1733,6 +1734,7 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_ time_minor_pinning += TV_ELAPSED (btv, atv); SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), (long long)TV_ELAPSED (btv, atv)); SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ()); + sgen_client_pinning_end (); remset.start_scan_remsets (); @@ -1745,9 +1747,6 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_ sgen_pin_stats_report (); - /* FIXME: Why do we do this at this specific, seemingly random, point? */ - sgen_client_collecting_minor (&fin_ready_queue, &critical_fin_queue); - TV_GETTIME (atv); time_minor_scan_pinned += TV_ELAPSED (btv, atv); @@ -1821,6 +1820,10 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_ sgen_debug_dump_heap ("minor", gc_stats.minor_gc_count - 1, NULL); + //This is used by the profiler to report GC roots + //Invanants: heap's finished, no more moves left. Pin queue no longer in use, we can do whatever with it + sgen_client_collecting_minor_report_roots (&fin_ready_queue, &critical_fin_queue); + /* prepare the pin queue for the next collection */ sgen_finish_pinning (); if (sgen_have_pending_finalizers ()) { @@ -1928,8 +1931,6 @@ major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_ sgen_cement_force_pinned (); } - sgen_client_collecting_major_1 (); - /* * pin_queue now contains all candidate pointers, sorted and * uniqued. We must do two passes now to figure out which @@ -1980,6 +1981,7 @@ major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_ time_major_pinning += TV_ELAPSED (atv, btv); SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), (long long)TV_ELAPSED (atv, btv)); SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ()); + sgen_client_pinning_end (); if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) sgen_finish_pinning_for_conc (); @@ -2005,13 +2007,9 @@ major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_ main_gc_thread = mono_native_thread_self (); #endif - sgen_client_collecting_major_2 (); - TV_GETTIME (atv); time_major_scan_pinned += TV_ELAPSED (btv, atv); - sgen_client_collecting_major_3 (&fin_ready_queue, &critical_fin_queue); - enqueue_scan_from_roots_jobs (gc_thread_gray_queue, heap_start, heap_end, object_ops_nopar, FALSE); TV_GETTIME (btv); @@ -2202,6 +2200,8 @@ major_finish_collection (SgenGrayQueue *gc_thread_gray_queue, const char *reason if (do_concurrent_checks && concurrent_collection_in_progress) sgen_debug_check_nursery_is_clean (); + sgen_client_collecting_major_report_roots (&fin_ready_queue, &critical_fin_queue); + /* prepare the pin queue for the next collection */ sgen_finish_pinning (); @@ -2692,10 +2692,13 @@ sgen_have_pending_finalizers (void) * We do not coalesce roots. */ int -sgen_register_root (char *start, size_t size, SgenDescriptor descr, int root_type, int source, const char *msg) +sgen_register_root (char *start, size_t size, SgenDescriptor descr, int root_type, int source, void *key, const char *msg) { RootRecord new_root; int i; + + sgen_client_root_registered (start, size, source, key, msg); + LOCK_GC; for (i = 0; i < ROOT_TYPE_NUM; ++i) { RootRecord *root = (RootRecord *)sgen_hash_table_lookup (&roots_hash [i], start); @@ -2734,6 +2737,8 @@ sgen_deregister_root (char* addr) int root_type; RootRecord root; + sgen_client_root_deregistered (addr); + LOCK_GC; for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) { if (sgen_hash_table_remove (&roots_hash [root_type], addr, &root)) @@ -3614,7 +3619,7 @@ sgen_gc_init (void) sgen_card_table_init (&remset); - sgen_register_root (NULL, 0, sgen_make_user_root_descriptor (sgen_mark_normal_gc_handles), ROOT_TYPE_NORMAL, MONO_ROOT_SOURCE_GC_HANDLE, "normal gc handles"); + sgen_register_root (NULL, 0, sgen_make_user_root_descriptor (sgen_mark_normal_gc_handles), ROOT_TYPE_NORMAL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "normal gc handles"); gc_initialized = 1; diff --git a/mono/sgen/sgen-gc.h b/mono/sgen/sgen-gc.h index 2faad109d1ec0..3b05cb2b63be5 100644 --- a/mono/sgen/sgen-gc.h +++ b/mono/sgen/sgen-gc.h @@ -392,7 +392,7 @@ enum { extern SgenHashTable roots_hash [ROOT_TYPE_NUM]; -int sgen_register_root (char *start, size_t size, SgenDescriptor descr, int root_type, int source, const char *msg); +int sgen_register_root (char *start, size_t size, SgenDescriptor descr, int root_type, int source, void *key, const char *msg); void sgen_deregister_root (char* addr); typedef void (*IterateObjectCallbackFunc) (GCObject*, size_t, void*); @@ -971,6 +971,7 @@ typedef gpointer (*SgenGCHandleIterateCallback) (gpointer hidden, GCHandleType h void sgen_gchandle_iterate (GCHandleType handle_type, int max_generation, SgenGCHandleIterateCallback callback, gpointer user); void sgen_gchandle_set_target (guint32 gchandle, GCObject *obj); void sgen_mark_normal_gc_handles (void *addr, SgenUserMarkFunc mark_func, void *gc_data); +void sgen_gc_handles_report_roots (SgenUserReportRootFunc mark_func, void *gc_data); gpointer sgen_gchandle_get_metadata (guint32 gchandle); /* Other globals */ diff --git a/mono/sgen/sgen-gchandles.c b/mono/sgen/sgen-gchandles.c index 555d5bf483f68..1c8783351b224 100644 --- a/mono/sgen/sgen-gchandles.c +++ b/mono/sgen/sgen-gchandles.c @@ -94,15 +94,24 @@ static void bucket_alloc_callback (gpointer *bucket, guint32 new_bucket_size, gboolean alloc) { if (alloc) - sgen_register_root ((char *)bucket, new_bucket_size, SGEN_DESCRIPTOR_NULL, ROOT_TYPE_PINNED, MONO_ROOT_SOURCE_GC_HANDLE, "pinned gc handles"); + sgen_register_root ((char *)bucket, new_bucket_size, SGEN_DESCRIPTOR_NULL, ROOT_TYPE_PINNED, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "pinned gc handles"); else sgen_deregister_root ((char *)bucket); } +static void +bucket_alloc_report_root (gpointer *bucket, guint32 new_bucket_size, gboolean alloc) +{ + if (alloc) + sgen_client_root_registered ((char *)bucket, new_bucket_size, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "normal gc handles"); + else + sgen_client_root_deregistered ((char *)bucket); +} + static HandleData gc_handles [] = { { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_WEAK) }, { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_WEAK_TRACK) }, - { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_NORMAL) }, + { SGEN_ARRAY_LIST_INIT (bucket_alloc_report_root, is_slot_set, try_occupy_slot, -1), (HANDLE_NORMAL) }, { SGEN_ARRAY_LIST_INIT (bucket_alloc_callback, is_slot_set, try_occupy_slot, -1), (HANDLE_PINNED) } }; @@ -132,6 +141,24 @@ sgen_mark_normal_gc_handles (void *addr, SgenUserMarkFunc mark_func, void *gc_da } SGEN_ARRAY_LIST_END_FOREACH_SLOT; } +void +sgen_gc_handles_report_roots (SgenUserReportRootFunc report_func, void *gc_data) +{ + HandleData *handles = gc_handles_for_type (HANDLE_NORMAL); + SgenArrayList *array = &handles->entries_array; + volatile gpointer *slot; + gpointer hidden, revealed; + + SGEN_ARRAY_LIST_FOREACH_SLOT (array, slot) { + hidden = *slot; + revealed = MONO_GC_REVEAL_POINTER (hidden, FALSE); + + if (MONO_GC_HANDLE_IS_OBJECT_POINTER (hidden)) + report_func ((void*)slot, revealed, gc_data); + } SGEN_ARRAY_LIST_END_FOREACH_SLOT; +} + + static guint32 alloc_handle (HandleData *handles, GCObject *obj, gboolean track) diff --git a/mono/sgen/sgen-marksweep.c b/mono/sgen/sgen-marksweep.c index 04feb680f2d77..6bb847564136e 100644 --- a/mono/sgen/sgen-marksweep.c +++ b/mono/sgen/sgen-marksweep.c @@ -1394,6 +1394,7 @@ mark_pinned_objects_in_block (MSBlockInfo *block, size_t first_entry, size_t las continue; MS_MARK_OBJECT_AND_ENQUEUE (obj, sgen_obj_get_descriptor (obj), block, queue); sgen_pin_stats_register_object (obj, GENERATION_OLD); + sgen_client_pinned_major_heap_object (obj); last_index = index; } diff --git a/mono/sgen/sgen-pinning.c b/mono/sgen/sgen-pinning.c index 0e711d1aa15a2..c73701cb8af86 100644 --- a/mono/sgen/sgen-pinning.c +++ b/mono/sgen/sgen-pinning.c @@ -43,6 +43,7 @@ sgen_init_pinning (void) { memset (pin_hash_filter, 0, sizeof (pin_hash_filter)); pin_queue.mem_type = INTERNAL_MEM_PIN_QUEUE; + sgen_client_pinning_start (); } void @@ -367,6 +368,7 @@ pin_from_hash (CementHashEntry *hash, gboolean has_been_reset) if (has_been_reset) SGEN_ASSERT (5, hash [i].count >= SGEN_CEMENT_THRESHOLD, "Cementing hash inconsistent"); + sgen_client_pinned_cemented_object (hash [i].obj); sgen_pin_stage_ptr (hash [i].obj); binary_protocol_cement_stage (hash [i].obj); /* FIXME: do pin stats if enabled */ From 693e9ad44139a100fcfafb7d334bbc9f44a0e309 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 9 Oct 2017 09:39:20 -0400 Subject: [PATCH 2/8] [debug] Improve error message --- mono/profiler/log.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mono/profiler/log.c b/mono/profiler/log.c index a92e724aee274..eaaae6766a872 100644 --- a/mono/profiler/log.c +++ b/mono/profiler/log.c @@ -543,7 +543,8 @@ init_thread (gboolean add_to_lls) */ if (add_to_lls) { MonoThreadHazardPointers *hp = mono_hazard_pointer_get (); - g_assert (mono_lls_insert (&log_profiler.profiler_thread_list, hp, &thread->node) && "Why can't we insert the thread in the LLS?"); + if (!mono_lls_insert (&log_profiler.profiler_thread_list, hp, &thread->node)) + g_error ("%s: failed to insert thread %p in log_profiler.profiler_thread_list, found = %s", __func__, (gpointer) thread->node.key, mono_lls_find (&log_profiler.profiler_thread_list, hp, thread->node.key) ? "true" : "false"); clear_hazard_pointers (hp); } From 1e4898dd6278305a2993a4786e8c1e6ddd6ac68c Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 16 Oct 2017 11:58:50 -0400 Subject: [PATCH 3/8] fixup! [profiler] Rework GC roots reporting. --- .../Mono.Profiler.Log/LogEvents.cs | 10 ++++-- .../Mono.Profiler.Log/LogProcessor.cs | 31 +++++++++---------- mono/profiler/log.c | 6 ++-- mono/profiler/log.h | 4 +-- mono/profiler/mprof-report.c | 4 +-- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs index 19e8cfbea6b9e..fbfbee2a45991 100644 --- a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs +++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs @@ -252,11 +252,17 @@ public sealed class HeapRootsEvent : LogEvent { public struct HeapRoot { - public long AddressPointer { get; internal set; } - public long ObjectPointer { get; internal set; } + + public LogHeapRootAttributes Attributes { get; internal set; } + + public long ExtraInfo { get; internal set; } + + public long AddressPointer { get; internal set; } } + public long MaxGenerationCollectionCount { get; internal set; } + public IReadOnlyList Roots { get; internal set; } internal override void Accept (LogEventVisitor visitor) diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs index 7316144d4fd1e..e5a2b0867447a 100644 --- a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs +++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs @@ -392,41 +392,38 @@ LogEvent ReadEvent () ev = new HeapEndEvent (); break; case LogEventType.HeapObject: { - HeapObjectEvent hoe = new HeapObjectEvent { + ev = new HeapObjectEvent { ObjectPointer = ReadObject (), ClassPointer = ReadPointer (), ObjectSize = (long) _reader.ReadULeb128 (), + References = new HeapObjectEvent.HeapObjectReference [(int) _reader.ReadULeb128 ()], }; - var list = new HeapObjectEvent.HeapObjectReference [(int) _reader.ReadULeb128 ()]; - - for (var i = 0; i < list.Length; i++) { - list [i] = new HeapObjectEvent.HeapObjectReference { + for (var i = 0; i < ev.References.Length; i++) { + ev.References [i] = new HeapObjectEvent.HeapObjectReference { Offset = (long) _reader.ReadULeb128 (), ObjectPointer = ReadObject (), }; } - hoe.References = list; - ev = hoe; - break; } case LogEventType.HeapRoots: { - var hre = new HeapRootsEvent (); - var list = new HeapRootsEvent.HeapRoot [(int) _reader.ReadULeb128 ()]; + ev = new HeapRootsEvent () { + Roots = new HeapRootsEvent.HeapRoot [(int) _reader.ReadULeb128 ()], + MaxGenerationCollectionCount = StreamHeader.FormatVersion < 15 ? (long) _reader.ReadULeb128 () : -1, + }; - for (var i = 0; i < list.Length; i++) { - list [i] = new HeapRootsEvent.HeapRoot { - AddressPointer = ReadPointer (), - ObjectPointer = ReadObject () + for (var i = 0; i < ev.Roots.Length; i++) { + ev.Roots [i] = new HeapRootsEvent.HeapRoot { + ObjectPointer = ReadObject (), + Attributes = StreamHeader.FormatVersion < 13 ? (LogHeapRootAttributes) _reader.ReadULeb128 () : StreamHeader.FormatVersion < 15 ? (LogHeapRootAttributes) _reader.ReadByte () : (LogHeapRootAttributes) -1, + ExtraInfo = StreamHeader.FormatVersion < 15 ? (long) _reader.ReadULeb128 () : 0, + AddressPointer = StreamHeader.FormatVersion < 15 ? -1 : ReadPointer (), }; } - hre.Roots = list; - ev = hre; - break; } case LogEventType.HeapRootRegister: { diff --git a/mono/profiler/log.c b/mono/profiler/log.c index eaaae6766a872..87fe6c823f68a 100644 --- a/mono/profiler/log.c +++ b/mono/profiler/log.c @@ -1207,8 +1207,8 @@ gc_roots (MonoProfiler *prof, uint64_t num, const mono_byte *const *addresses, c EVENT_SIZE /* event */ + LEB128_SIZE /* num */ + num * ( - LEB128_SIZE /* address */ + - LEB128_SIZE /* object */ + LEB128_SIZE /* object */ + + LEB128_SIZE /* address */ ) ); @@ -1216,8 +1216,8 @@ gc_roots (MonoProfiler *prof, uint64_t num, const mono_byte *const *addresses, c emit_value (logbuffer, num); for (int i = 0; i < num; ++i) { + emit_obj (logbuffer, objects [i]); emit_ptr (logbuffer, addresses [i]); - emit_obj (logbuffer, (void*)objects [i]); } EXIT_LOG; diff --git a/mono/profiler/log.h b/mono/profiler/log.h index ef14b3d6e5015..dd5c3532596c0 100644 --- a/mono/profiler/log.h +++ b/mono/profiler/log.h @@ -9,7 +9,7 @@ #define LOG_HEADER_ID 0x4D505A01 #define LOG_VERSION_MAJOR 2 #define LOG_VERSION_MINOR 0 -#define LOG_DATA_VERSION 14 +#define LOG_DATA_VERSION 15 /* * Changes in major/minor versions: @@ -75,7 +75,7 @@ removed type field from TYPE_SAMPLE_HIT removed MONO_GC_EVENT_{MARK,RECLAIM}_{START,END} reverted the root_type field back to uleb128 - GC Roots events now have a different, and saner, format. + * version 15: GC Roots events now have a different, and saner, format. added gc root register/unregister events to be used to source gc root information */ diff --git a/mono/profiler/mprof-report.c b/mono/profiler/mprof-report.c index 679c591773dd7..f2f7dea5d9d05 100644 --- a/mono/profiler/mprof-report.c +++ b/mono/profiler/mprof-report.c @@ -76,7 +76,7 @@ #define HASH_SIZE 9371 #define SMALL_HASH_SIZE 31 -/* Version < 14 root type enum */ +/* Version < 15 root type enum */ typedef enum { /* Upper 2 bytes. */ MONO_PROFILER_GC_ROOT_PINNING = 1 << 8, @@ -2708,7 +2708,7 @@ decode_buffer (ProfContext *ctx) fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num); } else if (subtype == TYPE_HEAP_ROOT) { uintptr_t num; - if (ctx->data_version > 13) { + if (ctx->data_version > 14) { int i; uint64_t tdiff = decode_uleb128 (p + 1, &p); LOG_TIME (time_base, tdiff); From 82f2c60cdf8be7c4a2e1986e74c8071009901a08 Mon Sep 17 00:00:00 2001 From: Rodrigo Kumpera Date: Wed, 25 Oct 2017 10:38:21 -0700 Subject: [PATCH 4/8] [profiler] Fix compilation of the C# lib. --- .../Mono.Profiler.Log/LogProcessor.cs | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs index e5a2b0867447a..cce28a1607afe 100644 --- a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs +++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs @@ -392,15 +392,16 @@ LogEvent ReadEvent () ev = new HeapEndEvent (); break; case LogEventType.HeapObject: { + var refs = Array.Empty (); ev = new HeapObjectEvent { ObjectPointer = ReadObject (), ClassPointer = ReadPointer (), ObjectSize = (long) _reader.ReadULeb128 (), - References = new HeapObjectEvent.HeapObjectReference [(int) _reader.ReadULeb128 ()], + References = refs = new HeapObjectEvent.HeapObjectReference [(int) _reader.ReadULeb128 ()], }; - for (var i = 0; i < ev.References.Length; i++) { - ev.References [i] = new HeapObjectEvent.HeapObjectReference { + for (var i = 0; i < refs.Length; i++) { + refs [i] = new HeapObjectEvent.HeapObjectReference { Offset = (long) _reader.ReadULeb128 (), ObjectPointer = ReadObject (), }; @@ -410,18 +411,35 @@ LogEvent ReadEvent () } case LogEventType.HeapRoots: { + var roots = Array.Empty (); ev = new HeapRootsEvent () { - Roots = new HeapRootsEvent.HeapRoot [(int) _reader.ReadULeb128 ()], + Roots = roots = new HeapRootsEvent.HeapRoot [(int) _reader.ReadULeb128 ()], MaxGenerationCollectionCount = StreamHeader.FormatVersion < 15 ? (long) _reader.ReadULeb128 () : -1, }; - for (var i = 0; i < ev.Roots.Length; i++) { - ev.Roots [i] = new HeapRootsEvent.HeapRoot { - ObjectPointer = ReadObject (), - Attributes = StreamHeader.FormatVersion < 13 ? (LogHeapRootAttributes) _reader.ReadULeb128 () : StreamHeader.FormatVersion < 15 ? (LogHeapRootAttributes) _reader.ReadByte () : (LogHeapRootAttributes) -1, - ExtraInfo = StreamHeader.FormatVersion < 15 ? (long) _reader.ReadULeb128 () : 0, - AddressPointer = StreamHeader.FormatVersion < 15 ? -1 : ReadPointer (), - }; + for (var i = 0; i < roots.Length; i++) { + if (StreamHeader.FormatVersion < 13) { + roots [i] = new HeapRootsEvent.HeapRoot { + ObjectPointer = ReadObject (), + Attributes = (LogHeapRootAttributes) _reader.ReadULeb128 (), + ExtraInfo = (long) _reader.ReadULeb128 (), + AddressPointer = -1, + }; + } else if (StreamHeader.FormatVersion < 15) { + roots [i] = new HeapRootsEvent.HeapRoot { + ObjectPointer = ReadObject (), + Attributes = (LogHeapRootAttributes) _reader.ReadByte (), + ExtraInfo = (long) _reader.ReadULeb128 (), + AddressPointer = -1, + }; + } else { + roots [i] = new HeapRootsEvent.HeapRoot { + ObjectPointer = ReadObject (), + Attributes = (LogHeapRootAttributes) 0, + ExtraInfo = 0, + AddressPointer = ReadPointer (), + }; + } } break; From fb5c193af275ee7eb9e5fce2ed9d2dcb56d6e031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Tue, 28 Nov 2017 02:38:32 +0100 Subject: [PATCH 5/8] [profiler] Add thread_stopping and thread_exited profiler events. thread_stopping occurs earlier than thread_stopped, before any of the detach code has run. thread_exited occurs after thread_stopped, once all detach logic has finished. --- mono/metadata/profiler-events.h | 2 ++ mono/metadata/threads.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/mono/metadata/profiler-events.h b/mono/metadata/profiler-events.h index e557ccb05d051..8e44ae76619d2 100644 --- a/mono/metadata/profiler-events.h +++ b/mono/metadata/profiler-events.h @@ -84,7 +84,9 @@ MONO_PROFILER_EVENT_1(monitor_failed, MonitorFailed, MonoObject *, object) MONO_PROFILER_EVENT_1(monitor_acquired, MonitorAcquired, MonoObject *, object) MONO_PROFILER_EVENT_1(thread_started, ThreadStarted, uintptr_t, tid) +MONO_PROFILER_EVENT_1(thread_stopping, ThreadStopped, uintptr_t, tid) MONO_PROFILER_EVENT_1(thread_stopped, ThreadStopped, uintptr_t, tid) +MONO_PROFILER_EVENT_1(thread_exited, ThreadUnloaded, uintptr_t, tid) MONO_PROFILER_EVENT_2(thread_name, ThreadName, uintptr_t, tid, const char *, name) MONO_PROFILER_EVENT_2(sample_hit, SampleHit, const mono_byte *, ip, const void *, context) diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 1b2efe4df0d65..5f43ce22a3323 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -749,6 +749,10 @@ mono_thread_detach_internal (MonoInternalThread *thread) THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid)); + /* Can happen when we attach the profiler helper thread in order to heapshot. */ + if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread) + MONO_PROFILER_RAISE (thread_stopping, (thread->tid)); + #ifndef HOST_WIN32 mono_w32mutex_abandon (); #endif @@ -889,6 +893,10 @@ mono_thread_detach_internal (MonoInternalThread *thread) mono_thread_info_unset_internal_thread_gchandle ((MonoThreadInfo*) thread->thread_info); + /* Can happen when we attach the profiler helper thread in order to heapshot. */ + if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread) + MONO_PROFILER_RAISE (thread_exited, (thread->tid)); + /* Don't need to close the handle to this thread, even though we took a * reference in mono_thread_attach (), because the GC will do it * when the Thread object is finalised. From 02b5c12a1fa4179010421b8db2fc2252e1107735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Tue, 28 Nov 2017 02:40:07 +0100 Subject: [PATCH 6/8] [profiler] Use thread_exited profiler event for recording thread end events. With the revamped GC root reporting work, some GC root unregister events will arrive after the thread_stopped event. This will cause the thread to be re-added to the thread list in the profiler, and never be removed until program exit. This meant that if such a thread had actually exited and a new thread would reuse its thread ID, that new thread would fail to add itself to the thread list, leading to failures like this one: init_thread: failed to insert thread 0x70000b761000 in log_profiler.profiler_thread_list, found = true By using the new thread_exited event, we remove the thread from the thread list after these GC root unregister events have arrived, thereby ensuring that the thread won't be incorrectly 'resurrected'. --- mono/profiler/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mono/profiler/log.c b/mono/profiler/log.c index da411d40ee62e..575cc5f8b02fb 100644 --- a/mono/profiler/log.c +++ b/mono/profiler/log.c @@ -4726,7 +4726,7 @@ mono_profiler_init_log (const char *desc) mono_profiler_set_gc_event_callback (handle, gc_event); mono_profiler_set_thread_started_callback (handle, thread_start); - mono_profiler_set_thread_stopped_callback (handle, thread_end); + mono_profiler_set_thread_exited_callback (handle, thread_end); mono_profiler_set_thread_name_callback (handle, thread_name); mono_profiler_set_domain_loaded_callback (handle, domain_loaded); From 75ecc2b53bdfdc8c4b7593d49ee04d613901e741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Tue, 28 Nov 2017 02:50:46 +0100 Subject: [PATCH 7/8] [profiler] Remove tools_thread checks when raising profiler thread events. These checks are no longer necessary as these thread callbacks are not invoked for tools threads in the first place. --- mono/metadata/threads.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 5f43ce22a3323..13cfb195a6085 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -749,9 +749,7 @@ mono_thread_detach_internal (MonoInternalThread *thread) THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid)); - /* Can happen when we attach the profiler helper thread in order to heapshot. */ - if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread) - MONO_PROFILER_RAISE (thread_stopping, (thread->tid)); + MONO_PROFILER_RAISE (thread_stopping, (thread->tid)); #ifndef HOST_WIN32 mono_w32mutex_abandon (); @@ -849,11 +847,8 @@ mono_thread_detach_internal (MonoInternalThread *thread) mono_release_type_locks (thread); - /* Can happen when we attach the profiler helper thread in order to heapshot. */ - if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread) { - MONO_PROFILER_RAISE (thread_stopped, (thread->tid)); - MONO_PROFILER_RAISE (gc_root_unregister, (((MonoThreadInfo*)thread->thread_info)->stack_start_limit)); - } + MONO_PROFILER_RAISE (thread_stopped, (thread->tid)); + MONO_PROFILER_RAISE (gc_root_unregister, (((MonoThreadInfo*)thread->thread_info)->stack_start_limit)); mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1); @@ -893,9 +888,7 @@ mono_thread_detach_internal (MonoInternalThread *thread) mono_thread_info_unset_internal_thread_gchandle ((MonoThreadInfo*) thread->thread_info); - /* Can happen when we attach the profiler helper thread in order to heapshot. */ - if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread) - MONO_PROFILER_RAISE (thread_exited, (thread->tid)); + MONO_PROFILER_RAISE (thread_exited, (thread->tid)); /* Don't need to close the handle to this thread, even though we took a * reference in mono_thread_attach (), because the GC will do it @@ -1300,9 +1293,7 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach) if (mono_thread_attach_cb) mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), info->stack_end); - /* Can happen when we attach the profiler helper thread in order to heapshot. */ - if (!mono_thread_info_current ()->tools_thread) - fire_attach_profiler_events (tid); + fire_attach_profiler_events (tid); return thread; } From c98ca41d00db70a8409169faa40f44e819906a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Tue, 28 Nov 2017 08:26:52 +0100 Subject: [PATCH 8/8] [profiler] Add an assertion to the log profiler to catch the thread resurrection case. --- mono/profiler/log.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/mono/profiler/log.c b/mono/profiler/log.c index 575cc5f8b02fb..c823c8f1672ed 100644 --- a/mono/profiler/log.c +++ b/mono/profiler/log.c @@ -166,6 +166,12 @@ typedef struct { int small_id; } MonoProfilerThread; +// Default value in `profiler_tls` for new threads. +#define MONO_PROFILER_THREAD_ZERO ((MonoProfilerThread *) NULL) + +// This is written to `profiler_tls` to indicate that a thread has stopped. +#define MONO_PROFILER_THREAD_DEAD ((MonoProfilerThread *) -1) + // Do not use these TLS macros directly unless you know what you're doing. #ifdef HOST_WIN32 @@ -234,19 +240,19 @@ process_id (void) #define ENTER_LOG(COUNTER, BUFFER, SIZE) \ do { \ MonoProfilerThread *thread__ = get_thread (); \ - if (thread__->attached) \ - buffer_lock (); \ g_assert (!thread__->busy && "Why are we trying to write a new event while already writing one?"); \ thread__->busy = TRUE; \ mono_atomic_inc_i32 ((COUNTER)); \ + if (thread__->attached) \ + buffer_lock (); \ LogBuffer *BUFFER = ensure_logbuf_unsafe (thread__, (SIZE)) #define EXIT_LOG_EXPLICIT(SEND) \ - thread__->busy = FALSE; \ if ((SEND)) \ send_log_unsafe (TRUE); \ if (thread__->attached) \ buffer_unlock (); \ + thread__->busy = FALSE; \ } while (0) // Pass these to EXIT_LOG_EXPLICIT () for easier reading. @@ -512,6 +518,8 @@ init_thread (gboolean add_to_lls) { MonoProfilerThread *thread = PROF_TLS_GET (); + g_assert (thread != MONO_PROFILER_THREAD_DEAD && "Why are we trying to resurrect a stopped thread?"); + /* * Sometimes we may try to initialize a thread twice. One example is the * main thread: We initialize it when setting up the profiler, but we will @@ -523,14 +531,14 @@ init_thread (gboolean add_to_lls) * These cases are harmless anyhow. Just return if we've already done the * initialization work. */ - if (thread) + if (thread != MONO_PROFILER_THREAD_ZERO) return thread; thread = g_malloc (sizeof (MonoProfilerThread)); thread->node.key = thread_id (); thread->attached = add_to_lls; thread->call_depth = 0; - thread->busy = 0; + thread->busy = FALSE; thread->ended = FALSE; init_buffer_state (thread); @@ -560,7 +568,7 @@ deinit_thread (MonoProfilerThread *thread) g_assert (!thread->attached && "Why are we manually freeing an attached thread?"); g_free (thread); - PROF_TLS_SET (NULL); + PROF_TLS_SET (MONO_PROFILER_THREAD_DEAD); } static MonoProfilerThread * @@ -2040,7 +2048,7 @@ thread_end (MonoProfiler *prof, uintptr_t tid) thread->ended = TRUE; remove_thread (thread); - PROF_TLS_SET (NULL); + PROF_TLS_SET (MONO_PROFILER_THREAD_DEAD); } static void