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