source: mainline/kernel/generic/src/udebug/udebug_ops.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: 10.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 operations.
36 */
37
38#include <print.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 printf("udebug_begin()\n");
166
167 mutex_lock(&TASK->udebug.lock);
168 printf("debugging task %llu\n", TASK->taskid);
169
170 if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) {
171 mutex_unlock(&TASK->udebug.lock);
172 printf("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 printf("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 printf("udebug_end()\n");
213
214 mutex_lock(&TASK->udebug.lock);
215 printf("task %llu\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 printf("udebug_set_mask()\n");
227
228 printf("debugging task %llu\n", TASK->taskid);
229
230 mutex_lock(&TASK->udebug.lock);
231
232 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
233 mutex_unlock(&TASK->udebug.lock);
234 printf("udebug_set_mask(): not active debuging session\n");
235
236 return EINVAL;
237 }
238
239 TASK->udebug.evmask = mask;
240
241 mutex_unlock(&TASK->udebug.lock);
242
243 return 0;
244}
245
246
247int udebug_go(thread_t *t, call_t *call)
248{
249 int rc;
250
251// printf("udebug_go()\n");
252
253 /* On success, this will lock t->udebug.lock */
254 rc = _thread_op_begin(t, false);
255 if (rc != EOK) {
256 return rc;
257 }
258
259 t->udebug.go_call = call;
260 t->udebug.stop = false;
261 t->udebug.cur_event = 0; /* none */
262
263 /*
264 * Neither t's lock nor threads_lock may be held during wakeup
265 */
266 waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
267
268 _thread_op_end(t);
269
270 return 0;
271}
272
273int udebug_stop(thread_t *t, call_t *call)
274{
275 int rc;
276
277 printf("udebug_stop()\n");
278 mutex_lock(&TASK->udebug.lock);
279
280 /*
281 * On success, this will lock t->udebug.lock. Note that this makes sure
282 * the thread is not stopped.
283 */
284 rc = _thread_op_begin(t, true);
285 if (rc != EOK) {
286 return rc;
287 }
288
289 /* Take GO away from the thread */
290 t->udebug.stop = true;
291
292 if (!t->udebug.stoppable) {
293 /* Answer will be sent when the thread becomes stoppable */
294 _thread_op_end(t);
295 return 0;
296 }
297
298 /*
299 * Answer GO call
300 */
301 printf("udebug_stop - answering go call\n");
302
303 /* Make sure nobody takes this call away from us */
304 call = t->udebug.go_call;
305 t->udebug.go_call = NULL;
306
307 IPC_SET_RETVAL(call->data, 0);
308 IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP);
309 printf("udebug_stop/ipc_answer\n");
310
311 THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
312
313 _thread_op_end(t);
314
315 ipc_answer(&TASK->answerbox, call);
316 mutex_unlock(&TASK->udebug.lock);
317
318 printf("udebog_stop/done\n");
319 return 0;
320}
321
322int udebug_thread_read(void **buffer, size_t buf_size, size_t *n)
323{
324 thread_t *t;
325 link_t *cur;
326 unative_t tid;
327 unsigned copied_ids;
328 ipl_t ipl;
329 unative_t *id_buffer;
330 int flags;
331 size_t max_ids;
332
333 printf("udebug_thread_read()\n");
334
335 /* Allocate a buffer to hold thread IDs */
336 id_buffer = malloc(buf_size, 0);
337
338 mutex_lock(&TASK->udebug.lock);
339
340 /* Verify task state */
341 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
342 mutex_unlock(&TASK->udebug.lock);
343 return EINVAL;
344 }
345
346 ipl = interrupts_disable();
347 spinlock_lock(&TASK->lock);
348 /* Copy down the thread IDs */
349
350 max_ids = buf_size / sizeof(unative_t);
351 copied_ids = 0;
352
353 /* FIXME: make sure the thread isn't past debug shutdown... */
354 for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
355 /* Do not write past end of buffer */
356 if (copied_ids >= max_ids) break;
357
358 t = list_get_instance(cur, thread_t, th_link);
359
360 spinlock_lock(&t->lock);
361 flags = t->flags;
362 spinlock_unlock(&t->lock);
363
364 /* Not interested in kernel threads */
365 if ((flags & THREAD_FLAG_USPACE) != 0) {
366 /* Using thread struct pointer as identification hash */
367 tid = (unative_t) t;
368 id_buffer[copied_ids++] = tid;
369 }
370 }
371
372 spinlock_unlock(&TASK->lock);
373 interrupts_restore(ipl);
374
375 mutex_unlock(&TASK->udebug.lock);
376
377 *buffer = id_buffer;
378 *n = copied_ids * sizeof(unative_t);
379
380 return 0;
381}
382
383int udebug_args_read(thread_t *t, void **buffer)
384{
385 int rc;
386 unative_t *arg_buffer;
387
388// printf("udebug_args_read()\n");
389
390 /* Prepare a buffer to hold the arguments */
391 arg_buffer = malloc(6 * sizeof(unative_t), 0);
392
393 /* On success, this will lock t->udebug.lock */
394 rc = _thread_op_begin(t, false);
395 if (rc != EOK) {
396 return rc;
397 }
398
399 /* Additionally we need to verify that we are inside a syscall */
400 if (t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B &&
401 t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E) {
402 _thread_op_end(t);
403 return EINVAL;
404 }
405
406 /* Copy to a local buffer before releasing the lock */
407 memcpy(arg_buffer, t->udebug.syscall_args, 6 * sizeof(unative_t));
408
409 _thread_op_end(t);
410
411 *buffer = arg_buffer;
412 return 0;
413}
414
415int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer)
416{
417 void *data_buffer;
418 int rc;
419
420 /* Verify task state */
421 mutex_lock(&TASK->udebug.lock);
422
423 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
424 mutex_unlock(&TASK->udebug.lock);
425 return EBUSY;
426 }
427
428 data_buffer = malloc(n, 0);
429
430// printf("udebug_mem_read: src=%u, size=%u\n", uspace_addr, n);
431
432 /* NOTE: this is not strictly from a syscall... but that shouldn't
433 * be a problem */
434 rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n);
435 mutex_unlock(&TASK->udebug.lock);
436
437 if (rc != 0) return rc;
438
439 *buffer = data_buffer;
440 return 0;
441}
442
443/** @}
444 */
Note: See TracBrowser for help on using the repository browser.