Commit | Line | Data |
---|---|---|
b0bdc297 C |
1 | /* mprof_mon.c 1.1 9/14/90 11:59:04 */ |
2 | /* Copyright (c) 1987, Benjamin G. Zorn */ | |
3 | ||
4 | /* mprof_mon -- code that is attached to executing programs. | |
5 | */ | |
6 | ||
7 | #include <stdio.h> | |
8 | #include <sys/file.h> | |
9 | #include "mprof.h" | |
10 | ||
11 | #ifdef mips | |
12 | #include <filehdr.h> | |
13 | #include <syms.h> | |
14 | #include <ldfcn.h> | |
15 | ||
16 | int intloc; /* for use by assembly routines */ | |
17 | #endif | |
18 | ||
19 | /* local routines */ | |
20 | ||
21 | #ifdef mips | |
22 | pPDR getpdr(); | |
23 | #endif | |
24 | ||
25 | void mp_note_alloc(); | |
26 | void mp_note_free(); | |
27 | mpcell mp_note_parent(); | |
28 | void mp_note_leaf(); | |
29 | ||
30 | mpsstk mprof(); | |
31 | void mprof_note_free(); | |
32 | void mprof_startup(); | |
33 | void mprof_writefile(); | |
34 | void mprof_cleanup(); | |
35 | ||
36 | /* local variables */ | |
37 | ||
38 | char *mprof_filename = "mprof.data"; | |
39 | int mprof_autosave = 0; | |
40 | int mprof_file; | |
41 | bool mprof_initialized = FALSE; | |
42 | bool mprofing = TRUE; | |
43 | int mprof_create_mask = 0644; | |
44 | ||
45 | extern int main(); | |
46 | unsigned mp_root_address = CRT0_ADDRESS; | |
47 | ||
48 | int mprof_bound1 = 32; | |
49 | int mprof_bound2 = 256; | |
50 | int mprof_bound3 = 2048; | |
51 | ||
52 | int mprof_alloc_bins[MP_NUM_BINS]; | |
53 | int mprof_free_bins[MP_NUM_BINS]; | |
54 | ||
55 | ||
56 | /* mp_zero_bins() -- initialize the bins to zero | |
57 | */ | |
58 | ||
59 | void | |
60 | mp_zero_bins() | |
61 | { | |
62 | int i; | |
63 | ||
64 | for (i = 0; i < MP_NUM_BINS; i++) | |
65 | mprof_alloc_bins[i] = 0; | |
66 | for (i = 0; i < MP_NUM_BINS; i++) | |
67 | mprof_free_bins[i] = 0; | |
68 | } | |
69 | ||
70 | ||
71 | /* mp_inc_bin(bin, size) -- increment the bin of the appropriate size. | |
72 | */ | |
73 | ||
74 | void | |
75 | mp_inc_bin(bin, size) | |
76 | int bin[]; | |
77 | int size; | |
78 | { | |
79 | if (size < 0) { | |
80 | fprintf(stderr, "mp_inc_bin -- negative size\n"); | |
81 | } else if (size < MP_NUM_BINS - 2) { | |
82 | bin[size]++; | |
83 | } else { | |
84 | bin[MP_NUM_BINS - 2]++; | |
85 | bin[MP_NUM_BINS - 1] += size; | |
86 | } | |
87 | } | |
88 | ||
89 | /* mp_print_bins(file) -- print the bins to a file | |
90 | */ | |
91 | ||
92 | void | |
93 | mp_print_bins(file) | |
94 | { | |
95 | int i; | |
96 | char digits[32]; | |
97 | ||
98 | for (i = 0; i < MP_NUM_BINS; i++) { | |
99 | sprintf(digits, "%d\n", mprof_alloc_bins[i]); | |
100 | write(file, digits, strlen(digits)); | |
101 | } | |
102 | for (i = 0; i < MP_NUM_BINS; i++) { | |
103 | sprintf(digits, "%d\n", mprof_free_bins[i]); | |
104 | write(file, digits, strlen(digits)); | |
105 | } | |
106 | } | |
107 | ||
108 | ||
109 | /* mp_note_alloc -- note allocation by bin. There are currently | |
110 | * four bins with boundaries that are potentially user setable. | |
111 | */ | |
112 | ||
113 | void | |
114 | mp_note_alloc(d, nbytes) | |
115 | mpdata d; | |
116 | int nbytes; | |
117 | { | |
118 | if (nbytes <= mprof_bound1) { | |
119 | dt_b_small(d)+=nbytes; | |
120 | dt_d_small(d)+=nbytes; | |
121 | dt_n_small(d)+=1; | |
122 | } else if (nbytes <= mprof_bound2) { | |
123 | dt_b_med(d)+=nbytes; | |
124 | dt_d_med(d)+=nbytes; | |
125 | dt_n_med(d)+=1; | |
126 | } else if (nbytes <= mprof_bound3) { | |
127 | dt_b_large(d)+=nbytes; | |
128 | dt_d_large(d)+=nbytes; | |
129 | dt_n_large(d)+=1; | |
130 | } else { | |
131 | dt_b_xlarge(d)+=nbytes; | |
132 | dt_d_xlarge(d)+=nbytes; | |
133 | dt_n_xlarge(d)+=1; | |
134 | } | |
135 | } | |
136 | ||
137 | /* mp_note_free -- note when a memory block is released. | |
138 | */ | |
139 | ||
140 | void | |
141 | mp_note_free(d, nbytes) | |
142 | mpdata d; | |
143 | int nbytes; | |
144 | { | |
145 | if (nbytes <= mprof_bound1) { | |
146 | dt_d_small(d)-=nbytes; | |
147 | } else if (nbytes <= mprof_bound2) { | |
148 | dt_d_med(d)-=nbytes; | |
149 | } else if (nbytes <= mprof_bound3) { | |
150 | dt_d_large(d)-=nbytes; | |
151 | } else { | |
152 | dt_d_xlarge(d)-=nbytes; | |
153 | } | |
154 | } | |
155 | ||
156 | /* mp_note_parent -- record a caller/callee relationship. | |
157 | * Allocate a data cell and put the parent on the parents list if | |
158 | * necessary. | |
159 | */ | |
160 | ||
161 | mpcell | |
162 | mp_note_parent(p, c, nbytes) | |
163 | mpsym p, c; | |
164 | int nbytes; | |
165 | { | |
166 | mpcell ppair; | |
167 | mpdata dcell; | |
168 | ||
169 | /* if yes -- is parent already listed? | |
170 | */ | |
171 | ppair = mp_has_parent(c, p); | |
172 | ||
173 | if (!mp_null(ppair)) { | |
174 | ||
175 | /* if yes -- increment count of calls from parent | |
176 | */ | |
177 | mp_note_alloc((mpdata) mp_cdr(ppair), nbytes); | |
178 | ||
179 | } else { | |
180 | ||
181 | /* if no -- add this parent to the list of parents | |
182 | */ | |
183 | dcell = mp_new_data(); | |
184 | mp_note_alloc(dcell, nbytes); | |
185 | ppair = mp_cons((int) p, (mpcell) dcell); | |
186 | fn_parents(c) = mp_cons((int) ppair, fn_parents(c)); | |
187 | } | |
188 | ||
189 | return ppair; | |
190 | } | |
191 | ||
192 | /* mp_note_leaf -- note allocation directly in the function. | |
193 | */ | |
194 | ||
195 | void | |
196 | mp_note_leaf(l, nbytes) | |
197 | mpsym l; | |
198 | int nbytes; | |
199 | { | |
200 | mp_note_alloc(fn_lcount(l), nbytes); | |
201 | } | |
202 | ||
203 | ||
204 | ||
205 | #define MAXCALLS 5000 | |
206 | ||
207 | /* variables to record information about the monitoring | |
208 | */ | |
209 | ||
210 | int mprof_cs_maxdepth; | |
211 | int mprof_cs_sameC; | |
212 | int mprof_cs_allC; | |
213 | ||
214 | int mprof_allocC; | |
215 | int mprof_freeC; | |
216 | ||
217 | int mprof_debug = 0; | |
218 | int no_call_graph = 0; | |
219 | ||
220 | ||
221 | unsigned pcs1[MAXCALLS]; | |
222 | unsigned pcs2[MAXCALLS]; | |
223 | unsigned *fpcs; | |
224 | mpsym fsyms[MAXCALLS]; | |
225 | mpcell fpcells[MAXCALLS]; | |
226 | int fstk_i; | |
227 | unsigned *last_fpcs; | |
228 | int last_fstk_i; | |
229 | ||
230 | unsigned short_callstack[SHORT_CALLSTACK_SIZE]; | |
231 | ||
232 | ||
233 | mpsstk | |
234 | mprof(nbytes) | |
235 | int nbytes; | |
236 | { | |
237 | unsigned first_local; /* WARNING -- This MUST be the first | |
238 | * local variable in this function. | |
239 | */ | |
240 | unsigned fp; | |
241 | unsigned ret_addr; | |
242 | mpsym child, parent; | |
243 | int i, lasti, j; | |
244 | int lookupcount, samecount; | |
245 | unsigned *pcstmp; | |
246 | mpsstk leakdata; | |
247 | ||
248 | #ifdef mips | |
249 | pPDR pdr; | |
250 | #endif | |
251 | ||
252 | if (!mprof_initialized) { | |
253 | mprof_startup(); | |
254 | mprof_initialized = TRUE; | |
255 | } | |
256 | ||
257 | mprof_allocC++; | |
258 | mp_inc_bin(mprof_alloc_bins, nbytes); | |
259 | ||
260 | if (mprof_autosave && | |
261 | (mprof_allocC % mprof_autosave) == 0) { | |
262 | mprof_writefile(); | |
263 | } | |
264 | ||
265 | fstk_i = 0; | |
266 | ||
267 | /* gather return addresses from the callstack | |
268 | */ | |
269 | #ifndef mips | |
270 | fp = get_current_fp(first_local); | |
271 | ret_addr = ret_addr_from_fp(fp); | |
272 | ||
273 | /* Step back 1 frame (to the caller of malloc) | |
274 | */ | |
275 | fp = prev_fp_from_fp(fp); | |
276 | ret_addr = ret_addr_from_fp(fp); | |
277 | ||
278 | while (ret_addr > mp_root_address) { | |
279 | if (no_call_graph && (fstk_i > SHORT_CALLSTACK_SIZE)) | |
280 | break; | |
281 | ||
282 | fpcs[fstk_i] = ret_addr; | |
283 | fstk_i++; | |
284 | fp = prev_fp_from_fp(fp); | |
285 | if (fp == 0) break; | |
286 | ret_addr = ret_addr_from_fp(fp); | |
287 | } | |
288 | #else | |
289 | get31(); | |
290 | pdr = getpdr(intloc); | |
291 | getsp(); | |
292 | fp = intloc; | |
293 | ret_addr = getretaddr(&fp, pdr); /* fp is changed */ | |
294 | ||
295 | /* Step back 1 frame (to the caller of malloc) */ | |
296 | pdr = getpdr(ret_addr); | |
297 | ret_addr = getretaddr(&fp, pdr); /* fp is changed */ | |
298 | ||
299 | while (ret_addr > mp_root_address) { | |
300 | if (no_call_graph && (fstk_i > SHORT_CALLSTACK_SIZE)) | |
301 | break; | |
302 | ||
303 | fpcs[fstk_i] = ret_addr; | |
304 | fstk_i++; | |
305 | pdr = getpdr(ret_addr); | |
306 | ret_addr = getretaddr(&fp, pdr); /* fp is updated */ | |
307 | } | |
308 | #endif | |
309 | ||
310 | /* note last N addresses (short_callstack) for the leak table | |
311 | */ | |
312 | for (i = 0; i < SHORT_CALLSTACK_SIZE; i++) { | |
313 | short_callstack[i] = 0; | |
314 | } | |
315 | for (i = 0; ((i < SHORT_CALLSTACK_SIZE) && (i < fstk_i)); i++) { | |
316 | short_callstack[i] = fpcs[i]; | |
317 | } | |
318 | leakdata = mp_add_leak_table(short_callstack, nbytes); | |
319 | if (no_call_graph) { | |
320 | /* note the direct allocation | |
321 | */ | |
322 | mp_note_leaf(pc_lookup(fpcs[0]), nbytes); | |
323 | return leakdata; | |
324 | } | |
325 | ||
326 | /* note maximum stack depth | |
327 | */ | |
328 | if (fstk_i > mprof_cs_maxdepth) | |
329 | mprof_cs_maxdepth = fstk_i; | |
330 | ||
331 | /* determine the overlap with the last callstack | |
332 | */ | |
333 | i = fstk_i - 1; | |
334 | lasti = last_fstk_i - 1; | |
335 | while ((lasti > 0) && | |
336 | (i > 0) && | |
337 | (fpcs[i] == last_fpcs[lasti])) { | |
338 | i--; | |
339 | lasti--; | |
340 | } | |
341 | ||
342 | /* i is the index of the first difference of pc's in the stack | |
343 | * i+1 is the number of pc's that need to be looked up | |
344 | */ | |
345 | lookupcount = i + 1; | |
346 | ||
347 | /* put the new calls in the stack of functions | |
348 | */ | |
349 | if (lookupcount != 0) { | |
350 | for (j = fstk_i - lookupcount; j < fstk_i; j++) { | |
351 | fsyms[j] = pc_lookup(fpcs[fstk_i - j - 1]); | |
352 | } | |
353 | } | |
354 | ||
355 | samecount = fstk_i - lookupcount; | |
356 | ||
357 | mprof_cs_sameC += samecount; | |
358 | mprof_cs_allC += fstk_i; | |
359 | ||
360 | /* record the parent/child relations | |
361 | */ | |
362 | ||
363 | i = 0; | |
364 | ||
365 | for (i = 0; i < (fstk_i - 1); i++) { | |
366 | parent = fsyms[i]; | |
367 | child = fsyms[i+1]; | |
368 | if (mprof_debug) | |
369 | printf("%d -> ", fn_addr(parent)); | |
370 | if (i < (samecount - 1)) { | |
371 | mp_note_alloc((mpdata) mp_cdr(fpcells[i+1]), nbytes); | |
372 | } else { | |
373 | fpcells[i+1] = mp_note_parent(parent, child, nbytes); | |
374 | } | |
375 | } | |
376 | if (mprof_debug) | |
377 | printf("%d\n", fn_addr(child)); | |
378 | mp_note_leaf(fsyms[(fstk_i - 1)], nbytes); | |
379 | ||
380 | /* swap the last pc stack with the current one | |
381 | */ | |
382 | pcstmp = fpcs; | |
383 | fpcs = last_fpcs; | |
384 | last_fpcs = pcstmp; | |
385 | last_fstk_i = fstk_i; | |
386 | return leakdata; | |
387 | } | |
388 | ||
389 | void | |
390 | mprof_note_free(leakdata, nbytes) | |
391 | mpsstk leakdata; | |
392 | int nbytes; | |
393 | { | |
394 | mpsym f; | |
395 | unsigned addr; | |
396 | ||
397 | addr = leakdata->sstack[0]; | |
398 | ||
399 | mprof_freeC++; | |
400 | mp_inc_bin(mprof_free_bins, nbytes); | |
401 | ||
402 | f = pc_lookup(addr); | |
403 | mp_note_free(fn_lcount(f), nbytes); | |
404 | ||
405 | mp_remove_leak_table(leakdata, nbytes); | |
406 | } | |
407 | ||
408 | void | |
409 | mprof_startup() | |
410 | { | |
411 | #ifdef sun | |
412 | on_exit(mprof_exit, NULL); | |
413 | #endif | |
414 | if (strcmp(mprof_filename, "") == 0) { | |
415 | mprof_file = 1; | |
416 | } else { | |
417 | mprof_file = open(mprof_filename, | |
418 | (O_WRONLY | O_CREAT | O_TRUNC), | |
419 | mprof_create_mask); | |
420 | } | |
421 | mpstruct_init(); | |
422 | mp_zero_bins(); | |
423 | mpleak_init(); | |
424 | ||
425 | mprof_cs_maxdepth = 0; | |
426 | mprof_cs_sameC = 0; | |
427 | mprof_cs_allC = 0; | |
428 | ||
429 | mprof_allocC = 0; | |
430 | mprof_freeC = 0; | |
431 | ||
432 | last_fstk_i = 0; | |
433 | fpcs = pcs1; | |
434 | last_fpcs = pcs2; | |
435 | ||
436 | #ifdef mips | |
437 | pdrinit(); | |
438 | #endif | |
439 | ||
440 | } | |
441 | ||
442 | void | |
443 | mprof_writefile() | |
444 | { | |
445 | char stats[256]; | |
446 | extern int mprof_fmemC, mprof_dmemC, mprof_lmemC, mprof_smemC; | |
447 | ||
448 | ftruncate(mprof_file, 0); | |
449 | lseek(mprof_file, 0L, 0); | |
450 | ||
451 | sprintf(stats, "alloc=%d free=%d depth=%d same=%d all=%d\n", | |
452 | mprof_allocC, | |
453 | mprof_freeC, | |
454 | mprof_cs_maxdepth, | |
455 | mprof_cs_sameC, | |
456 | mprof_cs_allC); | |
457 | write(mprof_file, stats, strlen(stats)); | |
458 | ||
459 | sprintf(stats, "fmem=%d dmem=%d lmem=%d smem=%d\n", | |
460 | (mprof_fmemC * MPSYM_SIZE) / 1024, | |
461 | (mprof_dmemC * MPDATA_SIZE) / 1024, | |
462 | (mprof_lmemC * MPCELL_SIZE) / 1024, | |
463 | (mprof_smemC * MPSSTK_SIZE) / 1024); | |
464 | ||
465 | write(mprof_file, stats, strlen(stats)); | |
466 | ||
467 | mp_print_bins(mprof_file); | |
468 | ||
469 | mp_print_leak_table(mprof_file); | |
470 | ||
471 | mprof_print(mprof_file); | |
472 | } | |
473 | ||
474 | void | |
475 | mprof_cleanup() | |
476 | { | |
477 | if (mprof_initialized) { | |
478 | mprof_writefile(); | |
479 | close(mprof_file); | |
480 | } | |
481 | } | |
482 | ||
483 | \f | |
484 | /* external interface -- | |
485 | ||
486 | void | |
487 | set_mprof_autosave(count) -- set the autosave count of profile data | |
488 | int count; count = 0 (default) implies no autosave | |
489 | ||
490 | void | |
491 | mprof_stop() -- stop the memory profile in progress | |
492 | ||
493 | void | |
494 | mprof_restart(datafile) -- restart memory profiling | |
495 | ||
496 | */ | |
497 | ||
498 | void | |
499 | set_mprof_autosave(count) | |
500 | int count; | |
501 | { | |
502 | mprof_autosave = count; | |
503 | } | |
504 | ||
505 | ||
506 | void | |
507 | mprof_restart(datafile) | |
508 | char *datafile; | |
509 | { | |
510 | if (mprofing) | |
511 | fprintf(stderr, | |
512 | "mprof_restart -- restart ingnored; memory profiling in progress\n"); | |
513 | else { | |
514 | mprof_initialized = FALSE; | |
515 | mprofing = TRUE; | |
516 | mprof_filename = datafile; | |
517 | } | |
518 | } | |
519 | ||
520 | void | |
521 | mprof_stop() | |
522 | { | |
523 | if (!mprofing) | |
524 | fprintf(stderr, | |
525 | "mprof_stop -- stop ingnored; memory profiling not in progress\n"); | |
526 | else { | |
527 | mprof_cleanup(); | |
528 | mprofing = FALSE; | |
529 | } | |
530 | } | |
531 | ||
532 | #ifdef mips | |
533 | ||
534 | pPDR pdrarray; | |
535 | LDFILE *ldptr; | |
536 | ||
537 | pdrinit() | |
538 | { | |
539 | int i; | |
540 | SYMR asym; | |
541 | extern char **__Argv; /* hack */ | |
542 | pFDR pfd; | |
543 | ||
544 | ldptr = ldopen(__Argv[0], NULL); /* hack */ | |
545 | pdrarray = (pPDR) malloc (sizeof(PDR) * SYMHEADER(ldptr).ipdMax); | |
546 | ||
547 | #ifdef notdef /* doesn't work for libraries compiled -O */ | |
548 | /* read in pdr table */ | |
549 | for (i = 0; i < SYMHEADER(ldptr).ipdMax - 1; i++) { | |
550 | if (ldgetpd(ldptr, i, &pdrarray[i]) != SUCCESS) { | |
551 | printf("bad pdr %d\n", i); | |
552 | i--; | |
553 | continue; | |
554 | } | |
555 | } | |
556 | #endif | |
557 | ||
558 | /* indirectly read in pdr table through the file descriptor table */ | |
559 | for (pfd = PFD(ldptr); pfd < PFD(ldptr) + SYMHEADER(ldptr).ifdMax; | |
560 | pfd++) { | |
561 | for (i = pfd->ipdFirst; i < pfd->ipdFirst + pfd->cpd; i++) { | |
562 | if (ldgetpd (ldptr, i, &pdrarray[i]) != SUCCESS) { | |
563 | fprintf(stderr, "can't read pdr %d\n", i); | |
564 | exit (1); | |
565 | } | |
566 | if (pdrarray[i].isym != isymNil) { | |
567 | if (ldtbread(ldptr, pfd->csym ? pdrarray[i].isym : | |
568 | pdrarray[i].isym + SYMHEADER(ldptr).isymMax, &asym) | |
569 | != SUCCESS) { | |
570 | fprintf(stderr, "can't read symbol"); | |
571 | exit (1); | |
572 | } | |
573 | pdrarray[i].adr = asym.value; | |
574 | } | |
575 | } | |
576 | } | |
577 | ||
578 | /* This is guaranteed to be between __start and main. */ | |
579 | mp_root_address = pdrarray[1].adr - 1; | |
580 | } | |
581 | ||
582 | pPDR | |
583 | getpdr(loc) | |
584 | int loc; | |
585 | { | |
586 | int low = 0, high = SYMHEADER(ldptr).ipdMax - 1, mid; | |
587 | ||
588 | /* do binary search on address */ | |
589 | while (low <= high) { | |
590 | mid = (low + high) / 2; | |
591 | if (loc < pdrarray[mid].adr) { | |
592 | high = mid - 1; | |
593 | } else if (loc > pdrarray[mid].adr) { | |
594 | low = mid + 1; | |
595 | } else { | |
596 | return (&pdrarray[mid]); | |
597 | } | |
598 | } | |
599 | ||
600 | return (&pdrarray[low - 1]); | |
601 | ||
602 | } | |
603 | ||
604 | getretaddr(pfp, pdr) | |
605 | int *pfp; | |
606 | pPDR pdr; | |
607 | { | |
608 | int fp = *pfp; | |
609 | int retaddr; | |
610 | int saved31loc; | |
611 | ||
612 | /* return return address and update fp | |
613 | 1. I am told what my current fp and pdr is | |
614 | 2. see what the return register is | |
615 | 3. see if return reg is on stack | |
616 | 4. add the framesize to framereg to get the virtual fp | |
617 | 5. add the frameoffset to fp to get to the save register area | |
618 | 6. read the stack to get the return address | |
619 | */ | |
620 | ||
621 | if (pdr->pcreg < 0) { | |
622 | punt("return addreses not in a saved register"); | |
623 | } | |
624 | ||
625 | if (!(pdr->regmask & (1 << pdr->pcreg))) { | |
626 | /* in a register and register is not saved */ | |
627 | punt("don't know how to get register"); | |
628 | } | |
629 | ||
630 | if (pdr->framereg != 29) punt("framereg != 29"); | |
631 | fp += pdr->frameoffset; | |
632 | ||
633 | saved31loc = fp + pdr->regoffset; | |
634 | ||
635 | /* assume pcreg is 31, else have to figure out where it is in saved | |
636 | area */ | |
637 | if (pdr->pcreg != 31) punt("return reg not 31"); | |
638 | ||
639 | retaddr = *(int *) saved31loc; | |
640 | ||
641 | *pfp = fp; | |
642 | return (retaddr); | |
643 | ||
644 | } | |
645 | ||
646 | punt(str) | |
647 | char *str; | |
648 | { | |
649 | fprintf(stderr, "%s\n", str); | |
650 | exit(1); | |
651 | } | |
652 | #endif |