diff options
author | rwatson <rwatson@FreeBSD.org> | 2007-12-02 21:52:18 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2007-12-02 21:52:18 +0000 |
commit | e891688481c41562e510d6f1861bff8c23833f7a (patch) | |
tree | 5568edda37125a23b81e8e575a2b52506d89a7cd | |
parent | 47c04783148c89c69ab3bc4802e99cf28c31faf3 (diff) | |
download | FreeBSD-src-e891688481c41562e510d6f1861bff8c23833f7a.zip FreeBSD-src-e891688481c41562e510d6f1861bff8c23833f7a.tar.gz |
Add another new sysctl in support of the forthcoming procstat(1) to
support its -k argument:
kern.proc.kstack - dump the kernel stack of a process, if debugging
is permitted.
This sysctl is present if either "options DDB" or "options STACK" is
compiled into the kernel. Having support for tracing the kernel
stacks of processes from user space makes it much easier to debug
(or understand) specific wmesg's while avoiding the need to enter
DDB in order to determine the path by which a process came to be
blocked on a particular wait channel or lock.
-rw-r--r-- | sys/kern/kern_proc.c | 106 | ||||
-rw-r--r-- | sys/sys/sysctl.h | 1 | ||||
-rw-r--r-- | sys/sys/user.h | 19 |
3 files changed, 126 insertions, 0 deletions
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index d2f9c0e..818efa7 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_ktrace.h" #include "opt_kstack_pages.h" +#include "opt_stack.h" #include <sys/param.h> #include <sys/systm.h> @@ -49,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysent.h> #include <sys/sched.h> #include <sys/smp.h> +#include <sys/stack.h> #include <sys/sysctl.h> #include <sys/filedesc.h> #include <sys/tty.h> @@ -1440,6 +1442,105 @@ sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) return (error); } +#if defined(STACK) || defined(DDB) +static int +sysctl_kern_proc_kstack(SYSCTL_HANDLER_ARGS) +{ + struct kinfo_kstack *kkstp; + int error, i, *name, numthreads; + lwpid_t *lwpidarray; + struct thread *td; + struct stack *st; + struct sbuf sb; + struct proc *p; + + name = (int *)arg1; + if ((p = pfind((pid_t)name[0])) == NULL) + return (ESRCH); + if ((error = p_candebug(curthread, p))) { + PROC_UNLOCK(p); + return (error); + } + _PHOLD(p); + PROC_UNLOCK(p); + + kkstp = malloc(sizeof(*kkstp), M_TEMP, M_WAITOK); + st = stack_create(); + + lwpidarray = NULL; + numthreads = 0; + PROC_SLOCK(p); +repeat: + if (numthreads < p->p_numthreads) { + if (lwpidarray != NULL) { + free(lwpidarray, M_TEMP); + lwpidarray = NULL; + } + numthreads = p->p_numthreads; + PROC_SUNLOCK(p); + lwpidarray = malloc(sizeof(*lwpidarray) * numthreads, M_TEMP, + M_WAITOK | M_ZERO); + PROC_SLOCK(p); + goto repeat; + } + PROC_SUNLOCK(p); + i = 0; + + /* + * XXXRW: During the below loop, execve(2) and countless other sorts + * of changes could have taken place. Should we check to see if the + * vmspace has been replaced, or the like, in order to prevent + * giving a snapshot that spans, say, execve(2), with some threads + * before and some after? Among other things, the credentials could + * have changed, in which case the right to extract debug info might + * no longer be assured. + */ + PROC_LOCK(p); + FOREACH_THREAD_IN_PROC(p, td) { + KASSERT(i < numthreads, + ("sysctl_kern_proc_kstack: numthreads")); + lwpidarray[i] = td->td_tid; + i++; + } + numthreads = i; + for (i = 0; i < numthreads; i++) { + td = thread_find(p, lwpidarray[i]); + if (td == NULL) { + continue; + } + bzero(kkstp, sizeof(*kkstp)); + (void)sbuf_new(&sb, kkstp->kkst_trace, + sizeof(kkstp->kkst_trace), SBUF_FIXEDLEN); + thread_lock(td); + kkstp->kkst_tid = td->td_tid; + if (TD_IS_SWAPPED(td)) + kkstp->kkst_state = KKST_STATE_SWAPPED; + else if (TD_IS_RUNNING(td)) + kkstp->kkst_state = KKST_STATE_RUNNING; + else { + kkstp->kkst_state = KKST_STATE_STACKOK; + stack_save_td(st, td); + } + thread_unlock(td); + PROC_UNLOCK(p); + stack_sbuf_print(&sb, st); + sbuf_finish(&sb); + sbuf_delete(&sb); + error = SYSCTL_OUT(req, kkstp, sizeof(*kkstp)); + PROC_LOCK(p); + if (error) + break; + } + _PRELE(p); + PROC_UNLOCK(p); + if (lwpidarray != NULL) + free(lwpidarray, M_TEMP); + stack_destroy(st); + free(kkstp, M_TEMP); + return (error); +} +#endif + SYSCTL_NODE(_kern, KERN_PROC, proc, CTLFLAG_RD, 0, "Process table"); SYSCTL_PROC(_kern_proc, KERN_PROC_ALL, all, CTLFLAG_RD|CTLTYPE_STRUCT, @@ -1511,3 +1612,8 @@ static SYSCTL_NODE(_kern_proc, (KERN_PROC_PROC | KERN_PROC_INC_THREAD), proc_td, static SYSCTL_NODE(_kern_proc, KERN_PROC_VMMAP, vmmap, CTLFLAG_RD, sysctl_kern_proc_vmmap, "Process vm map entries"); + +#if defined(STACK) || defined(DDB) +static SYSCTL_NODE(_kern_proc, KERN_PROC_KSTACK, kstack, CTLFLAG_RD, + sysctl_kern_proc_kstack, "Process kernel stacks"); +#endif diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index 20622a5..653dfd5 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -458,6 +458,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry); #define KERN_PROC_PATHNAME 12 /* path to executable */ #define KERN_PROC_VMMAP 13 /* VM map entries for process */ #define KERN_PROC_FILEDESC 14 /* File descriptors for process */ +#define KERN_PROC_KSTACK 15 /* Kernel stacks for process */ #define KERN_PROC_INC_THREAD 0x10 /* * modifier for pid, pgrp, tty, * uid, ruid, gid, rgid and proc diff --git a/sys/sys/user.h b/sys/sys/user.h index 9e4f14f..92203f0 100644 --- a/sys/sys/user.h +++ b/sys/sys/user.h @@ -319,4 +319,23 @@ struct kinfo_vmentry { int _kve_ispare[8]; /* Space for more stuff. */ }; +/* + * The KERN_PROC_KSTACK sysctl allows a process to dump the kernel stacks of + * another process as a series of entries. Each stack is represented by a + * series of symbol names and offsets as generated by stack_sbuf_print(9). + */ +#define KKST_MAXLEN 1024 + +#define KKST_STATE_STACKOK 0 /* Stack is valid. */ +#define KKST_STATE_SWAPPED 1 /* Stack swapped out. */ +#define KKST_STATE_RUNNING 2 /* Stack ephemeral. */ + +struct kinfo_kstack { + lwpid_t kkst_tid; /* ID of thread. */ + int kkst_state; /* Validity of stack. */ + char kkst_trace[KKST_MAXLEN]; /* String representing stack. */ + void *_kkst_pspare[8]; /* Space for more stuff. */ + int _kkst_ispare[8]; /* Space for more stuff. */ +}; + #endif |