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