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

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

Use LOG() instead of printf() for debug messages.

  • Property mode set to 100644
File size: 10.2 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
38#include <debug.h>
39#include <proc/task.h>
40#include <proc/thread.h>
41#include <arch.h>
42#include <errno.h>
43#include <syscall/copy.h>
44#include <ipc/ipc.h>
45#include <udebug/udebug.h>
46#include <udebug/udebug_ops.h>
47
48/**
49 * Prepare a thread for a debugging operation.
50 *
51 * Simply put, return thread t with t->udebug.lock held,
52 * but only if it verifies all conditions.
53 *
54 * Specifically, verifies that thread t exists, is a userspace thread,
55 * and belongs to the current task (TASK). Verifies, that the thread
56 * has (or hasn't) go according to having_go (typically false).
57 * It also locks t->udebug.lock, making sure that t->udebug.debug_active
58 * is true - that the thread is in a valid debugging session.
59 *
60 * With this verified and the t->udebug.lock mutex held, it is ensured
61 * that the thread cannot leave the debugging session, let alone cease
62 * to exist.
63 *
64 * In this function, holding the TASK->udebug.lock mutex prevents the
65 * thread from leaving the debugging session, while relaxing from
66 * the t->lock spinlock to the t->udebug.lock mutex.
67 *
68 * Returns EOK if all went well, or an error code otherwise.
69 */
70static int _thread_op_begin(thread_t *t, bool having_go)
71{
72 task_id_t taskid;
73 ipl_t ipl;
74
75 taskid = TASK->taskid;
76
77 mutex_lock(&TASK->udebug.lock);
78
79 /* thread_exists() must be called with threads_lock held */
80 ipl = interrupts_disable();
81 spinlock_lock(&threads_lock);
82
83 if (!thread_exists(t)) {
84 spinlock_unlock(&threads_lock);
85 interrupts_restore(ipl);
86 mutex_unlock(&TASK->udebug.lock);
87 return ENOENT;
88 }
89
90 /* t->lock is enough to ensure the thread's existence */
91 spinlock_lock(&t->lock);
92 spinlock_unlock(&threads_lock);
93
94 /* Verify that 't' is a userspace thread */
95 if ((t->flags & THREAD_FLAG_USPACE) == 0) {
96 /* It's not, deny its existence */
97 spinlock_unlock(&t->lock);
98 interrupts_restore(ipl);
99 mutex_unlock(&TASK->udebug.lock);
100 return ENOENT;
101 }
102
103 /* Verify debugging state */
104 if (t->udebug.debug_active != true) {
105 /* Not in debugging session or undesired GO state */
106 spinlock_unlock(&t->lock);
107 interrupts_restore(ipl);
108 mutex_unlock(&TASK->udebug.lock);
109 return ENOENT;
110 }
111
112 /*
113 * Since the thread has debug_active == true, TASK->udebug.lock
114 * is enough to ensure its existence and that debug_active remains
115 * true.
116 */
117 spinlock_unlock(&t->lock);
118 interrupts_restore(ipl);
119
120 /* Only mutex TASK->udebug.lock left */
121
122 /* Now verify that the thread belongs to the current task */
123 if (t->task != TASK) {
124 /* No such thread belonging this task*/
125 mutex_unlock(&TASK->udebug.lock);
126 return ENOENT;
127 }
128
129 /*
130 * Now we need to grab the thread's debug lock for synchronization
131 * of the threads stoppability/stop state.
132 */
133 mutex_lock(&t->udebug.lock);
134
135 /* The big task mutex is no longer needed */
136 mutex_unlock(&TASK->udebug.lock);
137
138 if (!t->udebug.stop != having_go) {
139 /* Not in debugging session or undesired GO state */
140 mutex_unlock(&t->udebug.lock);
141 return EINVAL;
142 }
143
144 /* Only t->udebug.lock left */
145
146 return EOK; /* All went well */
147}
148
149
150static void _thread_op_end(thread_t *t)
151{
152 mutex_unlock(&t->udebug.lock);
153}
154
155/**
156 * \return 0 (ok, but not done yet), 1 (done) or negative error code.
157 */
158int udebug_begin(call_t *call)
159{
160 int reply;
161
162 thread_t *t;
163 link_t *cur;
164
165 LOG("udebug_begin()\n");
166
167 mutex_lock(&TASK->udebug.lock);
168 LOG("debugging task %llu\n", TASK->taskid);
169
170 if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) {
171 mutex_unlock(&TASK->udebug.lock);
172 LOG("udebug_begin(): busy error\n");
173
174 return EBUSY;
175 }
176
177 TASK->udebug.dt_state = UDEBUG_TS_BEGINNING;
178 TASK->udebug.begin_call = call;
179 TASK->udebug.debugger = call->sender;
180
181 if (TASK->udebug.not_stoppable_count == 0) {
182 TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
183 TASK->udebug.begin_call = NULL;
184 reply = 1; /* immediate reply */
185 } else {
186 reply = 0; /* no reply */
187 }
188
189 /* Set udebug.debug_active on all of the task's userspace threads */
190
191 for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
192 t = list_get_instance(cur, thread_t, th_link);
193
194 mutex_lock(&t->udebug.lock);
195 if ((t->flags & THREAD_FLAG_USPACE) != 0)
196 t->udebug.debug_active = true;
197 mutex_unlock(&t->udebug.lock);
198 }
199
200 mutex_unlock(&TASK->udebug.lock);
201
202 LOG("udebug_begin() done (%s)\n",
203 reply ? "reply" : "stoppability wait");
204
205 return reply;
206}
207
208int udebug_end(void)
209{
210 int rc;
211
212 LOG("udebug_end()\n");
213
214 mutex_lock(&TASK->udebug.lock);
215 LOG("task %" PRIu64 "\n", TASK->taskid);
216
217 rc = udebug_task_cleanup(TASK);
218
219 mutex_unlock(&TASK->udebug.lock);
220
221 return rc;
222}
223
224int udebug_set_evmask(udebug_evmask_t mask)
225{
226 LOG("udebug_set_mask()\n");
227
228 mutex_lock(&TASK->udebug.lock);
229
230 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
231 mutex_unlock(&TASK->udebug.lock);
232 LOG("udebug_set_mask(): not active debuging session\n");
233
234 return EINVAL;
235 }
236
237 TASK->udebug.evmask = mask;
238
239 mutex_unlock(&TASK->udebug.lock);
240
241 return 0;
242}
243
244
245int udebug_go(thread_t *t, call_t *call)
246{
247 int rc;
248
249 /* On success, this will lock t->udebug.lock */
250 rc = _thread_op_begin(t, false);
251 if (rc != EOK) {
252 return rc;
253 }
254
255 t->udebug.go_call = call;
256 t->udebug.stop = false;
257 t->udebug.cur_event = 0; /* none */
258
259 /*
260 * Neither t's lock nor threads_lock may be held during wakeup
261 */
262 waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
263
264 _thread_op_end(t);
265
266 return 0;
267}
268
269int udebug_stop(thread_t *t, call_t *call)
270{
271 int rc;
272
273 LOG("udebug_stop()\n");
274 mutex_lock(&TASK->udebug.lock);
275
276 /*
277 * On success, this will lock t->udebug.lock. Note that this makes sure
278 * the thread is not stopped.
279 */
280 rc = _thread_op_begin(t, true);
281 if (rc != EOK) {
282 return rc;
283 }
284
285 /* Take GO away from the thread */
286 t->udebug.stop = true;
287
288 if (!t->udebug.stoppable) {
289 /* Answer will be sent when the thread becomes stoppable */
290 _thread_op_end(t);
291 return 0;
292 }
293
294 /*
295 * Answer GO call
296 */
297 LOG("udebug_stop - answering go call\n");
298
299 /* Make sure nobody takes this call away from us */
300 call = t->udebug.go_call;
301 t->udebug.go_call = NULL;
302
303 IPC_SET_RETVAL(call->data, 0);
304 IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP);
305 LOG("udebug_stop/ipc_answer\n");
306
307 THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
308
309 _thread_op_end(t);
310
311 ipc_answer(&TASK->answerbox, call);
312 mutex_unlock(&TASK->udebug.lock);
313
314 LOG("udebog_stop/done\n");
315 return 0;
316}
317
318int udebug_thread_read(void **buffer, size_t buf_size, size_t *n)
319{
320 thread_t *t;
321 link_t *cur;
322 unative_t tid;
323 unsigned copied_ids;
324 ipl_t ipl;
325 unative_t *id_buffer;
326 int flags;
327 size_t max_ids;
328
329 LOG("udebug_thread_read()\n");
330
331 /* Allocate a buffer to hold thread IDs */
332 id_buffer = malloc(buf_size, 0);
333
334 mutex_lock(&TASK->udebug.lock);
335
336 /* Verify task state */
337 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
338 mutex_unlock(&TASK->udebug.lock);
339 return EINVAL;
340 }
341
342 ipl = interrupts_disable();
343 spinlock_lock(&TASK->lock);
344 /* Copy down the thread IDs */
345
346 max_ids = buf_size / sizeof(unative_t);
347 copied_ids = 0;
348
349 /* FIXME: make sure the thread isn't past debug shutdown... */
350 for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
351 /* Do not write past end of buffer */
352 if (copied_ids >= max_ids) break;
353
354 t = list_get_instance(cur, thread_t, th_link);
355
356 spinlock_lock(&t->lock);
357 flags = t->flags;
358 spinlock_unlock(&t->lock);
359
360 /* Not interested in kernel threads */
361 if ((flags & THREAD_FLAG_USPACE) != 0) {
362 /* Using thread struct pointer as identification hash */
363 tid = (unative_t) t;
364 id_buffer[copied_ids++] = tid;
365 }
366 }
367
368 spinlock_unlock(&TASK->lock);
369 interrupts_restore(ipl);
370
371 mutex_unlock(&TASK->udebug.lock);
372
373 *buffer = id_buffer;
374 *n = copied_ids * sizeof(unative_t);
375
376 return 0;
377}
378
379int udebug_args_read(thread_t *t, void **buffer)
380{
381 int rc;
382 unative_t *arg_buffer;
383
384 /* Prepare a buffer to hold the arguments */
385 arg_buffer = malloc(6 * sizeof(unative_t), 0);
386
387 /* On success, this will lock t->udebug.lock */
388 rc = _thread_op_begin(t, false);
389 if (rc != EOK) {
390 return rc;
391 }
392
393 /* Additionally we need to verify that we are inside a syscall */
394 if (t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B &&
395 t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E) {
396 _thread_op_end(t);
397 return EINVAL;
398 }
399
400 /* Copy to a local buffer before releasing the lock */
401 memcpy(arg_buffer, t->udebug.syscall_args, 6 * sizeof(unative_t));
402
403 _thread_op_end(t);
404
405 *buffer = arg_buffer;
406 return 0;
407}
408
409int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer)
410{
411 void *data_buffer;
412 int rc;
413
414 /* Verify task state */
415 mutex_lock(&TASK->udebug.lock);
416
417 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
418 mutex_unlock(&TASK->udebug.lock);
419 return EBUSY;
420 }
421
422 data_buffer = malloc(n, 0);
423
424 /* NOTE: this is not strictly from a syscall... but that shouldn't
425 * be a problem */
426 rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n);
427 mutex_unlock(&TASK->udebug.lock);
428
429 if (rc != 0) return rc;
430
431 *buffer = data_buffer;
432 return 0;
433}
434
435/** @}
436 */
Note: See TracBrowser for help on using the repository browser.