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:
authorAlexander Kyte <alexmkyte@gmail.com>2018-10-19 01:59:38 +0300
committerAlexander Köplinger <alex.koeplinger@outlook.com>2018-10-19 01:59:38 +0300
commit5ed4143b4f460d0a7a7d4ab745297bd9ab3c0cc9 (patch)
tree1e8c390b42e1725b0cfc07ccb27db8ae8d4f5914
parentf0db92c2b6d3e6690cdc1069237f034570950e62 (diff)
[2018-06] Crash Reporter V2 (#11162)
* [runtime] Add icall to format crashes to disk in uniform way * [crash] More gentle error handling around stack walking * [crash] Make self-summarizier not need managed thread state * [crash] Remove reliance on sgen/gchandles * [crash] Refactor dumper mechanism * [crash] Make threads not die during thread dump * [crash] Add silent option to dumper * [crash] Print error when unable to dump thread * [crash] Add icalls to directly trigger telemetry-style walks * [crash] Increase maximum frame depth * [crash] Add support for concurrent global dump requests * [crash] Switch from CAS+Sleep to semaphore for dumper Locks and GC-Safe regions are both unsafe in a signal handler context. CAS primitives and the sleep syscall was used before. Semaphores appear to be safe, enabling the following cleanup. * [runtime] Make find_method use stack memory for dumper-facing entry point * [crash] Enable install_state_summarizer on all platforms * [crash] Dump managed stacks from controlling thread * [crash] Fix string races with DumpTotal icall * [crash] Fix space in crash file name * [crash] Fix lifetime and size checks on mono-state memory * [runtime] Make sequence point lookup lockless * [crash] Fix managed exception dumper icall format * [crash] Add tests for concurrent dumping * [crash] Print half-completed dump when space runs out * [crash] Bump static memory for crash string to 500k Note: - This memory isn't accessed until we're crashing - On any modern machine, unused static memory is lazily allocated. When we access it, the fault will trigger the MMU to map memory for us. - This is guaranteed to be reentrant and safe. mmap (the alternative) is surely fairly stable on many systems, but this is not guaranteed. The documentation pages for mmap specifically claim it as not safe in these async contexts. * [crash] Make whitelist lookup lockless * [crash] Split unmanaged ptrs into method ip + offset * [crash] Copy safer strings with crash privacy disabled * [crash] Fix BindingFlags undefined with mcs+linux x64 * [crash] Fix icall declarations for new style * [crash] Squashed support for RegisterReportingForNativeLib * [crash] Disable tests on linux / non-osx platforms
-rw-r--r--configure.ac4
-rw-r--r--mcs/build/platforms/linux.make2
-rw-r--r--mcs/class/corlib/Mono/Runtime.cs55
-rw-r--r--mcs/class/corlib/Test/System/ExceptionTest.cs180
-rw-r--r--mono/metadata/class-internals.h3
-rw-r--r--mono/metadata/debug-helpers.c12
-rw-r--r--mono/metadata/domain-internals.h2
-rw-r--r--mono/metadata/icall-def.h4
-rw-r--r--mono/metadata/icall.c97
-rw-r--r--mono/metadata/mono-debug.c34
-rw-r--r--mono/metadata/object-internals.h4
-rw-r--r--mono/metadata/threads-types.h36
-rw-r--r--mono/metadata/threads.c473
-rw-r--r--mono/mini/aot-runtime.c2
-rw-r--r--mono/mini/debugger-agent.c43
-rw-r--r--mono/mini/interp/transform.c6
-rw-r--r--mono/mini/mini-exceptions.c263
-rw-r--r--mono/mini/mini-posix.c23
-rw-r--r--mono/mini/mini-runtime.c32
-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.c131
-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, 1085 insertions, 349 deletions
diff --git a/configure.ac b/configure.ac
index 4f9112113d2..e534ac652e1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -40,7 +40,7 @@ MONO_VERSION_BUILD=`echo $VERSION | cut -d . -f 3`
# This can be reset to 0 when Mono's version number is bumped
# since it's part of the corlib version (the prefix '1' in the full
# version number is to ensure the number isn't treated as octal in C)
-MONO_CORLIB_COUNTER=11
+MONO_CORLIB_COUNTER=14
MONO_CORLIB_VERSION=`printf "1%02d%02d%02d%03d" $MONO_VERSION_MAJOR $MONO_VERSION_MINOR 0 $MONO_CORLIB_COUNTER`
AC_DEFINE_UNQUOTED(MONO_CORLIB_VERSION,$MONO_CORLIB_VERSION,[Version of the corlib-runtime interface])
@@ -2331,7 +2331,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>], [
diff --git a/mcs/build/platforms/linux.make b/mcs/build/platforms/linux.make
index 02348318afe..b0a7ebb9af0 100644
--- a/mcs/build/platforms/linux.make
+++ b/mcs/build/platforms/linux.make
@@ -6,7 +6,7 @@
PLATFORM_MCS_FLAGS =
PLATFORM_RUNTIME = $(RUNTIME)
PLATFORM_CORLIB = mscorlib.dll
-PLATFORM_TEST_HARNESS_EXCLUDES =
+PLATFORM_TEST_HARNESS_EXCLUDES = NotOnLinux,
EXTERNAL_MCS = mcs
EXTERNAL_MBAS = mbas
diff --git a/mcs/class/corlib/Mono/Runtime.cs b/mcs/class/corlib/Mono/Runtime.cs
index 1addbac49ac..1c8cdc4a1ab 100644
--- a/mcs/class/corlib/Mono/Runtime.cs
+++ b/mcs/class/corlib/Mono/Runtime.cs
@@ -63,7 +63,7 @@ namespace Mono {
// Should not be removed intended for external use
// Safe to be called using reflection
- // Format is undefined only for use as a string for reporting
+ // Format is undefined only for use as a string for
[MethodImplAttribute (MethodImplOptions.InternalCall)]
#if MOBILE || XAMMAC_4_5
public
@@ -105,6 +105,21 @@ namespace Mono {
[MethodImplAttribute (MethodImplOptions.InternalCall)]
static extern void SendMicrosoftTelemetry_internal (IntPtr payload, ulong portable_hash, ulong unportable_hash);
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ static extern void WriteStateToFile_internal (IntPtr payload, ulong portable_hash, ulong unportable_hash);
+
+ static void
+ 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))
+ {
+ WriteStateToFile_internal (payload_chars.Value, portable_hash, unportable_hash);
+ }
+ }
+
static void SendMicrosoftTelemetry (string payload_str, ulong portable_hash, ulong unportable_hash)
{
if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
@@ -150,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 36f37e9c66b..68a550b51eb 100644
--- a/mcs/class/corlib/Test/System/ExceptionTest.cs
+++ b/mcs/class/corlib/Test/System/ExceptionTest.cs
@@ -15,6 +15,9 @@ using System.Runtime.Serialization;
using NUnit.Framework;
+using System.Threading.Tasks;
+using System.Reflection;
+
namespace MonoTests.System
{
[TestFixture]
@@ -400,6 +403,183 @@ namespace MonoTests.System
Assert.IsNull (a.InnerException.Source);
}
+#if !MOBILE
+ // Ensure that we can convert a stacktrace to a
+ // telemetry message
+ //
+ [Test]
+ [Category("NotOnWindows")]
+ [Category("NotOnLinux")]
+ public void StacktraceToState ()
+ {
+ try {
+ throw new Exception ("#0");
+ } catch (Exception exc) {
+ var monoType = Type.GetType ("Mono.Runtime", false);
+ var convert = monoType.GetMethod("ExceptionToState", BindingFlags.NonPublic | BindingFlags.Static);
+ object [] convert_params = new object[] {exc};
+ var output = (Tuple<String, ulong, ulong>) convert.Invoke(null, convert_params);
+
+ 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");
+
+ // Console.WriteLine (dump);
+ // dump should look something like:
+ // {
+ // "protocol_version" : "0.0.1",
+ // "configuration" : {
+ // "version" : "5.19.0 (managed_telemetry_pipeline/d342c73e320 Wed Aug 15 14:40:40 EDT 2018)",
+ // "tlc" : "normal",
+ // "sigsgev" : "altstack",
+ // "notifications" : "kqueue",
+ // "architecture" : "amd64",
+ // "disabled_features" : "none",
+ // "smallconfig" : "disabled",
+ // "bigarrays" : "disabled",
+ // "softdebug" : "enabled",
+ // "interpreter" : "enabled",
+ // "llvm_support" : "disabled",
+ // "suspend" : "hybrid"
+ // },
+ // "memory" : {
+ // "Resident Size" : "40693760",
+ // "Virtual Size" : "4521312256",
+ // "minor_gc_time" : "216992",
+ // "major_gc_time" : "0",
+ // "minor_gc_count" : "6",
+ // "major_gc_count" : "0",
+ // "major_gc_time_concurrent" : "0"
+ // },
+ // "threads" : [
+ // {
+ // "is_managed" : false,
+ // "managed_thread_ptr" : "0x0",
+ // "thread_info_addr" : "0x0",
+ // "native_thread_id" : "0x0",
+ // "managed_frames" : [
+ // {
+ // "is_managed" : "true",
+ // "guid" : "43A03618-E657-44B0-B9FA-F63314A3C1B2",
+ // "token" : "0x60008da",
+ // "native_offset" : "0xb2",
+ // "il_offset" : "0x00000"
+ // }
+ // ]
+ // }
+ // ]
+ // }
+ }
+ }
+
+ 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")]
+ [Category("NotOnLinux")]
+ public void DumpICallSingleOnce ()
+ {
+ DumpSingle ();
+ }
+
+ [Test]
+ [Category("NotOnWindows")]
+ [Category("NotOnLinux")]
+ 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")]
+ [Category("NotOnLinux")]
+ public void DumpICallSingleAsync ()
+ {
+ // checks that dumping works in an async context
+ var t = Task.Run(() => DumpSingle ());
+ t.Wait ();
+ }
+
+ [Test]
+ [Category("NotOnWindows")]
+ [Category("NotOnLinux")]
+ public void DumpICallTotalOnce ()
+ {
+ DumpTotal ();
+ }
+
+ [Test]
+ [Category("NotOnWindows")]
+ [Category("NotOnLinux")]
+ public void DumpICallTotalRepeated ()
+ {
+ // checks that the state doesn't get broken with repeated use
+ DumpTotal ();
+ DumpTotal ();
+ DumpTotal ();
+ }
+
+ [Test]
+ [Category("NotOnWindows")]
+ [Category("NotOnLinux")]
+ public void DumpICallTotalAsync ()
+ {
+ // checks that dumping works in an async context
+ var t = Task.Run(() => DumpTotal ());
+ t.Wait ();
+ }
+
+ [Test]
+ [Category("NotOnWindows")]
+ [Category("NotOnLinux")]
+ 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]
public void StackTrace ()
{
diff --git a/mono/metadata/class-internals.h b/mono/metadata/class-internals.h
index 08238ade9b2..297ecc80e18 100644
--- a/mono/metadata/class-internals.h
+++ b/mono/metadata/class-internals.h
@@ -1110,6 +1110,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 7487237950d..b6b72371a2f 100644
--- a/mono/metadata/debug-helpers.c
+++ b/mono/metadata/debug-helpers.c
@@ -58,8 +58,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);
@@ -74,8 +74,8 @@ wrapper_type_names [MONO_WRAPPER_NUM + 1] = {
NULL
};
-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);
@@ -917,7 +917,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, "");
@@ -933,7 +933,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 2a961aa0103..cc9045778a5 100644
--- a/mono/metadata/domain-internals.h
+++ b/mono/metadata/domain-internals.h
@@ -249,6 +249,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-def.h b/mono/metadata/icall-def.h
index e10d9915333..0918f2f4498 100644
--- a/mono/metadata/icall-def.h
+++ b/mono/metadata/icall-def.h
@@ -93,11 +93,15 @@ 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))
ICALL_TYPE(RTCLASS, "Mono.RuntimeClassHandle", RTCLASS_1)
HANDLES(ICALL(RTCLASS_1, "GetTypeFromClass", ves_icall_Mono_RuntimeClassHandle_GetTypeFromClass))
diff --git a/mono/metadata/icall.c b/mono/metadata/icall.c
index 62f329a4dd8..03752fd9730 100644
--- a/mono/metadata/icall.c
+++ b/mono/metadata/icall.c
@@ -5921,6 +5921,103 @@ ves_icall_Mono_Runtime_SendMicrosoftTelemetry (char *payload, guint64 portable_h
#endif
}
+ICALL_EXPORT void
+ves_icall_Mono_Runtime_DumpTelemetry (char *payload, guint64 portable_hash, guint64 unportable_hash, MonoError *error)
+{
+#ifdef TARGET_OSX
+ MonoStackHash hashes;
+ memset (&hashes, 0, sizeof (MonoStackHash));
+ hashes.offset_free_hash = portable_hash;
+ hashes.offset_rich_hash = unportable_hash;
+ mono_crash_dump (payload, &hashes);
+#endif
+}
+
+ICALL_EXPORT MonoStringHandle
+ves_icall_Mono_Runtime_DumpStateSingle (guint64 *portable_hash, guint64 *unportable_hash, MonoError *error)
+{
+ MonoStringHandle result;
+
+#ifdef TARGET_OSX
+ 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;
+}
+
+
+// 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;
+
+#ifdef TARGET_OSX
+ 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 ();
+
+ 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 void
+ves_icall_Mono_Runtime_RegisterReportingForNativeLib (const char *path_suffix, const char *module_name)
+{
+#ifdef TARGET_OSX
+ mono_get_eh_callbacks ()->mono_register_native_library (path_suffix, module_name);
+#endif
+}
+
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 1b0f618b5ec..5ac0245829a 100644
--- a/mono/metadata/mono-debug.c
+++ b/mono/metadata/mono-debug.c
@@ -600,8 +600,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;
@@ -611,17 +611,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;
@@ -667,7 +673,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;
@@ -678,19 +684,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;
}
@@ -705,10 +711,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;
@@ -716,13 +722,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 eaabf5df488..7d4a30d98f8 100644
--- a/mono/metadata/object-internals.h
+++ b/mono/metadata/object-internals.h
@@ -655,8 +655,10 @@ typedef struct {
void (*mono_clear_abort_threshold) (void);
void (*mono_reraise_exception) (MonoException *ex);
#ifdef TARGET_OSX
- 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);
#endif
} MonoRuntimeExceptionHandlingCallbacks;
diff --git a/mono/metadata/threads-types.h b/mono/metadata/threads-types.h
index 761f60429ce..25ef03f7402 100644
--- a/mono/metadata/threads-types.h
+++ b/mono/metadata/threads-types.h
@@ -340,8 +340,9 @@ mono_set_thread_dump_dir(gchar* dir);
#ifdef TARGET_OSX
#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;
@@ -354,6 +355,8 @@ typedef struct {
} managed_data;
struct {
intptr_t ip;
+ gint32 offset;
+ const char *module;
gboolean is_trampoline;
gboolean has_name;
} unmanaged_data;
@@ -365,13 +368,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;
+ gpointer *jit_tls;
+ gpointer *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];
@@ -379,10 +398,19 @@ 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);
-#endif
+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 // TARGET_OSX
#endif /* _MONO_METADATA_THREADS_TYPES_H_ */
diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c
index 152038ab038..69acdec9e9e 100644
--- a/mono/metadata/threads.c
+++ b/mono/metadata/threads.c
@@ -3756,6 +3756,11 @@ typedef struct {
MonoInternalThread **threads;
} CollectThreadsUserData;
+typedef struct {
+ int nthreads, max_threads;
+ MonoNativeThreadId *threads;
+} CollectThreadIdsUserData;
+
static void
collect_thread (gpointer key, gpointer value, gpointer user)
{
@@ -3766,6 +3771,16 @@ collect_thread (gpointer key, gpointer value, gpointer user)
ud->threads [ud->nthreads ++] = thread;
}
+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.
@@ -3791,6 +3806,27 @@ collect_threads (MonoInternalThread **thread_array, 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)
{
@@ -5892,250 +5928,311 @@ mono_set_thread_dump_dir (gchar* dir) {
}
#ifdef TARGET_OSX
+static gboolean
+mono_threads_summarize_native_self (MonoThreadSummary *out, MonoContext *ctx)
+{
+ memset (out, 0, sizeof (MonoThreadSummary));
+ out->ctx = ctx;
-static size_t num_threads_summarized = 0;
+ MonoNativeThreadId current = mono_native_thread_id_get();
+ out->native_thread_id = (intptr_t) current;
-// mono_thread_internal_current doesn't always work in signal
-// handler contexts. This does.
-static MonoInternalThread *
-find_missing_thread (MonoNativeThreadId id)
-{
- MonoInternalThread *thread_array [128];
- int nthreads = collect_threads (thread_array, 128);
+ mono_get_eh_callbacks ()->mono_summarize_unmanaged_stack (out);
- for (int i=0; i < nthreads; i++) {
- MonoNativeThreadId tid = thread_get_tid (thread_array [i]);
- if (tid == id)
- return thread_array [i];
- }
- return NULL;
+ 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;
+ gboolean success = mono_threads_summarize_native_self (out, ctx);
- MonoNativeThreadId current = mono_native_thread_id_get();
- MonoInternalThread *thread = find_missing_thread (current);
+ // Finish this on the same thread
- // Not one of ours
- if (!thread)
- return FALSE;
+ if (success)
+ mono_get_eh_callbacks ()->mono_summarize_managed_stack (out);
- 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;
+ return success;
+}
+
+#define MAX_NUM_THREADS 128
+typedef struct {
+ gint32 has_owner; // state of this memory
+
+ MonoSemType update; // notify of addition of threads
+
+ int nthreads;
+ MonoNativeThreadId thread_array [MAX_NUM_THREADS]; // ids of threads we're dumping
+
+ int nthreads_attached; // Number of threads self-registered
+ MonoThreadSummary *all_threads [MAX_NUM_THREADS];
+
+ gboolean silent; // print to stdout
+} SummarizerGlobalState;
+
+static gboolean
+summarizer_state_init (SummarizerGlobalState *state, MonoNativeThreadId current, int *my_index)
+{
+ 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);
}
- mono_get_eh_callbacks ()->mono_summarize_stack (domain, out, ctx);
- // FIXME: handle failure gracefully
- // Enable when doing unmanaged
- /*g_assert (out->num_frames > 0);*/
- /*if (out->num_frames == 0)*/
- /*return FALSE;*/
+ for (int i = 0; i < state->nthreads; i++) {
+ if (state->thread_array [i] == current) {
+ *my_index = i;
+ break;
+ }
+ }
- return TRUE;
+ return not_started;
}
-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;
-
-static const char *
-thread_summary_state_to_str (MonoSummaryState state)
+static void
+summarizer_signal_other_threads (SummarizerGlobalState *state, MonoNativeThreadId current, int current_idx)
{
- 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";
+ sigset_t sigset, old_sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGTERM);
+
+ for (int i=0; i < state->nthreads; i++) {
+ sigprocmask (SIG_UNBLOCK, &sigset, &old_sigset);
+
+ 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
}
}
-static gint32 summary_started;
-static gint32 summarizing_thread_state;
-static MonoNativeThreadId summarizing_thread;
+// Returns true when there are shared global references to "this_thread"
+static gboolean
+summarizer_post_dump (SummarizerGlobalState *state, MonoThreadSummary *this_thread, int current_idx)
+{
+ mono_memory_barrier ();
-gboolean
-mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes)
+ 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;
+ }
+
+ // 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);
+
+ return TRUE;
+}
+
+// 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)
{
- gint32 started_state = mono_atomic_cas_i32 (&summary_started, 1 /* set */, 0 /* compare */);
- gboolean not_started = started_state == 0;
+ gint64 milliseconds_in_second = 1000;
+ gint64 timeout_total = milliseconds_in_second * timeout_seconds;
- MonoNativeThreadId current = mono_native_thread_id_get ();
+ gint64 end = mono_msec_ticks () + timeout_total;
+
+ while (TRUE) {
+ if (mono_atomic_load_i32 ((volatile gint32 *) &state->nthreads_attached) == state->nthreads)
+ break;
- MOSTLY_ASYNC_SAFE_PRINTF("Entering thread summarizer from %zu\n", current);
+ gint64 now = mono_msec_ticks ();
+ gint64 remaining = end - now;
+ if (remaining <= 0)
+ break;
- 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.
- MonoInternalThread *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++) {
- MonoNativeThreadId tid = thread_get_tid (thread_array [i]);
- if (current == tid)
- continue;
+ mono_os_sem_timedwait (&state->update, remaining, MONO_SEM_FLAGS_NONE);
+ }
- // Request every other thread dumps themselves before us
- MonoThreadInfo *info = (MonoThreadInfo*) thread_array [i]->thread_info;
+ return;
+}
- mono_memory_barrier ();
- size_t old_num_summarized = num_threads_summarized;
+static void
+summarizer_state_term (SummarizerGlobalState *state, gchar **out, gchar *mem, size_t provided_size)
+{
+ // See the array writes
+ mono_memory_barrier ();
- sigprocmask (SIG_UNBLOCK, &sigset, &old_sigset);
+ 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);
- 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);
+ if (new_old_value != old_value)
+ old_value = new_old_value;
+ else
+ break;
+ }
- if (summarizing_thread_state != MONO_SUMMARY_EXPECT) {
- const char *name = thread_summary_state_to_str (summarizing_thread_state);
- g_error ("Status after init wrong: %s\n", name);
- }
+ MonoThreadSummary *thread = (MonoThreadSummary *) old_value;
+ if (!thread)
+ continue;
- summarizing_thread = tid;
- mono_threads_pthread_kill (info, SIGTERM);
+ // 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);
- // Number of seconds to give each dumping thread
- int count = 4;
+ mono_summarize_native_state_add_thread (thread, thread->ctx);
- while (old_num_summarized == num_threads_summarized && count > 0) {
- sleep (1);
- mono_memory_barrier ();
- const char *name = thread_summary_state_to_str (summarizing_thread_state);
- MOSTLY_ASYNC_SAFE_PRINTF("Waiting for signalled thread %zu to collect stacktrace. Status: %s\n", tid, name);
- count--;
- }
- if (count == 0) {
- MonoBoolean timeout_abort_success;
+ // 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 ();
- // 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;
+ mono_os_sem_destroy (&state->update);
- case MONO_SUMMARY_EMPTY: {
- g_assert (summarizing_thread == (MonoNativeThreadId) NULL);
- break;
+ memset (state, 0, sizeof (*state));
+ mono_atomic_store_i32 ((volatile gint32 *)&state->has_owner, 0);
+}
- case MONO_SUMMARY_EXAMINE:
- MOSTLY_ASYNC_SAFE_PRINTF("Timed out, thread did not finish dumping\n");
+static void
+summarizer_state_wait (MonoThreadSummary *thread)
+{
+ gint64 milliseconds_in_second = 1000;
- MonoNativeThreadId old_val = mono_atomic_cas_ptr ((gpointer) &summarizing_thread, GINT_TO_POINTER(tid) /* set */, NULL /* compare */);
- g_assertf (old_val == NULL, "Attempting to abandon dumping of thread, and thread changed.", NULL);
+ // 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_EXAMINE /* compare */);
- g_assertf (timeout_abort_state == MONO_SUMMARY_EXAMINE, "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;
- }
- case MONO_SUMMARY_EXPECT: {
- MOSTLY_ASYNC_SAFE_PRINTF("Timed out, thread did not respond to signal\n");
+ int current_idx;
+ MonoNativeThreadId current = mono_native_thread_id_get ();
+ gboolean this_thread_controls = summarizer_state_init (&state, current, &current_idx);
- MonoNativeThreadId old_val = mono_atomic_cas_ptr ((gpointer) &summarizing_thread, NULL /* set */, GINT_TO_POINTER(tid) /* compare */);
- g_assertf (tid == old_val, "Attempting to abandon dumping of thread %zu, and thread changed to %zu.", tid, old_val);
+ if (this_thread_controls) {
+ state.silent = silent;
+ summarizer_signal_other_threads (&state, current, current_idx);
+ }
- 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);
+ MonoThreadSummary this_thread;
- break;
- }
- default:
- g_assert_not_reached ();
- }
- }
- }
+ 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);
- // After dumping all the other threads, we dump our own.
- summarizing_thread = current;
+ // 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);
}
- mono_memory_barrier ();
+ // 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);
- MOSTLY_ASYNC_SAFE_PRINTF("Self-reporting for thread %zu. Registered summarizing thread right now is %zu\n", current, summarizing_thread);
- g_assert (current == summarizing_thread);
+ // Wait up to 2 seconds for all of the other threads to catch up
+ summary_timedwait (&state, 2);
- 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 (summarizing_thread_state);
- MOSTLY_ASYNC_SAFE_PRINTF ("Dumping thread could not obtain ownership of dumping memory. Timeout? Enum was %s", name);
- goto fail;
- }
+ 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 {
- summarizing_thread_state = MONO_SUMMARY_EXAMINE;
- mono_memory_barrier ();
+ // Wait here, keeping our stack memory alive
+ // for the dumper
+ summarizer_state_wait (&this_thread);
}
- // 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;
- }
+ // FIXME: How many threads should be counted?
+ if (hashes)
+ *hashes = this_thread.hashes;
- mono_summarize_native_state_add_thread (&this_thread, ctx);
+ return TRUE;
+}
- 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);
- }
+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;
- mono_memory_barrier ();
- num_threads_summarized++;
- mono_memory_barrier ();
+ while (TRUE) {
+ gint64 next_request_id = mono_atomic_load_i64 ((volatile gint64 *) &request_available_to_run);
- if (not_started) {
- // We are the dumper
- *out = mono_summarize_native_state_end ();
- if (hashes)
- *hashes = this_thread.hashes;
- return TRUE;
+ 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 4aa032a1a16..0aee854e35e 100644
--- a/mono/mini/aot-runtime.c
+++ b/mono/mini/aot-runtime.c
@@ -3270,6 +3270,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-agent.c b/mono/mini/debugger-agent.c
index 7bd26dad12e..8ced1d78dc5 100644
--- a/mono/mini/debugger-agent.c
+++ b/mono/mini/debugger-agent.c
@@ -4296,7 +4296,6 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
int i, j;
MonoSeqPointInfo *seq_points;
MonoDomain *domain;
- MonoMethod *jmethod;
if (!breakpoints)
return;
@@ -4320,20 +4319,16 @@ 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);
@@ -4352,22 +4347,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);
}
diff --git a/mono/mini/interp/transform.c b/mono/mini/interp/transform.c
index 0cfdf0afdcc..0c50be8ec04 100644
--- a/mono/mini/interp/transform.c
+++ b/mono/mini/interp/transform.c
@@ -1791,7 +1791,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;
@@ -1878,6 +1878,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
@@ -4852,7 +4854,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 dd04c1edbcc..ad25cfdf155 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);
#ifdef TARGET_OSX
-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);
#endif
static gboolean
@@ -236,8 +238,10 @@ mono_exceptions_init (void)
cbs.mono_walk_stack_with_state = mono_walk_stack_with_state;
#ifdef TARGET_OSX
- 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;
#endif
if (mono_llvm_only) {
@@ -1088,7 +1092,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 (), (MonoJitTlsData *)thread->jit_data, mono_get_lmf (), unwind_options, user_data);
+ mono_walk_stack_full (func, start_ctx, mono_domain_get (), (MonoJitTlsData *)thread->jit_data, mono_get_lmf (), unwind_options, user_data, FALSE);
}
/**
@@ -1123,7 +1127,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
@@ -1144,15 +1148,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;
@@ -1195,14 +1200,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));
@@ -1219,14 +1237,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;
@@ -1251,7 +1279,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];
}
@@ -1266,6 +1294,7 @@ typedef struct {
int num_frames;
int max_frames;
MonoStackHash *hashes;
+ const char *error;
} MonoSummarizeUserData;
static void
@@ -1282,37 +1311,78 @@ copy_summary_string_safe (char *in, const char *out)
return;
}
+typedef struct {
+ char *suffix;
+ char *exported_name;
+} MonoLibWhitelistEntry;
+
+static GList *native_library_whitelist;
+
+static void
+mono_crash_reporting_register_native_library (const char *module_path, const char *module_name)
+{
+ // Examples: libsystem_pthread.dylib -> "pthread"
+ // Examples: libsystem_platform.dylib -> "platform"
+ // Examples: mono-sgen -> "mono" from above line
+ MonoLibWhitelistEntry *entry = g_new0 (MonoLibWhitelistEntry, 1);
+ entry->suffix = g_strdup (module_path);
+ entry->exported_name = g_strdup (module_name);
+ native_library_whitelist = g_list_append (native_library_whitelist, entry);
+}
+
static gboolean
-mono_get_portable_ip (intptr_t in_ip, intptr_t *out_ip, char *out_name)
+check_whitelisted_module (const char *in_name, const char **out_module)
{
- // 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 (g_str_has_suffix (in_name, "mono-sgen")) {
+ if (out_module)
+ *out_module = "mono";
+ return TRUE;
+ }
+
+ for (GList *cursor = native_library_whitelist; cursor; cursor = cursor->next) {
+ MonoLibWhitelistEntry *iter = (MonoLibWhitelistEntry *) cursor->data;
+ if (!g_str_has_suffix (in_name, iter->suffix))
+ continue;
+ if (out_module)
+ *out_module = iter->exported_name;
+ return TRUE;
+ }
+
+ return FALSE;
+}
- success = dladdr ((void*)in_ip, &info);
+static intptr_t
+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
+mono_get_portable_ip (intptr_t in_ip, intptr_t *out_ip, gint32 *out_offset, 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 ((intptr_t) info.dli_saddr, (intptr_t) info.dli_fbase);
+ *out_offset = in_ip - (intptr_t) info.dli_saddr;
-#ifndef MONO_PRIVATE_CRASHES
- if (info.dli_saddr && out_name)
- copy_summary_string_safe (out_name, info.dli_sname);
-#endif
return TRUE;
}
@@ -1343,34 +1413,40 @@ summarize_offset_rich_hash (intptr_t accum, MonoFrameSummary *frame)
} else {
hash_accum += mono_metadata_str_hash (frame->str_descr);
hash_accum += frame->managed_data.token;
- hash_accum += frame->managed_data.native_offset;
+ hash_accum += frame->managed_data.il_offset;
}
return hash_accum;
}
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 (!managed && 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;
-
+
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);
@@ -1379,40 +1455,67 @@ 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 frame_native_offset, gboolean managed, gpointer user_data)
+{
+ int il_offset = -1;
+
+ if (managed && method) {
+ MonoDebugSourceLocation *location = mono_debug_lookup_source_location (method, frame_native_offset, mono_domain_get ());
+ if (location) {
+ il_offset = location->il_offset;
+ mono_debug_free_source_location (location);
+ }
+ }
+
+ intptr_t portable_ip = 0;
+ gint32 offset = 0;
+ mono_get_portable_ip ((intptr_t) ip, &portable_ip, &offset, NULL, NULL);
+
+ return summarize_frame_internal (method, (gpointer) portable_ip, frame_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;
- intptr_t ip = 0x0;
- mono_get_portable_ip ((intptr_t) MONO_CONTEXT_GET_IP (ctx), &ip, NULL);
+ if (frame->ji && (frame->ji->is_trampoline || frame->ji->async))
+ return FALSE; // Keep unwinding
+ intptr_t ip = 0;
+ gint32 offset = 0;
+ mono_get_portable_ip ((intptr_t) MONO_CONTEXT_GET_IP (ctx), &ip, &offset, 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, offset, frame->il_offset, is_managed, data);
}
static void
@@ -1427,13 +1530,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));
@@ -1448,12 +1551,21 @@ 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
- //
+ //
#ifdef HAVE_BACKTRACE_SYMBOLS
intptr_t frame_ips [MONO_MAX_SUMMARY_FRAMES];
@@ -1461,8 +1573,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.offset, &frame->unmanaged_data.module, (char *) frame->str_descr);
if (!success)
continue;
@@ -1471,6 +1584,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
@@ -3125,7 +3250,7 @@ mono_handle_native_crash (const char *signal, void *ctx, MONO_SIG_HANDLER_INFO_T
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 ();
}
diff --git a/mono/mini/mini-posix.c b/mono/mini/mini-posix.c
index bdfe9267fb2..91ac6ae67dd 100644
--- a/mono/mini/mini-posix.c
+++ b/mono/mini/mini-posix.c
@@ -218,28 +218,23 @@ MONO_SIG_HANDLER_FUNC (static, sigterm_signal_handler)
{
MONO_SIG_HANDLER_GET_CONTEXT;
- // 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
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 ();
if (mono_merp_enabled ()) {
pid_t crashed_pid = getpid ();
mono_merp_invoke (crashed_pid, "SIGTERM", output, &hashes);
} else {
- // 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);
}
mono_chain_signal (MONO_SIG_HANDLER_PARAMS);
- exit (1);
}
#endif
@@ -398,8 +393,14 @@ remove_signal_handler (int signo)
void
mini_register_sigterm_handler (void)
{
- /* 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
diff --git a/mono/mini/mini-runtime.c b/mono/mini/mini-runtime.c
index 286f3ba4893..2e39e6e9a16 100644
--- a/mono/mini/mini-runtime.c
+++ b/mono/mini/mini-runtime.c
@@ -2511,6 +2511,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);
@@ -2561,6 +2563,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;
diff --git a/mono/mini/mini-runtime.h b/mono/mini/mini-runtime.h
index 6243537e5df..fb315f1d517 100644
--- a/mono/mini/mini-runtime.h
+++ b/mono/mini/mini-runtime.h
@@ -444,6 +444,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 ff39e605cc2..dc4dba3e7c8 100644
--- a/mono/mini/mini.c
+++ b/mono/mini/mini.c
@@ -3858,7 +3858,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 8f8c08a3ba9..091bdb33cff 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 023f5f187f4..af04d0dbc39 100644
--- a/mono/utils/mono-state.c
+++ b/mono/utils/mono-state.c
@@ -25,21 +25,44 @@ extern GCStats mono_gc_stats;
#include <mach/task_info.h>
#endif
-#define MONO_MAX_SUMMARY_LEN 900
+#define MONO_MAX_SUMMARY_LEN 500000
+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 memory to create crash dump json blob. Current state:\n%s\n", static_gstr.str);
+}
+
static void
mono_native_state_add_ctx (JsonWriter *writer, MonoContext *ctx)
{
@@ -48,14 +71,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));
@@ -73,45 +99,76 @@ 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);
+ 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);
+
+ 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");
- if (frame->unmanaged_data.ip)
- mono_json_writer_printf (writer, "\"%p\"", (void *) frame->unmanaged_data.ip);
- else
- mono_json_writer_printf (writer, "\"outside mono-sgen\"");
+ mono_json_writer_object_key (writer, "native_address");
+ if (frame->unmanaged_data.ip) {
+ mono_json_writer_printf (writer, "\"0x%" PRIx64 "\"", frame->unmanaged_data.ip);
+ } else
+ mono_json_writer_printf (writer, "\"unregistered\"");
+
+ if (frame->unmanaged_data.ip) {
+ mono_json_writer_printf (writer, ",\n");
+
+ assert_has_space ();
+ mono_json_writer_indent (writer);
+ mono_json_writer_object_key (writer, "native_offset");
+ mono_json_writer_printf (writer, "\"0x%05x\"", frame->unmanaged_data.offset);
+ }
+
+ if (frame->unmanaged_data.module) {
+ mono_json_writer_printf (writer, ",\n");
+
+ assert_has_space ();
+ mono_json_writer_indent (writer);
+ mono_json_writer_object_key (writer, "native_module");
+ mono_json_writer_printf (writer, "\"%s\"", frame->unmanaged_data.module);
+ }
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);
@@ -148,6 +205,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");
}
@@ -155,24 +214,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);
@@ -226,6 +297,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);
@@ -238,6 +310,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");
@@ -258,10 +331,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");
@@ -269,6 +344,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 HAVE_KW_THREAD
@@ -277,6 +353,7 @@ mono_native_state_add_version (JsonWriter *writer)
mono_json_writer_printf (writer, "\"normal\",\n");
#endif /* HAVE_KW_THREAD */
+ assert_has_space ();
mono_json_writer_indent (writer);
mono_json_writer_object_key(writer, "sigsgev");
#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
@@ -285,6 +362,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
@@ -295,14 +373,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
@@ -311,6 +392,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
@@ -319,6 +401,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)
@@ -327,6 +410,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
@@ -335,6 +419,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
@@ -345,6 +430,7 @@ mono_native_state_add_version (JsonWriter *writer)
#endif
#endif
+ assert_has_space ();
mono_json_writer_indent_pop (writer);
mono_json_writer_indent (writer);
mono_json_writer_object_end (writer);
@@ -354,6 +440,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);
@@ -365,10 +452,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);
@@ -377,22 +466,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);
@@ -406,9 +500,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);
@@ -423,6 +517,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");
@@ -434,6 +529,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);
@@ -480,9 +576,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);
}
@@ -513,7 +614,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.%d.%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 a4195c48567..582b26d7dcf 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 5c5d8d9116b..d222957a20c 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 932a8f93645..296cf2f671c 100644
--- a/mono/utils/mono-threads.h
+++ b/mono/utils/mono-threads.h
@@ -589,6 +589,9 @@ mono_native_thread_create (MonoNativeThreadId *tid, gpointer 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);