gcore.c revision 69896
155682Smarkm/*-
278527Sassar * Copyright (c) 1992, 1993
355682Smarkm *	The Regents of the University of California.  All rights reserved.
455682Smarkm *
555682Smarkm * Redistribution and use in source and binary forms, with or without
655682Smarkm * modification, are permitted provided that the following conditions
755682Smarkm * are met:
855682Smarkm * 1. Redistributions of source code must retain the above copyright
955682Smarkm *    notice, this list of conditions and the following disclaimer.
1055682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer in the
1255682Smarkm *    documentation and/or other materials provided with the distribution.
1355682Smarkm * 3. All advertising materials mentioning features or use of this software
1455682Smarkm *    must display the following acknowledgement:
1555682Smarkm *	This product includes software developed by the University of
1655682Smarkm *	California, Berkeley and its contributors.
1755682Smarkm * 4. Neither the name of the University nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#ifndef lint
3555682Smarkmstatic const char copyright[] =
36178825Sdfr"@(#) Copyright (c) 1992, 1993\n\
3755682Smarkm	The Regents of the University of California.  All rights reserved.\n";
3855682Smarkm#endif /* not lint */
3955682Smarkm
4055682Smarkm#ifndef lint
4155682Smarkm#if 0
4278527Sassarstatic char sccsid[] = "@(#)gcore.c	8.2 (Berkeley) 9/23/93";
4378527Sassar#endif
4478527Sassarstatic const char rcsid[] =
4578527Sassar  "$FreeBSD: head/usr.bin/gcore/gcore.c 69896 2000-12-12 07:25:57Z mckusick $";
4678527Sassar#endif /* not lint */
4778527Sassar
4878527Sassar/*
4978527Sassar * Originally written by Eric Cooper in Fall 1981.
5078527Sassar * Inspired by a version 6 program by Len Levin, 1978.
5178527Sassar * Several pieces of code lifted from Bill Joy's 4BSD ps.
5278527Sassar * Most recently, hacked beyond recognition for 4.4BSD by Steven McCanne,
53178825Sdfr * Lawrence Berkeley Laboratory.
54178825Sdfr *
55178825Sdfr * Portions of this software were developed by the Computer Systems
56178825Sdfr * Engineering group at Lawrence Berkeley Laboratory under DARPA
57178825Sdfr * contract BG 91-66 and contributed to Berkeley.
58178825Sdfr */
5955682Smarkm#include <sys/param.h>
60178825Sdfr#include <sys/time.h>
61178825Sdfr#include <sys/stat.h>
6255682Smarkm#include <sys/proc.h>
6355682Smarkm#include <sys/user.h>
6455682Smarkm#include <sys/sysctl.h>
6578527Sassar
6655682Smarkm#include <machine/vmparam.h>
67178825Sdfr
6855682Smarkm#include <a.out.h>
6978527Sassar#include <elf.h>
7078527Sassar#include <err.h>
7178527Sassar#include <fcntl.h>
7278527Sassar#include <kvm.h>
7378527Sassar#include <limits.h>
7478527Sassar#include <signal.h>
7578527Sassar#include <stdio.h>
7678527Sassar#include <stdlib.h>
7778527Sassar#include <string.h>
7855682Smarkm#include <unistd.h>
7978527Sassar
8078527Sassar#include "extern.h"
8178527Sassar
8278527Sassarstatic void	core __P((int, int, struct kinfo_proc *));
8378527Sassarstatic void	datadump __P((int, int, struct kinfo_proc *, u_long, int));
8478527Sassarstatic void	killed __P((int));
8578527Sassarstatic void	restart_target __P((void));
8678527Sassarstatic void	usage __P((void)) __dead2;
8778527Sassarstatic void	userdump __P((int, struct kinfo_proc *, u_long, int));
8878527Sassar
8978527Sassarkvm_t *kd;
9078527Sassar
9178527Sassarstatic int data_offset;
9255682Smarkmstatic pid_t pid;
9355682Smarkm
9455682Smarkmint
9555682Smarkmmain(argc, argv)
96178825Sdfr	int argc;
97178825Sdfr	char *argv[];
98178825Sdfr{
99178825Sdfr	struct kinfo_proc *ki = NULL;
100178825Sdfr	struct exec exec;
101178825Sdfr	int ch, cnt, efd, fd, sflag, uid;
102178825Sdfr	char *binfile, *corefile;
103178825Sdfr	char errbuf[_POSIX2_LINE_MAX], fname[MAXPATHLEN + 1];
104178825Sdfr	int is_aout;
105178825Sdfr
106178825Sdfr	sflag = 0;
107178825Sdfr	corefile = NULL;
108178825Sdfr        while ((ch = getopt(argc, argv, "c:s")) != -1) {
109178825Sdfr                switch (ch) {
110178825Sdfr                case 'c':
111178825Sdfr			corefile = optarg;
112178825Sdfr                        break;
113178825Sdfr		case 's':
114178825Sdfr			sflag = 1;
115178825Sdfr			break;
116		default:
117			usage();
118			break;
119		}
120	}
121	argv += optind;
122	argc -= optind;
123
124	/* XXX we should check that the pid argument is really a number */
125	switch (argc) {
126	case 1:
127		pid = atoi(argv[0]);
128		asprintf(&binfile, "/proc/%d/file", pid);
129		if (binfile == NULL)
130			errx(1, "allocation failure");
131		break;
132	case 2:
133		pid = atoi(argv[1]);
134		binfile = argv[0];
135		break;
136	default:
137		usage();
138	}
139
140	efd = open(binfile, O_RDONLY, 0);
141	if (efd < 0)
142		err(1, "%s", binfile);
143
144	cnt = read(efd, &exec, sizeof(exec));
145	if (cnt != sizeof(exec))
146		errx(1, "%s exec header: %s",
147		    binfile, cnt > 0 ? strerror(EIO) : strerror(errno));
148	if (!N_BADMAG(exec)) {
149		is_aout = 1;
150		/*
151		 * This legacy a.out support uses the kvm interface instead
152		 * of procfs.
153		 */
154		kd = kvm_openfiles(0, 0, 0, O_RDONLY, errbuf);
155		if (kd == NULL)
156			errx(1, "%s", errbuf);
157
158		uid = getuid();
159
160		ki = kvm_getprocs(kd, KERN_PROC_PID, pid, &cnt);
161		if (ki == NULL || cnt != 1)
162			errx(1, "%d: not found", pid);
163
164		if (ki->ki_ruid != uid && uid != 0)
165			errx(1, "%d: not owner", pid);
166
167		if (ki->ki_stat == SZOMB)
168			errx(1, "%d: zombie", pid);
169
170		if (ki->ki_flag & P_WEXIT)
171			errx(1, "%d: process exiting", pid);
172		if (ki->ki_flag & P_SYSTEM)	/* Swapper or pagedaemon. */
173			errx(1, "%d: system process", pid);
174		if (exec.a_text != ptoa(ki->ki_tsize))
175			errx(1, "The executable %s does not belong to"
176			    " process %d!\n"
177			    "Text segment size (in bytes): executable %ld,"
178			    " process %d", binfile, pid, exec.a_text,
179			     ptoa(ki->ki_tsize));
180		data_offset = N_DATOFF(exec);
181	} else if (IS_ELF(*(Elf_Ehdr *)&exec)) {
182		is_aout = 0;
183		close(efd);
184	} else
185		errx(1, "Invalid executable file");
186
187	if (corefile == NULL) {
188		(void)snprintf(fname, sizeof(fname), "core.%d", pid);
189		corefile = fname;
190	}
191	fd = open(corefile, O_RDWR|O_CREAT|O_TRUNC, DEFFILEMODE);
192	if (fd < 0)
193		err(1, "%s", corefile);
194
195	if (sflag) {
196		signal(SIGHUP, killed);
197		signal(SIGINT, killed);
198		signal(SIGTERM, killed);
199		if (kill(pid, SIGSTOP) == -1)
200			err(1, "%d: stop signal", pid);
201		atexit(restart_target);
202	}
203
204	if (is_aout)
205		core(efd, fd, ki);
206	else
207		elf_coredump(fd, pid);
208
209	(void)close(fd);
210	exit(0);
211}
212
213/*
214 * core --
215 *	Build the core file.
216 */
217void
218core(efd, fd, ki)
219	int efd;
220	int fd;
221	struct kinfo_proc *ki;
222{
223	union {
224		struct user user;
225		char ubytes[ctob(UPAGES)];
226	} uarea;
227	int tsize = ki->ki_tsize;
228	int dsize = ki->ki_dsize;
229	int ssize = ki->ki_ssize;
230	int cnt;
231
232	/* Read in user struct */
233	cnt = kvm_read(kd, (u_long)ki->ki_addr, &uarea, sizeof(uarea));
234	if (cnt != sizeof(uarea))
235		errx(1, "read user structure: %s",
236		    cnt > 0 ? strerror(EIO) : strerror(errno));
237
238	/*
239	 * Fill in the eproc vm parameters, since these are garbage unless
240	 * the kernel is dumping core or something.
241	 */
242	uarea.user.u_kproc = *ki;
243
244	/* Dump user area */
245	cnt = write(fd, &uarea, sizeof(uarea));
246	if (cnt != sizeof(uarea))
247		errx(1, "write user structure: %s",
248		    cnt > 0 ? strerror(EIO) : strerror(errno));
249
250	/* Dump data segment */
251	datadump(efd, fd, ki, USRTEXT + ctob(tsize), dsize);
252
253	/* Dump stack segment */
254	userdump(fd, ki, USRSTACK - ctob(ssize), ssize);
255
256	/* Dump machine dependent portions of the core. */
257	md_core(kd, fd, ki);
258}
259
260void
261datadump(efd, fd, kp, addr, npage)
262	register int efd;
263	register int fd;
264	struct kinfo_proc *kp;
265	register u_long addr;
266	register int npage;
267{
268	register int cc, delta;
269	char buffer[PAGE_SIZE];
270
271	delta = data_offset - addr;
272	while (--npage >= 0) {
273		cc = kvm_uread(kd, kp, addr, buffer, PAGE_SIZE);
274		if (cc != PAGE_SIZE) {
275			/* Try to read the page from the executable. */
276			if (lseek(efd, (off_t)addr + delta, SEEK_SET) == -1)
277				err(1, "seek executable: %s", strerror(errno));
278			cc = read(efd, buffer, sizeof(buffer));
279			if (cc != sizeof(buffer)) {
280				if (cc < 0)
281					err(1, "read executable");
282				else	/* Assume untouched bss page. */
283					bzero(buffer, sizeof(buffer));
284			}
285		}
286		cc = write(fd, buffer, PAGE_SIZE);
287		if (cc != PAGE_SIZE)
288			errx(1, "write data segment: %s",
289			    cc > 0 ? strerror(EIO) : strerror(errno));
290		addr += PAGE_SIZE;
291	}
292}
293
294static void
295killed(sig)
296	int sig;
297{
298	restart_target();
299	signal(sig, SIG_DFL);
300	kill(getpid(), sig);
301}
302
303static void
304restart_target()
305{
306	kill(pid, SIGCONT);
307}
308
309void
310userdump(fd, kp, addr, npage)
311	register int fd;
312	struct kinfo_proc *kp;
313	register u_long addr;
314	register int npage;
315{
316	register int cc;
317	char buffer[PAGE_SIZE];
318
319	while (--npage >= 0) {
320		cc = kvm_uread(kd, kp, addr, buffer, PAGE_SIZE);
321		if (cc != PAGE_SIZE)
322			/* Could be an untouched fill-with-zero page. */
323			bzero(buffer, PAGE_SIZE);
324		cc = write(fd, buffer, PAGE_SIZE);
325		if (cc != PAGE_SIZE)
326			errx(1, "write stack segment: %s",
327			    cc > 0 ? strerror(EIO) : strerror(errno));
328		addr += PAGE_SIZE;
329	}
330}
331
332void
333usage()
334{
335	(void)fprintf(stderr, "usage: gcore [-s] [-c core] executable pid\n");
336	exit(1);
337}
338