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