source: mainline/kernel/generic/src/udebug/udebug.c@ 384c488

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

Replace 'stop' in udebug_thread_t with 'go' for consistency with nomenclature.

  • Property mode set to 100644
File size: 14.8 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 hooks and data structure management.
36 *
37 * Udebug is an interface that makes userspace debuggers possible.
38 *
39 * Functions in this file are executed directly in each thread, which
40 * may or may not be the subject of debugging. The udebug_stoppable_begin/end()
41 * functions are also executed in the clock interrupt handler. To avoid
42 * deadlock, functions in this file are protected from the interrupt
43 * by locking the recursive lock THREAD->udebug.int_lock (just an atomic
44 * variable). This prevents udebug_stoppable_begin/end() from being
45 * executed in the interrupt handler (they are skipped).
46 *
47 * Functions in udebug_ops.c and udebug_ipc.c execute in different threads,
48 * so they needn't be protected from the (preemptible) interrupt-initiated
49 * code.
50 */
51
52#include <synch/waitq.h>
53#include <debug.h>
54#include <udebug/udebug.h>
55#include <errno.h>
56#include <arch.h>
57
58static inline void udebug_int_lock(void)
59{
60 atomic_inc(&THREAD->udebug.int_lock);
61}
62
63static inline void udebug_int_unlock(void)
64{
65 atomic_dec(&THREAD->udebug.int_lock);
66}
67
68/** Initialize udebug part of task structure.
69 *
70 * Called as part of task structure initialization.
71 * @param ut Pointer to the structure to initialize.
72 */
73void udebug_task_init(udebug_task_t *ut)
74{
75 mutex_initialize(&ut->lock, MUTEX_PASSIVE);
76 ut->dt_state = UDEBUG_TS_INACTIVE;
77 ut->begin_call = NULL;
78 ut->not_stoppable_count = 0;
79 ut->evmask = 0;
80}
81
82/** Initialize udebug part of thread structure.
83 *
84 * Called as part of thread structure initialization.
85 * @param ut Pointer to the structure to initialize.
86 */
87void udebug_thread_initialize(udebug_thread_t *ut)
88{
89 mutex_initialize(&ut->lock, MUTEX_PASSIVE);
90 waitq_initialize(&ut->go_wq);
91
92 /*
93 * At the beginning the thread is stoppable, so int_lock be set, too.
94 */
95 atomic_set(&ut->int_lock, 1);
96
97 ut->go_call = NULL;
98 ut->go = false;
99 ut->stoppable = true;
100 ut->debug_active = false;
101 ut->cur_event = 0; /* none */
102}
103
104/** Wait for a GO message.
105 *
106 * When a debugging event occurs in a thread or the thread is stopped,
107 * this function is called to block the thread until a GO message
108 * is received.
109 *
110 * @param wq The wait queue used by the thread to wait for GO messages.
111 */
112static void udebug_wait_for_go(waitq_t *wq)
113{
114 int rc;
115 ipl_t ipl;
116
117 ipl = waitq_sleep_prepare(wq);
118
119 wq->missed_wakeups = 0; /* Enforce blocking. */
120 rc = waitq_sleep_timeout_unsafe(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
121
122 waitq_sleep_finish(wq, rc, ipl);
123}
124
125/** Do a preliminary check that a debugging session is in progress.
126 *
127 * This only requires the THREAD->udebug.lock mutex (and not TASK->udebug.lock
128 * mutex). For an undebugged task, this will never block (while there could be
129 * collisions by different threads on the TASK mutex), thus improving SMP
130 * perormance for undebugged tasks.
131 *
132 * @return True if the thread was in a debugging session when the function
133 * checked, false otherwise.
134 */
135static bool udebug_thread_precheck(void)
136{
137 bool res;
138
139 mutex_lock(&THREAD->udebug.lock);
140 res = THREAD->udebug.debug_active;
141 mutex_unlock(&THREAD->udebug.lock);
142
143 return res;
144}
145
146/** Start of stoppable section.
147 *
148 * A stoppable section is a section of code where if the thread can be stoped. In other words,
149 * if a STOP operation is issued, the thread is guaranteed not to execute
150 * any userspace instructions until the thread is resumed.
151 *
152 * Having stoppable sections is better than having stopping points, since
153 * a thread can be stopped even when it is blocked indefinitely in a system
154 * call (whereas it would not reach any stopping point).
155 */
156void udebug_stoppable_begin(void)
157{
158 int nsc;
159 call_t *db_call, *go_call;
160
161 ASSERT(THREAD);
162 ASSERT(TASK);
163
164 udebug_int_lock();
165
166 /* Early check for undebugged tasks */
167 if (!udebug_thread_precheck()) {
168 udebug_int_unlock();
169 return;
170 }
171
172 mutex_lock(&TASK->udebug.lock);
173
174 nsc = --TASK->udebug.not_stoppable_count;
175
176 /* Lock order OK, THREAD->udebug.lock is after TASK->udebug.lock */
177 mutex_lock(&THREAD->udebug.lock);
178 ASSERT(THREAD->udebug.stoppable == false);
179 THREAD->udebug.stoppable = true;
180
181 if (TASK->udebug.dt_state == UDEBUG_TS_BEGINNING && nsc == 0) {
182 /*
183 * This was the last non-stoppable thread. Reply to
184 * DEBUG_BEGIN call.
185 */
186
187 db_call = TASK->udebug.begin_call;
188 ASSERT(db_call);
189
190 TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
191 TASK->udebug.begin_call = NULL;
192
193 IPC_SET_RETVAL(db_call->data, 0);
194 ipc_answer(&TASK->answerbox, db_call);
195
196 } else if (TASK->udebug.dt_state == UDEBUG_TS_ACTIVE) {
197 /*
198 * Active debugging session
199 */
200
201 if (THREAD->udebug.debug_active == true &&
202 THREAD->udebug.go == false) {
203 /*
204 * Thread was requested to stop - answer go call
205 */
206
207 /* Make sure nobody takes this call away from us */
208 go_call = THREAD->udebug.go_call;
209 THREAD->udebug.go_call = NULL;
210 ASSERT(go_call);
211
212 IPC_SET_RETVAL(go_call->data, 0);
213 IPC_SET_ARG1(go_call->data, UDEBUG_EVENT_STOP);
214
215 THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
216
217 ipc_answer(&TASK->answerbox, go_call);
218 }
219 }
220
221 mutex_unlock(&THREAD->udebug.lock);
222 mutex_unlock(&TASK->udebug.lock);
223}
224
225/** End of a stoppable section.
226 *
227 * This is the point where the thread will block if it is stopped.
228 * (As, by definition, a stopped thread must not leave its stoppable section).
229 */
230void udebug_stoppable_end(void)
231{
232 /* Early check for undebugged tasks */
233 if (!udebug_thread_precheck()) {
234 udebug_int_unlock();
235 return;
236 }
237
238restart:
239 mutex_lock(&TASK->udebug.lock);
240 mutex_lock(&THREAD->udebug.lock);
241
242 if (THREAD->udebug.debug_active &&
243 THREAD->udebug.go == false) {
244 TASK->udebug.begin_call = NULL;
245 mutex_unlock(&THREAD->udebug.lock);
246 mutex_unlock(&TASK->udebug.lock);
247
248 udebug_wait_for_go(&THREAD->udebug.go_wq);
249
250 goto restart;
251 /* Must try again - have to lose stoppability atomically. */
252 } else {
253 ++TASK->udebug.not_stoppable_count;
254 ASSERT(THREAD->udebug.stoppable == true);
255 THREAD->udebug.stoppable = false;
256
257 mutex_unlock(&THREAD->udebug.lock);
258 mutex_unlock(&TASK->udebug.lock);
259 }
260
261 udebug_int_unlock();
262}
263
264/** Upon being scheduled to run, check if the current thread should stop.
265 *
266 * This function is called from clock(). Preemption is enabled.
267 * interrupts are disabled, but since this is called after
268 * being scheduled-in, we can enable them, if we're careful enough
269 * not to allow arbitrary recursion or deadlock with the thread context.
270 */
271void udebug_before_thread_runs(void)
272{
273 ipl_t ipl;
274
275 return;
276 ASSERT(!PREEMPTION_DISABLED);
277
278 /*
279 * Prevent agains re-entering, such as when preempted inside this
280 * function.
281 */
282 if (atomic_get(&THREAD->udebug.int_lock) != 0)
283 return;
284
285 udebug_int_lock();
286
287 ipl = interrupts_enable();
288
289 /* Now we're free to do whatever we need (lock mutexes, sleep, etc.) */
290
291 /* Check if we're supposed to stop */
292 udebug_stoppable_begin();
293 udebug_stoppable_end();
294
295 interrupts_restore(ipl);
296
297 udebug_int_unlock();
298}
299
300/** Syscall event hook.
301 *
302 * Must be called before and after servicing a system call. This generates
303 * a SYSCALL_B or SYSCALL_E event, depending on the value of @a end_variant.
304 */
305void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3,
306 unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc,
307 bool end_variant)
308{
309 call_t *call;
310 udebug_event_t etype;
311
312 etype = end_variant ? UDEBUG_EVENT_SYSCALL_E : UDEBUG_EVENT_SYSCALL_B;
313
314 udebug_int_lock();
315
316 /* Early check for undebugged tasks */
317 if (!udebug_thread_precheck()) {
318 udebug_int_unlock();
319 return;
320 }
321
322 mutex_lock(&TASK->udebug.lock);
323 mutex_lock(&THREAD->udebug.lock);
324
325 /* Must only generate events when in debugging session and is go. */
326 if (THREAD->udebug.debug_active != true ||
327 THREAD->udebug.go == false ||
328 (TASK->udebug.evmask & UDEBUG_EVMASK(etype)) == 0) {
329 mutex_unlock(&THREAD->udebug.lock);
330 mutex_unlock(&TASK->udebug.lock);
331 return;
332 }
333
334 //printf("udebug_syscall_event\n");
335 call = THREAD->udebug.go_call;
336 THREAD->udebug.go_call = NULL;
337
338 IPC_SET_RETVAL(call->data, 0);
339 IPC_SET_ARG1(call->data, etype);
340 IPC_SET_ARG2(call->data, id);
341 IPC_SET_ARG3(call->data, rc);
342 //printf("udebug_syscall_event/ipc_answer\n");
343
344 THREAD->udebug.syscall_args[0] = a1;
345 THREAD->udebug.syscall_args[1] = a2;
346 THREAD->udebug.syscall_args[2] = a3;
347 THREAD->udebug.syscall_args[3] = a4;
348 THREAD->udebug.syscall_args[4] = a5;
349 THREAD->udebug.syscall_args[5] = a6;
350
351 /*
352 * Make sure udebug.go is false when going to sleep
353 * in case we get woken up by DEBUG_END. (At which
354 * point it must be back to the initial true value).
355 */
356 THREAD->udebug.go = false;
357 THREAD->udebug.cur_event = etype;
358
359 ipc_answer(&TASK->answerbox, call);
360
361 mutex_unlock(&THREAD->udebug.lock);
362 mutex_unlock(&TASK->udebug.lock);
363
364 udebug_wait_for_go(&THREAD->udebug.go_wq);
365
366 udebug_int_unlock();
367}
368
369/** Thread-creation event hook.
370 *
371 * Must be called when a new userspace thread is created in the debugged
372 * task. Generates a THREAD_B event.
373 *
374 * @param t Structure of the thread being created. Not locked, as the
375 * thread is not executing yet.
376 */
377void udebug_thread_b_event(struct thread *t)
378{
379 call_t *call;
380
381 udebug_int_lock();
382
383 mutex_lock(&TASK->udebug.lock);
384 mutex_lock(&THREAD->udebug.lock);
385
386 LOG("udebug_thread_b_event\n");
387 LOG("- check state\n");
388
389 /* Must only generate events when in debugging session */
390 if (THREAD->udebug.debug_active != true) {
391 LOG("- debug_active: %s, udebug.go: %s\n",
392 THREAD->udebug.debug_active ? "yes(+)" : "no(-)",
393 THREAD->udebug.go ? "yes(-)" : "no(+)");
394 mutex_unlock(&THREAD->udebug.lock);
395 mutex_unlock(&TASK->udebug.lock);
396 return;
397 }
398
399 LOG("- trigger event\n");
400
401 call = THREAD->udebug.go_call;
402 THREAD->udebug.go_call = NULL;
403 IPC_SET_RETVAL(call->data, 0);
404 IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_B);
405 IPC_SET_ARG2(call->data, (unative_t)t);
406
407 /*
408 * Make sure udebug.go is false when going to sleep
409 * in case we get woken up by DEBUG_END. (At which
410 * point it must be back to the initial true value).
411 */
412 THREAD->udebug.go = false;
413 THREAD->udebug.cur_event = UDEBUG_EVENT_THREAD_B;
414
415 ipc_answer(&TASK->answerbox, call);
416
417 mutex_unlock(&THREAD->udebug.lock);
418 mutex_unlock(&TASK->udebug.lock);
419
420 LOG("- sleep\n");
421 udebug_wait_for_go(&THREAD->udebug.go_wq);
422
423 udebug_int_unlock();
424}
425
426/** Thread-termination event hook.
427 *
428 * Must be called when the current thread is terminating.
429 * Generates a THREAD_E event.
430 */
431void udebug_thread_e_event(void)
432{
433 call_t *call;
434
435 udebug_int_lock();
436
437 mutex_lock(&TASK->udebug.lock);
438 mutex_lock(&THREAD->udebug.lock);
439
440 LOG("udebug_thread_e_event\n");
441 LOG("- check state\n");
442
443 /* Must only generate events when in debugging session. */
444 if (THREAD->udebug.debug_active != true) {
445/* printf("- debug_active: %s, udebug.go: %s\n",
446 THREAD->udebug.debug_active ? "yes(+)" : "no(-)",
447 THREAD->udebug.go ? "yes(-)" : "no(+)");*/
448 mutex_unlock(&THREAD->udebug.lock);
449 mutex_unlock(&TASK->udebug.lock);
450 return;
451 }
452
453 LOG("- trigger event\n");
454
455 call = THREAD->udebug.go_call;
456 THREAD->udebug.go_call = NULL;
457 IPC_SET_RETVAL(call->data, 0);
458 IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_E);
459
460 /* Prevent any further debug activity in thread. */
461 THREAD->udebug.debug_active = false;
462 THREAD->udebug.cur_event = 0; /* none */
463 THREAD->udebug.go = false; /* set to initial value */
464
465 ipc_answer(&TASK->answerbox, call);
466
467 mutex_unlock(&THREAD->udebug.lock);
468 mutex_unlock(&TASK->udebug.lock);
469
470 /* Leave int_lock enabled. */
471 /* This event does not sleep - debugging has finished in this thread. */
472}
473
474/**
475 * Terminate task debugging session.
476 *
477 * Gracefully terminates the debugging session for a task. If the debugger
478 * is still waiting for events on some threads, it will receive a
479 * FINISHED event for each of them.
480 *
481 * @param ta Task structure. ta->udebug.lock must be already locked.
482 * @return Zero on success or negative error code.
483 */
484int udebug_task_cleanup(struct task *ta)
485{
486 thread_t *t;
487 link_t *cur;
488 int flags;
489 ipl_t ipl;
490
491 LOG("udebug_task_cleanup()\n");
492 LOG("task %" PRIu64 "\n", ta->taskid);
493
494 udebug_int_lock();
495
496 if (ta->udebug.dt_state != UDEBUG_TS_BEGINNING &&
497 ta->udebug.dt_state != UDEBUG_TS_ACTIVE) {
498 LOG("udebug_task_cleanup(): task not being debugged\n");
499 return EINVAL;
500 }
501
502 /* Finish debugging of all userspace threads */
503 for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
504 t = list_get_instance(cur, thread_t, th_link);
505
506 mutex_lock(&t->udebug.lock);
507
508 ipl = interrupts_disable();
509 spinlock_lock(&t->lock);
510
511 flags = t->flags;
512
513 spinlock_unlock(&t->lock);
514 interrupts_restore(ipl);
515
516 /* Only process userspace threads. */
517 if ((flags & THREAD_FLAG_USPACE) != 0) {
518 /* Prevent any further debug activity in thread. */
519 t->udebug.debug_active = false;
520 t->udebug.cur_event = 0; /* none */
521
522 /* Is the thread still go? */
523 if (t->udebug.go == true) {
524 /*
525 * Yes, so clear go. As debug_active == false,
526 * this doesn't affect anything.
527 */
528 t->udebug.go = false;
529
530 /* Answer GO call */
531 LOG("answer GO call with EVENT_FINISHED\n");
532 IPC_SET_RETVAL(t->udebug.go_call->data, 0);
533 IPC_SET_ARG1(t->udebug.go_call->data,
534 UDEBUG_EVENT_FINISHED);
535
536 ipc_answer(&ta->answerbox, t->udebug.go_call);
537 t->udebug.go_call = NULL;
538 } else {
539 /*
540 * Debug_stop is already at initial value.
541 * Yet this means the thread needs waking up.
542 */
543
544 /*
545 * t's lock must not be held when calling
546 * waitq_wakeup.
547 */
548 waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
549 }
550 }
551 mutex_unlock(&t->udebug.lock);
552 }
553
554 ta->udebug.dt_state = UDEBUG_TS_INACTIVE;
555 ta->udebug.debugger = NULL;
556
557 udebug_int_unlock();
558
559 return 0;
560}
561
562
563/** @}
564 */
Note: See TracBrowser for help on using the repository browser.