Subversion Repositories HelenOS

Rev

Rev 3441 | Rev 3474 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3438 svoboda 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
 
3441 svoboda 38
#include <debug.h>
3438 svoboda 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
 */
70
static 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
 
150
static 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
 */
158
int udebug_begin(call_t *call)
159
{
160
	int reply;
161
 
162
	thread_t *t;
163
	link_t *cur;
164
 
3441 svoboda 165
	LOG("udebug_begin()\n");
3438 svoboda 166
 
167
	mutex_lock(&TASK->udebug.lock);
3441 svoboda 168
	LOG("debugging task %llu\n", TASK->taskid);
3438 svoboda 169
 
170
	if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) {
171
		mutex_unlock(&TASK->udebug.lock);
3441 svoboda 172
		LOG("udebug_begin(): busy error\n");
3438 svoboda 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
 
3441 svoboda 202
	LOG("udebug_begin() done (%s)\n", 
3438 svoboda 203
	    reply ? "reply" : "stoppability wait");
204
 
205
	return reply;
206
}
207
 
208
int udebug_end(void)
209
{
210
	int rc;
211
 
3441 svoboda 212
	LOG("udebug_end()\n");
3438 svoboda 213
 
214
	mutex_lock(&TASK->udebug.lock);
3441 svoboda 215
	LOG("task %" PRIu64 "\n", TASK->taskid);
3438 svoboda 216
 
217
	rc = udebug_task_cleanup(TASK);
218
 
219
	mutex_unlock(&TASK->udebug.lock);
220
 
221
	return rc;
222
}
223
 
224
int udebug_set_evmask(udebug_evmask_t mask)
225
{
3441 svoboda 226
	LOG("udebug_set_mask()\n");
3438 svoboda 227
 
228
	mutex_lock(&TASK->udebug.lock);
229
 
230
	if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
231
		mutex_unlock(&TASK->udebug.lock);
3441 svoboda 232
		LOG("udebug_set_mask(): not active debuging session\n");
3438 svoboda 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
 
245
int 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
 
269
int udebug_stop(thread_t *t, call_t *call)
270
{
271
	int rc;
272
 
3441 svoboda 273
	LOG("udebug_stop()\n");
3438 svoboda 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
	 */
3441 svoboda 297
	LOG("udebug_stop - answering go call\n");
3438 svoboda 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);
3441 svoboda 305
	LOG("udebug_stop/ipc_answer\n");
3438 svoboda 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
 
3441 svoboda 314
	LOG("udebog_stop/done\n");
3438 svoboda 315
	return 0;
316
}
317
 
318
int 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
 
3441 svoboda 329
	LOG("udebug_thread_read()\n");
3438 svoboda 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
 
379
int 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
 
409
int 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
 */