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