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