Changes in kernel/generic/src/udebug/udebug_ops.c [ae5aa90:96b02eb9] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/src/udebug/udebug_ops.c
rae5aa90 r96b02eb9 33 33 /** 34 34 * @file 35 * @brief 35 * @brief Udebug operations. 36 36 * 37 37 * Udebug operations on tasks and threads are implemented here. The … … 39 39 * when servicing udebug IPC messages. 40 40 */ 41 41 42 42 #include <debug.h> 43 43 #include <proc/task.h> … … 46 46 #include <errno.h> 47 47 #include <print.h> 48 #include <str.h> 48 49 #include <syscall/copy.h> 49 50 #include <ipc/ipc.h> 50 51 #include <udebug/udebug.h> 51 52 #include <udebug/udebug_ops.h> 52 53 /** 54 53 #include <memstr.h> 54 55 /** Prepare a thread for a debugging operation. 55 56 * 56 57 * Simply put, return thread t with t->udebug.lock held, … … 71 72 * the t->lock spinlock to the t->udebug.lock mutex. 72 73 * 73 * @param t 74 * @param being_go 74 * @param thread Pointer, need not at all be valid. 75 * @param being_go Required thread state. 75 76 * 76 77 * Returns EOK if all went well, or an error code otherwise. 77 */ 78 static int _thread_op_begin(thread_t *t, bool being_go) 79 { 80 task_id_t taskid; 81 ipl_t ipl; 82 83 taskid = TASK->taskid; 84 85 mutex_lock(&TASK->udebug.lock); 86 78 * 79 */ 80 static int _thread_op_begin(thread_t *thread, bool being_go) 81 { 82 mutex_lock(&TASK->udebug.lock); 83 87 84 /* thread_exists() must be called with threads_lock held */ 88 ipl = interrupts_disable(); 89 spinlock_lock(&threads_lock); 90 91 if (!thread_exists(t)) { 92 spinlock_unlock(&threads_lock); 93 interrupts_restore(ipl); 85 irq_spinlock_lock(&threads_lock, true); 86 87 if (!thread_exists(thread)) { 88 irq_spinlock_unlock(&threads_lock, true); 94 89 mutex_unlock(&TASK->udebug.lock); 95 90 return ENOENT; 96 91 } 97 98 /* t->lock is enough to ensure the thread's existence */ 99 spinlock_lock(&t->lock); 100 spinlock_unlock(&threads_lock); 101 102 /* Verify that 't' is a userspace thread. */ 103 if ((t->flags & THREAD_FLAG_USPACE) == 0) { 92 93 /* thread->lock is enough to ensure the thread's existence */ 94 irq_spinlock_exchange(&threads_lock, &thread->lock); 95 96 /* Verify that 'thread' is a userspace thread. */ 97 if ((thread->flags & THREAD_FLAG_USPACE) == 0) { 104 98 /* It's not, deny its existence */ 105 spinlock_unlock(&t->lock); 106 interrupts_restore(ipl); 99 irq_spinlock_unlock(&thread->lock, true); 107 100 mutex_unlock(&TASK->udebug.lock); 108 101 return ENOENT; 109 102 } 110 103 111 104 /* Verify debugging state. */ 112 if (t ->udebug.active != true) {105 if (thread->udebug.active != true) { 113 106 /* Not in debugging session or undesired GO state */ 114 spinlock_unlock(&t->lock); 115 interrupts_restore(ipl); 107 irq_spinlock_unlock(&thread->lock, true); 116 108 mutex_unlock(&TASK->udebug.lock); 117 109 return ENOENT; 118 110 } 119 111 120 112 /* 121 113 * Since the thread has active == true, TASK->udebug.lock 122 114 * is enough to ensure its existence and that active remains 123 115 * true. 116 * 124 117 */ 125 spinlock_unlock(&t->lock); 126 interrupts_restore(ipl); 127 118 irq_spinlock_unlock(&thread->lock, true); 119 128 120 /* Only mutex TASK->udebug.lock left. */ 129 121 130 122 /* Now verify that the thread belongs to the current task. */ 131 if (t ->task != TASK) {123 if (thread->task != TASK) { 132 124 /* No such thread belonging this task*/ 133 125 mutex_unlock(&TASK->udebug.lock); 134 126 return ENOENT; 135 127 } 136 128 137 129 /* 138 130 * Now we need to grab the thread's debug lock for synchronization 139 131 * of the threads stoppability/stop state. 132 * 140 133 */ 141 mutex_lock(&t ->udebug.lock);142 134 mutex_lock(&thread->udebug.lock); 135 143 136 /* The big task mutex is no longer needed. */ 144 137 mutex_unlock(&TASK->udebug.lock); 145 146 if (t ->udebug.go != being_go) {138 139 if (thread->udebug.go != being_go) { 147 140 /* Not in debugging session or undesired GO state. */ 148 mutex_unlock(&t ->udebug.lock);141 mutex_unlock(&thread->udebug.lock); 149 142 return EINVAL; 150 143 } 151 152 /* Only t ->udebug.lock left. */153 154 return EOK; 144 145 /* Only thread->udebug.lock left. */ 146 147 return EOK; /* All went well. */ 155 148 } 156 149 157 150 /** End debugging operation on a thread. */ 158 static void _thread_op_end(thread_t *t )159 { 160 mutex_unlock(&t ->udebug.lock);151 static void _thread_op_end(thread_t *thread) 152 { 153 mutex_unlock(&thread->udebug.lock); 161 154 } 162 155 … … 172 165 * all the threads become stoppable (i.e. they can be considered stopped). 173 166 * 174 * @param call The BEGIN call we are servicing. 175 * @return 0 (OK, but not done yet), 1 (done) or negative error code. 167 * @param call The BEGIN call we are servicing. 168 * 169 * @return 0 (OK, but not done yet), 1 (done) or negative error code. 170 * 176 171 */ 177 172 int udebug_begin(call_t *call) 178 173 { 179 int reply; 180 181 thread_t *t; 182 link_t *cur; 183 184 LOG("Debugging task %llu", TASK->taskid); 185 mutex_lock(&TASK->udebug.lock); 186 174 LOG("Debugging task %" PRIu64, TASK->taskid); 175 176 mutex_lock(&TASK->udebug.lock); 177 187 178 if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) { 188 179 mutex_unlock(&TASK->udebug.lock); 189 180 return EBUSY; 190 181 } 191 182 192 183 TASK->udebug.dt_state = UDEBUG_TS_BEGINNING; 193 184 TASK->udebug.begin_call = call; 194 185 TASK->udebug.debugger = call->sender; 195 186 187 int reply; 188 196 189 if (TASK->udebug.not_stoppable_count == 0) { 197 190 TASK->udebug.dt_state = UDEBUG_TS_ACTIVE; 198 191 TASK->udebug.begin_call = NULL; 199 reply = 1; /* immediate reply */ 200 } else { 201 reply = 0; /* no reply */ 202 } 192 reply = 1; /* immediate reply */ 193 } else 194 reply = 0; /* no reply */ 203 195 204 196 /* Set udebug.active on all of the task's userspace threads. */ 205 197 198 link_t *cur; 206 199 for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) { 207 t = list_get_instance(cur, thread_t, th_link); 208 209 mutex_lock(&t->udebug.lock); 210 if ((t->flags & THREAD_FLAG_USPACE) != 0) 211 t->udebug.active = true; 212 mutex_unlock(&t->udebug.lock); 213 } 214 200 thread_t *thread = list_get_instance(cur, thread_t, th_link); 201 202 mutex_lock(&thread->udebug.lock); 203 if ((thread->flags & THREAD_FLAG_USPACE) != 0) { 204 thread->udebug.active = true; 205 mutex_unlock(&thread->udebug.lock); 206 condvar_broadcast(&thread->udebug.active_cv); 207 } else 208 mutex_unlock(&thread->udebug.lock); 209 } 210 215 211 mutex_unlock(&TASK->udebug.lock); 216 212 return reply; … … 220 216 * 221 217 * Closes the debugging session for the current task. 218 * 222 219 * @return Zero on success or negative error code. 220 * 223 221 */ 224 222 int udebug_end(void) 225 223 { 226 int rc;227 228 224 LOG("Task %" PRIu64, TASK->taskid); 229 230 mutex_lock(&TASK->udebug.lock); 231 rc = udebug_task_cleanup(TASK);232 mutex_unlock(&TASK->udebug.lock); 233 225 226 mutex_lock(&TASK->udebug.lock); 227 int rc = udebug_task_cleanup(TASK); 228 mutex_unlock(&TASK->udebug.lock); 229 234 230 return rc; 235 231 } … … 239 235 * Sets the event mask that determines which events are enabled. 240 236 * 241 * @param mask Or combination of events that should be enabled. 242 * @return Zero on success or negative error code. 237 * @param mask Or combination of events that should be enabled. 238 * 239 * @return Zero on success or negative error code. 240 * 243 241 */ 244 242 int udebug_set_evmask(udebug_evmask_t mask) 245 243 { 246 244 LOG("mask = 0x%x", mask); 247 248 mutex_lock(&TASK->udebug.lock); 249 245 246 mutex_lock(&TASK->udebug.lock); 247 250 248 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) { 251 249 mutex_unlock(&TASK->udebug.lock); 252 250 return EINVAL; 253 251 } 254 252 255 253 TASK->udebug.evmask = mask; 256 254 mutex_unlock(&TASK->udebug.lock); 257 255 258 256 return 0; 259 257 } … … 265 263 * a debugging event or STOP occurs, at which point the thread loses GO. 266 264 * 267 * @param t The thread to operate on (unlocked and need not be valid). 268 * @param call The GO call that we are servicing. 269 */ 270 int udebug_go(thread_t *t, call_t *call) 271 { 272 int rc; 273 274 /* On success, this will lock t->udebug.lock. */ 275 rc = _thread_op_begin(t, false); 276 if (rc != EOK) { 265 * @param thread The thread to operate on (unlocked and need not be valid). 266 * @param call The GO call that we are servicing. 267 * 268 */ 269 int udebug_go(thread_t *thread, call_t *call) 270 { 271 /* On success, this will lock thread->udebug.lock. */ 272 int rc = _thread_op_begin(thread, false); 273 if (rc != EOK) 277 274 return rc; 278 } 279 280 t->udebug.go_call = call; 281 t->udebug.go = true; 282 t->udebug.cur_event = 0; /* none */ 283 275 276 thread->udebug.go_call = call; 277 thread->udebug.go = true; 278 thread->udebug.cur_event = 0; /* none */ 279 284 280 /* 285 * Neither t's lock nor threads_lock may be held during wakeup. 281 * Neither thread's lock nor threads_lock may be held during wakeup. 282 * 286 283 */ 287 waitq_wakeup(&t ->udebug.go_wq, WAKEUP_FIRST);288 289 _thread_op_end(t );290 284 waitq_wakeup(&thread->udebug.go_wq, WAKEUP_FIRST); 285 286 _thread_op_end(thread); 287 291 288 return 0; 292 289 } … … 297 294 * can be considered stopped). 298 295 * 299 * @param t The thread to operate on (unlocked and need not be valid). 300 * @param call The GO call that we are servicing. 301 */ 302 int udebug_stop(thread_t *t, call_t *call) 303 { 304 int rc; 305 296 * @param thread The thread to operate on (unlocked and need not be valid). 297 * @param call The GO call that we are servicing. 298 * 299 */ 300 int udebug_stop(thread_t *thread, call_t *call) 301 { 306 302 LOG("udebug_stop()"); 307 303 308 304 /* 309 * On success, this will lock t->udebug.lock. Note that this makes sure 310 * the thread is not stopped. 305 * On success, this will lock thread->udebug.lock. Note that this 306 * makes sure the thread is not stopped. 307 * 311 308 */ 312 rc = _thread_op_begin(t, true);313 if (rc != EOK) {309 int rc = _thread_op_begin(thread, true); 310 if (rc != EOK) 314 311 return rc; 315 } 316 312 317 313 /* Take GO away from the thread. */ 318 t ->udebug.go = false;319 320 if (t ->udebug.stoppable != true) {314 thread->udebug.go = false; 315 316 if (thread->udebug.stoppable != true) { 321 317 /* Answer will be sent when the thread becomes stoppable. */ 322 _thread_op_end(t );318 _thread_op_end(thread); 323 319 return 0; 324 320 } 325 321 326 322 /* 327 323 * Answer GO call. 324 * 328 325 */ 329 326 330 327 /* Make sure nobody takes this call away from us. */ 331 call = t ->udebug.go_call;332 t ->udebug.go_call = NULL;333 328 call = thread->udebug.go_call; 329 thread->udebug.go_call = NULL; 330 334 331 IPC_SET_RETVAL(call->data, 0); 335 332 IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP); 336 333 337 334 THREAD->udebug.cur_event = UDEBUG_EVENT_STOP; 338 339 _thread_op_end(t );340 335 336 _thread_op_end(thread); 337 341 338 mutex_lock(&TASK->udebug.lock); 342 339 ipc_answer(&TASK->answerbox, call); 343 340 mutex_unlock(&TASK->udebug.lock); 344 341 345 342 return 0; 346 343 } … … 354 351 * 355 352 * If the sequence is longer than @a buf_size bytes, only as much hashes 356 * as can fit are copied. The number of thread hashes copied is stored 357 * in @a n. 353 * as can fit are copied. The number of bytes copied is stored in @a stored. 354 * The total number of thread bytes that could have been saved had there been 355 * enough space is stored in @a needed. 358 356 * 359 357 * The rationale for having @a buf_size is that this function is only … … 361 359 * a maximum size for the userspace buffer. 362 360 * 363 * @param buffer The buffer for storing thread hashes. 364 * @param buf_size Buffer size in bytes. 365 * @param n The actual number of hashes copied will be stored here. 366 */ 367 int udebug_thread_read(void **buffer, size_t buf_size, size_t *n) 368 { 369 thread_t *t; 370 link_t *cur; 371 unative_t tid; 372 unsigned copied_ids; 373 ipl_t ipl; 374 unative_t *id_buffer; 375 int flags; 376 size_t max_ids; 377 361 * @param buffer The buffer for storing thread hashes. 362 * @param buf_size Buffer size in bytes. 363 * @param stored The actual number of bytes copied will be stored here. 364 * @param needed Total number of hashes that could have been saved. 365 * 366 */ 367 int udebug_thread_read(void **buffer, size_t buf_size, size_t *stored, 368 size_t *needed) 369 { 378 370 LOG("udebug_thread_read()"); 379 371 380 372 /* Allocate a buffer to hold thread IDs */ 381 id_buffer = malloc(buf_size, 0);382 383 mutex_lock(&TASK->udebug.lock); 384 373 sysarg_t *id_buffer = malloc(buf_size + 1, 0); 374 375 mutex_lock(&TASK->udebug.lock); 376 385 377 /* Verify task state */ 386 378 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) { … … 388 380 return EINVAL; 389 381 } 390 391 i pl = interrupts_disable();392 spinlock_lock(&TASK->lock);382 383 irq_spinlock_lock(&TASK->lock, true); 384 393 385 /* Copy down the thread IDs */ 394 395 max_ids = buf_size / sizeof(unative_t); 396 copied_ids = 0; 397 386 387 size_t max_ids = buf_size / sizeof(sysarg_t); 388 size_t copied_ids = 0; 389 size_t extra_ids = 0; 390 398 391 /* FIXME: make sure the thread isn't past debug shutdown... */ 392 link_t *cur; 399 393 for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) { 400 /* Do not write past end of buffer */ 401 if (copied_ids >= max_ids) break; 402 403 t = list_get_instance(cur, thread_t, th_link); 404 405 spinlock_lock(&t->lock); 406 flags = t->flags; 407 spinlock_unlock(&t->lock); 408 394 thread_t *thread = list_get_instance(cur, thread_t, th_link); 395 396 irq_spinlock_lock(&thread->lock, false); 397 int flags = thread->flags; 398 irq_spinlock_unlock(&thread->lock, false); 399 409 400 /* Not interested in kernel threads. */ 410 if ((flags & THREAD_FLAG_USPACE) != 0) { 401 if ((flags & THREAD_FLAG_USPACE) == 0) 402 continue; 403 404 if (copied_ids < max_ids) { 411 405 /* Using thread struct pointer as identification hash */ 412 tid = (unative_t) t; 413 id_buffer[copied_ids++] = tid; 414 } 415 } 416 417 spinlock_unlock(&TASK->lock); 418 interrupts_restore(ipl); 419 420 mutex_unlock(&TASK->udebug.lock); 421 406 id_buffer[copied_ids++] = (sysarg_t) thread; 407 } else 408 extra_ids++; 409 } 410 411 irq_spinlock_unlock(&TASK->lock, true); 412 413 mutex_unlock(&TASK->udebug.lock); 414 422 415 *buffer = id_buffer; 423 *n = copied_ids * sizeof(unative_t); 424 416 *stored = copied_ids * sizeof(sysarg_t); 417 *needed = (copied_ids + extra_ids) * sizeof(sysarg_t); 418 419 return 0; 420 } 421 422 /** Read task name. 423 * 424 * Returns task name as non-terminated string in a newly allocated buffer. 425 * Also returns the size of the data. 426 * 427 * @param data Place to store pointer to newly allocated block. 428 * @param data_size Place to store size of the data. 429 * 430 * @return EOK. 431 * 432 */ 433 int udebug_name_read(char **data, size_t *data_size) 434 { 435 size_t name_size = str_size(TASK->name) + 1; 436 437 *data = malloc(name_size, 0); 438 *data_size = name_size; 439 440 memcpy(*data, TASK->name, name_size); 441 425 442 return 0; 426 443 } … … 436 453 * this function will fail with an EINVAL error code. 437 454 * 438 * @param buffer The buffer for storing thread hashes. 439 */ 440 int udebug_args_read(thread_t *t, void **buffer) 441 { 442 int rc; 443 unative_t *arg_buffer; 444 455 * @param thread Thread where call arguments are to be read. 456 * @param buffer Place to store pointer to new buffer. 457 * 458 * @return EOK on success, ENOENT if @a t is invalid, EINVAL 459 * if thread state is not valid for this operation. 460 * 461 */ 462 int udebug_args_read(thread_t *thread, void **buffer) 463 { 445 464 /* Prepare a buffer to hold the arguments. */ 446 arg_buffer = malloc(6 * sizeof(unative_t), 0);447 465 sysarg_t *arg_buffer = malloc(6 * sizeof(sysarg_t), 0); 466 448 467 /* On success, this will lock t->udebug.lock. */ 449 rc = _thread_op_begin(t, false);450 if (rc != EOK) {468 int rc = _thread_op_begin(thread, false); 469 if (rc != EOK) 451 470 return rc; 452 } 453 471 454 472 /* Additionally we need to verify that we are inside a syscall. */ 455 if ( t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B&&456 t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E) {457 _thread_op_end(t );473 if ((thread->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B) && 474 (thread->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E)) { 475 _thread_op_end(thread); 458 476 return EINVAL; 459 477 } 460 478 461 479 /* Copy to a local buffer before releasing the lock. */ 462 memcpy(arg_buffer, t ->udebug.syscall_args, 6 * sizeof(unative_t));463 464 _thread_op_end(t );465 480 memcpy(arg_buffer, thread->udebug.syscall_args, 6 * sizeof(sysarg_t)); 481 482 _thread_op_end(thread); 483 466 484 *buffer = arg_buffer; 485 return 0; 486 } 487 488 /** Read the register state of the thread. 489 * 490 * The contents of the thread's istate structure are copied to a newly 491 * allocated buffer and a pointer to it is written to @a buffer. The size of 492 * the buffer will be sizeof(istate_t). 493 * 494 * Currently register state cannot be read if the thread is inside a system 495 * call (as opposed to an exception). This is an implementation limit. 496 * 497 * @param thread Thread whose state is to be read. 498 * @param buffer Place to store pointer to new buffer. 499 * 500 * @return EOK on success, ENOENT if @a t is invalid, EINVAL 501 * if thread is not in valid state, EBUSY if istate 502 * is not available. 503 * 504 */ 505 int udebug_regs_read(thread_t *thread, void **buffer) 506 { 507 /* Prepare a buffer to hold the data. */ 508 istate_t *state_buf = malloc(sizeof(istate_t), 0); 509 510 /* On success, this will lock t->udebug.lock */ 511 int rc = _thread_op_begin(thread, false); 512 if (rc != EOK) 513 return rc; 514 515 istate_t *state = thread->udebug.uspace_state; 516 if (state == NULL) { 517 _thread_op_end(thread); 518 return EBUSY; 519 } 520 521 /* Copy to the allocated buffer */ 522 memcpy(state_buf, state, sizeof(istate_t)); 523 524 _thread_op_end(thread); 525 526 *buffer = (void *) state_buf; 467 527 return 0; 468 528 } … … 474 534 * and a pointer to it is written into @a buffer. 475 535 * 476 * @param uspace_addr Address from where to start reading. 477 * @param n Number of bytes to read. 478 * @param buffer For storing a pointer to the allocated buffer. 479 */ 480 int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer) 481 { 482 void *data_buffer; 483 int rc; 484 536 * @param uspace_addr Address from where to start reading. 537 * @param n Number of bytes to read. 538 * @param buffer For storing a pointer to the allocated buffer. 539 * 540 */ 541 int udebug_mem_read(sysarg_t uspace_addr, size_t n, void **buffer) 542 { 485 543 /* Verify task state */ 486 544 mutex_lock(&TASK->udebug.lock); 487 545 488 546 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) { 489 547 mutex_unlock(&TASK->udebug.lock); 490 548 return EBUSY; 491 549 } 492 493 data_buffer = malloc(n, 0); 494 495 /* NOTE: this is not strictly from a syscall... but that shouldn't 496 * be a problem */ 497 rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n); 498 mutex_unlock(&TASK->udebug.lock); 499 500 if (rc != 0) return rc; 501 550 551 void *data_buffer = malloc(n, 0); 552 553 /* 554 * NOTE: this is not strictly from a syscall... but that shouldn't 555 * be a problem 556 * 557 */ 558 int rc = copy_from_uspace(data_buffer, (void *) uspace_addr, n); 559 mutex_unlock(&TASK->udebug.lock); 560 561 if (rc != 0) 562 return rc; 563 502 564 *buffer = data_buffer; 503 565 return 0;
Note:
See TracChangeset
for help on using the changeset viewer.