xref: /haiku/src/kits/debug/DebugLooper.cpp (revision 53446ebf805fa4851775128a7dbaf12692d005ad)
1 /*
2  * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <DebugLooper.h>
8 
9 #include <new>
10 
11 #include <AutoLocker.h>
12 #include <DebugMessageHandler.h>
13 #include <TeamDebugger.h>
14 #include <util/DoublyLinkedList.h>
15 
16 
17 struct BDebugLooper::Debugger {
18 	BTeamDebugger*			debugger;
19 	BDebugMessageHandler*	handler;
20 
DebuggerBDebugLooper::Debugger21 	Debugger(BTeamDebugger* debugger, BDebugMessageHandler* handler)
22 		:
23 		debugger(debugger),
24 		handler(handler)
25 	{
26 	}
27 };
28 
29 
30 struct BDebugLooper::Job : DoublyLinkedListLinkImpl<Job> {
JobBDebugLooper::Job31 	Job()
32 		:
33 		fDoneSemaphore(-1)
34 	{
35 	}
36 
~JobBDebugLooper::Job37 	virtual ~Job()
38 	{
39 	}
40 
WaitBDebugLooper::Job41 	status_t Wait(BLocker& lock)
42 	{
43 		fDoneSemaphore = create_sem(0, "debug looper job");
44 
45 		lock.Unlock();
46 
47 		while (acquire_sem(fDoneSemaphore) == B_INTERRUPTED) {
48 		}
49 
50 		lock.Lock();
51 
52 		delete_sem(fDoneSemaphore);
53 		fDoneSemaphore = -1;
54 
55 		return fResult;
56 	}
57 
DoneBDebugLooper::Job58 	void Done(status_t result)
59 	{
60 		fResult = result;
61 		release_sem(fDoneSemaphore);
62 	}
63 
64 	virtual status_t Do(BDebugLooper* looper) = 0;
65 
66 protected:
67 	sem_id			fDoneSemaphore;
68 	status_t		fResult;
69 };
70 
71 
72 struct BDebugLooper::JobList : DoublyLinkedList<Job> {
73 };
74 
75 
76 struct BDebugLooper::AddDebuggerJob : Job {
AddDebuggerJobBDebugLooper::AddDebuggerJob77 	AddDebuggerJob(BTeamDebugger* debugger,
78 		BDebugMessageHandler* handler)
79 		:
80 		fDebugger(debugger),
81 		fHandler(handler)
82 	{
83 	}
84 
DoBDebugLooper::AddDebuggerJob85 	virtual status_t Do(BDebugLooper* looper)
86 	{
87 		Debugger* debugger = new(std::nothrow) Debugger(fDebugger, fHandler);
88 		if (debugger == NULL || !looper->fDebuggers.AddItem(debugger)) {
89 			delete debugger;
90 			return B_NO_MEMORY;
91 		}
92 
93 		return B_OK;
94 	}
95 
96 private:
97 	BTeamDebugger*			fDebugger;
98 	BDebugMessageHandler*	fHandler;
99 };
100 
101 
102 struct BDebugLooper::RemoveDebuggerJob : Job {
RemoveDebuggerJobBDebugLooper::RemoveDebuggerJob103 	RemoveDebuggerJob(team_id team)
104 		:
105 		fTeam(team)
106 	{
107 	}
108 
DoBDebugLooper::RemoveDebuggerJob109 	virtual status_t Do(BDebugLooper* looper)
110 	{
111 		for (int32 i = 0; Debugger* debugger = looper->fDebuggers.ItemAt(i);
112 				i++) {
113 			if (debugger->debugger->Team() == fTeam) {
114 				delete looper->fDebuggers.RemoveItemAt(i);
115 				return B_OK;
116 			}
117 		}
118 
119 		return B_ENTRY_NOT_FOUND;
120 	}
121 
122 private:
123 	team_id	fTeam;
124 };
125 
126 
127 // #pragma mark -
128 
129 
BDebugLooper()130 BDebugLooper::BDebugLooper()
131 	:
132 	fLock("debug looper"),
133 	fThread(-1),
134 	fOwnsThread(false),
135 	fTerminating(false),
136 	fNotified(false),
137 	fJobs(NULL),
138 	fEventSemaphore(-1)
139 {
140 }
141 
142 
~BDebugLooper()143 BDebugLooper::~BDebugLooper()
144 {
145 }
146 
147 
148 status_t
Init()149 BDebugLooper::Init()
150 {
151 	status_t error = fLock.InitCheck();
152 	if (error != B_OK)
153 		return error;
154 
155 	AutoLocker<BLocker> locker(fLock);
156 
157 	if (fThread >= 0)
158 		return B_BAD_VALUE;
159 
160 	if (fJobs == NULL) {
161 		fJobs = new(std::nothrow) JobList;
162 		if (fJobs == NULL)
163 			return B_NO_MEMORY;
164 	}
165 
166 	if (fEventSemaphore < 0) {
167 		fEventSemaphore = create_sem(0, "debug looper event");
168 		if (fEventSemaphore < 0)
169 			return fEventSemaphore;
170 	}
171 
172 	return B_OK;
173 }
174 
175 
176 thread_id
Run(bool spawnThread)177 BDebugLooper::Run(bool spawnThread)
178 {
179 	AutoLocker<BLocker> locker(fLock);
180 
181 	if (fThread >= 0)
182 		return B_BAD_VALUE;
183 
184 	fNotified = false;
185 
186 	if (spawnThread) {
187 		fThread = spawn_thread(&_MessageLoopEntry, "debug looper",
188 			B_NORMAL_PRIORITY, this);
189 		if (fThread < 0)
190 			return fThread;
191 
192 		fOwnsThread = true;
193 
194 		resume_thread(fThread);
195 		return B_OK;
196 	}
197 
198 	fThread = find_thread(NULL);
199 	fOwnsThread = false;
200 
201 	_MessageLoop();
202 	return B_OK;
203 }
204 
205 
206 void
Quit()207 BDebugLooper::Quit()
208 {
209 	AutoLocker<BLocker> locker(fLock);
210 
211 	fTerminating = true;
212 	_Notify();
213 }
214 
215 
216 status_t
AddTeamDebugger(BTeamDebugger * debugger,BDebugMessageHandler * handler)217 BDebugLooper::AddTeamDebugger(BTeamDebugger* debugger,
218 	BDebugMessageHandler* handler)
219 {
220 	if (debugger == NULL || handler == NULL)
221 		return B_BAD_VALUE;
222 
223 	AddDebuggerJob job(debugger, handler);
224 	return _DoJob(&job);
225 }
226 
227 
228 bool
RemoveTeamDebugger(BTeamDebugger * debugger)229 BDebugLooper::RemoveTeamDebugger(BTeamDebugger* debugger)
230 {
231 	if (debugger == NULL)
232 		return false;
233 
234 	RemoveDebuggerJob job(debugger->Team());
235 	return _DoJob(&job) == B_OK;
236 }
237 
238 
239 bool
RemoveTeamDebugger(team_id team)240 BDebugLooper::RemoveTeamDebugger(team_id team)
241 {
242 	if (team < 0)
243 		return false;
244 
245 	RemoveDebuggerJob job(team);
246 	return _DoJob(&job) == B_OK;
247 }
248 
249 
250 /*static*/ status_t
_MessageLoopEntry(void * data)251 BDebugLooper::_MessageLoopEntry(void* data)
252 {
253 	return ((BDebugLooper*)data)->_MessageLoop();
254 }
255 
256 
257 status_t
_MessageLoop()258 BDebugLooper::_MessageLoop()
259 {
260 	while (true) {
261 		// prepare the wait info array
262 		int32 debuggerCount = fDebuggers.CountItems();
263 		object_wait_info waitInfos[debuggerCount + 1];
264 
265 		for (int32 i = 0; i < debuggerCount; i++) {
266 			waitInfos[i].object
267 				= fDebuggers.ItemAt(i)->debugger->DebuggerPort();
268 			waitInfos[i].type = B_OBJECT_TYPE_PORT;
269 			waitInfos[i].events = B_EVENT_READ;
270 		}
271 
272 		waitInfos[debuggerCount].object = fEventSemaphore;
273 		waitInfos[debuggerCount].type = B_OBJECT_TYPE_SEMAPHORE;
274 		waitInfos[debuggerCount].events = B_EVENT_ACQUIRE_SEMAPHORE;
275 
276 		// wait for the next event
277 		wait_for_objects(waitInfos, debuggerCount + 1);
278 
279 		AutoLocker<BLocker> locker(fLock);
280 
281 		// handle all pending jobs
282 		bool handledJobs = fJobs->Head() != NULL;
283 		while (Job* job = fJobs->RemoveHead())
284 			job->Done(job->Do(this));
285 
286 		// acquire notification semaphore and mark unnotified
287 		if ((waitInfos[debuggerCount].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0)
288 			acquire_sem(fEventSemaphore);
289 		fNotified = false;
290 
291 		if (fTerminating)
292 			return B_OK;
293 
294 		// Always loop when jobs were executed, since that might add/remove
295 		// debuggers.
296 		if (handledJobs)
297 			continue;
298 
299 		// read a pending port message
300 		for (int32 i = 0; i < debuggerCount; i++) {
301 			if ((waitInfos[i].events & B_EVENT_READ) != 0) {
302 				Debugger* debugger = fDebuggers.ItemAt(i);
303 
304 				// read the message
305 				debug_debugger_message_data message;
306 				int32 code;
307 				ssize_t messageSize = read_port(
308 					debugger->debugger->DebuggerPort(), &code, &message,
309 					sizeof(message));
310 				if (messageSize < 0)
311 					continue;
312 
313 				// handle the message
314 				bool continueThread = debugger->handler->HandleDebugMessage(
315 					code, message);
316 
317 				// If requested, tell the thread to continue (only when there
318 				// is a thread and the message was synchronous).
319 				if (continueThread && message.origin.thread >= 0
320 						&& message.origin.nub_port >= 0) {
321 					debugger->debugger->ContinueThread(message.origin.thread);
322 				}
323 
324 				// Handle only one message -- the hook might have added/removed
325 				// debuggers which makes further iteration problematic.
326 				break;
327 			}
328 		}
329 	}
330 }
331 
332 
333 status_t
_DoJob(Job * job)334 BDebugLooper::_DoJob(Job* job)
335 {
336 	AutoLocker<BLocker> locker(fLock);
337 
338 	// execute directly, if in looper thread or not running yet
339 	if (fThread < 0 || fThread == find_thread(NULL))
340 		return job->Do(this);
341 
342 	// execute in the looper thread
343 	fJobs->Add(job);
344 	_Notify();
345 
346 	return job->Wait(fLock);
347 }
348 
349 
350 void
_Notify()351 BDebugLooper::_Notify()
352 {
353 	if (fNotified)
354 		return;
355 
356 	fNotified = true;
357 	release_sem(fEventSemaphore);
358 }
359