Changes in kernel/generic/src/udebug/udebug.c [9441458:96b02eb9] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
TabularUnified kernel/generic/src/udebug/udebug.c ¶
r9441458 r96b02eb9 33 33 /** 34 34 * @file 35 * @brief 35 * @brief Udebug hooks and data structure management. 36 36 * 37 37 * Udebug is an interface that makes userspace debuggers possible. 38 38 */ 39 39 40 40 #include <synch/waitq.h> 41 41 #include <debug.h> … … 45 45 #include <arch.h> 46 46 47 48 47 /** Initialize udebug part of task structure. 49 48 * 50 49 * Called as part of task structure initialization. 51 * @param ut Pointer to the structure to initialize. 50 * @param ut Pointer to the structure to initialize. 51 * 52 52 */ 53 53 void udebug_task_init(udebug_task_t *ut) … … 63 63 * 64 64 * Called as part of thread structure initialization. 65 * @param ut Pointer to the structure to initialize. 65 * 66 * @param ut Pointer to the structure to initialize. 67 * 66 68 */ 67 69 void udebug_thread_initialize(udebug_thread_t *ut) … … 69 71 mutex_initialize(&ut->lock, MUTEX_PASSIVE); 70 72 waitq_initialize(&ut->go_wq); 71 73 condvar_initialize(&ut->active_cv); 74 72 75 ut->go_call = NULL; 73 76 ut->uspace_state = NULL; … … 75 78 ut->stoppable = true; 76 79 ut->active = false; 77 ut->cur_event = 0; /* none */80 ut->cur_event = 0; /* None */ 78 81 } 79 82 … … 84 87 * is received. 85 88 * 86 * @param wq The wait queue used by the thread to wait for GO messages. 89 * @param wq The wait queue used by the thread to wait for GO messages. 90 * 87 91 */ 88 92 static void udebug_wait_for_go(waitq_t *wq) 89 93 { 90 int rc; 91 ipl_t ipl; 92 93 ipl = waitq_sleep_prepare(wq); 94 95 wq->missed_wakeups = 0; /* Enforce blocking. */ 96 rc = waitq_sleep_timeout_unsafe(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE); 97 94 ipl_t ipl = waitq_sleep_prepare(wq); 95 96 wq->missed_wakeups = 0; /* Enforce blocking. */ 97 int rc = waitq_sleep_timeout_unsafe(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE); 98 98 99 waitq_sleep_finish(wq, rc, ipl); 99 100 } … … 101 102 /** Start of stoppable section. 102 103 * 103 * A stoppable section is a section of code where if the thread can be stoped. In other words, 104 * if a STOP operation is issued, the thread is guaranteed not to execute 105 * any userspace instructions until the thread is resumed. 104 * A stoppable section is a section of code where if the thread can 105 * be stoped. In other words, if a STOP operation is issued, the thread 106 * is guaranteed not to execute any userspace instructions until the 107 * thread is resumed. 106 108 * 107 109 * Having stoppable sections is better than having stopping points, since 108 110 * a thread can be stopped even when it is blocked indefinitely in a system 109 111 * call (whereas it would not reach any stopping point). 112 * 110 113 */ 111 114 void udebug_stoppable_begin(void) 112 115 { 113 int nsc;114 call_t *db_call, *go_call;115 116 116 ASSERT(THREAD); 117 117 ASSERT(TASK); 118 118 119 119 mutex_lock(&TASK->udebug.lock); 120 121 nsc = --TASK->udebug.not_stoppable_count;122 120 121 int nsc = --TASK->udebug.not_stoppable_count; 122 123 123 /* Lock order OK, THREAD->udebug.lock is after TASK->udebug.lock */ 124 124 mutex_lock(&THREAD->udebug.lock); 125 125 ASSERT(THREAD->udebug.stoppable == false); 126 126 THREAD->udebug.stoppable = true; 127 128 if ( TASK->udebug.dt_state == UDEBUG_TS_BEGINNING && nsc == 0) {127 128 if ((TASK->udebug.dt_state == UDEBUG_TS_BEGINNING) && (nsc == 0)) { 129 129 /* 130 130 * This was the last non-stoppable thread. Reply to 131 131 * DEBUG_BEGIN call. 132 * 132 133 */ 133 134 db_call = TASK->udebug.begin_call;134 135 call_t *db_call = TASK->udebug.begin_call; 135 136 ASSERT(db_call); 136 137 137 138 TASK->udebug.dt_state = UDEBUG_TS_ACTIVE; 138 139 TASK->udebug.begin_call = NULL; 139 140 140 141 IPC_SET_RETVAL(db_call->data, 0); 141 ipc_answer(&TASK->answerbox, db_call); 142 142 ipc_answer(&TASK->answerbox, db_call); 143 143 } else if (TASK->udebug.dt_state == UDEBUG_TS_ACTIVE) { 144 144 /* 145 145 * Active debugging session 146 146 */ 147 147 148 148 if (THREAD->udebug.active == true && 149 149 THREAD->udebug.go == false) { 150 150 /* 151 151 * Thread was requested to stop - answer go call 152 * 152 153 */ 153 154 154 155 /* Make sure nobody takes this call away from us */ 155 go_call = THREAD->udebug.go_call;156 call_t *go_call = THREAD->udebug.go_call; 156 157 THREAD->udebug.go_call = NULL; 157 158 ASSERT(go_call); 158 159 159 160 IPC_SET_RETVAL(go_call->data, 0); 160 161 IPC_SET_ARG1(go_call->data, UDEBUG_EVENT_STOP); 161 162 162 163 THREAD->udebug.cur_event = UDEBUG_EVENT_STOP; 163 164 ipc_answer(&TASK->answerbox, go_call); 164 ipc_answer(&TASK->answerbox, go_call); 165 165 } 166 166 } 167 167 168 168 mutex_unlock(&THREAD->udebug.lock); 169 169 mutex_unlock(&TASK->udebug.lock); … … 173 173 * 174 174 * This is the point where the thread will block if it is stopped. 175 * (As, by definition, a stopped thread must not leave its stoppable section). 175 * (As, by definition, a stopped thread must not leave its stoppable 176 * section). 177 * 176 178 */ 177 179 void udebug_stoppable_end(void) … … 180 182 mutex_lock(&TASK->udebug.lock); 181 183 mutex_lock(&THREAD->udebug.lock); 182 183 if ( THREAD->udebug.active && THREAD->udebug.go == false) {184 185 if ((THREAD->udebug.active) && (THREAD->udebug.go == false)) { 184 186 mutex_unlock(&THREAD->udebug.lock); 185 187 mutex_unlock(&TASK->udebug.lock); 186 188 187 189 udebug_wait_for_go(&THREAD->udebug.go_wq); 188 190 189 191 goto restart; 190 192 /* Must try again - have to lose stoppability atomically. */ … … 193 195 ASSERT(THREAD->udebug.stoppable == true); 194 196 THREAD->udebug.stoppable = false; 195 197 196 198 mutex_unlock(&THREAD->udebug.lock); 197 199 mutex_unlock(&TASK->udebug.lock); … … 202 204 * 203 205 * This function is called from clock(). 206 * 204 207 */ 205 208 void udebug_before_thread_runs(void) … … 214 217 * Must be called before and after servicing a system call. This generates 215 218 * a SYSCALL_B or SYSCALL_E event, depending on the value of @a end_variant. 216 */ 217 void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3, 218 unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc, 219 * 220 */ 221 void udebug_syscall_event(sysarg_t a1, sysarg_t a2, sysarg_t a3, 222 sysarg_t a4, sysarg_t a5, sysarg_t a6, sysarg_t id, sysarg_t rc, 219 223 bool end_variant) 220 224 { 221 call_t *call; 222 udebug_event_t etype; 223 224 etype = end_variant ? UDEBUG_EVENT_SYSCALL_E : UDEBUG_EVENT_SYSCALL_B; 225 225 udebug_event_t etype = 226 end_variant ? UDEBUG_EVENT_SYSCALL_E : UDEBUG_EVENT_SYSCALL_B; 227 226 228 mutex_lock(&TASK->udebug.lock); 227 229 mutex_lock(&THREAD->udebug.lock); 228 230 229 231 /* Must only generate events when in debugging session and is go. */ 230 232 if (THREAD->udebug.active != true || THREAD->udebug.go == false || … … 234 236 return; 235 237 } 236 238 237 239 /* Fill in the GO response. */ 238 call = THREAD->udebug.go_call;240 call_t *call = THREAD->udebug.go_call; 239 241 THREAD->udebug.go_call = NULL; 240 242 241 243 IPC_SET_RETVAL(call->data, 0); 242 244 IPC_SET_ARG1(call->data, etype); 243 245 IPC_SET_ARG2(call->data, id); 244 246 IPC_SET_ARG3(call->data, rc); 245 247 246 248 THREAD->udebug.syscall_args[0] = a1; 247 249 THREAD->udebug.syscall_args[1] = a2; … … 250 252 THREAD->udebug.syscall_args[4] = a5; 251 253 THREAD->udebug.syscall_args[5] = a6; 252 254 253 255 /* 254 256 * Make sure udebug.go is false when going to sleep 255 257 * in case we get woken up by DEBUG_END. (At which 256 258 * point it must be back to the initial true value). 259 * 257 260 */ 258 261 THREAD->udebug.go = false; 259 262 THREAD->udebug.cur_event = etype; 260 263 261 264 ipc_answer(&TASK->answerbox, call); 262 265 263 266 mutex_unlock(&THREAD->udebug.lock); 264 267 mutex_unlock(&TASK->udebug.lock); 265 268 266 269 udebug_wait_for_go(&THREAD->udebug.go_wq); 267 270 } … … 279 282 * and get a THREAD_B event for them. 280 283 * 281 * @param t Structure of the thread being created. Not locked, as the 282 * thread is not executing yet. 283 * @param ta Task to which the thread should be attached. 284 */ 285 void udebug_thread_b_event_attach(struct thread *t, struct task *ta) 286 { 287 call_t *call; 288 284 * @param thread Structure of the thread being created. Not locked, as the 285 * thread is not executing yet. 286 * @param task Task to which the thread should be attached. 287 * 288 */ 289 void udebug_thread_b_event_attach(struct thread *thread, struct task *task) 290 { 289 291 mutex_lock(&TASK->udebug.lock); 290 292 mutex_lock(&THREAD->udebug.lock); 291 292 thread_attach(t , ta);293 293 294 thread_attach(thread, task); 295 294 296 LOG("Check state"); 295 297 296 298 /* Must only generate events when in debugging session */ 297 299 if (THREAD->udebug.active != true) { 298 300 LOG("udebug.active: %s, udebug.go: %s", 299 THREAD->udebug.active ? "Yes(+)" : "No", 300 THREAD->udebug.go ? "Yes(-)" : "No"); 301 THREAD->udebug.active ? "Yes(+)" : "No", 302 THREAD->udebug.go ? "Yes(-)" : "No"); 303 301 304 mutex_unlock(&THREAD->udebug.lock); 302 305 mutex_unlock(&TASK->udebug.lock); 303 306 return; 304 307 } 305 308 306 309 LOG("Trigger event"); 307 call = THREAD->udebug.go_call; 310 311 call_t *call = THREAD->udebug.go_call; 312 308 313 THREAD->udebug.go_call = NULL; 309 314 IPC_SET_RETVAL(call->data, 0); 310 315 IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_B); 311 IPC_SET_ARG2(call->data, ( unative_t)t);312 316 IPC_SET_ARG2(call->data, (sysarg_t) thread); 317 313 318 /* 314 319 * Make sure udebug.go is false when going to sleep 315 320 * in case we get woken up by DEBUG_END. (At which 316 321 * point it must be back to the initial true value). 322 * 317 323 */ 318 324 THREAD->udebug.go = false; 319 325 THREAD->udebug.cur_event = UDEBUG_EVENT_THREAD_B; 320 326 321 327 ipc_answer(&TASK->answerbox, call); 322 328 323 329 mutex_unlock(&THREAD->udebug.lock); 324 330 mutex_unlock(&TASK->udebug.lock); 325 331 326 332 LOG("Wait for Go"); 327 333 udebug_wait_for_go(&THREAD->udebug.go_wq); … … 332 338 * Must be called when the current thread is terminating. 333 339 * Generates a THREAD_E event. 340 * 334 341 */ 335 342 void udebug_thread_e_event(void) 336 343 { 337 call_t *call;338 339 344 mutex_lock(&TASK->udebug.lock); 340 345 mutex_lock(&THREAD->udebug.lock); 341 346 342 347 LOG("Check state"); 343 348 344 349 /* Must only generate events when in debugging session. */ 345 350 if (THREAD->udebug.active != true) { 346 351 LOG("udebug.active: %s, udebug.go: %s", 347 THREAD->udebug.active ? "Yes" : "No", 348 THREAD->udebug.go ? "Yes" : "No"); 352 THREAD->udebug.active ? "Yes" : "No", 353 THREAD->udebug.go ? "Yes" : "No"); 354 349 355 mutex_unlock(&THREAD->udebug.lock); 350 356 mutex_unlock(&TASK->udebug.lock); 351 357 return; 352 358 } 353 359 354 360 LOG("Trigger event"); 355 call = THREAD->udebug.go_call; 361 362 call_t *call = THREAD->udebug.go_call; 363 356 364 THREAD->udebug.go_call = NULL; 357 365 IPC_SET_RETVAL(call->data, 0); 358 366 IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_E); 359 367 360 368 /* Prevent any further debug activity in thread. */ 361 369 THREAD->udebug.active = false; 362 THREAD->udebug.cur_event = 0; /* none */363 THREAD->udebug.go = false; /* set to initial value */364 370 THREAD->udebug.cur_event = 0; /* None */ 371 THREAD->udebug.go = false; /* Set to initial value */ 372 365 373 ipc_answer(&TASK->answerbox, call); 366 374 367 375 mutex_unlock(&THREAD->udebug.lock); 368 376 mutex_unlock(&TASK->udebug.lock); 369 370 /* 377 378 /* 371 379 * This event does not sleep - debugging has finished 372 380 * in this thread. 381 * 373 382 */ 374 383 } 375 384 376 /** 377 * Terminate task debugging session. 378 * 379 * Gracefully terminates the debugging session for a task. If the debugger 385 /** Terminate task debugging session. 386 * 387 * Gracefully terminate the debugging session for a task. If the debugger 380 388 * is still waiting for events on some threads, it will receive a 381 389 * FINISHED event for each of them. 382 390 * 383 * @param ta Task structure. ta->udebug.lock must be already locked. 384 * @return Zero on success or negative error code. 385 */ 386 int udebug_task_cleanup(struct task *ta) 387 { 388 thread_t *t; 391 * @param task Task structure. task->udebug.lock must be already locked. 392 * 393 * @return Zero on success or negative error code. 394 * 395 */ 396 int udebug_task_cleanup(struct task *task) 397 { 398 ASSERT(mutex_locked(&task->udebug.lock)); 399 400 if ((task->udebug.dt_state != UDEBUG_TS_BEGINNING) && 401 (task->udebug.dt_state != UDEBUG_TS_ACTIVE)) { 402 return EINVAL; 403 } 404 405 LOG("Task %" PRIu64, task->taskid); 406 407 /* Finish debugging of all userspace threads */ 389 408 link_t *cur; 390 int flags; 391 ipl_t ipl; 392 393 if (ta->udebug.dt_state != UDEBUG_TS_BEGINNING && 394 ta->udebug.dt_state != UDEBUG_TS_ACTIVE) { 395 return EINVAL; 396 } 397 398 LOG("Task %" PRIu64, ta->taskid); 399 400 /* Finish debugging of all userspace threads */ 401 for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) { 402 t = list_get_instance(cur, thread_t, th_link); 403 404 mutex_lock(&t->udebug.lock); 405 406 ipl = interrupts_disable(); 407 spinlock_lock(&t->lock); 408 409 flags = t->flags; 410 411 spinlock_unlock(&t->lock); 412 interrupts_restore(ipl); 413 409 for (cur = task->th_head.next; cur != &task->th_head; cur = cur->next) { 410 thread_t *thread = list_get_instance(cur, thread_t, th_link); 411 412 mutex_lock(&thread->udebug.lock); 413 unsigned int flags = thread->flags; 414 414 415 /* Only process userspace threads. */ 415 416 if ((flags & THREAD_FLAG_USPACE) != 0) { 416 417 /* Prevent any further debug activity in thread. */ 417 t ->udebug.active = false;418 t ->udebug.cur_event = 0; /* none */419 418 thread->udebug.active = false; 419 thread->udebug.cur_event = 0; /* None */ 420 420 421 /* Is the thread still go? */ 421 if (t ->udebug.go == true) {422 if (thread->udebug.go == true) { 422 423 /* 423 * Yes, so clear go. As active == false,424 * Yes, so clear go. As active == false, 424 425 * this doesn't affect anything. 426 ( 425 427 */ 426 t ->udebug.go = false;427 428 thread->udebug.go = false; 429 428 430 /* Answer GO call */ 429 431 LOG("Answer GO call with EVENT_FINISHED."); 430 IPC_SET_RETVAL(t->udebug.go_call->data, 0); 431 IPC_SET_ARG1(t->udebug.go_call->data, 432 433 IPC_SET_RETVAL(thread->udebug.go_call->data, 0); 434 IPC_SET_ARG1(thread->udebug.go_call->data, 432 435 UDEBUG_EVENT_FINISHED); 433 434 ipc_answer(&ta ->answerbox, t->udebug.go_call);435 t ->udebug.go_call = NULL;436 437 ipc_answer(&task->answerbox, thread->udebug.go_call); 438 thread->udebug.go_call = NULL; 436 439 } else { 437 440 /* 438 441 * Debug_stop is already at initial value. 439 442 * Yet this means the thread needs waking up. 443 * 440 444 */ 441 445 442 446 /* 443 * t 's lock must not be held when calling447 * thread's lock must not be held when calling 444 448 * waitq_wakeup. 449 * 445 450 */ 446 waitq_wakeup(&t ->udebug.go_wq, WAKEUP_FIRST);451 waitq_wakeup(&thread->udebug.go_wq, WAKEUP_FIRST); 447 452 } 448 } 449 mutex_unlock(&t->udebug.lock); 450 } 451 452 ta->udebug.dt_state = UDEBUG_TS_INACTIVE; 453 ta->udebug.debugger = NULL; 454 453 454 mutex_unlock(&thread->udebug.lock); 455 condvar_broadcast(&thread->udebug.active_cv); 456 } else 457 mutex_unlock(&thread->udebug.lock); 458 } 459 460 task->udebug.dt_state = UDEBUG_TS_INACTIVE; 461 task->udebug.debugger = NULL; 462 455 463 return 0; 456 464 } 457 465 466 /** Wait for debugger to handle a fault in this thread. 467 * 468 * When a thread faults and someone is subscribed to the FAULT kernel event, 469 * this function is called to wait for a debugging session to give userspace 470 * a chance to examine the faulting thead/task. When the debugging session 471 * is over, this function returns (so that thread/task cleanup can continue). 472 * 473 */ 474 void udebug_thread_fault(void) 475 { 476 udebug_stoppable_begin(); 477 478 /* Wait until a debugger attends to us. */ 479 mutex_lock(&THREAD->udebug.lock); 480 while (!THREAD->udebug.active) 481 condvar_wait(&THREAD->udebug.active_cv, &THREAD->udebug.lock); 482 mutex_unlock(&THREAD->udebug.lock); 483 484 /* Make sure the debugging session is over before proceeding. */ 485 mutex_lock(&THREAD->udebug.lock); 486 while (THREAD->udebug.active) 487 condvar_wait(&THREAD->udebug.active_cv, &THREAD->udebug.lock); 488 mutex_unlock(&THREAD->udebug.lock); 489 490 udebug_stoppable_end(); 491 } 458 492 459 493 /** @}
Note:
See TracChangeset
for help on using the changeset viewer.