Make libprocstat(3) extract procstat notes from a process core file.

PR:		kern/173723
Suggested by:	jhb
Glanced by:	kib
MFC after:	1 month
This commit is contained in:
Mikolaj Golub 2013-04-20 07:47:26 +00:00
parent 4f1def6889
commit 7153ad2b72
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=249666
8 changed files with 453 additions and 17 deletions

View File

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

View File

@ -17,4 +17,5 @@ FBSD_1.2 {
FBSD_1.3 {
procstat_get_shm_info;
procstat_open_core;
};

262
lib/libprocstat/core.c Normal file
View File

@ -0,0 +1,262 @@
/*-
* Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
* 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 <sys/param.h>
#include <sys/elf.h>
#include <sys/user.h>
#include <assert.h>
#include <err.h>
#include <fcntl.h>
#include <gelf.h>
#include <libelf.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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);
}

45
lib/libprocstat/core.h Normal file
View File

@ -0,0 +1,45 @@
/*-
* Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
* 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_ */

View File

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

View File

@ -96,11 +96,13 @@ __FBSDID("$FreeBSD$");
#include <libprocstat.h>
#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)
@ -230,6 +255,15 @@ procstat_getprocs(struct procstat *procstat, int what, int arg,
goto fail;
}
/* 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);
}

View File

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

View File

@ -34,6 +34,7 @@ struct procstat {
kvm_t *kd;
void *vmentries;
void *files;
struct procstat_core *core;
};
#endif /* !_LIBPROCSTAT_INTERNAL_H_ */