diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index d2f9c0e87cd3..818efa7c4da3 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 #include @@ -49,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -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 20622a560b5d..653dfd57b946 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 9e4f14f163cf..92203f07539e 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