Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / lib / libpthread_dbg / pthread_dbg.c
blob0f14d3cbcae81ffa48231eeb69bf4439c1e8e1b9
1 /* $NetBSD: pthread_dbg.c,v 1.39 2007/10/16 15:06:11 ad Exp $ */
3 /*-
4 * Copyright (c) 2002 Wasabi Systems, Inc.
5 * All rights reserved.
7 * Written by Nathan J. Williams for Wasabi Systems, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific
23 * prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
38 #include <sys/cdefs.h>
39 __RCSID("$NetBSD: pthread_dbg.c,v 1.39 2007/10/16 15:06:11 ad Exp $");
41 #define __EXPOSE_STACK 1
43 #include <sys/param.h>
44 #include <sys/types.h>
45 #include <sys/lock.h>
47 #include <stddef.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <errno.h>
51 #include <unistd.h>
52 #include <lwp.h>
54 #include <machine/reg.h>
56 #include <pthread.h>
57 #include <pthread_int.h>
58 #include <pthread_dbg.h>
59 #include <pthread_dbg_int.h>
61 #define PT_STACKMASK (proc->stackmask)
63 /* Compensate for debuggers that want a zero ID to be a sentinel */
64 #define TN_OFFSET 1
66 static int td__getthread(td_proc_t *proc, caddr_t addr, td_thread_t **threadp);
68 int
69 td_open(struct td_proc_callbacks_t *cb, void *arg, td_proc_t **procp)
71 td_proc_t *proc;
72 caddr_t addr;
73 int dbg;
74 int val;
76 proc = malloc(sizeof(*proc));
77 if (proc == NULL)
78 return TD_ERR_NOMEM;
79 proc->cb = cb;
80 proc->arg = arg;
82 val = LOOKUP(proc, "pthread__dbg", &addr);
83 if (val != 0) {
84 if (val == TD_ERR_NOSYM)
85 val = TD_ERR_NOLIB;
86 goto error;
88 proc->dbgaddr = addr;
90 val = LOOKUP(proc, "pthread__allqueue", &addr);
91 if (val != 0) {
92 if (val == TD_ERR_NOSYM)
93 val = TD_ERR_NOLIB;
94 goto error;
96 proc->allqaddr = addr;
98 val = LOOKUP(proc, "pthread__tsd_alloc", &addr);
99 if (val != 0) {
100 if (val == TD_ERR_NOSYM)
101 val = TD_ERR_NOLIB;
102 goto error;
104 proc->tsdallocaddr = addr;
106 val = LOOKUP(proc, "pthread__tsd_destructors", &addr);
107 if (val != 0) {
108 if (val == TD_ERR_NOSYM)
109 val = TD_ERR_NOLIB;
110 goto error;
112 proc->tsddestaddr = addr;
114 val = READ(proc, proc->dbgaddr, &dbg, sizeof(int));
115 if (val != 0)
116 goto error;
118 if (dbg != 0) {
119 /* Another instance of libpthread_dbg is already attached. */
120 val = TD_ERR_INUSE;
121 goto error;
124 val = LOOKUP(proc, "pthread__stacksize_lg", &addr);
125 if (val == 0)
126 proc->stacksizeaddr = addr;
127 else
128 proc->stacksizeaddr = NULL;
129 proc->stacksizelg = -1;
130 proc->stacksize = 0;
131 proc->stackmask = 0;
133 proc->regbuf = NULL;
134 proc->fpregbuf = NULL;
136 dbg = getpid();
138 * If this fails it probably means we're debugging a core file and
139 * can't write to it.
140 * If it's something else we'll lose the next time we hit WRITE,
141 * but not before, and that's OK.
143 WRITE(proc, proc->dbgaddr, &dbg, sizeof(int));
145 PTQ_INIT(&proc->threads);
147 *procp = proc;
149 return 0;
151 error:
152 free(proc);
153 return val;
157 td_close(td_proc_t *proc)
159 int dbg;
160 td_thread_t *t, *next;
162 dbg = 0;
164 * Error returns from this write are mot really a problem;
165 * the process doesn't exist any more.
167 WRITE(proc, proc->dbgaddr, &dbg, sizeof(int));
169 /* Deallocate the list of thread structures */
170 for (t = PTQ_FIRST(&proc->threads); t; t = next) {
171 next = PTQ_NEXT(t, list);
172 PTQ_REMOVE(&proc->threads, t, list);
173 free(t);
175 if (proc->regbuf != NULL) {
176 free(proc->regbuf);
177 free(proc->fpregbuf);
180 free(proc);
181 return 0;
186 td_thr_iter(td_proc_t *proc, int (*call)(td_thread_t *, void *), void *callarg)
188 int val;
189 caddr_t next;
190 pthread_queue_t allq;
191 td_thread_t *thread;
193 val = READ(proc, proc->allqaddr, &allq, sizeof(allq));
194 if (val != 0)
195 return val;
197 next = (void *)allq.ptqh_first;
198 while (next != NULL) {
199 val = td__getthread(proc, next, &thread);
200 if (val != 0)
201 return val;
202 val = (*call)(thread, callarg);
203 if (val != 0)
204 return 0;
206 val = READ(proc,
207 next + offsetof(struct __pthread_st, pt_allq.ptqe_next),
208 &next, sizeof(next));
209 if (val != 0)
210 return val;
212 return 0;
216 td_thr_info(td_thread_t *thread, td_thread_info_t *info)
218 int tmp, val;
220 val = READ(thread->proc, thread->addr, &tmp, sizeof(tmp));
221 if (val != 0)
222 return val;
224 if (tmp != PT_MAGIC)
225 return TD_ERR_BADTHREAD;
227 info->thread_addr = thread->addr;
228 if ((val = READ(thread->proc,
229 thread->addr + offsetof(struct __pthread_st, pt_state),
230 &tmp, sizeof(tmp))) != 0)
231 return val;
232 switch (tmp) {
233 case PT_STATE_RUNNING:
234 info->thread_state = TD_STATE_RUNNING;
235 break;
236 #ifdef XXXLWP
237 case PT_STATE_SUSPENDED:
238 info->thread_state = TD_STATE_SUSPENDED;
239 break;
240 #endif
241 case PT_STATE_ZOMBIE:
242 info->thread_state = TD_STATE_ZOMBIE;
243 break;
244 default:
245 info->thread_state = TD_STATE_UNKNOWN;
248 info->thread_type = TD_TYPE_USER;
250 if ((val = READ(thread->proc,
251 thread->addr + offsetof(struct __pthread_st, pt_stack),
252 &info->thread_stack, sizeof(stack_t))) != 0)
253 return val;
255 if ((val = READ(thread->proc,
256 thread->addr + offsetof(struct __pthread_st, pt_errno),
257 &info->thread_errno, sizeof(info->thread_errno))) != 0)
258 return val;
260 if ((val = READ(thread->proc,
261 thread->addr + offsetof(struct __pthread_st, pt_lid),
262 &info->thread_id, sizeof(info->thread_id))) != 0)
263 return val;
265 info->thread_id += TN_OFFSET;
267 return 0;
271 td_thr_getname(td_thread_t *thread, char *name, int len)
273 int val, tmp;
274 caddr_t nameaddr;
277 val = READ(thread->proc, thread->addr, &tmp, sizeof(tmp));
278 if (val != 0)
279 return val;
281 if (tmp != PT_MAGIC)
282 return TD_ERR_BADTHREAD;
284 if ((val = READ(thread->proc,
285 thread->addr + offsetof(struct __pthread_st, pt_name),
286 &nameaddr, sizeof(nameaddr))) != 0)
287 return val;
289 if (nameaddr == 0)
290 name[0] = '\0';
291 else if ((val = READ(thread->proc, nameaddr,
292 name, (size_t)MIN(PTHREAD_MAX_NAMELEN_NP, len))) != 0)
293 return val;
295 return 0;
299 td_thr_getregs(td_thread_t *thread, int regset, void *buf)
301 int tmp, val;
303 if ((val = READ(thread->proc,
304 thread->addr + offsetof(struct __pthread_st, pt_state),
305 &tmp, sizeof(tmp))) != 0)
306 return val;
308 switch (tmp) {
309 case PT_STATE_RUNNING:
311 * The register state of the thread is live in the
312 * inferior process's register state.
314 val = GETREGS(thread->proc, regset, thread->lwp, buf);
315 if (val != 0)
316 return val;
317 break;
318 case PT_STATE_ZOMBIE:
319 default:
320 return TD_ERR_BADTHREAD;
323 return 0;
327 td_thr_setregs(td_thread_t *thread, int regset, void *buf)
329 int val, tmp;
331 if ((val = READ(thread->proc,
332 thread->addr + offsetof(struct __pthread_st, pt_state),
333 &tmp, sizeof(tmp))) != 0)
334 return val;
336 switch (tmp) {
337 case PT_STATE_RUNNING:
339 * The register state of the thread is live in the
340 * inferior process's register state.
342 val = SETREGS(thread->proc, regset, thread->lwp, buf);
343 if (val != 0)
344 return val;
345 break;
346 case PT_STATE_ZOMBIE:
347 default:
348 return TD_ERR_BADTHREAD;
351 return 0;
355 td_map_pth2thr(td_proc_t *proc, pthread_t thread, td_thread_t **threadp)
357 int magic, val;
359 val = READ(proc, (void *)thread, &magic, sizeof(magic));
360 if (val != 0)
361 return val;
363 if (magic != PT_MAGIC)
364 return TD_ERR_NOOBJ;
366 val = td__getthread(proc, (void *)thread, threadp);
367 if (val != 0)
368 return val;
370 return 0;
374 td_map_id2thr(td_proc_t *proc, int threadid, td_thread_t **threadp)
376 int val, num;
377 caddr_t next;
378 pthread_queue_t allq;
379 td_thread_t *thread;
382 val = READ(proc, proc->allqaddr, &allq, sizeof(allq));
383 if (val != 0)
384 return val;
386 /* Correct for offset */
387 threadid -= TN_OFFSET;
388 next = (void *)allq.ptqh_first;
389 while (next != NULL) {
390 val = READ(proc,
391 next + offsetof(struct __pthread_st, pt_lid),
392 &num, sizeof(num));
394 if (num == threadid)
395 break;
397 val = READ(proc,
398 next + offsetof(struct __pthread_st, pt_allq.ptqe_next),
399 &next, sizeof(next));
400 if (val != 0)
401 return val;
404 if (next == 0) {
405 /* A matching thread was not found. */
406 return TD_ERR_NOOBJ;
409 val = td__getthread(proc, next, &thread);
410 if (val != 0)
411 return val;
412 *threadp = thread;
414 return 0;
418 td_tsd_iter(td_proc_t *proc,
419 int (*call)(pthread_key_t, void (*)(void *), void *), void *arg)
421 int val;
422 int i;
423 void *allocated;
424 void (*destructor)(void *);
426 for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
427 val = READ(proc, proc->tsdallocaddr + i * sizeof(allocated),
428 &allocated, sizeof(allocated));
429 if (val != 0)
430 return val;
432 if ((uintptr_t)allocated) {
433 val = READ(proc, proc->tsddestaddr +
434 i * sizeof(destructor),
435 &destructor, sizeof(destructor));
436 if (val != 0)
437 return val;
439 val = (call)(i, destructor, arg);
440 if (val != 0)
441 return val;
445 return 0;
448 /* Suspend a thread from running */
450 td_thr_suspend(td_thread_t *thread)
452 int tmp, val;
454 /* validate the thread */
455 val = READ(thread->proc, thread->addr, &tmp, sizeof(tmp));
456 if (val != 0)
457 return val;
458 if (tmp != PT_MAGIC)
459 return TD_ERR_BADTHREAD;
461 val = READ(thread->proc, thread->addr +
462 offsetof(struct __pthread_st, pt_lid),
463 &tmp, sizeof(tmp));
464 if (val != 0)
465 return val;
467 /* XXXLWP continue the sucker */;
469 return 0;
472 /* Restore a suspended thread to its previous state */
474 td_thr_resume(td_thread_t *thread)
476 int tmp, val;
478 /* validate the thread */
479 val = READ(thread->proc, thread->addr, &tmp, sizeof(tmp));
480 if (val != 0)
481 return val;
482 if (tmp != PT_MAGIC)
483 return TD_ERR_BADTHREAD;
485 val = READ(thread->proc, thread->addr +
486 offsetof(struct __pthread_st, pt_lid),
487 &tmp, sizeof(tmp));
488 if (val != 0)
489 return val;
491 /* XXXLWP continue the sucker */;
493 return 0;
496 static int
497 td__getthread(td_proc_t *proc, caddr_t addr, td_thread_t **threadp)
499 td_thread_t *thread;
502 * Check if we've allocated a descriptor for this thread.
503 * Sadly, this makes iterating over a set of threads O(N^2)
504 * in the number of threads. More sophisticated data structures
505 * can wait.
507 PTQ_FOREACH(thread, &proc->threads, list) {
508 if (thread->addr == addr)
509 break;
511 if (thread == NULL) {
512 thread = malloc(sizeof(*thread));
513 if (thread == NULL)
514 return TD_ERR_NOMEM;
515 thread->proc = proc;
516 thread->addr = addr;
517 thread->lwp = 0;
518 PTQ_INSERT_HEAD(&proc->threads, thread, list);
521 *threadp = thread;
522 return 0;
526 td_thr_tsd(td_thread_t *thread, pthread_key_t key, void **value)
528 int val;
530 val = READ(thread->proc, thread->addr +
531 offsetof(struct __pthread_st, pt_specific) +
532 key * sizeof(void *), value, sizeof(*value));
534 return val;