Changeset View
Changeset View
Standalone View
Standalone View
usr.bin/gcore/gcore.c
Show First 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||||||||
* Lawrence Berkeley Laboratory. | * Lawrence Berkeley Laboratory. | ||||||||||
* | * | ||||||||||
* Portions of this software were developed by the Computer Systems | * Portions of this software were developed by the Computer Systems | ||||||||||
* Engineering group at Lawrence Berkeley Laboratory under DARPA | * Engineering group at Lawrence Berkeley Laboratory under DARPA | ||||||||||
* contract BG 91-66 and contributed to Berkeley. | * contract BG 91-66 and contributed to Berkeley. | ||||||||||
*/ | */ | ||||||||||
#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> | ||||||||||
#include <unistd.h> | #include <unistd.h> | ||||||||||
#include "extern.h" | #include "extern.h" | ||||||||||
int pflags; | int pflags; | ||||||||||
static void killed(int); | 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); | ||||||||||
static int | |||||||||||
open_corefile(char *corefile) | |||||||||||
{ | |||||||||||
char fname[MAXPATHLEN]; | |||||||||||
int fd; | |||||||||||
if (corefile == NULL) { | |||||||||||
(void)snprintf(fname, sizeof(fname), "core.%d", pid); | |||||||||||
corefile = fname; | |||||||||||
} | |||||||||||
fd = open(corefile, O_RDWR | O_CREAT | O_TRUNC, DEFFILEMODE); | |||||||||||
if (fd < 0) | |||||||||||
err(1, "%s", corefile); | |||||||||||
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, 0); | |||||||||||
markj: Shouldn't the wait happen only if PT_COREDUMP was successful? | |||||||||||
Done Inline ActionsNo, in all cases when the victim thread was unsuspended. As you noted in ptrace(2) comment, it is hard for userspace to know exactly when to wait, but also it is quite dirty for kernel either to try to suppress clearing of P_WAITED, or to wait internally. I think the acceptable solution is to wait there with WNOHANG. It would eat stray notification if there is any, and waitpid(2) returns 0 when there was no process to report. I admit that it is inelegant but it is the least evil as I see the problem. kib: No, in all cases when the victim thread was unsuspended. As you noted in ptrace(2) comment, it… | |||||||||||
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; | |||||||||||
Done Inline Actions
markj: | |||||||||||
} | |||||||||||
exit(ret); | |||||||||||
} | |||||||||||
int | int | ||||||||||
main(int argc, char *argv[]) | main(int argc, char *argv[]) | ||||||||||
{ | { | ||||||||||
int ch, efd, fd, name[4]; | int ch, efd, fd, name[4]; | ||||||||||
char *binfile, *corefile; | char *binfile, *corefile; | ||||||||||
char passpath[MAXPATHLEN], fname[MAXPATHLEN]; | char passpath[MAXPATHLEN]; | ||||||||||
struct dumpers **d, *dumper; | struct dumpers **d, *dumper; | ||||||||||
size_t len; | size_t len; | ||||||||||
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; | ||||||||||
break; | break; | ||||||||||
case 'f': | case 'f': | ||||||||||
pflags |= PFLAGS_FULL; | pflags |= PFLAGS_FULL; | ||||||||||
break; | break; | ||||||||||
case 'k': | |||||||||||
kflag = true; | |||||||||||
break; | |||||||||||
default: | default: | ||||||||||
usage(); | usage(); | ||||||||||
break; | break; | ||||||||||
} | } | ||||||||||
} | } | ||||||||||
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; | ||||||||||
name[3] = pid; | name[3] = pid; | ||||||||||
len = sizeof(passpath); | len = sizeof(passpath); | ||||||||||
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) | ||||||||||
err(1, "%s", binfile); | err(1, "%s", binfile); | ||||||||||
dumper = NULL; | dumper = NULL; | ||||||||||
SET_FOREACH(d, dumpset) { | SET_FOREACH(d, dumpset) { | ||||||||||
lseek(efd, 0, SEEK_SET); | lseek(efd, 0, SEEK_SET); | ||||||||||
if (((*d)->ident)(efd, pid, binfile)) { | if (((*d)->ident)(efd, pid, binfile)) { | ||||||||||
dumper = (*d); | dumper = (*d); | ||||||||||
lseek(efd, 0, SEEK_SET); | lseek(efd, 0, SEEK_SET); | ||||||||||
break; | break; | ||||||||||
} | } | ||||||||||
} | } | ||||||||||
if (dumper == NULL) | if (dumper == NULL) | ||||||||||
errx(1, "Invalid executable file"); | errx(1, "Invalid executable file"); | ||||||||||
if (corefile == NULL) { | fd = open_corefile(corefile); | ||||||||||
(void)snprintf(fname, sizeof(fname), "core.%d", pid); | |||||||||||
corefile = fname; | |||||||||||
} | |||||||||||
fd = open(corefile, O_RDWR|O_CREAT|O_TRUNC, DEFFILEMODE); | |||||||||||
if (fd < 0) | |||||||||||
err(1, "%s", corefile); | |||||||||||
dumper->dump(efd, fd, pid); | dumper->dump(efd, fd, pid); | ||||||||||
(void)close(fd); | (void)close(fd); | ||||||||||
(void)close(efd); | (void)close(efd); | ||||||||||
exit(0); | exit(0); | ||||||||||
} | } | ||||||||||
void | 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); | ||||||||||
} | } |
Shouldn't the wait happen only if PT_COREDUMP was successful?