source: mainline/kernel/generic/src/udebug/udebug.c@ ca61894

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ca61894 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 13.4 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
40#include <assert.h>
41#include <debug.h>
42#include <synch/waitq.h>
43#include <udebug/udebug.h>
44#include <errno.h>
45#include <print.h>
46#include <arch.h>
47#include <proc/task.h>
48#include <proc/thread.h>
49
50/** Initialize udebug part of task structure.
51 *
52 * Called as part of task structure initialization.
53 * @param ut Pointer to the structure to initialize.
54 *
55 */
56void udebug_task_init(udebug_task_t *ut)
57{
58 mutex_initialize(&ut->lock, MUTEX_PASSIVE);
59 ut->dt_state = UDEBUG_TS_INACTIVE;
60 ut->begin_call = NULL;
61 ut->not_stoppable_count = 0;
62 ut->evmask = 0;
63}
64
65/** Initialize udebug part of thread structure.
66 *
67 * Called as part of thread structure initialization.
68 *
69 * @param ut Pointer to the structure to initialize.
70 *
71 */
72void udebug_thread_initialize(udebug_thread_t *ut)
73{
74 mutex_initialize(&ut->lock, MUTEX_PASSIVE);
75 waitq_initialize(&ut->go_wq);
76 condvar_initialize(&ut->active_cv);
77
78 ut->go_call = NULL;
79 ut->uspace_state = NULL;
80 ut->go = false;
81 ut->stoppable = true;
82 ut->active = false;
83 ut->cur_event = 0; /* None */
84}
85
86/** Wait for a GO message.
87 *
88 * When a debugging event occurs in a thread or the thread is stopped,
89 * this function is called to block the thread until a GO message
90 * is received.
91 *
92 * @param wq The wait queue used by the thread to wait for GO messages.
93 *
94 */
95static void udebug_wait_for_go(waitq_t *wq)
96{
97 ipl_t ipl = waitq_sleep_prepare(wq);
98
99 wq->missed_wakeups = 0; /* Enforce blocking. */
100 bool blocked;
101 (void) waitq_sleep_timeout_unsafe(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE, &blocked);
102 waitq_sleep_finish(wq, blocked, ipl);
103}
104
105/** Start of stoppable section.
106 *
107 * A stoppable section is a section of code where if the thread can
108 * be stoped. In other words, if a STOP operation is issued, the thread
109 * is guaranteed not to execute any userspace instructions until the
110 * thread is resumed.
111 *
112 * Having stoppable sections is better than having stopping points, since
113 * a thread can be stopped even when it is blocked indefinitely in a system
114 * call (whereas it would not reach any stopping point).
115 *
116 */
117void udebug_stoppable_begin(void)
118{
119 assert(THREAD);
120 assert(TASK);
121
122 mutex_lock(&TASK->udebug.lock);
123
124 int nsc = --TASK->udebug.not_stoppable_count;
125
126 /* Lock order OK, THREAD->udebug.lock is after TASK->udebug.lock */
127 mutex_lock(&THREAD->udebug.lock);
128 assert(THREAD->udebug.stoppable == false);
129 THREAD->udebug.stoppable = true;
130
131 if ((TASK->udebug.dt_state == UDEBUG_TS_BEGINNING) && (nsc == 0)) {
132 /*
133 * This was the last non-stoppable thread. Reply to
134 * DEBUG_BEGIN call.
135 *
136 */
137
138 call_t *db_call = TASK->udebug.begin_call;
139 assert(db_call);
140
141 TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
142 TASK->udebug.begin_call = NULL;
143
144 IPC_SET_RETVAL(db_call->data, 0);
145 ipc_answer(&TASK->answerbox, db_call);
146 } else if (TASK->udebug.dt_state == UDEBUG_TS_ACTIVE) {
147 /*
148 * Active debugging session
149 */
150
151 if (THREAD->udebug.active == true &&
152 THREAD->udebug.go == false) {
153 /*
154 * Thread was requested to stop - answer go call
155 *
156 */
157
158 /* Make sure nobody takes this call away from us */
159 call_t *go_call = THREAD->udebug.go_call;
160 THREAD->udebug.go_call = NULL;
161 assert(go_call);
162
163 IPC_SET_RETVAL(go_call->data, 0);
164 IPC_SET_ARG1(go_call->data, UDEBUG_EVENT_STOP);
165
166 THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
167 ipc_answer(&TASK->answerbox, go_call);
168 }
169 }
170
171 mutex_unlock(&THREAD->udebug.lock);
172 mutex_unlock(&TASK->udebug.lock);
173}
174
175/** End of a stoppable section.
176 *
177 * This is the point where the thread will block if it is stopped.
178 * (As, by definition, a stopped thread must not leave its stoppable
179 * section).
180 *
181 */
182void udebug_stoppable_end(void)
183{
184restart:
185 mutex_lock(&TASK->udebug.lock);
186 mutex_lock(&THREAD->udebug.lock);
187
188 if ((THREAD->udebug.active) && (THREAD->udebug.go == false)) {
189 mutex_unlock(&THREAD->udebug.lock);
190 mutex_unlock(&TASK->udebug.lock);
191
192 udebug_wait_for_go(&THREAD->udebug.go_wq);
193
194 goto restart;
195 /* Must try again - have to lose stoppability atomically. */
196 } else {
197 ++TASK->udebug.not_stoppable_count;
198 assert(THREAD->udebug.stoppable == true);
199 THREAD->udebug.stoppable = false;
200
201 mutex_unlock(&THREAD->udebug.lock);
202 mutex_unlock(&TASK->udebug.lock);
203 }
204}
205
206/** Upon being scheduled to run, check if the current thread should stop.
207 *
208 * This function is called from clock().
209 *
210 */
211void udebug_before_thread_runs(void)
212{
213 /* Check if we're supposed to stop */
214 udebug_stoppable_begin();
215 udebug_stoppable_end();
216}
217
218/** Syscall event hook.
219 *
220 * Must be called before and after servicing a system call. This generates
221 * a SYSCALL_B or SYSCALL_E event, depending on the value of @a end_variant.
222 *
223 */
224void udebug_syscall_event(sysarg_t a1, sysarg_t a2, sysarg_t a3,
225 sysarg_t a4, sysarg_t a5, sysarg_t a6, sysarg_t id, sysarg_t rc,
226 bool end_variant)
227{
228 udebug_event_t etype =
229 end_variant ? UDEBUG_EVENT_SYSCALL_E : UDEBUG_EVENT_SYSCALL_B;
230
231 mutex_lock(&TASK->udebug.lock);
232 mutex_lock(&THREAD->udebug.lock);
233
234 /* Must only generate events when in debugging session and is go. */
235 if (THREAD->udebug.active != true || THREAD->udebug.go == false ||
236 (TASK->udebug.evmask & UDEBUG_EVMASK(etype)) == 0) {
237 mutex_unlock(&THREAD->udebug.lock);
238 mutex_unlock(&TASK->udebug.lock);
239 return;
240 }
241
242 /* Fill in the GO response. */
243 call_t *call = THREAD->udebug.go_call;
244 THREAD->udebug.go_call = NULL;
245
246 IPC_SET_RETVAL(call->data, 0);
247 IPC_SET_ARG1(call->data, etype);
248 IPC_SET_ARG2(call->data, id);
249 IPC_SET_ARG3(call->data, rc);
250
251 THREAD->udebug.syscall_args[0] = a1;
252 THREAD->udebug.syscall_args[1] = a2;
253 THREAD->udebug.syscall_args[2] = a3;
254 THREAD->udebug.syscall_args[3] = a4;
255 THREAD->udebug.syscall_args[4] = a5;
256 THREAD->udebug.syscall_args[5] = a6;
257
258 /*
259 * Make sure udebug.go is false when going to sleep
260 * in case we get woken up by DEBUG_END. (At which
261 * point it must be back to the initial true value).
262 *
263 */
264 THREAD->udebug.go = false;
265 THREAD->udebug.cur_event = etype;
266
267 ipc_answer(&TASK->answerbox, call);
268
269 mutex_unlock(&THREAD->udebug.lock);
270 mutex_unlock(&TASK->udebug.lock);
271
272 udebug_wait_for_go(&THREAD->udebug.go_wq);
273}
274
275/** Thread-creation event hook combined with attaching the thread.
276 *
277 * Must be called when a new userspace thread is created in the debugged
278 * task. Generates a THREAD_B event. Also attaches the thread @a t
279 * to the task @a ta.
280 *
281 * This is necessary to avoid a race condition where the BEGIN and THREAD_READ
282 * requests would be handled inbetween attaching the thread and checking it
283 * for being in a debugging session to send the THREAD_B event. We could then
284 * either miss threads or get some threads both in the thread list
285 * and get a THREAD_B event for them.
286 *
287 * @param thread Structure of the thread being created. Not locked, as the
288 * thread is not executing yet.
289 * @param task Task to which the thread should be attached.
290 *
291 */
292void udebug_thread_b_event_attach(struct thread *thread, struct task *task)
293{
294 mutex_lock(&TASK->udebug.lock);
295 mutex_lock(&THREAD->udebug.lock);
296
297 thread_attach(thread, task);
298
299 LOG("Check state");
300
301 /* Must only generate events when in debugging session */
302 if (THREAD->udebug.active != true) {
303 LOG("udebug.active: %s, udebug.go: %s",
304 THREAD->udebug.active ? "Yes(+)" : "No",
305 THREAD->udebug.go ? "Yes(-)" : "No");
306
307 mutex_unlock(&THREAD->udebug.lock);
308 mutex_unlock(&TASK->udebug.lock);
309 return;
310 }
311
312 LOG("Trigger event");
313
314 call_t *call = THREAD->udebug.go_call;
315
316 THREAD->udebug.go_call = NULL;
317 IPC_SET_RETVAL(call->data, 0);
318 IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_B);
319 IPC_SET_ARG2(call->data, (sysarg_t) thread);
320
321 /*
322 * Make sure udebug.go is false when going to sleep
323 * in case we get woken up by DEBUG_END. (At which
324 * point it must be back to the initial true value).
325 *
326 */
327 THREAD->udebug.go = false;
328 THREAD->udebug.cur_event = UDEBUG_EVENT_THREAD_B;
329
330 ipc_answer(&TASK->answerbox, call);
331
332 mutex_unlock(&THREAD->udebug.lock);
333 mutex_unlock(&TASK->udebug.lock);
334
335 LOG("Wait for Go");
336 udebug_wait_for_go(&THREAD->udebug.go_wq);
337}
338
339/** Thread-termination event hook.
340 *
341 * Must be called when the current thread is terminating.
342 * Generates a THREAD_E event.
343 *
344 */
345void udebug_thread_e_event(void)
346{
347 mutex_lock(&TASK->udebug.lock);
348 mutex_lock(&THREAD->udebug.lock);
349
350 LOG("Check state");
351
352 /* Must only generate events when in debugging session. */
353 if (THREAD->udebug.active != true) {
354 LOG("udebug.active: %s, udebug.go: %s",
355 THREAD->udebug.active ? "Yes" : "No",
356 THREAD->udebug.go ? "Yes" : "No");
357
358 mutex_unlock(&THREAD->udebug.lock);
359 mutex_unlock(&TASK->udebug.lock);
360 return;
361 }
362
363 LOG("Trigger event");
364
365 call_t *call = THREAD->udebug.go_call;
366
367 THREAD->udebug.go_call = NULL;
368 IPC_SET_RETVAL(call->data, 0);
369 IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_E);
370
371 /* Prevent any further debug activity in thread. */
372 THREAD->udebug.active = false;
373 THREAD->udebug.cur_event = 0; /* None */
374 THREAD->udebug.go = false; /* Set to initial value */
375
376 ipc_answer(&TASK->answerbox, call);
377
378 mutex_unlock(&THREAD->udebug.lock);
379 mutex_unlock(&TASK->udebug.lock);
380
381 /*
382 * This event does not sleep - debugging has finished
383 * in this thread.
384 *
385 */
386}
387
388/** Terminate task debugging session.
389 *
390 * Gracefully terminate the debugging session for a task. If the debugger
391 * is still waiting for events on some threads, it will receive a
392 * FINISHED event for each of them.
393 *
394 * @param task Task structure. task->udebug.lock must be already locked.
395 *
396 * @return Zero on success or an error code.
397 *
398 */
399errno_t udebug_task_cleanup(struct task *task)
400{
401 assert(mutex_locked(&task->udebug.lock));
402
403 if ((task->udebug.dt_state != UDEBUG_TS_BEGINNING) &&
404 (task->udebug.dt_state != UDEBUG_TS_ACTIVE)) {
405 return EINVAL;
406 }
407
408 LOG("Task %" PRIu64, task->taskid);
409
410 /* Finish debugging of all userspace threads */
411 list_foreach(task->threads, th_link, thread_t, thread) {
412 mutex_lock(&thread->udebug.lock);
413
414 /* Only process userspace threads. */
415 if (thread->uspace) {
416 /* Prevent any further debug activity in thread. */
417 thread->udebug.active = false;
418 thread->udebug.cur_event = 0; /* None */
419
420 /* Is the thread still go? */
421 if (thread->udebug.go == true) {
422 /*
423 * Yes, so clear go. As active == false,
424 * this doesn't affect anything.
425 (
426 */
427 thread->udebug.go = false;
428
429 /* Answer GO call */
430 LOG("Answer GO call with EVENT_FINISHED.");
431
432 IPC_SET_RETVAL(thread->udebug.go_call->data, 0);
433 IPC_SET_ARG1(thread->udebug.go_call->data,
434 UDEBUG_EVENT_FINISHED);
435
436 ipc_answer(&task->answerbox, thread->udebug.go_call);
437 thread->udebug.go_call = NULL;
438 } else {
439 /*
440 * Debug_stop is already at initial value.
441 * Yet this means the thread needs waking up.
442 *
443 */
444
445 /*
446 * thread's lock must not be held when calling
447 * waitq_wakeup.
448 *
449 */
450 waitq_wakeup(&thread->udebug.go_wq, WAKEUP_FIRST);
451 }
452
453 mutex_unlock(&thread->udebug.lock);
454 condvar_broadcast(&thread->udebug.active_cv);
455 } else
456 mutex_unlock(&thread->udebug.lock);
457 }
458
459 task->udebug.dt_state = UDEBUG_TS_INACTIVE;
460 task->udebug.debugger = NULL;
461
462 return 0;
463}
464
465/** Wait for debugger to handle a fault in this thread.
466 *
467 * When a thread faults and someone is subscribed to the FAULT kernel event,
468 * this function is called to wait for a debugging session to give userspace
469 * a chance to examine the faulting thead/task. When the debugging session
470 * is over, this function returns (so that thread/task cleanup can continue).
471 *
472 */
473void udebug_thread_fault(void)
474{
475 udebug_stoppable_begin();
476
477 /* Wait until a debugger attends to us. */
478 mutex_lock(&THREAD->udebug.lock);
479 while (!THREAD->udebug.active)
480 condvar_wait(&THREAD->udebug.active_cv, &THREAD->udebug.lock);
481 mutex_unlock(&THREAD->udebug.lock);
482
483 /* Make sure the debugging session is over before proceeding. */
484 mutex_lock(&THREAD->udebug.lock);
485 while (THREAD->udebug.active)
486 condvar_wait(&THREAD->udebug.active_cv, &THREAD->udebug.lock);
487 mutex_unlock(&THREAD->udebug.lock);
488
489 udebug_stoppable_end();
490}
491
492/** @}
493 */
Note: See TracBrowser for help on using the repository browser.