gcore: add option to dump core using kernel facility

-k switch causes gcore to use ptrace(PT_COREDUMP) instead of manually
reading process memory and constructing the core.

Reviewed by:	markj
Tested by:	pho
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
Differential revision:	https://reviews.freebsd.org/D29955
This commit is contained in:
Konstantin Belousov 2021-04-24 14:20:24 +03:00
parent c192228b73
commit 73e8f06ac5
2 changed files with 80 additions and 11 deletions

View File

@ -28,7 +28,7 @@
.\" @(#)gcore.1 8.2 (Berkeley) 4/18/94 .\" @(#)gcore.1 8.2 (Berkeley) 4/18/94
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd July 13, 2016 .Dd April 24, 2021
.Dt GCORE 1 .Dt GCORE 1
.Os .Os
.Sh NAME .Sh NAME
@ -37,6 +37,7 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl f .Op Fl f
.Op Fl k
.Op Fl c Ar core .Op Fl c Ar core
.Op Ar executable .Op Ar executable
.Ar pid .Ar pid
@ -58,13 +59,24 @@ The following options are available:
Write the core file to the specified file instead of Write the core file to the specified file instead of
.Dq Pa core.<pid> . .Dq Pa core.<pid> .
.It Fl f .It Fl f
Dumps all available segments, excluding only malformed and undumpable segments. Dumps all available segments, excluding only malformed and undumpable
segments.
Unlike the default invocation, this flag dumps mappings of devices which Unlike the default invocation, this flag dumps mappings of devices which
may invalidate the state of device transactions or trigger other unexpected may invalidate the state of device transactions or trigger other unexpected
behavior. behavior.
As a result, this flag should only be used when the behavior of the As a result, this flag should only be used when the behavior of the
application and any devices it has mapped is fully understood and any side application and any devices it has mapped is fully understood and any side
effects can be controlled or tolerated. effects can be controlled or tolerated.
.It Fl k
Use the
.Xr ptrace 2
.Dv PT_COREDUMP
kernel facility to write the core dump, instead of reading the process'
memory and constructing the dump file in
.Nm
itself.
This is faster, and the dump is written by the
same kernel code that writes core dumps upon fatal signals.
.El .El
.Sh FILES .Sh FILES
.Bl -tag -width /var/log/messages -compact .Bl -tag -width /var/log/messages -compact

View File

@ -56,13 +56,16 @@ __FBSDID("$FreeBSD$");
*/ */
#include <sys/param.h> #include <sys/param.h>
#include <sys/ptrace.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/linker_set.h> #include <sys/linker_set.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <sys/wait.h>
#include <err.h> #include <err.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -75,6 +78,7 @@ static void killed(int);
static void usage(void) __dead2; static void usage(void) __dead2;
static pid_t pid; static pid_t pid;
static bool kflag = false;
SET_DECLARE(dumpset, struct dumpers); SET_DECLARE(dumpset, struct dumpers);
@ -82,6 +86,7 @@ static int
open_corefile(char *corefile) open_corefile(char *corefile)
{ {
char fname[MAXPATHLEN]; char fname[MAXPATHLEN];
int fd;
if (corefile == NULL) { if (corefile == NULL) {
(void)snprintf(fname, sizeof(fname), "core.%d", pid); (void)snprintf(fname, sizeof(fname), "core.%d", pid);
@ -93,6 +98,45 @@ open_corefile(char *corefile)
return (fd); return (fd);
} }
static void
kcoredump(int fd, pid_t pid)
{
struct ptrace_coredump pc;
int error, res, ret, waited;
error = ptrace(PT_ATTACH, pid, NULL, 0);
if (error != 0)
err(1, "attach");
waited = waitpid(pid, &res, 0);
if (waited == -1)
err(1, "wait for STOP");
ret = 0;
memset(&pc, 0, sizeof(pc));
pc.pc_fd = fd;
pc.pc_flags = (pflags & PFLAGS_FULL) != 0 ? PC_ALL : 0;
error = ptrace(PT_COREDUMP, pid, (void *)&pc, sizeof(pc));
if (error == -1) {
warn("coredump");
ret = 1;
}
waited = waitpid(pid, &res, WNOHANG);
if (waited == -1) {
warn("wait after coredump");
ret = 1;
}
error = ptrace(PT_DETACH, pid, NULL, 0);
if (error == -1) {
warn("detach failed, check process status");
ret = 1;
}
exit(ret);
}
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
@ -104,7 +148,7 @@ main(int argc, char *argv[])
pflags = 0; pflags = 0;
corefile = NULL; corefile = NULL;
while ((ch = getopt(argc, argv, "c:f")) != -1) { while ((ch = getopt(argc, argv, "c:fk")) != -1) {
switch (ch) { switch (ch) {
case 'c': case 'c':
corefile = optarg; corefile = optarg;
@ -112,6 +156,9 @@ main(int argc, char *argv[])
case 'f': case 'f':
pflags |= PFLAGS_FULL; pflags |= PFLAGS_FULL;
break; break;
case 'k':
kflag = true;
break;
default: default:
usage(); usage();
break; break;
@ -119,10 +166,26 @@ main(int argc, char *argv[])
} }
argv += optind; argv += optind;
argc -= optind; argc -= optind;
/* XXX we should check that the pid argument is really a number */ /* XXX we should check that the pid argument is really a number */
switch (argc) { switch (argc) {
case 1: case 1:
pid = atoi(argv[0]); pid = atoi(argv[0]);
break;
case 2:
binfile = argv[0];
pid = atoi(argv[1]);
break;
default:
usage();
}
if (kflag) {
fd = open_corefile(corefile);
kcoredump(fd, pid);
}
if (argc == 1) {
name[0] = CTL_KERN; name[0] = CTL_KERN;
name[1] = KERN_PROC; name[1] = KERN_PROC;
name[2] = KERN_PROC_PATHNAME; name[2] = KERN_PROC_PATHNAME;
@ -131,13 +194,6 @@ main(int argc, char *argv[])
if (sysctl(name, 4, passpath, &len, NULL, 0) == -1) if (sysctl(name, 4, passpath, &len, NULL, 0) == -1)
errx(1, "kern.proc.pathname failure"); errx(1, "kern.proc.pathname failure");
binfile = passpath; binfile = passpath;
break;
case 2:
pid = atoi(argv[1]);
binfile = argv[0];
break;
default:
usage();
} }
efd = open(binfile, O_RDONLY, 0); efd = open(binfile, O_RDONLY, 0);
if (efd < 0) if (efd < 0)
@ -165,6 +221,7 @@ void
usage(void) usage(void)
{ {
(void)fprintf(stderr, "usage: gcore [-c core] [executable] pid\n"); (void)fprintf(stderr,
"usage: gcore [-kf] [-c core] [executable] pid\n");
exit(1); exit(1);
} }