Rev 3441 | Rev 3600 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 3441 | Rev 3457 | ||
---|---|---|---|
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; |