xref: /original-bsd/usr.bin/gcore/gcore.c (revision ed5ac44c)
1 /*
2  * Copyright (c) 1982 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1982 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)gcore.c	5.10 (Berkeley) 05/02/91";
16 #endif /* not lint */
17 
18 /*
19  * gcore - get core images of running processes
20  *
21  * Author: Eric Cooper
22  * Written: Fall 1981.
23  *
24  * Inspired by a version 6 program by Len Levin, 1978.
25  * Several pieces of code lifted from Bill Joy's 4BSD ps.
26  *
27  * Permission to copy or modify this program in whole or in part is hereby
28  * granted, provided that the above credits are preserved.
29  *
30  * This code performs a simple simulation of the virtual memory system in user
31  * code.  If the virtual memory system changes, this program must be modified
32  * accordingly.  It must also be recompiled whenever system data structures
33  * change.
34  */
35 #include <machine/pte.h>
36 #include <sys/param.h>
37 #include <sys/user.h>
38 #include <sys/proc.h>
39 #include <sys/vm.h>
40 #include <setjmp.h>
41 #include <stdio.h>
42 #include <nlist.h>
43 #include <varargs.h>
44 
45 /* Various macros for efficiency. */
46 
47 #define	min(a, b)	(a < b ? a : b)
48 
49 #define	Seek(f, pos) {\
50 	if (lseek(f, (long) (pos), 0) != (long) (pos)) \
51 		panic("seek error"); \
52 }
53 
54 #define	Read(f, addr, n) {\
55 	if (read(f, (char *) (addr), (int) (n)) != (int) (n)) \
56 		panic("read error"); \
57 }
58 
59 #define	Get(f, pos, addr, n) {\
60 	Seek(f, pos); \
61 	Read(f, addr, n); \
62 }
63 
64 struct	nlist nl[] = {
65 	{ "_proc" },
66 #define	X_PROC		0
67 	{ "_Usrptmap" },
68 #define	X_USRPTMA	1
69 	{ "_usrpt" },
70 #define	X_USRPT		2
71 	{ "_nswap" },
72 #define	X_NSWAP		3
73 	{ "_nproc" },
74 #define	X_NPROC		4
75 	{ "_dmmin" },
76 #define	X_DMMIN		5
77 	{ "_dmmax" },
78 #define	X_DMMAX		6
79 	{ 0 },
80 };
81 
82 #define FEW	20		/* for fewer system calls */
83 struct	proc proc[FEW];
84 
85 union {
86 	struct user user;
87 	char upages[UPAGES][NBPG];
88 } user;
89 #define u	user.user
90 #define uarea	user.upages
91 
92 #define NLIST	"/vmunix"
93 #define KMEM	"/dev/kmem"
94 #define MEM	"/dev/mem"
95 #define SWAP	"/dev/drum"	/* "/dev/swap" on some systems */
96 
97 int	nproc;
98 int	nswap;
99 int	dmmin, dmmax;
100 struct	pte *Usrptmap, *usrpt;
101 char	coref[20];
102 int	kmem, mem, swap, cor;
103 jmp_buf	cont_frame;
104 
105 main(argc, argv)
106 	int argc;
107 	char **argv;
108 {
109 	register int i, j;
110 	register struct proc *p;
111 	off_t procbase, procp;
112 	int pid, uid;
113 	char c;
114 
115 	if (argc < 2) {
116 		printf("Usage: %s pid ...\n", argv[0]);
117 		exit(1);
118 	}
119 	openfiles();
120 	getkvars();
121 	procbase = getword(nl[X_PROC].n_value);
122 	nproc = getword(nl[X_NPROC].n_value);
123 	nswap = getword(nl[X_NSWAP].n_value);
124 	dmmin = getword(nl[X_DMMIN].n_value);
125 	dmmax = getword(nl[X_DMMAX].n_value);
126 	while (--argc > 0) {
127 		if ((pid = atoi(*++argv)) <= 0 || setjmp(cont_frame))
128 			continue;
129 		printf("%d: ", pid);
130 		procp = procbase;
131 		for (i = 0; i < nproc; i += FEW) {
132 			Seek(kmem, procp);
133 			j = nproc - i;
134 			if (j > FEW)
135 				j = FEW;
136 			j *= sizeof(struct proc);
137 			Read(kmem, (char *) proc, j);
138 			procp += j;
139 			for (j = j / sizeof(struct proc) - 1; j >= 0; j--) {
140 				p = &proc[j];
141 				if (p->p_pid == pid)
142 					goto found;
143 			}
144 		}
145 		printf("Process not found.\n");
146 		continue;
147 	found:
148 		if (p->p_uid != (uid = getuid()) && uid != 0) {
149 			printf("Not owner.\n");
150 			continue;
151 		}
152 		if (p->p_stat == SZOMB) {
153 			printf("Zombie.\n");
154 			continue;
155 		}
156 		if (p->p_flag & SWEXIT)
157 			printf("Warning: process exiting.\n");
158 		if (p->p_flag & SSYS) {
159 			printf("System process.\n");
160 			/* i.e. swapper or pagedaemon */
161 			continue;
162 		}
163 		sprintf(coref, "core.%d", pid);
164 		if ((cor = creat(coref, 0666)) < 0) {
165 			perror(coref);
166 			exit(1);
167 		}
168 		core(p);
169 		close(cor);
170 		printf("%s dumped\n", coref);
171 	}
172 	exit(0);
173 }
174 
175 getword(loc)
176 	off_t loc;
177 {
178 	int word;
179 
180 	Get(kmem, loc, &word, sizeof(int));
181 	return (word);
182 }
183 
184 openfiles()
185 {
186 	kmem = open(KMEM, 0);
187 	if (kmem < 0) {
188 		perror(KMEM);
189 		exit(1);
190 	}
191 	mem = open(MEM, 0);
192 	if (mem < 0) {
193 		perror(MEM);
194 		exit(1);
195 	}
196 	swap = open(SWAP, 0);
197 	if (swap < 0) {
198 		perror(SWAP);
199 		exit(1);
200 	}
201 }
202 
203 getkvars()
204 {
205 	nlist(NLIST, nl);
206 	if (nl[0].n_type == 0) {
207 		printf("%s: No namelist\n", NLIST);
208 		exit(1);
209 	}
210 	Usrptmap = (struct pte *) nl[X_USRPTMA].n_value;
211 	usrpt = (struct pte *) nl[X_USRPT].n_value;
212 }
213 
214 /*
215  * Get the system page table entries (mapping the user page table).
216  * These are the entries Usrptmap[i .. i + szpt],
217  * where i = btokmx(p->p_p0br) and szpt = p->p_szpt.
218  * For our purposes, we can skip over the ptes mapping
219  * the text segment ptes.
220  */
221 struct pte	*syspt;		/* pte's from Usrptmap */
222 int		nsysptes;
223 
224 getsyspt(p)
225 	register struct proc *p;
226 {
227 	nsysptes = p->p_szpt - (p->p_tsize / NPTEPG);
228 	syspt = (struct pte *) malloc(nsysptes * sizeof(struct pte));
229 	if (syspt == NULL)
230 		panic("can't alloc %d page table entries", nsysptes);
231 	Get(kmem, &Usrptmap[btokmx(p->p_p0br) + (p->p_tsize / NPTEPG)],
232 		syspt, nsysptes * sizeof(struct pte));
233 }
234 
235 /*
236  * Get the user page table for a segment.
237  * seg 0 = p0 (not including text)
238  * seg 1 = p1 (stack and u area)
239  * The system pt is consulted to find each page of user ptes.
240  */
241 struct pte *
242 getpt(p, seg)
243 	register struct proc *p;
244 	int seg;
245 {
246 	register int i;
247 	register struct pte *spt;
248 	struct pte *pt;
249 	int nptes, offset, n;
250 
251 	if (seg == 0) {
252 		nptes = p->p_dsize;
253 		spt = syspt;
254 		offset = p->p_tsize % NPTEPG;
255 	} else {
256 		nptes = p->p_ssize + UPAGES;
257 		spt = syspt + (nsysptes - ctopt(nptes));
258 		offset = -nptes % NPTEPG;
259 		if (offset < 0)
260 			offset += NPTEPG;
261 	}
262 	pt = (struct pte *) malloc(nptes * sizeof(struct pte));
263 	if (pt == NULL)
264 		panic("can't alloc %d page table entries", nptes);
265 	for (i = 0; i < nptes; i += n) {
266 		n = min(NPTEPG - offset, nptes - i);
267 		Get(mem, ctob(spt->pg_pfnum) + offset * sizeof(struct pte),
268 		     pt + i, n * sizeof(struct pte));
269 		spt++;
270 		offset = 0;
271 	}
272 	return (pt);
273 }
274 
275 /*
276  * Build the core file.
277  */
278 core(p)
279 	register struct proc *p;
280 {
281 	register struct pte *p0, *p1;
282 
283 	if (p->p_flag & SLOAD) {		/* page tables are resident */
284 		getsyspt(p);
285 		p0 = getpt(p, 0);
286 		p1 = getpt(p, 1);
287 #ifdef	DEBUG
288 		showpt(syspt, nsysptes, "system");
289 		showpt(p0, p->p_dsize, "p0");
290 		showpt(p1, p->p_ssize + UPAGES, "p1");
291 #endif
292 	} else
293 		p0 = p1 = NULL;			/* not actually used */
294 	getu(p, &p1[p->p_ssize]);			/* u area */
295 	getseg(p, p->p_dsize, p0, &u.u_dmap, 0);	/* data */
296 	getseg(p, p->p_ssize, p1, &u.u_smap, 1);	/* stack */
297 	if (p->p_flag & SLOAD) {
298 		free((char *) syspt);
299 		free((char *) p0);
300 		free((char *) p1);
301 	}
302 }
303 
304 /*
305  * Get the u area.
306  * Keeps around the u structure for later use
307  * (the data and stack disk map structures).
308  */
309 getu(p, pages)
310 	register struct proc *p;
311 	register struct pte *pages;
312 {
313 	register int i;
314 
315 	if ((p->p_flag & SLOAD) == 0) {
316 		Get(swap, ctob(p->p_swaddr), uarea, ctob(UPAGES));
317 		write(cor, uarea, ctob(UPAGES));
318 		return;
319 	}
320 	for (i = 0; i < UPAGES; i += CLSIZE) {
321 		Get(mem, ctob(pages[i].pg_pfnum), uarea[i], ctob(CLSIZE));
322 		write(cor, uarea[i], ctob(CLSIZE));
323 	}
324 }
325 
326 /*
327  * Copy a segment to the core file.
328  * The segment is described by its size in clicks,
329  * its page table, its disk map, and whether or not
330  * it grows backwards.
331  * Note that the page table address is allowed to be meaningless
332  * if the process is swapped out.
333  */
334 getseg(p, segsize, pages, map, rev)
335 	register struct proc *p;
336 	int segsize;
337 	register struct pte *pages;
338 	struct dmap *map;
339 	int rev;
340 {
341 	register int i;
342 	struct dblock db;
343 	int size;
344 	char buf[ctob(CLSIZE)];
345 
346 	for (i = 0; i < segsize; i += CLSIZE) {
347 		size = min(CLSIZE, segsize - i);
348 		if ((p->p_flag & SLOAD) == 0 || pages[i].pg_fod ||
349 		    pages[i].pg_pfnum == 0) {
350 			vstodb(i, size, map, &db, rev);
351 			Get(swap, ctob(db.db_base), buf, ctob(size));
352 			write(cor, buf, ctob(size));
353 		} else {
354 			Get(mem, ctob(pages[i].pg_pfnum), buf, ctob(size));
355 			write(cor, buf, ctob(size));
356 		}
357 	}
358 }
359 
360 /*
361  * Given a base/size pair in virtual swap area,
362  * return a physical base/size pair which is the
363  * (largest) initial, physically contiguous block.
364  */
365 vstodb(vsbase, vssize, dmp, dbp, rev)
366 	register int vsbase;
367 	int vssize;
368 	struct dmap *dmp;
369 	register struct dblock *dbp;
370 	int rev;
371 {
372 	register int blk = dmmin;
373 	register swblk_t *ip = dmp->dm_map;
374 
375 	if (vsbase < 0 || vsbase + vssize > dmp->dm_size)
376 		panic("can't make sense out of virtual memory (gcore probably needs to be recompiled)");
377 	while (vsbase >= blk) {
378 		vsbase -= blk;
379 		if (blk < dmmax)
380 			blk *= 2;
381 		ip++;
382 	}
383 	if (*ip <= 0 || *ip + blk > nswap)
384 		panic("vstodb *ip");
385 	dbp->db_size = MIN(vssize, blk - vsbase);
386 	dbp->db_base = *ip + (rev ? blk - (vsbase + dbp->db_size) : vsbase);
387 }
388 
389 #ifdef lint
390 /*VARARGS1*/
391 panic(fmt)
392 	char *fmt;
393 {
394 
395 	fmt = fmt;
396 	longjmp(cont_frame, 1);
397 }
398 #else /* lint */
399 panic(va_alist)
400 	va_dcl
401 {
402 	va_list ap;
403 	char *fmt;
404 
405 	va_start(ap);
406 	fmt = va_arg(ap, char *);
407 	(void) vprintf(fmt, ap);
408 	va_end(ap);
409 	(void) printf("\n");
410 	longjmp(cont_frame, 1);
411 }
412 #endif /* lint */
413 
414 /*
415  * Debugging routine to print out page table.
416  */
417 #ifdef	DEBUG
418 showpt(pt, n, s)
419 	struct pte *pt;
420 	int n;
421 	char *s;
422 {
423 	register struct pte *p;
424 	register int i;
425 
426 	printf("*** %s page table\n", s);
427 	for (i = 0, p = pt; i < n; i++, p++)
428 		if (! p->pg_fod)
429 			printf("%d: %x\n", i, p->pg_pfnum);
430 }
431 #endif
432