Permalink
Browse files

2009-02-09 Martin Baulig <martin@ximian.com>

	**** Backport of 126360 ****

	Fix #466012 and #423518.

	* backend/server/x86-linux-ptrace.c
	(_server_ptrace_wait_for_new_thread): New static function.  Wait
	for a newly created thread to stop.

	* backend/SingleSteppingEngine.cs
	(SSE.StartThread): Added `do_execute' argument and resume the
	target when it's true.
	(SSE.OperationInitAfterFork.DoExecute): Call `sse.do_continue ()'.

	* backend/ProcessServant.cs
	(ProcessServant.ThreadCreated): If `do_attach' is false, then the
	newly created thread has already stopped, so we tell the SSE to
	resume it.

	* backend/ThreadManager.cs
	(ThreadManager.wait_thread_main): Ignore `SIGSTOP' events for
	unknown threads.


svn path=/branches/mono-2-2/debugger/; revision=126632
  • Loading branch information...
1 parent 76df64f commit d13ec0a08433bbf6651fd293b34abf699785c3d6 Martin Baulig committed Feb 11, 2009
View
@@ -1,3 +1,27 @@
+2009-02-09 Martin Baulig <martin@ximian.com>
+
+ **** Backport of 126360 ****
+
+ Fix #466012 and #423518.
+
+ * backend/server/x86-linux-ptrace.c
+ (_server_ptrace_wait_for_new_thread): New static function. Wait
+ for a newly created thread to stop.
+
+ * backend/SingleSteppingEngine.cs
+ (SSE.StartThread): Added `do_execute' argument and resume the
+ target when it's true.
+ (SSE.OperationInitAfterFork.DoExecute): Call `sse.do_continue ()'.
+
+ * backend/ProcessServant.cs
+ (ProcessServant.ThreadCreated): If `do_attach' is false, then the
+ newly created thread has already stopped, so we tell the SSE to
+ resume it.
+
+ * backend/ThreadManager.cs
+ (ThreadManager.wait_thread_main): Ignore `SIGSTOP' events for
+ unknown threads.
+
2009-01-15 Martin Baulig <martin@ximian.com>
**** Backport of 123467 ****
@@ -182,13 +182,13 @@ internal void ThreadCreated (Inferior inferior, int pid, bool do_attach)
SingleSteppingEngine new_thread = new SingleSteppingEngine (
manager, this, new_inferior, pid);
- Report.Debug (DebugFlags.Threads, "Thread created: {0} {1}", pid, new_thread);
+ Report.Debug (DebugFlags.Threads, "Thread created: {0} {1} {2}", pid, new_thread, do_attach);
// Order is important: first add the new engine to the manager's hash table,
// then call inferior.Initialize() / inferior.Attach().
manager.AddEngine (new_thread);
- new_thread.StartThread (do_attach);
+ new_thread.StartThread (do_attach, true);
if ((mono_manager != null) && !do_attach)
mono_manager.ThreadCreated (new_thread);
@@ -274,7 +274,7 @@ internal void ChildExecd (Inferior inferior)
thread_hash [inferior.PID] = new_thread;
manager.ProcessExecd (new_thread);
- new_thread.StartThread (false);
+ new_thread.StartThread (false, false);
manager.Debugger.OnProcessExecdEvent (this);
manager.Debugger.OnThreadCreatedEvent (new_thread.Thread);
@@ -114,13 +114,16 @@ internal class SingleSteppingEngine : ThreadServant
this.pid = pid;
}
- public CommandResult StartThread (bool do_attach)
+ public CommandResult StartThread (bool do_attach, bool do_execute)
{
CommandResult result = new ThreadCommandResult (thread);
if (do_attach)
current_operation = new OperationInitialize (this, result);
- else
+ else {
current_operation = new OperationRun (this, result);
+ if (do_execute)
+ current_operation.Execute ();
+ }
return result;
}
@@ -2637,7 +2640,9 @@ public OperationInitAfterFork (SingleSteppingEngine sse)
}
protected override void DoExecute ()
- { }
+ {
+ sse.do_continue ();
+ }
protected override EventResult DoProcessEvent (Inferior.ChildEvent cevent,
out TargetEventArgs args)
View
@@ -74,8 +74,6 @@ internal ThreadManager (DebuggerServant backend)
bool abort_requested;
bool waiting;
- Queue<PendingEventInfo> pending_sigstop;
-
[DllImport("monodebuggerserver")]
static extern int mono_debugger_server_global_init ();
@@ -441,6 +439,9 @@ void start_wait_thread ()
Report.Debug (DebugFlags.Threads, "Wait thread abort: {0}",
DebuggerWaitHandle.CurrentThread);
ST.Thread.ResetAbort ();
+ } catch (Exception ex) {
+ Report.Debug (DebugFlags.Threads, "FUCK: {0}", ex);
+ throw;
}
Report.Debug (DebugFlags.Threads, "Wait thread exiting: {0}",
@@ -454,6 +455,8 @@ bool wait_thread_main ()
waiting = true;
again:
+ Report.Debug (DebugFlags.Wait, "Wait thread again");
+
int pid = 0, status = 0;
if (abort_requested) {
Report.Debug (DebugFlags.Wait,
@@ -473,55 +476,17 @@ bool wait_thread_main ()
return false;
}
- bool has_pending_sigstop = false;
- check_pending_sigstop:
- if ((pending_sigstop != null) && (pending_sigstop.Count > 0)) {
- PendingEventInfo pending = pending_sigstop.Peek ();
-
- /*
- * There is a race condition in the Linux kernel which shows up on >= 2.6.27:
- *
- * When creating a new thread, the initial stopping event of that thread is sometimes
- * sent before sending the `PTRACE_EVENT_CLONE' for it.
- *
- * Here, we check whether we already received the thread creation event and process the
- * pending stopping event if necessary.
- *
- * Since we're just trying to catch a race condition, discard all pending events which
- * were received more than 200 seconds ago.
- *
- */
-
- if (thread_hash.Contains (pending.PID)) {
- pid = pending.PID;
- status = pending.Status;
-
- has_pending_sigstop = true;
- pending_sigstop.Dequeue ();
- } else if ((DateTime.Now - pending.TimeStamp).TotalSeconds >= 200) {
- Report.Error ("WARNING: Got stop event for unknown pid {0}; timed-out waiting for thread creation.",
- pending.PID);
- pending_sigstop.Dequeue ();
- goto check_pending_sigstop;
- }
- }
-
- if (!has_pending_sigstop) {
- Report.Debug (DebugFlags.Wait, "Wait thread waiting");
+ Report.Debug (DebugFlags.Wait, "Wait thread waiting");
- //
- // Wait until we got an event from the target or a command from the user.
- //
+ //
+ // Wait until we got an event from the target or a command from the user.
+ //
- pid = mono_debugger_server_global_wait (out status);
+ pid = mono_debugger_server_global_wait (out status);
- Report.Debug (DebugFlags.Wait,
- "Wait thread received event: {0} {1:x}",
- pid, status);
- } else {
- Report.Debug (DebugFlags.Wait,
- "Wait thread processing pending sigstop: {0} {1:x}", pid, status);
- }
+ Report.Debug (DebugFlags.Wait,
+ "Wait thread received event: {0} {1:x}",
+ pid, status);
//
// Note: `pid' is basically just an unique number which identifies the
@@ -542,8 +507,11 @@ bool wait_thread_main ()
* When creating a new thread, the initial stopping event of that thread is sometimes
* sent before sending the `PTRACE_EVENT_CLONE' for it.
*
- * Whenever we get an event for an unknown thread, we check whether it's actually a
- * SIGSTOP and then queue it. *
+ * Because of this, we explicitly wait for the new thread to stop and ignore any
+ * "early" stopping signals.
+ *
+ * See also the comments in _server_ptrace_wait_for_new_thread() in x86-linux-ptrace.c
+ * and bugs #423518 and #466012.
*
*/
@@ -554,11 +522,7 @@ bool wait_thread_main ()
return true;
}
- if (pending_sigstop == null)
- pending_sigstop = new Queue<PendingEventInfo> ();
-
- pending_sigstop.Enqueue (new PendingEventInfo (pid, status));
- Report.Debug (DebugFlags.Wait, "Got SIGSTOP for unknown pid {0}, queueing event.", pid);
+ Report.Debug (DebugFlags.Wait, "Ignoring SIGSTOP from unknown pid {0}.", pid);
goto again;
}
@@ -586,6 +550,7 @@ private void RequestWait ()
{
if (waiting)
throw new InternalError ();
+ Report.Debug (DebugFlags.Wait, "Signalling wait event");
wait_event.Set ();
}
@@ -194,6 +194,70 @@ server_ptrace_global_wait (guint32 *status_ret)
return ret;
}
+static gboolean
+_server_ptrace_wait_for_new_thread (ServerHandle *handle)
+{
+ guint32 ret, status = 0;
+
+ /*
+ * There is a race condition in the Linux kernel which shows up on >= 2.6.27:
+ *
+ * When creating a new thread, the initial stopping event of that thread is sometimes
+ * sent before sending the `PTRACE_EVENT_CLONE' for it.
+ *
+ * Because of this, we wait here until the new thread has been stopped and ignore
+ * any "early" stopping events.
+ *
+ * See also bugs #423518 and #466012.
+. *
+ */
+
+ if (!g_static_mutex_trylock (&wait_mutex)) {
+ /* This should never happen, but let's not deadlock here. */
+ g_warning (G_STRLOC ": Can't lock mutex: %d", handle->inferior->pid);
+ return FALSE;
+ }
+
+ /*
+ * If the call succeeds, then we're already stopped.
+ */
+
+ if (x86_arch_get_registers (handle) == COMMAND_ERROR_NONE) {
+ g_static_mutex_unlock (&wait_mutex);
+ return TRUE;
+ }
+
+ /*
+ * We own the `wait_mutex', so no other thread is currently waiting for the target
+ * and we can safely wait for it here.
+ */
+
+ ret = waitpid (handle->inferior->pid, &status, WUNTRACED | __WALL | __WCLONE);
+
+ /*
+ * Safety check: make sure we got the correct event.
+ */
+
+ if ((ret != handle->inferior->pid) || !WIFSTOPPED (status) || (WSTOPSIG (status) != SIGSTOP)) {
+ g_warning (G_STRLOC ": Wait failed: %d", handle->inferior->pid);
+ g_static_mutex_unlock (&wait_mutex);
+ return FALSE;
+ }
+
+ /*
+ * Just as an extra safety check.
+ */
+
+ if (x86_arch_get_registers (handle) != COMMAND_ERROR_NONE) {
+ g_static_mutex_unlock (&wait_mutex);
+ g_warning (G_STRLOC ": Failed to get registers: %d", handle->inferior->pid);
+ return FALSE;
+ }
+
+ g_static_mutex_unlock (&wait_mutex);
+ return TRUE;
+}
+
static ServerCommandError
server_ptrace_stop (ServerHandle *handle)
{
@@ -46,6 +46,9 @@ _server_ptrace_finalize_inferior (ServerHandle *handle);
static ServerCommandError
server_ptrace_get_signal_info (ServerHandle *handle, SignalInfo **sinfo);
+static gboolean
+_server_ptrace_wait_for_new_thread (ServerHandle *handle);
+
#include "x86-ptrace.h"
#endif
@@ -509,6 +509,9 @@ server_ptrace_initialize_thread (ServerHandle *handle, guint32 pid)
inferior->pid = pid;
inferior->is_thread = TRUE;
+ if (!_server_ptrace_wait_for_new_thread (handle))
+ return COMMAND_ERROR_INTERNAL_ERROR;
+
return _server_ptrace_setup_inferior (handle);
}

0 comments on commit d13ec0a

Please sign in to comment.