source: mainline/kernel/generic/src/udebug/udebug.c@ 9a1b20c

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

Merge syscall tracer (trace) and relevant part of udebug interface from tracing to trunk.

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