source: mainline/kernel/generic/src/udebug/udebug.c@ 3926f30

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3926f30 was 3926f30, checked in by Jiri Svoboda <jirik.svoboda@…>, 17 years ago

Document functions in udebug_ipc.c

  • Property mode set to 100644
File size: 14.8 KB
Line 
1/*
2 * Copyright (c) 2008 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup generic
30 * @{
31 */
32
33/**
34 * @file
35 * @brief Udebug hooks and data structure management.
36 *
37 * Udebug is an interface that makes userspace debuggers possible.
38 *
39 * Functions in this file are executed directly in each thread, which
40 * may or may not be the subject of debugging. The udebug_stoppable_begin/end()
41 * functions are also executed in the clock interrupt handler. To avoid
42 * deadlock, functions in this file are protected from the interrupt
43 * by locking the recursive lock THREAD->udebug.int_lock (just an atomic
44 * variable). This prevents udebug_stoppable_begin/end() from being
45 * executed in the interrupt handler (they are skipped).
46 *
47 * Functions in udebug_ops.c and udebug_ipc.c execute in different threads,
48 * so they needn't be protected from the (preemptible) interrupt-initiated
49 * code.
50 */
51
52#include <synch/waitq.h>
53#include <debug.h>
54#include <udebug/udebug.h>
55#include <errno.h>
56#include <arch.h>
57
58static inline void udebug_int_lock(void)
59{
60 atomic_inc(&THREAD->udebug.int_lock);
61}
62
63static inline void udebug_int_unlock(void)
64{
65 atomic_dec(&THREAD->udebug.int_lock);
66}
67
68/** Initialize udebug part of task structure.
69 *
70 * Called as part of task structure initialization.
71 * @param ut Pointer to the structure to initialize.
72 */
73void udebug_task_init(udebug_task_t *ut)
74{
75 mutex_initialize(&ut->lock, MUTEX_PASSIVE);
76 ut->dt_state = UDEBUG_TS_INACTIVE;
77 ut->begin_call = NULL;
78 ut->not_stoppable_count = 0;
79 ut->evmask = 0;
80}
81
82/** Initialize udebug part of thread structure.
83 *
84 * Called as part of thread structure initialization.
85 * @param ut Pointer to the structure to initialize.
86 */
87void udebug_thread_initialize(udebug_thread_t *ut)
88{
89 mutex_initialize(&ut->lock, MUTEX_PASSIVE);
90 waitq_initialize(&ut->go_wq);
91
92 /*
93 * At the beginning the thread is stoppable, so int_lock be set, too.
94 */
95 atomic_set(&ut->int_lock, 1);
96
97 ut->go_call = NULL;
98 ut->stop = true;
99 ut->stoppable = true;
100 ut->debug_active = false;
101 ut->cur_event = 0; /* none */
102}
103
104/** Wait for a GO message.
105 *
106 * When a debugging event occurs in a thread or the thread is stopped,
107 * this function is called to block the thread until a GO message
108 * is received.
109 *
110 * @param wq The wait queue used by the thread to wait for GO messages.
111 */
112static void udebug_wait_for_go(waitq_t *wq)
113{
114 int rc;
115 ipl_t ipl;
116
117 ipl = waitq_sleep_prepare(wq);
118
119 wq->missed_wakeups = 0; /* Enforce blocking. */
120 rc = waitq_sleep_timeout_unsafe(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
121
122 waitq_sleep_finish(wq, rc, ipl);
123}
124
125/** Do a preliminary check that a debugging session is in progress.
126 *
127 * This only requires the THREAD->udebug.lock mutex (and not TASK->udebug.lock
128 * mutex). For an undebugged task, this will never block (while there could be
129 * collisions by different threads on the TASK mutex), thus improving SMP
130 * perormance for undebugged tasks.
131 *
132 * @return True if the thread was in a debugging session when the function
133 * checked, false otherwise.
134 */
135static bool udebug_thread_precheck(void)
136{
137 bool res;
138
139 mutex_lock(&THREAD->udebug.lock);
140 res = THREAD->udebug.debug_active;
141 mutex_unlock(&THREAD->udebug.lock);
142
143 return res;
144}
145
146/** Start of stoppable section.
147 *
148 * A stoppable section is a section of code where if the thread can be stoped. In other words,
149 * if a STOP operation is issued, the thread is guaranteed not to execute
150 * any userspace instructions until the thread is resumed.
151 *
152 * Having stoppable sections is better than having stopping points, since
153 * a thread can be stopped even when it is blocked indefinitely in a system
154 * call (whereas it would not reach any stopping point).
155 */
156void udebug_stoppable_begin(void)
157{
158 int nsc;
159 call_t *db_call, *go_call;
160
161 ASSERT(THREAD);
162 ASSERT(TASK);
163
164 udebug_int_lock();
165
166 /* Early check for undebugged tasks */
167 if (!udebug_thread_precheck()) {
168 udebug_int_unlock();
169 return;
170 }
171
172 mutex_lock(&TASK->udebug.lock);
173
174 nsc = --TASK->udebug.not_stoppable_count;
175
176 /* Lock order OK, THREAD->udebug.lock is after TASK->udebug.lock */
177 mutex_lock(&THREAD->udebug.lock);
178 ASSERT(THREAD->udebug.stoppable == false);
179 THREAD->udebug.stoppable = true;
180
181 if (TASK->udebug.dt_state == UDEBUG_TS_BEGINNING && nsc == 0) {
182 /*
183 * This was the last non-stoppable thread. Reply to
184 * DEBUG_BEGIN call.
185 */
186
187 db_call = TASK->udebug.begin_call;
188 ASSERT(db_call);
189
190 TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
191 TASK->udebug.begin_call = NULL;
192
193 IPC_SET_RETVAL(db_call->data, 0);
194 ipc_answer(&TASK->answerbox, db_call);
195
196 } else if (TASK->udebug.dt_state == UDEBUG_TS_ACTIVE) {
197 /*
198 * Active debugging session
199 */
200
201 if (THREAD->udebug.debug_active && THREAD->udebug.stop) {
202 /*
203 * Thread was requested to stop - answer go call
204 */
205
206 /* Make sure nobody takes this call away from us */
207 go_call = THREAD->udebug.go_call;
208 THREAD->udebug.go_call = NULL;
209 ASSERT(go_call);
210
211 IPC_SET_RETVAL(go_call->data, 0);
212 IPC_SET_ARG1(go_call->data, UDEBUG_EVENT_STOP);
213
214 THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
215
216 ipc_answer(&TASK->answerbox, go_call);
217 }
218 }
219
220 mutex_unlock(&THREAD->udebug.lock);
221 mutex_unlock(&TASK->udebug.lock);
222}
223
224/** End of a stoppable section.
225 *
226 * This is the point where the thread will block if it is stopped.
227 * (As, by definition, a stopped thread must not leave its stoppable section).
228 */
229void udebug_stoppable_end(void)
230{
231 /* Early check for undebugged tasks */
232 if (!udebug_thread_precheck()) {
233 udebug_int_unlock();
234 return;
235 }
236
237restart:
238 mutex_lock(&TASK->udebug.lock);
239 mutex_lock(&THREAD->udebug.lock);
240
241 if (THREAD->udebug.debug_active &&
242 THREAD->udebug.stop == true) {
243 TASK->udebug.begin_call = NULL;
244 mutex_unlock(&THREAD->udebug.lock);
245 mutex_unlock(&TASK->udebug.lock);
246
247 udebug_wait_for_go(&THREAD->udebug.go_wq);
248
249 goto restart;
250 /* must try again - have to lose stoppability atomically */
251 } else {
252 ++TASK->udebug.not_stoppable_count;
253 ASSERT(THREAD->udebug.stoppable == true);
254 THREAD->udebug.stoppable = false;
255
256 mutex_unlock(&THREAD->udebug.lock);
257 mutex_unlock(&TASK->udebug.lock);
258 }
259
260 udebug_int_unlock();
261}
262
263/** Upon being scheduled to run, check if the current thread should stop.
264 *
265 * This function is called from clock(). Preemption is enabled.
266 * interrupts are disabled, but since this is called after
267 * being scheduled-in, we can enable them, if we're careful enough
268 * not to allow arbitrary recursion or deadlock with the thread context.
269 */
270void udebug_before_thread_runs(void)
271{
272 ipl_t ipl;
273
274 return;
275 ASSERT(!PREEMPTION_DISABLED);
276
277 /*
278 * Prevent agains re-entering, such as when preempted inside this
279 * function.
280 */
281 if (atomic_get(&THREAD->udebug.int_lock) != 0)
282 return;
283
284 udebug_int_lock();
285
286 ipl = interrupts_enable();
287
288 /* Now we're free to do whatever we need (lock mutexes, sleep, etc.) */
289
290 /* Check if we're supposed to stop */
291 udebug_stoppable_begin();
292 udebug_stoppable_end();
293
294 interrupts_restore(ipl);
295
296 udebug_int_unlock();
297}
298
299/** Syscall event hook.
300 *
301 * Must be called before and after servicing a system call. This generates
302 * a SYSCALL_B or SYSCALL_E event, depending on the value of @a end_variant.
303 */
304void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3,
305 unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc,
306 bool end_variant)
307{
308 call_t *call;
309 udebug_event_t etype;
310
311 etype = end_variant ? UDEBUG_EVENT_SYSCALL_E : UDEBUG_EVENT_SYSCALL_B;
312
313 udebug_int_lock();
314
315 /* Early check for undebugged tasks */
316 if (!udebug_thread_precheck()) {
317 udebug_int_unlock();
318 return;
319 }
320
321 mutex_lock(&TASK->udebug.lock);
322 mutex_lock(&THREAD->udebug.lock);
323
324 /* Must only generate events when in debugging session and have go */
325 if (THREAD->udebug.debug_active != true ||
326 THREAD->udebug.stop == true ||
327 (TASK->udebug.evmask & UDEBUG_EVMASK(etype)) == 0) {
328 mutex_unlock(&THREAD->udebug.lock);
329 mutex_unlock(&TASK->udebug.lock);
330 return;
331 }
332
333 //printf("udebug_syscall_event\n");
334 call = THREAD->udebug.go_call;
335 THREAD->udebug.go_call = NULL;
336
337 IPC_SET_RETVAL(call->data, 0);
338 IPC_SET_ARG1(call->data, etype);
339 IPC_SET_ARG2(call->data, id);
340 IPC_SET_ARG3(call->data, rc);
341 //printf("udebug_syscall_event/ipc_answer\n");
342
343 THREAD->udebug.syscall_args[0] = a1;
344 THREAD->udebug.syscall_args[1] = a2;
345 THREAD->udebug.syscall_args[2] = a3;
346 THREAD->udebug.syscall_args[3] = a4;
347 THREAD->udebug.syscall_args[4] = a5;
348 THREAD->udebug.syscall_args[5] = a6;
349
350 /*
351 * Make sure udebug.stop is true when going to sleep
352 * in case we get woken up by DEBUG_END. (At which
353 * point it must be back to the initial true value).
354 */
355 THREAD->udebug.stop = true;
356 THREAD->udebug.cur_event = etype;
357
358 ipc_answer(&TASK->answerbox, call);
359
360 mutex_unlock(&THREAD->udebug.lock);
361 mutex_unlock(&TASK->udebug.lock);
362
363 udebug_wait_for_go(&THREAD->udebug.go_wq);
364
365 udebug_int_unlock();
366}
367
368/** Thread-creation event hook.
369 *
370 * Must be called when a new userspace thread is created in the debugged
371 * task. Generates a THREAD_B event.
372 *
373 * @param t Structure of the thread being created. Not locked, as the
374 * thread is not executing yet.
375 */
376void udebug_thread_b_event(struct thread *t)
377{
378 call_t *call;
379
380 udebug_int_lock();
381
382 mutex_lock(&TASK->udebug.lock);
383 mutex_lock(&THREAD->udebug.lock);
384
385 LOG("udebug_thread_b_event\n");
386 LOG("- check state\n");
387
388 /* Must only generate events when in debugging session */
389 if (THREAD->udebug.debug_active != true) {
390 LOG("- debug_active: %s, udebug.stop: %s\n",
391 THREAD->udebug.debug_active ? "yes(+)" : "no(-)",
392 THREAD->udebug.stop ? "yes(-)" : "no(+)");
393 mutex_unlock(&THREAD->udebug.lock);
394 mutex_unlock(&TASK->udebug.lock);
395 return;
396 }
397
398 LOG("- trigger event\n");
399
400 call = THREAD->udebug.go_call;
401 THREAD->udebug.go_call = NULL;
402 IPC_SET_RETVAL(call->data, 0);
403 IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_B);
404 IPC_SET_ARG2(call->data, (unative_t)t);
405
406 /*
407 * Make sure udebug.stop is true when going to sleep
408 * in case we get woken up by DEBUG_END. (At which
409 * point it must be back to the initial true value).
410 */
411 THREAD->udebug.stop = true;
412 THREAD->udebug.cur_event = UDEBUG_EVENT_THREAD_B;
413
414 ipc_answer(&TASK->answerbox, call);
415
416 mutex_unlock(&THREAD->udebug.lock);
417 mutex_unlock(&TASK->udebug.lock);
418
419 LOG("- sleep\n");
420 udebug_wait_for_go(&THREAD->udebug.go_wq);
421
422 udebug_int_unlock();
423}
424
425/** Thread-termination event hook.
426 *
427 * Must be called when the current thread is terminating.
428 * Generates a THREAD_E event.
429 */
430void udebug_thread_e_event(void)
431{
432 call_t *call;
433
434 udebug_int_lock();
435
436 mutex_lock(&TASK->udebug.lock);
437 mutex_lock(&THREAD->udebug.lock);
438
439 LOG("udebug_thread_e_event\n");
440 LOG("- check state\n");
441
442 /* Must only generate events when in debugging session */
443 if (THREAD->udebug.debug_active != true) {
444/* printf("- debug_active: %s, udebug.stop: %s\n",
445 THREAD->udebug.debug_active ? "yes(+)" : "no(-)",
446 THREAD->udebug.stop ? "yes(-)" : "no(+)");*/
447 mutex_unlock(&THREAD->udebug.lock);
448 mutex_unlock(&TASK->udebug.lock);
449 return;
450 }
451
452 LOG("- trigger event\n");
453
454 call = THREAD->udebug.go_call;
455 THREAD->udebug.go_call = NULL;
456 IPC_SET_RETVAL(call->data, 0);
457 IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_E);
458
459 /* Prevent any further debug activity in thread */
460 THREAD->udebug.debug_active = false;
461 THREAD->udebug.cur_event = 0; /* none */
462 THREAD->udebug.stop = true; /* set to initial value */
463
464 ipc_answer(&TASK->answerbox, call);
465
466 mutex_unlock(&THREAD->udebug.lock);
467 mutex_unlock(&TASK->udebug.lock);
468
469 /* Leave int_lock enabled */
470 /* This event does not sleep - debugging has finished in this thread */
471}
472
473/**
474 * Terminate task debugging session.
475 *
476 * Gracefully terminates the debugging session for a task. If the debugger
477 * is still waiting for events on some threads, it will receive a
478 * FINISHED event for each of them.
479 *
480 * @param ta Task structure. ta->udebug.lock must be already locked.
481 * @return Zero on success or negative error code.
482 */
483int udebug_task_cleanup(struct task *ta)
484{
485 thread_t *t;
486 link_t *cur;
487 int flags;
488 ipl_t ipl;
489
490 LOG("udebug_task_cleanup()\n");
491 LOG("task %" PRIu64 "\n", ta->taskid);
492
493 udebug_int_lock();
494
495 if (ta->udebug.dt_state != UDEBUG_TS_BEGINNING &&
496 ta->udebug.dt_state != UDEBUG_TS_ACTIVE) {
497 LOG("udebug_task_cleanup(): task not being debugged\n");
498 return EINVAL;
499 }
500
501 /* Finish debugging of all userspace threads */
502 for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
503 t = list_get_instance(cur, thread_t, th_link);
504
505 mutex_lock(&t->udebug.lock);
506
507 ipl = interrupts_disable();
508 spinlock_lock(&t->lock);
509
510 flags = t->flags;
511
512 spinlock_unlock(&t->lock);
513 interrupts_restore(ipl);
514
515 /* Only process userspace threads */
516 if ((flags & THREAD_FLAG_USPACE) != 0) {
517 /* Prevent any further debug activity in thread */
518 t->udebug.debug_active = false;
519 t->udebug.cur_event = 0; /* none */
520
521 /* Still has go? */
522 if (t->udebug.stop == false) {
523 /*
524 * Yes, so clear go. As debug_active == false,
525 * this doesn't affect anything.
526 */
527 t->udebug.stop = true;
528
529 /* Answer GO call */
530 LOG("answer GO call with EVENT_FINISHED\n");
531 IPC_SET_RETVAL(t->udebug.go_call->data, 0);
532 IPC_SET_ARG1(t->udebug.go_call->data,
533 UDEBUG_EVENT_FINISHED);
534
535 ipc_answer(&ta->answerbox, t->udebug.go_call);
536 t->udebug.go_call = NULL;
537 } else {
538 /*
539 * Debug_stop is already at initial value.
540 * Yet this means the thread needs waking up.
541 */
542
543 /*
544 * t's lock must not be held when calling
545 * waitq_wakeup.
546 */
547 waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
548 }
549 }
550 mutex_unlock(&t->udebug.lock);
551 }
552
553 ta->udebug.dt_state = UDEBUG_TS_INACTIVE;
554 ta->udebug.debugger = NULL;
555
556 udebug_int_unlock();
557
558 return 0;
559}
560
561
562/** @}
563 */
Note: See TracBrowser for help on using the repository browser.