Rev 3428 | Rev 3611 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 3428 | Rev 3471 | ||
---|---|---|---|
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 46... | Line 48... | ||
46 | * so they needn't be protected from the (preemptible) interrupt-initiated |
48 | * so they needn't be protected from the (preemptible) interrupt-initiated |
47 | * code. |
49 | * code. |
48 | */ |
50 | */ |
49 | 51 | ||
50 | #include <synch/waitq.h> |
52 | #include <synch/waitq.h> |
51 | #include <print.h> |
53 | #include <debug.h> |
52 | #include <udebug/udebug.h> |
54 | #include <udebug/udebug.h> |
53 | #include <errno.h> |
55 | #include <errno.h> |
54 | #include <arch.h> |
56 | #include <arch.h> |
55 | 57 | ||
56 | static inline void udebug_int_lock(void) |
58 | static inline void udebug_int_lock(void) |
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 88... | Line 100... | ||
88 | ut->stoppable = true; |
100 | ut->stoppable = true; |
89 | ut->debug_active = false; |
101 | ut->debug_active = false; |
90 | ut->cur_event = 0; /* none */ |
102 | ut->cur_event = 0; /* none */ |
91 | } |
103 | } |
92 | 104 | ||
- | 105 | /** Wait for a GO message. |
|
- | 106 | * |
|
- | 107 | * When a debugging event occurs in a thread or the thread is stopped, |
|
- | 108 | * this function is called to block the thread until a GO message |
|
- | 109 | * is received. |
|
- | 110 | * |
|
- | 111 | * @param wq The wait queue used by the thread to wait for GO messages. |
|
- | 112 | */ |
|
93 | static void udebug_wait_for_go(waitq_t *wq) |
113 | static void udebug_wait_for_go(waitq_t *wq) |
94 | { |
114 | { |
95 | int rc; |
115 | int rc; |
96 | ipl_t ipl; |
116 | ipl_t ipl; |
97 | 117 | ||
Line 103... | Line 123... | ||
103 | waitq_sleep_finish(wq, rc, ipl); |
123 | waitq_sleep_finish(wq, rc, ipl); |
104 | } |
124 | } |
105 | 125 | ||
106 | /** Do a preliminary check that a debugging session is in progress. |
126 | /** Do a preliminary check that a debugging session is in progress. |
107 | * |
127 | * |
108 | * This only requires the THREAD->udebug.lock mutex (and not |
128 | * This only requires the THREAD->udebug.lock mutex (and not TASK->udebug.lock |
109 | * TASK->udebug.lock mutex). For an undebugged task, this will |
129 | * mutex). For an undebugged task, this will never block (while there could be |
110 | * never block (while there could be collisions by different threads |
130 | * collisions by different threads on the TASK mutex), thus improving SMP |
111 | * on the TASK mutex), thus improving SMP perormance for undebugged tasks. |
131 | * perormance for undebugged tasks. |
- | 132 | * |
|
- | 133 | * @return True if the thread was in a debugging session when the function |
|
- | 134 | * checked, false otherwise. |
|
112 | */ |
135 | */ |
113 | static bool udebug_thread_precheck(void) |
136 | static bool udebug_thread_precheck(void) |
114 | { |
137 | { |
115 | bool res; |
138 | bool res; |
116 | 139 | ||
Line 119... | Line 142... | ||
119 | mutex_unlock(&THREAD->udebug.lock); |
142 | mutex_unlock(&THREAD->udebug.lock); |
120 | 143 | ||
121 | return res; |
144 | return res; |
122 | } |
145 | } |
123 | 146 | ||
- | 147 | /** Start of stoppable section. |
|
- | 148 | * |
|
- | 149 | * A stoppable section is a section of code where if the thread can be stoped. In other words, |
|
- | 150 | * if a STOP operation is issued, the thread is guaranteed not to execute |
|
- | 151 | * any userspace instructions until the thread is resumed. |
|
- | 152 | * |
|
- | 153 | * Having stoppable sections is better than having stopping points, since |
|
- | 154 | * a thread can be stopped even when it is blocked indefinitely in a system |
|
- | 155 | * call (whereas it would not reach any stopping point). |
|
- | 156 | */ |
|
124 | void udebug_stoppable_begin(void) |
157 | void udebug_stoppable_begin(void) |
125 | { |
158 | { |
126 | int nsc; |
159 | int nsc; |
127 | call_t *db_call, *go_call; |
160 | call_t *db_call, *go_call; |
128 | 161 | ||
Line 187... | Line 220... | ||
187 | 220 | ||
188 | mutex_unlock(&THREAD->udebug.lock); |
221 | mutex_unlock(&THREAD->udebug.lock); |
189 | mutex_unlock(&TASK->udebug.lock); |
222 | mutex_unlock(&TASK->udebug.lock); |
190 | } |
223 | } |
191 | 224 | ||
- | 225 | /** End of a stoppable section. |
|
- | 226 | * |
|
- | 227 | * This is the point where the thread will block if it is stopped. |
|
- | 228 | * (As, by definition, a stopped thread must not leave its stoppable section). |
|
- | 229 | */ |
|
192 | void udebug_stoppable_end(void) |
230 | void udebug_stoppable_end(void) |
193 | { |
231 | { |
194 | /* Early check for undebugged tasks */ |
232 | /* Early check for undebugged tasks */ |
195 | if (!udebug_thread_precheck()) { |
233 | if (!udebug_thread_precheck()) { |
196 | udebug_int_unlock(); |
234 | udebug_int_unlock(); |
Line 257... | Line 295... | ||
257 | interrupts_restore(ipl); |
295 | interrupts_restore(ipl); |
258 | 296 | ||
259 | udebug_int_unlock(); |
297 | udebug_int_unlock(); |
260 | } |
298 | } |
261 | 299 | ||
- | 300 | /** Syscall event hook. |
|
- | 301 | * |
|
- | 302 | * Must be called before and after servicing a system call. This generates |
|
- | 303 | * a SYSCALL_B or SYSCALL_E event, depending on the value of @a end_variant. |
|
- | 304 | */ |
|
262 | void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3, |
305 | void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3, |
263 | unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc, |
306 | unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc, |
264 | bool end_variant) |
307 | bool end_variant) |
265 | { |
308 | { |
266 | call_t *call; |
309 | call_t *call; |
Line 321... | Line 364... | ||
321 | udebug_wait_for_go(&THREAD->udebug.go_wq); |
364 | udebug_wait_for_go(&THREAD->udebug.go_wq); |
322 | 365 | ||
323 | udebug_int_unlock(); |
366 | udebug_int_unlock(); |
324 | } |
367 | } |
325 | 368 | ||
- | 369 | /** Thread-creation event hook. |
|
- | 370 | * |
|
- | 371 | * Must be called when a new userspace thread is created in the debugged |
|
- | 372 | * task. Generates a THREAD_B event. |
|
- | 373 | * |
|
- | 374 | * @param t Structure of the thread being created. Not locked, as the |
|
- | 375 | * thread is not executing yet. |
|
- | 376 | */ |
|
326 | void udebug_thread_b_event(struct thread *t) |
377 | void udebug_thread_b_event(struct thread *t) |
327 | { |
378 | { |
328 | call_t *call; |
379 | call_t *call; |
329 | 380 | ||
330 | udebug_int_lock(); |
381 | udebug_int_lock(); |
331 | 382 | ||
332 | mutex_lock(&TASK->udebug.lock); |
383 | mutex_lock(&TASK->udebug.lock); |
333 | mutex_lock(&THREAD->udebug.lock); |
384 | mutex_lock(&THREAD->udebug.lock); |
334 | 385 | ||
335 | printf("udebug_thread_b_event\n"); |
386 | LOG("udebug_thread_b_event\n"); |
336 | printf("- check state\n"); |
387 | LOG("- check state\n"); |
337 | 388 | ||
338 | /* Must only generate events when in debugging session */ |
389 | /* Must only generate events when in debugging session */ |
339 | if (THREAD->udebug.debug_active != true) { |
390 | if (THREAD->udebug.debug_active != true) { |
340 | printf("- debug_active: %s, udebug.stop: %s\n", |
391 | LOG("- debug_active: %s, udebug.stop: %s\n", |
341 | THREAD->udebug.debug_active ? "yes(+)" : "no(-)", |
392 | THREAD->udebug.debug_active ? "yes(+)" : "no(-)", |
342 | THREAD->udebug.stop ? "yes(-)" : "no(+)"); |
393 | THREAD->udebug.stop ? "yes(-)" : "no(+)"); |
343 | mutex_unlock(&THREAD->udebug.lock); |
394 | mutex_unlock(&THREAD->udebug.lock); |
344 | mutex_unlock(&TASK->udebug.lock); |
395 | mutex_unlock(&TASK->udebug.lock); |
345 | return; |
396 | return; |
346 | } |
397 | } |
347 | 398 | ||
348 | printf("- trigger event\n"); |
399 | LOG("- trigger event\n"); |
349 | 400 | ||
350 | call = THREAD->udebug.go_call; |
401 | call = THREAD->udebug.go_call; |
351 | THREAD->udebug.go_call = NULL; |
402 | THREAD->udebug.go_call = NULL; |
352 | IPC_SET_RETVAL(call->data, 0); |
403 | IPC_SET_RETVAL(call->data, 0); |
353 | IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_B); |
404 | IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_B); |
Line 364... | Line 415... | ||
364 | ipc_answer(&TASK->answerbox, call); |
415 | ipc_answer(&TASK->answerbox, call); |
365 | 416 | ||
366 | mutex_unlock(&THREAD->udebug.lock); |
417 | mutex_unlock(&THREAD->udebug.lock); |
367 | mutex_unlock(&TASK->udebug.lock); |
418 | mutex_unlock(&TASK->udebug.lock); |
368 | 419 | ||
369 | printf("- sleep\n"); |
420 | LOG("- sleep\n"); |
370 | udebug_wait_for_go(&THREAD->udebug.go_wq); |
421 | udebug_wait_for_go(&THREAD->udebug.go_wq); |
371 | 422 | ||
372 | udebug_int_unlock(); |
423 | udebug_int_unlock(); |
373 | } |
424 | } |
374 | 425 | ||
- | 426 | /** Thread-termination event hook. |
|
- | 427 | * |
|
- | 428 | * Must be called when the current thread is terminating. |
|
- | 429 | * Generates a THREAD_E event. |
|
- | 430 | */ |
|
375 | void udebug_thread_e_event(void) |
431 | void udebug_thread_e_event(void) |
376 | { |
432 | { |
377 | call_t *call; |
433 | call_t *call; |
378 | 434 | ||
379 | udebug_int_lock(); |
435 | udebug_int_lock(); |
380 | 436 | ||
381 | mutex_lock(&TASK->udebug.lock); |
437 | mutex_lock(&TASK->udebug.lock); |
382 | mutex_lock(&THREAD->udebug.lock); |
438 | mutex_lock(&THREAD->udebug.lock); |
383 | 439 | ||
384 | // printf("udebug_thread_e_event\n"); |
440 | LOG("udebug_thread_e_event\n"); |
385 | // printf("- check state\n"); |
441 | LOG("- check state\n"); |
386 | 442 | ||
387 | /* Must only generate events when in debugging session */ |
443 | /* Must only generate events when in debugging session */ |
388 | if (THREAD->udebug.debug_active != true) { |
444 | if (THREAD->udebug.debug_active != true) { |
389 | /* printf("- debug_active: %s, udebug.stop: %s\n", |
445 | /* printf("- debug_active: %s, udebug.stop: %s\n", |
390 | THREAD->udebug.debug_active ? "yes(+)" : "no(-)", |
446 | THREAD->udebug.debug_active ? "yes(+)" : "no(-)", |
Line 392... | Line 448... | ||
392 | mutex_unlock(&THREAD->udebug.lock); |
448 | mutex_unlock(&THREAD->udebug.lock); |
393 | mutex_unlock(&TASK->udebug.lock); |
449 | mutex_unlock(&TASK->udebug.lock); |
394 | return; |
450 | return; |
395 | } |
451 | } |
396 | 452 | ||
397 | // printf("- trigger event\n"); |
453 | LOG("- trigger event\n"); |
398 | 454 | ||
399 | call = THREAD->udebug.go_call; |
455 | call = THREAD->udebug.go_call; |
400 | THREAD->udebug.go_call = NULL; |
456 | THREAD->udebug.go_call = NULL; |
401 | IPC_SET_RETVAL(call->data, 0); |
457 | IPC_SET_RETVAL(call->data, 0); |
402 | IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_E); |
458 | IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_E); |
Line 479... | Line 535... | ||
479 | } |
535 | } |
480 | 536 | ||
481 | /** |
537 | /** |
482 | * Terminate task debugging session. |
538 | * Terminate task debugging session. |
483 | * |
539 | * |
- | 540 | * Gracefully terminates the debugging session for a task. If the debugger |
|
- | 541 | * is still waiting for events on some threads, it will receive a |
|
- | 542 | * FINISHED event for each of them. |
|
- | 543 | * |
|
484 | * \param ta->udebug.lock must be already locked. |
544 | * @param ta Task structure. ta->udebug.lock must be already locked. |
485 | * \return Zero on success or negative error code. |
545 | * @return Zero on success or negative error code. |
486 | */ |
546 | */ |
487 | int udebug_task_cleanup(struct task *ta) |
547 | int udebug_task_cleanup(struct task *ta) |
488 | { |
548 | { |
489 | thread_t *t; |
549 | thread_t *t; |
490 | link_t *cur; |
550 | link_t *cur; |
491 | int flags; |
551 | int flags; |
492 | ipl_t ipl; |
552 | ipl_t ipl; |
493 | 553 | ||
494 | printf("udebug_task_cleanup()\n"); |
554 | LOG("udebug_task_cleanup()\n"); |
495 | printf("task %llu\n", ta->taskid); |
555 | LOG("task %" PRIu64 "\n", ta->taskid); |
496 | 556 | ||
497 | udebug_int_lock(); |
557 | udebug_int_lock(); |
498 | 558 | ||
499 | if (ta->udebug.dt_state != UDEBUG_TS_BEGINNING && |
559 | if (ta->udebug.dt_state != UDEBUG_TS_BEGINNING && |
500 | ta->udebug.dt_state != UDEBUG_TS_ACTIVE) { |
560 | ta->udebug.dt_state != UDEBUG_TS_ACTIVE) { |
501 | printf("udebug_task_cleanup(): task not being debugged\n"); |
561 | LOG("udebug_task_cleanup(): task not being debugged\n"); |
502 | return EINVAL; |
562 | return EINVAL; |
503 | } |
563 | } |
504 | 564 | ||
505 | /* Finish debugging of all userspace threads */ |
565 | /* Finish debugging of all userspace threads */ |
506 | for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) { |
566 | for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) { |
Line 529... | Line 589... | ||
529 | * this doesn't affect anything. |
589 | * this doesn't affect anything. |
530 | */ |
590 | */ |
531 | t->udebug.stop = true; |
591 | t->udebug.stop = true; |
532 | 592 | ||
533 | /* Answer GO call */ |
593 | /* Answer GO call */ |
534 | printf("answer GO call with EVENT_FINISHED\n"); |
594 | LOG("answer GO call with EVENT_FINISHED\n"); |
535 | IPC_SET_RETVAL(t->udebug.go_call->data, 0); |
595 | IPC_SET_RETVAL(t->udebug.go_call->data, 0); |
536 | IPC_SET_ARG1(t->udebug.go_call->data, UDEBUG_EVENT_FINISHED); |
596 | IPC_SET_ARG1(t->udebug.go_call->data, |
- | 597 | UDEBUG_EVENT_FINISHED); |
|
537 | 598 | ||
538 | ipc_answer(&ta->answerbox, t->udebug.go_call); |
599 | ipc_answer(&ta->answerbox, t->udebug.go_call); |
539 | t->udebug.go_call = NULL; |
600 | t->udebug.go_call = NULL; |
540 | } else { |
601 | } else { |
541 | /* |
602 | /* |