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