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:
parent
b7b63db789
commit
f1fca82ed5
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=249558
@ -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)
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user