BSD 4_4_Lite2 development
[unix-history] / usr / src / contrib / mprof / mprof_mon.c
CommitLineData
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
16int intloc; /* for use by assembly routines */
17#endif
18
19/* local routines */
20
21#ifdef mips
22pPDR getpdr();
23#endif
24
25void mp_note_alloc();
26void mp_note_free();
27mpcell mp_note_parent();
28void mp_note_leaf();
29
30mpsstk mprof();
31void mprof_note_free();
32void mprof_startup();
33void mprof_writefile();
34void mprof_cleanup();
35
36/* local variables */
37
38char *mprof_filename = "mprof.data";
39int mprof_autosave = 0;
40int mprof_file;
41bool mprof_initialized = FALSE;
42bool mprofing = TRUE;
43int mprof_create_mask = 0644;
44
45extern int main();
46unsigned mp_root_address = CRT0_ADDRESS;
47
48int mprof_bound1 = 32;
49int mprof_bound2 = 256;
50int mprof_bound3 = 2048;
51
52int mprof_alloc_bins[MP_NUM_BINS];
53int mprof_free_bins[MP_NUM_BINS];
54
55
56/* mp_zero_bins() -- initialize the bins to zero
57 */
58
59void
60mp_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
74void
75mp_inc_bin(bin, size)
76int bin[];
77int 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
92void
93mp_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
113void
114mp_note_alloc(d, nbytes)
115mpdata d;
116int 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
140void
141mp_note_free(d, nbytes)
142mpdata d;
143int 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
161mpcell
162mp_note_parent(p, c, nbytes)
163mpsym p, c;
164int 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
195void
196mp_note_leaf(l, nbytes)
197mpsym l;
198int 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
210int mprof_cs_maxdepth;
211int mprof_cs_sameC;
212int mprof_cs_allC;
213
214int mprof_allocC;
215int mprof_freeC;
216
217int mprof_debug = 0;
218int no_call_graph = 0;
219
220
221unsigned pcs1[MAXCALLS];
222unsigned pcs2[MAXCALLS];
223unsigned *fpcs;
224mpsym fsyms[MAXCALLS];
225mpcell fpcells[MAXCALLS];
226int fstk_i;
227unsigned *last_fpcs;
228int last_fstk_i;
229
230unsigned short_callstack[SHORT_CALLSTACK_SIZE];
231
232
233mpsstk
234mprof(nbytes)
235int 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
389void
390mprof_note_free(leakdata, nbytes)
391mpsstk leakdata;
392int 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
408void
409mprof_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
442void
443mprof_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
474void
475mprof_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
498void
499set_mprof_autosave(count)
500int count;
501{
502 mprof_autosave = count;
503}
504
505
506void
507mprof_restart(datafile)
508char *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
520void
521mprof_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
534pPDR pdrarray;
535LDFILE *ldptr;
536
537pdrinit()
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
582pPDR
583getpdr(loc)
584int 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
604getretaddr(pfp, pdr)
605int *pfp;
606pPDR 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
646punt(str)
647char *str;
648{
649 fprintf(stderr, "%s\n", str);
650 exit(1);
651}
652#endif