commit 59582e48d1c49c3a7451cc2bac5eee40d866cbb7 Author: Mikolaj Golub Date: Mon Feb 11 00:02:16 2013 +0200 Re-factor the code to provide kern_proc_filedesc_out(), kern_proc_out(), and kern_proc_vmmap_out() functions to output a process kinfo structures to sbuf, to make the code reusable. The functions are going to be used in the coredump routine to store procstat info in the core program header notes. diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 87d75ea..ff87fb9 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -3132,9 +3133,9 @@ CTASSERT(sizeof(struct kinfo_file) == KINFO_FILE_SIZE); #endif static int -export_fd_for_sysctl(void *data, int type, int fd, int fflags, int refcnt, +export_fd_to_sb(void *data, int type, int fd, int fflags, int refcnt, int64_t offset, int fd_is_cap, cap_rights_t fd_cap_rights, - struct kinfo_file *kif, struct sysctl_req *req) + struct kinfo_file *kif, struct sbuf *sb) { struct { int fflag; @@ -3208,35 +3209,31 @@ export_fd_for_sysctl(void *data, int type, int fd, int fflags, int refcnt, kif->kf_structsize = offsetof(struct kinfo_file, kf_path) + strlen(kif->kf_path) + 1; kif->kf_structsize = roundup(kif->kf_structsize, sizeof(uint64_t)); - error = SYSCTL_OUT(req, kif, kif->kf_structsize); + error = sbuf_bcat(sb, kif, kif->kf_structsize); return (error); } + /* - * Get per-process file descriptors for use by procstat(1), et al. + * Store a process file descriptor information to sbuf. + * + * Takes a locked proc as argument, and returns with the proc unlocked. */ -static int -sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) +int +kern_proc_filedesc_out(struct proc *p, struct sbuf *sb) { struct file *fp; struct filedesc *fdp; struct kinfo_file *kif; - struct proc *p; struct vnode *cttyvp, *textvp, *tracevp; - size_t oldidx; int64_t offset; void *data; - int error, i, *name; + int error, i; int fd_is_cap, type, refcnt, fflags; cap_rights_t fd_cap_rights; - name = (int *)arg1; - if ((p = pfind((pid_t)name[0])) == NULL) - return (ESRCH); - if ((error = p_candebug(curthread, p))) { - PROC_UNLOCK(p); - return (error); - } + PROC_LOCK_ASSERT(p, MA_OWNED); + /* ktrace vnode */ tracevp = p->p_tracevp; if (tracevp != NULL) @@ -3256,14 +3253,15 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) PROC_UNLOCK(p); kif = malloc(sizeof(*kif), M_TEMP, M_WAITOK); if (tracevp != NULL) - export_fd_for_sysctl(tracevp, KF_TYPE_VNODE, KF_FD_TYPE_TRACE, - FREAD | FWRITE, -1, -1, 0, 0, kif, req); + export_fd_to_sb(tracevp, KF_TYPE_VNODE, KF_FD_TYPE_TRACE, + FREAD | FWRITE, -1, -1, 0, 0, kif, sb); if (textvp != NULL) - export_fd_for_sysctl(textvp, KF_TYPE_VNODE, KF_FD_TYPE_TEXT, - FREAD, -1, -1, 0, 0, kif, req); + export_fd_to_sb(textvp, KF_TYPE_VNODE, KF_FD_TYPE_TEXT, + FREAD, -1, -1, 0, 0, kif, sb); if (cttyvp != NULL) - export_fd_for_sysctl(cttyvp, KF_TYPE_VNODE, KF_FD_TYPE_CTTY, - FREAD | FWRITE, -1, -1, 0, 0, kif, req); + export_fd_to_sb(cttyvp, KF_TYPE_VNODE, KF_FD_TYPE_CTTY, + FREAD | FWRITE, -1, -1, 0, 0, kif, sb); + error = 0; if (fdp == NULL) goto fail; FILEDESC_SLOCK(fdp); @@ -3272,8 +3270,8 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) vref(fdp->fd_cdir); data = fdp->fd_cdir; FILEDESC_SUNLOCK(fdp); - export_fd_for_sysctl(data, KF_TYPE_VNODE, KF_FD_TYPE_CWD, - FREAD, -1, -1, 0, 0, kif, req); + export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_CWD, + FREAD, -1, -1, 0, 0, kif, sb); FILEDESC_SLOCK(fdp); } /* root directory */ @@ -3281,8 +3279,8 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) vref(fdp->fd_rdir); data = fdp->fd_rdir; FILEDESC_SUNLOCK(fdp); - export_fd_for_sysctl(data, KF_TYPE_VNODE, KF_FD_TYPE_ROOT, - FREAD, -1, -1, 0, 0, kif, req); + export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_ROOT, + FREAD, -1, -1, 0, 0, kif, sb); FILEDESC_SLOCK(fdp); } /* jail directory */ @@ -3290,8 +3288,8 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) vref(fdp->fd_jdir); data = fdp->fd_jdir; FILEDESC_SUNLOCK(fdp); - export_fd_for_sysctl(data, KF_TYPE_VNODE, KF_FD_TYPE_JAIL, - FREAD, -1, -1, 0, 0, kif, req); + export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_JAIL, + FREAD, -1, -1, 0, 0, kif, sb); FILEDESC_SLOCK(fdp); } for (i = 0; i < fdp->fd_nfiles; i++) { @@ -3386,26 +3384,14 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) * re-validate and re-evaluate its properties when * the loop continues. */ - oldidx = req->oldidx; if (type == KF_TYPE_VNODE || type == KF_TYPE_FIFO) FILEDESC_SUNLOCK(fdp); - error = export_fd_for_sysctl(data, type, i, fflags, refcnt, - offset, fd_is_cap, fd_cap_rights, kif, req); + error = export_fd_to_sb(data, type, i, fflags, refcnt, + offset, fd_is_cap, fd_cap_rights, kif, sb); if (type == KF_TYPE_VNODE || type == KF_TYPE_FIFO) FILEDESC_SLOCK(fdp); - if (error) { - if (error == ENOMEM) { - /* - * The hack to keep the ABI of sysctl - * kern.proc.filedesc intact, but not - * to account a partially copied - * kinfo_file into the oldidx. - */ - req->oldidx = oldidx; - error = 0; - } + if (error) break; - } } FILEDESC_SUNLOCK(fdp); fail: @@ -3415,6 +3401,35 @@ fail: return (error); } +#define FILEDESC_SBUF_SIZE (sizeof(struct kinfo_file) * 5) + +/* + * Get per-process file descriptors for use by procstat(1), et al. + */ +static int +sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) +{ + struct sbuf sb; + struct proc *p; + int error, error2, *name; + + name = (int *)arg1; + + sbuf_new_for_sysctl(&sb, NULL, FILEDESC_SBUF_SIZE, req); + error = pget((pid_t)name[0], PGET_CANDEBUG, &p); + if (error != 0) { + sbuf_delete(&sb); + return (error); + } + error = kern_proc_filedesc_out(p, &sb); + error2 = sbuf_finish(&sb); + sbuf_delete(&sb); + error = error != 0 ? error : error2; + if (error == ENOMEM) + error = 0; /* XXXMG */ + return (error); +} + int vntype_to_kinfo(int vtype) { diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 2a74a59..e51d3ac 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -128,8 +128,6 @@ static void doenterpgrp(struct proc *, struct pgrp *); static void orphanpg(struct pgrp *pg); static void fill_kinfo_aggregate(struct proc *p, struct kinfo_proc *kp); static void fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp); -static void fill_kinfo_thread(struct thread *td, struct kinfo_proc *kp, - int preferthread); static void pgadjustjobc(struct pgrp *pgrp, int entering); static void pgdelete(struct pgrp *); static int proc_ctor(void *mem, int size, void *arg, int flags); @@ -1090,6 +1088,7 @@ zpfind(pid_t pid) #define KERN_PROC_ZOMBMASK 0x3 #define KERN_PROC_NOTHREADS 0x4 +#define KERN_PROC_MASK32 0x8 #ifdef COMPAT_FREEBSD32 @@ -1192,57 +1191,87 @@ freebsd32_kinfo_proc_out(const struct kinfo_proc *ki, struct kinfo_proc32 *ki32) } static int -sysctl_out_proc_copyout(struct kinfo_proc *ki, struct sysctl_req *req) +freebsd32_kinfo_proc_out_sb(struct kinfo_proc *ki, struct sbuf *sb) { struct kinfo_proc32 ki32; - int error; - if (req->flags & SCTL_MASK32) { - freebsd32_kinfo_proc_out(ki, &ki32); - error = SYSCTL_OUT(req, &ki32, sizeof(struct kinfo_proc32)); - } else - error = SYSCTL_OUT(req, ki, sizeof(struct kinfo_proc)); - return (error); + freebsd32_kinfo_proc_out(ki, &ki32); + return (sbuf_bcat(sb, &ki32, sizeof(struct kinfo_proc32))); } -#else +#endif static int -sysctl_out_proc_copyout(struct kinfo_proc *ki, struct sysctl_req *req) +kinfo_proc_out_sb(struct kinfo_proc *ki, struct sbuf *sb) { - return (SYSCTL_OUT(req, ki, sizeof(struct kinfo_proc))); + return (sbuf_bcat(sb, ki, sizeof(struct kinfo_proc))); } -#endif /* - * Must be called with the process locked and will return with it unlocked. + * Must be called with the process locked and will return unlocked. */ static int -sysctl_out_proc(struct proc *p, struct sysctl_req *req, int flags) +kern_proc_out_flagged(struct proc *p, struct sbuf *sb, int flags) { struct thread *td; struct kinfo_proc kinfo_proc; - int error = 0; - struct proc *np; - pid_t pid = p->p_pid; + int error; PROC_LOCK_ASSERT(p, MA_OWNED); MPASS(FIRST_THREAD_IN_PROC(p) != NULL); + error = 0; fill_kinfo_proc(p, &kinfo_proc); - if (flags & KERN_PROC_NOTHREADS) - error = sysctl_out_proc_copyout(&kinfo_proc, req); - else { + if ((flags & KERN_PROC_NOTHREADS) != 0) { +#ifdef COMPAT_FREEBSD32 + if ((flags & KERN_PROC_MASK32) != 0) + error = freebsd32_kinfo_proc_out_sb(&kinfo_proc, sb); + else +#endif + error = kinfo_proc_out_sb(&kinfo_proc, sb); + } else { FOREACH_THREAD_IN_PROC(p, td) { fill_kinfo_thread(td, &kinfo_proc, 1); - error = sysctl_out_proc_copyout(&kinfo_proc, req); +#ifdef COMPAT_FREEBSD32 + if ((flags & KERN_PROC_MASK32) != 0) + error = freebsd32_kinfo_proc_out_sb( + &kinfo_proc, sb); + else +#endif + error = kinfo_proc_out_sb(&kinfo_proc, sb); if (error) break; } } PROC_UNLOCK(p); - if (error) + return (error); +} + +int +kern_proc_out(struct proc *p, struct sbuf *sb) +{ + + return (kern_proc_out_flagged(p, sb, 0)); +} + +static int +sysctl_out_proc(struct proc *p, struct sysctl_req *req, int flags) +{ + struct sbuf sb; + struct kinfo_proc ki; + struct proc *np; + int error, error2; + pid_t pid; + + pid = p->p_pid; + sbuf_new_for_sysctl(&sb, (char *)&ki, sizeof(ki), req); + error = kern_proc_out_flagged(p, &sb, flags); + error2 = sbuf_finish(&sb); + sbuf_delete(&sb); + if (error != 0) return (error); - if (flags & KERN_PROC_ZOMBMASK) + else if (error2 != 0) + return (error2); + if ((flags & KERN_PROC_ZOMBMASK) != 0) np = zpfind(pid); else { if (pid == 0) @@ -1276,6 +1305,10 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) flags = 0; oid_number &= ~KERN_PROC_INC_THREAD; } +#ifdef COMPAT_FREEBSD32 + if (req->flags & SCTL_MASK32) + flags |= KERN_PROC_MASK32; +#endif if (oid_number == KERN_PROC_PID) { if (namelen != 1) return (EINVAL); @@ -2118,8 +2151,11 @@ sysctl_kern_proc_ovmmap(SYSCTL_HANDLER_ARGS) CTASSERT(sizeof(struct kinfo_vmentry) == KINFO_VMENTRY_SIZE); #endif -static int -sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) +/* + * Must be called with the process locked and will return unlocked. + */ +int +kern_proc_vmmap_out(struct proc *p, struct sbuf *sb) { vm_map_entry_t entry, tmp_entry; unsigned int last_timestamp; @@ -2127,16 +2163,15 @@ sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) struct kinfo_vmentry *kve; struct vattr va; struct ucred *cred; - int error, *name; + int error; struct vnode *vp; - struct proc *p; struct vmspace *vm; vm_map_t map; - name = (int *)arg1; - error = pget((pid_t)name[0], PGET_WANTREAD, &p); - if (error != 0) - return (error); + PROC_LOCK_ASSERT(p, MA_OWNED); + + _PHOLD(p); + PROC_UNLOCK(p); vm = vmspace_acquire_ref(p); if (vm == NULL) { PRELE(p); @@ -2144,6 +2179,7 @@ sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) } kve = malloc(sizeof(*kve), M_TEMP, M_WAITOK); + error = 0; map = &vm->vm_map; vm_map_lock_read(map); for (entry = map->header.next; entry != &map->header; @@ -2283,7 +2319,7 @@ sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) strlen(kve->kve_path) + 1; kve->kve_structsize = roundup(kve->kve_structsize, sizeof(uint64_t)); - error = SYSCTL_OUT(req, kve, kve->kve_structsize); + error = sbuf_bcat(sb, kve, kve->kve_structsize); vm_map_lock_read(map); if (error) break; @@ -2299,6 +2335,26 @@ sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) return (error); } +static int +sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) +{ + struct proc *p; + struct sbuf sb; + int error, error2, *name; + + name = (int *)arg1; + sbuf_new_for_sysctl(&sb, NULL, sizeof(struct kinfo_vmentry), req); + error = pget((pid_t)name[0], PGET_CANDEBUG | PGET_NOTWEXIT, &p); + if (error != 0) { + sbuf_delete(&sb); + return (error); + } + error = kern_proc_vmmap_out(p, &sb); + error2 = sbuf_finish(&sb); + sbuf_delete(&sb); + return (error != 0 ? error : error2); +} + #if defined(STACK) || defined(DDB) static int sysctl_kern_proc_kstack(SYSCTL_HANDLER_ARGS) diff --git a/sys/sys/user.h b/sys/sys/user.h index ddaccb8..afbb26e 100644 --- a/sys/sys/user.h +++ b/sys/sys/user.h @@ -493,6 +493,11 @@ struct kinfo_kstack { }; #ifdef _KERNEL +struct sbuf; + +int kern_proc_filedesc_out(struct proc *p, struct sbuf *sb); +int kern_proc_out(struct proc *p, struct sbuf *sb); +int kern_proc_vmmap_out(struct proc *p, struct sbuf *sb); int vntype_to_kinfo(int vtype); #endif /* !_KERNEL */ commit 77bff5d4932dce0db93064e38bae642e314d574a Author: Mikolaj Golub Date: Mon Feb 11 00:11:48 2013 +0200 Add a new set of notes to a process core dump to store kinfo_proc, kinfo_file, and kinfo_vmentry data. The userland tools (procstat(1)) will be taught to extract this data, so providing additional info for postmortem analysis. diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 4c1cb5b..e3040e8 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -65,6 +66,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include @@ -1035,6 +1037,16 @@ __elfN(freebsd_fixup)(register_t **stack_base, struct image_params *imgp) * Code for generating ELF core dumps. */ +#ifdef KINFO_FILE_SIZE +CTASSERT(sizeof(struct kinfo_file) == KINFO_FILE_SIZE); +#endif +#ifdef KINFO_PROC_SIZE +CTASSERT(sizeof(struct kinfo_proc) == KINFO_PROC_SIZE); +#endif +#ifdef KINFO_VMENTRY_SIZE +CTASSERT(sizeof(struct kinfo_vmentry) == KINFO_VMENTRY_SIZE); +#endif + typedef void (*segment_callback)(vm_map_entry_t, void *); /* Closure for cb_put_phdr(). */ @@ -1049,14 +1061,31 @@ struct sseg_closure { size_t size; /* Total size of all writable segments. */ }; +/* Procstat buffers. */ +struct pstat_bufs { + struct sbuf *proc_sb; + struct sbuf *fdesc_sb; + struct sbuf *vmmap_sb; +}; + +/* Core header info. */ +struct header_info { + size_t size; /* Header size. */ + void *data; /* Header data. */ + struct pstat_bufs *pstat; /* Procstat buffers. */ +}; + static void cb_put_phdr(vm_map_entry_t, void *); static void cb_size_segment(vm_map_entry_t, void *); static void each_writable_segment(struct thread *, segment_callback, void *); static int __elfN(corehdr)(struct thread *, struct vnode *, struct ucred *, - int, void *, size_t, gzFile); -static void __elfN(puthdr)(struct thread *, void *, size_t *, int); + int, struct header_info *, gzFile); +static void __elfN(puthdr)(struct thread *, struct header_info *, int); static void __elfN(putnote)(void *, size_t *, const char *, int, const void *, size_t); +static struct pstat_bufs *pstat_bufs_alloc(void); +static void pstat_bufs_free(struct pstat_bufs *); +static struct sbuf *pstat_sbuf_for_proc(struct proc *p, size_t extra); #ifdef COMPRESS_USER_CORES extern int compress_user_cores; @@ -1083,14 +1112,54 @@ core_output(struct vnode *vp, void *base, size_t len, off_t offset, return (error); } +static struct pstat_bufs * +pstat_bufs_alloc(void) +{ + + return (malloc(sizeof(struct pstat_bufs), M_TEMP, M_ZERO|M_WAITOK)); +} + +static void +pstat_bufs_free(struct pstat_bufs *pstat) +{ + + if (pstat->proc_sb != NULL) + sbuf_delete(pstat->proc_sb); + if (pstat->fdesc_sb != NULL) + sbuf_delete(pstat->fdesc_sb); + if (pstat->vmmap_sb != NULL) + sbuf_delete(pstat->vmmap_sb); + free(pstat, M_TEMP); +} + +struct sbuf * +pstat_sbuf_for_proc(struct proc *p, size_t extra) +{ + struct sbuf *sb; + size_t size; + + PROC_LOCK_ASSERT(p, MA_OWNED); +retry: + size = p->p_numthreads * sizeof(struct kinfo_proc) + extra; + PROC_UNLOCK(p); + sb = sbuf_new(NULL, NULL, size, SBUF_FIXEDLEN); + PROC_LOCK(p); + if (size != p->p_numthreads * sizeof(struct kinfo_proc) + extra) { + /* XXXMG: is it possible in coredump? */ + sbuf_delete(sb); + goto retry; + } + + return (sb); +} + int __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) { struct ucred *cred = td->td_ucred; int error = 0; struct sseg_closure seginfo; - void *hdr; - size_t hdrsize; + struct header_info hinfo; gzFile gzfile = Z_NULL; char *core_buf = NULL; @@ -1100,7 +1169,9 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) int doing_compress = flags & IMGACT_CORE_COMPRESS; #endif - hdr = NULL; + hinfo.size = 0; + hinfo.data = NULL; + hinfo.pstat = pstat_bufs_alloc(); #ifdef COMPRESS_USER_CORES if (doing_compress) { @@ -1130,22 +1201,21 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) /* * Calculate the size of the core file header area by making - * a dry run of generating it. Nothing is written, but the - * size is calculated. + * a dry run of generating it. Although header data is not written, + * only the size is calculated, prosctat data is collected to sbuf. */ - hdrsize = 0; - __elfN(puthdr)(td, (void *)NULL, &hdrsize, seginfo.count); + __elfN(puthdr)(td, &hinfo, seginfo.count); #ifdef RACCT PROC_LOCK(td->td_proc); - error = racct_add(td->td_proc, RACCT_CORE, hdrsize + seginfo.size); + error = racct_add(td->td_proc, RACCT_CORE, hinfo.size + seginfo.size); PROC_UNLOCK(td->td_proc); if (error != 0) { error = EFAULT; goto done; } #endif - if (hdrsize + seginfo.size >= limit) { + if (hinfo.size + seginfo.size >= limit) { error = EFAULT; goto done; } @@ -1154,13 +1224,12 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) * Allocate memory for building the header, fill it up, * and write it out. */ - hdr = malloc(hdrsize, M_TEMP, M_WAITOK); - if (hdr == NULL) { + hinfo.data = malloc(hinfo.size, M_TEMP, M_WAITOK); + if (hinfo.data == NULL) { error = EINVAL; goto done; } - error = __elfN(corehdr)(td, vp, cred, seginfo.count, hdr, hdrsize, - gzfile); + error = __elfN(corehdr)(td, vp, cred, seginfo.count, &hinfo, gzfile); /* Write the contents of all of the writable segments. */ if (error == 0) { @@ -1168,8 +1237,8 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) off_t offset; int i; - php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; - offset = hdrsize; + php = (Elf_Phdr *)((char *)hinfo.data + sizeof(Elf_Ehdr)) + 1; + offset = hinfo.size; for (i = 0; i < seginfo.count; i++) { error = core_output(vp, (caddr_t)(uintptr_t)php->p_vaddr, php->p_filesz, offset, cred, NOCRED, curthread, core_buf, gzfile); @@ -1193,7 +1262,8 @@ done: gzclose(gzfile); #endif - free(hdr, M_TEMP); + free(hinfo.data, M_TEMP); + pstat_bufs_free(hinfo.pstat); return (error); } @@ -1310,30 +1380,23 @@ each_writable_segment(td, func, closure) * the page boundary. */ static int -__elfN(corehdr)(td, vp, cred, numsegs, hdr, hdrsize, gzfile) - struct thread *td; - struct vnode *vp; - struct ucred *cred; - int numsegs; - size_t hdrsize; - void *hdr; - gzFile gzfile; +__elfN(corehdr)(struct thread *td, struct vnode *vp, struct ucred *cred, + int numsegs, struct header_info* hinfo, gzFile gzfile) { - size_t off; /* Fill in the header. */ - bzero(hdr, hdrsize); - off = 0; - __elfN(puthdr)(td, hdr, &off, numsegs); + bzero(hinfo->data, hinfo->size); + hinfo->size = 0; + __elfN(puthdr)(td, hinfo, numsegs); if (!gzfile) { /* Write it to the core file. */ - return (vn_rdwr_inchunks(UIO_WRITE, vp, hdr, hdrsize, (off_t)0, - UIO_SYSSPACE, IO_UNIT | IO_DIRECT, cred, NOCRED, NULL, - td)); + return (vn_rdwr_inchunks(UIO_WRITE, vp, hinfo->data, + hinfo->size, (off_t)0, UIO_SYSSPACE, + IO_UNIT | IO_DIRECT, cred, NOCRED, NULL, td)); } else { #ifdef COMPRESS_USER_CORES - if (gzwrite(gzfile, hdr, hdrsize) != hdrsize) { + if (gzwrite(gzfile, hinfo->data, hinfo->size) != hinfo->size) { log(LOG_WARNING, "Failed to compress core file header for process" " %s.\n", curproc->p_comm); @@ -1367,7 +1430,7 @@ typedef thrmisc_t elf_thrmisc_t; #endif static void -__elfN(puthdr)(struct thread *td, void *dst, size_t *off, int numsegs) +__elfN(puthdr)(struct thread *td, struct header_info *hinfo, int numsegs) { struct { elf_prstatus_t status; @@ -1381,7 +1444,14 @@ __elfN(puthdr)(struct thread *td, void *dst, size_t *off, int numsegs) elf_thrmisc_t *thrmisc; struct proc *p; struct thread *thr; - size_t ehoff, noteoff, notesz, phoff; + struct pstat_bufs *pstat; + struct sbuf *sb; + void *dst; + size_t ehoff, noteoff, notesz, *off, phoff; + int error, structsize; + + off = &hinfo->size; + dst = hinfo->data; p = td->td_proc; @@ -1392,6 +1462,7 @@ __elfN(puthdr)(struct thread *td, void *dst, size_t *off, int numsegs) *off += (numsegs + 1) * sizeof(Elf_Phdr); noteoff = *off; + /* * Don't allocate space for the notes if we're just calculating * the size of the header. We also don't collect the data. @@ -1467,6 +1538,46 @@ __elfN(puthdr)(struct thread *td, void *dst, size_t *off, int numsegs) if (thr == td) thr = TAILQ_NEXT(thr, td_plist); } + pstat = hinfo->pstat; + if (dst == NULL) { + /* The first pass. Collect pstat. */ + /* proc */ + PROC_LOCK(p); + sb = pstat_sbuf_for_proc(p, sizeof(structsize) + 1); + structsize = sizeof(struct kinfo_proc); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + kern_proc_out(p, sb); + error = sbuf_finish(sb); + KASSERT(error == 0, ("proc sbuf failed")); + KASSERT(pstat->proc_sb == NULL, ("proc_sb in use")); + pstat->proc_sb = sb; + /* fdesc */ + sb = sbuf_new_auto(); + structsize = sizeof(struct kinfo_file); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + PROC_LOCK(p); + kern_proc_filedesc_out(p, sb); + error = sbuf_finish(sb); + KASSERT(error == 0, ("fdesc sbuf failed")); + KASSERT(pstat->fdesc_sb == NULL, ("fdesc_sb in use")); + pstat->fdesc_sb = sb; + /* vmmap */ + sb = sbuf_new_auto(); + structsize = sizeof(struct kinfo_vmentry); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + PROC_LOCK(p); + kern_proc_vmmap_out(p, sb); + error = sbuf_finish(sb); + KASSERT(error == 0, ("vmmap sbuf failed")); + KASSERT(pstat->vmmap_sb == NULL, ("vmmap_sb in use")); + pstat->vmmap_sb = sb; + } + __elfN(putnote)(dst, off, "FreeBSD", NT_PROCSTAT_PROC, + sbuf_data(pstat->proc_sb), sbuf_len(pstat->proc_sb)); + __elfN(putnote)(dst, off, "FreeBSD", NT_PROCSTAT_FILES, + sbuf_data(pstat->fdesc_sb), sbuf_len(pstat->fdesc_sb)); + __elfN(putnote)(dst, off, "FreeBSD", NT_PROCSTAT_VMMAP, + sbuf_data(pstat->vmmap_sb), sbuf_len(pstat->vmmap_sb)); notesz = *off - noteoff; diff --git a/sys/sys/elf_common.h b/sys/sys/elf_common.h index 8f02ef1..ec4fee3 100644 --- a/sys/sys/elf_common.h +++ b/sys/sys/elf_common.h @@ -487,6 +487,9 @@ typedef struct { #define NT_FPREGSET 2 /* Floating point registers. */ #define NT_PRPSINFO 3 /* Process state info. */ #define NT_THRMISC 7 /* Thread miscellaneous info. */ +#define NT_PROCSTAT_PROC 8 /* Procstat proc info. */ +#define NT_PROCSTAT_FILES 9 /* Procstat files info. */ +#define NT_PROCSTAT_VMMAP 10 /* Procstat vmmap info. */ /* Symbol Binding - ELFNN_ST_BIND - st_info */ #define STB_LOCAL 0 /* Local symbol */ commit d66ed83f677a63c0ffad3323205abdaf6eafcd9e Author: Mikolaj Golub Date: Mon Feb 11 00:26:53 2013 +0200 Teach libprocstat(3) to extract procstat notes from a process core file. diff --git a/lib/libprocstat/Makefile b/lib/libprocstat/Makefile index a29afc7..1ba2398 100644 --- a/lib/libprocstat/Makefile +++ b/lib/libprocstat/Makefile @@ -6,6 +6,7 @@ LIB= procstat SRCS= cd9660.c \ common_kvm.c \ + core.c \ libprocstat.c \ msdosfs.c \ udf.c @@ -17,8 +18,8 @@ INCS= libprocstat.h CFLAGS+= -I. -I${.CURDIR} -D_KVM_VNODE SHLIB_MAJOR= 1 -DPADD= ${LIBKVM} ${LIBUTIL} -LDADD= -lkvm -lutil +DPADD= ${LIBELF} ${LIBKVM} ${LIBUTIL} +LDADD= -lelf -lkvm -lutil MAN= libprocstat.3 diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map index 0509066..14e65c6 100644 --- a/lib/libprocstat/Symbol.map +++ b/lib/libprocstat/Symbol.map @@ -17,4 +17,6 @@ FBSD_1.2 { FBSD_1.3 { procstat_get_shm_info; + procstat_open_core; + procstat_getvmmap; }; diff --git a/lib/libprocstat/core.c b/lib/libprocstat/core.c new file mode 100644 index 0000000..91b681e --- /dev/null +++ b/lib/libprocstat/core.c @@ -0,0 +1,236 @@ +/*- + * Copyright (c) 2013 Mikolaj Golub + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" + +#define PROCSTAT_CORE_MAGIC 0x012DADB8 +struct procstat_core +{ + int pc_magic; + int pc_fd; + Elf *pc_elf; + GElf_Ehdr pc_ehdr; + GElf_Phdr pc_phdr; +}; + +struct procstat_core * +procstat_core_open(const char *filename) +{ + struct procstat_core *core; + Elf *e; + GElf_Ehdr ehdr; + GElf_Phdr phdr; + size_t nph; + int fd, i; + + if (elf_version(EV_CURRENT) == EV_NONE) { + warnx("ELF library too old"); + return (NULL); + } + fd = open(filename, O_RDONLY, 0); + if (fd == -1) { + warn("open(%s)", filename); + return (NULL); + } + e = elf_begin(fd, ELF_C_READ, NULL); + if (e == NULL) { + warnx("elf_begin: %s", elf_errmsg(-1)); + goto fail; + } + if (elf_kind(e) != ELF_K_ELF) { + warnx("%s is not an ELF object", filename); + goto fail; + } + if (gelf_getehdr(e, &ehdr) == NULL) { + warnx("gelf_getehdr: %s", elf_errmsg(-1)); + goto fail; + } + if (ehdr.e_type != ET_CORE) { + warnx("%s is not a CORE file", filename); + goto fail; + } + if (elf_getphnum(e, &nph) == 0) { + warnx("program headers not found"); + goto fail; + } + for (i = 0; i < ehdr.e_phnum; i++) { + if (gelf_getphdr(e, i, &phdr) != &phdr) { + warnx("gelf_getphdr: %s", elf_errmsg(-1)); + goto fail; + } + if (phdr.p_type == PT_NOTE) + break; + } + if (i == ehdr.e_phnum) { + warnx("NOTE program header not found"); + goto fail; + } + core = malloc(sizeof(struct procstat_core)); + if (core == NULL) { + warn("malloc(%zu)", sizeof(struct procstat_core)); + goto fail; + } + core->pc_magic = PROCSTAT_CORE_MAGIC; + core->pc_fd = fd; + core->pc_elf = e; + core->pc_ehdr = ehdr; + core->pc_phdr = phdr; + + return (core); +fail: + if (e != NULL) + elf_end(e); + close(fd); + + return (NULL); +} + +void +procstat_core_close(struct procstat_core *core) +{ + + assert(core->pc_magic == PROCSTAT_CORE_MAGIC); + + elf_end(core->pc_elf); + close(core->pc_fd); + free(core); +} + +#ifdef KINFO_FILE_SIZE +CTASSERT(sizeof(struct kinfo_file) == KINFO_FILE_SIZE); +#endif +#ifdef KINFO_PROC_SIZE +CTASSERT(sizeof(struct kinfo_proc) == KINFO_PROC_SIZE); +#endif +#ifdef KINFO_VMENTRY_SIZE +CTASSERT(sizeof(struct kinfo_vmentry) == KINFO_VMENTRY_SIZE); +#endif + +void * +procstat_core_get(struct procstat_core *core, enum psc_type type, size_t *lenp) +{ + Elf_Note nhdr; + off_t offset, eoffset; + size_t len; + u_int32_t n_type; + int fd, cstructsize, structsize; + char *buf, nbuf[8]; + + assert(core->pc_magic == PROCSTAT_CORE_MAGIC); + + switch(type) { + case PSC_TYPE_PROC: + n_type = NT_PROCSTAT_PROC; + structsize = sizeof(kinfo_proc); + break; + case PSC_TYPE_FILES: + n_type = NT_PROCSTAT_FILES; + structsize = sizeof(kinfo_file); + break; + case PSC_TYPE_VMMAP: + n_type = NT_PROCSTAT_VMMAP; + structsize = sizeof(vmentry_size); + break; + default: + warnx("unknown core stat type: %d", type); + return (NULL); + } + + offset = core->pc_phdr.p_offset; + eoffset = offset + core->pc_phdr.p_filesz; + fd = core->pc_fd; + + while (offset < eoffset) { + if (lseek(fd, offset, SEEK_SET) == -1) { + warn("lseek"); + return (NULL); + } + if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) { + warn("read"); + return (NULL); + } + offset += sizeof(nhdr) + nhdr.n_namesz + nhdr.n_descsz; + + if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0) + break; + if (nhdr.n_type != n_type) + continue; + if (nhdr.n_namesz != 8) + continue; + if (read(fd, nbuf, sizeof(nbuf)) != sizeof(nbuf)) { + warn("read"); + return (NULL); + } + if (strcmp(nbuf, "FreeBSD") != 0) + continue; + if (nhdr.n_descsz < sizeof(cstructsize)) { + warnx("corrupted core file"); + return (NULL); + } + if (read(fd, &cstructsize, sizeof(cstructsize)) != + sizeof(cstructsize)) { + warn("read"); + return (NULL); + } + if (cstructsize != structsize) { + warnx("version mismatch"); + return (NULL); + } + len = nhdr.n_descsz - sizeof(cstructsize); + if (len == 0) + return (NULL); + buf = malloc(len); + if (buf == NULL) { + warn("malloc(%zu)", len); + return (NULL); + } + if (read(fd, buf, len) != (ssize_t)len) { + warn("read"); + free(buf); + return (NULL); + } + *lenp = len; + + return (buf); + } + + return (NULL); +} diff --git a/lib/libprocstat/core.h b/lib/libprocstat/core.h new file mode 100644 index 0000000..b32dd3a --- /dev/null +++ b/lib/libprocstat/core.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2013 Mikolaj Golub + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _CORE_H +#define _CORE_H + +enum psc_type { + PSC_TYPE_PROC, + PSC_TYPE_FILES, + PSC_TYPE_VMMAP, +}; + +struct procstat_core; + +void procstat_core_close(struct procstat_core *core); +void *procstat_core_get(struct procstat_core *core, enum psc_type type, + size_t *lenp); +struct procstat_core *procstat_core_open(const char *filename); + +#endif /* !_CORE_H_ */ diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index 9d9c111..201ddb1 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -96,11 +96,13 @@ __FBSDID("$FreeBSD$"); #include #include "libprocstat_internal.h" #include "common_kvm.h" +#include "core.h" int statfs(const char *, struct statfs *); /* XXX */ #define PROCSTAT_KVM 1 #define PROCSTAT_SYSCTL 2 +#define PROCSTAT_CORE 3 static char *getmnton(kvm_t *kd, struct mount *m); static struct filestat_list *procstat_getfiles_kvm( @@ -137,6 +139,8 @@ procstat_close(struct procstat *procstat) assert(procstat); if (procstat->type == PROCSTAT_KVM) kvm_close(procstat->kd); + else if (procstat->type == PROCSTAT_CORE) + procstat_core_close(procstat->core); free(procstat); } @@ -177,6 +181,27 @@ procstat_open_kvm(const char *nlistf, const char *memf) return (procstat); } +struct procstat * +procstat_open_core(const char *filename) +{ + struct procstat *procstat; + struct procstat_core *core; + + procstat = calloc(1, sizeof(*procstat)); + if (procstat == NULL) { + warn("malloc()"); + return (NULL); + } + core = procstat_core_open(filename); + if (core == NULL) { + free(procstat); + return (NULL); + } + procstat->type = PROCSTAT_CORE; + procstat->core = core; + return (procstat); +} + struct kinfo_proc * procstat_getprocs(struct procstat *procstat, int what, int arg, unsigned int *count) @@ -231,6 +256,14 @@ procstat_getprocs(struct procstat *procstat, int what, int arg, } /* Perform simple consistency checks. */ if ((len % sizeof(*p)) != 0 || p->ki_structsize != sizeof(*p)) { + warnx("kinfo_proc structure size mismatch (len = %zu)", len); + goto fail; + } + *count = len / sizeof(*p); + return (p); + } else if (procstat->type == PROCSTAT_CORE) { + p = procstat_core_get(procstat->core, PSC_TYPE_PROC, &len); + if ((len % sizeof(*p)) != 0 || p->ki_structsize != sizeof(*p)) { warnx("kinfo_proc structure size mismatch"); goto fail; } @@ -258,8 +291,9 @@ procstat_freeprocs(struct procstat *procstat __unused, struct kinfo_proc *p) struct filestat_list * procstat_getfiles(struct procstat *procstat, struct kinfo_proc *kp, int mmapped) { - - if (procstat->type == PROCSTAT_SYSCTL) + + if (procstat->type == PROCSTAT_SYSCTL || + procstat->type == PROCSTAT_CORE) return (procstat_getfiles_sysctl(procstat, kp, mmapped)); else if (procstat->type == PROCSTAT_KVM) return (procstat_getfiles_kvm(procstat, kp, mmapped)); @@ -647,6 +681,131 @@ kinfo_uflags2fst(int fd) return (0); } +static struct kinfo_file * +kinfo_getfile_core(struct procstat_core *core, int *cntp) +{ + int cnt; + size_t len; + char *buf, *bp, *eb; + struct kinfo_file *kif, *kp, *kf; + + buf = procstat_core_get(core, PSC_TYPE_FILES, &len); + if (buf == NULL) + return (NULL); + /* + * XXXMG: The code below is just copy&past from libutil. + * The code duplication can be avoided if libutil + * is extended to provide something like: + * struct kinfo_file *kinfo_getfile_from_buf(const char *buf, + * size_t len, int *cntp); + */ + + /* Pass 1: count items */ + cnt = 0; + bp = buf; + eb = buf + len; + while (bp < eb) { + kf = (struct kinfo_file *)(uintptr_t)bp; + bp += kf->kf_structsize; + cnt++; + } + + kif = calloc(cnt, sizeof(*kif)); + if (kif == NULL) { + free(buf); + return (NULL); + } + bp = buf; + eb = buf + len; + kp = kif; + /* Pass 2: unpack */ + while (bp < eb) { + kf = (struct kinfo_file *)(uintptr_t)bp; + /* Copy/expand into pre-zeroed buffer */ + memcpy(kp, kf, kf->kf_structsize); + /* Advance to next packed record */ + bp += kf->kf_structsize; + /* Set field size to fixed length, advance */ + kp->kf_structsize = sizeof(*kp); + kp++; + } + free(buf); + *cntp = cnt; + return (kif); /* Caller must free() return value */ +} + +static struct kinfo_vmentry * +kinfo_getvmmap_core(struct procstat_core *core, int *cntp) +{ + int cnt; + size_t len; + char *buf, *bp, *eb; + struct kinfo_vmentry *kiv, *kp, *kv; + + buf = procstat_core_get(core, PSC_TYPE_VMMAP, &len); + if (buf == NULL) + return (NULL); + + /* + * XXXMG: The code below is just copy&past from libutil. + * The code duplication can be avoided if libutil + * is extended to provide something like: + * struct kinfo_vmentry *kinfo_getvmmap_from_buf(const char *buf, + * size_t len, int *cntp); + */ + + /* Pass 1: count items */ + cnt = 0; + bp = buf; + eb = buf + len; + while (bp < eb) { + kv = (struct kinfo_vmentry *)(uintptr_t)bp; + bp += kv->kve_structsize; + cnt++; + } + + kiv = calloc(cnt, sizeof(*kiv)); + if (kiv == NULL) { + free(buf); + return (NULL); + } + bp = buf; + eb = buf + len; + kp = kiv; + /* Pass 2: unpack */ + while (bp < eb) { + kv = (struct kinfo_vmentry *)(uintptr_t)bp; + /* Copy/expand into pre-zeroed buffer */ + memcpy(kp, kv, kv->kve_structsize); + /* Advance to next packed record */ + bp += kv->kve_structsize; + /* Set field size to fixed length, advance */ + kp->kve_structsize = sizeof(*kp); + kp++; + } + free(buf); + *cntp = cnt; + return (kiv); /* Caller must free() return value */ +} + +struct kinfo_vmentry * +procstat_getvmmap(struct procstat *procstat, struct kinfo_proc *kp, + unsigned int *cntp) +{ + switch(procstat->type) { + case PROCSTAT_KVM: + warnx("kvm method is not supported"); + return (NULL); + case PROCSTAT_SYSCTL: + return (kinfo_getvmmap(kp->ki_pid, cntp)); + case PROCSTAT_CORE: + return (kinfo_getvmmap_core(procstat->core, cntp)); + default: + warnx("unknown access method: %d", procstat->type); + return (NULL); + } +} + static struct filestat_list * procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, int mmapped) { @@ -662,10 +821,14 @@ procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, int m cap_rights_t cap_rights; assert(kp); + assert(procstat->type == PROCSTAT_SYSCTL || + procstat->type == PROCSTAT_CORE); if (kp->ki_fd == NULL) return (NULL); - files = kinfo_getfile(kp->ki_pid, &cnt); + files = procstat->type == PROCSTAT_CORE ? + kinfo_getfile_core(procstat->core, &cnt) : + kinfo_getfile(kp->ki_pid, &cnt); if (files == NULL && errno != EPERM) { warn("kinfo_getfile()"); return (NULL); @@ -703,7 +866,9 @@ procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, int m STAILQ_INSERT_TAIL(head, entry, next); } if (mmapped != 0) { - vmentries = kinfo_getvmmap(kp->ki_pid, &cnt); + vmentries = procstat->type == PROCSTAT_CORE ? + kinfo_getvmmap_core(procstat->core, &cnt) : + kinfo_getvmmap(kp->ki_pid, &cnt); procstat->vmentries = vmentries; if (vmentries == NULL || cnt == 0) goto fail; @@ -743,7 +908,8 @@ procstat_get_pipe_info(struct procstat *procstat, struct filestat *fst, if (procstat->type == PROCSTAT_KVM) { return (procstat_get_pipe_info_kvm(procstat->kd, fst, ps, errbuf)); - } else if (procstat->type == PROCSTAT_SYSCTL) { + } else if (procstat->type == PROCSTAT_SYSCTL || + procstat->type == PROCSTAT_CORE) { return (procstat_get_pipe_info_sysctl(fst, ps, errbuf)); } else { warnx("unknown access method: %d", procstat->type); @@ -807,7 +973,8 @@ procstat_get_pts_info(struct procstat *procstat, struct filestat *fst, if (procstat->type == PROCSTAT_KVM) { return (procstat_get_pts_info_kvm(procstat->kd, fst, pts, errbuf)); - } else if (procstat->type == PROCSTAT_SYSCTL) { + } else if (procstat->type == PROCSTAT_SYSCTL || + procstat->type == PROCSTAT_CORE) { return (procstat_get_pts_info_sysctl(fst, pts, errbuf)); } else { warnx("unknown access method: %d", procstat->type); @@ -869,7 +1036,8 @@ procstat_get_shm_info(struct procstat *procstat, struct filestat *fst, if (procstat->type == PROCSTAT_KVM) { return (procstat_get_shm_info_kvm(procstat->kd, fst, shm, errbuf)); - } else if (procstat->type == PROCSTAT_SYSCTL) { + } else if (procstat->type == PROCSTAT_SYSCTL || + procstat->type == PROCSTAT_CORE) { return (procstat_get_shm_info_sysctl(fst, shm, errbuf)); } else { warnx("unknown access method: %d", procstat->type); @@ -949,7 +1117,8 @@ procstat_get_vnode_info(struct procstat *procstat, struct filestat *fst, if (procstat->type == PROCSTAT_KVM) { return (procstat_get_vnode_info_kvm(procstat->kd, fst, vn, errbuf)); - } else if (procstat->type == PROCSTAT_SYSCTL) { + } else if (procstat->type == PROCSTAT_SYSCTL || + procstat->type == PROCSTAT_CORE) { return (procstat_get_vnode_info_sysctl(fst, vn, errbuf)); } else { warnx("unknown access method: %d", procstat->type); @@ -1151,7 +1320,8 @@ procstat_get_socket_info(struct procstat *procstat, struct filestat *fst, if (procstat->type == PROCSTAT_KVM) { return (procstat_get_socket_info_kvm(procstat->kd, fst, sock, errbuf)); - } else if (procstat->type == PROCSTAT_SYSCTL) { + } else if (procstat->type == PROCSTAT_SYSCTL || + procstat->type == PROCSTAT_CORE) { return (procstat_get_socket_info_sysctl(fst, sock, errbuf)); } else { warnx("unknown access method: %d", procstat->type); diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h index 662ea37..8b6dcc4 100644 --- a/lib/libprocstat/libprocstat.h +++ b/lib/libprocstat/libprocstat.h @@ -153,6 +153,8 @@ struct filestat_list *procstat_getfiles(struct procstat *procstat, struct kinfo_proc *kp, int mmapped); struct kinfo_proc *procstat_getprocs(struct procstat *procstat, int what, int arg, unsigned int *count); +struct kinfo_vmentry *procstat_getvmmap(struct procstat *procstat, + struct kinfo_proc *kp, unsigned int *count); int procstat_get_pipe_info(struct procstat *procstat, struct filestat *fst, struct pipestat *pipe, char *errbuf); int procstat_get_pts_info(struct procstat *procstat, struct filestat *fst, @@ -163,6 +165,7 @@ int procstat_get_socket_info(struct procstat *procstat, struct filestat *fst, struct sockstat *sock, char *errbuf); int procstat_get_vnode_info(struct procstat *procstat, struct filestat *fst, struct vnstat *vn, char *errbuf); +struct procstat *procstat_open_core(const char *filename); struct procstat *procstat_open_sysctl(void); struct procstat *procstat_open_kvm(const char *nlistf, const char *memf); __END_DECLS diff --git a/lib/libprocstat/libprocstat_internal.h b/lib/libprocstat/libprocstat_internal.h index 1c1d842..ff1e742 100644 --- a/lib/libprocstat/libprocstat_internal.h +++ b/lib/libprocstat/libprocstat_internal.h @@ -34,6 +34,7 @@ struct procstat { kvm_t *kd; void *vmentries; void *files; + struct procstat_core *core; }; #endif /* !_LIBPROCSTAT_INTERNAL_H_ */ commit 88e616ba0c313833b192d17f052404d4dd557080 Author: Mikolaj Golub Date: Mon Feb 11 00:28:31 2013 +0200 Make use of newly added libprocstat(3) ability to extract procstat info from a process core file. So now one can run procstat(1) on a process core e.g. to get list of files opened by process when it crashed: root@lisa:/ # procstat -f /root/vi.core PID COMM FD T V FLAGS REF OFFSET PRO NAME 658 vi text v r r-------- - - - /usr/bin/vi 658 vi ctty v c rw------- - - - /dev/pts/0 658 vi cwd v d r-------- - - - /root 658 vi root v d r-------- - - - / 658 vi 0 v c rw------- 11 3208 - /dev/pts/0 658 vi 1 v c rw------- 11 3208 - /dev/pts/0 658 vi 2 v c rw------- 11 3208 - /dev/pts/0 658 vi 3 v r r----n-l- 1 0 - /tmp/vi.0AYKz3Lps7 658 vi 4 v r rw------- 1 0 - /var/tmp/vi.recover/vi.GaGYsz 658 vi 5 v r rw------- 1 0 - - The functionality is still limited to only several options. diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c index 934e292..6377c55 100644 --- a/usr.bin/procstat/procstat.c +++ b/usr.bin/procstat/procstat.c @@ -50,7 +50,7 @@ usage(void) fprintf(stderr, "usage: procstat [-h] [-C] [-M core] [-N system] " "[-w interval] \n"); fprintf(stderr, " [-b | -c | -e | -f | -i | -j | -k | " - "-l | -s | -t | -v | -x] [-a | pid ...]\n"); + "-l | -s | -t | -v | -x] [-a | pid | core ...]\n"); exit(EX_USAGE); } @@ -79,7 +79,7 @@ procstat(struct procstat *prstat, struct kinfo_proc *kipp) else if (tflag) procstat_threads(kipp); else if (vflag) - procstat_vm(kipp); + procstat_vm(prstat, kipp); else if (xflag) procstat_auxv(kipp); else @@ -116,7 +116,7 @@ main(int argc, char *argv[]) int ch, interval, tmp; int i; struct kinfo_proc *p; - struct procstat *prstat; + struct procstat *prstat, *cprstat; long l; pid_t pid; char *dummy; @@ -255,18 +255,29 @@ main(int argc, char *argv[]) } for (i = 0; i < argc; i++) { l = strtol(argv[i], &dummy, 10); - if (*dummy != '\0') - usage(); - if (l < 0) + if (*dummy == '\0') { + if (l < 0) + usage(); + pid = l; + + p = procstat_getprocs(prstat, KERN_PROC_PID, pid, &cnt); + if (p == NULL) + errx(1, "procstat_getprocs()"); + if (cnt != 0) + procstat(prstat, p); + procstat_freeprocs(prstat, p); + } else if (access(argv[i], R_OK) == 0) { + cprstat = procstat_open_core(argv[i]); + p = procstat_getprocs(cprstat, KERN_PROC_PID, pid, &cnt); + if (p == NULL) + errx(1, "procstat_getprocs()"); + if (cnt != 0) + procstat(cprstat, p); + procstat_freeprocs(cprstat, p); + procstat_close(cprstat); + } else { usage(); - pid = l; - - p = procstat_getprocs(prstat, KERN_PROC_PID, pid, &cnt); - if (p == NULL) - errx(1, "procstat_getprocs()"); - if (cnt != 0) - procstat(prstat, p); - procstat_freeprocs(prstat, p); + } /* Suppress header after first process. */ hflag = 1; diff --git a/usr.bin/procstat/procstat.h b/usr.bin/procstat/procstat.h index e65436d..3904a81 100644 --- a/usr.bin/procstat/procstat.h +++ b/usr.bin/procstat/procstat.h @@ -46,6 +46,6 @@ void procstat_rlimit(struct kinfo_proc *kipp); void procstat_sigs(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_threads(struct kinfo_proc *kipp); void procstat_threads_sigs(struct procstat *prstat, struct kinfo_proc *kipp); -void procstat_vm(struct kinfo_proc *kipp); +void procstat_vm(struct procstat *procstat, struct kinfo_proc *kipp); #endif /* !PROCSTAT_H */ diff --git a/usr.bin/procstat/procstat_vm.c b/usr.bin/procstat/procstat_vm.c index 66f29ae..d44b9c2 100644 --- a/usr.bin/procstat/procstat_vm.c +++ b/usr.bin/procstat/procstat_vm.c @@ -41,7 +41,7 @@ #include "procstat.h" void -procstat_vm(struct kinfo_proc *kipp) +procstat_vm(struct procstat *procstat, struct kinfo_proc *kipp) { struct kinfo_vmentry *freep, *kve; int ptrwidth; @@ -54,7 +54,7 @@ procstat_vm(struct kinfo_proc *kipp) "PID", ptrwidth, "START", ptrwidth, "END", "PRT", "RES", "PRES", "REF", "SHD", "FL", "TP", "PATH"); - freep = kinfo_getvmmap(kipp->ki_pid, &cnt); + freep = procstat_getvmmap(procstat, kipp, &cnt); if (freep == NULL) return; for (i = 0; i < cnt; i++) {