[Haiku-commits] r31194 - in haiku/trunk: headers/private/kernel src/system/kernel src/system/kernel/debug

bonefish at mail.berlios.de bonefish at mail.berlios.de
Tue Jun 23 03:39:53 CEST 2009


Author: bonefish
Date: 2009-06-23 03:39:51 +0200 (Tue, 23 Jun 2009)
New Revision: 31194
ViewCVS: http://svn.berlios.de/viewcvs/haiku?rev=31194&view=rev

Modified:
   haiku/trunk/headers/private/kernel/user_debugger.h
   haiku/trunk/src/system/kernel/debug/user_debugger.cpp
   haiku/trunk/src/system/kernel/thread.cpp
Log:
Added team_debug_info::debugger_changed_condition to serialize changes to the
installed team debugger and adjusted the code accordingly. It's not needed yet,
but I intend to add support for software breakpoints and those require a bit of
uninitialization that needs to be synchronized with debugger changes and can't
be done with interrupts disabled.


Modified: haiku/trunk/headers/private/kernel/user_debugger.h
===================================================================
--- haiku/trunk/headers/private/kernel/user_debugger.h	2009-06-23 01:32:46 UTC (rev 31193)
+++ haiku/trunk/headers/private/kernel/user_debugger.h	2009-06-23 01:39:51 UTC (rev 31194)
@@ -20,6 +20,7 @@
 #define	B_DEBUG_PROFILE_BUFFER_FLUSH_THRESHOLD	70			/* in % */
 
 
+struct ConditionVariable;
 struct function_profile_info;
 struct thread;
 
@@ -61,6 +62,14 @@
 	vint32		image_event;
 		// counter incremented whenever an image is created/deleted
 
+	struct ConditionVariable* debugger_changed_condition;
+		// Set whenever someone is going (or planning) to change the debugger.
+		// If one wants to do the same, one has to wait for this condition.
+		// Both threads lock (outer) and team debug info lock (inner) have to
+		// be held when accessing this field. After setting to a condition
+		// variable the thread won't be deleted (until unsetting it) -- it might
+		// be removed from the team hash table, though.
+
 	struct arch_team_debug_info	arch_info;
 };
 

Modified: haiku/trunk/src/system/kernel/debug/user_debugger.cpp
===================================================================
--- haiku/trunk/src/system/kernel/debug/user_debugger.cpp	2009-06-23 01:32:46 UTC (rev 31193)
+++ haiku/trunk/src/system/kernel/debug/user_debugger.cpp	2009-06-23 01:39:51 UTC (rev 31194)
@@ -223,8 +223,10 @@
 		info->causing_thread = -1;
 		info->image_event = 0;
 
-		if (initLock)
+		if (initLock) {
 			B_INITIALIZE_SPINLOCK(&info->lock);
+			info->debugger_changed_condition = NULL;
+		}
 	}
 }
 
@@ -348,6 +350,90 @@
 }
 
 
+static status_t
+prepare_debugger_change(team_id teamID, ConditionVariable& condition,
+	struct team*& team)
+{
+	// We look up the team by ID, even in case of the current team, so we can be
+	// sure, that the team is not already dying.
+	if (teamID == B_CURRENT_TEAM)
+		teamID = thread_get_current_thread()->team->id;
+
+	while (true) {
+		// get the team
+		InterruptsSpinLocker teamLocker(gTeamSpinlock);
+
+		team = team_get_team_struct_locked(teamID);
+		if (team == NULL)
+			return B_BAD_TEAM_ID;
+
+		// don't allow messing with the kernel team
+		if (team == team_get_kernel_team())
+			return B_NOT_ALLOWED;
+
+		// check whether the condition is already set
+		SpinLocker threadLocker(gThreadSpinlock);
+		SpinLocker debugInfoLocker(team->debug_info.lock);
+
+		if (team->debug_info.debugger_changed_condition == NULL) {
+			// nobody there yet -- set our condition variable and be done
+			team->debug_info.debugger_changed_condition = &condition;
+			return B_OK;
+		}
+
+		// we'll have to wait
+		ConditionVariableEntry entry;
+		team->debug_info.debugger_changed_condition->Add(&entry);
+
+		debugInfoLocker.Unlock();
+		threadLocker.Unlock();
+		teamLocker.Unlock();
+
+		entry.Wait();
+	}
+}
+
+
+static void
+prepare_debugger_change(struct team* team, ConditionVariable& condition)
+{
+	while (true) {
+		// check whether the condition is already set
+		InterruptsSpinLocker threadLocker(gThreadSpinlock);
+		SpinLocker debugInfoLocker(team->debug_info.lock);
+
+		if (team->debug_info.debugger_changed_condition == NULL) {
+			// nobody there yet -- set our condition variable and be done
+			team->debug_info.debugger_changed_condition = &condition;
+			return;
+		}
+
+		// we'll have to wait
+		ConditionVariableEntry entry;
+		team->debug_info.debugger_changed_condition->Add(&entry);
+
+		debugInfoLocker.Unlock();
+		threadLocker.Unlock();
+
+		entry.Wait();
+	}
+}
+
+
+static void
+finish_debugger_change(struct team* team)
+{
+	// unset our condition variable and notify all threads waiting on it
+	InterruptsSpinLocker threadLocker(gThreadSpinlock);
+	SpinLocker debugInfoLocker(team->debug_info.lock);
+
+	ConditionVariable* condition = team->debug_info.debugger_changed_condition;
+	team->debug_info.debugger_changed_condition = NULL;
+
+	condition->NotifyAll(true);
+}
+
+
 void
 user_debug_prepare_for_exec()
 {
@@ -1406,11 +1492,13 @@
 	TRACE(("nub_thread_cleanup(%ld): debugger port: %ld\n", nubThread->id,
 		nubThread->team->debug_info.debugger_port));
 
+	ConditionVariable debugChangeCondition;
+	prepare_debugger_change(nubThread->team, debugChangeCondition);
+
 	team_debug_info teamDebugInfo;
 	bool destroyDebugInfo = false;
 
 	cpu_status state = disable_interrupts();
-	GRAB_TEAM_LOCK();
 	GRAB_TEAM_DEBUG_INFO_LOCK(nubThread->team->debug_info);
 
 	team_debug_info &info = nubThread->team->debug_info;
@@ -1425,9 +1513,10 @@
 	update_threads_debugger_installed_flag(nubThread->team);
 
 	RELEASE_TEAM_DEBUG_INFO_LOCK(nubThread->team->debug_info);
-	RELEASE_TEAM_LOCK();
 	restore_interrupts(state);
 
+	finish_debugger_change(nubThread->team);
+
 	if (destroyDebugInfo)
 		destroy_team_debug_info(&teamDebugInfo);
 
@@ -2526,14 +2615,24 @@
 	}
 	team_id debuggerTeam = debuggerPortInfo.team;
 
-	// check the debugger team: It must neither be the kernel team nor the
-	// debugged team
+	// Check the debugger team: It must neither be the kernel team nor the
+	// debugged team.
 	if (debuggerTeam == team_get_kernel_team_id() || debuggerTeam == teamID) {
 		TRACE(("install_team_debugger(): Can't debug kernel or debugger team. "
 			"debugger: %ld, debugged: %ld\n", debuggerTeam, teamID));
 		return B_NOT_ALLOWED;
 	}
 
+	// get the team
+	struct team* team;
+	ConditionVariable debugChangeCondition;
+	error = prepare_debugger_change(teamID, debugChangeCondition, team);
+	if (error != B_OK)
+		return error;
+
+	// get the real team ID
+	teamID = team->id;
+
 	// check, if a debugger is already installed
 
 	bool done = false;
@@ -2544,90 +2643,73 @@
 	port_id nubPort = -1;
 
 	cpu_status state = disable_interrupts();
-	GRAB_TEAM_LOCK();
+	GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
 
-	// get a real team ID
-	// We look up the team by ID, even in case of the current team, so we can be
-	// sure, that the team is not already dying.
-	if (teamID == B_CURRENT_TEAM)
-		teamID = thread_get_current_thread()->team->id;
+	int32 teamDebugFlags = team->debug_info.flags;
 
-	struct team *team = team_get_team_struct_locked(teamID);
+	if (teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
+		// There's already a debugger installed.
+		if (teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_HANDOVER) {
+			if (dontReplace) {
+				// We're fine with already having a debugger.
+				error = B_OK;
+				done = true;
+				result = team->debug_info.nub_port;
+			} else if (
+				teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_HANDING_OVER) {
+				// Another debugger is in the process of installing itself
+				// as the team's debugger.
+				error = (dontReplace ? B_OK : B_BAD_VALUE);
+				done = true;
+				result = team->debug_info.nub_port;
+			} else {
+				// a handover to another debugger is requested
+				// Set the handing-over flag -- we'll clear both flags after
+				// having sent the handed-over message to the new debugger.
+				atomic_or(&team->debug_info.flags,
+					B_TEAM_DEBUG_DEBUGGER_HANDING_OVER);
 
-	if (team) {
-		GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
+				oldDebuggerPort = team->debug_info.debugger_port;
+				result = nubPort = team->debug_info.nub_port;
+				if (causingThread < 0)
+					causingThread = team->debug_info.causing_thread;
 
-		int32 teamDebugFlags = team->debug_info.flags;
+				// set the new debugger
+				install_team_debugger_init_debug_infos(team, debuggerTeam,
+					debuggerPort, nubPort, team->debug_info.nub_thread,
+					team->debug_info.debugger_write_lock, causingThread);
 
-		if (team == team_get_kernel_team()) {
-			// don't allow to debug the kernel
-			error = B_NOT_ALLOWED;
-		} else if (teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
-			// There's already a debugger installed.
-			if (teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_HANDOVER) {
-				if (dontReplace) {
-					// We're fine with already having a debugger.
-					error = B_OK;
-					done = true;
-					result = team->debug_info.nub_port;
-				} else if (
-					teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_HANDING_OVER) {
-					// Another debugger is in the process of installing itself
-					// as the team's debugger.
-					error = (dontReplace ? B_OK : B_BAD_VALUE);
-					done = true;
-					result = team->debug_info.nub_port;
-				} else {
-					// a handover to another debugger is requested
-					// Set the handing-over flag -- we'll clear both flags after
-					// having sent the handed-over message to the new debugger.
-					atomic_or(&team->debug_info.flags,
-						B_TEAM_DEBUG_DEBUGGER_HANDING_OVER);
+				releaseDebugInfoLock = false;
+				handOver = true;
+				done = true;
 
-					oldDebuggerPort = team->debug_info.debugger_port;
-					result = nubPort = team->debug_info.nub_port;
-					if (causingThread < 0)
-						causingThread = team->debug_info.causing_thread;
-
-					// set the new debugger
-					install_team_debugger_init_debug_infos(team, debuggerTeam,
-						debuggerPort, nubPort, team->debug_info.nub_thread,
-						team->debug_info.debugger_write_lock, causingThread);
-
-					releaseDebugInfoLock = false;
-					handOver = true;
-					done = true;
-
-					// finally set the new port owner
-					if (set_port_owner(nubPort, debuggerTeam) != B_OK) {
-						// The old debugger must just have died. Just proceed as
-						// if there was no debugger installed. We may still be too
-						// early, in which case we'll fail, but this race condition
-						// should be unbelievably rare and relatively harmless.
-						handOver = false;
-						done = false;
-					}
+				// finally set the new port owner
+				if (set_port_owner(nubPort, debuggerTeam) != B_OK) {
+					// The old debugger must just have died. Just proceed as
+					// if there was no debugger installed. We may still be too
+					// early, in which case we'll fail, but this race condition
+					// should be unbelievably rare and relatively harmless.
+					handOver = false;
+					done = false;
 				}
-			} else {
-				// there's already a debugger installed
-				error = (dontReplace ? B_OK : B_BAD_VALUE);
-				done = true;
-				result = team->debug_info.nub_port;
 			}
-		} else if ((teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_DISABLED) != 0
-			&& useDefault) {
-			// No debugger yet, disable_debugger() had been invoked, and we
-			// would install the default debugger. Just fail.
-			error = B_BAD_VALUE;
+		} else {
+			// there's already a debugger installed
+			error = (dontReplace ? B_OK : B_BAD_VALUE);
+			done = true;
+			result = team->debug_info.nub_port;
 		}
+	} else if ((teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_DISABLED) != 0
+		&& useDefault) {
+		// No debugger yet, disable_debugger() had been invoked, and we
+		// would install the default debugger. Just fail.
+		error = B_BAD_VALUE;
+	}
 
-		// in case of a handover the lock has already been released
-		if (releaseDebugInfoLock)
-			RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
-	} else
-		error = B_BAD_TEAM_ID;
+	// in case of a handover the lock has already been released
+	if (releaseDebugInfoLock)
+		RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
 
-	RELEASE_TEAM_LOCK();
 	restore_interrupts(state);
 
 	if (handOver) {
@@ -2650,31 +2732,25 @@
 		}
 
 		// clear the handed-over and handing-over flags
-		cpu_status state = disable_interrupts();
-		GRAB_TEAM_LOCK();
+		state = disable_interrupts();
+		GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
 
-		team = team_get_team_struct_locked(teamID);
+		int32 teamDebugFlags = team->debug_info.flags;
 
-		if (team) {
-			GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
-
-			int32 teamDebugFlags = team->debug_info.flags;
-
-			if ((teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) != 0
-				&& (teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_HANDING_OVER) != 0
-				&& team->debug_info.debugger_port == debuggerPort) {
-				// Everything is as we left it above, so just clear the flags.
-				atomic_and(&team->debug_info.flags,
-					~(B_TEAM_DEBUG_DEBUGGER_HANDOVER
-						| B_TEAM_DEBUG_DEBUGGER_HANDING_OVER));
-			}
-
-			RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
+		if ((teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) != 0
+			&& (teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_HANDING_OVER) != 0
+			&& team->debug_info.debugger_port == debuggerPort) {
+			// Everything is as we left it above, so just clear the flags.
+			atomic_and(&team->debug_info.flags,
+				~(B_TEAM_DEBUG_DEBUGGER_HANDOVER
+					| B_TEAM_DEBUG_DEBUGGER_HANDING_OVER));
 		}
 
-		RELEASE_TEAM_LOCK();
+		RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
 		restore_interrupts(state);
 
+		finish_debugger_change(team);
+
 		// notify the nub thread
 		kill_interruptable_write_port(nubPort, B_DEBUG_MESSAGE_HANDED_OVER,
 			NULL, 0);
@@ -2697,6 +2773,7 @@
 	if (done || error != B_OK) {
 		TRACE(("install_team_debugger() done1: %ld\n",
 			(error == B_OK ? result : error)));
+		finish_debugger_change(team);
 		return (error == B_OK ? result : error);
 	}
 
@@ -2736,33 +2813,26 @@
 	// now adjust the debug info accordingly
 	if (error == B_OK) {
 		state = disable_interrupts();
-		GRAB_TEAM_LOCK();
+		GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
 
-		// look up again, to make sure the team isn't dying
-		team = team_get_team_struct_locked(teamID);
+		if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
+			// there's already a debugger installed
+			error = (dontReplace ? B_OK : B_BAD_VALUE);
+			done = true;
+			result = team->debug_info.nub_port;
 
-		if (team) {
-			GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
+			RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
+		} else {
+			install_team_debugger_init_debug_infos(team, debuggerTeam,
+				debuggerPort, nubPort, nubThread, debuggerWriteLock,
+				causingThread);
+		}
 
-			if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
-				// there's already a debugger installed
-				error = (dontReplace ? B_OK : B_BAD_VALUE);
-				done = true;
-				result = team->debug_info.nub_port;
-
-				RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
-			} else {
-				install_team_debugger_init_debug_infos(team, debuggerTeam,
-					debuggerPort, nubPort, nubThread, debuggerWriteLock,
-					causingThread);
-			}
-		} else
-			error = B_BAD_TEAM_ID;
-
-		RELEASE_TEAM_LOCK();
 		restore_interrupts(state);
 	}
 
+	finish_debugger_change(team);
+
 	// if everything went fine, resume the nub thread, otherwise clean up
 	if (error == B_OK && !done) {
 		resume_thread(nubThread);
@@ -2882,36 +2952,29 @@
 status_t
 _user_remove_team_debugger(team_id teamID)
 {
-	struct team_debug_info info;
+	struct team* team;
+	ConditionVariable debugChangeCondition;
+	status_t error = prepare_debugger_change(teamID, debugChangeCondition,
+		team);
+	if (error != B_OK)
+		return error;
 
-	status_t error = B_OK;
+	InterruptsSpinLocker debugInfoLocker(team->debug_info.lock);
 
-	cpu_status state = disable_interrupts();
-	GRAB_TEAM_LOCK();
+	struct team_debug_info info;
+	if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
+		// there's a debugger installed
+		info = team->debug_info;
+		clear_team_debug_info(&team->debug_info, false);
+	} else {
+		// no debugger installed
+		error = B_BAD_VALUE;
+	}
 
-	struct team *team = (teamID == B_CURRENT_TEAM
-		? thread_get_current_thread()->team
-		: team_get_team_struct_locked(teamID));
+	debugInfoLocker.Unlock();
 
-	if (team) {
-		GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
+	finish_debugger_change(team);
 
-		if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
-			// there's a debugger installed
-			info = team->debug_info;
-			clear_team_debug_info(&team->debug_info, false);
-		} else {
-			// no debugger installed
-			error = B_BAD_VALUE;
-		}
-
-		RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
-	} else
-		error = B_BAD_TEAM_ID;
-
-	RELEASE_TEAM_LOCK();
-	restore_interrupts(state);
-
 	// clean up the info, if there was a debugger installed
 	if (error == B_OK)
 		destroy_team_debug_info(&info);

Modified: haiku/trunk/src/system/kernel/thread.cpp
===================================================================
--- haiku/trunk/src/system/kernel/thread.cpp	2009-06-23 01:32:46 UTC (rev 31193)
+++ haiku/trunk/src/system/kernel/thread.cpp	2009-06-23 01:39:51 UTC (rev 31194)
@@ -1400,6 +1400,8 @@
 
 	struct job_control_entry *death = NULL;
 	struct death_entry* threadDeathEntry = NULL;
+	ConditionVariableEntry waitForDebuggerEntry;
+	bool waitForDebugger = false;
 
 	if (team != team_get_kernel_team()) {
 		user_debug_thread_exiting(thread);
@@ -1430,6 +1432,16 @@
 		cachedDeathSem = team->death_sem;
 
 		if (deleteTeam) {
+			// If a debugger change is in progess for the team, we'll have to
+			// wait until it is done later.
+			GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
+			if (team->debug_info.debugger_changed_condition != NULL) {
+				team->debug_info.debugger_changed_condition->Add(
+					&waitForDebuggerEntry);
+				waitForDebugger = true;
+			}
+			RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
+
 			struct team *parent = team->parent;
 
 			// remember who our parent was so we can send a signal
@@ -1500,6 +1512,10 @@
 
 	// delete the team if we're its main thread
 	if (deleteTeam) {
+		// wait for a debugger change to be finished first
+		if (waitForDebugger)
+			waitForDebuggerEntry.Wait();
+
 		team_delete_team(team);
 
 		// we need to delete any death entry that made it to here




More information about the Haiku-commits mailing list