Add a new set of notes to a process core dump to store procstat data.

The notes format is a header of sizeof(int), which stores the size of
the corresponding data structure to provide some versioning, and data
in the format as it is returned by a related sysctl call.

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
MFC after:	1 month
This commit is contained in:
Mikolaj Golub 2013-04-16 19:19:14 +00:00
parent b7b63db789
commit f1fca82ed5
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=249558
2 changed files with 288 additions and 1 deletions

View File

@ -66,6 +66,7 @@ __FBSDID("$FreeBSD$");
#include <sys/vnode.h>
#include <sys/syslog.h>
#include <sys/eventhandler.h>
#include <sys/user.h>
#include <net/zlib.h>
@ -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)

View File

@ -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 */