Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac6
-rw-r--r--mcs/class/corlib/Mono/Runtime.cs44
-rw-r--r--mcs/class/corlib/Test/System/ExceptionTest.cs98
-rw-r--r--mono/metadata/class-internals.h3
-rw-r--r--mono/metadata/debug-helpers.c8
-rw-r--r--mono/metadata/domain-internals.h2
-rw-r--r--mono/metadata/icall-decl.h3
-rw-r--r--mono/metadata/icall-def.h3
-rw-r--r--mono/metadata/icall.c87
-rw-r--r--mono/metadata/mono-debug.c34
-rw-r--r--mono/metadata/object-internals.h4
-rw-r--r--mono/metadata/threads-types.h40
-rw-r--r--mono/metadata/threads.c500
-rw-r--r--mono/mini/aot-runtime.c2
-rw-r--r--mono/mini/debugger-engine.c47
-rw-r--r--mono/mini/interp/transform.c6
-rw-r--r--mono/mini/mini-exceptions.c275
-rw-r--r--mono/mini/mini-posix.c32
-rw-r--r--mono/mini/mini-runtime.c34
-rw-r--r--mono/mini/mini-runtime.h1
-rw-r--r--mono/mini/mini.c2
-rw-r--r--mono/mini/seq-points.c5
-rw-r--r--mono/mini/seq-points.h2
-rw-r--r--mono/utils/mono-state.c110
-rw-r--r--mono/utils/mono-state.h2
-rw-r--r--mono/utils/mono-threads-posix.c13
-rw-r--r--mono/utils/mono-threads.h3
27 files changed, 1000 insertions, 366 deletions
diff --git a/configure.ac b/configure.ac
index 92458681819..d0e1e5253e0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,7 +46,7 @@ MONO_VERSION_BUILD=`echo $VERSION | cut -d . -f 3`
# There is no ordering of corlib versions, no old or new,
# the runtime expects an exact match.
#
-MONO_CORLIB_VERSION=E06DCE2D-7852-4BBA-AD9F-54D67EEF1FF9
+MONO_CORLIB_VERSION=5F2CEB7B-13B1-4694-A0D5-3DD7EDE81388
#
# Put a quoted #define in config.h.
@@ -2425,7 +2425,7 @@ if test x$host_win32 = xno; then
AC_CHECK_HEADERS(pthread.h)
AC_CHECK_HEADERS(pthread_np.h)
AC_CHECK_FUNCS(pthread_mutex_timedlock)
- AC_CHECK_FUNCS(pthread_getattr_np pthread_attr_get_np pthread_setname_np pthread_cond_timedwait_relative_np)
+ AC_CHECK_FUNCS(pthread_getattr_np pthread_attr_get_np pthread_getname_np pthread_setname_np pthread_cond_timedwait_relative_np)
AC_CHECK_FUNCS(pthread_kill)
AC_MSG_CHECKING(for PTHREAD_MUTEX_RECURSIVE)
AC_TRY_COMPILE([ #include <pthread.h>], [
@@ -5766,7 +5766,7 @@ echo "
zlib: $zlib_msg
BTLS: $enable_btls$btls_platform_string
jemalloc: $with_jemalloc (always use: $with_jemalloc_always)
- crash reporting: $crash_reporting
+ crash reporting: $crash_reporting (private crashes: $with_crash_privacy)
$disabled
"
if test x$with_static_mono = xno -a "x$host_win32" != "xyes"; then
diff --git a/mcs/class/corlib/Mono/Runtime.cs b/mcs/class/corlib/Mono/Runtime.cs
index 133e824eb38..34d0669088f 100644
--- a/mcs/class/corlib/Mono/Runtime.cs
+++ b/mcs/class/corlib/Mono/Runtime.cs
@@ -106,17 +106,17 @@ namespace Mono {
static extern void SendMicrosoftTelemetry_internal (IntPtr payload, ulong portable_hash, ulong unportable_hash);
[MethodImplAttribute (MethodImplOptions.InternalCall)]
- static extern void WriteStateToDisk_internal (IntPtr payload, ulong portable_hash, ulong unportable_hash);
+ static extern void WriteStateToFile_internal (IntPtr payload, ulong portable_hash, ulong unportable_hash);
static void
- WriteStateToDisk (Exception exc)
+ WriteStateToFile (Exception exc)
{
ulong portable_hash;
ulong unportable_hash;
string payload_str = ExceptionToState_internal (exc, out portable_hash, out unportable_hash);
using (var payload_chars = RuntimeMarshal.MarshalString (payload_str))
{
- WriteStateToDisk_internal (payload_chars.Value, portable_hash, unportable_hash);
+ WriteStateToFile_internal (payload_chars.Value, portable_hash, unportable_hash);
}
}
@@ -165,5 +165,43 @@ namespace Mono {
}
#endif
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ static extern string DumpStateSingle_internal (out ulong portable_hash, out ulong unportable_hash);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ static extern string DumpStateTotal_internal (out ulong portable_hash, out ulong unportable_hash);
+
+ static Tuple<String, ulong, ulong>
+ DumpStateSingle ()
+ {
+ ulong portable_hash;
+ ulong unportable_hash;
+ string payload_str = DumpStateSingle_internal (out portable_hash, out unportable_hash);
+
+ return new Tuple<String, ulong, ulong> (payload_str, portable_hash, unportable_hash);
+ }
+
+ static Tuple<String, ulong, ulong>
+ DumpStateTotal ()
+ {
+ ulong portable_hash;
+ ulong unportable_hash;
+ string payload_str = DumpStateTotal_internal (out portable_hash, out unportable_hash);
+
+ return new Tuple<String, ulong, ulong> (payload_str, portable_hash, unportable_hash);
+ }
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ static extern void RegisterReportingForNativeLib_internal (IntPtr modulePathSuffix, IntPtr moduleName);
+
+ static void RegisterReportingForNativeLib (string modulePathSuffix_str, string moduleName_str)
+ {
+ using (var modulePathSuffix_chars = RuntimeMarshal.MarshalString (modulePathSuffix_str))
+ using (var moduleName_chars = RuntimeMarshal.MarshalString (moduleName_str))
+ {
+ RegisterReportingForNativeLib_internal (modulePathSuffix_chars.Value, moduleName_chars.Value);
+ }
+ }
+
}
}
diff --git a/mcs/class/corlib/Test/System/ExceptionTest.cs b/mcs/class/corlib/Test/System/ExceptionTest.cs
index 3563d9a4033..97123e9ab25 100644
--- a/mcs/class/corlib/Test/System/ExceptionTest.cs
+++ b/mcs/class/corlib/Test/System/ExceptionTest.cs
@@ -17,6 +17,8 @@ using System.Reflection;
using NUnit.Framework;
+using System.Threading.Tasks;
+
namespace MonoTests.System
{
[TestFixture]
@@ -473,6 +475,102 @@ namespace MonoTests.System
// }
}
}
+
+ void DumpSingle ()
+ {
+ var monoType = Type.GetType ("Mono.Runtime", false);
+ var convert = monoType.GetMethod("DumpStateSingle", BindingFlags.NonPublic | BindingFlags.Static);
+ var output = (Tuple<String, ulong, ulong>) convert.Invoke(null, Array.Empty<object> ());
+
+ var dump = output.Item1;
+ var portable_hash = output.Item2;
+ var unportable_hash = output.Item3;
+
+ Assert.IsTrue (portable_hash != 0, "#1");
+ Assert.IsTrue (unportable_hash != 0, "#2");
+ Assert.IsTrue (dump.Length > 0, "#3");
+ }
+
+ void DumpTotal ()
+ {
+ var monoType = Type.GetType ("Mono.Runtime", false);
+ var convert = monoType.GetMethod("DumpStateTotal", BindingFlags.NonPublic | BindingFlags.Static);
+ var output = (Tuple<String, ulong, ulong>) convert.Invoke(null, Array.Empty<object> ());
+
+ var dump = output.Item1;
+ var portable_hash = output.Item2;
+ var unportable_hash = output.Item3;
+
+ Assert.IsTrue (portable_hash != 0, "#1");
+ Assert.IsTrue (unportable_hash != 0, "#2");
+ Assert.IsTrue (dump.Length > 0, "#3");
+ }
+
+ [Test]
+ [Category("NotOnWindows")]
+ public void DumpICallSingleOnce ()
+ {
+ DumpSingle ();
+ }
+
+ [Test]
+ [Category("NotOnWindows")]
+ public void DumpICallSingleConcurrent ()
+ {
+ // checks that self-dumping works in parallel, locklessly
+ int amt = 20;
+ var tasks = new Task [amt];
+ for (int i=0; i < amt; i++)
+ tasks [i] = Task.Run(() => DumpSingle ());
+ Task.WaitAll (tasks);
+ }
+
+ [Test]
+ [Category("NotOnWindows")]
+ public void DumpICallSingleAsync ()
+ {
+ // checks that dumping works in an async context
+ var t = Task.Run(() => DumpSingle ());
+ t.Wait ();
+ }
+
+ [Test]
+ [Category("NotOnWindows")]
+ public void DumpICallTotalOnce ()
+ {
+ DumpTotal ();
+ }
+
+ [Test]
+ [Category("NotOnWindows")]
+ public void DumpICallTotalRepeated ()
+ {
+ // checks that the state doesn't get broken with repeated use
+ DumpTotal ();
+ DumpTotal ();
+ DumpTotal ();
+ }
+
+ [Test]
+ [Category("NotOnWindows")]
+ public void DumpICallTotalAsync ()
+ {
+ // checks that dumping works in an async context
+ var t = Task.Run(() => DumpTotal ());
+ t.Wait ();
+ }
+
+ [Test]
+ [Category("NotOnWindows")]
+ public void DumpICallTotalConcurrent ()
+ {
+ int amt = 3;
+ var tasks = new Task [amt];
+ for (int i=0; i < amt; i++)
+ tasks [i] = Task.Run(() => DumpTotal ());
+ Task.WaitAll (tasks);
+ }
+
#endif
[Test]
diff --git a/mono/metadata/class-internals.h b/mono/metadata/class-internals.h
index 3e5636a1cbf..fe46bea70a2 100644
--- a/mono/metadata/class-internals.h
+++ b/mono/metadata/class-internals.h
@@ -1109,6 +1109,9 @@ mono_method_get_name_full (MonoMethod *method, gboolean signature, gboolean ret,
char *
mono_method_get_full_name (MonoMethod *method);
+const char*
+mono_wrapper_type_to_str (guint32 wrapper_type);
+
MonoArrayType *mono_dup_array_type (MonoImage *image, MonoArrayType *a);
MonoMethodSignature *mono_metadata_signature_deep_dup (MonoImage *image, MonoMethodSignature *sig);
diff --git a/mono/metadata/debug-helpers.c b/mono/metadata/debug-helpers.c
index 06d7b76b016..3d3c6cfb403 100644
--- a/mono/metadata/debug-helpers.c
+++ b/mono/metadata/debug-helpers.c
@@ -59,8 +59,8 @@ static const gint16 opidx [] = {
#undef WRAPPER
};
-static const char*
-wrapper_type_to_str (guint32 wrapper_type)
+const char*
+mono_wrapper_type_to_str (guint32 wrapper_type)
{
g_assert (wrapper_type < MONO_WRAPPER_NUM);
@@ -902,7 +902,7 @@ mono_method_get_name_full (MonoMethod *method, gboolean signature, gboolean ret,
}
if (method->wrapper_type != MONO_WRAPPER_NONE)
- sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type));
+ sprintf (wrapper, "(wrapper %s) ", mono_wrapper_type_to_str (method->wrapper_type));
else
strcpy (wrapper, "");
@@ -918,7 +918,7 @@ mono_method_get_name_full (MonoMethod *method, gboolean signature, gboolean ret,
}
if (method->wrapper_type != MONO_WRAPPER_NONE)
- sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type));
+ sprintf (wrapper, "(wrapper %s) ", mono_wrapper_type_to_str (method->wrapper_type));
else
strcpy (wrapper, "");
if (ret && sig) {
diff --git a/mono/metadata/domain-internals.h b/mono/metadata/domain-internals.h
index b3b51497a87..375e955aa23 100644
--- a/mono/metadata/domain-internals.h
+++ b/mono/metadata/domain-internals.h
@@ -253,6 +253,8 @@ struct _MonoJitInfo {
/* FIXME: Embed this after the structure later*/
gpointer gc_info; /* Currently only used by SGen */
+
+ gpointer seq_points;
MonoJitExceptionInfo clauses [MONO_ZERO_LEN_ARRAY];
/* There is an optional MonoGenericJitInfo after the clauses */
diff --git a/mono/metadata/icall-decl.h b/mono/metadata/icall-decl.h
index 10d7ae276f5..560f3f7ca05 100644
--- a/mono/metadata/icall-decl.h
+++ b/mono/metadata/icall-decl.h
@@ -379,5 +379,8 @@ ICALL_EXPORT void ves_icall_System_Runtime_RuntimeImports_ZeroMemory (guint8*, g
ICALL_EXPORT void ves_icall_System_Runtime_RuntimeImports_ecvt_s(char*, size_t, double, int, int*, int*);
ICALL_EXPORT void ves_icall_get_method_info (MonoMethod*, MonoMethodInfo*, MonoError*);
ICALL_EXPORT void* ves_icall_System_Reflection_Assembly_GetManifestResourceInternal (MonoReflectionAssemblyHandle assembly_h, MonoStringHandle name, gint32* size, MonoReflectionModuleHandleOut ref_module, MonoError*);
+ICALL_EXPORT MonoStringHandle ves_icall_Mono_Runtime_DumpStateSingle (guint64 *portable_hash, guint64 *unportable_hash, MonoError *error);
+ICALL_EXPORT MonoStringHandle ves_icall_Mono_Runtime_DumpStateTotal (guint64 *portable_hash, guint64 *unportable_hash, MonoError *error);
+ICALL_EXPORT void ves_icall_Mono_Runtime_RegisterReportingForNativeLib (const char *path_suffix, const char *module_name);
#endif // __MONO_METADATA_ICALL_H__
diff --git a/mono/metadata/icall-def.h b/mono/metadata/icall-def.h
index 42a1907cfba..0c0c42c0756 100644
--- a/mono/metadata/icall-def.h
+++ b/mono/metadata/icall-def.h
@@ -91,10 +91,13 @@ HANDLES(ICALL(TLS_PROVIDER_FACTORY_1, "IsBtlsSupported", ves_icall_Mono_TlsProvi
ICALL_TYPE(RUNTIME, "Mono.Runtime", RUNTIME_1)
HANDLES(ICALL(RUNTIME_1, "DisableMicrosoftTelemetry", ves_icall_Mono_Runtime_DisableMicrosoftTelemetry))
+HANDLES(ICALL(RUNTIME_15, "DumpStateSingle_internal", ves_icall_Mono_Runtime_DumpStateSingle))
+HANDLES(ICALL(RUNTIME_16, "DumpStateTotal_internal", ves_icall_Mono_Runtime_DumpStateTotal))
HANDLES(ICALL(RUNTIME_2, "EnableMicrosoftTelemetry_internal", ves_icall_Mono_Runtime_EnableMicrosoftTelemetry))
HANDLES(ICALL(RUNTIME_3, "ExceptionToState_internal", ves_icall_Mono_Runtime_ExceptionToState))
HANDLES(ICALL(RUNTIME_4, "GetDisplayName", ves_icall_Mono_Runtime_GetDisplayName))
HANDLES(ICALL(RUNTIME_12, "GetNativeStackTrace", ves_icall_Mono_Runtime_GetNativeStackTrace))
+HANDLES(ICALL(RUNTIME_17, "RegisterReportingForNativeLib_internal", ves_icall_Mono_Runtime_RegisterReportingForNativeLib))
HANDLES(ICALL(RUNTIME_13, "SendMicrosoftTelemetry_internal", ves_icall_Mono_Runtime_SendMicrosoftTelemetry))
HANDLES(ICALL(RUNTIME_14, "WriteStateToFile_internal", ves_icall_Mono_Runtime_DumpTelemetry))
diff --git a/mono/metadata/icall.c b/mono/metadata/icall.c
index 2be713caed9..30a57cacb08 100644
--- a/mono/metadata/icall.c
+++ b/mono/metadata/icall.c
@@ -5866,6 +5866,93 @@ ves_icall_Mono_Runtime_DumpTelemetry (char *payload, guint64 portable_hash, guin
#endif
}
+ICALL_EXPORT MonoStringHandle
+ves_icall_Mono_Runtime_DumpStateSingle (guint64 *portable_hash, guint64 *unportable_hash, MonoError *error)
+{
+ MonoStringHandle result;
+
+#ifndef DISABLE_CRASH_REPORTING
+ MonoStackHash hashes;
+ memset (&hashes, 0, sizeof (MonoStackHash));
+ MonoContext *ctx = NULL;
+
+ MonoThreadSummary this_thread;
+ if (!mono_threads_summarize_one (&this_thread, ctx))
+ return mono_string_new_handle (mono_domain_get (), "", error);
+
+ *portable_hash = (guint64) this_thread.hashes.offset_free_hash;
+ *unportable_hash = (guint64) this_thread.hashes.offset_rich_hash;
+
+ JsonWriter writer;
+ mono_json_writer_init (&writer);
+ mono_native_state_init (&writer);
+ gboolean first_thread_added = TRUE;
+ mono_native_state_add_thread (&writer, &this_thread, NULL, first_thread_added);
+ char *output = mono_native_state_free (&writer, FALSE);
+ result = mono_string_new_handle (mono_domain_get (), output, error);
+ g_free (output);
+#else
+ *portable_hash = 0;
+ *unportable_hash = 0;
+ result = mono_string_new_handle (mono_domain_get (), "", error);
+#endif
+
+ return result;
+}
+
+
+ICALL_EXPORT void
+ves_icall_Mono_Runtime_RegisterReportingForNativeLib (const char *path_suffix, const char *module_name)
+{
+#ifndef DISABLE_CRASH_REPORTING
+ mono_get_eh_callbacks ()->mono_register_native_library (path_suffix, module_name);
+#endif
+}
+
+// Number derived from trials on relevant hardware.
+// If it seems large, please confirm it's safe to shrink
+// before doing so.
+#define MONO_MAX_SUMMARY_LEN_ICALL 500000
+
+ICALL_EXPORT MonoStringHandle
+ves_icall_Mono_Runtime_DumpStateTotal (guint64 *portable_hash, guint64 *unportable_hash, MonoError *error)
+{
+ MonoStringHandle result;
+
+#ifndef DISABLE_CRASH_REPORTING
+ char *scratch = g_new0 (gchar, MONO_MAX_SUMMARY_LEN_ICALL);
+
+ char *out;
+ MonoStackHash hashes;
+ memset (&hashes, 0, sizeof (MonoStackHash));
+ MonoContext *ctx = NULL;
+
+ mono_get_runtime_callbacks ()->install_state_summarizer ();
+
+ // if this works
+ // - give back loader lock during stop
+ mono_thread_info_set_is_async_context (TRUE);
+ gboolean success = mono_threads_summarize (ctx, &out, &hashes, TRUE, FALSE, scratch, MONO_MAX_SUMMARY_LEN_ICALL);
+ mono_thread_info_set_is_async_context (FALSE);
+
+ if (!success)
+ return mono_string_new_handle (mono_domain_get (), "", error);
+
+ *portable_hash = (guint64) hashes.offset_free_hash;
+ *unportable_hash = (guint64) hashes.offset_rich_hash;
+ result = mono_string_new_handle (mono_domain_get (), out, error);
+
+ // out is now a pointer into garbage memory
+ g_free (scratch);
+#else
+ *portable_hash = 0;
+ *unportable_hash = 0;
+ result = mono_string_new_handle (mono_domain_get (), "", error);
+#endif
+
+ return result;
+}
+
ICALL_EXPORT MonoBoolean
ves_icall_System_Reflection_AssemblyName_ParseAssemblyName (const char *name, MonoAssemblyName *aname, MonoBoolean *is_version_defined_arg, MonoBoolean *is_token_defined_arg)
{
diff --git a/mono/metadata/mono-debug.c b/mono/metadata/mono-debug.c
index 7da5a60e7dd..93203b52f1d 100644
--- a/mono/metadata/mono-debug.c
+++ b/mono/metadata/mono-debug.c
@@ -610,8 +610,8 @@ read_variable (MonoDebugVarInfo *var, guint8 *ptr, guint8 **rptr)
*rptr = ptr;
}
-void
-mono_debug_free_method_jit_info (MonoDebugMethodJitInfo *jit)
+static void
+mono_debug_free_method_jit_info_full (MonoDebugMethodJitInfo *jit, gboolean stack)
{
if (!jit)
return;
@@ -621,17 +621,23 @@ mono_debug_free_method_jit_info (MonoDebugMethodJitInfo *jit)
g_free (jit->locals);
g_free (jit->gsharedvt_info_var);
g_free (jit->gsharedvt_locals_var);
- g_free (jit);
+ if (!stack)
+ g_free (jit);
+}
+
+void
+mono_debug_free_method_jit_info (MonoDebugMethodJitInfo *jit)
+{
+ return mono_debug_free_method_jit_info_full (jit, FALSE);
}
static MonoDebugMethodJitInfo *
-mono_debug_read_method (MonoDebugMethodAddress *address)
+mono_debug_read_method (MonoDebugMethodAddress *address, MonoDebugMethodJitInfo *jit)
{
- MonoDebugMethodJitInfo *jit;
guint32 i;
guint8 *ptr;
- jit = g_new0 (MonoDebugMethodJitInfo, 1);
+ memset (jit, 0, sizeof (*jit));
jit->code_start = address->code_start;
jit->code_size = address->code_size;
@@ -677,7 +683,7 @@ mono_debug_read_method (MonoDebugMethodAddress *address)
}
static MonoDebugMethodJitInfo *
-find_method (MonoMethod *method, MonoDomain *domain)
+find_method (MonoMethod *method, MonoDomain *domain, MonoDebugMethodJitInfo *jit)
{
MonoDebugDataTable *table;
MonoDebugMethodAddress *address;
@@ -688,19 +694,19 @@ find_method (MonoMethod *method, MonoDomain *domain)
if (!address)
return NULL;
- return mono_debug_read_method (address);
+ return mono_debug_read_method (address, jit);
}
MonoDebugMethodJitInfo *
mono_debug_find_method (MonoMethod *method, MonoDomain *domain)
{
- MonoDebugMethodJitInfo *res;
+ MonoDebugMethodJitInfo *res = g_new0 (MonoDebugMethodJitInfo, 1);
if (mono_debug_format == MONO_DEBUG_FORMAT_NONE)
return NULL;
mono_debugger_lock ();
- res = find_method (method, domain);
+ find_method (method, domain, res);
mono_debugger_unlock ();
return res;
}
@@ -715,10 +721,10 @@ mono_debug_lookup_method_addresses (MonoMethod *method)
static gint32
il_offset_from_address (MonoMethod *method, MonoDomain *domain, guint32 native_offset)
{
- MonoDebugMethodJitInfo *jit;
+ MonoDebugMethodJitInfo mem;
int i;
- jit = find_method (method, domain);
+ MonoDebugMethodJitInfo *jit = find_method (method, domain, &mem);
if (!jit || !jit->line_numbers)
goto cleanup_and_fail;
@@ -726,13 +732,13 @@ il_offset_from_address (MonoMethod *method, MonoDomain *domain, guint32 native_o
MonoDebugLineNumberEntry lne = jit->line_numbers [i];
if (lne.native_offset <= native_offset) {
- mono_debug_free_method_jit_info (jit);
+ mono_debug_free_method_jit_info_full (jit, TRUE);
return lne.il_offset;
}
}
cleanup_and_fail:
- mono_debug_free_method_jit_info (jit);
+ mono_debug_free_method_jit_info_full (jit, TRUE);
return -1;
}
diff --git a/mono/metadata/object-internals.h b/mono/metadata/object-internals.h
index d2a6448e341..188aad2783d 100644
--- a/mono/metadata/object-internals.h
+++ b/mono/metadata/object-internals.h
@@ -701,8 +701,10 @@ typedef struct {
gboolean (*mono_above_abort_threshold) (void);
void (*mono_clear_abort_threshold) (void);
void (*mono_reraise_exception) (MonoException *ex);
- void (*mono_summarize_stack) (MonoDomain *domain, MonoThreadSummary *out, MonoContext *crash_ctx);
+ void (*mono_summarize_managed_stack) (MonoThreadSummary *out);
+ void (*mono_summarize_unmanaged_stack) (MonoThreadSummary *out);
void (*mono_summarize_exception) (MonoException *exc, MonoThreadSummary *out);
+ void (*mono_register_native_library) (const char *module_path, const char *module_name);
} MonoRuntimeExceptionHandlingCallbacks;
MONO_COLD void mono_set_pending_exception (MonoException *exc);
diff --git a/mono/metadata/threads-types.h b/mono/metadata/threads-types.h
index 02c001495d8..50b685555e6 100644
--- a/mono/metadata/threads-types.h
+++ b/mono/metadata/threads-types.h
@@ -539,8 +539,9 @@ MONO_COLD void
mono_set_pending_exception_handle (MonoExceptionHandle exc);
#define MONO_MAX_SUMMARY_NAME_LEN 140
+#define MONO_MAX_THREAD_NAME_LEN 140
#define MONO_MAX_SUMMARY_THREADS 32
-#define MONO_MAX_SUMMARY_FRAMES 40
+#define MONO_MAX_SUMMARY_FRAMES 80
typedef struct {
gboolean is_managed;
@@ -550,9 +551,17 @@ typedef struct {
int il_offset;
int native_offset;
const char *guid;
+
+#ifndef MONO_PRIVATE_CRASHES
+ // We use ifdef to make it a compile-time error to store this
+ // symbolicated string on release builds
+ const char *name;
+#endif
+
} managed_data;
struct {
intptr_t ip;
+ const char *module;
gboolean is_trampoline;
gboolean has_name;
} unmanaged_data;
@@ -564,13 +573,29 @@ typedef struct {
} MonoStackHash;
typedef struct {
+ gboolean done; // Needed because cond wait can have spurious wakeups
+ MonoSemType done_wait; // Readers are finished with this
+
+ // For managed stack walking
+
+ MonoDomain *domain;
+ MonoJitTlsData *jit_tls;
+ MonoLMF *lmf;
+
+ // Emitted attributes
+
gboolean is_managed;
- const char *name;
+ char name [MONO_MAX_THREAD_NAME_LEN];
+
intptr_t managed_thread_ptr;
intptr_t info_addr;
intptr_t native_thread_id;
+ // Print reason we don't have a complete
+ // managed trace
+ const char *error_msg;
+
int num_managed_frames;
MonoFrameSummary managed_frames [MONO_MAX_SUMMARY_FRAMES];
@@ -578,9 +603,18 @@ typedef struct {
MonoFrameSummary unmanaged_frames [MONO_MAX_SUMMARY_FRAMES];
MonoStackHash hashes;
+
+ MonoContext *ctx;
+ MonoContext ctx_mem;
} MonoThreadSummary;
gboolean
-mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes);
+mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gboolean signal_handler_controller, gchar *mem, size_t provided_size);
+
+gboolean
+mono_threads_summarize_execute (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gchar *mem, size_t provided_size);
+
+gboolean
+mono_threads_summarize_one (MonoThreadSummary *out, MonoContext *ctx);
#endif /* _MONO_METADATA_THREADS_TYPES_H_ */
diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c
index 30b8807eedd..dd096922457 100644
--- a/mono/metadata/threads.c
+++ b/mono/metadata/threads.c
@@ -3809,9 +3809,15 @@ get_thread_dump (MonoThreadInfo *info, gpointer ud)
typedef struct {
int nthreads, max_threads;
+
guint32 *threads;
} CollectThreadsUserData;
+typedef struct {
+ int nthreads, max_threads;
+ MonoNativeThreadId *threads;
+} CollectThreadIdsUserData;
+
static void
collect_thread (gpointer key, gpointer value, gpointer user)
{
@@ -3822,6 +3828,16 @@ collect_thread (gpointer key, gpointer value, gpointer user)
ud->threads [ud->nthreads ++] = mono_gchandle_new (&thread->obj, TRUE);
}
+static void
+collect_thread_id (gpointer key, gpointer value, gpointer user)
+{
+ CollectThreadIdsUserData *ud = (CollectThreadIdsUserData *)user;
+ MonoInternalThread *thread = (MonoInternalThread *)value;
+
+ if (ud->nthreads < ud->max_threads)
+ ud->threads [ud->nthreads ++] = thread_get_tid (thread);
+}
+
/*
* Collect running threads into the THREADS array.
* THREADS should be an array allocated on the stack.
@@ -3847,6 +3863,27 @@ collect_threads (guint32 *thread_handles, int max_threads)
return ud.nthreads;
}
+static int
+collect_thread_ids (MonoNativeThreadId *thread_ids, int max_threads)
+{
+ CollectThreadIdsUserData ud;
+
+ mono_memory_barrier ();
+ if (!threads)
+ return 0;
+
+ memset (&ud, 0, sizeof (ud));
+ /* This array contains refs, but its on the stack, so its ok */
+ ud.threads = thread_ids;
+ ud.max_threads = max_threads;
+
+ mono_threads_lock ();
+ mono_g_hash_table_foreach (threads, collect_thread_id, &ud);
+ mono_threads_unlock ();
+
+ return ud.nthreads;
+}
+
static void
dump_thread (MonoInternalThread *thread, ThreadDumpUserData *ud, FILE* output_file)
{
@@ -6021,291 +6058,324 @@ mono_set_thread_dump_dir (gchar* dir) {
#ifdef DISABLE_CRASH_REPORTING
gboolean
-mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes)
+mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gboolean signal_handler_controller, gchar *mem, size_t provided_size)
{
return FALSE;
}
-#else
+gboolean
+mono_threads_summarize_one (MonoThreadSummary *out, MonoContext *ctx)
+{
+ return FALSE;
+}
-static size_t num_threads_summarized = 0;
+#else
-// mono_thread_internal_current doesn't always work in signal
-// handler contexts. This does.
static gboolean
-find_missing_thread (MonoNativeThreadId id, guint32 *out)
+mono_threads_summarize_native_self (MonoThreadSummary *out, MonoContext *ctx)
{
- guint32 thread_array [128];
- int nthreads = collect_threads (thread_array, 128);
- gboolean success = FALSE;
+ memset (out, 0, sizeof (MonoThreadSummary));
+ out->ctx = ctx;
- for (int i=0; i < nthreads; i++) {
- guint32 handle = thread_array [i];
- MonoInternalThread *thread = (MonoInternalThread *) mono_gchandle_get_target (handle);
- MonoNativeThreadId tid = thread_get_tid (thread);
- if (tid == id) {
- *out = handle;
- success = TRUE;
- } else {
- mono_gchandle_free (handle);
- }
- }
+ MonoNativeThreadId current = mono_native_thread_id_get();
+ out->native_thread_id = (intptr_t) current;
- return success;
+ mono_get_eh_callbacks ()->mono_summarize_unmanaged_stack (out);
+
+ mono_native_thread_get_name (current, out->name, MONO_MAX_SUMMARY_NAME_LEN);
+
+ // FIXME: Figure out how to store and look these up?
+ /*MonoDomain *domain = thread->obj.vtable->domain;*/
+ /*out->managed_thread_ptr = (intptr_t) get_current_thread_ptr_for_domain (domain, thread);*/
+ /*out->info_addr = (intptr_t) thread->thread_info;*/
+ return TRUE;
}
-static gboolean
+// Not safe to call from signal handler
+gboolean
mono_threads_summarize_one (MonoThreadSummary *out, MonoContext *ctx)
{
- MonoDomain *domain;
- guint32 handle;
+ gboolean success = mono_threads_summarize_native_self (out, ctx);
- MonoNativeThreadId current = mono_native_thread_id_get();
+ // Finish this on the same thread
- // Not one of ours
- if (!find_missing_thread (current, &handle))
- return FALSE;
+ if (success)
+ mono_get_eh_callbacks ()->mono_summarize_managed_stack (out);
- MonoInternalThread *thread = (MonoInternalThread *) mono_gchandle_get_target (handle);
+ return success;
+}
- memset (out, 0, sizeof (MonoThreadSummary));
- domain = thread->obj.vtable->domain;
- out->native_thread_id = (intptr_t) thread_get_tid (thread);
- out->managed_thread_ptr = (intptr_t) get_current_thread_ptr_for_domain (domain, thread);
- out->info_addr = (intptr_t) thread->thread_info;
- if (thread->name) {
- char *name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, NULL);
- out->name = name;
- }
- mono_get_eh_callbacks ()->mono_summarize_stack (domain, out, ctx);
+#define MAX_NUM_THREADS 128
+typedef struct {
+ gint32 has_owner; // state of this memory
- // FIXME: handle failure gracefully
- // Enable when doing unmanaged
- /*g_assert (out->num_frames > 0);*/
- /*if (out->num_frames == 0)*/
- /*return FALSE;*/
+ MonoSemType update; // notify of addition of threads
- mono_gchandle_free (handle);
+ int nthreads;
+ MonoNativeThreadId thread_array [MAX_NUM_THREADS]; // ids of threads we're dumping
- return TRUE;
-}
+ int nthreads_attached; // Number of threads self-registered
+ MonoThreadSummary *all_threads [MAX_NUM_THREADS];
-typedef enum {
- MONO_SUMMARY_EMPTY = 0x0,
- MONO_SUMMARY_EXPECT = 0x1,
- MONO_SUMMARY_IN_PROGRESS = 0x2,
- MONO_SUMMARY_EXAMINE = 0x3,
- MONO_SUMMARY_MUTATE_SHARED = 0x4
-} MonoSummaryState;
+ gboolean silent; // print to stdout
+} SummarizerGlobalState;
-static const char *
-thread_summary_state_to_str (MonoSummaryState state)
+static gboolean
+summarizer_state_init (SummarizerGlobalState *state, MonoNativeThreadId current, int *my_index)
{
- switch (state) {
- case MONO_SUMMARY_EMPTY:
- return "MONO_SUMMARY_EMPTY";
- case MONO_SUMMARY_EXPECT:
- return "MONO_SUMMARY_EXPECT";
- case MONO_SUMMARY_IN_PROGRESS:
- return "MONO_SUMMARY_IN_PROGRESS";
- case MONO_SUMMARY_EXAMINE:
- return "MONO_SUMMARY_EXAMINE";
- case MONO_SUMMARY_MUTATE_SHARED:
- return "MONO_SUMMARY_MUTATE_SHARED";
- default:
- g_assert_not_reached ();
+ gint32 started_state = mono_atomic_cas_i32 (&state->has_owner, 1 /* set */, 0 /* compare */);
+ gboolean not_started = started_state == 0;
+ if (not_started) {
+ state->nthreads = collect_thread_ids (state->thread_array, MAX_NUM_THREADS);
+ mono_os_sem_init (&state->update, 0);
}
-}
-static gint32 summary_started;
-static gint32 summarizing_thread_state;
-static MonoNativeThreadId summarizing_thread;
+ for (int i = 0; i < state->nthreads; i++) {
+ if (state->thread_array [i] == current) {
+ *my_index = i;
+ break;
+ }
+ }
-static inline MonoNativeThreadId
-mono_atomic_cas_native_thread_id (volatile MonoNativeThreadId *dest, MonoNativeThreadId exch, MonoNativeThreadId comp)
+ return not_started;
+}
+
+static void
+summarizer_signal_other_threads (SummarizerGlobalState *state, MonoNativeThreadId current, int current_idx)
{
- // FIXME static_assert
- g_assert (sizeof (MonoNativeThreadId) == 4 || sizeof (MonoNativeThreadId) == sizeof (gpointer));
+ sigset_t sigset, old_sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGTERM);
- // Extra casts are needed to avoid warnings. MonoNativeThreadId can be an integer or a pointer.
+ for (int i=0; i < state->nthreads; i++) {
+ sigprocmask (SIG_UNBLOCK, &sigset, &old_sigset);
- if (sizeof (MonoNativeThreadId) == 4)
- return (MonoNativeThreadId)(gsize)mono_atomic_cas_i32 ((gint32*)dest, (gint32)(gsize)exch, (gint32)(gsize)comp);
- return (MonoNativeThreadId)(gsize)mono_atomic_cas_ptr ((gpointer*)dest, (gpointer)(gsize)exch, (gpointer)(gsize)comp);
+ if (i == current_idx)
+ continue;
+
+ #ifdef HAVE_PTHREAD_KILL
+ pthread_kill (state->thread_array [i], SIGTERM);
+
+ if (!state->silent)
+ MOSTLY_ASYNC_SAFE_PRINTF("Pkilling 0x%zx from 0x%zx\n", state->thread_array [i], current);
+ #else
+ g_error ("pthread_kill () is not supported by this platform");
+ #endif
+ }
}
-gboolean
-mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes)
+// Returns true when there are shared global references to "this_thread"
+static gboolean
+summarizer_post_dump (SummarizerGlobalState *state, MonoThreadSummary *this_thread, int current_idx)
{
- gint32 started_state = mono_atomic_cas_i32 (&summary_started, 1 /* set */, 0 /* compare */);
- gboolean not_started = started_state == 0;
+ mono_memory_barrier ();
- MonoNativeThreadId current = mono_native_thread_id_get ();
+ gpointer old = mono_atomic_cas_ptr ((volatile gpointer *)&state->all_threads [current_idx], this_thread, NULL);
+
+ if (old == GINT_TO_POINTER (-1)) {
+ MOSTLY_ASYNC_SAFE_PRINTF ("Trying to register response after dumping period ended");
+ return FALSE;
+ } else if (old != NULL) {
+ MOSTLY_ASYNC_SAFE_PRINTF ("Thread dump raced for thread slot.");
+ return FALSE;
+ }
- MOSTLY_ASYNC_SAFE_PRINTF("Entering thread summarizer from %zx\n", current);
+ // We added our pointer
+ gint32 count = mono_atomic_inc_i32 ((volatile gint32 *) &state->nthreads_attached);
+ if (count == state->nthreads)
+ mono_os_sem_post (&state->update);
- if (not_started) {
- // Setup state
- mono_summarize_native_state_begin ();
-
- if (!current)
- g_error ("Can't get native thread ID");
-
- // Note: this list can get out of date. We can have
- // threads disappear from existince between us getting
- // this list and us dumping them. We have to do our best.
- //
- // Thankfully the memory behind these pointers is always leaked,
- // we never have dangling pointers.
- guint32 thread_array [128];
- int nthreads = collect_threads (thread_array, 128);
-
- if (nthreads == 0)
- MOSTLY_ASYNC_SAFE_PRINTF("No managed threads detected, error occured before thread init\n");
-
- sigset_t sigset, old_sigset;
- sigemptyset(&sigset);
- sigaddset(&sigset, SIGTERM);
-
- for (int i=0; i < nthreads; i++) {
- guint32 handle = thread_array [i];
- MonoInternalThread *thread = (MonoInternalThread *) mono_gchandle_get_target (handle);
-
- MonoNativeThreadId tid = thread_get_tid (thread);
- if (current == tid)
- continue;
+ return TRUE;
+}
- // Request every other thread dumps themselves before us
- MonoThreadInfo *info = thread->thread_info;
+// A lockless spinwait with a timeout
+// Used in environments where locks are unsafe
+//
+// If set_pos is true, we wait until the expected number of threads have
+// responded and then count that the expected number are set. If it is not true,
+// then we wait for them to be unset.
+static void
+summary_timedwait (SummarizerGlobalState *state, int timeout_seconds)
+{
+ gint64 milliseconds_in_second = 1000;
+ gint64 timeout_total = milliseconds_in_second * timeout_seconds;
- mono_memory_barrier ();
- size_t old_num_summarized = num_threads_summarized;
+ gint64 end = mono_msec_ticks () + timeout_total;
- sigprocmask (SIG_UNBLOCK, &sigset, &old_sigset);
+ while (TRUE) {
+ if (mono_atomic_load_i32 ((volatile gint32 *) &state->nthreads_attached) == state->nthreads)
+ break;
- gint32 prev_state = mono_atomic_cas_i32(&summarizing_thread_state, MONO_SUMMARY_EXPECT /* set */, MONO_SUMMARY_EMPTY /* compare */);
- g_assertf(prev_state == MONO_SUMMARY_EMPTY, "Summary memory was not in a clean state prior to entry", NULL);
+ gint64 now = mono_msec_ticks ();
+ gint64 remaining = end - now;
+ if (remaining <= 0)
+ break;
- if (summarizing_thread_state != MONO_SUMMARY_EXPECT) {
- const char *name = thread_summary_state_to_str ((MonoSummaryState)summarizing_thread_state);
- g_error ("Status after init wrong: %s\n", name);
- }
+ mono_os_sem_timedwait (&state->update, remaining, MONO_SEM_FLAGS_NONE);
+ }
- summarizing_thread = tid;
- mono_threads_pthread_kill (info, SIGTERM);
+ return;
+}
- // Number of rounds of 40 milliseconds seconds to give each dumping thread
- int count = 400;
+static void
+summarizer_state_term (SummarizerGlobalState *state, gchar **out, gchar *mem, size_t provided_size)
+{
+ // See the array writes
+ mono_memory_barrier ();
- while (old_num_summarized == num_threads_summarized && count > 0) {
- if (thread->state & (ThreadState_Unstarted | ThreadState_Aborted | ThreadState_Stopped))
- break;
+ mono_summarize_native_state_begin (mem, provided_size);
+ for (int i=0; i < state->nthreads; i++) {
+ gpointer old_value = NULL;
+ while (TRUE) {
+ // Lock array slot with sentinel and get value set previously
+ // This lets late dumpers know that they're late.
+ gpointer new_old_value = mono_atomic_cas_ptr ((volatile gpointer *) &state->all_threads [i], GINT_TO_POINTER(-1), old_value);
- usleep (10);
- mono_memory_barrier ();
- const char *name = thread_summary_state_to_str ((MonoSummaryState)summarizing_thread_state);
- MOSTLY_ASYNC_SAFE_PRINTF("Waiting for signalled thread %zx to collect stacktrace. Status: %s\n. Thread state: 0x%x\n", tid, name, thread->state);
- count--;
- }
+ if (new_old_value != old_value)
+ old_value = new_old_value;
+ else
+ break;
+ }
- // Free the gchandle; we're done waiting on this one, no matter how the waiting went.
- mono_gchandle_free (handle);
+ MonoThreadSummary *thread = (MonoThreadSummary *) old_value;
+ if (!thread)
+ continue;
- if (count == 0) {
- // After timeout
- // Thread may have been in lock or something, may have died
- // Either way, didn't respond. Rather than failing, we have to
- // skip it.
- mono_memory_barrier ();
- switch (summarizing_thread_state) {
- case MONO_SUMMARY_MUTATE_SHARED:
- g_error ("Timed out when writing json log!");
- break;
+ // We are doing this dump on the controlling thread because this isn't
+ // an async context. There's still some reliance on malloc here, but it's
+ // much more stable to do it all from the controlling thread.
+ mono_get_eh_callbacks ()->mono_summarize_managed_stack (thread);
- case MONO_SUMMARY_EMPTY: {
- g_assert (summarizing_thread == (MonoNativeThreadId) NULL);
- break;
+ mono_summarize_native_state_add_thread (thread, thread->ctx);
- case MONO_SUMMARY_EXAMINE:
- MOSTLY_ASYNC_SAFE_PRINTF("Timed out, thread did not finish dumping\n");
+ // Set non-shared state to notify the waiting thread to clean up
+ // without having to keep our shared state alive
+ mono_atomic_store_i32 (&thread->done, 0x1);
+ mono_os_sem_post (&thread->done_wait);
+ }
+ *out = mono_summarize_native_state_end ();
- MonoNativeThreadId old_val = mono_atomic_cas_native_thread_id (&summarizing_thread, tid /* set */, 0 /* compare */);
- g_assertf (old_val == NULL, "Attempting to abandon dumping of thread, and thread changed.", NULL);
+ mono_os_sem_destroy (&state->update);
- gint32 timeout_abort_state = mono_atomic_cas_i32(&summarizing_thread_state, MONO_SUMMARY_EMPTY /* set */, MONO_SUMMARY_EXAMINE /* compare */);
- g_assertf (timeout_abort_state == MONO_SUMMARY_EXAMINE, "Thread state changed during timeout abort", NULL);
+ memset (state, 0, sizeof (*state));
+ mono_atomic_store_i32 ((volatile gint32 *)&state->has_owner, 0);
+}
- break;
- }
- case MONO_SUMMARY_EXPECT: {
- MOSTLY_ASYNC_SAFE_PRINTF("Timed out, thread did not respond to signal\n");
+static void
+summarizer_state_wait (MonoThreadSummary *thread)
+{
+ gint64 milliseconds_in_second = 1000;
- MonoNativeThreadId old_val = mono_atomic_cas_native_thread_id (&summarizing_thread, 0 /* set */, tid /* compare */);
- g_assertf (tid == old_val, "Attempting to abandon dumping of thread %zx, and thread changed to %zx.", tid, old_val);
+ // cond_wait can spuriously wake up, so we need to check
+ // done
+ while (!mono_atomic_load_i32 (&thread->done))
+ mono_os_sem_timedwait (&thread->done_wait, milliseconds_in_second, MONO_SEM_FLAGS_NONE);
+}
- gint32 timeout_abort_state = mono_atomic_cas_i32(&summarizing_thread_state, MONO_SUMMARY_EMPTY /* set */, MONO_SUMMARY_EXPECT /* compare */);
- g_assertf (timeout_abort_state == MONO_SUMMARY_EXPECT, "Thread state changed during timeout abort", NULL);
+gboolean
+mono_threads_summarize_execute (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gchar *mem, size_t provided_size)
+{
+ static SummarizerGlobalState state;
- break;
- }
- default:
- g_assert_not_reached ();
- }
- }
- }
+ int current_idx;
+ MonoNativeThreadId current = mono_native_thread_id_get ();
+ gboolean this_thread_controls = summarizer_state_init (&state, current, &current_idx);
- // After dumping all the other threads, we dump our own.
- summarizing_thread = current;
+ if (this_thread_controls) {
+ state.silent = silent;
+ summarizer_signal_other_threads (&state, current, current_idx);
}
- mono_memory_barrier ();
+ MonoThreadSummary this_thread;
- MOSTLY_ASYNC_SAFE_PRINTF("Self-reporting for thread %zx. Registered summarizing thread right now is %zx\n", current, summarizing_thread);
- g_assert (current == summarizing_thread);
+ if (mono_threads_summarize_native_self (&this_thread, ctx)) {
+ // Init the synchronization between the controlling thread and the
+ // providing thread
+ mono_os_sem_init (&this_thread.done_wait, 0);
- if (!not_started) {
- gint32 restart_state = mono_atomic_cas_i32 (&summarizing_thread_state, MONO_SUMMARY_EXAMINE /* set */, MONO_SUMMARY_EXPECT /* compare */);
- if (restart_state != MONO_SUMMARY_EXPECT) {
- const char *name = thread_summary_state_to_str ((MonoSummaryState)summarizing_thread_state);
- MOSTLY_ASYNC_SAFE_PRINTF ("Dumping thread could not obtain ownership of dumping memory. Timeout? Enum was %s", name);
- goto fail;
- }
- } else {
- summarizing_thread_state = MONO_SUMMARY_EXAMINE;
- mono_memory_barrier ();
+ // Store a reference to our stack memory into global state
+ gboolean success = summarizer_post_dump (&state, &this_thread, current_idx);
+ if (!success && !state.silent)
+ MOSTLY_ASYNC_SAFE_PRINTF("Thread 0x%zx reported itself.\n", current);
+ } else if (!state.silent) {
+ MOSTLY_ASYNC_SAFE_PRINTF("Thread 0x%zx couldn't report itself.\n", current);
}
- // Dump ourselves
- MonoThreadSummary this_thread;
- if (mono_threads_summarize_one (&this_thread, ctx)) {
- gint32 write_state = mono_atomic_cas_i32 (&summarizing_thread_state, MONO_SUMMARY_MUTATE_SHARED /* set */, MONO_SUMMARY_EXAMINE /* compare */);
- if (write_state != MONO_SUMMARY_EXAMINE) {
- MOSTLY_ASYNC_SAFE_PRINTF ("Terminated when walking stack, thread dump lost!\n");
- goto fail;
- }
+ // From summarizer, wait and dump.
+ if (this_thread_controls) {
+ if (!state.silent)
+ MOSTLY_ASYNC_SAFE_PRINTF("Entering thread summarizer pause from 0x%zx\n", current);
- mono_summarize_native_state_add_thread (&this_thread, ctx);
+ // Wait up to 2 seconds for all of the other threads to catch up
+ summary_timedwait (&state, 2);
- gint32 write_done_state = mono_atomic_cas_i32 (&summarizing_thread_state, MONO_SUMMARY_EMPTY /* set */, MONO_SUMMARY_MUTATE_SHARED /* compare */);
- g_assertf (write_done_state == MONO_SUMMARY_MUTATE_SHARED, "Memory unsafety: dumping thread ownership of shared memory ignored!", NULL);
+ if (!state.silent)
+ MOSTLY_ASYNC_SAFE_PRINTF("Finished thread summarizer pause from 0x%zx.\n", current);
+
+ // Dump and cleanup all the stack memory
+ summarizer_state_term (&state, out, mem, provided_size);
+ } else {
+ // Wait here, keeping our stack memory alive
+ // for the dumper
+ summarizer_state_wait (&this_thread);
}
- mono_memory_barrier ();
- num_threads_summarized++;
- mono_memory_barrier ();
+ // FIXME: How many threads should be counted?
+ if (hashes)
+ *hashes = this_thread.hashes;
- if (not_started) {
- // We are the dumper
- *out = mono_summarize_native_state_end ();
- if (hashes)
- *hashes = this_thread.hashes;
- return TRUE;
+ return TRUE;
+}
+
+gboolean
+mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gboolean signal_handler_controller, gchar *mem, size_t provided_size)
+{
+ // The staggered values are due to the need to use inc_i64 for the first value
+ static gint64 next_pending_request_id = 0;
+ static gint64 request_available_to_run = 1;
+ gint64 this_request_id = mono_atomic_inc_i64 ((volatile gint64 *) &next_pending_request_id);
+
+ // This is a global queue of summary requests.
+ // It's not safe to signal a thread while they're in the
+ // middle of a dump. Dladdr is not reentrant. It's the one lock
+ // we rely on being able to take.
+ //
+ // We don't use it in almost any other place in managed code, so
+ // our problem is in the stack dumping code racing with the signalling code.
+ //
+ // A dump is wait-free to the degree that it's not going to loop indefinitely.
+ // If we're running from a crash handler block, we're not in any position to
+ // wait for an in-flight dump to finish. If we crashed while dumping, we cannot dump.
+ // We should simply return so we can die cleanly.
+ //
+ // signal_handler_controller should be set only from a handler that expects itself to be the only
+ // entry point, where the runtime already being dumping means we should just give up
+
+ gboolean success = FALSE;
+
+ while (TRUE) {
+ gint64 next_request_id = mono_atomic_load_i64 ((volatile gint64 *) &request_available_to_run);
+
+ if (next_request_id == this_request_id) {
+ success = mono_threads_summarize_execute (ctx, out, hashes, silent, mem, provided_size);
+
+ // Only the thread that gets the ticket can unblock future dumpers.
+ mono_atomic_inc_i64 ((volatile gint64 *) &request_available_to_run);
+ break;
+ } else if (signal_handler_controller) {
+ // We're done. We can't do anything.
+ MOSTLY_ASYNC_SAFE_PRINTF ("Attempted to dump for critical failure when already in dump. Error reporting crashed?");
+ break;
+ } else {
+ if (!silent)
+ MOSTLY_ASYNC_SAFE_PRINTF ("Waiting for in-flight dump to complete.");
+ sleep (2);
+ }
}
-fail:
- while (1)
- sleep (10);
+ return success;
}
+
#endif
diff --git a/mono/mini/aot-runtime.c b/mono/mini/aot-runtime.c
index d14f2f881ad..c3180a732fa 100644
--- a/mono/mini/aot-runtime.c
+++ b/mono/mini/aot-runtime.c
@@ -3307,6 +3307,8 @@ decode_exception_debug_info (MonoAotModule *amodule, MonoDomain *domain,
else
mono_seq_point_info_free (seq_points);
mono_domain_unlock (domain);
+
+ jinfo->seq_points = seq_points;
}
/* Load debug info */
diff --git a/mono/mini/debugger-engine.c b/mono/mini/debugger-engine.c
index 588fa6fae2b..4260fff6703 100644
--- a/mono/mini/debugger-engine.c
+++ b/mono/mini/debugger-engine.c
@@ -302,7 +302,6 @@ mono_de_add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
int i, j;
MonoSeqPointInfo *seq_points;
MonoDomain *domain;
- MonoMethod *jmethod;
if (!breakpoints)
return;
@@ -326,20 +325,18 @@ mono_de_add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
}
if (!found) {
- MonoMethod *declaring = NULL;
-
- jmethod = jinfo_get_method (ji);
- if (jmethod->is_inflated)
- declaring = mono_method_get_declaring_generic_method (jmethod);
-
- mono_domain_lock (domain);
- seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, jmethod);
- if (!seq_points && declaring)
- seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, declaring);
- mono_domain_unlock (domain);
- if (!seq_points)
- /* Could be AOT code */
- continue;
+ seq_points = (MonoSeqPointInfo *) ji->seq_points;
+
+ if (!seq_points) {
+ MonoMethod *jmethod = jinfo_get_method (ji);
+ if (jmethod->is_inflated) {
+ MonoJitInfo *seq_ji;
+ MonoMethod *declaring = mono_method_get_declaring_generic_method (jmethod);
+ mono_jit_search_all_backends_for_jit_info (domain, declaring, &seq_ji);
+ seq_points = (MonoSeqPointInfo *) seq_ji->seq_points;
+ }
+ }
+
g_assert (seq_points);
insert_breakpoint (seq_points, domain, ji, bp, NULL);
@@ -358,22 +355,8 @@ set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_
if (error)
error_init (error);
- code = mono_jit_find_compiled_method_with_jit_info (domain, method, &ji);
- if (!code) {
- ERROR_DECL_VALUE (oerror);
-
- /* Might be AOTed code */
- mono_class_init (method->klass);
- code = mono_aot_get_method (domain, method, &oerror);
- if (code) {
- mono_error_assert_ok (&oerror);
- ji = mono_jit_info_table_find (domain, code);
- } else {
- /* Might be interpreted */
- ji = mini_get_interp_callbacks ()->find_jit_info (domain, method);
- }
- g_assert (ji);
- }
+ code = mono_jit_search_all_backends_for_jit_info (domain, method, &ji);
+ g_assert (ji);
insert_breakpoint (seq_points, domain, ji, bp, error);
}
@@ -389,8 +372,8 @@ static void
collect_domain_bp (gpointer key, gpointer value, gpointer user_data)
{
GHashTableIter iter;
- MonoDomain *domain = (MonoDomain*)key;
MonoSeqPointInfo *seq_points;
+ MonoDomain *domain = (MonoDomain*)key;
CollectDomainData *ud = (CollectDomainData*)user_data;
MonoMethod *m;
diff --git a/mono/mini/interp/transform.c b/mono/mini/interp/transform.c
index f6272c3570e..2a256ebd3a0 100644
--- a/mono/mini/interp/transform.c
+++ b/mono/mini/interp/transform.c
@@ -1932,7 +1932,7 @@ collect_pred_seq_points (TransformData *td, InterpBasicBlock *bb, SeqPoint *seqp
}
static void
-save_seq_points (TransformData *td)
+save_seq_points (TransformData *td, MonoJitInfo *jinfo)
{
InterpMethod *rtm = td->rtm;
GByteArray *array;
@@ -2019,6 +2019,8 @@ save_seq_points (TransformData *td)
mono_domain_lock (domain);
g_hash_table_insert (domain_jit_info (domain)->seq_points, rtm->method, info);
mono_domain_unlock (domain);
+
+ jinfo->seq_points = info;
}
static void
@@ -5017,7 +5019,7 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, unsig
}
}
- save_seq_points (td);
+ save_seq_points (td, jinfo);
exit:
mono_basic_block_free (original_bb);
diff --git a/mono/mini/mini-exceptions.c b/mono/mini/mini-exceptions.c
index fcd1cf8bd20..9b27b342df9 100644
--- a/mono/mini/mini-exceptions.c
+++ b/mono/mini/mini-exceptions.c
@@ -119,7 +119,7 @@ static gpointer throw_corlib_exception_func;
static MonoFtnPtrEHCallback ftnptr_eh_callback;
-static void mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoDomain *domain, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data);
+static void mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoDomain *domain, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data, gboolean crash_context);
static void mono_raise_exception_with_ctx (MonoException *exc, MonoContext *ctx);
static void mono_runtime_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data);
static gboolean mono_current_thread_has_handle_block_guard (void);
@@ -127,8 +127,10 @@ static gboolean mono_install_handler_block_guard (MonoThreadUnwindState *ctx);
static void mono_uninstall_current_handler_block_guard (void);
static gboolean mono_exception_walk_trace_internal (MonoException *ex, MonoExceptionFrameWalk func, gpointer user_data);
-static void mono_summarize_stack (MonoDomain *domain, MonoThreadSummary *out, MonoContext *crash_ctx);
+static void mono_summarize_managed_stack (MonoThreadSummary *out);
+static void mono_summarize_unmanaged_stack (MonoThreadSummary *out);
static void mono_summarize_exception (MonoException *exc, MonoThreadSummary *out);
+static void mono_crash_reporting_register_native_library (const char *module_path, const char *module_name);
static gboolean
first_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer addr)
@@ -236,8 +238,10 @@ mono_exceptions_init (void)
cbs.mono_walk_stack_with_ctx = mono_runtime_walk_stack_with_ctx;
cbs.mono_walk_stack_with_state = mono_walk_stack_with_state;
- cbs.mono_summarize_stack = mono_summarize_stack;
+ cbs.mono_summarize_managed_stack = mono_summarize_managed_stack;
+ cbs.mono_summarize_unmanaged_stack = mono_summarize_unmanaged_stack;
cbs.mono_summarize_exception = mono_summarize_exception;
+ cbs.mono_register_native_library = mono_crash_reporting_register_native_library;
if (mono_llvm_only) {
cbs.mono_raise_exception = mono_llvm_raise_exception;
@@ -1110,7 +1114,7 @@ mono_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnw
start_ctx = &extra_ctx;
}
- mono_walk_stack_full (func, start_ctx, mono_domain_get (), thread->jit_data, mono_get_lmf (), unwind_options, user_data);
+ mono_walk_stack_full (func, start_ctx, mono_domain_get (), thread->jit_data, mono_get_lmf (), unwind_options, user_data, FALSE);
}
/**
@@ -1145,7 +1149,7 @@ mono_walk_stack_with_state (MonoJitStackWalk func, MonoThreadUnwindState *state,
(MonoDomain *)state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
(MonoJitTlsData *)state->unwind_data [MONO_UNWIND_DATA_JIT_TLS],
(MonoLMF *)state->unwind_data [MONO_UNWIND_DATA_LMF],
- unwind_options, user_data);
+ unwind_options, user_data, FALSE);
}
void
@@ -1166,15 +1170,16 @@ mono_walk_stack (MonoJitStackWalk func, MonoUnwindOptions options, void *user_da
* \param thread the thread whose stack to walk, can be NULL to use the current thread
* \param lmf the LMF of \p thread, can be NULL to use the LMF of the current thread
* \param user_data data passed to the callback
+ * \param crash_context tells us that we're in a context where it's not safe to lock or allocate
* This function walks the stack of a thread, starting from the state
* represented by \p start_ctx. For each frame the callback
* function is called with the relevant info. The walk ends when no more
* managed stack frames are found or when the callback returns a TRUE value.
*/
static void
-mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoDomain *domain, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data)
+mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoDomain *domain, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data, gboolean crash_context)
{
- gint il_offset, i;
+ gint il_offset;
MonoContext ctx, new_ctx;
StackFrameInfo frame;
gboolean res;
@@ -1217,14 +1222,27 @@ mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoDomain
}
#endif
- g_assert (start_ctx);
- g_assert (domain);
- g_assert (jit_tls);
+ if (!start_ctx) {
+ g_warning ("start_ctx required for stack walk");
+ return;
+ }
+
+ if (!domain) {
+ g_warning ("domain required for stack walk");
+ return;
+ }
+
+ if (!jit_tls) {
+ g_warning ("jit_tls required for stack walk");
+ return;
+ }
+
/*The LMF will be null if the target have no managed frames.*/
/* g_assert (lmf); */
-
- if (async)
- g_assert (unwind_options == MONO_UNWIND_NONE);
+ if (async && (unwind_options & MONO_UNWIND_LOOKUP_ACTUAL_METHOD)) {
+ g_warning ("async && (unwind_options & MONO_UNWIND_LOOKUP_ACTUAL_METHOD) not legal");
+ return;
+ }
memcpy (&ctx, start_ctx, sizeof (MonoContext));
memset (reg_locations, 0, sizeof (reg_locations));
@@ -1241,14 +1259,24 @@ mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoDomain
goto next;
if ((unwind_options & MONO_UNWIND_LOOKUP_IL_OFFSET) && frame.ji) {
- MonoDebugSourceLocation *source;
+ MonoDebugSourceLocation *source = NULL;
- source = mono_debug_lookup_source_location (jinfo_get_method (frame.ji), frame.native_offset, domain);
+ // Don't do this when we can be in a signal handler
+ if (!crash_context)
+ source = mono_debug_lookup_source_location (jinfo_get_method (frame.ji), frame.native_offset, domain);
if (source) {
il_offset = source->il_offset;
} else {
+ MonoSeqPointInfo *seq_points = NULL;
+
+ // It's more reliable to look into the global cache if possible
+ if (crash_context)
+ seq_points = (MonoSeqPointInfo *) frame.ji->seq_points;
+ else
+ seq_points = mono_get_seq_points (domain, jinfo_get_method (frame.ji));
+
SeqPoint sp;
- if (mono_find_prev_seq_point_for_native_offset (domain, jinfo_get_method (frame.ji), frame.native_offset, NULL, &sp))
+ if (seq_points && mono_seq_point_find_prev_by_native_offset (seq_points, frame.native_offset, &sp))
il_offset = sp.il_offset;
else
il_offset = -1;
@@ -1273,7 +1301,7 @@ mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoDomain
next:
if (get_reg_locations) {
- for (i = 0; i < MONO_MAX_IREGS; ++i)
+ for (int i = 0; i < MONO_MAX_IREGS; ++i)
if (new_reg_locations [i])
reg_locations [i] = new_reg_locations [i];
}
@@ -1283,8 +1311,15 @@ next:
}
#ifdef DISABLE_CRASH_REPORTING
-static void
-mono_summarize_stack (MonoDomain *domain, MonoThreadSummary *out, MonoContext *crash_ctx)
+
+static void
+mono_summarize_managed_stack (MonoThreadSummary *out)
+{
+ return;
+}
+
+static void
+mono_summarize_unmanaged_stack (MonoThreadSummary *out)
{
return;
}
@@ -1295,6 +1330,13 @@ mono_summarize_exception (MonoException *exc, MonoThreadSummary *out)
return;
}
+static void
+mono_crash_reporting_register_native_library (const char *module_path, const char *module_name)
+{
+ return;
+}
+
+
#else
typedef struct {
@@ -1302,6 +1344,7 @@ typedef struct {
int num_frames;
int max_frames;
MonoStackHash *hashes;
+ const char *error;
} MonoSummarizeUserData;
static void
@@ -1318,32 +1361,87 @@ copy_summary_string_safe (char *in, const char *out)
return;
}
-static gboolean
-mono_get_portable_ip (intptr_t in_ip, intptr_t *out_ip, char *out_name)
+static GHashTable *native_library_whitelist;
+
+static void
+mono_crash_reporting_register_native_library (const char *module_path, const char *module_name)
{
- // We're only able to get reliable info about pointers in this assembly
Dl_info info;
- int success = dladdr ((void*) mono_get_portable_ip, &info);
- intptr_t this_module = (intptr_t) info.dli_fbase;
- g_assert (success);
+ if (!native_library_whitelist) {
+ native_library_whitelist = g_hash_table_new_full (NULL, NULL, NULL, g_free);
+
+ dladdr ((void*) mono_crash_reporting_register_native_library, &info);
+
+ if (info.dli_fname && strlen(info.dli_fname) > 0)
+ g_hash_table_insert (native_library_whitelist, g_strdup (info.dli_fname), g_strdup ("mono"));
+ }
+
+ // Examples: libsystem_pthread.dylib -> "pthread"
+ // Examples: libsystem_platform.dylib -> "platform"
+ // Examples: mono-sgen -> "mono" from above line
+ g_hash_table_insert (native_library_whitelist, g_strdup (module_path), g_strdup (module_name));
+}
+
+static gboolean
+mono_make_portable_ip (intptr_t in_ip, intptr_t module_base)
+{
+ // FIXME: Make generalize away from llvm tools?
+ // So lldb starts the pointer base at 0x100000000
+ // and expects to get pointers as (offset + constant)
+ //
+ // Quirk shared by:
+ // /usr/bin/symbols -- symbols version: @(#)PROGRAM:symbols PROJECT:SamplingTools-63501
+ // *CoreSymbolicationDT.framework version: 63750*/
+ intptr_t offset = in_ip - module_base;
+ intptr_t magic_value = offset + 0x100000000;
+ return magic_value;
+}
+
+static gboolean
+check_whitelisted_module (const char *in_name, const char **out_module)
+{
+ if (!native_library_whitelist) {
+ if (g_str_has_suffix (in_name, "mono-sgen")) {
+ if (out_module)
+ *out_module = "mono";
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ GHashTableIter iter;
+ char *file_suffix;
+ char *module_suffix;
+ g_hash_table_iter_init (&iter, native_library_whitelist);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &file_suffix, (gpointer *) &module_suffix)) {
+ if (!g_str_has_suffix (in_name, file_suffix))
+ continue;
+ if (out_module)
+ *out_module = module_suffix;
+ return TRUE;
+ }
+
+ /*fprintf (stderr, "%s == %s\n", info.dli_fname, *out_module);*/
- success = dladdr ((void*)in_ip, &info);
+ return FALSE;
+}
+
+static gboolean
+mono_get_portable_ip (intptr_t in_ip, intptr_t *out_ip, const char **out_module, char *out_name)
+{
+ // Note: it's not safe for us to be interrupted while inside of dl_addr, because if we
+ // try to call dl_addr while interrupted while inside the lock, we will try to take a
+ // non-recursive lock twice on this thread, and will deadlock.
+ Dl_info info;
+ gboolean success = dladdr ((void*)in_ip, &info);
if (!success)
return FALSE;
- if ((intptr_t) info.dli_fbase == this_module) {
- // FIXME: Make generalize away from llvm tools?
- // So lldb starts the pointer base at 0x100000000
- // and expects to get pointers as (offset + constant)
- //
- // Quirk shared by:
- // /usr/bin/symbols -- symbols version: @(#)PROGRAM:symbols PROJECT:SamplingTools-63501
- // *CoreSymbolicationDT.framework version: 63750*/
- intptr_t offset = in_ip - this_module;
- intptr_t magic_value = offset + 0x100000000;
+ if (!check_whitelisted_module (info.dli_fname, out_module))
+ return FALSE;
- *out_ip = magic_value;
- }
+ *out_ip = mono_make_portable_ip (in_ip, (intptr_t) info.dli_fbase);
#ifndef MONO_PRIVATE_CRASHES
if (info.dli_saddr && out_name)
@@ -1386,27 +1484,38 @@ summarize_offset_rich_hash (guint64 accum, MonoFrameSummary *frame)
}
static gboolean
-summarize_frame_internal (MonoMethod *method, gpointer ip, size_t native_offset, gboolean managed, gpointer user_data)
+summarize_frame_internal (MonoMethod *method, gpointer ip, size_t native_offset, int il_offset, gboolean managed, gpointer user_data)
{
MonoSummarizeUserData *ud = (MonoSummarizeUserData *) user_data;
- g_assert (ud->num_frames + 1 < ud->max_frames);
+
+ gboolean valid_state = ud->num_frames + 1 < ud->max_frames;
+ if (!valid_state) {
+ ud->error = "Exceeded the maximum number of frames";
+ return TRUE;
+ }
+
MonoFrameSummary *dest = &ud->frames [ud->num_frames];
- ud->num_frames++;
dest->unmanaged_data.ip = (intptr_t) ip;
dest->is_managed = managed;
- if (method && method->wrapper_type != MONO_WRAPPER_NONE) {
+ if (method && method->wrapper_type != MONO_WRAPPER_NONE && method->wrapper_type < MONO_WRAPPER_NUM) {
dest->is_managed = FALSE;
dest->unmanaged_data.has_name = TRUE;
-
- copy_summary_string_safe (dest->str_descr, mono_method_full_name (method, TRUE));
+ copy_summary_string_safe (dest->str_descr, mono_wrapper_type_to_str (method->wrapper_type));
}
-
- MonoDebugSourceLocation *location = NULL;
+
+#ifndef MONO_PRIVATE_CRASHES
+ if (method)
+ dest->managed_data.name = (char *) method->name;
+#endif
if (managed) {
- g_assert (method);
+ if (!method) {
+ ud->error = "Managed method frame, but no provided managed method";
+ return TRUE;
+ }
+
MonoImage *image = mono_class_get_image (method->klass);
// Used for hashing, more stable across rebuilds than using GUID
copy_summary_string_safe (dest->str_descr, image->assembly_name);
@@ -1415,40 +1524,62 @@ summarize_frame_internal (MonoMethod *method, gpointer ip, size_t native_offset,
dest->managed_data.native_offset = native_offset;
dest->managed_data.token = method->token;
- location = mono_debug_lookup_source_location (method, native_offset, mono_domain_get ());
+ dest->managed_data.il_offset = il_offset;
} else {
dest->managed_data.token = -1;
}
- if (location) {
- dest->managed_data.il_offset = location->il_offset;
-
- mono_debug_free_source_location (location);
- }
ud->hashes->offset_free_hash = summarize_offset_free_hash (ud->hashes->offset_free_hash, dest);
ud->hashes->offset_rich_hash = summarize_offset_rich_hash (ud->hashes->offset_rich_hash, dest);
+ // We return FALSE, so we're continuing walking
+ // And we increment the pointer because we're done with this cell in the array
+ ud->num_frames++;
return FALSE;
}
static gboolean
+summarize_frame_managed_walk (MonoMethod *method, gpointer ip, size_t native_offset, gboolean managed, gpointer user_data)
+{
+ int il_offset = -1;
+
+ if (managed && method) {
+ MonoDebugSourceLocation *location = mono_debug_lookup_source_location (method, native_offset, mono_domain_get ());
+ if (location) {
+ il_offset = location->il_offset;
+ mono_debug_free_source_location (location);
+ }
+ }
+
+ return summarize_frame_internal (method, ip, native_offset, il_offset, managed, user_data);
+}
+
+
+static gboolean
summarize_frame (StackFrameInfo *frame, MonoContext *ctx, gpointer data)
{
+ MonoSummarizeUserData *ud = (MonoSummarizeUserData *) data;
// Don't record trampolines between managed frames
if (frame->ji && frame->ji->is_trampoline)
return TRUE;
- MonoMethod *method = NULL;
+ if (frame->ji && (frame->ji->is_trampoline || frame->ji->async))
+ return FALSE; // Keep unwinding
+
intptr_t ip = 0x0;
- mono_get_portable_ip ((intptr_t) MONO_CONTEXT_GET_IP (ctx), &ip, NULL);
+ mono_get_portable_ip ((intptr_t) MONO_CONTEXT_GET_IP (ctx), &ip, NULL, NULL);
+ // Don't need to handle return status "success" because this ip is stored below only, NULL is okay
+ gboolean is_managed = (frame->type == FRAME_TYPE_MANAGED || frame->type == FRAME_TYPE_INTERP);
+ MonoMethod *method = NULL;
if (frame && frame->ji && frame->type != FRAME_TYPE_TRAMPOLINE)
method = jinfo_get_method (frame->ji);
- gboolean is_managed = (method != NULL);
+ if (is_managed)
+ method = jinfo_get_method (frame->ji);
- return summarize_frame_internal (method, (gpointer) ip, frame->native_offset, is_managed, data);
+ return summarize_frame_internal (method, (gpointer) ip, frame->native_offset, frame->il_offset, is_managed, data);
}
static void
@@ -1463,13 +1594,13 @@ mono_summarize_exception (MonoException *exc, MonoThreadSummary *out)
data.frames = out->managed_frames;
data.hashes = &out->hashes;
- mono_exception_walk_trace (exc, summarize_frame_internal, &data);
+ mono_exception_walk_trace (exc, summarize_frame_managed_walk, &data);
out->num_managed_frames = data.num_frames;
}
static void
-mono_summarize_stack (MonoDomain *domain, MonoThreadSummary *out, MonoContext *crash_ctx)
+mono_summarize_managed_stack (MonoThreadSummary *out)
{
MonoSummarizeUserData data;
memset (&data, 0, sizeof (MonoSummarizeUserData));
@@ -1484,9 +1615,18 @@ mono_summarize_stack (MonoDomain *domain, MonoThreadSummary *out, MonoContext *c
//
// Summarize managed stack
//
- mono_walk_stack_with_ctx (summarize_frame, crash_ctx, MONO_UNWIND_LOOKUP_IL_OFFSET, &data);
+ mono_walk_stack_full (summarize_frame, out->ctx, out->domain, out->jit_tls, out->lmf, MONO_UNWIND_LOOKUP_IL_OFFSET, &data, TRUE);
out->num_managed_frames = data.num_frames;
+ if (data.error != NULL)
+ out->error_msg = data.error;
+}
+
+// Always runs on the dumped thread
+static void
+mono_summarize_unmanaged_stack (MonoThreadSummary *out)
+{
+ MONO_ARCH_CONTEXT_DEF
//
// Summarize unmanaged stack
//
@@ -1497,8 +1637,9 @@ mono_summarize_stack (MonoDomain *domain, MonoThreadSummary *out, MonoContext *c
for (int i =0; i < out->num_unmanaged_frames; ++i) {
intptr_t ip = frame_ips [i];
+ MonoFrameSummary *frame = &out->unmanaged_frames [i];
- int success = mono_get_portable_ip (ip, &out->unmanaged_frames [i].unmanaged_data.ip, (char *) &out->unmanaged_frames [i].str_descr);
+ int success = mono_get_portable_ip (ip, &frame->unmanaged_data.ip, &frame->unmanaged_data.module, (char *) frame->str_descr);
if (!success)
continue;
@@ -1507,6 +1648,18 @@ mono_summarize_stack (MonoDomain *domain, MonoThreadSummary *out, MonoContext *c
}
#endif
+ out->lmf = mono_get_lmf ();
+
+ MonoThreadInfo *thread = mono_thread_info_current_unchecked ();
+ out->jit_tls = thread->jit_data;
+ out->domain = mono_domain_get ();
+
+ if (!out->ctx) {
+ out->ctx = &out->ctx_mem;
+ mono_arch_flush_register_windows ();
+ MONO_INIT_CONTEXT_FROM_FUNC (out->ctx, mono_summarize_unmanaged_stack);
+ }
+
return;
}
#endif
diff --git a/mono/mini/mini-posix.c b/mono/mini/mini-posix.c
index 5340d46fec4..132163d478d 100644
--- a/mono/mini/mini-posix.c
+++ b/mono/mini/mini-posix.c
@@ -229,15 +229,12 @@ MONO_SIG_HANDLER_FUNC (static, sigterm_signal_handler)
MONO_SIG_HANDLER_GET_CONTEXT;
#ifndef DISABLE_CRASH_REPORTING
- // Note: this function only returns for a single thread
- // When it's invoked on other threads once the dump begins,
- // those threads perform their dumps and then sleep until we
- // die. The dump ends with the exit(1) below
+ // Note: this is only run from the non-controlling thread
MonoContext mctx;
gchar *output = NULL;
MonoStackHash hashes;
mono_sigctx_to_monoctx (ctx, &mctx);
- if (!mono_threads_summarize (&mctx, &output, &hashes))
+ if (!mono_threads_summarize_execute (&mctx, &output, &hashes, FALSE, NULL, 0))
g_assert_not_reached ();
#ifdef TARGET_OSX
@@ -247,14 +244,13 @@ MONO_SIG_HANDLER_FUNC (static, sigterm_signal_handler)
} else
#endif
{
- // Only the dumping-supervisor thread exits mono_thread_summarize
- MOSTLY_ASYNC_SAFE_PRINTF("Unhandled exception dump: \n######\n%s\n######\n", output);
- sleep (3);
+ // Controlling thread gets the dump
+ if (output)
+ MOSTLY_ASYNC_SAFE_PRINTF("Unhandled exception dump: \n######\n%s\n######\n", output);
}
#endif
mono_chain_signal (MONO_SIG_HANDLER_PARAMS);
- exit (1);
}
#if (defined (USE_POSIX_BACKEND) && defined (SIGRTMIN)) || defined (SIGPROF)
@@ -414,8 +410,14 @@ void
mini_register_sigterm_handler (void)
{
#ifndef DISABLE_CRASH_REPORTING
- /* always catch SIGTERM, conditionals inside of handler */
- add_signal_handler (SIGTERM, sigterm_signal_handler, 0);
+ static gboolean enabled;
+
+ if (!enabled) {
+ enabled = TRUE;
+
+ /* always catch SIGTERM, conditionals inside of handler */
+ add_signal_handler (SIGTERM, sigterm_signal_handler, 0);
+ }
#endif
}
@@ -1022,8 +1024,14 @@ dump_native_stacktrace (const char *signal, void *ctx)
if (!leave) {
mono_sigctx_to_monoctx (ctx, &mctx);
// Do before forking
- if (!mono_threads_summarize (&mctx, &output, &hashes))
+ if (!mono_threads_summarize (&mctx, &output, &hashes, FALSE, TRUE, NULL, 0))
g_assert_not_reached ();
+
+ // Wait for the other threads to clean up and exit their handlers
+ // We can't lock / wait indefinitely, in case one of these threads got stuck somehow
+ // while dumping.
+ mono_runtime_printf_err ("\nWaiting for dumping threads to resume\n");
+ sleep (1);
}
// We want our crash, and don't have telemetry
diff --git a/mono/mini/mini-runtime.c b/mono/mini/mini-runtime.c
index 15e43cd0349..0aca3667cd1 100644
--- a/mono/mini/mini-runtime.c
+++ b/mono/mini/mini-runtime.c
@@ -2544,6 +2544,8 @@ mono_jit_free_method (MonoDomain *domain, MonoMethod *method)
g_hash_table_remove (info->jump_trampoline_hash, method);
g_hash_table_remove (info->seq_points, method);
+ ji->ji->seq_points = NULL;
+
/* requires the domain lock - took above */
mono_conc_hashtable_remove (info->runtime_invoke_hash, method);
@@ -2594,6 +2596,36 @@ mono_jit_free_method (MonoDomain *domain, MonoMethod *method)
}
gpointer
+mono_jit_search_all_backends_for_jit_info (MonoDomain *domain, MonoMethod *method, MonoJitInfo **out_ji)
+{
+ gpointer code;
+ MonoJitInfo *ji;
+
+ code = mono_jit_find_compiled_method_with_jit_info (domain, method, &ji);
+ if (!code) {
+ ERROR_DECL_VALUE (oerror);
+
+ /* Might be AOTed code */
+ mono_class_init (method->klass);
+ code = mono_aot_get_method (domain, method, &oerror);
+ if (code) {
+ mono_error_assert_ok (&oerror);
+ ji = mono_jit_info_table_find (domain, code);
+ } else {
+ if (!is_ok (&oerror))
+ mono_error_cleanup (&oerror);
+
+ /* Might be interpreted */
+ ji = mini_get_interp_callbacks ()->find_jit_info (domain, method);
+ }
+ }
+
+ *out_ji = ji;
+
+ return code;
+}
+
+gpointer
mono_jit_find_compiled_method_with_jit_info (MonoDomain *domain, MonoMethod *method, MonoJitInfo **ji)
{
MonoDomain *target_domain;
@@ -4319,7 +4351,7 @@ mini_init (const char *filename, const char *runtime_version)
#endif
callbacks.get_weak_field_indexes = mono_aot_get_weak_field_indexes;
-#ifdef TARGET_OSX
+#ifndef DISABLE_CRASH_REPORTING
callbacks.install_state_summarizer = mini_register_sigterm_handler;
#endif
diff --git a/mono/mini/mini-runtime.h b/mono/mini/mini-runtime.h
index 915b0dedd53..ce49a570ee6 100644
--- a/mono/mini/mini-runtime.h
+++ b/mono/mini/mini-runtime.h
@@ -451,6 +451,7 @@ MonoJumpInfoToken* mono_jump_info_token_new2 (MonoMemPool *mp, MonoImage *image,
gpointer mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *patch_info, gboolean run_cctors, MonoError *error) MONO_LLVM_INTERNAL;
void mini_register_jump_site (MonoDomain *domain, MonoMethod *method, gpointer ip);
void mini_patch_jump_sites (MonoDomain *domain, MonoMethod *method, gpointer addr);
+gpointer mono_jit_search_all_backends_for_jit_info (MonoDomain *domain, MonoMethod *method, MonoJitInfo **ji);
gpointer mono_jit_find_compiled_method_with_jit_info (MonoDomain *domain, MonoMethod *method, MonoJitInfo **ji);
gpointer mono_jit_find_compiled_method (MonoDomain *domain, MonoMethod *method);
gpointer mono_jit_compile_method (MonoMethod *method, MonoError *error);
diff --git a/mono/mini/mini.c b/mono/mini/mini.c
index c102737b2f4..d3e013c0ebc 100644
--- a/mono/mini/mini.c
+++ b/mono/mini/mini.c
@@ -3874,7 +3874,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl
}
MONO_TIME_TRACK (mono_jit_stats.jit_gc_create_gc_map, mini_gc_create_gc_map (cfg));
- MONO_TIME_TRACK (mono_jit_stats.jit_save_seq_point_info, mono_save_seq_point_info (cfg));
+ MONO_TIME_TRACK (mono_jit_stats.jit_save_seq_point_info, mono_save_seq_point_info (cfg, cfg->jit_info));
if (!cfg->compile_aot) {
mono_save_xdebug_info (cfg);
diff --git a/mono/mini/seq-points.c b/mono/mini/seq-points.c
index 603f22d5e08..5081f2bab1f 100644
--- a/mono/mini/seq-points.c
+++ b/mono/mini/seq-points.c
@@ -99,7 +99,7 @@ collect_pred_seq_points (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, GS
}
void
-mono_save_seq_point_info (MonoCompile *cfg)
+mono_save_seq_point_info (MonoCompile *cfg, MonoJitInfo *jinfo)
{
MonoBasicBlock *bb;
GSList *bb_seq_points, *l;
@@ -244,6 +244,9 @@ mono_save_seq_point_info (MonoCompile *cfg)
else
mono_seq_point_info_free (cfg->seq_point_info);
mono_domain_unlock (domain);
+
+ g_assert (jinfo);
+ jinfo->seq_points = cfg->seq_point_info;
}
g_ptr_array_free (cfg->seq_points, TRUE);
diff --git a/mono/mini/seq-points.h b/mono/mini/seq-points.h
index e2a98696ef6..05ebaf63f45 100644
--- a/mono/mini/seq-points.h
+++ b/mono/mini/seq-points.h
@@ -10,7 +10,7 @@
#include <mono/metadata/seq-points-data.h>
void
-mono_save_seq_point_info (MonoCompile *cfg);
+mono_save_seq_point_info (MonoCompile *cfg, MonoJitInfo *jinfo);
MonoSeqPointInfo*
mono_get_seq_points (MonoDomain *domain, MonoMethod *method);
diff --git a/mono/utils/mono-state.c b/mono/utils/mono-state.c
index 736c667990e..c981664abdb 100644
--- a/mono/utils/mono-state.c
+++ b/mono/utils/mono-state.c
@@ -26,21 +26,44 @@ extern GCStats mono_gc_stats;
#include <mach/task_info.h>
#endif
-#define MONO_MAX_SUMMARY_LEN 900
+#define MONO_MAX_SUMMARY_LEN 2500
+static gchar output_dump_str [MONO_MAX_SUMMARY_LEN];
+
static JsonWriter writer;
static GString static_gstr;
-static char output_dump_str [MONO_MAX_SUMMARY_LEN];
-static void mono_json_writer_init_static (void) {
+static void mono_json_writer_init_memory (gchar *output_dump_str, int len)
+{
+ memset (&static_gstr, 0, sizeof (static_gstr));
+ memset (&writer, 0, sizeof (writer));
+ memset (output_dump_str, 0, len * sizeof (gchar));
+
static_gstr.len = 0;
- static_gstr.allocated_len = MONO_MAX_SUMMARY_LEN;
+ static_gstr.allocated_len = len;
static_gstr.str = output_dump_str;
- memset (output_dump_str, 0, sizeof (output_dump_str));
writer.indent = 0;
writer.text = &static_gstr;
}
+static void mono_json_writer_init_with_static (void)
+{
+ return mono_json_writer_init_memory (output_dump_str, MONO_MAX_SUMMARY_LEN);
+}
+
+static void assert_has_space (void)
+{
+ // Each individual key/value append should be roughly less than this many characters
+ const int margin = 35;
+
+ // Not using static, exit
+ if (static_gstr.allocated_len == 0)
+ return;
+
+ if (static_gstr.allocated_len - static_gstr.len < margin)
+ g_error ("Ran out of space to create crash dump json blob.");
+}
+
static void
mono_native_state_add_ctx (JsonWriter *writer, MonoContext *ctx)
{
@@ -49,14 +72,17 @@ mono_native_state_add_ctx (JsonWriter *writer, MonoContext *ctx)
mono_json_writer_object_key(writer, "ctx");
mono_json_writer_object_begin(writer);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "IP");
mono_json_writer_printf (writer, "\"%p\",\n", (gpointer) MONO_CONTEXT_GET_IP (ctx));
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "SP");
mono_json_writer_printf (writer, "\"%p\",\n", (gpointer) MONO_CONTEXT_GET_SP (ctx));
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "BP");
mono_json_writer_printf (writer, "\"%p\"\n", (gpointer) MONO_CONTEXT_GET_BP (ctx));
@@ -74,37 +100,53 @@ mono_native_state_add_frame (JsonWriter *writer, MonoFrameSummary *frame)
mono_json_writer_object_begin(writer);
if (frame->is_managed) {
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "is_managed");
mono_json_writer_printf (writer, "\"%s\",\n", frame->is_managed ? "true" : "false");
}
if (frame->unmanaged_data.is_trampoline) {
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "is_trampoline");
mono_json_writer_printf (writer, "\"true\",");
}
if (frame->is_managed) {
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "guid");
mono_json_writer_printf (writer, "\"%s\",\n", frame->managed_data.guid);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "token");
mono_json_writer_printf (writer, "\"0x%05x\",\n", frame->managed_data.token);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "native_offset");
mono_json_writer_printf (writer, "\"0x%x\",\n", frame->managed_data.native_offset);
+#ifndef MONO_PRIVATE_CRASHES
+ if (frame->managed_data.name != NULL) {
+ assert_has_space ();
+ mono_json_writer_indent (writer);
+ mono_json_writer_object_key(writer, "method_name");
+ mono_json_writer_printf (writer, "\"%s\",\n", frame->managed_data.name);
+ }
+#endif
+
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "il_offset");
mono_json_writer_printf (writer, "\"0x%05x\"\n", frame->managed_data.il_offset);
} else {
+ assert_has_space ();
mono_json_writer_indent (writer);
- mono_json_writer_object_key(writer, "native_address");
+ mono_json_writer_object_key (writer, "native_address");
if (frame->unmanaged_data.ip)
mono_json_writer_printf (writer, "\"%p\"", (void *) frame->unmanaged_data.ip);
else
@@ -113,6 +155,7 @@ mono_native_state_add_frame (JsonWriter *writer, MonoFrameSummary *frame)
if (frame->unmanaged_data.has_name) {
mono_json_writer_printf (writer, ",\n");
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "unmanaged_name");
mono_json_writer_printf (writer, "\"%s\"\n", frame->str_descr);
@@ -149,6 +192,8 @@ mono_native_state_add_frames (JsonWriter *writer, int num_frames, MonoFrameSumma
void
mono_native_state_add_thread (JsonWriter *writer, MonoThreadSummary *thread, MonoContext *ctx, gboolean first_thread)
{
+ assert_has_space ();
+
if (!first_thread) {
mono_json_writer_printf (writer, ",\n");
}
@@ -156,24 +201,36 @@ mono_native_state_add_thread (JsonWriter *writer, MonoThreadSummary *thread, Mon
mono_json_writer_indent (writer);
mono_json_writer_object_begin(writer);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "is_managed");
mono_json_writer_printf (writer, "%s,\n", thread->is_managed ? "true" : "false");
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "managed_thread_ptr");
mono_json_writer_printf (writer, "\"0x%x\",\n", (gpointer) thread->managed_thread_ptr);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "thread_info_addr");
mono_json_writer_printf (writer, "\"0x%x\",\n", (gpointer) thread->info_addr);
- if (thread->name) {
+ if (thread->error_msg != NULL) {
+ assert_has_space ();
+ mono_json_writer_indent (writer);
+ mono_json_writer_object_key(writer, "dumping_error");
+ mono_json_writer_printf (writer, "\"%s\",\n", thread->error_msg);
+ }
+
+ if (thread->name [0] != '\0') {
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "thread_name");
mono_json_writer_printf (writer, "\"%s\",\n", thread->name);
}
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "native_thread_id");
mono_json_writer_printf (writer, "\"0x%x\",\n", (gpointer) thread->native_thread_id);
@@ -227,6 +284,7 @@ mono_native_state_add_ee_info (JsonWriter *writer)
/*aot_mode = "error";*/
/*}*/
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "execution_context");
mono_json_writer_object_begin(writer);
@@ -239,6 +297,7 @@ mono_native_state_add_ee_info (JsonWriter *writer)
/*mono_json_writer_object_key(writer, "mono_use_llvm");*/
/*mono_json_writer_printf (writer, "\"%s\",\n", mono_use_llvm ? "true" : "false");*/
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "coop-enabled");
mono_json_writer_printf (writer, "\"%s\"\n", mono_threads_is_cooperative_suspension_enabled () ? "true" : "false");
@@ -259,10 +318,12 @@ mono_native_state_add_ee_info (JsonWriter *writer)
static void
mono_native_state_add_version (JsonWriter *writer)
{
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "configuration");
mono_json_writer_object_begin(writer);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "version");
@@ -270,6 +331,7 @@ mono_native_state_add_version (JsonWriter *writer)
mono_json_writer_printf (writer, "\"%s\",\n", build);
g_free (build);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "tlc");
#ifdef MONO_KEYWORD_THREAD
@@ -278,6 +340,7 @@ mono_native_state_add_version (JsonWriter *writer)
mono_json_writer_printf (writer, "\"normal\",\n");
#endif /* MONO_KEYWORD_THREAD */
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "sigsgev");
#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
@@ -286,6 +349,7 @@ mono_native_state_add_version (JsonWriter *writer)
mono_json_writer_printf (writer, "\"normal\",\n");
#endif
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "notifications");
#ifdef HAVE_EPOLL
@@ -296,14 +360,17 @@ mono_native_state_add_version (JsonWriter *writer)
mono_json_writer_printf (writer, "\"thread+polling\",\n");
#endif
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "architecture");
mono_json_writer_printf (writer, "\"%s\",\n", MONO_ARCHITECTURE);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "disabled_features");
mono_json_writer_printf (writer, "\"%s\",\n", DISABLED_FEATURES);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "smallconfig");
#ifdef MONO_SMALL_CONFIG
@@ -312,6 +379,7 @@ mono_native_state_add_version (JsonWriter *writer)
mono_json_writer_printf (writer, "\"disabled\",\n");
#endif
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "bigarrays");
#ifdef MONO_BIG_ARRAYS
@@ -320,6 +388,7 @@ mono_native_state_add_version (JsonWriter *writer)
mono_json_writer_printf (writer, "\"disabled\",\n");
#endif
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "softdebug");
#if !defined(DISABLE_SDB)
@@ -328,6 +397,7 @@ mono_native_state_add_version (JsonWriter *writer)
mono_json_writer_printf (writer, "\"disabled\",\n");
#endif
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "interpreter");
#ifndef DISABLE_INTERPRETER
@@ -336,6 +406,7 @@ mono_native_state_add_version (JsonWriter *writer)
mono_json_writer_printf (writer, "\"disabled\",\n");
#endif
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "llvm_support");
#ifdef MONO_ARCH_LLVM_SUPPORTED
@@ -347,11 +418,13 @@ mono_native_state_add_version (JsonWriter *writer)
#endif
const char *susp_policy = mono_threads_suspend_policy_name ();
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key (writer, "suspend");
mono_json_writer_printf (writer, "\"%s\"\n", susp_policy);
+ assert_has_space ();
mono_json_writer_indent_pop (writer);
mono_json_writer_indent (writer);
mono_json_writer_object_end (writer);
@@ -361,6 +434,7 @@ mono_native_state_add_version (JsonWriter *writer)
static void
mono_native_state_add_memory (JsonWriter *writer)
{
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "memory");
mono_json_writer_object_begin(writer);
@@ -372,10 +446,12 @@ mono_native_state_add_memory (JsonWriter *writer)
task_name_t task = mach_task_self ();
task_info(task, TASK_BASIC_INFO, (task_info_t) &t_info, &t_info_count);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "Resident Size");
mono_json_writer_printf (writer, "\"%lu\",\n", t_info.resident_size);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "Virtual Size");
mono_json_writer_printf (writer, "\"%lu\",\n", t_info.virtual_size);
@@ -384,22 +460,27 @@ mono_native_state_add_memory (JsonWriter *writer)
GCStats stats;
memcpy (&stats, &mono_gc_stats, sizeof (GCStats));
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "minor_gc_time");
mono_json_writer_printf (writer, "\"%lu\",\n", stats.minor_gc_time);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "major_gc_time");
mono_json_writer_printf (writer, "\"%lu\",\n", stats.major_gc_time);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "minor_gc_count");
mono_json_writer_printf (writer, "\"%lu\",\n", stats.minor_gc_count);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "major_gc_count");
mono_json_writer_printf (writer, "\"%lu\",\n", stats.major_gc_count);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "major_gc_time_concurrent");
mono_json_writer_printf (writer, "\"%lu\"\n", stats.major_gc_time_concurrent);
@@ -413,9 +494,9 @@ mono_native_state_add_memory (JsonWriter *writer)
static void
mono_native_state_add_prologue (JsonWriter *writer)
{
- mono_json_writer_init (writer);
mono_json_writer_object_begin(writer);
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "protocol_version");
mono_json_writer_printf (writer, "\"%s\",\n", MONO_NATIVE_STATE_PROTOCOL_VERSION);
@@ -430,6 +511,7 @@ mono_native_state_add_prologue (JsonWriter *writer)
const char *assertion_msg = g_get_assertion_message ();
if (assertion_msg != NULL) {
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "assertion_message");
@@ -445,6 +527,7 @@ mono_native_state_add_prologue (JsonWriter *writer)
}
// Start threads array
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "threads");
mono_json_writer_array_begin (writer);
@@ -491,9 +574,14 @@ mono_native_state_free (JsonWriter *writer, gboolean free_data)
}
void
-mono_summarize_native_state_begin (void)
+mono_summarize_native_state_begin (gchar *mem, int size)
{
- mono_json_writer_init_static ();
+ // Shared global mutable memory, only use when VM crashing
+ if (!mem)
+ mono_json_writer_init_with_static ();
+ else
+ mono_json_writer_init_memory (mem, size);
+
mono_native_state_init (&writer);
}
@@ -522,7 +610,7 @@ mono_crash_dump (const char *jsonFile, MonoStackHash *hashes)
// Save up to 100 dump files for a given stacktrace hash
for (int increment = 0; increment < 100; increment++) {
FILE* fp;
- char *name = g_strdup_printf ("mono_crash.%" PRIx64 " .%d.json", hashes->offset_free_hash, increment);
+ char *name = g_strdup_printf ("mono_crash.%" PRIx64 ".%d.json", hashes->offset_free_hash, increment);
if ((fp = fopen (name, "ab"))) {
if (ftell (fp) == 0) {
diff --git a/mono/utils/mono-state.h b/mono/utils/mono-state.h
index 0c646ca0006..d69dbf4ab3f 100644
--- a/mono/utils/mono-state.h
+++ b/mono/utils/mono-state.h
@@ -27,7 +27,7 @@ MONO_BEGIN_DECLS
*/
void
-mono_summarize_native_state_begin (void);
+mono_summarize_native_state_begin (char *mem, int size);
char *
mono_summarize_native_state_end (void);
diff --git a/mono/utils/mono-threads-posix.c b/mono/utils/mono-threads-posix.c
index fa2169ffd45..2aabb05debe 100644
--- a/mono/utils/mono-threads-posix.c
+++ b/mono/utils/mono-threads-posix.c
@@ -211,6 +211,19 @@ mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
return pthread_create (tid, NULL, (void *(*)(void *)) func, arg) == 0;
}
+size_t
+mono_native_thread_get_name (MonoNativeThreadId tid, char *name_out, size_t max_len)
+{
+#ifdef HAVE_PTHREAD_GETNAME_NP
+ int error = pthread_getname_np(tid, name_out, max_len);
+ if (error != 0)
+ return 0;
+ return strlen(name_out);
+#else
+ return 0;
+#endif
+}
+
void
mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
{
diff --git a/mono/utils/mono-threads.h b/mono/utils/mono-threads.h
index f02437e1c71..7a94b5b3423 100644
--- a/mono/utils/mono-threads.h
+++ b/mono/utils/mono-threads.h
@@ -620,6 +620,9 @@ mono_native_thread_create (MonoNativeThreadId *tid, T func, gpointer arg)
MONO_API void
mono_native_thread_set_name (MonoNativeThreadId tid, const char *name);
+size_t
+mono_native_thread_get_name (MonoNativeThreadId tid, char *name_out, size_t max_len);
+
MONO_API gboolean
mono_native_thread_join (MonoNativeThreadId tid);