Subversion Repositories HelenOS

Rev

Rev 4337 | 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.
3474 svoboda 36
 *
37
 * Udebug operations on tasks and threads are implemented here. The
38
 * functions defined here are called from the udebug_ipc module
39
 * when servicing udebug IPC messages.
3438 svoboda 40
 */
41
 
3441 svoboda 42
#include <debug.h>
3438 svoboda 43
#include <proc/task.h>
44
#include <proc/thread.h>
45
#include <arch.h>
46
#include <errno.h>
4342 svoboda 47
#include <print.h>
3438 svoboda 48
#include <syscall/copy.h>
49
#include <ipc/ipc.h>
50
#include <udebug/udebug.h>
51
#include <udebug/udebug_ops.h>
52
 
53
/**
54
 * Prepare a thread for a debugging operation.
55
 *
56
 * Simply put, return thread t with t->udebug.lock held,
57
 * but only if it verifies all conditions.
58
 *
59
 * Specifically, verifies that thread t exists, is a userspace thread,
60
 * and belongs to the current task (TASK). Verifies, that the thread
3674 svoboda 61
 * is (or is not) go according to being_go (typically false).
4337 svoboda 62
 * It also locks t->udebug.lock, making sure that t->udebug.active
3438 svoboda 63
 * is true - that the thread is in a valid debugging session.
64
 *
65
 * With this verified and the t->udebug.lock mutex held, it is ensured
66
 * that the thread cannot leave the debugging session, let alone cease
67
 * to exist.
68
 *
69
 * In this function, holding the TASK->udebug.lock mutex prevents the
70
 * thread from leaving the debugging session, while relaxing from
71
 * the t->lock spinlock to the t->udebug.lock mutex.
72
 *
3474 svoboda 73
 * @param t		Pointer, need not at all be valid.
3674 svoboda 74
 * @param being_go	Required thread state.
3474 svoboda 75
 *
3438 svoboda 76
 * Returns EOK if all went well, or an error code otherwise.
77
 */
3674 svoboda 78
static int _thread_op_begin(thread_t *t, bool being_go)
3438 svoboda 79
{
80
	task_id_t taskid;
81
	ipl_t ipl;
82
 
83
	taskid = TASK->taskid;
84
 
85
	mutex_lock(&TASK->udebug.lock);
86
 
87
	/* thread_exists() must be called with threads_lock held */
88
	ipl = interrupts_disable();
89
	spinlock_lock(&threads_lock);
90
 
91
	if (!thread_exists(t)) {
92
		spinlock_unlock(&threads_lock);
93
		interrupts_restore(ipl);
94
		mutex_unlock(&TASK->udebug.lock);
95
		return ENOENT;
96
	}
97
 
98
	/* t->lock is enough to ensure the thread's existence */
99
	spinlock_lock(&t->lock);
100
	spinlock_unlock(&threads_lock);
101
 
3674 svoboda 102
	/* Verify that 't' is a userspace thread. */
3438 svoboda 103
	if ((t->flags & THREAD_FLAG_USPACE) == 0) {
104
		/* It's not, deny its existence */
105
		spinlock_unlock(&t->lock);
106
		interrupts_restore(ipl);
107
		mutex_unlock(&TASK->udebug.lock);
108
		return ENOENT;
109
	}
110
 
3674 svoboda 111
	/* Verify debugging state. */
4337 svoboda 112
	if (t->udebug.active != true) {
3438 svoboda 113
		/* Not in debugging session or undesired GO state */
114
		spinlock_unlock(&t->lock);
115
		interrupts_restore(ipl);
116
		mutex_unlock(&TASK->udebug.lock);
117
		return ENOENT;
118
	}
119
 
120
	/*
4337 svoboda 121
	 * Since the thread has active == true, TASK->udebug.lock
122
	 * is enough to ensure its existence and that active remains
3438 svoboda 123
	 * true.
124
	 */
125
	spinlock_unlock(&t->lock);
126
	interrupts_restore(ipl);
127
 
3674 svoboda 128
	/* Only mutex TASK->udebug.lock left. */
3438 svoboda 129
 
3674 svoboda 130
	/* Now verify that the thread belongs to the current task. */
3438 svoboda 131
	if (t->task != TASK) {
132
		/* No such thread belonging this task*/
133
		mutex_unlock(&TASK->udebug.lock);
134
		return ENOENT;
135
	}
136
 
137
	/*
138
	 * Now we need to grab the thread's debug lock for synchronization
139
	 * of the threads stoppability/stop state.
140
	 */
141
	mutex_lock(&t->udebug.lock);
142
 
3674 svoboda 143
	/* The big task mutex is no longer needed. */
3438 svoboda 144
	mutex_unlock(&TASK->udebug.lock);
145
 
3674 svoboda 146
	if (t->udebug.go != being_go) {
147
		/* Not in debugging session or undesired GO state. */
3438 svoboda 148
		mutex_unlock(&t->udebug.lock);
149
		return EINVAL;
150
	}
151
 
3674 svoboda 152
	/* Only t->udebug.lock left. */
3438 svoboda 153
 
3674 svoboda 154
	return EOK;	/* All went well. */
3438 svoboda 155
}
156
 
3474 svoboda 157
/** End debugging operation on a thread. */
3438 svoboda 158
static void _thread_op_end(thread_t *t)
159
{
160
	mutex_unlock(&t->udebug.lock);
161
}
162
 
3474 svoboda 163
/** Begin debugging the current task.
164
 *
165
 * Initiates a debugging session for the current task (and its threads).
166
 * When the debugging session has started a reply will be sent to the
167
 * UDEBUG_BEGIN call. This may happen immediately in this function if
168
 * all the threads in this task are stoppable at the moment and in this
169
 * case the function returns 1.
170
 *
171
 * Otherwise the function returns 0 and the reply will be sent as soon as
172
 * all the threads become stoppable (i.e. they can be considered stopped).
173
 *
174
 * @param call	The BEGIN call we are servicing.
175
 * @return 	0 (OK, but not done yet), 1 (done) or negative error code.
3438 svoboda 176
 */
177
int udebug_begin(call_t *call)
178
{
179
	int reply;
180
 
181
	thread_t *t;
182
	link_t *cur;
183
 
3441 svoboda 184
	LOG("udebug_begin()\n");
3438 svoboda 185
 
186
	mutex_lock(&TASK->udebug.lock);
3441 svoboda 187
	LOG("debugging task %llu\n", TASK->taskid);
3438 svoboda 188
 
189
	if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) {
190
		mutex_unlock(&TASK->udebug.lock);
3441 svoboda 191
		LOG("udebug_begin(): busy error\n");
3438 svoboda 192
 
193
		return EBUSY;
194
	}
195
 
196
	TASK->udebug.dt_state = UDEBUG_TS_BEGINNING;
197
	TASK->udebug.begin_call = call;
198
	TASK->udebug.debugger = call->sender;
199
 
200
	if (TASK->udebug.not_stoppable_count == 0) {
201
		TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
202
		TASK->udebug.begin_call = NULL;
203
		reply = 1; /* immediate reply */
204
	} else {
205
		reply = 0; /* no reply */
206
	}
207
 
4337 svoboda 208
	/* Set udebug.active on all of the task's userspace threads. */
3438 svoboda 209
 
210
	for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
211
		t = list_get_instance(cur, thread_t, th_link);
212
 
213
		mutex_lock(&t->udebug.lock);
214
		if ((t->flags & THREAD_FLAG_USPACE) != 0)
4337 svoboda 215
			t->udebug.active = true;
3438 svoboda 216
		mutex_unlock(&t->udebug.lock);
217
	}
218
 
219
	mutex_unlock(&TASK->udebug.lock);
220
 
3441 svoboda 221
	LOG("udebug_begin() done (%s)\n", 
3438 svoboda 222
	    reply ? "reply" : "stoppability wait");
223
 
224
	return reply;
225
}
226
 
3474 svoboda 227
/** Finish debugging the current task.
228
 *
229
 * Closes the debugging session for the current task.
230
 * @return Zero on success or negative error code.
231
 */
3438 svoboda 232
int udebug_end(void)
233
{
234
	int rc;
235
 
3441 svoboda 236
	LOG("udebug_end()\n");
3438 svoboda 237
 
238
	mutex_lock(&TASK->udebug.lock);
3441 svoboda 239
	LOG("task %" PRIu64 "\n", TASK->taskid);
3438 svoboda 240
 
241
	rc = udebug_task_cleanup(TASK);
242
 
243
	mutex_unlock(&TASK->udebug.lock);
244
 
245
	return rc;
246
}
247
 
3474 svoboda 248
/** Set the event mask.
249
 *
250
 * Sets the event mask that determines which events are enabled.
251
 *
252
 * @param mask	Or combination of events that should be enabled.
253
 * @return	Zero on success or negative error code.
254
 */
3438 svoboda 255
int udebug_set_evmask(udebug_evmask_t mask)
256
{
3441 svoboda 257
	LOG("udebug_set_mask()\n");
3438 svoboda 258
 
259
	mutex_lock(&TASK->udebug.lock);
260
 
261
	if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
262
		mutex_unlock(&TASK->udebug.lock);
3441 svoboda 263
		LOG("udebug_set_mask(): not active debuging session\n");
3438 svoboda 264
 
265
		return EINVAL;
266
	}
267
 
268
	TASK->udebug.evmask = mask;
269
 
270
	mutex_unlock(&TASK->udebug.lock);
271
 
272
	return 0;
273
}
274
 
3474 svoboda 275
/** Give thread GO.
276
 *
3674 svoboda 277
 * Upon recieving a go message, the thread is given GO. Being GO
3474 svoboda 278
 * means the thread is allowed to execute userspace code (until
279
 * a debugging event or STOP occurs, at which point the thread loses GO.
280
 *
281
 * @param t	The thread to operate on (unlocked and need not be valid).
282
 * @param call	The GO call that we are servicing.
283
 */
3438 svoboda 284
int udebug_go(thread_t *t, call_t *call)
285
{
286
	int rc;
287
 
3674 svoboda 288
	/* On success, this will lock t->udebug.lock. */
3438 svoboda 289
	rc = _thread_op_begin(t, false);
290
	if (rc != EOK) {
291
		return rc;
292
	}
293
 
294
	t->udebug.go_call = call;
3674 svoboda 295
	t->udebug.go = true;
3438 svoboda 296
	t->udebug.cur_event = 0;	/* none */
297
 
298
	/*
3674 svoboda 299
	 * Neither t's lock nor threads_lock may be held during wakeup.
3438 svoboda 300
	 */
301
	waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
302
 
303
	_thread_op_end(t);
304
 
305
	return 0;
306
}
307
 
3474 svoboda 308
/** Stop a thread (i.e. take its GO away)
309
 *
310
 * Generates a STOP event as soon as the thread becomes stoppable (i.e.
311
 * can be considered stopped).
312
 *
313
 * @param t	The thread to operate on (unlocked and need not be valid).
314
 * @param call	The GO call that we are servicing.
315
 */
3438 svoboda 316
int udebug_stop(thread_t *t, call_t *call)
317
{
318
	int rc;
319
 
3441 svoboda 320
	LOG("udebug_stop()\n");
3438 svoboda 321
 
322
	/*
323
	 * On success, this will lock t->udebug.lock. Note that this makes sure
324
	 * the thread is not stopped.
325
	 */
326
	rc = _thread_op_begin(t, true);
327
	if (rc != EOK) {
328
		return rc;
329
	}
330
 
3674 svoboda 331
	/* Take GO away from the thread. */
332
	t->udebug.go = false;
3438 svoboda 333
 
3674 svoboda 334
	if (t->udebug.stoppable != true) {
335
		/* Answer will be sent when the thread becomes stoppable. */
3438 svoboda 336
		_thread_op_end(t);
337
		return 0;
338
	}
339
 
340
	/*
3674 svoboda 341
	 * Answer GO call.
3438 svoboda 342
	 */
3441 svoboda 343
	LOG("udebug_stop - answering go call\n");
3438 svoboda 344
 
3674 svoboda 345
	/* Make sure nobody takes this call away from us. */
3438 svoboda 346
	call = t->udebug.go_call;
347
	t->udebug.go_call = NULL;
348
 
349
	IPC_SET_RETVAL(call->data, 0);
350
	IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP);
3441 svoboda 351
	LOG("udebug_stop/ipc_answer\n");
3438 svoboda 352
 
353
	THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
354
 
355
	_thread_op_end(t);
356
 
3674 svoboda 357
	mutex_lock(&TASK->udebug.lock);
3438 svoboda 358
	ipc_answer(&TASK->answerbox, call);
359
	mutex_unlock(&TASK->udebug.lock);
360
 
3441 svoboda 361
	LOG("udebog_stop/done\n");
3438 svoboda 362
	return 0;
363
}
364
 
3474 svoboda 365
/** Read the list of userspace threads in the current task.
366
 *
367
 * The list takes the form of a sequence of thread hashes (i.e. the pointers
368
 * to thread structures). A buffer of size @a buf_size is allocated and
369
 * a pointer to it written to @a buffer. The sequence of hashes is written
370
 * into this buffer.
371
 *
372
 * If the sequence is longer than @a buf_size bytes, only as much hashes
373
 * as can fit are copied. The number of thread hashes copied is stored
374
 * in @a n.
375
 *
376
 * The rationale for having @a buf_size is that this function is only
377
 * used for servicing the THREAD_READ message, which always specifies
378
 * a maximum size for the userspace buffer.
379
 *
380
 * @param buffer	The buffer for storing thread hashes.
381
 * @param buf_size	Buffer size in bytes.
382
 * @param n		The actual number of hashes copied will be stored here.
383
 */
3438 svoboda 384
int udebug_thread_read(void **buffer, size_t buf_size, size_t *n)
385
{
386
	thread_t *t;
387
	link_t *cur;
388
	unative_t tid;
389
	unsigned copied_ids;
390
	ipl_t ipl;
391
	unative_t *id_buffer;
392
	int flags;
393
	size_t max_ids;
394
 
3441 svoboda 395
	LOG("udebug_thread_read()\n");
3438 svoboda 396
 
397
	/* Allocate a buffer to hold thread IDs */
398
	id_buffer = malloc(buf_size, 0);
399
 
400
	mutex_lock(&TASK->udebug.lock);
401
 
402
	/* Verify task state */
403
	if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
404
		mutex_unlock(&TASK->udebug.lock);
405
		return EINVAL;
406
	}
407
 
408
	ipl = interrupts_disable();
409
	spinlock_lock(&TASK->lock);
410
	/* Copy down the thread IDs */
411
 
412
	max_ids = buf_size / sizeof(unative_t);
413
	copied_ids = 0;
414
 
415
	/* FIXME: make sure the thread isn't past debug shutdown... */
416
	for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
417
		/* Do not write past end of buffer */
418
		if (copied_ids >= max_ids) break;
419
 
420
		t = list_get_instance(cur, thread_t, th_link);
421
 
422
		spinlock_lock(&t->lock);
423
		flags = t->flags;
424
		spinlock_unlock(&t->lock);
425
 
3674 svoboda 426
		/* Not interested in kernel threads. */
3438 svoboda 427
		if ((flags & THREAD_FLAG_USPACE) != 0) {
428
			/* Using thread struct pointer as identification hash */
429
			tid = (unative_t) t;
430
			id_buffer[copied_ids++] = tid;
431
		}
432
	}
433
 
434
	spinlock_unlock(&TASK->lock);
435
	interrupts_restore(ipl);
436
 
437
	mutex_unlock(&TASK->udebug.lock);
438
 
439
	*buffer = id_buffer;
440
	*n = copied_ids * sizeof(unative_t);
441
 
442
	return 0;
443
}
444
 
3474 svoboda 445
/** Read the arguments of a system call.
446
 *
447
 * The arguments of the system call being being executed are copied
448
 * to an allocated buffer and a pointer to it is written to @a buffer.
449
 * The size of the buffer is exactly such that it can hold the maximum number
450
 * of system-call arguments.
451
 *
452
 * Unless the thread is currently blocked in a SYSCALL_B or SYSCALL_E event,
453
 * this function will fail with an EINVAL error code.
454
 *
455
 * @param buffer	The buffer for storing thread hashes.
456
 */
3438 svoboda 457
int udebug_args_read(thread_t *t, void **buffer)
458
{
459
	int rc;
460
	unative_t *arg_buffer;
461
 
3674 svoboda 462
	/* Prepare a buffer to hold the arguments. */
3438 svoboda 463
	arg_buffer = malloc(6 * sizeof(unative_t), 0);
464
 
3674 svoboda 465
	/* On success, this will lock t->udebug.lock. */
3438 svoboda 466
	rc = _thread_op_begin(t, false);
467
	if (rc != EOK) {
468
		return rc;
469
	}
470
 
3674 svoboda 471
	/* Additionally we need to verify that we are inside a syscall. */
3438 svoboda 472
	if (t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B &&
473
	    t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E) {
474
		_thread_op_end(t);
475
		return EINVAL;
476
	}
477
 
3674 svoboda 478
	/* Copy to a local buffer before releasing the lock. */
3438 svoboda 479
	memcpy(arg_buffer, t->udebug.syscall_args, 6 * sizeof(unative_t));
480
 
481
	_thread_op_end(t);
482
 
483
	*buffer = arg_buffer;
484
	return 0;
485
}
486
 
3474 svoboda 487
/** Read the memory of the debugged task.
488
 *
489
 * Reads @a n bytes from the address space of the debugged task, starting
490
 * from @a uspace_addr. The bytes are copied into an allocated buffer
491
 * and a pointer to it is written into @a buffer.
492
 *
493
 * @param uspace_addr	Address from where to start reading.
494
 * @param n		Number of bytes to read.
495
 * @param buffer	For storing a pointer to the allocated buffer.
496
 */
3438 svoboda 497
int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer)
498
{
499
	void *data_buffer;
500
	int rc;
501
 
502
	/* Verify task state */
503
	mutex_lock(&TASK->udebug.lock);
504
 
505
	if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
506
		mutex_unlock(&TASK->udebug.lock);
507
		return EBUSY;
508
	}
509
 
510
	data_buffer = malloc(n, 0);
511
 
512
	/* NOTE: this is not strictly from a syscall... but that shouldn't
513
	 * be a problem */
514
	rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n);
515
	mutex_unlock(&TASK->udebug.lock);
516
 
517
	if (rc != 0) return rc;
518
 
519
	*buffer = data_buffer;
520
	return 0;
521
}
522
 
523
/** @}
524
 */