Rev 3448 | Rev 3674 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 3448 | Rev 3474 | ||
|---|---|---|---|
| Line 30... | Line 30... | ||
| 30 | * @{ |
30 | * @{ |
| 31 | */ |
31 | */ |
| 32 | 32 | ||
| 33 | /** |
33 | /** |
| 34 | * @file |
34 | * @file |
| 35 | * @brief Udebug. |
35 | * @brief Udebug hooks and data structure management. |
| - | 36 | * |
|
| - | 37 | * Udebug is an interface that makes userspace debuggers possible. |
|
| 36 | * |
38 | * |
| 37 | * Functions in this file are executed directly in each thread, which |
39 | * Functions in this file are executed directly in each thread, which |
| 38 | * may or may not be the subject of debugging. The udebug_stoppable_begin/end() |
40 | * may or may not be the subject of debugging. The udebug_stoppable_begin/end() |
| 39 | * functions are also executed in the clock interrupt handler. To avoid |
41 | * functions are also executed in the clock interrupt handler. To avoid |
| 40 | * deadlock, functions in this file are protected from the interrupt |
42 | * deadlock, functions in this file are protected from the interrupt |
| Line 61... | Line 63... | ||
| 61 | static inline void udebug_int_unlock(void) |
63 | static inline void udebug_int_unlock(void) |
| 62 | { |
64 | { |
| 63 | atomic_dec(&THREAD->udebug.int_lock); |
65 | atomic_dec(&THREAD->udebug.int_lock); |
| 64 | } |
66 | } |
| 65 | 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 | */ |
|
| 66 | void udebug_task_init(udebug_task_t *ut) |
73 | void udebug_task_init(udebug_task_t *ut) |
| 67 | { |
74 | { |
| 68 | mutex_initialize(&ut->lock, MUTEX_PASSIVE); |
75 | mutex_initialize(&ut->lock, MUTEX_PASSIVE); |
| 69 | ut->dt_state = UDEBUG_TS_INACTIVE; |
76 | ut->dt_state = UDEBUG_TS_INACTIVE; |
| 70 | ut->begin_call = NULL; |
77 | ut->begin_call = NULL; |
| 71 | ut->not_stoppable_count = 0; |
78 | ut->not_stoppable_count = 0; |
| 72 | ut->evmask = 0; |
79 | ut->evmask = 0; |
| 73 | } |
80 | } |
| 74 | 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 | */ |
|
| 75 | void udebug_thread_initialize(udebug_thread_t *ut) |
87 | void udebug_thread_initialize(udebug_thread_t *ut) |
| 76 | { |
88 | { |
| 77 | mutex_initialize(&ut->lock, MUTEX_PASSIVE); |
89 | mutex_initialize(&ut->lock, MUTEX_PASSIVE); |
| 78 | waitq_initialize(&ut->go_wq); |
90 | waitq_initialize(&ut->go_wq); |
| 79 | 91 | ||
| Line 87... | Line 99... | ||
| 87 | ut->stoppable = true; |
99 | ut->stoppable = true; |
| 88 | ut->debug_active = false; |
100 | ut->debug_active = false; |
| 89 | ut->cur_event = 0; /* none */ |
101 | ut->cur_event = 0; /* none */ |
| 90 | } |
102 | } |
| 91 | 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 | */ |
|
| 92 | static void udebug_wait_for_go(waitq_t *wq) |
112 | static void udebug_wait_for_go(waitq_t *wq) |
| 93 | { |
113 | { |
| 94 | int rc; |
114 | int rc; |
| 95 | ipl_t ipl; |
115 | ipl_t ipl; |
| 96 | 116 | ||
| Line 102... | Line 122... | ||
| 102 | waitq_sleep_finish(wq, rc, ipl); |
122 | waitq_sleep_finish(wq, rc, ipl); |
| 103 | } |
123 | } |
| 104 | 124 | ||
| 105 | /** Do a preliminary check that a debugging session is in progress. |
125 | /** Do a preliminary check that a debugging session is in progress. |
| 106 | * |
126 | * |
| 107 | * This only requires the THREAD->udebug.lock mutex (and not |
127 | * This only requires the THREAD->udebug.lock mutex (and not TASK->udebug.lock |
| 108 | * TASK->udebug.lock mutex). For an undebugged task, this will |
128 | * mutex). For an undebugged task, this will never block (while there could be |
| 109 | * never block (while there could be collisions by different threads |
129 | * collisions by different threads on the TASK mutex), thus improving SMP |
| 110 | * on the TASK mutex), thus improving SMP perormance for undebugged tasks. |
130 | * perormance for undebugged tasks. |
| - | 131 | * |
|
| - | 132 | * @return True if the thread was in a debugging session when the function |
|
| - | 133 | * checked, false otherwise. |
|
| 111 | */ |
134 | */ |
| 112 | static bool udebug_thread_precheck(void) |
135 | static bool udebug_thread_precheck(void) |
| 113 | { |
136 | { |
| 114 | bool res; |
137 | bool res; |
| 115 | 138 | ||
| Line 118... | Line 141... | ||
| 118 | mutex_unlock(&THREAD->udebug.lock); |
141 | mutex_unlock(&THREAD->udebug.lock); |
| 119 | 142 | ||
| 120 | return res; |
143 | return res; |
| 121 | } |
144 | } |
| 122 | 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 | */ |
|
| 123 | void udebug_stoppable_begin(void) |
156 | void udebug_stoppable_begin(void) |
| 124 | { |
157 | { |
| 125 | int nsc; |
158 | int nsc; |
| 126 | call_t *db_call, *go_call; |
159 | call_t *db_call, *go_call; |
| 127 | 160 | ||
| Line 186... | Line 219... | ||
| 186 | 219 | ||
| 187 | mutex_unlock(&THREAD->udebug.lock); |
220 | mutex_unlock(&THREAD->udebug.lock); |
| 188 | mutex_unlock(&TASK->udebug.lock); |
221 | mutex_unlock(&TASK->udebug.lock); |
| 189 | } |
222 | } |
| 190 | 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 | */ |
|
| 191 | void udebug_stoppable_end(void) |
229 | void udebug_stoppable_end(void) |
| 192 | { |
230 | { |
| 193 | /* Early check for undebugged tasks */ |
231 | /* Early check for undebugged tasks */ |
| 194 | if (!udebug_thread_precheck()) { |
232 | if (!udebug_thread_precheck()) { |
| 195 | udebug_int_unlock(); |
233 | udebug_int_unlock(); |
| Line 256... | Line 294... | ||
| 256 | interrupts_restore(ipl); |
294 | interrupts_restore(ipl); |
| 257 | 295 | ||
| 258 | udebug_int_unlock(); |
296 | udebug_int_unlock(); |
| 259 | } |
297 | } |
| 260 | 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 | */ |
|
| 261 | void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3, |
304 | void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3, |
| 262 | unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc, |
305 | unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc, |
| 263 | bool end_variant) |
306 | bool end_variant) |
| 264 | { |
307 | { |
| 265 | call_t *call; |
308 | call_t *call; |
| Line 320... | Line 363... | ||
| 320 | udebug_wait_for_go(&THREAD->udebug.go_wq); |
363 | udebug_wait_for_go(&THREAD->udebug.go_wq); |
| 321 | 364 | ||
| 322 | udebug_int_unlock(); |
365 | udebug_int_unlock(); |
| 323 | } |
366 | } |
| 324 | 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 | */ |
|
| 325 | void udebug_thread_b_event(struct thread *t) |
376 | void udebug_thread_b_event(struct thread *t) |
| 326 | { |
377 | { |
| 327 | call_t *call; |
378 | call_t *call; |
| 328 | 379 | ||
| 329 | udebug_int_lock(); |
380 | udebug_int_lock(); |
| Line 369... | Line 420... | ||
| 369 | udebug_wait_for_go(&THREAD->udebug.go_wq); |
420 | udebug_wait_for_go(&THREAD->udebug.go_wq); |
| 370 | 421 | ||
| 371 | udebug_int_unlock(); |
422 | udebug_int_unlock(); |
| 372 | } |
423 | } |
| 373 | 424 | ||
| - | 425 | /** Thread-termination event hook. |
|
| - | 426 | * |
|
| - | 427 | * Must be called when the current thread is terminating. |
|
| - | 428 | * Generates a THREAD_E event. |
|
| - | 429 | */ |
|
| 374 | void udebug_thread_e_event(void) |
430 | void udebug_thread_e_event(void) |
| 375 | { |
431 | { |
| 376 | call_t *call; |
432 | call_t *call; |
| 377 | 433 | ||
| 378 | udebug_int_lock(); |
434 | udebug_int_lock(); |
| Line 415... | Line 471... | ||
| 415 | } |
471 | } |
| 416 | 472 | ||
| 417 | /** |
473 | /** |
| 418 | * Terminate task debugging session. |
474 | * Terminate task debugging session. |
| 419 | * |
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 | * |
|
| 420 | * \param ta->udebug.lock must be already locked. |
480 | * @param ta Task structure. ta->udebug.lock must be already locked. |
| 421 | * \return Zero on success or negative error code. |
481 | * @return Zero on success or negative error code. |
| 422 | */ |
482 | */ |
| 423 | int udebug_task_cleanup(struct task *ta) |
483 | int udebug_task_cleanup(struct task *ta) |
| 424 | { |
484 | { |
| 425 | thread_t *t; |
485 | thread_t *t; |
| 426 | link_t *cur; |
486 | link_t *cur; |
| Line 467... | Line 527... | ||
| 467 | t->udebug.stop = true; |
527 | t->udebug.stop = true; |
| 468 | 528 | ||
| 469 | /* Answer GO call */ |
529 | /* Answer GO call */ |
| 470 | LOG("answer GO call with EVENT_FINISHED\n"); |
530 | LOG("answer GO call with EVENT_FINISHED\n"); |
| 471 | IPC_SET_RETVAL(t->udebug.go_call->data, 0); |
531 | IPC_SET_RETVAL(t->udebug.go_call->data, 0); |
| 472 | IPC_SET_ARG1(t->udebug.go_call->data, UDEBUG_EVENT_FINISHED); |
532 | IPC_SET_ARG1(t->udebug.go_call->data, |
| - | 533 | UDEBUG_EVENT_FINISHED); |
|
| 473 | 534 | ||
| 474 | ipc_answer(&ta->answerbox, t->udebug.go_call); |
535 | ipc_answer(&ta->answerbox, t->udebug.go_call); |
| 475 | t->udebug.go_call = NULL; |
536 | t->udebug.go_call = NULL; |
| 476 | } else { |
537 | } else { |
| 477 | /* |
538 | /* |