Rev 3428 | Rev 3606 | 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 | /* |