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