source: mainline/kernel/generic/src/udebug/udebug_ops.c@ bf1fb9f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bf1fb9f was ae5aa90, checked in by Jiri Svoboda <jirik.svoboda@…>, 16 years ago

Clean up various log messages, mostly udebug.

  • Property mode set to 100644
File size: 13.3 KB
Line 
1/*
2 * Copyright (c) 2008 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup generic
30 * @{
31 */
32
33/**
34 * @file
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.
40 */
41
42#include <debug.h>
43#include <proc/task.h>
44#include <proc/thread.h>
45#include <arch.h>
46#include <errno.h>
47#include <print.h>
48#include <syscall/copy.h>
49#include <ipc/ipc.h>
50#include <udebug/udebug.h>
51#include <udebug/udebug_ops.h>
52
53/**
54 * Prepare a thread for a debugging operation.
55 *
56 * Simply put, return thread t with t->udebug.lock held,
57 * but only if it verifies all conditions.
58 *
59 * Specifically, verifies that thread t exists, is a userspace thread,
60 * and belongs to the current task (TASK). Verifies, that the thread
61 * is (or is not) go according to being_go (typically false).
62 * It also locks t->udebug.lock, making sure that t->udebug.active
63 * is true - that the thread is in a valid debugging session.
64 *
65 * With this verified and the t->udebug.lock mutex held, it is ensured
66 * that the thread cannot leave the debugging session, let alone cease
67 * to exist.
68 *
69 * In this function, holding the TASK->udebug.lock mutex prevents the
70 * thread from leaving the debugging session, while relaxing from
71 * the t->lock spinlock to the t->udebug.lock mutex.
72 *
73 * @param t Pointer, need not at all be valid.
74 * @param being_go Required thread state.
75 *
76 * Returns EOK if all went well, or an error code otherwise.
77 */
78static 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
87 /* 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);
94 mutex_unlock(&TASK->udebug.lock);
95 return ENOENT;
96 }
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) {
104 /* It's not, deny its existence */
105 spinlock_unlock(&t->lock);
106 interrupts_restore(ipl);
107 mutex_unlock(&TASK->udebug.lock);
108 return ENOENT;
109 }
110
111 /* Verify debugging state. */
112 if (t->udebug.active != true) {
113 /* Not in debugging session or undesired GO state */
114 spinlock_unlock(&t->lock);
115 interrupts_restore(ipl);
116 mutex_unlock(&TASK->udebug.lock);
117 return ENOENT;
118 }
119
120 /*
121 * Since the thread has active == true, TASK->udebug.lock
122 * is enough to ensure its existence and that active remains
123 * true.
124 */
125 spinlock_unlock(&t->lock);
126 interrupts_restore(ipl);
127
128 /* Only mutex TASK->udebug.lock left. */
129
130 /* Now verify that the thread belongs to the current task. */
131 if (t->task != TASK) {
132 /* No such thread belonging this task*/
133 mutex_unlock(&TASK->udebug.lock);
134 return ENOENT;
135 }
136
137 /*
138 * Now we need to grab the thread's debug lock for synchronization
139 * of the threads stoppability/stop state.
140 */
141 mutex_lock(&t->udebug.lock);
142
143 /* The big task mutex is no longer needed. */
144 mutex_unlock(&TASK->udebug.lock);
145
146 if (t->udebug.go != being_go) {
147 /* Not in debugging session or undesired GO state. */
148 mutex_unlock(&t->udebug.lock);
149 return EINVAL;
150 }
151
152 /* Only t->udebug.lock left. */
153
154 return EOK; /* All went well. */
155}
156
157/** End debugging operation on a thread. */
158static void _thread_op_end(thread_t *t)
159{
160 mutex_unlock(&t->udebug.lock);
161}
162
163/** Begin debugging the current task.
164 *
165 * Initiates a debugging session for the current task (and its threads).
166 * When the debugging session has started a reply will be sent to the
167 * UDEBUG_BEGIN call. This may happen immediately in this function if
168 * all the threads in this task are stoppable at the moment and in this
169 * case the function returns 1.
170 *
171 * Otherwise the function returns 0 and the reply will be sent as soon as
172 * all the threads become stoppable (i.e. they can be considered stopped).
173 *
174 * @param call The BEGIN call we are servicing.
175 * @return 0 (OK, but not done yet), 1 (done) or negative error code.
176 */
177int udebug_begin(call_t *call)
178{
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
187 if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) {
188 mutex_unlock(&TASK->udebug.lock);
189 return EBUSY;
190 }
191
192 TASK->udebug.dt_state = UDEBUG_TS_BEGINNING;
193 TASK->udebug.begin_call = call;
194 TASK->udebug.debugger = call->sender;
195
196 if (TASK->udebug.not_stoppable_count == 0) {
197 TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
198 TASK->udebug.begin_call = NULL;
199 reply = 1; /* immediate reply */
200 } else {
201 reply = 0; /* no reply */
202 }
203
204 /* Set udebug.active on all of the task's userspace threads. */
205
206 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
215 mutex_unlock(&TASK->udebug.lock);
216 return reply;
217}
218
219/** Finish debugging the current task.
220 *
221 * Closes the debugging session for the current task.
222 * @return Zero on success or negative error code.
223 */
224int udebug_end(void)
225{
226 int rc;
227
228 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
234 return rc;
235}
236
237/** Set the event mask.
238 *
239 * Sets the event mask that determines which events are enabled.
240 *
241 * @param mask Or combination of events that should be enabled.
242 * @return Zero on success or negative error code.
243 */
244int udebug_set_evmask(udebug_evmask_t mask)
245{
246 LOG("mask = 0x%x", mask);
247
248 mutex_lock(&TASK->udebug.lock);
249
250 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
251 mutex_unlock(&TASK->udebug.lock);
252 return EINVAL;
253 }
254
255 TASK->udebug.evmask = mask;
256 mutex_unlock(&TASK->udebug.lock);
257
258 return 0;
259}
260
261/** Give thread GO.
262 *
263 * Upon recieving a go message, the thread is given GO. Being GO
264 * means the thread is allowed to execute userspace code (until
265 * a debugging event or STOP occurs, at which point the thread loses GO.
266 *
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 */
270int 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) {
277 return rc;
278 }
279
280 t->udebug.go_call = call;
281 t->udebug.go = true;
282 t->udebug.cur_event = 0; /* none */
283
284 /*
285 * Neither t's lock nor threads_lock may be held during wakeup.
286 */
287 waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
288
289 _thread_op_end(t);
290
291 return 0;
292}
293
294/** Stop a thread (i.e. take its GO away)
295 *
296 * Generates a STOP event as soon as the thread becomes stoppable (i.e.
297 * can be considered stopped).
298 *
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 */
302int udebug_stop(thread_t *t, call_t *call)
303{
304 int rc;
305
306 LOG("udebug_stop()");
307
308 /*
309 * On success, this will lock t->udebug.lock. Note that this makes sure
310 * the thread is not stopped.
311 */
312 rc = _thread_op_begin(t, true);
313 if (rc != EOK) {
314 return rc;
315 }
316
317 /* Take GO away from the thread. */
318 t->udebug.go = false;
319
320 if (t->udebug.stoppable != true) {
321 /* Answer will be sent when the thread becomes stoppable. */
322 _thread_op_end(t);
323 return 0;
324 }
325
326 /*
327 * Answer GO call.
328 */
329
330 /* Make sure nobody takes this call away from us. */
331 call = t->udebug.go_call;
332 t->udebug.go_call = NULL;
333
334 IPC_SET_RETVAL(call->data, 0);
335 IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP);
336
337 THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
338
339 _thread_op_end(t);
340
341 mutex_lock(&TASK->udebug.lock);
342 ipc_answer(&TASK->answerbox, call);
343 mutex_unlock(&TASK->udebug.lock);
344
345 return 0;
346}
347
348/** Read the list of userspace threads in the current task.
349 *
350 * The list takes the form of a sequence of thread hashes (i.e. the pointers
351 * to thread structures). A buffer of size @a buf_size is allocated and
352 * a pointer to it written to @a buffer. The sequence of hashes is written
353 * into this buffer.
354 *
355 * 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.
358 *
359 * The rationale for having @a buf_size is that this function is only
360 * used for servicing the THREAD_READ message, which always specifies
361 * a maximum size for the userspace buffer.
362 *
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 */
367int 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
378 LOG("udebug_thread_read()");
379
380 /* Allocate a buffer to hold thread IDs */
381 id_buffer = malloc(buf_size, 0);
382
383 mutex_lock(&TASK->udebug.lock);
384
385 /* Verify task state */
386 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
387 mutex_unlock(&TASK->udebug.lock);
388 return EINVAL;
389 }
390
391 ipl = interrupts_disable();
392 spinlock_lock(&TASK->lock);
393 /* Copy down the thread IDs */
394
395 max_ids = buf_size / sizeof(unative_t);
396 copied_ids = 0;
397
398 /* FIXME: make sure the thread isn't past debug shutdown... */
399 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
409 /* Not interested in kernel threads. */
410 if ((flags & THREAD_FLAG_USPACE) != 0) {
411 /* 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
422 *buffer = id_buffer;
423 *n = copied_ids * sizeof(unative_t);
424
425 return 0;
426}
427
428/** Read the arguments of a system call.
429 *
430 * The arguments of the system call being being executed are copied
431 * to an allocated buffer and a pointer to it is written to @a buffer.
432 * The size of the buffer is exactly such that it can hold the maximum number
433 * of system-call arguments.
434 *
435 * Unless the thread is currently blocked in a SYSCALL_B or SYSCALL_E event,
436 * this function will fail with an EINVAL error code.
437 *
438 * @param buffer The buffer for storing thread hashes.
439 */
440int udebug_args_read(thread_t *t, void **buffer)
441{
442 int rc;
443 unative_t *arg_buffer;
444
445 /* Prepare a buffer to hold the arguments. */
446 arg_buffer = malloc(6 * sizeof(unative_t), 0);
447
448 /* On success, this will lock t->udebug.lock. */
449 rc = _thread_op_begin(t, false);
450 if (rc != EOK) {
451 return rc;
452 }
453
454 /* 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);
458 return EINVAL;
459 }
460
461 /* 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
466 *buffer = arg_buffer;
467 return 0;
468}
469
470/** Read the memory of the debugged task.
471 *
472 * Reads @a n bytes from the address space of the debugged task, starting
473 * from @a uspace_addr. The bytes are copied into an allocated buffer
474 * and a pointer to it is written into @a buffer.
475 *
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 */
480int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer)
481{
482 void *data_buffer;
483 int rc;
484
485 /* Verify task state */
486 mutex_lock(&TASK->udebug.lock);
487
488 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
489 mutex_unlock(&TASK->udebug.lock);
490 return EBUSY;
491 }
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
502 *buffer = data_buffer;
503 return 0;
504}
505
506/** @}
507 */
Note: See TracBrowser for help on using the repository browser.