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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f1380b7 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
RevLine 
[9a1b20c]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
[da1bafb]35 * @brief Udebug hooks and data structure management.
[7dc62af]36 *
37 * Udebug is an interface that makes userspace debuggers possible.
[9a1b20c]38 */
[da1bafb]39
[63e27ef]40#include <assert.h>
[0108984a]41#include <debug.h>
[63e27ef]42#include <synch/waitq.h>
[9a1b20c]43#include <udebug/udebug.h>
44#include <errno.h>
[1902113]45#include <print.h>
[9a1b20c]46#include <arch.h>
[1066041]47#include <proc/task.h>
48#include <proc/thread.h>
[9a1b20c]49
[7dc62af]50/** Initialize udebug part of task structure.
51 *
52 * Called as part of task structure initialization.
[da1bafb]53 * @param ut Pointer to the structure to initialize.
54 *
[7dc62af]55 */
[9a1b20c]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
[7dc62af]65/** Initialize udebug part of thread structure.
66 *
67 * Called as part of thread structure initialization.
[da1bafb]68 *
69 * @param ut Pointer to the structure to initialize.
70 *
[7dc62af]71 */
[9a1b20c]72void udebug_thread_initialize(udebug_thread_t *ut)
73{
74 mutex_initialize(&ut->lock, MUTEX_PASSIVE);
75 waitq_initialize(&ut->go_wq);
[a074b4f]76 condvar_initialize(&ut->active_cv);
[a35b458]77
[9a1b20c]78 ut->go_call = NULL;
[3ff2b54]79 ut->uspace_state = NULL;
[384c488]80 ut->go = false;
[9a1b20c]81 ut->stoppable = true;
[8af9950]82 ut->active = false;
[da1bafb]83 ut->cur_event = 0; /* None */
[9a1b20c]84}
85
[7dc62af]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 *
[da1bafb]92 * @param wq The wait queue used by the thread to wait for GO messages.
93 *
[7dc62af]94 */
[9a1b20c]95static void udebug_wait_for_go(waitq_t *wq)
96{
[da1bafb]97 ipl_t ipl = waitq_sleep_prepare(wq);
[a35b458]98
[da1bafb]99 wq->missed_wakeups = 0; /* Enforce blocking. */
[897fd8f1]100 bool blocked;
101 (void) waitq_sleep_timeout_unsafe(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE, &blocked);
102 waitq_sleep_finish(wq, blocked, ipl);
[9a1b20c]103}
104
[7dc62af]105/** Start of stoppable section.
106 *
[da1bafb]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.
[7dc62af]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).
[da1bafb]115 *
[7dc62af]116 */
[9a1b20c]117void udebug_stoppable_begin(void)
118{
[63e27ef]119 assert(THREAD);
120 assert(TASK);
[a35b458]121
[9a1b20c]122 mutex_lock(&TASK->udebug.lock);
[a35b458]123
[da1bafb]124 int nsc = --TASK->udebug.not_stoppable_count;
[a35b458]125
[9a1b20c]126 /* Lock order OK, THREAD->udebug.lock is after TASK->udebug.lock */
127 mutex_lock(&THREAD->udebug.lock);
[63e27ef]128 assert(THREAD->udebug.stoppable == false);
[9a1b20c]129 THREAD->udebug.stoppable = true;
[a35b458]130
[da1bafb]131 if ((TASK->udebug.dt_state == UDEBUG_TS_BEGINNING) && (nsc == 0)) {
[9a1b20c]132 /*
133 * This was the last non-stoppable thread. Reply to
134 * DEBUG_BEGIN call.
[da1bafb]135 *
[9a1b20c]136 */
[a35b458]137
[da1bafb]138 call_t *db_call = TASK->udebug.begin_call;
[63e27ef]139 assert(db_call);
[a35b458]140
[9a1b20c]141 TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
142 TASK->udebug.begin_call = NULL;
[a35b458]143
[9a1b20c]144 IPC_SET_RETVAL(db_call->data, 0);
[da1bafb]145 ipc_answer(&TASK->answerbox, db_call);
[9a1b20c]146 } else if (TASK->udebug.dt_state == UDEBUG_TS_ACTIVE) {
147 /*
148 * Active debugging session
149 */
[a35b458]150
[8af9950]151 if (THREAD->udebug.active == true &&
[384c488]152 THREAD->udebug.go == false) {
[9a1b20c]153 /*
154 * Thread was requested to stop - answer go call
[da1bafb]155 *
[9a1b20c]156 */
[a35b458]157
[9a1b20c]158 /* Make sure nobody takes this call away from us */
[da1bafb]159 call_t *go_call = THREAD->udebug.go_call;
[9a1b20c]160 THREAD->udebug.go_call = NULL;
[63e27ef]161 assert(go_call);
[a35b458]162
[9a1b20c]163 IPC_SET_RETVAL(go_call->data, 0);
164 IPC_SET_ARG1(go_call->data, UDEBUG_EVENT_STOP);
[a35b458]165
[9a1b20c]166 THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
[da1bafb]167 ipc_answer(&TASK->answerbox, go_call);
[9a1b20c]168 }
169 }
[a35b458]170
[9a1b20c]171 mutex_unlock(&THREAD->udebug.lock);
172 mutex_unlock(&TASK->udebug.lock);
173}
174
[7dc62af]175/** End of a stoppable section.
176 *
177 * This is the point where the thread will block if it is stopped.
[da1bafb]178 * (As, by definition, a stopped thread must not leave its stoppable
179 * section).
180 *
[7dc62af]181 */
[9a1b20c]182void udebug_stoppable_end(void)
183{
184restart:
185 mutex_lock(&TASK->udebug.lock);
186 mutex_lock(&THREAD->udebug.lock);
[a35b458]187
[da1bafb]188 if ((THREAD->udebug.active) && (THREAD->udebug.go == false)) {
[9a1b20c]189 mutex_unlock(&THREAD->udebug.lock);
190 mutex_unlock(&TASK->udebug.lock);
[a35b458]191
[9a1b20c]192 udebug_wait_for_go(&THREAD->udebug.go_wq);
[a35b458]193
[9a1b20c]194 goto restart;
[1378b2b]195 /* Must try again - have to lose stoppability atomically. */
[9a1b20c]196 } else {
197 ++TASK->udebug.not_stoppable_count;
[63e27ef]198 assert(THREAD->udebug.stoppable == true);
[9a1b20c]199 THREAD->udebug.stoppable = false;
[a35b458]200
[9a1b20c]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 *
[3ff2b54]208 * This function is called from clock().
[da1bafb]209 *
[9a1b20c]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
[7dc62af]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.
[da1bafb]222 *
[7dc62af]223 */
[96b02eb9]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,
[9a1b20c]226 bool end_variant)
227{
[da1bafb]228 udebug_event_t etype =
229 end_variant ? UDEBUG_EVENT_SYSCALL_E : UDEBUG_EVENT_SYSCALL_B;
[a35b458]230
[9a1b20c]231 mutex_lock(&TASK->udebug.lock);
232 mutex_lock(&THREAD->udebug.lock);
[a35b458]233
[1378b2b]234 /* Must only generate events when in debugging session and is go. */
[8af9950]235 if (THREAD->udebug.active != true || THREAD->udebug.go == false ||
[9a1b20c]236 (TASK->udebug.evmask & UDEBUG_EVMASK(etype)) == 0) {
237 mutex_unlock(&THREAD->udebug.lock);
238 mutex_unlock(&TASK->udebug.lock);
239 return;
240 }
[a35b458]241
[ae5aa90]242 /* Fill in the GO response. */
[da1bafb]243 call_t *call = THREAD->udebug.go_call;
[9a1b20c]244 THREAD->udebug.go_call = NULL;
[a35b458]245
[9a1b20c]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);
[a35b458]250
[9a1b20c]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;
[a35b458]257
[9a1b20c]258 /*
[384c488]259 * Make sure udebug.go is false when going to sleep
[9a1b20c]260 * in case we get woken up by DEBUG_END. (At which
261 * point it must be back to the initial true value).
[da1bafb]262 *
[9a1b20c]263 */
[384c488]264 THREAD->udebug.go = false;
[9a1b20c]265 THREAD->udebug.cur_event = etype;
[a35b458]266
[9a1b20c]267 ipc_answer(&TASK->answerbox, call);
[a35b458]268
[9a1b20c]269 mutex_unlock(&THREAD->udebug.lock);
270 mutex_unlock(&TASK->udebug.lock);
[a35b458]271
[9a1b20c]272 udebug_wait_for_go(&THREAD->udebug.go_wq);
273}
274
[13964ef]275/** Thread-creation event hook combined with attaching the thread.
[7dc62af]276 *
277 * Must be called when a new userspace thread is created in the debugged
[13964ef]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.
[7dc62af]286 *
[da1bafb]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 *
[7dc62af]291 */
[da1bafb]292void udebug_thread_b_event_attach(struct thread *thread, struct task *task)
[9a1b20c]293{
294 mutex_lock(&TASK->udebug.lock);
295 mutex_lock(&THREAD->udebug.lock);
[a35b458]296
[da1bafb]297 thread_attach(thread, task);
[a35b458]298
[ae5aa90]299 LOG("Check state");
[a35b458]300
[9a1b20c]301 /* Must only generate events when in debugging session */
[8af9950]302 if (THREAD->udebug.active != true) {
[ae5aa90]303 LOG("udebug.active: %s, udebug.go: %s",
[da1bafb]304 THREAD->udebug.active ? "Yes(+)" : "No",
305 THREAD->udebug.go ? "Yes(-)" : "No");
[a35b458]306
[9a1b20c]307 mutex_unlock(&THREAD->udebug.lock);
308 mutex_unlock(&TASK->udebug.lock);
309 return;
310 }
[a35b458]311
[ae5aa90]312 LOG("Trigger event");
[a35b458]313
[da1bafb]314 call_t *call = THREAD->udebug.go_call;
[a35b458]315
[9a1b20c]316 THREAD->udebug.go_call = NULL;
317 IPC_SET_RETVAL(call->data, 0);
318 IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_B);
[96b02eb9]319 IPC_SET_ARG2(call->data, (sysarg_t) thread);
[a35b458]320
[9a1b20c]321 /*
[384c488]322 * Make sure udebug.go is false when going to sleep
[9a1b20c]323 * in case we get woken up by DEBUG_END. (At which
324 * point it must be back to the initial true value).
[da1bafb]325 *
[9a1b20c]326 */
[384c488]327 THREAD->udebug.go = false;
[9a1b20c]328 THREAD->udebug.cur_event = UDEBUG_EVENT_THREAD_B;
[a35b458]329
[9a1b20c]330 ipc_answer(&TASK->answerbox, call);
[a35b458]331
[9a1b20c]332 mutex_unlock(&THREAD->udebug.lock);
333 mutex_unlock(&TASK->udebug.lock);
[a35b458]334
[ae5aa90]335 LOG("Wait for Go");
[9a1b20c]336 udebug_wait_for_go(&THREAD->udebug.go_wq);
337}
338
[7dc62af]339/** Thread-termination event hook.
340 *
341 * Must be called when the current thread is terminating.
342 * Generates a THREAD_E event.
[da1bafb]343 *
[7dc62af]344 */
[9a1b20c]345void udebug_thread_e_event(void)
346{
347 mutex_lock(&TASK->udebug.lock);
348 mutex_lock(&THREAD->udebug.lock);
[a35b458]349
[ae5aa90]350 LOG("Check state");
[a35b458]351
[1378b2b]352 /* Must only generate events when in debugging session. */
[8af9950]353 if (THREAD->udebug.active != true) {
[ae5aa90]354 LOG("udebug.active: %s, udebug.go: %s",
[da1bafb]355 THREAD->udebug.active ? "Yes" : "No",
356 THREAD->udebug.go ? "Yes" : "No");
[a35b458]357
[9a1b20c]358 mutex_unlock(&THREAD->udebug.lock);
359 mutex_unlock(&TASK->udebug.lock);
360 return;
361 }
[a35b458]362
[ae5aa90]363 LOG("Trigger event");
[a35b458]364
[da1bafb]365 call_t *call = THREAD->udebug.go_call;
[a35b458]366
[9a1b20c]367 THREAD->udebug.go_call = NULL;
368 IPC_SET_RETVAL(call->data, 0);
369 IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_E);
[a35b458]370
[1378b2b]371 /* Prevent any further debug activity in thread. */
[8af9950]372 THREAD->udebug.active = false;
[da1bafb]373 THREAD->udebug.cur_event = 0; /* None */
374 THREAD->udebug.go = false; /* Set to initial value */
[a35b458]375
[9a1b20c]376 ipc_answer(&TASK->answerbox, call);
[a35b458]377
[9a1b20c]378 mutex_unlock(&THREAD->udebug.lock);
379 mutex_unlock(&TASK->udebug.lock);
[a35b458]380
[da1bafb]381 /*
[32e6c9c]382 * This event does not sleep - debugging has finished
383 * in this thread.
[da1bafb]384 *
[32e6c9c]385 */
[9a1b20c]386}
387
[da1bafb]388/** Terminate task debugging session.
[9a1b20c]389 *
[da1bafb]390 * Gracefully terminate the debugging session for a task. If the debugger
[7dc62af]391 * is still waiting for events on some threads, it will receive a
392 * FINISHED event for each of them.
393 *
[1d432f9]394 * @param task Task structure. task->udebug.lock must be already locked.
[da1bafb]395 *
[cde999a]396 * @return Zero on success or an error code.
[da1bafb]397 *
[9a1b20c]398 */
[b7fd2a0]399errno_t udebug_task_cleanup(struct task *task)
[9a1b20c]400{
[63e27ef]401 assert(mutex_locked(&task->udebug.lock));
[1d432f9]402
[da1bafb]403 if ((task->udebug.dt_state != UDEBUG_TS_BEGINNING) &&
404 (task->udebug.dt_state != UDEBUG_TS_ACTIVE)) {
[9a1b20c]405 return EINVAL;
406 }
[a35b458]407
[da1bafb]408 LOG("Task %" PRIu64, task->taskid);
[a35b458]409
[9a1b20c]410 /* Finish debugging of all userspace threads */
[feeac0d]411 list_foreach(task->threads, th_link, thread_t, thread) {
[da1bafb]412 mutex_lock(&thread->udebug.lock);
[a35b458]413
[1378b2b]414 /* Only process userspace threads. */
[6eef3c4]415 if (thread->uspace) {
[1378b2b]416 /* Prevent any further debug activity in thread. */
[da1bafb]417 thread->udebug.active = false;
418 thread->udebug.cur_event = 0; /* None */
[a35b458]419
[1378b2b]420 /* Is the thread still go? */
[da1bafb]421 if (thread->udebug.go == true) {
[9a1b20c]422 /*
[da1bafb]423 * Yes, so clear go. As active == false,
[9a1b20c]424 * this doesn't affect anything.
[da1bafb]425 (
[9a1b20c]426 */
[da1bafb]427 thread->udebug.go = false;
[a35b458]428
[9a1b20c]429 /* Answer GO call */
[ae5aa90]430 LOG("Answer GO call with EVENT_FINISHED.");
[a35b458]431
[da1bafb]432 IPC_SET_RETVAL(thread->udebug.go_call->data, 0);
433 IPC_SET_ARG1(thread->udebug.go_call->data,
[3926f30]434 UDEBUG_EVENT_FINISHED);
[a35b458]435
[da1bafb]436 ipc_answer(&task->answerbox, thread->udebug.go_call);
437 thread->udebug.go_call = NULL;
[9a1b20c]438 } else {
439 /*
440 * Debug_stop is already at initial value.
441 * Yet this means the thread needs waking up.
[da1bafb]442 *
[9a1b20c]443 */
[a35b458]444
[9a1b20c]445 /*
[da1bafb]446 * thread's lock must not be held when calling
[9a1b20c]447 * waitq_wakeup.
[da1bafb]448 *
[9a1b20c]449 */
[da1bafb]450 waitq_wakeup(&thread->udebug.go_wq, WAKEUP_FIRST);
[9a1b20c]451 }
[a35b458]452
[da1bafb]453 mutex_unlock(&thread->udebug.lock);
454 condvar_broadcast(&thread->udebug.active_cv);
455 } else
456 mutex_unlock(&thread->udebug.lock);
[9a1b20c]457 }
[a35b458]458
[da1bafb]459 task->udebug.dt_state = UDEBUG_TS_INACTIVE;
460 task->udebug.debugger = NULL;
[a35b458]461
[9a1b20c]462 return 0;
463}
464
[0d21b53]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).
[da1bafb]471 *
[0d21b53]472 */
473void udebug_thread_fault(void)
474{
475 udebug_stoppable_begin();
[a35b458]476
[0d21b53]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);
[a35b458]482
[0d21b53]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);
[a35b458]488
[0d21b53]489 udebug_stoppable_end();
490}
[9a1b20c]491
492/** @}
493 */
Note: See TracBrowser for help on using the repository browser.