commit e3631fbbe64973fee88f542267b863f1d17d3e80 Author: Mikolaj Golub Date: Wed Mar 13 22:21:02 2013 +0200 Re-factor coredump routines. For each type of notes an output function is provided, which is used either to calculate the note size or output it to sbuf. On the first pass the notes are registered in a list and the resulting size is found, on the second pass the list is traversed outputing notes to sbuf. For the sbuf a drain routine is provided that writes data to a core file. The main goal of the change is to make coredump to write notes directly to the core file, without preliminary preparing them all in a memory buffer. Storing notes in memory is not a problem for the current, rather small, set of notes we write to the core, but it may becomes an issue when we start to store procstat notes. Reviewed by: jhb (initial version), kib Discussed with: jhb, kib MFC after: 3 weeks diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 813e37a..f4659c3 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 @@ -104,8 +105,8 @@ SYSCTL_NODE(_kern, OID_AUTO, __CONCAT(elf, __ELF_WORD_SIZE), CTLFLAG_RW, 0, #ifdef COMPRESS_USER_CORES static int compress_core(gzFile, char *, char *, unsigned int, struct thread * td); -#define CORE_BUF_SIZE (16 * 1024) #endif +#define CORE_BUF_SIZE (16 * 1024) int __elfN(fallback_brand) = -1; SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, @@ -1038,14 +1039,35 @@ struct sseg_closure { size_t size; /* Total size of all writable segments. */ }; +typedef void (*outfunc_t)(void *, struct sbuf *, size_t *); + +struct note_info { + int type; /* Note type. */ + outfunc_t outfunc; /* Output function. */ + void *outarg; /* Argument for the output function. */ + size_t outsize; /* Output size. */ + TAILQ_ENTRY(note_info) link; /* Link to the next note info. */ +}; + +TAILQ_HEAD(note_info_list, note_info); + 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); -static void __elfN(putnote)(void *, size_t *, const char *, int, - const void *, size_t); + int, void *, size_t, struct note_info_list *, size_t, gzFile); +static void __elfN(prepare_notes)(struct thread *, struct note_info_list *, + size_t *); +static void __elfN(puthdr)(struct thread *, void *, size_t, int, size_t); +static void __elfN(putnote)(struct note_info *, struct sbuf *); +static size_t register_note(struct note_info_list *, int, outfunc_t, void *); +static int sbuf_drain_core_output(void *, const char *, int); + +static void __elfN(note_fpregset)(void *, struct sbuf *, size_t *); +static void __elfN(note_prpsinfo)(void *, struct sbuf *, size_t *); +static void __elfN(note_prstatus)(void *, struct sbuf *, size_t *); +static void __elfN(note_threadmd)(void *, struct sbuf *, size_t *); +static void __elfN(note_thrmisc)(void *, struct sbuf *, size_t *); #ifdef COMPRESS_USER_CORES extern int compress_user_cores; @@ -1072,14 +1094,54 @@ core_output(struct vnode *vp, void *base, size_t len, off_t offset, return (error); } +/* Coredump output parameters for sbuf drain routine. */ +struct sbuf_drain_core_params { + off_t offset; + struct ucred *active_cred; + struct ucred *file_cred; + struct thread *td; + struct vnode *vp; +#ifdef COMPRESS_USER_CORES + gzFile gzfile; +#endif +}; + +/* + * Drain into a core file. + */ +static int +sbuf_drain_core_output(void *arg, const char *data, int len) +{ + struct sbuf_drain_core_params *p; + int error; + + p = (struct sbuf_drain_core_params *)arg; +#ifdef COMPRESS_USER_CORES + if (p->gzfile != Z_NULL) + error = compress_core(p->gzfile, NULL, __DECONST(char *, data), + len, p->td); + else +#endif + error = vn_rdwr_inchunks(UIO_WRITE, p->vp, + __DECONST(void *, data), len, p->offset, UIO_SYSSPACE, + IO_UNIT | IO_DIRECT, p->active_cred, p->file_cred, NULL, + p->td); + if (error != 0) + return (-error); + p->offset += len; + return (len); +} + 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; + struct note_info_list notelst; + struct note_info *ninfo; void *hdr; - size_t hdrsize; + size_t hdrsize, notesz, coresize; gzFile gzfile = Z_NULL; char *core_buf = NULL; @@ -1090,6 +1152,7 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) #endif hdr = NULL; + TAILQ_INIT(¬elst); #ifdef COMPRESS_USER_CORES if (doing_compress) { @@ -1118,30 +1181,29 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) each_writable_segment(td, cb_size_segment, &seginfo); /* - * 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. + * Collect info about the core file header area. */ - hdrsize = 0; - __elfN(puthdr)(td, (void *)NULL, &hdrsize, seginfo.count); + hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); + __elfN(prepare_notes)(td, ¬elst, ¬esz); + coresize = round_page(hdrsize + notesz) + seginfo.size; #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, coresize); PROC_UNLOCK(td->td_proc); if (error != 0) { error = EFAULT; goto done; } #endif - if (hdrsize + seginfo.size >= limit) { + if (coresize >= limit) { error = EFAULT; goto done; } /* * Allocate memory for building the header, fill it up, - * and write it out. + * and write it out following the notes. */ hdr = malloc(hdrsize, M_TEMP, M_WAITOK); if (hdr == NULL) { @@ -1149,7 +1211,7 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) goto done; } error = __elfN(corehdr)(td, vp, cred, seginfo.count, hdr, hdrsize, - gzfile); + ¬elst, notesz, gzfile); /* Write the contents of all of the writable segments. */ if (error == 0) { @@ -1158,7 +1220,7 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) int i; php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; - offset = hdrsize; + offset = round_page(hdrsize + notesz); 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); @@ -1181,8 +1243,12 @@ done: if (gzfile) gzclose(gzfile); #endif - - free(hdr, M_TEMP); + while ((ninfo = TAILQ_FIRST(¬elst)) != NULL) { + TAILQ_REMOVE(¬elst, ninfo, link); + free(ninfo, M_TEMP); + } + if (hdr != NULL) + free(hdr, M_TEMP); return (error); } @@ -1299,44 +1365,194 @@ 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, void *hdr, size_t hdrsize, struct note_info_list *notelst, + size_t notesz, gzFile gzfile) { - size_t off; + struct sbuf_drain_core_params params; + struct note_info *ninfo; + struct sbuf *sb; + int error; /* Fill in the header. */ bzero(hdr, hdrsize); - off = 0; - __elfN(puthdr)(td, hdr, &off, 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)); - } else { + __elfN(puthdr)(td, hdr, hdrsize, numsegs, notesz); + + params.offset = 0; + params.active_cred = cred; + params.file_cred = NOCRED; + params.td = td; + params.vp = vp; #ifdef COMPRESS_USER_CORES - if (gzwrite(gzfile, hdr, hdrsize) != hdrsize) { - log(LOG_WARNING, - "Failed to compress core file header for process" - " %s.\n", curproc->p_comm); - return (EFAULT); - } - else { - return (0); - } + params.gzfile = gzfile; +#endif + sb = sbuf_new(NULL, NULL, CORE_BUF_SIZE, SBUF_FIXEDLEN); + sbuf_set_drain(sb, sbuf_drain_core_output, ¶ms); + sbuf_start_section(sb, NULL); + sbuf_bcat(sb, hdr, hdrsize); + TAILQ_FOREACH(ninfo, notelst, link) + __elfN(putnote)(ninfo, sb); + /* Align up to a page boundary for the program segments. */ + sbuf_end_section(sb, -1, PAGE_SIZE, 0); + error = sbuf_finish(sb); + sbuf_delete(sb); + + return (error); +} + +static void +__elfN(prepare_notes)(struct thread *td, struct note_info_list *list, + size_t *sizep) +{ + struct proc *p; + struct thread *thr; + size_t size; + + p = td->td_proc; + size = 0; + + size += register_note(list, NT_PRPSINFO, __elfN(note_prpsinfo), p); + + /* + * To have the debugger select the right thread (LWP) as the initial + * thread, we dump the state of the thread passed to us in td first. + * This is the thread that causes the core dump and thus likely to + * be the right thread one wants to have selected in the debugger. + */ + thr = td; + while (thr != NULL) { + size += register_note(list, NT_PRSTATUS, + __elfN(note_prstatus), thr); + size += register_note(list, NT_FPREGSET, + __elfN(note_fpregset), thr); + size += register_note(list, NT_THRMISC, + __elfN(note_thrmisc), thr); + size += register_note(list, -1, + __elfN(note_threadmd), thr); + + thr = (thr == td) ? TAILQ_FIRST(&p->p_threads) : + TAILQ_NEXT(thr, td_plist); + if (thr == td) + thr = TAILQ_NEXT(thr, td_plist); + } + + *sizep = size; +} + +static void +__elfN(puthdr)(struct thread *td, void *hdr, size_t hdrsize, int numsegs, + size_t notesz) +{ + Elf_Ehdr *ehdr; + Elf_Phdr *phdr; + struct phdr_closure phc; + + ehdr = (Elf_Ehdr *)hdr; + phdr = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)); + + ehdr->e_ident[EI_MAG0] = ELFMAG0; + ehdr->e_ident[EI_MAG1] = ELFMAG1; + ehdr->e_ident[EI_MAG2] = ELFMAG2; + ehdr->e_ident[EI_MAG3] = ELFMAG3; + ehdr->e_ident[EI_CLASS] = ELF_CLASS; + ehdr->e_ident[EI_DATA] = ELF_DATA; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELFOSABI_FREEBSD; + ehdr->e_ident[EI_ABIVERSION] = 0; + ehdr->e_ident[EI_PAD] = 0; + ehdr->e_type = ET_CORE; +#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 + ehdr->e_machine = ELF_ARCH32; #else - panic("shouldn't be here"); + ehdr->e_machine = ELF_ARCH; #endif + ehdr->e_version = EV_CURRENT; + ehdr->e_entry = 0; + ehdr->e_phoff = sizeof(Elf_Ehdr); + ehdr->e_flags = 0; + ehdr->e_ehsize = sizeof(Elf_Ehdr); + ehdr->e_phentsize = sizeof(Elf_Phdr); + ehdr->e_phnum = numsegs + 1; + ehdr->e_shentsize = sizeof(Elf_Shdr); + ehdr->e_shnum = 0; + ehdr->e_shstrndx = SHN_UNDEF; + + /* + * Fill in the program header entries. + */ + + /* The note segement. */ + phdr->p_type = PT_NOTE; + phdr->p_offset = hdrsize; + phdr->p_vaddr = 0; + phdr->p_paddr = 0; + phdr->p_filesz = notesz; + phdr->p_memsz = 0; + phdr->p_flags = PF_R; + phdr->p_align = sizeof(Elf32_Size); + phdr++; + + /* All the writable segments from the program. */ + phc.phdr = phdr; + phc.offset = round_page(hdrsize + notesz); + each_writable_segment(td, cb_put_phdr, &phc); +} + +static size_t +register_note(struct note_info_list *list, int type, outfunc_t out, void *arg) +{ + struct note_info *ninfo; + size_t size, notesize; + + size = 0; + out(arg, NULL, &size); + ninfo = malloc(sizeof(*ninfo), M_TEMP, M_ZERO | M_WAITOK); + ninfo->type = type; + ninfo->outfunc = out; + ninfo->outarg = arg; + ninfo->outsize = size; + TAILQ_INSERT_TAIL(list, ninfo, link); + + if (type == -1) + return (size); + + notesize = sizeof(Elf_Note) + /* note header */ + roundup2(8, sizeof(Elf32_Size)) + /* note name ("FreeBSD") */ + roundup2(size, sizeof(Elf32_Size)); /* note description */ + + return (notesize); +} + +static void +__elfN(putnote)(struct note_info *ninfo, struct sbuf *sb) +{ + Elf_Note note; + ssize_t old_len; + + if (ninfo->type == -1) { + ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize); + return; } + + note.n_namesz = 8; /* strlen("FreeBSD") + 1 */ + note.n_descsz = ninfo->outsize; + note.n_type = ninfo->type; + + sbuf_bcat(sb, ¬e, sizeof(note)); + sbuf_start_section(sb, &old_len); + sbuf_bcat(sb, "FreeBSD", note.n_namesz); + sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); + if (note.n_descsz == 0) + return; + sbuf_start_section(sb, &old_len); + ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize); + sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); } +/* + * Miscellaneous note out functions. + */ + #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 #include @@ -1356,50 +1572,15 @@ typedef thrmisc_t elf_thrmisc_t; #endif static void -__elfN(puthdr)(struct thread *td, void *dst, size_t *off, int numsegs) +__elfN(note_prpsinfo)(void *arg, struct sbuf *sb, size_t *sizep) { - struct { - elf_prstatus_t status; - elf_prfpregset_t fpregset; - elf_prpsinfo_t psinfo; - elf_thrmisc_t thrmisc; - } *tempdata; - elf_prstatus_t *status; - elf_prfpregset_t *fpregset; - elf_prpsinfo_t *psinfo; - elf_thrmisc_t *thrmisc; struct proc *p; - struct thread *thr; - size_t ehoff, noteoff, notesz, phoff; - - p = td->td_proc; - - ehoff = *off; - *off += sizeof(Elf_Ehdr); - - phoff = *off; - *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. - */ - if (dst != NULL) { - tempdata = malloc(sizeof(*tempdata), M_TEMP, M_ZERO|M_WAITOK); - status = &tempdata->status; - fpregset = &tempdata->fpregset; - psinfo = &tempdata->psinfo; - thrmisc = &tempdata->thrmisc; - } else { - tempdata = NULL; - status = NULL; - fpregset = NULL; - psinfo = NULL; - thrmisc = NULL; - } + elf_prpsinfo_t *psinfo; - if (dst != NULL) { + p = (struct proc *)arg; + if (sb != NULL) { + KASSERT(*sizep == sizeof(*psinfo), ("invalid size")); + psinfo = malloc(sizeof(*psinfo), M_TEMP, M_ZERO | M_WAITOK); psinfo->pr_version = PRPSINFO_VERSION; psinfo->pr_psinfosz = sizeof(elf_prpsinfo_t); strlcpy(psinfo->pr_fname, p->p_comm, sizeof(psinfo->pr_fname)); @@ -1409,139 +1590,100 @@ __elfN(puthdr)(struct thread *td, void *dst, size_t *off, int numsegs) */ strlcpy(psinfo->pr_psargs, p->p_comm, sizeof(psinfo->pr_psargs)); + + sbuf_bcat(sb, psinfo, sizeof(*psinfo)); + free(psinfo, M_TEMP); } - __elfN(putnote)(dst, off, "FreeBSD", NT_PRPSINFO, psinfo, - sizeof *psinfo); + *sizep = sizeof(*psinfo); +} - /* - * To have the debugger select the right thread (LWP) as the initial - * thread, we dump the state of the thread passed to us in td first. - * This is the thread that causes the core dump and thus likely to - * be the right thread one wants to have selected in the debugger. - */ - thr = td; - while (thr != NULL) { - if (dst != NULL) { - status->pr_version = PRSTATUS_VERSION; - status->pr_statussz = sizeof(elf_prstatus_t); - status->pr_gregsetsz = sizeof(elf_gregset_t); - status->pr_fpregsetsz = sizeof(elf_fpregset_t); - status->pr_osreldate = osreldate; - status->pr_cursig = p->p_sig; - status->pr_pid = thr->td_tid; +static void +__elfN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct thread *td; + elf_prstatus_t *status; + + td = (struct thread *)arg; + if (sb != NULL) { + KASSERT(*sizep == sizeof(*status), ("invalid size")); + status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK); + status->pr_version = PRSTATUS_VERSION; + status->pr_statussz = sizeof(elf_prstatus_t); + status->pr_gregsetsz = sizeof(elf_gregset_t); + status->pr_fpregsetsz = sizeof(elf_fpregset_t); + status->pr_osreldate = osreldate; + status->pr_cursig = td->td_proc->p_sig; + status->pr_pid = td->td_tid; #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 - fill_regs32(thr, &status->pr_reg); - fill_fpregs32(thr, fpregset); + fill_regs32(td, &status->pr_reg); #else - fill_regs(thr, &status->pr_reg); - fill_fpregs(thr, fpregset); + fill_regs(td, &status->pr_reg); #endif - memset(&thrmisc->_pad, 0, sizeof (thrmisc->_pad)); - strcpy(thrmisc->pr_tname, thr->td_name); - } - __elfN(putnote)(dst, off, "FreeBSD", NT_PRSTATUS, status, - sizeof *status); - __elfN(putnote)(dst, off, "FreeBSD", NT_FPREGSET, fpregset, - sizeof *fpregset); - __elfN(putnote)(dst, off, "FreeBSD", NT_THRMISC, thrmisc, - sizeof *thrmisc); - /* - * Allow for MD specific notes, as well as any MD - * specific preparations for writing MI notes. - */ - __elfN(dump_thread)(thr, dst, off); - - thr = (thr == td) ? TAILQ_FIRST(&p->p_threads) : - TAILQ_NEXT(thr, td_plist); - if (thr == td) - thr = TAILQ_NEXT(thr, td_plist); + sbuf_bcat(sb, status, sizeof(*status)); + free(status, M_TEMP); } + *sizep = sizeof(*status); +} - notesz = *off - noteoff; - - if (dst != NULL) - free(tempdata, M_TEMP); - - /* Align up to a page boundary for the program segments. */ - *off = round_page(*off); - - if (dst != NULL) { - Elf_Ehdr *ehdr; - Elf_Phdr *phdr; - struct phdr_closure phc; +static void +__elfN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct thread *td; + elf_prfpregset_t *fpregset; - /* - * Fill in the ELF header. - */ - ehdr = (Elf_Ehdr *)((char *)dst + ehoff); - ehdr->e_ident[EI_MAG0] = ELFMAG0; - ehdr->e_ident[EI_MAG1] = ELFMAG1; - ehdr->e_ident[EI_MAG2] = ELFMAG2; - ehdr->e_ident[EI_MAG3] = ELFMAG3; - ehdr->e_ident[EI_CLASS] = ELF_CLASS; - ehdr->e_ident[EI_DATA] = ELF_DATA; - ehdr->e_ident[EI_VERSION] = EV_CURRENT; - ehdr->e_ident[EI_OSABI] = ELFOSABI_FREEBSD; - ehdr->e_ident[EI_ABIVERSION] = 0; - ehdr->e_ident[EI_PAD] = 0; - ehdr->e_type = ET_CORE; + td = (struct thread *)arg; + if (sb != NULL) { + KASSERT(*sizep == sizeof(*fpregset), ("invalid size")); + fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK); #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 - ehdr->e_machine = ELF_ARCH32; + fill_fpregs32(td, fpregset); #else - ehdr->e_machine = ELF_ARCH; + fill_fpregs(td, fpregset); #endif - ehdr->e_version = EV_CURRENT; - ehdr->e_entry = 0; - ehdr->e_phoff = phoff; - ehdr->e_flags = 0; - ehdr->e_ehsize = sizeof(Elf_Ehdr); - ehdr->e_phentsize = sizeof(Elf_Phdr); - ehdr->e_phnum = numsegs + 1; - ehdr->e_shentsize = sizeof(Elf_Shdr); - ehdr->e_shnum = 0; - ehdr->e_shstrndx = SHN_UNDEF; - - /* - * Fill in the program header entries. - */ - phdr = (Elf_Phdr *)((char *)dst + phoff); - - /* The note segement. */ - phdr->p_type = PT_NOTE; - phdr->p_offset = noteoff; - phdr->p_vaddr = 0; - phdr->p_paddr = 0; - phdr->p_filesz = notesz; - phdr->p_memsz = 0; - phdr->p_flags = PF_R; - phdr->p_align = sizeof(Elf32_Size); - phdr++; - - /* All the writable segments from the program. */ - phc.phdr = phdr; - phc.offset = *off; - each_writable_segment(td, cb_put_phdr, &phc); + sbuf_bcat(sb, fpregset, sizeof(*fpregset)); + free(fpregset, M_TEMP); } + *sizep = sizeof(*fpregset); } static void -__elfN(putnote)(void *dst, size_t *off, const char *name, int type, - const void *desc, size_t descsz) +__elfN(note_thrmisc)(void *arg, struct sbuf *sb, size_t *sizep) { - Elf_Note note; + struct thread *td; + elf_thrmisc_t thrmisc; + + td = (struct thread *)arg; + if (sb != NULL) { + KASSERT(*sizep == sizeof(thrmisc), ("invalid size")); + bzero(&thrmisc._pad, sizeof(thrmisc._pad)); + strcpy(thrmisc.pr_tname, td->td_name); + sbuf_bcat(sb, &thrmisc, sizeof(thrmisc)); + } + *sizep = sizeof(thrmisc); +} - note.n_namesz = strlen(name) + 1; - note.n_descsz = descsz; - note.n_type = type; - if (dst != NULL) - bcopy(¬e, (char *)dst + *off, sizeof note); - *off += sizeof note; - if (dst != NULL) - bcopy(name, (char *)dst + *off, note.n_namesz); - *off += roundup2(note.n_namesz, sizeof(Elf32_Size)); - if (dst != NULL) - bcopy(desc, (char *)dst + *off, note.n_descsz); - *off += roundup2(note.n_descsz, sizeof(Elf32_Size)); +/* + * Allow for MD specific notes, as well as any MD + * specific preparations for writing MI notes. + */ +static void +__elfN(note_threadmd)(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct thread *td; + void *buf; + size_t size; + + td = (struct thread *)arg; + size = *sizep; + buf = NULL; + if (size != 0 && sb != NULL) + buf = malloc(size, M_TEMP, M_ZERO | M_WAITOK); + size = 0; + __elfN(dump_thread)(td, buf, &size); + KASSERT(*sizep == size, ("invalid size")); + if (size != 0 && sb != NULL) + sbuf_bcat(sb, buf, size); + *sizep = size; } static boolean_t @@ -1636,6 +1778,8 @@ EXEC_SET(__CONCAT(elf, __ELF_WORD_SIZE), __elfN(execsw)); * routine gzwrite(). This copying is necessary because the content of the VM * segment may change between the compression pass and the crc-computation pass * in gzwrite(). This is because realtime threads may preempt the UNIX kernel. + * + * If inbuf is NULL it is assumed that data is already copied to 'dest_buf'. */ static int compress_core (gzFile file, char *inbuf, char *dest_buf, unsigned int len, @@ -1646,8 +1790,13 @@ compress_core (gzFile file, char *inbuf, char *dest_buf, unsigned int len, unsigned int chunk_len; while (len) { - chunk_len = (len > CORE_BUF_SIZE) ? CORE_BUF_SIZE : len; - copyin(inbuf, dest_buf, chunk_len); + if (inbuf != NULL) { + chunk_len = (len > CORE_BUF_SIZE) ? CORE_BUF_SIZE : len; + copyin(inbuf, dest_buf, chunk_len); + inbuf += chunk_len; + } else { + chunk_len = len; + } len_compressed = gzwrite(file, dest_buf, chunk_len); EVENTHANDLER_INVOKE(app_coredump_progress, td, len_compressed); @@ -1662,7 +1811,6 @@ compress_core (gzFile file, char *inbuf, char *dest_buf, unsigned int len, error = EFAULT; break; } - inbuf += chunk_len; len -= chunk_len; maybe_yield(); } commit 8a1481562f75a3ebaa43b64a070e3b919e885ee9 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 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. Reviewed by: kib MFC after: 3 weeks diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 8e7c851..b1a48bf 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 @@ -3140,9 +3141,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, cap_rights_t fd_cap_rights, struct kinfo_file *kif, - struct sysctl_req *req) + struct sbuf *sb, ssize_t *remainder) { struct { int fflag; @@ -3169,6 +3170,8 @@ export_fd_for_sysctl(void *data, int type, int fd, int fflags, int refcnt, int error; unsigned int i; + if (*remainder == 0) + return (0); bzero(kif, sizeof(*kif)); switch (type) { case KF_TYPE_FIFO: @@ -3213,32 +3216,40 @@ 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); + if (*remainder != -1) { + if (*remainder < kif->kf_structsize) { + /* Terminate export. */ + *remainder = 0; + return (0); + } + *remainder -= 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, ssize_t maxlen) { 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; + ssize_t remainder; + int error, i; int type, refcnt, fflags; cap_rights_t fd_cap_rights; - name = (int *)arg1; - error = pget((pid_t)name[0], PGET_CANDEBUG, &p); - if (error != 0) - return (error); + PROC_LOCK_ASSERT(p, MA_OWNED); + + remainder = maxlen; /* ktrace vnode */ tracevp = p->p_tracevp; if (tracevp != NULL) @@ -3258,14 +3269,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, kif, req); + export_fd_to_sb(tracevp, KF_TYPE_VNODE, KF_FD_TYPE_TRACE, + FREAD | FWRITE, -1, -1, 0, kif, sb, &remainder); if (textvp != NULL) - export_fd_for_sysctl(textvp, KF_TYPE_VNODE, KF_FD_TYPE_TEXT, - FREAD, -1, -1, 0, kif, req); + export_fd_to_sb(textvp, KF_TYPE_VNODE, KF_FD_TYPE_TEXT, + FREAD, -1, -1, 0, kif, sb, &remainder); if (cttyvp != NULL) - export_fd_for_sysctl(cttyvp, KF_TYPE_VNODE, KF_FD_TYPE_CTTY, - FREAD | FWRITE, -1, -1, 0, kif, req); + export_fd_to_sb(cttyvp, KF_TYPE_VNODE, KF_FD_TYPE_CTTY, + FREAD | FWRITE, -1, -1, 0, kif, sb, &remainder); + error = 0; if (fdp == NULL) goto fail; FILEDESC_SLOCK(fdp); @@ -3274,8 +3286,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, kif, req); + export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_CWD, + FREAD, -1, -1, 0, kif, sb, &remainder); FILEDESC_SLOCK(fdp); } /* root directory */ @@ -3283,8 +3295,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, kif, req); + export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_ROOT, + FREAD, -1, -1, 0, kif, sb, &remainder); FILEDESC_SLOCK(fdp); } /* jail directory */ @@ -3292,8 +3304,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, kif, req); + export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_JAIL, + FREAD, -1, -1, 0, kif, sb, &remainder); FILEDESC_SLOCK(fdp); } for (i = 0; i < fdp->fd_nfiles; i++) { @@ -3375,26 +3387,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_cap_rights, kif, req); + error = export_fd_to_sb(data, type, i, fflags, refcnt, + offset, fd_cap_rights, kif, sb, &remainder); 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: @@ -3404,6 +3404,34 @@ 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; + ssize_t maxlen; + 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); + } + maxlen = req->oldptr != NULL ? req->oldlen : -1; + error = kern_proc_filedesc_out(p, &sb, maxlen); + error2 = sbuf_finish(&sb); + sbuf_delete(&sb); + return (error != 0 ? error : error2); +} + int vntype_to_kinfo(int vtype) { diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index e3f3211..ded8950 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -1089,9 +1089,6 @@ zpfind(pid_t pid) return (p); } -#define KERN_PROC_ZOMBMASK 0x3 -#define KERN_PROC_NOTHREADS 0x4 - #ifdef COMPAT_FREEBSD32 /* @@ -1191,59 +1188,69 @@ freebsd32_kinfo_proc_out(const struct kinfo_proc *ki, struct kinfo_proc32 *ki32) CP(*ki, *ki32, ki_sflag); CP(*ki, *ki32, ki_tdflags); } - -static int -sysctl_out_proc_copyout(struct kinfo_proc *ki, struct sysctl_req *req) -{ - 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); -} -#else -static int -sysctl_out_proc_copyout(struct kinfo_proc *ki, struct sysctl_req *req) -{ - - return (SYSCTL_OUT(req, ki, sizeof(struct kinfo_proc))); -} #endif -/* - * Must be called with the process locked and will return with it unlocked. - */ -static int -sysctl_out_proc(struct proc *p, struct sysctl_req *req, int flags) +int +kern_proc_out(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; + struct kinfo_proc ki; +#ifdef COMPAT_FREEBSD32 + struct kinfo_proc32 ki32; +#endif + int error; PROC_LOCK_ASSERT(p, MA_OWNED); MPASS(FIRST_THREAD_IN_PROC(p) != NULL); - fill_kinfo_proc(p, &kinfo_proc); - if (flags & KERN_PROC_NOTHREADS) - error = sysctl_out_proc_copyout(&kinfo_proc, req); - else { + error = 0; + fill_kinfo_proc(p, &ki); + if ((flags & KERN_PROC_NOTHREADS) != 0) { +#ifdef COMPAT_FREEBSD32 + if ((flags & KERN_PROC_MASK32) != 0) { + freebsd32_kinfo_proc_out(&ki, &ki32); + error = sbuf_bcat(sb, &ki32, sizeof(ki32)); + } else +#endif + error = sbuf_bcat(sb, &ki, sizeof(ki)); + } else { FOREACH_THREAD_IN_PROC(p, td) { - fill_kinfo_thread(td, &kinfo_proc, 1); - error = sysctl_out_proc_copyout(&kinfo_proc, req); + fill_kinfo_thread(td, &ki, 1); +#ifdef COMPAT_FREEBSD32 + if ((flags & KERN_PROC_MASK32) != 0) { + freebsd32_kinfo_proc_out(&ki, &ki32); + error = sbuf_bcat(sb, &ki32, sizeof(ki32)); + } else +#endif + error = sbuf_bcat(sb, &ki, sizeof(ki)); if (error) break; } } PROC_UNLOCK(p); - if (error) + return (error); +} + +static int +sysctl_out_proc(struct proc *p, struct sysctl_req *req, int flags, + int doingzomb) +{ + 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(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 (doingzomb) np = zpfind(pid); else { if (pid == 0) @@ -1277,6 +1284,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); @@ -1286,7 +1297,7 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) error = pget((pid_t)name[0], PGET_CANSEE, &p); if (error != 0) return (error); - error = sysctl_out_proc(p, req, flags); + error = sysctl_out_proc(p, req, flags, 0); return (error); } @@ -1415,7 +1426,7 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) } - error = sysctl_out_proc(p, req, flags | doingzomb); + error = sysctl_out_proc(p, req, flags, doingzomb); if (error) { sx_sunlock(&allproc_lock); return (error); @@ -2119,8 +2130,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; @@ -2128,16 +2142,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); @@ -2145,6 +2158,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; @@ -2284,7 +2298,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; @@ -2300,6 +2314,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 5de76ac..2f6514b 100644 --- a/sys/sys/user.h +++ b/sys/sys/user.h @@ -491,6 +491,25 @@ struct kinfo_kstack { }; #ifdef _KERNEL +/* Flags for kern_proc_out function. */ +#define KERN_PROC_NOTHREADS 0x1 +#define KERN_PROC_MASK32 0x2 + +struct sbuf; + +/* + * The kern_proc out functions are helper functions to dump process + * miscellaneous kinfo structures to sbuf. The main consumers are KERN_PROC + * sysctls but they may also be used by other kernel subsystems. + * + * The functions manipulate the process locking state and expect the process + * to be locked on enter. On return the process is unlocked. + */ + +int kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen); +int kern_proc_out(struct proc *p, struct sbuf *sb, int flags); +int kern_proc_vmmap_out(struct proc *p, struct sbuf *sb); + int vntype_to_kinfo(int vtype); #endif /* !_KERNEL */ commit 4f181d4b3034ae881a2c09d70fc655f4779d5e7f Author: Mikolaj Golub Date: Sun Mar 31 12:36:09 2013 +0300 Similarly to proc_getargv() and proc_getenvv(), export proc_getauxv() to be able to reuse the code. MFC after: 3 weeks diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index ded8950..cb1ec9d 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -1759,6 +1759,27 @@ proc_getenvv(struct thread *td, struct proc *p, struct sbuf *sb) return (get_ps_strings(curthread, p, sb, PROC_ENV)); } +int +proc_getauxv(struct thread *td, struct proc *p, struct sbuf *sb) +{ + size_t vsize, size; + char **auxv; + int error; + + error = get_proc_vector(td, p, &auxv, &vsize, PROC_AUX); + if (error == 0) { +#ifdef COMPAT_FREEBSD32 + if (SV_PROC_FLAG(p, SV_ILP32) != 0) + size = vsize * sizeof(Elf32_Auxinfo); + else +#endif + size = vsize * sizeof(Elf_Auxinfo); + error = sbuf_bcat(sb, auxv, size); + free(auxv, M_TEMP); + } + return (error); +} + /* * This sysctl allows a process to retrieve the argument list or process * title for another process without groping around in the address space @@ -1864,9 +1885,8 @@ sysctl_kern_proc_auxv(SYSCTL_HANDLER_ARGS) int *name = (int *)arg1; u_int namelen = arg2; struct proc *p; - size_t vsize, size; - char **auxv; - int error; + struct sbuf sb; + int error, error2; if (namelen != 1) return (EINVAL); @@ -1878,21 +1898,12 @@ sysctl_kern_proc_auxv(SYSCTL_HANDLER_ARGS) PRELE(p); return (0); } - error = get_proc_vector(curthread, p, &auxv, &vsize, PROC_AUX); - if (error == 0) { -#ifdef COMPAT_FREEBSD32 - if (SV_PROC_FLAG(p, SV_ILP32) != 0) - size = vsize * sizeof(Elf32_Auxinfo); - else -#endif - size = vsize * sizeof(Elf_Auxinfo); - PRELE(p); - error = SYSCTL_OUT(req, auxv, size); - free(auxv, M_TEMP); - } else { - PRELE(p); - } - return (error); + sbuf_new_for_sysctl(&sb, NULL, GET_PS_STRINGS_CHUNK_SZ, req); + error = proc_getauxv(curthread, p, &sb); + error2 = sbuf_finish(&sb); + PRELE(p); + sbuf_delete(&sb); + return (error != 0 ? error : error2); } /* diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 4ce6f2d..4e2922e 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -873,6 +873,7 @@ struct pargs *pargs_alloc(int len); void pargs_drop(struct pargs *pa); void pargs_hold(struct pargs *pa); int proc_getargv(struct thread *td, struct proc *p, struct sbuf *sb); +int proc_getauxv(struct thread *td, struct proc *p, struct sbuf *sb); int proc_getenvv(struct thread *td, struct proc *p, struct sbuf *sb); void procinit(void); void proc_linkup0(struct proc *p, struct thread *td); commit 62f8914f24438879a7c2a4513254f18145ac6f9d Author: Mikolaj Golub Date: Tue Feb 26 23:44:22 2013 +0200 Add a new set of notes to a process core dump to store procstat data. The userland tools (procstat(1)) will be taught to extract this data, providing additional info for postmortem analysis. PR: kern/173723 Suggested by: jhb Discussed with: jhb, kib Reviewed by: jhb (initial version), kib diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index f4659c3..5bbdef5 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -66,6 +66,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include @@ -1062,12 +1063,22 @@ static void __elfN(puthdr)(struct thread *, void *, size_t, int, size_t); static void __elfN(putnote)(struct note_info *, struct sbuf *); static size_t register_note(struct note_info_list *, int, outfunc_t, void *); static int sbuf_drain_core_output(void *, const char *, int); +static int sbuf_drain_count(void *arg, const char *data, int len); static void __elfN(note_fpregset)(void *, struct sbuf *, size_t *); static void __elfN(note_prpsinfo)(void *, struct sbuf *, size_t *); static void __elfN(note_prstatus)(void *, struct sbuf *, size_t *); static void __elfN(note_threadmd)(void *, struct sbuf *, size_t *); static void __elfN(note_thrmisc)(void *, struct sbuf *, size_t *); +static void __elfN(note_procstat_auxv)(void *, struct sbuf *, size_t *); +static void __elfN(note_procstat_proc)(void *, struct sbuf *, size_t *); +static void __elfN(note_procstat_psstrings)(void *, struct sbuf *, size_t *); +static void note_procstat_files(void *, struct sbuf *, size_t *); +static void note_procstat_groups(void *, struct sbuf *, size_t *); +static void note_procstat_osrel(void *, struct sbuf *, size_t *); +static void note_procstat_rlimit(void *, struct sbuf *, size_t *); +static void note_procstat_umask(void *, struct sbuf *, size_t *); +static void note_procstat_vmmap(void *, struct sbuf *, size_t *); #ifdef COMPRESS_USER_CORES extern int compress_user_cores; @@ -1113,9 +1124,21 @@ static int sbuf_drain_core_output(void *arg, const char *data, int len) { struct sbuf_drain_core_params *p; - int error; + int error, locked; p = (struct sbuf_drain_core_params *)arg; + + /* + * Some kern_proc out routines that print to this sbuf may + * call us with the process lock held. Draining with the + * non-sleepable lock held is unsafe. The lock is needed for + * those routines when dumping a live process. In our case we + * can safely release the lock before draining and acquire + * again after. + */ + locked = PROC_LOCKED(p->td->td_proc); + if (locked) + PROC_UNLOCK(p->td->td_proc); #ifdef COMPRESS_USER_CORES if (p->gzfile != Z_NULL) error = compress_core(p->gzfile, NULL, __DECONST(char *, data), @@ -1126,12 +1149,27 @@ sbuf_drain_core_output(void *arg, const char *data, int len) __DECONST(void *, data), len, p->offset, UIO_SYSSPACE, IO_UNIT | IO_DIRECT, p->active_cred, p->file_cred, NULL, p->td); + if (locked) + PROC_LOCK(p->td->td_proc); if (error != 0) return (-error); p->offset += len; return (len); } +/* + * Drain into a counter. + */ +static int +sbuf_drain_count(void *arg, const char *data __unused, int len) +{ + size_t *sizep; + + sizep = (size_t *)arg; + *sizep += len; + return (len); +} + int __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) { @@ -1436,6 +1474,25 @@ __elfN(prepare_notes)(struct thread *td, struct note_info_list *list, thr = TAILQ_NEXT(thr, td_plist); } + size += register_note(list, NT_PROCSTAT_PROC, + __elfN(note_procstat_proc), p); + size += register_note(list, NT_PROCSTAT_FILES, + note_procstat_files, p); + size += register_note(list, NT_PROCSTAT_VMMAP, + note_procstat_vmmap, p); + size += register_note(list, NT_PROCSTAT_GROUPS, + note_procstat_groups, p); + size += register_note(list, NT_PROCSTAT_UMASK, + note_procstat_umask, p); + size += register_note(list, NT_PROCSTAT_RLIMIT, + note_procstat_rlimit, p); + size += register_note(list, NT_PROCSTAT_OSREL, + note_procstat_osrel, p); + size += register_note(list, NT_PROCSTAT_PSSTRINGS, + __elfN(note_procstat_psstrings), p); + size += register_note(list, NT_PROCSTAT_AUXV, + __elfN(note_procstat_auxv), p); + *sizep = size; } @@ -1562,6 +1619,9 @@ typedef struct fpreg32 elf_prfpregset_t; typedef struct fpreg32 elf_fpregset_t; typedef struct reg32 elf_gregset_t; typedef struct thrmisc32 elf_thrmisc_t; +#define ELF_KERN_PROC_MASK KERN_PROC_MASK32 +typedef struct kinfo_proc32 elf_kinfo_proc_t; +typedef uint32_t elf_ps_strings_t; #else typedef prstatus_t elf_prstatus_t; typedef prpsinfo_t elf_prpsinfo_t; @@ -1569,6 +1629,9 @@ typedef prfpregset_t elf_prfpregset_t; typedef prfpregset_t elf_fpregset_t; typedef gregset_t elf_gregset_t; typedef thrmisc_t elf_thrmisc_t; +#define ELF_KERN_PROC_MASK 0 +typedef struct kinfo_proc elf_kinfo_proc_t; +typedef vm_offset_t elf_ps_strings_t; #endif static void @@ -1686,6 +1749,221 @@ __elfN(note_threadmd)(void *arg, struct sbuf *sb, size_t *sizep) *sizep = size; } +#ifdef KINFO_PROC_SIZE +CTASSERT(sizeof(struct kinfo_proc) == KINFO_PROC_SIZE); +#endif + +static void +__elfN(note_procstat_proc)(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct proc *p; + size_t size; + int structsize; + + p = (struct proc *)arg; + size = sizeof(structsize) + p->p_numthreads * + sizeof(elf_kinfo_proc_t); + + if (sb != NULL) { + KASSERT(*sizep == size, ("invalid size")); + structsize = sizeof(elf_kinfo_proc_t); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + PROC_LOCK(p); + kern_proc_out(p, sb, ELF_KERN_PROC_MASK); + } + *sizep = size; +} + +#ifdef KINFO_FILE_SIZE +CTASSERT(sizeof(struct kinfo_file) == KINFO_FILE_SIZE); +#endif + +static void +note_procstat_files(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct proc *p; + size_t size; + int structsize; + + p = (struct proc *)arg; + if (sb == NULL) { + size = 0; + sb = sbuf_new(NULL, NULL, 128, SBUF_FIXEDLEN); + sbuf_set_drain(sb, sbuf_drain_count, &size); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + PROC_LOCK(p); + kern_proc_filedesc_out(p, sb, -1); + sbuf_finish(sb); + sbuf_delete(sb); + *sizep = size; + } else { + structsize = sizeof(struct kinfo_file); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + PROC_LOCK(p); + kern_proc_filedesc_out(p, sb, -1); + } +} + +#ifdef KINFO_VMENTRY_SIZE +CTASSERT(sizeof(struct kinfo_vmentry) == KINFO_VMENTRY_SIZE); +#endif + +static void +note_procstat_vmmap(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct proc *p; + size_t size; + int structsize; + + p = (struct proc *)arg; + if (sb == NULL) { + size = 0; + sb = sbuf_new(NULL, NULL, 128, SBUF_FIXEDLEN); + sbuf_set_drain(sb, sbuf_drain_count, &size); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + PROC_LOCK(p); + kern_proc_vmmap_out(p, sb); + sbuf_finish(sb); + sbuf_delete(sb); + *sizep = size; + } else { + structsize = sizeof(struct kinfo_vmentry); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + PROC_LOCK(p); + kern_proc_vmmap_out(p, sb); + } +} + +static void +note_procstat_groups(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct proc *p; + size_t size; + int structsize; + + p = (struct proc *)arg; + size = sizeof(structsize) + p->p_ucred->cr_ngroups * sizeof(gid_t); + if (sb != NULL) { + KASSERT(*sizep == size, ("invalid size")); + structsize = sizeof(gid_t); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + sbuf_bcat(sb, p->p_ucred->cr_groups, p->p_ucred->cr_ngroups * + sizeof(gid_t)); + } + *sizep = size; +} + +static void +note_procstat_umask(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct proc *p; + size_t size; + int structsize; + + p = (struct proc *)arg; + size = sizeof(structsize) + sizeof(p->p_fd->fd_cmask); + if (sb != NULL) { + KASSERT(*sizep == size, ("invalid size")); + structsize = sizeof(p->p_fd->fd_cmask); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + sbuf_bcat(sb, &p->p_fd->fd_cmask, sizeof(p->p_fd->fd_cmask)); + } + *sizep = size; +} + +static void +note_procstat_rlimit(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct proc *p; + struct rlimit rlim[RLIM_NLIMITS]; + size_t size; + int structsize, i; + + p = (struct proc *)arg; + size = sizeof(structsize) + sizeof(rlim); + if (sb != NULL) { + KASSERT(*sizep == size, ("invalid size")); + structsize = sizeof(rlim); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + PROC_LOCK(p); + for (i = 0; i < RLIM_NLIMITS; i++) + lim_rlimit(p, i, &rlim[i]); + PROC_UNLOCK(p); + sbuf_bcat(sb, rlim, sizeof(rlim)); + } + *sizep = size; +} + +static void +note_procstat_osrel(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct proc *p; + size_t size; + int structsize; + + p = (struct proc *)arg; + size = sizeof(structsize) + sizeof(p->p_osrel); + if (sb != NULL) { + KASSERT(*sizep == size, ("invalid size")); + structsize = sizeof(p->p_osrel); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + sbuf_bcat(sb, &p->p_osrel, sizeof(p->p_osrel)); + } + *sizep = size; +} + +static void +__elfN(note_procstat_psstrings)(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct proc *p; + elf_ps_strings_t ps_strings; + size_t size; + int structsize; + + p = (struct proc *)arg; + size = sizeof(structsize) + sizeof(ps_strings); + if (sb != NULL) { + KASSERT(*sizep == size, ("invalid size")); + structsize = sizeof(ps_strings); +#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 + ps_strings = PTROUT(p->p_sysent->sv_psstrings); +#else + ps_strings = p->p_sysent->sv_psstrings; +#endif + sbuf_bcat(sb, &structsize, sizeof(structsize)); + sbuf_bcat(sb, &ps_strings, sizeof(ps_strings)); + } + *sizep = size; +} + +static void +__elfN(note_procstat_auxv)(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct proc *p; + size_t size; + int structsize; + + p = (struct proc *)arg; + if (sb == NULL) { + size = 0; + sb = sbuf_new(NULL, NULL, 128, SBUF_FIXEDLEN); + sbuf_set_drain(sb, sbuf_drain_count, &size); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + PHOLD(p); + proc_getauxv(curthread, p, sb); + PRELE(p); + sbuf_finish(sb); + sbuf_delete(sb); + *sizep = size; + } else { + structsize = sizeof(Elf_Auxinfo); + sbuf_bcat(sb, &structsize, sizeof(structsize)); + PHOLD(p); + proc_getauxv(curthread, p, sb); + PRELE(p); + } +} + static boolean_t __elfN(parse_notes)(struct image_params *imgp, Elf_Brandnote *checknote, int32_t *osrel, const Elf_Phdr *pnote) diff --git a/sys/sys/elf_common.h b/sys/sys/elf_common.h index 8f02ef1..775a747 100644 --- a/sys/sys/elf_common.h +++ b/sys/sys/elf_common.h @@ -487,6 +487,15 @@ 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 data. */ +#define NT_PROCSTAT_FILES 9 /* Procstat files data. */ +#define NT_PROCSTAT_VMMAP 10 /* Procstat vmmap data. */ +#define NT_PROCSTAT_GROUPS 11 /* Procstat groups data. */ +#define NT_PROCSTAT_UMASK 12 /* Procstat umask data. */ +#define NT_PROCSTAT_RLIMIT 13 /* Procstat rlimit data. */ +#define NT_PROCSTAT_OSREL 14 /* Procstat osreldate data. */ +#define NT_PROCSTAT_PSSTRINGS 15 /* Procstat ps_strings data. */ +#define NT_PROCSTAT_AUXV 16 /* Procstat auxv data. */ /* Symbol Binding - ELFNN_ST_BIND - st_info */ #define STB_LOCAL 0 /* Local symbol */ commit 291ada3c0a5bf45c4f1ef585a0e4a41110fd45d8 Author: Mikolaj Golub Date: Mon Feb 11 00:26:53 2013 +0200 Make libprocstat(3) extract procstat notes from a process core file. PR: kern/173723 Suggested by: jhb Glanced by: kib MFC after: 1 month diff --git a/Makefile.inc1 b/Makefile.inc1 index 99c4cc9..137454c 100644 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -1382,7 +1382,7 @@ _prebuild_libs= ${_kerberos5_lib_libasn1} \ ${_kerberos5_lib_libwind} \ ${_lib_atf_libatf_c} \ lib/libbz2 ${_libcom_err} lib/libcrypt \ - lib/libexpat \ + lib/libelf lib/libexpat \ ${_lib_libgssapi} ${_lib_libipx} \ lib/libkiconv lib/libkvm lib/liblzma lib/libmd \ lib/ncurses/ncurses lib/ncurses/ncursesw \ 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..a078090 100644 --- a/lib/libprocstat/Symbol.map +++ b/lib/libprocstat/Symbol.map @@ -17,4 +17,5 @@ FBSD_1.2 { FBSD_1.3 { procstat_get_shm_info; + procstat_open_core; }; diff --git a/lib/libprocstat/core.c b/lib/libprocstat/core.c new file mode 100644 index 0000000..1ada459 --- /dev/null +++ b/lib/libprocstat/core.c @@ -0,0 +1,262 @@ +/*- + * 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 +#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; +}; + +static bool core_offset(struct procstat_core *core, off_t offset); +static bool core_read(struct procstat_core *core, void *buf, size_t len); + +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); +} + +void * +procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, + size_t *lenp) +{ + Elf_Note nhdr; + off_t offset, eoffset; + void *freebuf; + size_t len; + u_int32_t n_type; + int cstructsize, structsize; + char nbuf[8]; + + assert(core->pc_magic == PROCSTAT_CORE_MAGIC); + + switch(type) { + case PSC_TYPE_PROC: + n_type = NT_PROCSTAT_PROC; + structsize = sizeof(struct kinfo_proc); + break; + case PSC_TYPE_FILES: + n_type = NT_PROCSTAT_FILES; + structsize = sizeof(struct kinfo_file); + break; + case PSC_TYPE_VMMAP: + n_type = NT_PROCSTAT_VMMAP; + structsize = sizeof(struct kinfo_vmentry); + break; + default: + warnx("unknown core stat type: %d", type); + return (NULL); + } + + offset = core->pc_phdr.p_offset; + eoffset = offset + core->pc_phdr.p_filesz; + + while (offset < eoffset) { + if (!core_offset(core, offset)) + return (NULL); + if (!core_read(core, &nhdr, sizeof(nhdr))) + return (NULL); + + offset += sizeof(nhdr) + + roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) + + roundup2(nhdr.n_descsz, sizeof(Elf32_Size)); + + if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0) + break; + if (nhdr.n_type != n_type) + continue; + if (nhdr.n_namesz != 8) + continue; + if (!core_read(core, nbuf, sizeof(nbuf))) + return (NULL); + if (strcmp(nbuf, "FreeBSD") != 0) + continue; + if (nhdr.n_descsz < sizeof(cstructsize)) { + warnx("corrupted core file"); + return (NULL); + } + if (!core_read(core, &cstructsize, sizeof(cstructsize))) + return (NULL); + if (cstructsize != structsize) { + warnx("version mismatch"); + return (NULL); + } + len = nhdr.n_descsz - sizeof(cstructsize); + if (len == 0) + return (NULL); + if (buf != NULL) { + len = MIN(len, *lenp); + freebuf = NULL; + } else { + freebuf = buf = malloc(len); + if (buf == NULL) { + warn("malloc(%zu)", len); + return (NULL); + } + } + if (!core_read(core, buf, len)) { + free(freebuf); + return (NULL); + } + *lenp = len; + return (buf); + } + + return (NULL); +} + +static bool +core_offset(struct procstat_core *core, off_t offset) +{ + + assert(core->pc_magic == PROCSTAT_CORE_MAGIC); + + if (lseek(core->pc_fd, offset, SEEK_SET) == -1) { + warn("core: lseek(%jd)", (intmax_t)offset); + return (false); + } + return (true); +} + +static bool +core_read(struct procstat_core *core, void *buf, size_t len) +{ + ssize_t n; + + assert(core->pc_magic == PROCSTAT_CORE_MAGIC); + + n = read(core->pc_fd, buf, len); + if (n == -1) { + warn("core: read"); + return (false); + } + if (n < (ssize_t)len) { + warnx("core: short read"); + return (false); + } + return (true); +} diff --git a/lib/libprocstat/core.h b/lib/libprocstat/core.h new file mode 100644 index 0000000..8e2dd13 --- /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, + void * buf, size_t *lenp); +struct procstat_core *procstat_core_open(const char *filename); + +#endif /* !_CORE_H_ */ diff --git a/lib/libprocstat/libprocstat.3 b/lib/libprocstat/libprocstat.3 index dd163c2..a64376c 100644 --- a/lib/libprocstat/libprocstat.3 +++ b/lib/libprocstat/libprocstat.3 @@ -28,6 +28,7 @@ .Dt LIBPROCSTAT 3 .Os .Sh NAME +.Nm procstat_open_core , .Nm procstat_open_kvm , .Nm procstat_open_sysctl , .Nm procstat_close , @@ -105,6 +106,8 @@ .Fa "unsigned int *count" .Fc .Ft "struct procstat *" +.Fn procstat_open_core "const char *filename" +.Ft "struct procstat *" .Fn procstat_open_kvm "const char *nlistf" "const char *memf" .Ft "struct procstat *" .Fn procstat_open_sysctl void @@ -116,7 +119,11 @@ retrieval from the running kernel via the .Xr sysctl 3 library backend, and for post-mortem analysis via the .Xr kvm 3 -library backend. +library backend, or from the process +.Xr core 5 +file, searching for statistics in special +.Xr elf 3 +note sections. .Pp The .Fn procstat_open_kvm @@ -129,6 +136,16 @@ or library routines, respectively, to access kernel state information used to retrieve processes and files states. The +.Fn procstat_open_core +uses +.Xr elf 3 +routines to access statistics stored as a set of notes in a process +.Xr core 5 +file, written by the kernel at the moment of the process abnormal termination. +The +.Fa filename +argument is the process core file name. +The .Fa nlistf argument is the executable image of the kernel being examined. If this argument is @@ -145,7 +162,7 @@ is assumed. See .Xr kvm_open 3 for more details. -Both functions dynamically allocate and return a +The functions dynamically allocate and return a .Vt procstat structure pointer used in the rest of the .Nm libprocstat @@ -250,10 +267,12 @@ argument indicates an actual error message in case of failure. .Xr pipe 2 , .Xr shm_open 2 , .Xr socket 2 , +.Xr elf 3 , .Xr kvm 3 , .Xr queue 3 , .Xr sysctl 3 , .Xr pts 4 , +.Xr core 5 , .Xr vnode 9 .Sh HISTORY The diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index f23ec96..574d654 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,15 @@ 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, NULL, + &len); + if ((len % sizeof(*p)) != 0 || p->ki_structsize != sizeof(*p)) { warnx("kinfo_proc structure size mismatch"); goto fail; } @@ -258,13 +292,17 @@ 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) - return (procstat_getfiles_sysctl(procstat, kp, mmapped)); - else if (procstat->type == PROCSTAT_KVM) + + switch(procstat->type) { + case PROCSTAT_KVM: return (procstat_getfiles_kvm(procstat, kp, mmapped)); - else + case PROCSTAT_SYSCTL: + case PROCSTAT_CORE: + return (procstat_getfiles_sysctl(procstat, kp, mmapped)); + default: + warnx("unknown access method: %d", procstat->type); return (NULL); + } } void @@ -646,8 +684,62 @@ 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, NULL, &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 filestat_list * -procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, int mmapped) +procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, + int mmapped) { struct kinfo_file *kif, *files; struct kinfo_vmentry *kve, *vmentries; @@ -663,8 +755,16 @@ procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, int m assert(kp); if (kp->ki_fd == NULL) return (NULL); - - files = kinfo_getfile(kp->ki_pid, &cnt); + switch(procstat->type) { + case PROCSTAT_SYSCTL: + files = kinfo_getfile(kp->ki_pid, &cnt); + break; + case PROCSTAT_CORE: + files = kinfo_getfile_core(procstat->core, &cnt); + break; + default: + assert(!"invalid type"); + } if (files == NULL && errno != EPERM) { warn("kinfo_getfile()"); return (NULL); @@ -742,7 +842,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); @@ -806,7 +907,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); @@ -868,7 +970,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); @@ -948,7 +1051,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); @@ -1150,7 +1254,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); @@ -1401,3 +1506,4 @@ getmnton(kvm_t *kd, struct mount *m) mhead = mt; return (mt->mntonname); } + diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h index 1c55aa7..4ad9cee 100644 --- a/lib/libprocstat/libprocstat.h +++ b/lib/libprocstat/libprocstat.h @@ -162,6 +162,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 7b074f2fa13e2dc1801f27af2353e8f54b29cb5f Author: Mikolaj Golub Date: Tue Mar 12 16:53:40 2013 +0200 Add procstat_getvmmap function to get VM layout of a process. MFC after: 1 month diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map index a078090..aa74659 100644 --- a/lib/libprocstat/Symbol.map +++ b/lib/libprocstat/Symbol.map @@ -16,6 +16,8 @@ FBSD_1.2 { }; FBSD_1.3 { + procstat_freevmmap; procstat_get_shm_info; + procstat_getvmmap; procstat_open_core; }; diff --git a/lib/libprocstat/libprocstat.3 b/lib/libprocstat/libprocstat.3 index a64376c..18dc1e9 100644 --- a/lib/libprocstat/libprocstat.3 +++ b/lib/libprocstat/libprocstat.3 @@ -34,8 +34,10 @@ .Nm procstat_close , .Nm procstat_getfiles , .Nm procstat_getprocs , +.Nm procstat_getvmmap , .Nm procstat_freefiles , .Nm procstat_freeprocs , +.Nm procstat_freevmmap , .Nm procstat_get_pipe_info , .Nm procstat_get_pts_info , .Nm procstat_get_shm_info , @@ -57,6 +59,11 @@ .Fc .Ft void .Fn procstat_freeprocs "struct procstat *procstat" "struct kinfo_proc *p" +.Ft void +.Fo procstat_freevmmap +.Fa "struct procstat *procstat" +.Fa "struct kinfo_vmentry *vmmap" +.Fc .Ft int .Fo procstat_get_pipe_info .Fa "struct procstat *procstat" @@ -105,6 +112,12 @@ .Fa "int arg" .Fa "unsigned int *count" .Fc +.Ft "struct kinfo_vmentry *" +.Fo procstat_getvmmap +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "unsigned int *count" +.Fc .Ft "struct procstat *" .Fn procstat_open_core "const char *filename" .Ft "struct procstat *" @@ -214,6 +227,22 @@ The caller is responsible to free the allocated memory with a subsequent function call. .Pp The +.Fn procstat_getvmmap +function gets a pointer to the +.Vt procstat +structure initialized with one of the +.Fn procstat_open_* +functions, a pointer to +.Vt kinfo_proc +structure, and returns VM layout of the process as a dynamically allocated +array of +.Vt kinfo_vmentry +structures. +The caller is responsible to free the allocated memory with a subsequent +.Fn procstat_freevmmap +function call. +.Pp +The .Fn procstat_get_pipe_info , .Fn procstat_get_pts_info , .Fn procstat_get_shm_info , diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index 574d654..283f802 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -105,6 +105,8 @@ int statfs(const char *, struct statfs *); /* XXX */ #define PROCSTAT_CORE 3 static char *getmnton(kvm_t *kd, struct mount *m); +static struct kinfo_vmentry * kinfo_getvmmap_core(struct procstat_core *core, + int *cntp); static struct filestat_list *procstat_getfiles_kvm( struct procstat *procstat, struct kinfo_proc *kp, int mmapped); static struct filestat_list *procstat_getfiles_sysctl( @@ -802,7 +804,7 @@ procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, STAILQ_INSERT_TAIL(head, entry, next); } if (mmapped != 0) { - vmentries = kinfo_getvmmap(kp->ki_pid, &cnt); + vmentries = procstat_getvmmap(procstat, kp, &cnt); procstat->vmentries = vmentries; if (vmentries == NULL || cnt == 0) goto fail; @@ -1507,3 +1509,82 @@ getmnton(kvm_t *kd, struct mount *m) return (mt->mntonname); } +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, NULL, &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); + } +} + +void +procstat_freevmmap(struct procstat *procstat __unused, + struct kinfo_vmentry *vmmap) +{ + + free(vmmap); +} diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h index 4ad9cee..71d85a1 100644 --- a/lib/libprocstat/libprocstat.h +++ b/lib/libprocstat/libprocstat.h @@ -89,6 +89,7 @@ #define PS_FST_FFLAG_EXEC 0x2000 #define PS_FST_FFLAG_HASLOCK 0x4000 +struct kinfo_vmentry; struct procstat; struct filestat { int fs_type; /* Descriptor type. */ @@ -148,6 +149,8 @@ void procstat_close(struct procstat *procstat); void procstat_freeprocs(struct procstat *procstat, struct kinfo_proc *p); void procstat_freefiles(struct procstat *procstat, struct filestat_list *head); +void procstat_freevmmap(struct procstat *procstat, + struct kinfo_vmentry *vmmap); struct filestat_list *procstat_getfiles(struct procstat *procstat, struct kinfo_proc *kp, int mmapped); struct kinfo_proc *procstat_getprocs(struct procstat *procstat, @@ -162,6 +165,8 @@ 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 kinfo_vmentry *procstat_getvmmap(struct procstat *procstat, + struct kinfo_proc *kp, unsigned int *count); 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); commit d8f8506b40b4181bd1c87a9fd97dfb2ea2b7b8ce Author: Mikolaj Golub Date: Mon Mar 4 00:00:55 2013 +0200 Use procstat_getprocs(3) for retrieving thread information instead of direct sysctl calls. MFC after: 1 month diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c index 934e292..6b95246 100644 --- a/usr.bin/procstat/procstat.c +++ b/usr.bin/procstat/procstat.c @@ -77,7 +77,7 @@ procstat(struct procstat *prstat, struct kinfo_proc *kipp) else if (sflag) procstat_cred(kipp); else if (tflag) - procstat_threads(kipp); + procstat_threads(prstat, kipp); else if (vflag) procstat_vm(kipp); else if (xflag) diff --git a/usr.bin/procstat/procstat.h b/usr.bin/procstat/procstat.h index e65436d..718eb47 100644 --- a/usr.bin/procstat/procstat.h +++ b/usr.bin/procstat/procstat.h @@ -44,7 +44,7 @@ void procstat_files(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_kstack(struct kinfo_proc *kipp, int kflag); 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(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_threads_sigs(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_vm(struct kinfo_proc *kipp); diff --git a/usr.bin/procstat/procstat_sigs.c b/usr.bin/procstat/procstat_sigs.c index 70df250..49e2b19 100644 --- a/usr.bin/procstat/procstat_sigs.c +++ b/usr.bin/procstat/procstat_sigs.c @@ -86,48 +86,24 @@ procstat_sigs(struct procstat *prstat __unused, struct kinfo_proc *kipp) } void -procstat_threads_sigs(struct procstat *prstat __unused, struct kinfo_proc *kipp) +procstat_threads_sigs(struct procstat *procstat, struct kinfo_proc *kipp) { struct kinfo_proc *kip; pid_t pid; - int error, name[4], j; - unsigned int i; - size_t len; + int j; + unsigned int count, i; pid = kipp->ki_pid; if (!hflag) printf("%5s %6s %-16s %-7s %4s\n", "PID", "TID", "COMM", "SIG", "FLAGS"); - /* - * We need to re-query for thread information, so don't use *kipp. - */ - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_PID | KERN_PROC_INC_THREAD; - name[3] = pid; - - len = 0; - error = sysctl(name, 4, NULL, &len, NULL, 0); - if (error < 0 && errno != ESRCH) { - warn("sysctl: kern.proc.pid: %d", pid); - return; - } - if (error < 0) - return; - - kip = malloc(len); + kip = procstat_getprocs(procstat, KERN_PROC_PID | KERN_PROC_INC_THREAD, + pid, &count); if (kip == NULL) - err(-1, "malloc"); - - if (sysctl(name, 4, kip, &len, NULL, 0) < 0) { - warn("sysctl: kern.proc.pid: %d", pid); - free(kip); return; - } - - kinfo_proc_sort(kip, len / sizeof(*kipp)); - for (i = 0; i < len / sizeof(*kipp); i++) { + kinfo_proc_sort(kip, count); + for (i = 0; i < count; i++) { kipp = &kip[i]; for (j = 1; j <= _SIG_MAXSIG; j++) { printf("%5d ", pid); @@ -140,5 +116,5 @@ procstat_threads_sigs(struct procstat *prstat __unused, struct kinfo_proc *kipp) printf("\n"); } } - free(kip); + procstat_freeprocs(procstat, kip); } diff --git a/usr.bin/procstat/procstat_threads.c b/usr.bin/procstat/procstat_threads.c index ffd659c..6bd88da 100644 --- a/usr.bin/procstat/procstat_threads.c +++ b/usr.bin/procstat/procstat_threads.c @@ -40,47 +40,22 @@ #include "procstat.h" void -procstat_threads(struct kinfo_proc *kipp) +procstat_threads(struct procstat *procstat, struct kinfo_proc *kipp) { struct kinfo_proc *kip; - int error, name[4]; - unsigned int i; + unsigned int count, i; const char *str; - size_t len; if (!hflag) printf("%5s %6s %-16s %-16s %2s %4s %-7s %-9s\n", "PID", "TID", "COMM", "TDNAME", "CPU", "PRI", "STATE", "WCHAN"); - /* - * We need to re-query for thread information, so don't use *kipp. - */ - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_PID | KERN_PROC_INC_THREAD; - name[3] = kipp->ki_pid; - - len = 0; - error = sysctl(name, 4, NULL, &len, NULL, 0); - if (error < 0 && errno != ESRCH) { - warn("sysctl: kern.proc.pid: %d", kipp->ki_pid); - return; - } - if (error < 0) - return; - - kip = malloc(len); + kip = procstat_getprocs(procstat, KERN_PROC_PID | KERN_PROC_INC_THREAD, + kipp->ki_pid, &count); if (kip == NULL) - err(-1, "malloc"); - - if (sysctl(name, 4, kip, &len, NULL, 0) < 0) { - warn("sysctl: kern.proc.pid: %d", kipp->ki_pid); - free(kip); return; - } - - kinfo_proc_sort(kip, len / sizeof(*kipp)); - for (i = 0; i < len / sizeof(*kipp); i++) { + kinfo_proc_sort(kip, count); + for (i = 0; i < count; i++) { kipp = &kip[i]; printf("%5d ", kipp->ki_pid); printf("%6d ", kipp->ki_tid); @@ -139,5 +114,5 @@ procstat_threads(struct kinfo_proc *kipp) } printf("\n"); } - free(kip); + procstat_freeprocs(procstat, kip); } commit 657fa179a1bfeb3291a2252f2f029c7bfd580793 Author: Mikolaj Golub Date: Tue Mar 12 17:16:36 2013 +0200 Use more generic procstat_getvmmap(3) for retrieving VM layout of a process. MFC after: 1 month diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c index 6b95246..4e35412 100644 --- a/usr.bin/procstat/procstat.c +++ b/usr.bin/procstat/procstat.c @@ -79,7 +79,7 @@ procstat(struct procstat *prstat, struct kinfo_proc *kipp) else if (tflag) procstat_threads(prstat, kipp); else if (vflag) - procstat_vm(kipp); + procstat_vm(prstat, kipp); else if (xflag) procstat_auxv(kipp); else diff --git a/usr.bin/procstat/procstat.h b/usr.bin/procstat/procstat.h index 718eb47..391f187 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 procstat *prstat, 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 *prstat, 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++) { commit e6fe7d9aba7c46793795a99cc2d8269d820e8e08 Author: Mikolaj Golub Date: Sat Mar 23 15:35:43 2013 +0200 Add procstat_getgroups function to retrieve process groups. MFC after: 1 month diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map index aa74659..5ac5ee1 100644 --- a/lib/libprocstat/Symbol.map +++ b/lib/libprocstat/Symbol.map @@ -16,8 +16,10 @@ FBSD_1.2 { }; FBSD_1.3 { + procstat_freegroups; procstat_freevmmap; procstat_get_shm_info; + procstat_getgroups; procstat_getvmmap; procstat_open_core; }; diff --git a/lib/libprocstat/core.c b/lib/libprocstat/core.c index 1ada459..afaa76c 100644 --- a/lib/libprocstat/core.c +++ b/lib/libprocstat/core.c @@ -167,6 +167,10 @@ procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, n_type = NT_PROCSTAT_VMMAP; structsize = sizeof(struct kinfo_vmentry); break; + case PSC_TYPE_GROUPS: + n_type = NT_PROCSTAT_GROUPS; + structsize = sizeof(gid_t); + break; default: warnx("unknown core stat type: %d", type); return (NULL); diff --git a/lib/libprocstat/core.h b/lib/libprocstat/core.h index 8e2dd13..54efa29 100644 --- a/lib/libprocstat/core.h +++ b/lib/libprocstat/core.h @@ -33,6 +33,7 @@ enum psc_type { PSC_TYPE_PROC, PSC_TYPE_FILES, PSC_TYPE_VMMAP, + PSC_TYPE_GROUPS, }; struct procstat_core; diff --git a/lib/libprocstat/libprocstat.3 b/lib/libprocstat/libprocstat.3 index 18dc1e9..b38ce1c 100644 --- a/lib/libprocstat/libprocstat.3 +++ b/lib/libprocstat/libprocstat.3 @@ -33,9 +33,11 @@ .Nm procstat_open_sysctl , .Nm procstat_close , .Nm procstat_getfiles , +.Nm procstat_getgroups , .Nm procstat_getprocs , .Nm procstat_getvmmap , .Nm procstat_freefiles , +.Nm procstat_freegroups , .Nm procstat_freeprocs , .Nm procstat_freevmmap , .Nm procstat_get_pipe_info , @@ -52,12 +54,18 @@ .In libprocstat.h .Ft void .Fn procstat_close "struct procstat *procstat" +.Fc .Ft void .Fo procstat_freefiles .Fa "struct procstat *procstat" .Fa "struct filestat_list *head" .Fc .Ft void +.Fo procstat_freegroups +.Fa "struct procstat *procstat" +.Fa "gid_t *groups" +.Fc +.Ft void .Fn procstat_freeprocs "struct procstat *procstat" "struct kinfo_proc *p" .Ft void .Fo procstat_freevmmap @@ -105,6 +113,12 @@ .Fa "struct kinfo_proc *kp" .Fa "int mmapped" .Fc +.Ft "gid_t *" +.Fo procstat_getgroups +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "unsigned int *count" +.Fc .Ft "struct kinfo_proc *" .Fo procstat_getprocs .Fa "struct procstat *procstat" @@ -227,6 +241,19 @@ The caller is responsible to free the allocated memory with a subsequent function call. .Pp The +.Fn procstat_getgroups +function gets a pointer to the +.Vt procstat +structure, a pointer to +.Vt kinfo_proc +structure, and returns the process groups as a dynamically allocated array of +.Vt gid_t +elements. +The caller is responsible to free the allocated memory with a subsequent +.Fn procstat_freegroups +function call. +.Pp +The .Fn procstat_getvmmap function gets a pointer to the .Vt procstat diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index 283f802..b300b1f 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -132,6 +132,9 @@ static int procstat_get_vnode_info_kvm(kvm_t *kd, struct filestat *fst, struct vnstat *vn, char *errbuf); static int procstat_get_vnode_info_sysctl(struct filestat *fst, struct vnstat *vn, char *errbuf); +static gid_t *procstat_getgroups_core(struct procstat_core *core, + unsigned int *count); +static gid_t *procstat_getgroups_sysctl(pid_t pid, unsigned int *count); static int vntype2psfsttype(int type); void @@ -1588,3 +1591,67 @@ procstat_freevmmap(struct procstat *procstat __unused, free(vmmap); } + +static gid_t * +procstat_getgroups_sysctl(pid_t pid, unsigned int *cntp) +{ + int mib[4]; + size_t len; + gid_t *groups; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_GROUPS; + mib[3] = pid; + len = (sysconf(_SC_NGROUPS_MAX) + 1) * sizeof(gid_t); + groups = malloc(len); + if (groups == NULL) { + warn("malloc(%zu)", len); + return (NULL); + } + if (sysctl(mib, 4, groups, &len, NULL, 0) == -1) { + warn("sysctl: kern.proc.groups: %d", pid); + free(groups); + return (NULL); + } + *cntp = len / sizeof(gid_t); + return (groups); +} + +static gid_t * +procstat_getgroups_core(struct procstat_core *core, unsigned int *cntp) +{ + size_t len; + gid_t *groups; + + groups = procstat_core_get(core, PSC_TYPE_GROUPS, NULL, &len); + if (groups == NULL) + return (NULL); + *cntp = len / sizeof(gid_t); + return (groups); +} + +gid_t * +procstat_getgroups(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 (procstat_getgroups_sysctl(kp->ki_pid, cntp)); + case PROCSTAT_CORE: + return (procstat_getgroups_core(procstat->core, cntp)); + default: + warnx("unknown access method: %d", procstat->type); + return (NULL); + } +} + +void +procstat_freegroups(struct procstat *procstat __unused, gid_t *groups) +{ + + free(groups); +} diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h index 71d85a1..0c4e77a 100644 --- a/lib/libprocstat/libprocstat.h +++ b/lib/libprocstat/libprocstat.h @@ -146,6 +146,7 @@ STAILQ_HEAD(filestat_list, filestat); __BEGIN_DECLS void procstat_close(struct procstat *procstat); +void procstat_freegroups(struct procstat *procstat, gid_t *groups); void procstat_freeprocs(struct procstat *procstat, struct kinfo_proc *p); void procstat_freefiles(struct procstat *procstat, struct filestat_list *head); @@ -165,6 +166,8 @@ 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); +gid_t *procstat_getgroups(struct procstat *procstat, struct kinfo_proc *kp, + unsigned int *count); struct kinfo_vmentry *procstat_getvmmap(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *count); struct procstat *procstat_open_core(const char *filename); commit 849846d74cb6e4ffd9fa36c2afdbc6f85af3dcac Author: Mikolaj Golub Date: Sat Mar 23 15:37:50 2013 +0200 Use procstat_getgroups(3) for retrieving groups information instead of direct sysctl. MFC after: 1 month diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c index 4e35412..1e45a3b 100644 --- a/usr.bin/procstat/procstat.c +++ b/usr.bin/procstat/procstat.c @@ -75,7 +75,7 @@ procstat(struct procstat *prstat, struct kinfo_proc *kipp) else if (lflag) procstat_rlimit(kipp); else if (sflag) - procstat_cred(kipp); + procstat_cred(prstat, kipp); else if (tflag) procstat_threads(prstat, kipp); else if (vflag) diff --git a/usr.bin/procstat/procstat.h b/usr.bin/procstat/procstat.h index 391f187..eae3566 100644 --- a/usr.bin/procstat/procstat.h +++ b/usr.bin/procstat/procstat.h @@ -38,7 +38,7 @@ void procstat_args(struct kinfo_proc *kipp); void procstat_auxv(struct kinfo_proc *kipp); void procstat_basic(struct kinfo_proc *kipp); void procstat_bin(struct kinfo_proc *kipp); -void procstat_cred(struct kinfo_proc *kipp); +void procstat_cred(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_env(struct kinfo_proc *kipp); void procstat_files(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_kstack(struct kinfo_proc *kipp, int kflag); diff --git a/usr.bin/procstat/procstat_cred.c b/usr.bin/procstat/procstat_cred.c index 250747d..c294868 100644 --- a/usr.bin/procstat/procstat_cred.c +++ b/usr.bin/procstat/procstat_cred.c @@ -41,13 +41,10 @@ static const char *get_umask(struct kinfo_proc *kipp); void -procstat_cred(struct kinfo_proc *kipp) +procstat_cred(struct procstat *procstat, struct kinfo_proc *kipp) { - int i; - int mib[4]; - int ngroups; - size_t len; - gid_t *groups = NULL; + unsigned int i, ngroups; + gid_t *groups; if (!hflag) printf("%5s %-16s %5s %5s %5s %5s %5s %5s %5s %5s %-15s\n", @@ -66,30 +63,14 @@ procstat_cred(struct kinfo_proc *kipp) printf("%s", kipp->ki_cr_flags & CRED_FLAG_CAPMODE ? "C" : "-"); printf(" "); + groups = NULL; /* * We may have too many groups to fit in kinfo_proc's statically - * sized storage. If that occurs, attempt to retrieve them via - * sysctl. + * sized storage. If that occurs, attempt to retrieve them using + * libprocstat. */ - if (kipp->ki_cr_flags & KI_CRF_GRP_OVERFLOW) { - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_GROUPS; - mib[3] = kipp->ki_pid; - - ngroups = sysconf(_SC_NGROUPS_MAX) + 1; - len = ngroups * sizeof(gid_t); - if((groups = malloc(len)) == NULL) - err(-1, "malloc"); - - if (sysctl(mib, 4, groups, &len, NULL, 0) == -1) { - warn("sysctl: kern.proc.groups: %d " - "group list truncated", kipp->ki_pid); - free(groups); - groups = NULL; - } - ngroups = len / sizeof(gid_t); - } + if (kipp->ki_cr_flags & KI_CRF_GRP_OVERFLOW) + groups = procstat_getgroups(procstat, kipp, &ngroups); if (groups == NULL) { ngroups = kipp->ki_ngroups; groups = kipp->ki_groups; @@ -97,7 +78,7 @@ procstat_cred(struct kinfo_proc *kipp) for (i = 0; i < ngroups; i++) printf("%s%d", (i > 0) ? "," : "", groups[i]); if (groups != kipp->ki_groups) - free(groups); + procstat_freegroups(procstat, groups); printf("\n"); } commit b3c59528d16624a8cff4e3fb41e6a95b9d028ee5 Author: Mikolaj Golub Date: Sat Mar 23 16:23:27 2013 +0200 Add procstat_getumask function to retrieve a process umask. MFC after: 1 month diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map index 5ac5ee1..1970a7a 100644 --- a/lib/libprocstat/Symbol.map +++ b/lib/libprocstat/Symbol.map @@ -20,6 +20,7 @@ FBSD_1.3 { procstat_freevmmap; procstat_get_shm_info; procstat_getgroups; + procstat_getumask; procstat_getvmmap; procstat_open_core; }; diff --git a/lib/libprocstat/core.c b/lib/libprocstat/core.c index afaa76c..5732f40 100644 --- a/lib/libprocstat/core.c +++ b/lib/libprocstat/core.c @@ -171,6 +171,10 @@ procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, n_type = NT_PROCSTAT_GROUPS; structsize = sizeof(gid_t); break; + case PSC_TYPE_UMASK: + n_type = NT_PROCSTAT_UMASK; + structsize = sizeof(u_short); + break; default: warnx("unknown core stat type: %d", type); return (NULL); diff --git a/lib/libprocstat/core.h b/lib/libprocstat/core.h index 54efa29..6e7ed02 100644 --- a/lib/libprocstat/core.h +++ b/lib/libprocstat/core.h @@ -34,6 +34,7 @@ enum psc_type { PSC_TYPE_FILES, PSC_TYPE_VMMAP, PSC_TYPE_GROUPS, + PSC_TYPE_UMASK, }; struct procstat_core; diff --git a/lib/libprocstat/libprocstat.3 b/lib/libprocstat/libprocstat.3 index b38ce1c..06a66a0 100644 --- a/lib/libprocstat/libprocstat.3 +++ b/lib/libprocstat/libprocstat.3 @@ -35,6 +35,7 @@ .Nm procstat_getfiles , .Nm procstat_getgroups , .Nm procstat_getprocs , +.Nm procstat_getumask , .Nm procstat_getvmmap , .Nm procstat_freefiles , .Nm procstat_freegroups , @@ -126,6 +127,12 @@ .Fa "int arg" .Fa "unsigned int *count" .Fc +.Ft "int" +.Fo procstat_getumask +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "unsigned short *maskp" +.Fc .Ft "struct kinfo_vmentry *" .Fo procstat_getvmmap .Fa "struct procstat *procstat" @@ -254,6 +261,14 @@ The caller is responsible to free the allocated memory with a subsequent function call. .Pp The +.Fn procstat_getumask +function gets a pointer to the +.Vt procstat +structure, a pointer to +.Vt kinfo_proc +structure, and returns the process umask in the 3rd reference parameter. +.Pp +The .Fn procstat_getvmmap function gets a pointer to the .Vt procstat diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index b300b1f..1ee0f97 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -135,6 +135,9 @@ static int procstat_get_vnode_info_sysctl(struct filestat *fst, static gid_t *procstat_getgroups_core(struct procstat_core *core, unsigned int *count); static gid_t *procstat_getgroups_sysctl(pid_t pid, unsigned int *count); +static int procstat_getumask_core(struct procstat_core *core, + unsigned short *maskp); +static int procstat_getumask_sysctl(pid_t pid, unsigned short *maskp); static int vntype2psfsttype(int type); void @@ -1655,3 +1658,57 @@ procstat_freegroups(struct procstat *procstat __unused, gid_t *groups) free(groups); } + +static int +procstat_getumask_sysctl(pid_t pid, unsigned short *maskp) +{ + int error; + int mib[4]; + size_t len; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_UMASK; + mib[3] = pid; + len = sizeof(*maskp); + error = sysctl(mib, 4, maskp, &len, NULL, 0); + if (error != 0 && errno != ESRCH) + warn("sysctl: kern.proc.umask: %d", pid); + return (error); +} + +static int +procstat_getumask_core(struct procstat_core *core, unsigned short *maskp) +{ + size_t len; + unsigned short *buf; + + buf = procstat_core_get(core, PSC_TYPE_UMASK, NULL, &len); + if (buf == NULL) + return (-1); + if (len < sizeof(*maskp)) { + free(buf); + return (-1); + } + *maskp = *buf; + free(buf); + return (0); +} + +int +procstat_getumask(struct procstat *procstat, struct kinfo_proc *kp, + unsigned short *maskp) +{ + switch(procstat->type) { + case PROCSTAT_KVM: + warnx("kvm method is not supported"); + return (-1); + case PROCSTAT_SYSCTL: + return (procstat_getumask_sysctl(kp->ki_pid, maskp)); + case PROCSTAT_CORE: + return (procstat_getumask_core(procstat->core, maskp)); + default: + warnx("unknown access method: %d", procstat->type); + return (-1); + } +} diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h index 0c4e77a..8d14d0f 100644 --- a/lib/libprocstat/libprocstat.h +++ b/lib/libprocstat/libprocstat.h @@ -168,6 +168,8 @@ int procstat_get_vnode_info(struct procstat *procstat, struct filestat *fst, struct vnstat *vn, char *errbuf); gid_t *procstat_getgroups(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *count); +int procstat_getumask(struct procstat *procstat, struct kinfo_proc *kp, + unsigned short* umask); struct kinfo_vmentry *procstat_getvmmap(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *count); struct procstat *procstat_open_core(const char *filename); commit 2f87409b17625026fbc59f5156f5acb5136904ba Author: Mikolaj Golub Date: Sat Mar 23 16:24:27 2013 +0200 Use procstat_getumask(3) for retrieving umaks information instead of direct sysctl. MFC after: 1 month diff --git a/usr.bin/procstat/procstat_cred.c b/usr.bin/procstat/procstat_cred.c index c294868..f9a2241 100644 --- a/usr.bin/procstat/procstat_cred.c +++ b/usr.bin/procstat/procstat_cred.c @@ -38,7 +38,8 @@ #include "procstat.h" -static const char *get_umask(struct kinfo_proc *kipp); +static const char *get_umask(struct procstat *procstat, + struct kinfo_proc *kipp); void procstat_cred(struct procstat *procstat, struct kinfo_proc *kipp) @@ -59,7 +60,7 @@ procstat_cred(struct procstat *procstat, struct kinfo_proc *kipp) printf("%5d ", kipp->ki_groups[0]); printf("%5d ", kipp->ki_rgid); printf("%5d ", kipp->ki_svgid); - printf("%5s ", get_umask(kipp)); + printf("%5s ", get_umask(procstat, kipp)); printf("%s", kipp->ki_cr_flags & CRED_FLAG_CAPMODE ? "C" : "-"); printf(" "); @@ -84,21 +85,12 @@ procstat_cred(struct procstat *procstat, struct kinfo_proc *kipp) } static const char * -get_umask(struct kinfo_proc *kipp) +get_umask(struct procstat *procstat, struct kinfo_proc *kipp) { - int error; - int mib[4]; - size_t len; u_short fd_cmask; static char umask[4]; - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_UMASK; - mib[3] = kipp->ki_pid; - len = sizeof(fd_cmask); - error = sysctl(mib, 4, &fd_cmask, &len, NULL, 0); - if (error == 0) { + if (procstat_getumask(procstat, kipp, &fd_cmask) == 0) { snprintf(umask, 4, "%03o", fd_cmask); return (umask); } else { commit d911d8b45d2cad13b90a6ebb03f504f5db4732a5 Author: Mikolaj Golub Date: Sat Mar 23 13:25:27 2013 +0200 Add procstat_getrlimit function to retrieve a process resource limits info. MFC after: 1 month diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map index 1970a7a..461d320 100644 --- a/lib/libprocstat/Symbol.map +++ b/lib/libprocstat/Symbol.map @@ -20,6 +20,7 @@ FBSD_1.3 { procstat_freevmmap; procstat_get_shm_info; procstat_getgroups; + procstat_getrlimit; procstat_getumask; procstat_getvmmap; procstat_open_core; diff --git a/lib/libprocstat/core.c b/lib/libprocstat/core.c index 5732f40..2ff3975 100644 --- a/lib/libprocstat/core.c +++ b/lib/libprocstat/core.c @@ -175,6 +175,10 @@ procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, n_type = NT_PROCSTAT_UMASK; structsize = sizeof(u_short); break; + case PSC_TYPE_RLIMIT: + n_type = NT_PROCSTAT_RLIMIT; + structsize = sizeof(struct rlimit) * RLIM_NLIMITS; + break; default: warnx("unknown core stat type: %d", type); return (NULL); diff --git a/lib/libprocstat/core.h b/lib/libprocstat/core.h index 6e7ed02..967c193 100644 --- a/lib/libprocstat/core.h +++ b/lib/libprocstat/core.h @@ -35,6 +35,7 @@ enum psc_type { PSC_TYPE_VMMAP, PSC_TYPE_GROUPS, PSC_TYPE_UMASK, + PSC_TYPE_RLIMIT, }; struct procstat_core; diff --git a/lib/libprocstat/libprocstat.3 b/lib/libprocstat/libprocstat.3 index 06a66a0..bc78d68 100644 --- a/lib/libprocstat/libprocstat.3 +++ b/lib/libprocstat/libprocstat.3 @@ -128,6 +128,13 @@ .Fa "unsigned int *count" .Fc .Ft "int" +.Fo procstat_getrlimit +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "int which" +.Fa "struct rlimit* rlimit" +.Fc +.Ft "int" .Fo procstat_getumask .Fa "struct procstat *procstat" .Fa "struct kinfo_proc *kp" @@ -261,6 +268,16 @@ The caller is responsible to free the allocated memory with a subsequent function call. .Pp The +.Fn procstat_getrlimit +function gets a pointer to the +.Vt procstat +structure, a pointer to +.Vt kinfo_proc +structure, resource index +.Fa which , +and returns the actual resource limit in the 4th reference parameter. +.Pp +The .Fn procstat_getumask function gets a pointer to the .Vt procstat diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index 1ee0f97..04c1058 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -135,6 +136,10 @@ static int procstat_get_vnode_info_sysctl(struct filestat *fst, static gid_t *procstat_getgroups_core(struct procstat_core *core, unsigned int *count); static gid_t *procstat_getgroups_sysctl(pid_t pid, unsigned int *count); +static int procstat_getrlimit_core(struct procstat_core *core, int which, + struct rlimit* rlimit); +static int procstat_getrlimit_sysctl(pid_t pid, int which, + struct rlimit* rlimit); static int procstat_getumask_core(struct procstat_core *core, unsigned short *maskp); static int procstat_getumask_sysctl(pid_t pid, unsigned short *maskp); @@ -1712,3 +1717,66 @@ procstat_getumask(struct procstat *procstat, struct kinfo_proc *kp, return (-1); } } + +static int +procstat_getrlimit_sysctl(pid_t pid, int which, struct rlimit* rlimit) +{ + int error, name[5]; + size_t len; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_RLIMIT; + name[3] = pid; + name[4] = which; + len = sizeof(struct rlimit); + error = sysctl(name, 5, rlimit, &len, NULL, 0); + if (error < 0 && errno != ESRCH) { + warn("sysctl: kern.proc.rlimit: %d", pid); + return (-1); + } + if (error < 0 || len != sizeof(struct rlimit)) + return (-1); + return (0); +} + +static int +procstat_getrlimit_core(struct procstat_core *core, int which, + struct rlimit* rlimit) +{ + size_t len; + struct rlimit* rlimits; + + if (which < 0 || which >= RLIM_NLIMITS) { + errno = EINVAL; + warn("getrlimit: which"); + return (-1); + } + rlimits = procstat_core_get(core, PSC_TYPE_RLIMIT, NULL, &len); + if (rlimits == NULL) + return (-1); + if (len < sizeof(struct rlimit) * RLIM_NLIMITS) { + free(rlimits); + return (-1); + } + *rlimit = rlimits[which]; + return (0); +} + +int +procstat_getrlimit(struct procstat *procstat, struct kinfo_proc *kp, int which, + struct rlimit* rlimit) +{ + switch(procstat->type) { + case PROCSTAT_KVM: + warnx("kvm method is not supported"); + return (-1); + case PROCSTAT_SYSCTL: + return (procstat_getrlimit_sysctl(kp->ki_pid, which, rlimit)); + case PROCSTAT_CORE: + return (procstat_getrlimit_core(procstat->core, which, rlimit)); + default: + warnx("unknown access method: %d", procstat->type); + return (-1); + } +} diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h index 8d14d0f..05046af 100644 --- a/lib/libprocstat/libprocstat.h +++ b/lib/libprocstat/libprocstat.h @@ -91,6 +91,7 @@ struct kinfo_vmentry; struct procstat; +struct rlimit; struct filestat { int fs_type; /* Descriptor type. */ int fs_flags; /* filestat specific flags. */ @@ -170,6 +171,8 @@ gid_t *procstat_getgroups(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *count); int procstat_getumask(struct procstat *procstat, struct kinfo_proc *kp, unsigned short* umask); +int procstat_getrlimit(struct procstat *procstat, struct kinfo_proc *kp, + int which, struct rlimit* rlimit); struct kinfo_vmentry *procstat_getvmmap(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *count); struct procstat *procstat_open_core(const char *filename); commit d55fbd3bacfbab48523249aae9c65b6d4d77f49a Author: Mikolaj Golub Date: Sat Mar 23 13:27:31 2013 +0200 Use procstat_getrlimit(3) for retrieving rlimit information instead of direct sysctl calls. MFC after: 1 month diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c index 1e45a3b..86451b3 100644 --- a/usr.bin/procstat/procstat.c +++ b/usr.bin/procstat/procstat.c @@ -73,7 +73,7 @@ procstat(struct procstat *prstat, struct kinfo_proc *kipp) else if (kflag) procstat_kstack(kipp, kflag); else if (lflag) - procstat_rlimit(kipp); + procstat_rlimit(prstat, kipp); else if (sflag) procstat_cred(prstat, kipp); else if (tflag) diff --git a/usr.bin/procstat/procstat.h b/usr.bin/procstat/procstat.h index eae3566..d825e78 100644 --- a/usr.bin/procstat/procstat.h +++ b/usr.bin/procstat/procstat.h @@ -42,7 +42,7 @@ void procstat_cred(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_env(struct kinfo_proc *kipp); void procstat_files(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_kstack(struct kinfo_proc *kipp, int kflag); -void procstat_rlimit(struct kinfo_proc *kipp); +void procstat_rlimit(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_sigs(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_threads(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_threads_sigs(struct procstat *prstat, struct kinfo_proc *kipp); diff --git a/usr.bin/procstat/procstat_rlimit.c b/usr.bin/procstat/procstat_rlimit.c index 68230e5..f3ed5fc 100644 --- a/usr.bin/procstat/procstat_rlimit.c +++ b/usr.bin/procstat/procstat_rlimit.c @@ -86,31 +86,18 @@ humanize_rlimit(int indx, rlim_t limit) } void -procstat_rlimit(struct kinfo_proc *kipp) +procstat_rlimit(struct procstat *prstat, struct kinfo_proc *kipp) { struct rlimit rlimit; - int error, i, name[5]; - size_t len; + int i; if (!hflag) { printf("%5s %-16s %-16s %16s %16s\n", "PID", "COMM", "RLIMIT", "SOFT ", "HARD "); } - len = sizeof(struct rlimit); - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_RLIMIT; - name[3] = kipp->ki_pid; for (i = 0; i < RLIM_NLIMITS; i++) { - name[4] = i; - error = sysctl(name, 5, &rlimit, &len, NULL, 0); - if (error < 0 && errno != ESRCH) { - warn("sysctl: kern.proc.rlimit: %d", kipp->ki_pid); + if (procstat_getrlimit(prstat, kipp, i, &rlimit) == -1) return; - } - if (error < 0 || len != sizeof(struct rlimit)) - return; - printf("%5d %-16s %-16s ", kipp->ki_pid, kipp->ki_comm, rlimit_param[i].name); printf("%16s ", humanize_rlimit(i, rlimit.rlim_cur)); commit f597ef1bde59bd47af8f08bbf7ee7b1bdcb6e227 Author: Mikolaj Golub Date: Sat Mar 23 19:54:40 2013 +0200 Add procstat_getpathname function to retrieve a process executable. MFC after: 1 month diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map index 461d320..c4db0f3 100644 --- a/lib/libprocstat/Symbol.map +++ b/lib/libprocstat/Symbol.map @@ -20,6 +20,7 @@ FBSD_1.3 { procstat_freevmmap; procstat_get_shm_info; procstat_getgroups; + procstat_getpathname; procstat_getrlimit; procstat_getumask; procstat_getvmmap; diff --git a/lib/libprocstat/libprocstat.3 b/lib/libprocstat/libprocstat.3 index bc78d68..fd843cb 100644 --- a/lib/libprocstat/libprocstat.3 +++ b/lib/libprocstat/libprocstat.3 @@ -34,6 +34,7 @@ .Nm procstat_close , .Nm procstat_getfiles , .Nm procstat_getgroups , +.Nm procstat_getpathname , .Nm procstat_getprocs , .Nm procstat_getumask , .Nm procstat_getvmmap , @@ -128,6 +129,13 @@ .Fa "unsigned int *count" .Fc .Ft "int" +.Fo procstat_getpathname +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "char *pathname" +.Fa "size_t maxlen" +.Fc +.Ft "int" .Fo procstat_getrlimit .Fa "struct procstat *procstat" .Fa "struct kinfo_proc *kp" @@ -268,6 +276,18 @@ The caller is responsible to free the allocated memory with a subsequent function call. .Pp The +.Fn procstat_getpathname +function gets a pointer to the +.Vt procstat +structure, a pointer to +.Vt kinfo_proc +structure, and copies the path of the process executable to +.Fa pathname +buffer, limiting to +.Fa maxlen +characters. +.Pp +The .Fn procstat_getrlimit function gets a pointer to the .Vt procstat diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index 04c1058..e469f8e 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -136,6 +136,10 @@ static int procstat_get_vnode_info_sysctl(struct filestat *fst, static gid_t *procstat_getgroups_core(struct procstat_core *core, unsigned int *count); static gid_t *procstat_getgroups_sysctl(pid_t pid, unsigned int *count); +static int procstat_getpathname_core(struct procstat_core *core, + char *pathname, size_t maxlen); +static int procstat_getpathname_sysctl(pid_t pid, char *pathname, + size_t maxlen); static int procstat_getrlimit_core(struct procstat_core *core, int which, struct rlimit* rlimit); static int procstat_getrlimit_sysctl(pid_t pid, int which, @@ -1780,3 +1784,64 @@ procstat_getrlimit(struct procstat *procstat, struct kinfo_proc *kp, int which, return (-1); } } + +static int +procstat_getpathname_sysctl(pid_t pid, char *pathname, size_t maxlen) +{ + int error, name[4]; + size_t len; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_PATHNAME; + name[3] = pid; + len = maxlen; + error = sysctl(name, 4, pathname, &len, NULL, 0); + if (error != 0 && errno != ESRCH) + warn("sysctl: kern.proc.pathname: %d", pid); + if (len == 0) + pathname[0] = '\0'; + return (error); +} + +static int +procstat_getpathname_core(struct procstat_core *core, char *pathname, + size_t maxlen) +{ + struct kinfo_file *files; + int cnt, i, result; + + files = kinfo_getfile_core(core, &cnt); + if (files == NULL) + return (-1); + result = -1; + for (i = 0; i < cnt; i++) { + if (files[i].kf_fd != KF_FD_TYPE_TEXT) + continue; + strncpy(pathname, files[i].kf_path, maxlen); + result = 0; + break; + } + free(files); + return (result); +} + +int +procstat_getpathname(struct procstat *procstat, struct kinfo_proc *kp, + char *pathname, size_t maxlen) +{ + switch(procstat->type) { + case PROCSTAT_KVM: + warnx("kvm method is not supported"); + return (-1); + case PROCSTAT_SYSCTL: + return (procstat_getpathname_sysctl(kp->ki_pid, pathname, + maxlen)); + case PROCSTAT_CORE: + return (procstat_getpathname_core(procstat->core, pathname, + maxlen)); + default: + warnx("unknown access method: %d", procstat->type); + return (-1); + } +} diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h index 05046af..24826ec 100644 --- a/lib/libprocstat/libprocstat.h +++ b/lib/libprocstat/libprocstat.h @@ -169,10 +169,12 @@ int procstat_get_vnode_info(struct procstat *procstat, struct filestat *fst, struct vnstat *vn, char *errbuf); gid_t *procstat_getgroups(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *count); -int procstat_getumask(struct procstat *procstat, struct kinfo_proc *kp, - unsigned short* umask); +int procstat_getpathname(struct procstat *procstat, struct kinfo_proc *kp, + char *pathname, size_t maxlen); int procstat_getrlimit(struct procstat *procstat, struct kinfo_proc *kp, int which, struct rlimit* rlimit); +int procstat_getumask(struct procstat *procstat, struct kinfo_proc *kp, + unsigned short* umask); struct kinfo_vmentry *procstat_getvmmap(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *count); struct procstat *procstat_open_core(const char *filename); commit 764d119c0af49c0b1d52aa5fa4a2b50f9544e46d Author: Mikolaj Golub Date: Sat Mar 23 21:07:01 2013 +0200 Add procstat_getosrel function to retrieve a process osrel info. MFC after: 1 month diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map index c4db0f3..569c6f4 100644 --- a/lib/libprocstat/Symbol.map +++ b/lib/libprocstat/Symbol.map @@ -20,6 +20,7 @@ FBSD_1.3 { procstat_freevmmap; procstat_get_shm_info; procstat_getgroups; + procstat_getosrel; procstat_getpathname; procstat_getrlimit; procstat_getumask; diff --git a/lib/libprocstat/core.c b/lib/libprocstat/core.c index 2ff3975..e750222 100644 --- a/lib/libprocstat/core.c +++ b/lib/libprocstat/core.c @@ -179,6 +179,10 @@ procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, n_type = NT_PROCSTAT_RLIMIT; structsize = sizeof(struct rlimit) * RLIM_NLIMITS; break; + case PSC_TYPE_OSREL: + n_type = NT_PROCSTAT_OSREL; + structsize = sizeof(int); + break; default: warnx("unknown core stat type: %d", type); return (NULL); diff --git a/lib/libprocstat/core.h b/lib/libprocstat/core.h index 967c193..4d572b1 100644 --- a/lib/libprocstat/core.h +++ b/lib/libprocstat/core.h @@ -36,6 +36,7 @@ enum psc_type { PSC_TYPE_GROUPS, PSC_TYPE_UMASK, PSC_TYPE_RLIMIT, + PSC_TYPE_OSREL, }; struct procstat_core; diff --git a/lib/libprocstat/libprocstat.3 b/lib/libprocstat/libprocstat.3 index fd843cb..9fecf87 100644 --- a/lib/libprocstat/libprocstat.3 +++ b/lib/libprocstat/libprocstat.3 @@ -34,6 +34,7 @@ .Nm procstat_close , .Nm procstat_getfiles , .Nm procstat_getgroups , +.Nm procstat_getosrel , .Nm procstat_getpathname , .Nm procstat_getprocs , .Nm procstat_getumask , @@ -120,6 +121,11 @@ .Fa "struct procstat *procstat" .Fa "struct kinfo_proc *kp" .Fa "unsigned int *count" +.Ft int +.Fo procstat_getosrel +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "int *osrelp" .Fc .Ft "struct kinfo_proc *" .Fo procstat_getprocs @@ -276,6 +282,14 @@ The caller is responsible to free the allocated memory with a subsequent function call. .Pp The +.Fn procstat_getosrel +function gets a pointer to the +.Vt procstat +structure, a pointer to +.Vt kinfo_proc +structure, and returns osrel date in the 3rd reference parameter. +.Pp +The .Fn procstat_getpathname function gets a pointer to the .Vt procstat diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index e469f8e..6a71bf4 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -1845,3 +1845,55 @@ procstat_getpathname(struct procstat *procstat, struct kinfo_proc *kp, return (-1); } } + +static int +procstat_getosrel_sysctl(pid_t pid, int *osrelp) +{ + int error, name[4]; + size_t len; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_OSREL; + name[3] = pid; + len = sizeof(*osrelp); + error = sysctl(name, 4, osrelp, &len, NULL, 0); + if (error != 0 && errno != ESRCH) + warn("sysctl: kern.proc.osrel: %d", pid); + return (error); +} + +static int +procstat_getosrel_core(struct procstat_core *core, int *osrelp) +{ + size_t len; + int *buf; + + buf = procstat_core_get(core, PSC_TYPE_OSREL, NULL, &len); + if (buf == NULL) + return (-1); + if (len < sizeof(*osrelp)) { + free(buf); + return (-1); + } + *osrelp = *buf; + free(buf); + return (0); +} + +int +procstat_getosrel(struct procstat *procstat, struct kinfo_proc *kp, int *osrelp) +{ + switch(procstat->type) { + case PROCSTAT_KVM: + warnx("kvm method is not supported"); + return (-1); + case PROCSTAT_SYSCTL: + return (procstat_getosrel_sysctl(kp->ki_pid, osrelp)); + case PROCSTAT_CORE: + return (procstat_getosrel_core(procstat->core, osrelp)); + default: + warnx("unknown access method: %d", procstat->type); + return (-1); + } +} diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h index 24826ec..353948b 100644 --- a/lib/libprocstat/libprocstat.h +++ b/lib/libprocstat/libprocstat.h @@ -169,6 +169,8 @@ int procstat_get_vnode_info(struct procstat *procstat, struct filestat *fst, struct vnstat *vn, char *errbuf); gid_t *procstat_getgroups(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *count); +int procstat_getosrel(struct procstat *procstat, struct kinfo_proc *kp, + int *osrelp); int procstat_getpathname(struct procstat *procstat, struct kinfo_proc *kp, char *pathname, size_t maxlen); int procstat_getrlimit(struct procstat *procstat, struct kinfo_proc *kp, commit d6f79f4e2b0eb585f8d29dddbb981f530f890bda Author: Mikolaj Golub Date: Sat Mar 23 21:08:06 2013 +0200 Use libprocstat(3) when retrieving binary information for a process. MFC after: 1 month diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c index 86451b3..16f2c99 100644 --- a/usr.bin/procstat/procstat.c +++ b/usr.bin/procstat/procstat.c @@ -59,7 +59,7 @@ procstat(struct procstat *prstat, struct kinfo_proc *kipp) { if (bflag) - procstat_bin(kipp); + procstat_bin(prstat, kipp); else if (cflag) procstat_args(kipp); else if (eflag) diff --git a/usr.bin/procstat/procstat.h b/usr.bin/procstat/procstat.h index d825e78..e651665 100644 --- a/usr.bin/procstat/procstat.h +++ b/usr.bin/procstat/procstat.h @@ -37,7 +37,7 @@ void kinfo_proc_sort(struct kinfo_proc *kipp, int count); void procstat_args(struct kinfo_proc *kipp); void procstat_auxv(struct kinfo_proc *kipp); void procstat_basic(struct kinfo_proc *kipp); -void procstat_bin(struct kinfo_proc *kipp); +void procstat_bin(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_cred(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_env(struct kinfo_proc *kipp); void procstat_files(struct procstat *prstat, struct kinfo_proc *kipp); diff --git a/usr.bin/procstat/procstat_bin.c b/usr.bin/procstat/procstat_bin.c index 718103d..dc88c38 100644 --- a/usr.bin/procstat/procstat_bin.c +++ b/usr.bin/procstat/procstat_bin.c @@ -40,40 +40,19 @@ #include "procstat.h" void -procstat_bin(struct kinfo_proc *kipp) +procstat_bin(struct procstat *prstat, struct kinfo_proc *kipp) { - char pathname[PATH_MAX]; - int error, osrel, name[4]; - size_t len; + int osrel; + static char pathname[PATH_MAX]; if (!hflag) printf("%5s %-16s %8s %s\n", "PID", "COMM", "OSREL", "PATH"); - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_PATHNAME; - name[3] = kipp->ki_pid; - - len = sizeof(pathname); - error = sysctl(name, 4, pathname, &len, NULL, 0); - if (error < 0 && errno != ESRCH) { - warn("sysctl: kern.proc.pathname: %d", kipp->ki_pid); - return; - } - if (error < 0) + if (procstat_getpathname(prstat, kipp, pathname, sizeof(pathname)) != 0) return; - if (len == 0 || strlen(pathname) == 0) + if (strlen(pathname) == 0) strcpy(pathname, "-"); - - name[2] = KERN_PROC_OSREL; - - len = sizeof(osrel); - error = sysctl(name, 4, &osrel, &len, NULL, 0); - if (error < 0 && errno != ESRCH) { - warn("sysctl: kern.proc.osrel: %d", kipp->ki_pid); - return; - } - if (error < 0) + if (procstat_getosrel(prstat, kipp, &osrel) != 0) return; printf("%5d ", kipp->ki_pid); commit 0eb443ac0ee0e1c1dc60cb62a5cf54d43636dacb Author: Mikolaj Golub Date: Sun Mar 31 13:06:18 2013 +0300 Extend libprocstat with functions to retrieve process command line arguments and environment variables. Suggested by: stas Reviewed by: jhb, stas (initial version) MFC after: 1 month diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map index 569c6f4..f2632c2 100644 --- a/lib/libprocstat/Symbol.map +++ b/lib/libprocstat/Symbol.map @@ -16,9 +16,13 @@ FBSD_1.2 { }; FBSD_1.3 { + procstat_freeargv; + procstat_freeenvv; procstat_freegroups; procstat_freevmmap; procstat_get_shm_info; + procstat_getargv; + procstat_getenvv; procstat_getgroups; procstat_getosrel; procstat_getpathname; diff --git a/lib/libprocstat/core.c b/lib/libprocstat/core.c index e750222..72dee88 100644 --- a/lib/libprocstat/core.c +++ b/lib/libprocstat/core.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -56,6 +57,10 @@ struct procstat_core static bool core_offset(struct procstat_core *core, off_t offset); static bool core_read(struct procstat_core *core, void *buf, size_t len); +static ssize_t core_read_mem(struct procstat_core *core, void *buf, + size_t len, vm_offset_t addr, bool readall); +static void *get_args(struct procstat_core *core, vm_offset_t psstrings, + enum psc_type type, void *buf, size_t *lenp); struct procstat_core * procstat_core_open(const char *filename) @@ -146,6 +151,7 @@ procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, { Elf_Note nhdr; off_t offset, eoffset; + vm_offset_t psstrings; void *freebuf; size_t len; u_int32_t n_type; @@ -183,6 +189,12 @@ procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, n_type = NT_PROCSTAT_OSREL; structsize = sizeof(int); break; + case PSC_TYPE_PSSTRINGS: + case PSC_TYPE_ARGV: + case PSC_TYPE_ENVV: + n_type = NT_PROCSTAT_PSSTRINGS; + structsize = sizeof(vm_offset_t); + break; default: warnx("unknown core stat type: %d", type); return (NULL); @@ -238,6 +250,19 @@ procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, free(freebuf); return (NULL); } + if (type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV) { + if (len < sizeof(psstrings)) { + free(freebuf); + return (NULL); + } + psstrings = *(vm_offset_t *)buf; + if (freebuf == NULL) + len = *lenp; + else + buf = NULL; + free(freebuf); + buf = get_args(core, psstrings, type, buf, &len); + } *lenp = len; return (buf); } @@ -276,3 +301,128 @@ core_read(struct procstat_core *core, void *buf, size_t len) } return (true); } + +static ssize_t +core_read_mem(struct procstat_core *core, void *buf, size_t len, + vm_offset_t addr, bool readall) +{ + GElf_Phdr phdr; + off_t offset; + int i; + + assert(core->pc_magic == PROCSTAT_CORE_MAGIC); + + for (i = 0; i < core->pc_ehdr.e_phnum; i++) { + if (gelf_getphdr(core->pc_elf, i, &phdr) != &phdr) { + warnx("gelf_getphdr: %s", elf_errmsg(-1)); + return (-1); + } + if (phdr.p_type != PT_LOAD) + continue; + if (addr < phdr.p_vaddr || addr > phdr.p_vaddr + phdr.p_memsz) + continue; + offset = phdr.p_offset + (addr - phdr.p_vaddr); + if ((phdr.p_vaddr + phdr.p_memsz) - addr < len) { + if (readall) { + warnx("format error: " + "attempt to read out of segment"); + return (-1); + } + len = (phdr.p_vaddr + phdr.p_memsz) - addr; + } + if (!core_offset(core, offset)) + return (-1); + if (!core_read(core, buf, len)) + return (-1); + return (len); + } + warnx("format error: address %ju not found", (uintmax_t)addr); + return (-1); +} + +#define ARGS_CHUNK_SZ 256 /* Chunk size (bytes) for get_args operations. */ + +static void * +get_args(struct procstat_core *core, vm_offset_t psstrings, enum psc_type type, + void *args, size_t *lenp) +{ + struct ps_strings pss; + void *freeargs; + vm_offset_t addr; + char **argv, *p; + size_t chunksz, done, len, nchr, size; + ssize_t n; + u_int i, nstr; + + assert(type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV); + + if (core_read_mem(core, &pss, sizeof(pss), psstrings, true) == -1) + return (NULL); + if (type == PSC_TYPE_ARGV) { + addr = (vm_offset_t)pss.ps_argvstr; + nstr = pss.ps_nargvstr; + } else /* type == PSC_TYPE_ENVV */ { + addr = (vm_offset_t)pss.ps_envstr; + nstr = pss.ps_nenvstr; + } + if (addr == 0 || nstr == 0) + return (NULL); + if (nstr > ARG_MAX) { + warnx("format error"); + return (NULL); + } + size = nstr * sizeof(char *); + argv = malloc(size); + if (argv == NULL) { + warn("malloc(%zu)", size); + return (NULL); + } + done = 0; + freeargs = NULL; + if (core_read_mem(core, argv, size, addr, true) == -1) + goto fail; + if (args != NULL) { + nchr = MIN(ARG_MAX, *lenp); + } else { + nchr = ARG_MAX; + freeargs = args = malloc(nchr); + if (args == NULL) { + warn("malloc(%zu)", nchr); + goto fail; + } + } + p = args; + for (i = 0; ; i++) { + if (i == nstr) + goto done; + /* + * The program may have scribbled into its argv array, e.g. to + * remove some arguments. If that has happened, break out + * before trying to read from NULL. + */ + if (argv[i] == NULL) + goto done; + for (addr = (vm_offset_t)argv[i]; ; addr += chunksz) { + chunksz = MIN(ARGS_CHUNK_SZ, nchr - 1 - done); + if (chunksz <= 0) + goto done; + n = core_read_mem(core, p, chunksz, addr, false); + if (n == -1) + goto fail; + len = strnlen(p, chunksz); + p += len; + done += len; + if (len != chunksz) + break; + } + *p++ = '\0'; + done++; + } +fail: + free(freeargs); + args = NULL; +done: + *lenp = done; + free(argv); + return (args); +} diff --git a/lib/libprocstat/core.h b/lib/libprocstat/core.h index 4d572b1..2361148 100644 --- a/lib/libprocstat/core.h +++ b/lib/libprocstat/core.h @@ -37,6 +37,9 @@ enum psc_type { PSC_TYPE_UMASK, PSC_TYPE_RLIMIT, PSC_TYPE_OSREL, + PSC_TYPE_PSSTRINGS, + PSC_TYPE_ARGV, + PSC_TYPE_ENVV, }; struct procstat_core; diff --git a/lib/libprocstat/libprocstat.3 b/lib/libprocstat/libprocstat.3 index 9fecf87..e1dc987 100644 --- a/lib/libprocstat/libprocstat.3 +++ b/lib/libprocstat/libprocstat.3 @@ -32,6 +32,8 @@ .Nm procstat_open_kvm , .Nm procstat_open_sysctl , .Nm procstat_close , +.Nm procstat_getargv , +.Nm procstat_getenvv , .Nm procstat_getfiles , .Nm procstat_getgroups , .Nm procstat_getosrel , @@ -39,6 +41,8 @@ .Nm procstat_getprocs , .Nm procstat_getumask , .Nm procstat_getvmmap , +.Nm procstat_freeargv , +.Nm procstat_freeenvv , .Nm procstat_freefiles , .Nm procstat_freegroups , .Nm procstat_freeprocs , @@ -59,6 +63,14 @@ .Fn procstat_close "struct procstat *procstat" .Fc .Ft void +.Fo procstat_freeargv +.Fa "struct procstat *procstat" +.Fc +.Ft void +.Fo procstat_freeenvv +.Fa "struct procstat *procstat" +.Fc +.Ft void .Fo procstat_freefiles .Fa "struct procstat *procstat" .Fa "struct filestat_list *head" @@ -110,6 +122,20 @@ .Fa "struct vnstat *vn" .Fa "char *errbuf" .Fc +.Ft "char **" +.Fo procstat_getargv +.Fa "struct procstat *procstat" +.Fa "const struct kinfo_proc *kp" +.Fa "size_t nchr" +.Fa "char *errbuf" +.Fc +.Ft "char **" +.Fo procstat_getenvv +.Fa "struct procstat *procstat" +.Fa "const struct kinfo_proc *kp" +.Fa "size_t nchr" +.Fa "char *errbuf" +.Fc .Ft "struct filestat_list *" .Fo procstat_getfiles .Fa "struct procstat *procstat" @@ -251,6 +277,50 @@ The caller is responsible to free the allocated memory with a subsequent function call. .Pp The +.Fn procstat_getargv +function gets a pointer to the +.Vt procstat +structure from one of the +.Fn procstat_open_* +functions, a pointer to +.Vt kinfo_proc +structure from the array obtained from the +.Fn kvm_getprocs +function, and returns a null-terminated argument vector that corresponds to +the command line arguments passed to the process. +The +.Fa nchr +argument indicates the maximum number of characters, including null bytes, +to use in building the strings. +If this amount is exceeded, the string causing the overflow is truncated and +the partial result is returned. +This is handy for programs that print only a one line summary of a +command and should not copy out large amounts of text only to ignore it. +If +.Fa nchr +is zero, no limit is imposed and all argument strings are returned. +The values of the returned argument vector refer the strings stored +in the +.Vt procstat +internal buffer. +A subsequent call of the function with the same +.Vt procstat +argument will reuse the buffer. +To free the allocated memory +.Fn procstat_freeargv +function call can be used, or it will be released on +.Fn procstat_close . +.Pp +The +.Fn procstat_getenvv +function is similar to +.Fn procstat_getargv +but returns the vector of environment strings. +The caller may free the allocated memory with a subsequent +.Fn procstat_freeenv +function call. +.Pp +The .Fn procstat_getfiles function gets a pointer to the .Vt procstat diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index 6a71bf4..52b9409 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -105,6 +105,8 @@ int statfs(const char *, struct statfs *); /* XXX */ #define PROCSTAT_SYSCTL 2 #define PROCSTAT_CORE 3 +static char **getargv(struct procstat *procstat, struct kinfo_proc *kp, + size_t nchr, int env); static char *getmnton(kvm_t *kd, struct mount *m); static struct kinfo_vmentry * kinfo_getvmmap_core(struct procstat_core *core, int *cntp); @@ -158,6 +160,8 @@ procstat_close(struct procstat *procstat) kvm_close(procstat->kd); else if (procstat->type == PROCSTAT_CORE) procstat_core_close(procstat->core); + procstat_freeargv(procstat); + procstat_freeenvv(procstat); free(procstat); } @@ -1524,6 +1528,180 @@ getmnton(kvm_t *kd, struct mount *m) return (mt->mntonname); } +/* + * Auxiliary structures and functions to get process environment or + * command line arguments. + */ +struct argvec { + char *buf; + size_t bufsize; + char **argv; + size_t argc; +}; + +static struct argvec * +argvec_alloc(size_t bufsize) +{ + struct argvec *av; + + av = malloc(sizeof(*av)); + if (av == NULL) + return (NULL); + av->bufsize = bufsize; + av->buf = malloc(av->bufsize); + if (av->buf == NULL) { + free(av); + return (NULL); + } + av->argc = 32; + av->argv = malloc(sizeof(char *) * av->argc); + if (av->argv == NULL) { + free(av->buf); + free(av); + return (NULL); + } + return av; +} + +static void +argvec_free(struct argvec * av) +{ + + free(av->argv); + free(av->buf); + free(av); +} + +static char ** +getargv(struct procstat *procstat, struct kinfo_proc *kp, size_t nchr, int env) +{ + int error, name[4], argc, i; + struct argvec *av, **avp; + enum psc_type type; + size_t len; + char *p, **argv; + + assert(procstat); + assert(kp); + if (procstat->type == PROCSTAT_KVM) { + warnx("can't use kvm access method"); + return (NULL); + } + if (procstat->type != PROCSTAT_SYSCTL && + procstat->type != PROCSTAT_CORE) { + warnx("unknown access method: %d", procstat->type); + return (NULL); + } + + if (nchr == 0 || nchr > ARG_MAX) + nchr = ARG_MAX; + + avp = (struct argvec **)(env ? &procstat->argv : &procstat->envv); + av = *avp; + + if (av == NULL) + { + av = argvec_alloc(nchr); + if (av == NULL) + { + warn("malloc(%zu)", nchr); + return (NULL); + } + *avp = av; + } else if (av->bufsize < nchr) { + av->buf = reallocf(av->buf, nchr); + if (av->buf == NULL) { + warn("malloc(%zu)", nchr); + return (NULL); + } + } + if (procstat->type == PROCSTAT_SYSCTL) { + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = env ? KERN_PROC_ENV : KERN_PROC_ARGS; + name[3] = kp->ki_pid; + len = nchr; + error = sysctl(name, 4, av->buf, &len, NULL, 0); + if (error != 0 && errno != ESRCH && errno != EPERM) + warn("sysctl(kern.proc.%s)", env ? "env" : "args"); + if (error != 0 || len == 0) + return (NULL); + } else /* procstat->type == PROCSTAT_CORE */ { + type = env ? PSC_TYPE_ENVV : PSC_TYPE_ARGV; + len = nchr; + if (procstat_core_get(procstat->core, type, av->buf, &len) + == NULL) { + return (NULL); + } + } + + argv = av->argv; + argc = av->argc; + i = 0; + for (p = av->buf; p < av->buf + len; p += strlen(p) + 1) { + argv[i++] = p; + if (i < argc) + continue; + /* Grow argv. */ + argc += argc; + argv = realloc(argv, sizeof(char *) * argc); + if (argv == NULL) { + warn("malloc(%zu)", sizeof(char *) * argc); + return (NULL); + } + av->argv = argv; + av->argc = argc; + } + argv[i] = NULL; + + return (argv); +} + +/* + * Return process command line arguments. + */ +char ** +procstat_getargv(struct procstat *procstat, struct kinfo_proc *p, size_t nchr) +{ + + return (getargv(procstat, p, nchr, 0)); +} + +/* + * Free the buffer allocated by procstat_getargv(). + */ +void +procstat_freeargv(struct procstat *procstat) +{ + + if (procstat->argv != NULL) { + argvec_free(procstat->argv); + procstat->argv = NULL; + } +} + +/* + * Return process environment. + */ +char ** +procstat_getenvv(struct procstat *procstat, struct kinfo_proc *p, size_t nchr) +{ + + return (getargv(procstat, p, nchr, 1)); +} + +/* + * Free the buffer allocated by procstat_getenvv(). + */ +void +procstat_freeenvv(struct procstat *procstat) +{ + if (procstat->envv != NULL) { + argvec_free(procstat->envv); + procstat->envv = NULL; + } +} + static struct kinfo_vmentry * kinfo_getvmmap_core(struct procstat_core *core, int *cntp) { diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h index 353948b..b7719c0 100644 --- a/lib/libprocstat/libprocstat.h +++ b/lib/libprocstat/libprocstat.h @@ -147,6 +147,8 @@ STAILQ_HEAD(filestat_list, filestat); __BEGIN_DECLS void procstat_close(struct procstat *procstat); +void procstat_freeargv(struct procstat *procstat); +void procstat_freeenvv(struct procstat *procstat); void procstat_freegroups(struct procstat *procstat, gid_t *groups); void procstat_freeprocs(struct procstat *procstat, struct kinfo_proc *p); void procstat_freefiles(struct procstat *procstat, @@ -167,6 +169,10 @@ 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); +char **procstat_getargv(struct procstat *procstat, struct kinfo_proc *p, + size_t nchr); +char **procstat_getenvv(struct procstat *procstat, struct kinfo_proc *p, + size_t nchr); gid_t *procstat_getgroups(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *count); int procstat_getosrel(struct procstat *procstat, struct kinfo_proc *kp, diff --git a/lib/libprocstat/libprocstat_internal.h b/lib/libprocstat/libprocstat_internal.h index ff1e742..6ca3197 100644 --- a/lib/libprocstat/libprocstat_internal.h +++ b/lib/libprocstat/libprocstat_internal.h @@ -34,6 +34,8 @@ struct procstat { kvm_t *kd; void *vmentries; void *files; + void *argv; + void *envv; struct procstat_core *core; }; commit c0738da9d74545596ce69ba2f326fe511b2cbbce Author: Mikolaj Golub Date: Sun Mar 31 13:12:08 2013 +0300 Use libprocstat(3) to retrieve process command line arguments and environment variables. MFC after: 1 month diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c index 16f2c99..73c3372 100644 --- a/usr.bin/procstat/procstat.c +++ b/usr.bin/procstat/procstat.c @@ -61,9 +61,9 @@ procstat(struct procstat *prstat, struct kinfo_proc *kipp) if (bflag) procstat_bin(prstat, kipp); else if (cflag) - procstat_args(kipp); + procstat_args(prstat, kipp); else if (eflag) - procstat_env(kipp); + procstat_env(prstat, kipp); else if (fflag) procstat_files(prstat, kipp); else if (iflag) diff --git a/usr.bin/procstat/procstat.h b/usr.bin/procstat/procstat.h index e651665..5d8d773 100644 --- a/usr.bin/procstat/procstat.h +++ b/usr.bin/procstat/procstat.h @@ -34,12 +34,12 @@ extern int hflag, nflag, Cflag; struct kinfo_proc; void kinfo_proc_sort(struct kinfo_proc *kipp, int count); -void procstat_args(struct kinfo_proc *kipp); +void procstat_args(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_auxv(struct kinfo_proc *kipp); void procstat_basic(struct kinfo_proc *kipp); void procstat_bin(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_cred(struct procstat *prstat, struct kinfo_proc *kipp); -void procstat_env(struct kinfo_proc *kipp); +void procstat_env(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_files(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_kstack(struct kinfo_proc *kipp, int kflag); void procstat_rlimit(struct procstat *prstat, struct kinfo_proc *kipp); diff --git a/usr.bin/procstat/procstat_args.c b/usr.bin/procstat/procstat_args.c index b13aa72..342b60f 100644 --- a/usr.bin/procstat/procstat_args.c +++ b/usr.bin/procstat/procstat_args.c @@ -40,52 +40,40 @@ #include "procstat.h" -static char args[ARG_MAX]; - static void -do_args(struct kinfo_proc *kipp, int env) +do_args(struct procstat *procstat, struct kinfo_proc *kipp, int env) { - int error, name[4]; - size_t len; - char *cp; + int i; + char **args; - if (!hflag) + if (!hflag) { printf("%5s %-16s %-53s\n", "PID", "COMM", env ? "ENVIRONMENT" : "ARGS"); - - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = env ? KERN_PROC_ENV : KERN_PROC_ARGS; - name[3] = kipp->ki_pid; - len = sizeof(args); - error = sysctl(name, 4, args, &len, NULL, 0); - if (error < 0 && errno != ESRCH && errno != EPERM) { - warn("sysctl: kern.proc.%s: %d: %d", env ? "env" : "args", - kipp->ki_pid, errno); - return; } - if (error < 0) + + args = env ? procstat_getenvv(procstat, kipp, 0) : + procstat_getargv(procstat, kipp, 0); + + printf("%5d %-16s", kipp->ki_pid, kipp->ki_comm); + + if (args == NULL) { + printf(" -\n"); return; - if (len == 0 || strlen(args) == 0) { - strcpy(args, "-"); - len = strlen(args) + 1; } - printf("%5d ", kipp->ki_pid); - printf("%-16s ", kipp->ki_comm); - for (cp = args; cp < args + len; cp += strlen(cp) + 1) - printf("%s%s", cp != args ? " " : "", cp); + for (i = 0; args[i] != NULL; i++) + printf(" %s", args[i]); printf("\n"); } void -procstat_args(struct kinfo_proc *kipp) +procstat_args(struct procstat *procstat, struct kinfo_proc *kipp) { - do_args(kipp, 0); + do_args(procstat, kipp, 0); } void -procstat_env(struct kinfo_proc *kipp) +procstat_env(struct procstat *procstat, struct kinfo_proc *kipp) { - do_args(kipp, 1); + do_args(procstat, kipp, 1); } commit 7ec81dcfb679487a96ffbf77014eb968d917c2c9 Author: Mikolaj Golub Date: Sun Mar 31 16:50:30 2013 +0300 Add procstat_getauxv function to retrieve a process auxiliary vector. MFC after: 1 month diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map index f2632c2..322d46f 100644 --- a/lib/libprocstat/Symbol.map +++ b/lib/libprocstat/Symbol.map @@ -17,11 +17,13 @@ FBSD_1.2 { FBSD_1.3 { procstat_freeargv; + procstat_freeauxv; procstat_freeenvv; procstat_freegroups; procstat_freevmmap; procstat_get_shm_info; procstat_getargv; + procstat_getauxv; procstat_getenvv; procstat_getgroups; procstat_getosrel; diff --git a/lib/libprocstat/core.c b/lib/libprocstat/core.c index 72dee88..b83cdfd 100644 --- a/lib/libprocstat/core.c +++ b/lib/libprocstat/core.c @@ -195,6 +195,10 @@ procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, n_type = NT_PROCSTAT_PSSTRINGS; structsize = sizeof(vm_offset_t); break; + case PSC_TYPE_AUXV: + n_type = NT_PROCSTAT_AUXV; + structsize = sizeof(Elf_Auxinfo); + break; default: warnx("unknown core stat type: %d", type); return (NULL); diff --git a/lib/libprocstat/core.h b/lib/libprocstat/core.h index 2361148..6639abc 100644 --- a/lib/libprocstat/core.h +++ b/lib/libprocstat/core.h @@ -40,6 +40,7 @@ enum psc_type { PSC_TYPE_PSSTRINGS, PSC_TYPE_ARGV, PSC_TYPE_ENVV, + PSC_TYPE_AUXV, }; struct procstat_core; diff --git a/lib/libprocstat/libprocstat.3 b/lib/libprocstat/libprocstat.3 index e1dc987..5a425e7 100644 --- a/lib/libprocstat/libprocstat.3 +++ b/lib/libprocstat/libprocstat.3 @@ -33,6 +33,7 @@ .Nm procstat_open_sysctl , .Nm procstat_close , .Nm procstat_getargv , +.Nm procstat_getauxv , .Nm procstat_getenvv , .Nm procstat_getfiles , .Nm procstat_getgroups , @@ -42,6 +43,7 @@ .Nm procstat_getumask , .Nm procstat_getvmmap , .Nm procstat_freeargv , +.Nm procstat_freeauxv , .Nm procstat_freeenvv , .Nm procstat_freefiles , .Nm procstat_freegroups , @@ -67,6 +69,11 @@ .Fa "struct procstat *procstat" .Fc .Ft void +.Fo procstat_freeauxv +.Fa "struct procstat *procstat" +.Fa "Elf_Auxinfo *auxv" +.Fc +.Ft void .Fo procstat_freeenvv .Fa "struct procstat *procstat" .Fc @@ -129,6 +136,12 @@ .Fa "size_t nchr" .Fa "char *errbuf" .Fc +.Ft "Elf_Auxinfo *" +.Fo procstat_getauxv +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "unsigned int *count" +.Fc .Ft "char **" .Fo procstat_getenvv .Fa "struct procstat *procstat" @@ -321,6 +334,19 @@ The caller may free the allocated memory with a subsequent function call. .Pp The +.Fn procstat_getauxv +function gets a pointer to the +.Vt procstat +structure, a pointer to +.Vt kinfo_proc +structure, and returns the auxiliary vector as a dynamically allocated array of +.Vt Elf_Auxinfo +elements. +The caller is responsible to free the allocated memory with a subsequent +.Fn procstat_freeauxv +function call. +.Pp +The .Fn procstat_getfiles function gets a pointer to the .Vt procstat diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index 52b9409..b70586d 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include #include @@ -110,6 +111,9 @@ static char **getargv(struct procstat *procstat, struct kinfo_proc *kp, static char *getmnton(kvm_t *kd, struct mount *m); static struct kinfo_vmentry * kinfo_getvmmap_core(struct procstat_core *core, int *cntp); +static Elf_Auxinfo *procstat_getauxv_core(struct procstat_core *core, + unsigned int *cntp); +static Elf_Auxinfo *procstat_getauxv_sysctl(pid_t pid, unsigned int *cntp); static struct filestat_list *procstat_getfiles_kvm( struct procstat *procstat, struct kinfo_proc *kp, int mmapped); static struct filestat_list *procstat_getfiles_sysctl( @@ -2075,3 +2079,151 @@ procstat_getosrel(struct procstat *procstat, struct kinfo_proc *kp, int *osrelp) return (-1); } } + +#define PROC_AUXV_MAX 256 + +#if __ELF_WORD_SIZE == 64 +static const char *elf32_sv_names[] = { + "Linux ELF32", + "FreeBSD ELF32", +}; + +static int +is_elf32_sysctl(pid_t pid) +{ + int error, name[4]; + size_t len, i; + static char sv_name[256]; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_SV_NAME; + name[3] = pid; + len = sizeof(sv_name); + error = sysctl(name, 4, sv_name, &len, NULL, 0); + if (error != 0 || len == 0) + return (0); + for (i = 0; i < sizeof(elf32_sv_names) / sizeof(*elf32_sv_names); i++) { + if (strncmp(sv_name, elf32_sv_names[i], sizeof(sv_name)) == 0) + return (1); + } + return (0); +} + +static Elf_Auxinfo * +procstat_getauxv32_sysctl(pid_t pid, unsigned int *cntp) +{ + Elf_Auxinfo *auxv; + Elf32_Auxinfo *auxv32; + void *ptr; + size_t len; + unsigned int i, count; + int name[4]; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_AUXV; + name[3] = pid; + len = PROC_AUXV_MAX * sizeof(Elf32_Auxinfo); + auxv = NULL; + auxv32 = malloc(len); + if (auxv32 == NULL) { + warn("malloc(%zu)", len); + goto out; + } + if (sysctl(name, 4, auxv32, &len, NULL, 0) == -1) { + if (errno != ESRCH && errno != EPERM) + warn("sysctl: kern.proc.auxv: %d: %d", pid, errno); + goto out; + } + count = len / sizeof(Elf_Auxinfo); + auxv = malloc(count * sizeof(Elf_Auxinfo)); + if (auxv == NULL) { + warn("malloc(%zu)", count * sizeof(Elf_Auxinfo)); + goto out; + } + for (i = 0; i < count; i++) { + /* + * XXX: We expect that values for a_type on a 32-bit platform + * are directly mapped to values on 64-bit one, which is not + * necessarily true. + */ + auxv[i].a_type = auxv32[i].a_type; + ptr = &auxv32[i].a_un; + auxv[i].a_un.a_val = *((uint32_t *)ptr); + } + *cntp = count; +out: + free(auxv32); + return (auxv); +} +#endif /* __ELF_WORD_SIZE == 64 */ + +static Elf_Auxinfo * +procstat_getauxv_sysctl(pid_t pid, unsigned int *cntp) +{ + Elf_Auxinfo *auxv; + int name[4]; + size_t len; + +#if __ELF_WORD_SIZE == 64 + if (is_elf32_sysctl(pid)) + return (procstat_getauxv32_sysctl(pid, cntp)); +#endif + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_AUXV; + name[3] = pid; + len = PROC_AUXV_MAX * sizeof(Elf_Auxinfo); + auxv = malloc(len); + if (auxv == NULL) { + warn("malloc(%zu)", len); + return (NULL); + } + if (sysctl(name, 4, auxv, &len, NULL, 0) == -1) { + if (errno != ESRCH && errno != EPERM) + warn("sysctl: kern.proc.auxv: %d: %d", pid, errno); + free(auxv); + return (NULL); + } + *cntp = len / sizeof(Elf_Auxinfo); + return (auxv); +} + +static Elf_Auxinfo * +procstat_getauxv_core(struct procstat_core *core, unsigned int *cntp) +{ + Elf_Auxinfo *auxv; + size_t len; + + auxv = procstat_core_get(core, PSC_TYPE_AUXV, NULL, &len); + if (auxv == NULL) + return (NULL); + *cntp = len / sizeof(Elf_Auxinfo); + return (auxv); +} + +Elf_Auxinfo * +procstat_getauxv(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 (procstat_getauxv_sysctl(kp->ki_pid, cntp)); + case PROCSTAT_CORE: + return (procstat_getauxv_core(procstat->core, cntp)); + default: + warnx("unknown access method: %d", procstat->type); + return (NULL); + } +} + +void +procstat_freeauxv(struct procstat *procstat __unused, Elf_Auxinfo *auxv) +{ + + free(auxv); +} diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h index b7719c0..3ac51e1 100644 --- a/lib/libprocstat/libprocstat.h +++ b/lib/libprocstat/libprocstat.h @@ -30,6 +30,14 @@ #define _LIBPROCSTAT_H_ /* + * XXX: sys/elf.h conflicts with zfs_context.h. Workaround this by not + * including conflicting parts when building zfs code. + */ +#ifndef ZFS +#include +#endif + +/* * Vnode types. */ #define PS_FST_VTYPE_VNON 1 @@ -148,6 +156,9 @@ STAILQ_HEAD(filestat_list, filestat); __BEGIN_DECLS void procstat_close(struct procstat *procstat); void procstat_freeargv(struct procstat *procstat); +#ifndef ZFS +void procstat_freeauxv(struct procstat *procstat, Elf_Auxinfo *auxv); +#endif void procstat_freeenvv(struct procstat *procstat); void procstat_freegroups(struct procstat *procstat, gid_t *groups); void procstat_freeprocs(struct procstat *procstat, struct kinfo_proc *p); @@ -171,6 +182,10 @@ int procstat_get_vnode_info(struct procstat *procstat, struct filestat *fst, struct vnstat *vn, char *errbuf); char **procstat_getargv(struct procstat *procstat, struct kinfo_proc *p, size_t nchr); +#ifndef ZFS +Elf_Auxinfo *procstat_getauxv(struct procstat *procstat, + struct kinfo_proc *kp, unsigned int *cntp); +#endif char **procstat_getenvv(struct procstat *procstat, struct kinfo_proc *p, size_t nchr); gid_t *procstat_getgroups(struct procstat *procstat, struct kinfo_proc *kp, commit e27747310330a7f3e5b172d4b8e76db528016ec7 Author: Mikolaj Golub Date: Sun Mar 31 16:52:55 2013 +0300 Use libprocstat(3) to retrieve ELF auxiliary vector. MFC after: 1 month diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c index 73c3372..1e28341 100644 --- a/usr.bin/procstat/procstat.c +++ b/usr.bin/procstat/procstat.c @@ -81,7 +81,7 @@ procstat(struct procstat *prstat, struct kinfo_proc *kipp) else if (vflag) procstat_vm(prstat, kipp); else if (xflag) - procstat_auxv(kipp); + procstat_auxv(prstat, kipp); else procstat_basic(kipp); } diff --git a/usr.bin/procstat/procstat.h b/usr.bin/procstat/procstat.h index 5d8d773..a0ec2e5d 100644 --- a/usr.bin/procstat/procstat.h +++ b/usr.bin/procstat/procstat.h @@ -35,7 +35,7 @@ struct kinfo_proc; void kinfo_proc_sort(struct kinfo_proc *kipp, int count); void procstat_args(struct procstat *prstat, struct kinfo_proc *kipp); -void procstat_auxv(struct kinfo_proc *kipp); +void procstat_auxv(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_basic(struct kinfo_proc *kipp); void procstat_bin(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_cred(struct procstat *prstat, struct kinfo_proc *kipp); diff --git a/usr.bin/procstat/procstat_auxv.c b/usr.bin/procstat/procstat_auxv.c index b78e13a..aa62594 100644 --- a/usr.bin/procstat/procstat_auxv.c +++ b/usr.bin/procstat/procstat_auxv.c @@ -43,113 +43,26 @@ #include "procstat.h" -#define PROC_AUXV_MAX 256 - -static Elf_Auxinfo auxv[PROC_AUXV_MAX]; -static char prefix[256]; - -#if __ELF_WORD_SIZE == 64 -static Elf32_Auxinfo auxv32[PROC_AUXV_MAX]; - -static const char *elf32_sv_names[] = { - "Linux ELF32", - "FreeBSD ELF32", -}; - -static int -is_elf32(pid_t pid) -{ - int error, name[4]; - size_t len, i; - static char sv_name[256]; - - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_SV_NAME; - name[3] = pid; - len = sizeof(sv_name); - error = sysctl(name, 4, sv_name, &len, NULL, 0); - if (error != 0 || len == 0) - return (0); - for (i = 0; i < sizeof(elf32_sv_names) / sizeof(*elf32_sv_names); i++) { - if (strncmp(sv_name, elf32_sv_names[i], sizeof(sv_name)) == 0) - return (1); - } - return (0); -} - -static size_t -retrieve_auxv32(pid_t pid) -{ - int name[4]; - size_t len, i; - void *ptr; - - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_AUXV; - name[3] = pid; - len = sizeof(auxv32); - if (sysctl(name, 4, auxv32, &len, NULL, 0) == -1) { - if (errno != ESRCH && errno != EPERM) - warn("sysctl: kern.proc.auxv: %d: %d", pid, errno); - return (0); - } - for (i = 0; i < len; i++) { - /* - * XXX: We expect that values for a_type on a 32-bit platform - * are directly mapped to those on 64-bit one, which is not - * necessarily true. - */ - auxv[i].a_type = auxv32[i].a_type; - ptr = &auxv32[i].a_un; - auxv[i].a_un.a_val = *((uint32_t *)ptr); - } - return (len); -} -#endif /* __ELF_WORD_SIZE == 64 */ - #define PRINT(name, spec, val) \ printf("%s %-16s " #spec "\n", prefix, #name, (val)) #define PRINT_UNKNOWN(type, val) \ printf("%s %16ld %#lx\n", prefix, (long)type, (u_long)(val)) -static size_t -retrieve_auxv(pid_t pid) -{ - int name[4]; - size_t len; - -#if __ELF_WORD_SIZE == 64 - if (is_elf32(pid)) - return (retrieve_auxv32(pid)); -#endif - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_AUXV; - name[3] = pid; - len = sizeof(auxv); - if (sysctl(name, 4, auxv, &len, NULL, 0) == -1) { - if (errno != ESRCH && errno != EPERM) - warn("sysctl: kern.proc.auxv: %d: %d", pid, errno); - return (0); - } - return (len); -} - void -procstat_auxv(struct kinfo_proc *kipp) +procstat_auxv(struct procstat *procstat, struct kinfo_proc *kipp) { - size_t len, i; + Elf_Auxinfo *auxv; + u_int count, i; + static char prefix[256]; if (!hflag) printf("%5s %-16s %-16s %-16s\n", "PID", "COMM", "AUXV", "VALUE"); - len = retrieve_auxv(kipp->ki_pid); - if (len == 0) + auxv = procstat_getauxv(procstat, kipp, &count); + if (auxv == NULL) return; snprintf(prefix, sizeof(prefix), "%5d %-16s", kipp->ki_pid, kipp->ki_comm); - for (i = 0; i < len; i++) { + for (i = 0; i < count; i++) { switch(auxv[i].a_type) { case AT_NULL: return; @@ -242,5 +155,6 @@ procstat_auxv(struct kinfo_proc *kipp) } } printf("\n"); + procstat_freeauxv(procstat, auxv); } commit 1c3d2959eef52aa7fcbcaabc5c719c14bf7ee0f7 Author: Mikolaj Golub Date: Sun Mar 31 19:37:41 2013 +0300 Add procstat_getkstack function to dump kernel stacks of a process. MFC after: 1 month diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map index 322d46f..085720c 100644 --- a/lib/libprocstat/Symbol.map +++ b/lib/libprocstat/Symbol.map @@ -20,12 +20,14 @@ FBSD_1.3 { procstat_freeauxv; procstat_freeenvv; procstat_freegroups; + procstat_freekstack; procstat_freevmmap; procstat_get_shm_info; procstat_getargv; procstat_getauxv; procstat_getenvv; procstat_getgroups; + procstat_getkstack; procstat_getosrel; procstat_getpathname; procstat_getrlimit; diff --git a/lib/libprocstat/libprocstat.3 b/lib/libprocstat/libprocstat.3 index 5a425e7..1367741 100644 --- a/lib/libprocstat/libprocstat.3 +++ b/lib/libprocstat/libprocstat.3 @@ -37,6 +37,7 @@ .Nm procstat_getenvv , .Nm procstat_getfiles , .Nm procstat_getgroups , +.Nm procstat_getkstack , .Nm procstat_getosrel , .Nm procstat_getpathname , .Nm procstat_getprocs , @@ -47,6 +48,7 @@ .Nm procstat_freeenvv , .Nm procstat_freefiles , .Nm procstat_freegroups , +.Nm procstat_freekstack , .Nm procstat_freeprocs , .Nm procstat_freevmmap , .Nm procstat_get_pipe_info , @@ -88,6 +90,11 @@ .Fa "gid_t *groups" .Fc .Ft void +.Fo procstat_freekstack +.Fa "struct procstat *procstat" +.Fa "struct kinfo_kstack *kkstp" +.Fc +.Ft void .Fn procstat_freeprocs "struct procstat *procstat" "struct kinfo_proc *p" .Ft void .Fo procstat_freevmmap @@ -166,6 +173,12 @@ .Fa "struct kinfo_proc *kp" .Fa "int *osrelp" .Fc +.Ft "struct kinfo_kstack *" +.Fo procstat_getkstack +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "unsigned int *count" +.Fc .Ft "struct kinfo_proc *" .Fo procstat_getprocs .Fa "struct procstat *procstat" @@ -378,6 +391,22 @@ The caller is responsible to free the allocated memory with a subsequent function call. .Pp The +.Fn procstat_getkstack +function gets a pointer to the +.Vt procstat +structure initialized with one of the +.Fn procstat_open_* +functions, a pointer to +.Vt kinfo_proc +structure, and returns kernel stacks of the process as a dynamically allocated +array of +.Vt kinfo_kstack +structures. +The caller is responsible to free the allocated memory with a subsequent +.Fn procstat_freekstack +function call. +.Pp +The .Fn procstat_getosrel function gets a pointer to the .Vt procstat diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index b70586d..a54b90d 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -142,6 +142,8 @@ static int procstat_get_vnode_info_sysctl(struct filestat *fst, static gid_t *procstat_getgroups_core(struct procstat_core *core, unsigned int *count); static gid_t *procstat_getgroups_sysctl(pid_t pid, unsigned int *count); +static struct kinfo_kstack *procstat_getkstack_sysctl(pid_t pid, + int *cntp); static int procstat_getpathname_core(struct procstat_core *core, char *pathname, size_t maxlen); static int procstat_getpathname_sysctl(pid_t pid, char *pathname, @@ -1764,6 +1766,7 @@ 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"); @@ -2227,3 +2230,70 @@ procstat_freeauxv(struct procstat *procstat __unused, Elf_Auxinfo *auxv) free(auxv); } + +static struct kinfo_kstack * +procstat_getkstack_sysctl(pid_t pid, int *cntp) +{ + struct kinfo_kstack *kkstp; + int error, name[4]; + size_t len; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_KSTACK; + name[3] = pid; + + len = 0; + error = sysctl(name, 4, NULL, &len, NULL, 0); + if (error < 0 && errno != ESRCH && errno != EPERM && errno != ENOENT) { + warn("sysctl: kern.proc.kstack: %d", pid); + return (NULL); + } + if (error == -1 && errno == ENOENT) { + warnx("sysctl: kern.proc.kstack unavailable" + " (options DDB or options STACK required in kernel)"); + return (NULL); + } + if (error == -1) + return (NULL); + kkstp = malloc(len); + if (kkstp == NULL) { + warn("malloc(%zu)", len); + return (NULL); + } + if (sysctl(name, 4, kkstp, &len, NULL, 0) == -1) { + warn("sysctl: kern.proc.pid: %d", pid); + free(kkstp); + return (NULL); + } + *cntp = len / sizeof(*kkstp); + + return (kkstp); +} + +struct kinfo_kstack * +procstat_getkstack(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 (procstat_getkstack_sysctl(kp->ki_pid, cntp)); + case PROCSTAT_CORE: + warnx("core method is not supported"); + return (NULL); + default: + warnx("unknown access method: %d", procstat->type); + return (NULL); + } +} + +void +procstat_freekstack(struct procstat *procstat __unused, + struct kinfo_kstack *kkstp) +{ + + free(kkstp); +} diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h index 3ac51e1..2c2662d 100644 --- a/lib/libprocstat/libprocstat.h +++ b/lib/libprocstat/libprocstat.h @@ -97,6 +97,7 @@ #define PS_FST_FFLAG_EXEC 0x2000 #define PS_FST_FFLAG_HASLOCK 0x4000 +struct kinfo_kstack; struct kinfo_vmentry; struct procstat; struct rlimit; @@ -161,6 +162,8 @@ void procstat_freeauxv(struct procstat *procstat, Elf_Auxinfo *auxv); #endif void procstat_freeenvv(struct procstat *procstat); void procstat_freegroups(struct procstat *procstat, gid_t *groups); +void procstat_freekstack(struct procstat *procstat, + struct kinfo_kstack *kkstp); void procstat_freeprocs(struct procstat *procstat, struct kinfo_proc *p); void procstat_freefiles(struct procstat *procstat, struct filestat_list *head); @@ -190,6 +193,8 @@ char **procstat_getenvv(struct procstat *procstat, struct kinfo_proc *p, size_t nchr); gid_t *procstat_getgroups(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *count); +struct kinfo_kstack *procstat_getkstack(struct procstat *procstat, + struct kinfo_proc *kp, unsigned int *count); int procstat_getosrel(struct procstat *procstat, struct kinfo_proc *kp, int *osrelp); int procstat_getpathname(struct procstat *procstat, struct kinfo_proc *kp, commit 3316d8ac5a5935b89ab164f79bf84b5e3ce8867c Author: Mikolaj Golub Date: Sun Mar 31 19:38:03 2013 +0300 Use procstat_getkstack(3) for retrieving process kernel stacks instead of direct sysctl calls. MFC after: 1 month diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c index 1e28341..366c8f1 100644 --- a/usr.bin/procstat/procstat.c +++ b/usr.bin/procstat/procstat.c @@ -71,7 +71,7 @@ procstat(struct procstat *prstat, struct kinfo_proc *kipp) else if (jflag) procstat_threads_sigs(prstat, kipp); else if (kflag) - procstat_kstack(kipp, kflag); + procstat_kstack(prstat, kipp, kflag); else if (lflag) procstat_rlimit(prstat, kipp); else if (sflag) diff --git a/usr.bin/procstat/procstat.h b/usr.bin/procstat/procstat.h index a0ec2e5d..57c9cb5 100644 --- a/usr.bin/procstat/procstat.h +++ b/usr.bin/procstat/procstat.h @@ -41,7 +41,8 @@ void procstat_bin(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_cred(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_env(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_files(struct procstat *prstat, struct kinfo_proc *kipp); -void procstat_kstack(struct kinfo_proc *kipp, int kflag); +void procstat_kstack(struct procstat *prstat, struct kinfo_proc *kipp, + int kflag); void procstat_rlimit(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_sigs(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_threads(struct procstat *prstat, struct kinfo_proc *kipp); diff --git a/usr.bin/procstat/procstat_kstack.c b/usr.bin/procstat/procstat_kstack.c index d887262..8ffa4f9 100644 --- a/usr.bin/procstat/procstat_kstack.c +++ b/usr.bin/procstat/procstat_kstack.c @@ -125,76 +125,35 @@ kinfo_kstack_sort(struct kinfo_kstack *kkstp, int count) void -procstat_kstack(struct kinfo_proc *kipp, int kflag) +procstat_kstack(struct procstat *procstat, struct kinfo_proc *kipp, int kflag) { struct kinfo_kstack *kkstp, *kkstp_free; struct kinfo_proc *kip, *kip_free; char trace[KKST_MAXLEN]; - int error, name[4]; unsigned int i, j; - size_t kip_len, kstk_len; + unsigned int kip_count, kstk_count; if (!hflag) printf("%5s %6s %-16s %-16s %-29s\n", "PID", "TID", "COMM", "TDNAME", "KSTACK"); - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_KSTACK; - name[3] = kipp->ki_pid; - - kstk_len = 0; - error = sysctl(name, 4, NULL, &kstk_len, NULL, 0); - if (error < 0 && errno != ESRCH && errno != EPERM && errno != ENOENT) { - warn("sysctl: kern.proc.kstack: %d", kipp->ki_pid); - return; - } - if (error < 0 && errno == ENOENT) { - warnx("sysctl: kern.proc.kstack unavailable"); - errx(-1, "options DDB or options STACK required in kernel"); - } - if (error < 0) - return; - - kkstp = kkstp_free = malloc(kstk_len); + kkstp = kkstp_free = procstat_getkstack(procstat, kipp, &kstk_count); if (kkstp == NULL) - err(-1, "malloc"); - - if (sysctl(name, 4, kkstp, &kstk_len, NULL, 0) < 0) { - warn("sysctl: kern.proc.pid: %d", kipp->ki_pid); - free(kkstp); return; - } /* * We need to re-query for thread information, so don't use *kipp. */ - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_PID | KERN_PROC_INC_THREAD; - name[3] = kipp->ki_pid; - - kip_len = 0; - error = sysctl(name, 4, NULL, &kip_len, NULL, 0); - if (error < 0 && errno != ESRCH) { - warn("sysctl: kern.proc.pid: %d", kipp->ki_pid); - return; - } - if (error < 0) - return; - - kip = kip_free = malloc(kip_len); - if (kip == NULL) - err(-1, "malloc"); + kip = kip_free = procstat_getprocs(procstat, + KERN_PROC_PID | KERN_PROC_INC_THREAD, kipp->ki_pid, &kip_count); - if (sysctl(name, 4, kip, &kip_len, NULL, 0) < 0) { - warn("sysctl: kern.proc.pid: %d", kipp->ki_pid); - free(kip); + if (kip == NULL) { + procstat_freekstack(procstat, kkstp_free); return; } - kinfo_kstack_sort(kkstp, kstk_len / sizeof(*kkstp)); - for (i = 0; i < kstk_len / sizeof(*kkstp); i++) { + kinfo_kstack_sort(kkstp, kstk_count); + for (i = 0; i < kstk_count; i++) { kkstp = &kkstp_free[i]; /* @@ -202,7 +161,7 @@ procstat_kstack(struct kinfo_proc *kipp, int kflag) * display the per-thread command line. */ kipp = NULL; - for (j = 0; j < kip_len / sizeof(*kipp); j++) { + for (j = 0; j < kip_count; j++) { kipp = &kip_free[j]; if (kkstp->kkst_tid == kipp->ki_tid) break; @@ -242,6 +201,6 @@ procstat_kstack(struct kinfo_proc *kipp, int kflag) kstack_cleanup(kkstp->kkst_trace, trace, kflag); printf("%-29s\n", trace); } - free(kip_free); - free(kkstp_free); + procstat_freekstack(procstat, kkstp_free); + procstat_freeprocs(procstat, kip_free); } commit 9591ed7aa5acd8dcf5a53006c64696f9f3c536f7 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 a list of files opened by a 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 - - PR: kern/173723 Suggested by: jhb MFC after: 1 month diff --git a/usr.bin/procstat/procstat.1 b/usr.bin/procstat/procstat.1 index 38681f8..0c112fa 100644 --- a/usr.bin/procstat/procstat.1 +++ b/usr.bin/procstat/procstat.1 @@ -38,7 +38,7 @@ .Op Fl C .Op Fl w Ar interval .Op Fl b | c | e | f | i | j | k | l | s | t | v | x -.Op Fl a | Ar pid ... +.Op Fl a | Ar pid | Ar core ... .Sh DESCRIPTION The .Nm @@ -47,6 +47,8 @@ utility displays detailed information about the processes identified by the arguments, or if the .Fl a flag is used, all processes. +It can also display information extracted from a process core file, if +the core file is specified as the argument. .Pp By default, basic process statistics are printed; one of the following options may be specified in order to select more detailed process information diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c index 366c8f1..3c1dafa 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); } @@ -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,19 +255,32 @@ main(int argc, char *argv[]) } for (i = 0; i < argc; i++) { l = strtol(argv[i], &dummy, 10); - if (*dummy != '\0') - usage(); - 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); - + 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 { + cprstat = procstat_open_core(argv[i]); + if (cprstat == NULL) { + warnx("procstat_open()"); + continue; + } + p = procstat_getprocs(cprstat, KERN_PROC_PID, + -1, &cnt); + if (p == NULL) + errx(1, "procstat_getprocs()"); + if (cnt != 0) + procstat(cprstat, p); + procstat_freeprocs(cprstat, p); + procstat_close(cprstat); + } /* Suppress header after first process. */ hflag = 1; } commit 5d657cc1fbca2adbb60af8ead057beb733f94530 Author: Mikolaj Golub Date: Sun Apr 7 18:31:59 2013 +0300 Sync gcore(1) with the recent changes in kernel code aimed at adding procstat notes to a process core file. Suggested by: jhb MFC after: 1 month diff --git a/usr.bin/gcore/Makefile b/usr.bin/gcore/Makefile index e83a48f..c4939d8 100644 --- a/usr.bin/gcore/Makefile +++ b/usr.bin/gcore/Makefile @@ -4,7 +4,7 @@ PROG= gcore SRCS= elfcore.c gcore.c DPADD= ${LIBUTIL} -LDADD= -lutil +LDADD= -lsbuf -lutil WARNS?= 1 diff --git a/usr.bin/gcore/elfcore.c b/usr.bin/gcore/elfcore.c index 8228c57..d6e702b 100644 --- a/usr.bin/gcore/elfcore.c +++ b/usr.bin/gcore/elfcore.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -71,16 +73,33 @@ struct sseg_closure { size_t size; /* Total size of all writable segments. */ }; +typedef void* (*notefunc_t)(void *, size_t *); + 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(vm_map_entry_t, segment_callback, void *closure); static void elf_detach(void); /* atexit() handler. */ -static void elf_puthdr(pid_t, vm_map_entry_t, void *, size_t *, int numsegs); -static void elf_putnote(void *dst, size_t *off, const char *name, int type, - const void *desc, size_t descsz); +static void *elf_note_fpregset(void *, size_t *); +static void *elf_note_prpsinfo(void *, size_t *); +static void *elf_note_prstatus(void *, size_t *); +static void *elf_note_thrmisc(void *, size_t *); +static void *elf_note_procstat_auxv(void *, size_t *); +static void *elf_note_procstat_files(void *, size_t *); +static void *elf_note_procstat_groups(void *, size_t *); +static void *elf_note_procstat_osrel(void *, size_t *); +static void *elf_note_procstat_proc(void *, size_t *); +static void *elf_note_procstat_psstrings(void *, size_t *); +static void *elf_note_procstat_rlimit(void *, size_t *); +static void *elf_note_procstat_umask(void *, size_t *); +static void *elf_note_procstat_vmmap(void *, size_t *); +static void elf_puthdr(pid_t, vm_map_entry_t, void *, size_t, size_t, size_t, + int); +static void elf_putnote(int, notefunc_t, void *, struct sbuf *); +static void elf_putnotes(pid_t, struct sbuf *, size_t *); static void freemap(vm_map_entry_t); static vm_map_entry_t readmap(pid_t); +static void *procstat_sysctl(void *, int, size_t, size_t *sizep); static pid_t g_pid; /* Pid being dumped, global for elf_detach */ @@ -114,8 +133,10 @@ elf_coredump(int efd __unused, int fd, pid_t pid) { vm_map_entry_t map; struct sseg_closure seginfo; + struct sbuf *sb; void *hdr; - size_t hdrsize; + size_t hdrsize, notesz, segoff; + ssize_t n, old_len; Elf_Phdr *php; int i; @@ -139,27 +160,32 @@ elf_coredump(int efd __unused, int fd, pid_t pid) each_writable_segment(map, cb_size_segment, &seginfo); /* - * 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. + * Build the header and the notes using sbuf and write to the file. */ - hdrsize = 0; - elf_puthdr(pid, map, NULL, &hdrsize, seginfo.count); - - /* - * Allocate memory for building the header, fill it up, - * and write it out. - */ - if ((hdr = calloc(1, hdrsize)) == NULL) - errx(1, "out of memory"); - + sb = sbuf_new_auto(); + hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); + /* Start header + notes section. */ + sbuf_start_section(sb, NULL); + /* Make empty header subsection. */ + sbuf_start_section(sb, &old_len); + sbuf_putc(sb, 0); + sbuf_end_section(sb, old_len, hdrsize, 0); + /* Put notes. */ + elf_putnotes(pid, sb, ¬esz); + /* Align up to a page boundary for the program segments. */ + sbuf_end_section(sb, -1, PAGE_SIZE, 0); + if (sbuf_finish(sb) != 0) + err(1, "sbuf_finish"); + hdr = sbuf_data(sb); + segoff = sbuf_len(sb); /* Fill in the header. */ - hdrsize = 0; - elf_puthdr(pid, map, hdr, &hdrsize, seginfo.count); + elf_puthdr(pid, map, hdr, hdrsize, notesz, segoff, seginfo.count); - /* Write it to the core file. */ - if (write(fd, hdr, hdrsize) == -1) + n = write(fd, hdr, segoff); + if (n == -1) err(1, "write"); + if (n < segoff) + errx(1, "short write"); /* Write the contents of all of the writable segments. */ php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; @@ -195,7 +221,7 @@ elf_coredump(int efd __unused, int fd, pid_t pid) } php++; } - free(hdr); + sbuf_delete(sb); freemap(map); } @@ -257,209 +283,136 @@ each_writable_segment(vm_map_entry_t map, segment_callback func, void *closure) } static void -elf_getstatus(pid_t pid, prpsinfo_t *psinfo) -{ - struct kinfo_proc kobj; - int name[4]; - size_t len; - - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_PID; - name[3] = pid; - - len = sizeof(kobj); - if (sysctl(name, 4, &kobj, &len, NULL, 0) == -1) - err(1, "error accessing kern.proc.pid.%u sysctl", pid); - if (kobj.ki_pid != pid) - err(1, "error accessing kern.proc.pid.%u sysctl datas", pid); - strncpy(psinfo->pr_fname, kobj.ki_comm, MAXCOMLEN); - strncpy(psinfo->pr_psargs, psinfo->pr_fname, PRARGSZ); -} - -/* - * Generate the ELF coredump header into the buffer at "dst". "dst" may - * be NULL, in which case the header is sized but not actually generated. - */ -static void -elf_puthdr(pid_t pid, vm_map_entry_t map, void *dst, size_t *off, int numsegs) +elf_putnotes(pid_t pid, struct sbuf *sb, size_t *sizep) { - struct ptrace_lwpinfo lwpinfo; - struct { - prstatus_t status; - prfpregset_t fpregset; - prpsinfo_t psinfo; - thrmisc_t thrmisc; - } *tempdata; - size_t ehoff; - size_t phoff; - size_t noteoff; - size_t notesz; - size_t threads; lwpid_t *tids; + size_t threads, old_len; + ssize_t size; int i; - prstatus_t *status; - prfpregset_t *fpregset; - prpsinfo_t *psinfo; - thrmisc_t *thrmisc; - - ehoff = *off; - *off += sizeof(Elf_Ehdr); - - phoff = *off; - *off += (numsegs + 1) * sizeof(Elf_Phdr); - - noteoff = *off; - - if (dst != NULL) { - if ((tempdata = calloc(1, sizeof(*tempdata))) == NULL) - errx(1, "out of memory"); - status = &tempdata->status; - fpregset = &tempdata->fpregset; - psinfo = &tempdata->psinfo; - thrmisc = &tempdata->thrmisc; - } else { - tempdata = NULL; - status = NULL; - fpregset = NULL; - psinfo = NULL; - thrmisc = NULL; - } - errno = 0; threads = ptrace(PT_GETNUMLWPS, pid, NULL, 0); if (errno) err(1, "PT_GETNUMLWPS"); + tids = malloc(threads * sizeof(*tids)); + if (tids == NULL) + errx(1, "out of memory"); + errno = 0; + ptrace(PT_GETLWPLIST, pid, (void *)tids, threads); + if (errno) + err(1, "PT_GETLWPLIST"); - if (dst != NULL) { - psinfo->pr_version = PRPSINFO_VERSION; - psinfo->pr_psinfosz = sizeof(prpsinfo_t); - elf_getstatus(pid, psinfo); - - } - elf_putnote(dst, off, "FreeBSD", NT_PRPSINFO, psinfo, - sizeof *psinfo); + sbuf_start_section(sb, &old_len); + elf_putnote(NT_PRPSINFO, elf_note_prpsinfo, &pid, sb); - if (dst != NULL) { - tids = malloc(threads * sizeof(*tids)); - if (tids == NULL) - errx(1, "out of memory"); - errno = 0; - ptrace(PT_GETLWPLIST, pid, (void *)tids, threads); - if (errno) - err(1, "PT_GETLWPLIST"); - } for (i = 0; i < threads; ++i) { - if (dst != NULL) { - status->pr_version = PRSTATUS_VERSION; - status->pr_statussz = sizeof(prstatus_t); - status->pr_gregsetsz = sizeof(gregset_t); - status->pr_fpregsetsz = sizeof(fpregset_t); - status->pr_osreldate = __FreeBSD_version; - status->pr_pid = tids[i]; - - ptrace(PT_GETREGS, tids[i], (void *)&status->pr_reg, 0); - ptrace(PT_GETFPREGS, tids[i], (void *)fpregset, 0); - ptrace(PT_LWPINFO, tids[i], (void *)&lwpinfo, - sizeof(lwpinfo)); - memset(&thrmisc->_pad, 0, sizeof(thrmisc->_pad)); - strcpy(thrmisc->pr_tname, lwpinfo.pl_tdname); - } - elf_putnote(dst, off, "FreeBSD", NT_PRSTATUS, status, - sizeof *status); - elf_putnote(dst, off, "FreeBSD", NT_FPREGSET, fpregset, - sizeof *fpregset); - elf_putnote(dst, off, "FreeBSD", NT_THRMISC, thrmisc, - sizeof *thrmisc); - } - - notesz = *off - noteoff; - - if (dst != NULL) { - free(tids); - free(tempdata); + elf_putnote(NT_PRSTATUS, elf_note_prstatus, tids + i, sb); + elf_putnote(NT_FPREGSET, elf_note_fpregset, tids + i, sb); + elf_putnote(NT_THRMISC, elf_note_thrmisc, tids + i, sb); } - /* Align up to a page boundary for the program segments. */ - *off = round_page(*off); - - if (dst != NULL) { - Elf_Ehdr *ehdr; - Elf_Phdr *phdr; - struct phdr_closure phc; - - /* - * Fill in the ELF header. - */ - ehdr = (Elf_Ehdr *)((char *)dst + ehoff); - ehdr->e_ident[EI_MAG0] = ELFMAG0; - ehdr->e_ident[EI_MAG1] = ELFMAG1; - ehdr->e_ident[EI_MAG2] = ELFMAG2; - ehdr->e_ident[EI_MAG3] = ELFMAG3; - ehdr->e_ident[EI_CLASS] = ELF_CLASS; - ehdr->e_ident[EI_DATA] = ELF_DATA; - ehdr->e_ident[EI_VERSION] = EV_CURRENT; - ehdr->e_ident[EI_OSABI] = ELFOSABI_FREEBSD; - ehdr->e_ident[EI_ABIVERSION] = 0; - ehdr->e_ident[EI_PAD] = 0; - ehdr->e_type = ET_CORE; - ehdr->e_machine = ELF_ARCH; - ehdr->e_version = EV_CURRENT; - ehdr->e_entry = 0; - ehdr->e_phoff = phoff; - ehdr->e_flags = 0; - ehdr->e_ehsize = sizeof(Elf_Ehdr); - ehdr->e_phentsize = sizeof(Elf_Phdr); - ehdr->e_phnum = numsegs + 1; - ehdr->e_shentsize = sizeof(Elf_Shdr); - ehdr->e_shnum = 0; - ehdr->e_shstrndx = SHN_UNDEF; - - /* - * Fill in the program header entries. - */ - phdr = (Elf_Phdr *)((char *)dst + phoff); - - /* The note segment. */ - phdr->p_type = PT_NOTE; - phdr->p_offset = noteoff; - phdr->p_vaddr = 0; - phdr->p_paddr = 0; - phdr->p_filesz = notesz; - phdr->p_memsz = 0; - phdr->p_flags = 0; - phdr->p_align = 0; - phdr++; - - /* All the writable segments from the program. */ - phc.phdr = phdr; - phc.offset = *off; - each_writable_segment(map, cb_put_phdr, &phc); - } + elf_putnote(NT_PROCSTAT_PROC, elf_note_procstat_proc, &pid, sb); + elf_putnote(NT_PROCSTAT_FILES, elf_note_procstat_files, &pid, sb); + elf_putnote(NT_PROCSTAT_VMMAP, elf_note_procstat_vmmap, &pid, sb); + elf_putnote(NT_PROCSTAT_GROUPS, elf_note_procstat_groups, &pid, sb); + elf_putnote(NT_PROCSTAT_UMASK, elf_note_procstat_umask, &pid, sb); + elf_putnote(NT_PROCSTAT_RLIMIT, elf_note_procstat_rlimit, &pid, sb); + elf_putnote(NT_PROCSTAT_OSREL, elf_note_procstat_osrel, &pid, sb); + elf_putnote(NT_PROCSTAT_PSSTRINGS, elf_note_procstat_psstrings, &pid, + sb); + elf_putnote(NT_PROCSTAT_AUXV, elf_note_procstat_auxv, &pid, sb); + + size = sbuf_end_section(sb, old_len, 1, 0); + if (size == -1) + err(1, "sbuf_end_section"); + free(tids); + *sizep = size; } /* - * Emit one note section to "dst", or just size it if "dst" is NULL. + * Emit one note section to sbuf. */ static void -elf_putnote(void *dst, size_t *off, const char *name, int type, - const void *desc, size_t descsz) +elf_putnote(int type, notefunc_t notefunc, void *arg, struct sbuf *sb) { Elf_Note note; + size_t descsz; + ssize_t old_len; + void *desc; - note.n_namesz = strlen(name) + 1; + desc = notefunc(arg, &descsz); + note.n_namesz = 8; /* strlen("FreeBSD") + 1 */ note.n_descsz = descsz; note.n_type = type; - if (dst != NULL) - bcopy(¬e, (char *)dst + *off, sizeof note); - *off += sizeof note; - if (dst != NULL) - bcopy(name, (char *)dst + *off, note.n_namesz); - *off += roundup2(note.n_namesz, sizeof(Elf_Size)); - if (dst != NULL) - bcopy(desc, (char *)dst + *off, note.n_descsz); - *off += roundup2(note.n_descsz, sizeof(Elf_Size)); + + sbuf_bcat(sb, ¬e, sizeof(note)); + sbuf_start_section(sb, &old_len); + sbuf_bcat(sb, "FreeBSD", note.n_namesz); + sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); + if (descsz == 0) + return; + sbuf_start_section(sb, &old_len); + sbuf_bcat(sb, desc, descsz); + sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); + free(desc); +} + +/* + * Generate the ELF coredump header. + */ +static void +elf_puthdr(pid_t pid, vm_map_entry_t map, void *hdr, size_t hdrsize, + size_t notesz, size_t segoff, int numsegs) +{ + Elf_Ehdr *ehdr; + Elf_Phdr *phdr; + struct phdr_closure phc; + + ehdr = (Elf_Ehdr *)hdr; + phdr = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)); + + ehdr->e_ident[EI_MAG0] = ELFMAG0; + ehdr->e_ident[EI_MAG1] = ELFMAG1; + ehdr->e_ident[EI_MAG2] = ELFMAG2; + ehdr->e_ident[EI_MAG3] = ELFMAG3; + ehdr->e_ident[EI_CLASS] = ELF_CLASS; + ehdr->e_ident[EI_DATA] = ELF_DATA; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELFOSABI_FREEBSD; + ehdr->e_ident[EI_ABIVERSION] = 0; + ehdr->e_ident[EI_PAD] = 0; + ehdr->e_type = ET_CORE; + ehdr->e_machine = ELF_ARCH; + ehdr->e_version = EV_CURRENT; + ehdr->e_entry = 0; + ehdr->e_phoff = sizeof(Elf_Ehdr); + ehdr->e_flags = 0; + ehdr->e_ehsize = sizeof(Elf_Ehdr); + ehdr->e_phentsize = sizeof(Elf_Phdr); + ehdr->e_phnum = numsegs + 1; + ehdr->e_shentsize = sizeof(Elf_Shdr); + ehdr->e_shnum = 0; + ehdr->e_shstrndx = SHN_UNDEF; + + /* + * Fill in the program header entries. + */ + + /* The note segement. */ + phdr->p_type = PT_NOTE; + phdr->p_offset = hdrsize; + phdr->p_vaddr = 0; + phdr->p_paddr = 0; + phdr->p_filesz = notesz; + phdr->p_memsz = 0; + phdr->p_flags = PF_R; + phdr->p_align = sizeof(Elf32_Size); + phdr++; + + /* All the writable segments from the program. */ + phc.phdr = phdr; + phc.offset = segoff; + each_writable_segment(map, cb_put_phdr, &phc); } /* @@ -530,5 +483,223 @@ readmap(pid_t pid) return (map); } +/* + * Miscellaneous note out functions. + */ + +static void * +elf_note_prpsinfo(void *arg, size_t *sizep) +{ + pid_t pid; + prpsinfo_t *psinfo; + struct kinfo_proc kip; + size_t len; + int name[4]; + + pid = *(pid_t *)arg; + psinfo = calloc(1, sizeof(*psinfo)); + if (psinfo == NULL) + errx(1, "out of memory"); + psinfo->pr_version = PRPSINFO_VERSION; + psinfo->pr_psinfosz = sizeof(prpsinfo_t); + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_PID; + name[3] = pid; + len = sizeof(kip); + if (sysctl(name, 4, &kip, &len, NULL, 0) == -1) + err(1, "kern.proc.pid.%u", pid); + if (kip.ki_pid != pid) + err(1, "kern.proc.pid.%u", pid); + strncpy(psinfo->pr_fname, kip.ki_comm, MAXCOMLEN); + strncpy(psinfo->pr_psargs, psinfo->pr_fname, PRARGSZ); + + *sizep = sizeof(*psinfo); + return (psinfo); +} + +static void * +elf_note_prstatus(void *arg, size_t *sizep) +{ + lwpid_t tid; + prstatus_t *status; + + tid = *(lwpid_t *)arg; + status = calloc(1, sizeof(*status)); + if (status == NULL) + errx(1, "out of memory"); + status->pr_version = PRSTATUS_VERSION; + status->pr_statussz = sizeof(prstatus_t); + status->pr_gregsetsz = sizeof(gregset_t); + status->pr_fpregsetsz = sizeof(fpregset_t); + status->pr_osreldate = __FreeBSD_version; + status->pr_pid = tid; + ptrace(PT_GETREGS, tid, (void *)&status->pr_reg, 0); + + *sizep = sizeof(*status); + return (status); +} + +static void * +elf_note_fpregset(void *arg, size_t *sizep) +{ + lwpid_t tid; + prfpregset_t *fpregset; + + tid = *(lwpid_t *)arg; + fpregset = calloc(1, sizeof(*fpregset)); + if (fpregset == NULL) + errx(1, "out of memory"); + ptrace(PT_GETFPREGS, tid, (void *)fpregset, 0); + + *sizep = sizeof(*fpregset); + return (fpregset); +} + +static void * +elf_note_thrmisc(void *arg, size_t *sizep) +{ + lwpid_t tid; + struct ptrace_lwpinfo lwpinfo; + thrmisc_t *thrmisc; + + tid = *(lwpid_t *)arg; + thrmisc = calloc(1, sizeof(*thrmisc)); + if (thrmisc == NULL) + errx(1, "out of memory"); + ptrace(PT_LWPINFO, tid, (void *)&lwpinfo, + sizeof(lwpinfo)); + memset(&thrmisc->_pad, 0, sizeof(thrmisc->_pad)); + strcpy(thrmisc->pr_tname, lwpinfo.pl_tdname); + + *sizep = sizeof(*thrmisc); + return (thrmisc); +} + +static void * +procstat_sysctl(void *arg, int what, size_t structsz, size_t *sizep) +{ + size_t len, oldlen; + pid_t pid; + int name[4], structsize; + void *buf, *p; + + pid = *(pid_t *)arg; + structsize = structsz; + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = what; + name[3] = pid; + len = 0; + if (sysctl(name, 4, NULL, &len, NULL, 0) == -1) + err(1, "kern.proc.%d.%u", what, pid); + buf = calloc(1, sizeof(structsize) + len * 4 / 3); + if (buf == NULL) + errx(1, "out of memory"); + bcopy(&structsize, buf, sizeof(structsize)); + p = (char *)buf + sizeof(structsize); + if (sysctl(name, 4, p, &len, NULL, 0) == -1) + err(1, "kern.proc.%d.%u", what, pid); + + *sizep = sizeof(structsize) + len; + return (buf); +} + +static void * +elf_note_procstat_proc(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_PID | KERN_PROC_INC_THREAD, + sizeof(struct kinfo_proc), sizep)); +} + +static void * +elf_note_procstat_files(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_FILEDESC, + sizeof(struct kinfo_file), sizep)); +} + +static void * +elf_note_procstat_vmmap(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_VMMAP, + sizeof(struct kinfo_vmentry), sizep)); +} + +static void * +elf_note_procstat_groups(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_GROUPS, + (int)sizeof(gid_t), sizep)); +} + +static void * +elf_note_procstat_umask(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_UMASK, sizeof(u_short), sizep)); +} + +static void * +elf_note_procstat_osrel(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_OSREL, sizeof(int), sizep)); +} + +static void * +elf_note_procstat_psstrings(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_PS_STRINGS, + sizeof(vm_offset_t), sizep)); +} + +static void * +elf_note_procstat_auxv(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_AUXV, + sizeof(Elf_Auxinfo), sizep)); +} + +static void * +elf_note_procstat_rlimit(void *arg, size_t *sizep) +{ + pid_t pid; + size_t len; + int i, name[5], structsize; + void *buf, *p; + + pid = *(pid_t *)arg; + structsize = sizeof(struct rlimit) * RLIM_NLIMITS; + buf = calloc(1, sizeof(structsize) + structsize); + if (buf == NULL) + errx(1, "out of memory"); + bcopy(&structsize, buf, sizeof(structsize)); + p = (char *)buf + sizeof(structsize); + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_RLIMIT; + name[3] = pid; + len = sizeof(struct rlimit); + for (i = 0; i < RLIM_NLIMITS; i++) { + name[4] = i; + if (sysctl(name, 5, p, &len, NULL, 0) == -1) + err(1, "kern.proc.rlimit.%u", pid); + if (len != sizeof(struct rlimit)) + errx(1, "kern.proc.rlimit.%u: short read", pid); + p += len; + } + + *sizep = sizeof(structsize) + structsize; + return (buf); +} + struct dumpers elfdump = { elf_ident, elf_coredump }; TEXT_SET(dumpset, elfdump);