Rev 3424 | Rev 3606 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 3424 | Rev 3471 | ||
|---|---|---|---|
| Line 31... | Line 31... | ||
| 31 | */ |
31 | */ |
| 32 | 32 | ||
| 33 | /** |
33 | /** |
| 34 | * @file |
34 | * @file |
| 35 | * @brief Udebug operations. |
35 | * @brief Udebug operations. |
| - | 36 | * |
|
| - | 37 | * Udebug operations on tasks and threads are implemented here. The |
|
| - | 38 | * functions defined here are called from the udebug_ipc module |
|
| - | 39 | * when servicing udebug IPC messages. |
|
| 36 | */ |
40 | */ |
| 37 | 41 | ||
| 38 | #include <print.h> |
42 | #include <debug.h> |
| 39 | #include <proc/task.h> |
43 | #include <proc/task.h> |
| 40 | #include <proc/thread.h> |
44 | #include <proc/thread.h> |
| 41 | #include <arch.h> |
45 | #include <arch.h> |
| 42 | #include <errno.h> |
46 | #include <errno.h> |
| 43 | #include <syscall/copy.h> |
47 | #include <syscall/copy.h> |
| Line 63... | Line 67... | ||
| 63 | * |
67 | * |
| 64 | * In this function, holding the TASK->udebug.lock mutex prevents the |
68 | * In this function, holding the TASK->udebug.lock mutex prevents the |
| 65 | * thread from leaving the debugging session, while relaxing from |
69 | * thread from leaving the debugging session, while relaxing from |
| 66 | * the t->lock spinlock to the t->udebug.lock mutex. |
70 | * the t->lock spinlock to the t->udebug.lock mutex. |
| 67 | * |
71 | * |
| - | 72 | * @param t Pointer, need not at all be valid. |
|
| - | 73 | * @param having_go Required thread state. |
|
| - | 74 | * |
|
| 68 | * Returns EOK if all went well, or an error code otherwise. |
75 | * Returns EOK if all went well, or an error code otherwise. |
| 69 | */ |
76 | */ |
| 70 | static int _thread_op_begin(thread_t *t, bool having_go) |
77 | static int _thread_op_begin(thread_t *t, bool having_go) |
| 71 | { |
78 | { |
| 72 | task_id_t taskid; |
79 | task_id_t taskid; |
| Line 144... | Line 151... | ||
| 144 | /* Only t->udebug.lock left */ |
151 | /* Only t->udebug.lock left */ |
| 145 | 152 | ||
| 146 | return EOK; /* All went well */ |
153 | return EOK; /* All went well */ |
| 147 | } |
154 | } |
| 148 | 155 | ||
| 149 | - | ||
| - | 156 | /** End debugging operation on a thread. */ |
|
| 150 | static void _thread_op_end(thread_t *t) |
157 | static void _thread_op_end(thread_t *t) |
| 151 | { |
158 | { |
| 152 | mutex_unlock(&t->udebug.lock); |
159 | mutex_unlock(&t->udebug.lock); |
| 153 | } |
160 | } |
| 154 | 161 | ||
| - | 162 | /** Begin debugging the current task. |
|
| 155 | /** |
163 | * |
| - | 164 | * Initiates a debugging session for the current task (and its threads). |
|
| - | 165 | * When the debugging session has started a reply will be sent to the |
|
| - | 166 | * UDEBUG_BEGIN call. This may happen immediately in this function if |
|
| - | 167 | * all the threads in this task are stoppable at the moment and in this |
|
| - | 168 | * case the function returns 1. |
|
| - | 169 | * |
|
| - | 170 | * Otherwise the function returns 0 and the reply will be sent as soon as |
|
| - | 171 | * all the threads become stoppable (i.e. they can be considered stopped). |
|
| - | 172 | * |
|
| - | 173 | * @param call The BEGIN call we are servicing. |
|
| 156 | * \return 0 (ok, but not done yet), 1 (done) or negative error code. |
174 | * @return 0 (OK, but not done yet), 1 (done) or negative error code. |
| 157 | */ |
175 | */ |
| 158 | int udebug_begin(call_t *call) |
176 | int udebug_begin(call_t *call) |
| 159 | { |
177 | { |
| 160 | int reply; |
178 | int reply; |
| 161 | 179 | ||
| 162 | thread_t *t; |
180 | thread_t *t; |
| 163 | link_t *cur; |
181 | link_t *cur; |
| 164 | 182 | ||
| 165 | printf("udebug_begin()\n"); |
183 | LOG("udebug_begin()\n"); |
| 166 | 184 | ||
| 167 | mutex_lock(&TASK->udebug.lock); |
185 | mutex_lock(&TASK->udebug.lock); |
| 168 | printf("debugging task %llu\n", TASK->taskid); |
186 | LOG("debugging task %llu\n", TASK->taskid); |
| 169 | 187 | ||
| 170 | if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) { |
188 | if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) { |
| 171 | mutex_unlock(&TASK->udebug.lock); |
189 | mutex_unlock(&TASK->udebug.lock); |
| 172 | printf("udebug_begin(): busy error\n"); |
190 | LOG("udebug_begin(): busy error\n"); |
| 173 | 191 | ||
| 174 | return EBUSY; |
192 | return EBUSY; |
| 175 | } |
193 | } |
| 176 | 194 | ||
| 177 | TASK->udebug.dt_state = UDEBUG_TS_BEGINNING; |
195 | TASK->udebug.dt_state = UDEBUG_TS_BEGINNING; |
| Line 197... | Line 215... | ||
| 197 | mutex_unlock(&t->udebug.lock); |
215 | mutex_unlock(&t->udebug.lock); |
| 198 | } |
216 | } |
| 199 | 217 | ||
| 200 | mutex_unlock(&TASK->udebug.lock); |
218 | mutex_unlock(&TASK->udebug.lock); |
| 201 | 219 | ||
| 202 | printf("udebug_begin() done (%s)\n", |
220 | LOG("udebug_begin() done (%s)\n", |
| 203 | reply ? "reply" : "stoppability wait"); |
221 | reply ? "reply" : "stoppability wait"); |
| 204 | 222 | ||
| 205 | return reply; |
223 | return reply; |
| 206 | } |
224 | } |
| 207 | 225 | ||
| - | 226 | /** Finish debugging the current task. |
|
| - | 227 | * |
|
| - | 228 | * Closes the debugging session for the current task. |
|
| - | 229 | * @return Zero on success or negative error code. |
|
| - | 230 | */ |
|
| 208 | int udebug_end(void) |
231 | int udebug_end(void) |
| 209 | { |
232 | { |
| 210 | int rc; |
233 | int rc; |
| 211 | 234 | ||
| 212 | printf("udebug_end()\n"); |
235 | LOG("udebug_end()\n"); |
| 213 | 236 | ||
| 214 | mutex_lock(&TASK->udebug.lock); |
237 | mutex_lock(&TASK->udebug.lock); |
| 215 | printf("task %llu\n", TASK->taskid); |
238 | LOG("task %" PRIu64 "\n", TASK->taskid); |
| 216 | 239 | ||
| 217 | rc = udebug_task_cleanup(TASK); |
240 | rc = udebug_task_cleanup(TASK); |
| 218 | 241 | ||
| 219 | mutex_unlock(&TASK->udebug.lock); |
242 | mutex_unlock(&TASK->udebug.lock); |
| 220 | 243 | ||
| 221 | return rc; |
244 | return rc; |
| 222 | } |
245 | } |
| 223 | 246 | ||
| - | 247 | /** Set the event mask. |
|
| - | 248 | * |
|
| - | 249 | * Sets the event mask that determines which events are enabled. |
|
| - | 250 | * |
|
| - | 251 | * @param mask Or combination of events that should be enabled. |
|
| - | 252 | * @return Zero on success or negative error code. |
|
| - | 253 | */ |
|
| 224 | int udebug_set_evmask(udebug_evmask_t mask) |
254 | int udebug_set_evmask(udebug_evmask_t mask) |
| 225 | { |
255 | { |
| 226 | printf("udebug_set_mask()\n"); |
256 | LOG("udebug_set_mask()\n"); |
| 227 | - | ||
| 228 | printf("debugging task %llu\n", TASK->taskid); |
- | |
| 229 | 257 | ||
| 230 | mutex_lock(&TASK->udebug.lock); |
258 | mutex_lock(&TASK->udebug.lock); |
| 231 | 259 | ||
| 232 | if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) { |
260 | if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) { |
| 233 | mutex_unlock(&TASK->udebug.lock); |
261 | mutex_unlock(&TASK->udebug.lock); |
| 234 | printf("udebug_set_mask(): not active debuging session\n"); |
262 | LOG("udebug_set_mask(): not active debuging session\n"); |
| 235 | 263 | ||
| 236 | return EINVAL; |
264 | return EINVAL; |
| 237 | } |
265 | } |
| 238 | 266 | ||
| 239 | TASK->udebug.evmask = mask; |
267 | TASK->udebug.evmask = mask; |
| Line 241... | Line 269... | ||
| 241 | mutex_unlock(&TASK->udebug.lock); |
269 | mutex_unlock(&TASK->udebug.lock); |
| 242 | 270 | ||
| 243 | return 0; |
271 | return 0; |
| 244 | } |
272 | } |
| 245 | 273 | ||
| - | 274 | /** Give thread GO. |
|
| - | 275 | * |
|
| - | 276 | * Upon recieving a go message, the thread is given GO. Having GO |
|
| - | 277 | * means the thread is allowed to execute userspace code (until |
|
| - | 278 | * a debugging event or STOP occurs, at which point the thread loses GO. |
|
| 246 | 279 | * |
|
| - | 280 | * @param t The thread to operate on (unlocked and need not be valid). |
|
| - | 281 | * @param call The GO call that we are servicing. |
|
| - | 282 | */ |
|
| 247 | int udebug_go(thread_t *t, call_t *call) |
283 | int udebug_go(thread_t *t, call_t *call) |
| 248 | { |
284 | { |
| 249 | int rc; |
285 | int rc; |
| 250 | 286 | ||
| 251 | // printf("udebug_go()\n"); |
- | |
| 252 | - | ||
| 253 | /* On success, this will lock t->udebug.lock */ |
287 | /* On success, this will lock t->udebug.lock */ |
| 254 | rc = _thread_op_begin(t, false); |
288 | rc = _thread_op_begin(t, false); |
| 255 | if (rc != EOK) { |
289 | if (rc != EOK) { |
| 256 | return rc; |
290 | return rc; |
| 257 | } |
291 | } |
| Line 268... | Line 302... | ||
| 268 | _thread_op_end(t); |
302 | _thread_op_end(t); |
| 269 | 303 | ||
| 270 | return 0; |
304 | return 0; |
| 271 | } |
305 | } |
| 272 | 306 | ||
| - | 307 | /** Stop a thread (i.e. take its GO away) |
|
| - | 308 | * |
|
| - | 309 | * Generates a STOP event as soon as the thread becomes stoppable (i.e. |
|
| - | 310 | * can be considered stopped). |
|
| - | 311 | * |
|
| - | 312 | * @param t The thread to operate on (unlocked and need not be valid). |
|
| - | 313 | * @param call The GO call that we are servicing. |
|
| - | 314 | */ |
|
| 273 | int udebug_stop(thread_t *t, call_t *call) |
315 | int udebug_stop(thread_t *t, call_t *call) |
| 274 | { |
316 | { |
| 275 | int rc; |
317 | int rc; |
| 276 | 318 | ||
| 277 | printf("udebug_stop()\n"); |
319 | LOG("udebug_stop()\n"); |
| 278 | mutex_lock(&TASK->udebug.lock); |
320 | mutex_lock(&TASK->udebug.lock); |
| 279 | 321 | ||
| 280 | /* |
322 | /* |
| 281 | * On success, this will lock t->udebug.lock. Note that this makes sure |
323 | * On success, this will lock t->udebug.lock. Note that this makes sure |
| 282 | * the thread is not stopped. |
324 | * the thread is not stopped. |
| Line 296... | Line 338... | ||
| 296 | } |
338 | } |
| 297 | 339 | ||
| 298 | /* |
340 | /* |
| 299 | * Answer GO call |
341 | * Answer GO call |
| 300 | */ |
342 | */ |
| 301 | printf("udebug_stop - answering go call\n"); |
343 | LOG("udebug_stop - answering go call\n"); |
| 302 | 344 | ||
| 303 | /* Make sure nobody takes this call away from us */ |
345 | /* Make sure nobody takes this call away from us */ |
| 304 | call = t->udebug.go_call; |
346 | call = t->udebug.go_call; |
| 305 | t->udebug.go_call = NULL; |
347 | t->udebug.go_call = NULL; |
| 306 | 348 | ||
| 307 | IPC_SET_RETVAL(call->data, 0); |
349 | IPC_SET_RETVAL(call->data, 0); |
| 308 | IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP); |
350 | IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP); |
| 309 | printf("udebug_stop/ipc_answer\n"); |
351 | LOG("udebug_stop/ipc_answer\n"); |
| 310 | 352 | ||
| 311 | THREAD->udebug.cur_event = UDEBUG_EVENT_STOP; |
353 | THREAD->udebug.cur_event = UDEBUG_EVENT_STOP; |
| 312 | 354 | ||
| 313 | _thread_op_end(t); |
355 | _thread_op_end(t); |
| 314 | 356 | ||
| 315 | ipc_answer(&TASK->answerbox, call); |
357 | ipc_answer(&TASK->answerbox, call); |
| 316 | mutex_unlock(&TASK->udebug.lock); |
358 | mutex_unlock(&TASK->udebug.lock); |
| 317 | 359 | ||
| 318 | printf("udebog_stop/done\n"); |
360 | LOG("udebog_stop/done\n"); |
| 319 | return 0; |
361 | return 0; |
| 320 | } |
362 | } |
| 321 | 363 | ||
| - | 364 | /** Read the list of userspace threads in the current task. |
|
| - | 365 | * |
|
| - | 366 | * The list takes the form of a sequence of thread hashes (i.e. the pointers |
|
| - | 367 | * to thread structures). A buffer of size @a buf_size is allocated and |
|
| - | 368 | * a pointer to it written to @a buffer. The sequence of hashes is written |
|
| - | 369 | * into this buffer. |
|
| - | 370 | * |
|
| - | 371 | * If the sequence is longer than @a buf_size bytes, only as much hashes |
|
| - | 372 | * as can fit are copied. The number of thread hashes copied is stored |
|
| - | 373 | * in @a n. |
|
| - | 374 | * |
|
| - | 375 | * The rationale for having @a buf_size is that this function is only |
|
| - | 376 | * used for servicing the THREAD_READ message, which always specifies |
|
| - | 377 | * a maximum size for the userspace buffer. |
|
| - | 378 | * |
|
| - | 379 | * @param buffer The buffer for storing thread hashes. |
|
| - | 380 | * @param buf_size Buffer size in bytes. |
|
| - | 381 | * @param n The actual number of hashes copied will be stored here. |
|
| - | 382 | */ |
|
| 322 | int udebug_thread_read(void **buffer, size_t buf_size, size_t *n) |
383 | int udebug_thread_read(void **buffer, size_t buf_size, size_t *n) |
| 323 | { |
384 | { |
| 324 | thread_t *t; |
385 | thread_t *t; |
| 325 | link_t *cur; |
386 | link_t *cur; |
| 326 | unative_t tid; |
387 | unative_t tid; |
| Line 328... | Line 389... | ||
| 328 | ipl_t ipl; |
389 | ipl_t ipl; |
| 329 | unative_t *id_buffer; |
390 | unative_t *id_buffer; |
| 330 | int flags; |
391 | int flags; |
| 331 | size_t max_ids; |
392 | size_t max_ids; |
| 332 | 393 | ||
| 333 | printf("udebug_thread_read()\n"); |
394 | LOG("udebug_thread_read()\n"); |
| 334 | 395 | ||
| 335 | /* Allocate a buffer to hold thread IDs */ |
396 | /* Allocate a buffer to hold thread IDs */ |
| 336 | id_buffer = malloc(buf_size, 0); |
397 | id_buffer = malloc(buf_size, 0); |
| 337 | 398 | ||
| 338 | mutex_lock(&TASK->udebug.lock); |
399 | mutex_lock(&TASK->udebug.lock); |
| Line 378... | Line 439... | ||
| 378 | *n = copied_ids * sizeof(unative_t); |
439 | *n = copied_ids * sizeof(unative_t); |
| 379 | 440 | ||
| 380 | return 0; |
441 | return 0; |
| 381 | } |
442 | } |
| 382 | 443 | ||
| - | 444 | /** Read the arguments of a system call. |
|
| - | 445 | * |
|
| - | 446 | * The arguments of the system call being being executed are copied |
|
| - | 447 | * to an allocated buffer and a pointer to it is written to @a buffer. |
|
| - | 448 | * The size of the buffer is exactly such that it can hold the maximum number |
|
| - | 449 | * of system-call arguments. |
|
| - | 450 | * |
|
| - | 451 | * Unless the thread is currently blocked in a SYSCALL_B or SYSCALL_E event, |
|
| - | 452 | * this function will fail with an EINVAL error code. |
|
| - | 453 | * |
|
| - | 454 | * @param buffer The buffer for storing thread hashes. |
|
| - | 455 | */ |
|
| 383 | int udebug_args_read(thread_t *t, void **buffer) |
456 | int udebug_args_read(thread_t *t, void **buffer) |
| 384 | { |
457 | { |
| 385 | int rc; |
458 | int rc; |
| 386 | unative_t *arg_buffer; |
459 | unative_t *arg_buffer; |
| 387 | 460 | ||
| 388 | // printf("udebug_args_read()\n"); |
- | |
| 389 | - | ||
| 390 | /* Prepare a buffer to hold the arguments */ |
461 | /* Prepare a buffer to hold the arguments */ |
| 391 | arg_buffer = malloc(6 * sizeof(unative_t), 0); |
462 | arg_buffer = malloc(6 * sizeof(unative_t), 0); |
| 392 | 463 | ||
| 393 | /* On success, this will lock t->udebug.lock */ |
464 | /* On success, this will lock t->udebug.lock */ |
| 394 | rc = _thread_op_begin(t, false); |
465 | rc = _thread_op_begin(t, false); |
| Line 468... | Line 539... | ||
| 468 | _thread_op_end(t); |
539 | _thread_op_end(t); |
| 469 | 540 | ||
| 470 | return 0; |
541 | return 0; |
| 471 | } |
542 | } |
| 472 | 543 | ||
| - | 544 | /** Read the memory of the debugged task. |
|
| - | 545 | * |
|
| - | 546 | * Reads @a n bytes from the address space of the debugged task, starting |
|
| - | 547 | * from @a uspace_addr. The bytes are copied into an allocated buffer |
|
| - | 548 | * and a pointer to it is written into @a buffer. |
|
| 473 | 549 | * |
|
| - | 550 | * @param uspace_addr Address from where to start reading. |
|
| - | 551 | * @param n Number of bytes to read. |
|
| - | 552 | * @param buffer For storing a pointer to the allocated buffer. |
|
| - | 553 | */ |
|
| 474 | int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer) |
554 | int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer) |
| 475 | { |
555 | { |
| 476 | void *data_buffer; |
556 | void *data_buffer; |
| 477 | int rc; |
557 | int rc; |
| 478 | 558 | ||
| Line 484... | Line 564... | ||
| 484 | return EBUSY; |
564 | return EBUSY; |
| 485 | } |
565 | } |
| 486 | 566 | ||
| 487 | data_buffer = malloc(n, 0); |
567 | data_buffer = malloc(n, 0); |
| 488 | 568 | ||
| 489 | // printf("udebug_mem_read: src=%u, size=%u\n", uspace_addr, n); |
- | |
| 490 | - | ||
| 491 | /* NOTE: this is not strictly from a syscall... but that shouldn't |
569 | /* NOTE: this is not strictly from a syscall... but that shouldn't |
| 492 | * be a problem */ |
570 | * be a problem */ |
| 493 | rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n); |
571 | rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n); |
| 494 | mutex_unlock(&TASK->udebug.lock); |
572 | mutex_unlock(&TASK->udebug.lock); |
| 495 | 573 | ||