Implement thread dump using new machinery.
authorRodrigo Kumpera <kumpera@gmail.com>
Wed, 11 May 2011 20:09:55 +0000 (17:09 -0300)
committerRodrigo Kumpera <kumpera@gmail.com>
Wed, 11 May 2011 20:09:55 +0000 (17:09 -0300)
* gc.c: Thread dump now uses the finalizer thread
to be more reliable.

* threads.c (mono_threads_perform_thread_dump): New
function that dumps all threads using the new machinery.

* mini-posix.c (sigquit_signal_handler): Use new machinery
if available.

mono/metadata/gc.c
mono/metadata/threads-types.h
mono/metadata/threads.c
mono/mini/mini-posix.c

index e7b56e89476e2b2dcd13afd0fbb330d42642a663..3a0a1ecda0f14a0f56c6fbf869d4759eeb4c51eb 100644 (file)
@@ -1074,6 +1074,8 @@ finalizer_thread (gpointer unused)
                WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
 #endif
 
+               mono_threads_perform_thread_dump ();
+
                mono_console_handle_async_ops ();
 
 #ifndef DISABLE_ATTACH
index 31933fd7d887ff2b7b8e632cc132b066e2483575..b3e303b645eb2d419ca065237c1e298cc7832ccc 100644 (file)
@@ -216,5 +216,6 @@ void*    mono_get_special_static_data   (uint32_t offset) MONO_INTERNAL;
 gpointer mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset) MONO_INTERNAL;
 
 MonoException* mono_thread_resume_interruption (void) MONO_INTERNAL;
+void mono_threads_perform_thread_dump (void) MONO_INTERNAL;
 
 #endif /* _MONO_METADATA_THREADS_TYPES_H_ */
index 44c4b08a049fbdbc0f9fd89f7956276e33b41805..cb401d12931272535c73934edd1fb60780a0ec7c 100644 (file)
@@ -3052,6 +3052,109 @@ collect_threads (gpointer key, gpointer value, gpointer user_data)
        }
 }
 
+static gboolean thread_dump_requested;
+
+static G_GNUC_UNUSED gboolean
+print_stack_frame_to_string (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
+{
+       GString *p = (GString*)data;
+       MonoMethod *method = NULL;
+       if (frame->ji)
+               method = frame->ji->method;
+
+       if (method) {
+               gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain);
+               g_string_append_printf (p, "  %s\n", location);
+               g_free (location);
+       } else
+               g_string_append_printf (p, "  at <unknown> <0x%05x>\n", frame->native_offset);
+
+       return FALSE;
+}
+
+static void
+print_thread_dump (MonoInternalThread *thread, MonoThreadInfo *info)
+{
+       GString* text = g_string_new (0);
+       char *name, *wapi_desc;
+       GError *error = NULL;
+
+       if (thread->name) {
+               name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, &error);
+               g_assert (!error);
+               g_string_append_printf (text, "\n\"%s\"", name);
+               g_free (name);
+       }
+       else if (thread->threadpool_thread)
+               g_string_append (text, "\n\"<threadpool thread>\"");
+       else
+               g_string_append (text, "\n\"<unnamed thread>\"");
+
+#if 0
+/* This no longer works with remote unwinding */
+#ifndef HOST_WIN32
+       wapi_desc = wapi_current_thread_desc ();
+       g_string_append_printf (text, " tid=0x%p this=0x%p %s\n", (gpointer)(gsize)thread->tid, thread,  wapi_desc);
+       free (wapi_desc);
+#endif
+#endif
+
+       mono_get_eh_callbacks ()->mono_walk_stack_with_state (print_stack_frame_to_string, &info->suspend_state, MONO_UNWIND_SIGNAL_SAFE, text);
+       mono_thread_info_resume (mono_thread_info_get_tid (info));
+
+       fprintf (stdout, "%s", text->str);
+
+#if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG
+       OutputDebugStringA(text->str);
+#endif
+
+       g_string_free (text, TRUE);
+       fflush (stdout);
+}
+
+static void
+dump_thread (gpointer key, gpointer value, gpointer user)
+{
+       MonoInternalThread *thread = (MonoInternalThread *)value;
+       MonoThreadInfo *info;
+
+       if (thread == mono_thread_internal_current ())
+               return;
+
+       /*
+       FIXME This still can hang if we stop a thread during malloc.
+       FIXME This can hang if we suspend on a critical method and the GC kicks in. A fix might be to have function
+       that takes a callback and runs it with the target suspended.
+       We probably should loop a bit around trying to get it to either managed code
+       or WSJ state.
+       */
+       info = mono_thread_info_safe_suspend_sync ((pthread_t)(gpointer)(gsize)thread->tid, FALSE);
+
+       if (!info)
+               return;
+
+       print_thread_dump (thread, info);
+}
+
+void
+mono_threads_perform_thread_dump (void)
+{
+       if (!thread_dump_requested)
+               return;
+
+       printf ("Full thread dump:\n");
+
+       /* 
+        * Make a copy of the hashtable since we can't do anything with
+        * threads while threads_mutex is held.
+        */
+       mono_threads_lock ();
+       mono_g_hash_table_foreach (threads, dump_thread, NULL);
+       mono_threads_unlock ();
+
+       thread_dump_requested = FALSE;
+}
+
 /**
  * mono_threads_request_thread_dump:
  *
@@ -3064,6 +3167,14 @@ mono_threads_request_thread_dump (void)
        struct wait_data *wait = &wait_data;
        int i;
 
+       /*The new thread dump code runs out of the finalizer thread. */
+       if (mono_thread_info_new_interrupt_enabled ()) {
+               thread_dump_requested = TRUE;
+               mono_gc_finalize_notify ();
+               return;
+       }
+
+
        memset (wait, 0, sizeof (struct wait_data));
 
        /* 
index 7870a5c8d84f6aea6626c4012db62c9885186fbf..1da7b7efbdbda739e9fd7932ee0a59e2f2a0249b 100644 (file)
@@ -382,17 +382,21 @@ SIG_HANDLER_SIGNATURE (sigquit_signal_handler)
        if (res)
                return;
 
-       printf ("Full thread dump:\n");
+       if (mono_thread_info_new_interrupt_enabled ()) {
+               mono_threads_request_thread_dump ();
+       } else {
+               printf ("Full thread dump:\n");
 
-       mono_threads_request_thread_dump ();
+               mono_threads_request_thread_dump ();
 
-       /*
-        * print_thread_dump () skips the current thread, since sending a signal
-        * to it would invoke the signal handler below the sigquit signal handler,
-        * and signal handlers don't create an lmf, so the stack walk could not
-        * be performed.
-        */
-       mono_print_thread_dump (ctx);
+               /*
+                * print_thread_dump () skips the current thread, since sending a signal
+                * to it would invoke the signal handler below the sigquit signal handler,
+                * and signal handlers don't create an lmf, so the stack walk could not
+                * be performed.
+                */
+               mono_print_thread_dump (ctx);
+       }
 
        mono_chain_signal (SIG_HANDLER_PARAMS);
 }