xref: /original-bsd/usr.bin/gcore/gcore.c (revision 62cd422e)
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.7 (Berkeley) 02/06/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 }
173 
174 getword(loc)
175 	off_t loc;
176 {
177 	int word;
178 
179 	Get(kmem, loc, &word, sizeof(int));
180 	return (word);
181 }
182 
183 openfiles()
184 {
185 	kmem = open(KMEM, 0);
186 	if (kmem < 0) {
187 		perror(KMEM);
188 		exit(1);
189 	}
190 	mem = open(MEM, 0);
191 	if (mem < 0) {
192 		perror(MEM);
193 		exit(1);
194 	}
195 	swap = open(SWAP, 0);
196 	if (swap < 0) {
197 		perror(SWAP);
198 		exit(1);
199 	}
200 }
201 
202 getkvars()
203 {
204 	nlist(NLIST, nl);
205 	if (nl[0].n_type == 0) {
206 		printf("%s: No namelist\n", NLIST);
207 		exit(1);
208 	}
209 	Usrptmap = (struct pte *) nl[X_USRPTMA].n_value;
210 	usrpt = (struct pte *) nl[X_USRPT].n_value;
211 }
212 
213 /*
214  * Get the system page table entries (mapping the user page table).
215  * These are the entries Usrptmap[i .. i + szpt],
216  * where i = btokmx(p->p_p0br) and szpt = p->p_szpt.
217  * For our purposes, we can skip over the ptes mapping
218  * the text segment ptes.
219  */
220 struct pte	*syspt;		/* pte's from Usrptmap */
221 int		nsysptes;
222 
223 getsyspt(p)
224 	register struct proc *p;
225 {
226 	nsysptes = p->p_szpt - (p->p_tsize / NPTEPG);
227 	syspt = (struct pte *) malloc(nsysptes * sizeof(struct pte));
228 	if (syspt == NULL)
229 		panic("can't alloc %d page table entries", nsysptes);
230 	Get(kmem, &Usrptmap[btokmx(p->p_p0br) + (p->p_tsize / NPTEPG)],
231 		syspt, nsysptes * sizeof(struct pte));
232 }
233 
234 /*
235  * Get the user page table for a segment.
236  * seg 0 = p0 (not including text)
237  * seg 1 = p1 (stack and u area)
238  * The system pt is consulted to find each page of user ptes.
239  */
240 struct pte *
241 getpt(p, seg)
242 	register struct proc *p;
243 	int seg;
244 {
245 	register int i;
246 	register struct pte *spt;
247 	struct pte *pt;
248 	int nptes, offset, n;
249 
250 	if (seg == 0) {
251 		nptes = p->p_dsize;
252 		spt = syspt;
253 		offset = p->p_tsize % NPTEPG;
254 	} else {
255 		nptes = p->p_ssize + UPAGES;
256 		spt = syspt + (nsysptes - ctopt(nptes));
257 		offset = -nptes % NPTEPG;
258 		if (offset < 0)
259 			offset += NPTEPG;
260 	}
261 	pt = (struct pte *) malloc(nptes * sizeof(struct pte));
262 	if (pt == NULL)
263 		panic("can't alloc %d page table entries", nptes);
264 	for (i = 0; i < nptes; i += n) {
265 		n = min(NPTEPG - offset, nptes - i);
266 		Get(mem, ctob(spt->pg_pfnum) + offset * sizeof(struct pte),
267 		     pt + i, n * sizeof(struct pte));
268 		spt++;
269 		offset = 0;
270 	}
271 	return (pt);
272 }
273 
274 /*
275  * Build the core file.
276  */
277 core(p)
278 	register struct proc *p;
279 {
280 	register struct pte *p0, *p1;
281 
282 	if (p->p_flag & SLOAD) {		/* page tables are resident */
283 		getsyspt(p);
284 		p0 = getpt(p, 0);
285 		p1 = getpt(p, 1);
286 #ifdef	DEBUG
287 		showpt(syspt, nsysptes, "system");
288 		showpt(p0, p->p_dsize, "p0");
289 		showpt(p1, p->p_ssize + UPAGES, "p1");
290 #endif
291 	} else
292 		p0 = p1 = NULL;			/* not actually used */
293 	getu(p, &p1[p->p_ssize]);			/* u area */
294 	getseg(p, p->p_dsize, p0, &u.u_dmap, 0);	/* data */
295 	getseg(p, p->p_ssize, p1, &u.u_smap, 1);	/* stack */
296 	if (p->p_flag & SLOAD) {
297 		free((char *) syspt);
298 		free((char *) p0);
299 		free((char *) p1);
300 	}
301 }
302 
303 /*
304  * Get the u area.
305  * Keeps around the u structure for later use
306  * (the data and stack disk map structures).
307  */
308 getu(p, pages)
309 	register struct proc *p;
310 	register struct pte *pages;
311 {
312 	register int i;
313 
314 	if ((p->p_flag & SLOAD) == 0) {
315 		Get(swap, ctob(p->p_swaddr), uarea, ctob(UPAGES));
316 		write(cor, uarea, ctob(UPAGES));
317 		return;
318 	}
319 	for (i = 0; i < UPAGES; i += CLSIZE) {
320 		Get(mem, ctob(pages[i].pg_pfnum), uarea[i], ctob(CLSIZE));
321 		write(cor, uarea[i], ctob(CLSIZE));
322 	}
323 }
324 
325 /*
326  * Copy a segment to the core file.
327  * The segment is described by its size in clicks,
328  * its page table, its disk map, and whether or not
329  * it grows backwards.
330  * Note that the page table address is allowed to be meaningless
331  * if the process is swapped out.
332  */
333 getseg(p, segsize, pages, map, rev)
334 	register struct proc *p;
335 	int segsize;
336 	register struct pte *pages;
337 	struct dmap *map;
338 	int rev;
339 {
340 	register int i;
341 	struct dblock db;
342 	int size;
343 	char buf[ctob(CLSIZE)];
344 
345 	for (i = 0; i < segsize; i += CLSIZE) {
346 		size = min(CLSIZE, segsize - i);
347 		if ((p->p_flag & SLOAD) == 0 || pages[i].pg_fod ||
348 		    pages[i].pg_pfnum == 0) {
349 			vstodb(i, size, map, &db, rev);
350 			Get(swap, ctob(db.db_base), buf, ctob(size));
351 			write(cor, buf, ctob(size));
352 		} else {
353 			Get(mem, ctob(pages[i].pg_pfnum), buf, ctob(size));
354 			write(cor, buf, ctob(size));
355 		}
356 	}
357 }
358 
359 /*
360  * Given a base/size pair in virtual swap area,
361  * return a physical base/size pair which is the
362  * (largest) initial, physically contiguous block.
363  */
364 vstodb(vsbase, vssize, dmp, dbp, rev)
365 	register int vsbase;
366 	int vssize;
367 	struct dmap *dmp;
368 	register struct dblock *dbp;
369 	int rev;
370 {
371 	register int blk = dmmin;
372 	register swblk_t *ip = dmp->dm_map;
373 
374 	if (vsbase < 0 || vsbase + vssize > dmp->dm_size)
375 		panic("can't make sense out of virtual memory (gcore probably needs to be recompiled)");
376 	while (vsbase >= blk) {
377 		vsbase -= blk;
378 		if (blk < dmmax)
379 			blk *= 2;
380 		ip++;
381 	}
382 	if (*ip <= 0 || *ip + blk > nswap)
383 		panic("vstodb *ip");
384 	dbp->db_size = MIN(vssize, blk - vsbase);
385 	dbp->db_base = *ip + (rev ? blk - (vsbase + dbp->db_size) : vsbase);
386 }
387 
388 #ifdef lint
389 /*VARARGS1*/
390 panic(fmt)
391 	char *fmt;
392 {
393 
394 	fmt = fmt;
395 	longjmp(cont_frame, 1);
396 }
397 #else /* lint */
398 panic(va_alist)
399 	va_dcl
400 {
401 	va_list ap;
402 	char *fmt;
403 
404 	va_start(ap);
405 	fmt = va_arg(ap, char *);
406 	(void) vprintf(fmt, ap);
407 	va_end(ap);
408 	(void) printf("\n");
409 	longjmp(cont_frame, 1);
410 }
411 #endif /* lint */
412 
413 /*
414  * Debugging routine to print out page table.
415  */
416 #ifdef	DEBUG
417 showpt(pt, n, s)
418 	struct pte *pt;
419 	int n;
420 	char *s;
421 {
422 	register struct pte *p;
423 	register int i;
424 
425 	printf("*** %s page table\n", s);
426 	for (i = 0, p = pt; i < n; i++, p++)
427 		if (! p->pg_fod)
428 			printf("%d: %x\n", i, p->pg_pfnum);
429 }
430 #endif
431