|
|
1.1 root 1: /*
2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22: /*
23: * @OSF_COPYRIGHT@
24: */
25: /*
26: * HISTORY
27: *
28: * Revision 1.1.1.1 1998/09/22 21:05:49 wsanchez
29: * Import of Mac OS X kernel (~semeria)
30: *
31: * Revision 1.1.1.1 1998/03/07 02:26:08 wsanchez
32: * Import of OSF Mach kernel (~mburg)
33: *
34: * Revision 1.1.5.1 1995/01/06 19:53:45 devrcs
35: * mk6 CR668 - 1.3b26 merge
36: * new file for mk6
37: * [1994/10/12 22:25:24 dwm]
38: *
39: * Revision 1.1.2.2 1994/05/16 19:19:22 meissner
40: * Protect against hash_ptr being null in _profile_update_stats.
41: * [1994/05/16 17:23:53 meissner]
42: *
43: * Remove _profile_cnt_to_hex, _profile_strbuffer.
44: * _profile_print_stats now takes const pointers.
45: * Use the new 64-bit arithmetic support instead of converting to double.
46: * Add _profile_merge_stats to merge statistics.
47: * [1994/04/28 21:45:04 meissner]
48: *
49: * If MACH_ASSERT is on in server or kernel, turn on profiling printfs.
50: * Print out fractional digits for average # of hash searches in stats.
51: * Update overflow_ticks for # times the lprofil counter overflows into high word.
52: * Don't make sizes of C/asm structures a const array, since it has pointers in it.
53: * Add support for converting 64 bit ints to a string.
54: * Use PROF_CNT_TO_DECIMAL where possible instead of PROF_CNT_TO_LDOUBLE.
55: * [1994/04/20 15:47:02 meissner]
56: *
57: * Revision 1.1.2.1 1994/04/08 17:51:51 meissner
58: * no change
59: * [1994/04/08 02:11:40 meissner]
60: *
61: * Make most stats 64 bits, except for things like memory allocation.
62: * [1994/04/02 14:58:28 meissner]
63: *
64: * Add some printfs under #idef DEBUG_PROFILE.
65: * [1994/03/29 21:00:11 meissner]
66: *
67: * Further changes for gprof/prof overflow support.
68: * Add overflow support for {gprof,prof,old,dummy}_mcount counters.
69: * [1994/03/17 20:13:31 meissner]
70: *
71: * Add gprof/prof overflow support
72: * [1994/03/17 14:56:51 meissner]
73: *
74: * Use memset instead of bzero.
75: * [1994/02/28 23:56:10 meissner]
76: *
77: * Add size of histogram counters & unused fields to profile_profil struct
78: * [1994/02/17 21:41:50 meissner]
79: *
80: * Allocate slop space for server in addition to microkernel.
81: * Add 3rd argument to _profile_print_stats for profil info.
82: * Print # histogram ticks too low/too high for server/mk.
83: * [1994/02/16 22:38:18 meissner]
84: *
85: * Calculate percentages for # of hash buckets.
86: * [1994/02/11 16:52:04 meissner]
87: *
88: * Print stats as an unsigned number.
89: * [1994/02/07 18:47:05 meissner]
90: *
91: * For kernel and server, include <kern/assert.h> not <assert.h>.
92: * Always do assert on comparing asm vs. C structure sizes.
93: * Add _profile_reset to reset profiling information.
94: * Add _profile_update_stats to update the statistics.
95: * Move _gprof_write code that updates hash stats to _profile_update_stats.
96: * Don't allocate space for basic block support just yet.
97: * Add support for range checking the gprof arc {from,self}pc addresses.
98: * _profile_debug now calls _profile_update_stats.
99: * Print how many times the acontext was locked.
100: * If DEBUG_PROFILE is defined, set pv->debug to 1.
101: * Expand copyright.
102: * [1994/02/07 12:41:03 meissner]
103: *
104: * Keep track of the number of times the kernel overflows the HISTCOUNTER counter.
105: * [1994/02/03 20:13:28 meissner]
106: *
107: * Add stats for {user,kernel,idle} mode in the kernel.
108: * [1994/02/03 15:17:31 meissner]
109: *
110: * Print unused stats in hex as well as decimal.
111: * [1994/02/03 14:52:20 meissner]
112: *
113: * _profile_print_stats no longer takes profile_{vars,md} pointer arguments.
114: * If stream is NULL, _profile_print_stats will use stdout.
115: * Separate _profile_update_stats from _gprof_write.
116: * [1994/02/03 00:58:55 meissner]
117: *
118: * Combine _profile_{vars,stats,md}; Allow more than one _profile_vars.
119: * [1994/02/01 12:04:01 meissner]
120: *
121: * Add allocation flag to _profile_md_init.
122: * Fix core dumps in _profile_print_stats if no profile_vars ptr passed.
123: * Print numbers in 12 columns, not 8.
124: * Print my_cpu/max_cpu if max_cpu != 0.
125: * Make allocations print like other stats.
126: * Use ACONTEXT_FIRST to start loop on, not ACONTEXT_PROF.
127: * [1994/01/28 23:33:26 meissner]
128: *
129: * Move callback pointers into separate allocation context.
130: * Add size fields for other structures to profile-vars.
131: * [1994/01/26 20:23:37 meissner]
132: *
133: * Allocate initial memory at startup.
134: * Print structure sizes and version number when printing stats.
135: * Initialize size fields and version numbers.
136: * Allocation context pointers moved to _profile_vars.
137: * [1994/01/25 01:46:04 meissner]
138: *
139: * Move init code here from assembly language.
140: * [1994/01/22 01:13:21 meissner]
141: *
142: * Include <profile/profile-internal.h> instead of "profile-md.h".
143: * [1994/01/20 20:56:49 meissner]
144: *
145: * Fixup copyright.
146: * [1994/01/18 23:08:02 meissner]
147: *
148: * Rename profile.h -> profile-md.h.
149: * [1994/01/18 19:44:57 meissner]
150: *
151: * Write out stats unused fields.
152: * Make _prof_write write out the prof stats gprof collects.
153: * [1994/01/15 18:40:37 meissner]
154: *
155: * Remove debug code called from profile-asm.s.
156: * Always print out the # of profil buckets.
157: * [1994/01/15 00:59:06 meissner]
158: *
159: * Fix typo.
160: * [1994/01/04 16:34:46 meissner]
161: *
162: * Move max hash bucket calculation into _gprof_write & put info in stats structure.
163: * [1994/01/04 16:15:17 meissner]
164: *
165: * Use _profile_printf to write diagnostics; add diag_stream to hold stream to write to.
166: * [1994/01/04 15:37:46 meissner]
167: *
168: * Correctly handle case where more than one allocation context was
169: * allocated due to multiple threads.
170: * Cast stats to long for output.
171: * Print number of profil buckets field in _profile_stats.
172: * Add support for GFUNC allocation context.
173: * [1994/01/04 14:26:00 meissner]
174: *
175: * CR 10198 - Initial version.
176: * [1994/01/01 22:44:10 meissne
177: *
178: * $EndLog$
179: */
180:
181: #include <profiling/profile-internal.h>
182: #include <stdlib.h>
183: #include <string.h>
184:
185: #if defined(MACH_KERNEL) || defined(_KERNEL)
186:
187: #include <mach_assert.h>
188: #if MACH_ASSERT && !defined(DEBUG_PROFILE)
189: #define DEBUG_PROFILE 1
190: #endif
191:
192: extern int printf(const char *, ...);
193: extern void panic(const char *);
194: #else
195: #include <assert.h>
196: #define panic(str) exit(1)
197: #endif
198:
199: #ifndef PROFILE_NUM_FUNCS
200: #define PROFILE_NUM_FUNCS 2000
201: #endif
202:
203: #ifndef PROFILE_NUM_ARCS
204: #define PROFILE_NUM_ARCS 8000
205: #endif
206:
207: /*
208: * Information passed on from profile-asm.s
209: */
210:
211: extern int _profile_do_stats;
212: extern size_t _profile_size;
213: extern size_t _profile_stats_size;
214: extern size_t _profile_md_size;
215: extern size_t _profile_profil_size;
216: extern size_t _profile_hash_size;
217:
218: /*
219: * All profiling variables, and a dummy gprof record.
220: */
221:
222: struct profile_vars _profile_vars = { 0 };
223: struct hasharc _gprof_dummy = { 0 };
224:
225: /*
226: * Forward references.
227: */
228:
229: static void *_profile_md_acontext(struct profile_vars *pv,
230: void *ptr,
231: size_t len,
232: acontext_type_t type);
233:
234: static void _profile_reset_alloc(struct profile_vars *,
235: acontext_type_t);
236:
237: extern void _bogus_function(void);
238:
239: /*
240: * Function to set up the initial allocation for a context block.
241: */
242:
243: static void *
244: _profile_md_acontext(struct profile_vars *pv,
245: void *ptr,
246: size_t len,
247: acontext_type_t type)
248: {
249: struct memory {
250: struct alloc_context context;
251: struct page_list plist;
252: int data[1];
253: };
254:
255: struct memory *mptr = (struct memory *)ptr;
256: struct alloc_context *context = &mptr->context;
257: struct page_list *plist = &mptr->plist;
258:
259: #ifdef DEBUG_PROFILE
260: _profile_printf("_profile_md_acontext: pv= 0x%lx, ptr= 0x%lx, len= %6ld, type= %d\n",
261: (long)pv,
262: (long)ptr,
263: (long)len,
264: (int)type);
265: #endif
266:
267: /* Fill in context block header */
268: context->next = pv->acontext[type];
269: context->plist = plist;
270: context->lock = 0;
271:
272: /* Fill in first page list information */
273: plist->ptr = plist->first = (void *)&mptr->data[0];
274: plist->next = (struct page_list *)0;
275: plist->bytes_free = len - ((char *)plist->ptr - (char *)ptr);
276: plist->bytes_allocated = 0;
277: plist->num_allocations = 0;
278:
279: /* Update statistics */
280: pv->stats.num_context[type]++;
281: pv->stats.wasted[type] += plist->bytes_free;
282: pv->stats.overhead[type] += len - plist->bytes_free;
283:
284: /* And setup context block */
285: pv->acontext[type] = context;
286:
287: return (void *)((char *)ptr+len);
288: }
289:
290:
291: /*
292: * Machine dependent function to initialize things.
293: */
294:
295: void
296: _profile_md_init(struct profile_vars *pv,
297: profile_type_t type,
298: profile_alloc_mem_t alloc_mem)
299: {
300: size_t page_size = pv->page_size;
301: size_t arc_size;
302: size_t func_size;
303: size_t misc_size;
304: size_t hash_size;
305: size_t extra_arc_size;
306: size_t extra_func_size;
307: size_t callback_size = page_size;
308: void *ptr;
309: acontext_type_t ac;
310: int i;
311: static struct {
312: size_t c_size; /* size C thinks structure is */
313: size_t *asm_size_ptr; /* pointer to size asm thinks struct is */
314: const char *name; /* structure name */
315: } sizes[] = {
316: { sizeof(struct profile_profil), &_profile_profil_size, "profile_profil" },
317: { sizeof(struct profile_stats), &_profile_stats_size, "profile_stats" },
318: { sizeof(struct profile_md), &_profile_md_size, "profile_md" },
319: { sizeof(struct profile_vars), &_profile_size, "profile_vars" }};
320:
321: #ifdef DEBUG_PROFILE
322: _profile_printf("_profile_md_init: pv = 0x%lx, type = %d, alloc = %d\n",
323: (long) pv,
324: (int)type,
325: (int)alloc_mem);
326: #endif
327:
328: for (i = 0; i < sizeof (sizes) / sizeof(sizes[0]); i++) {
329: if (sizes[i].c_size != *sizes[i].asm_size_ptr) {
330: _profile_printf("C thinks struct %s is %ld bytes, asm thinks it is %ld bytes\n",
331: sizes[i].name,
332: (long)sizes[i].c_size,
333: (long)*sizes[i].asm_size_ptr);
334:
335: panic(sizes[i].name);
336: }
337: }
338:
339: /* Figure out which function will handle compiler generated profiling */
340: if (type == PROFILE_GPROF) {
341: pv->md.save_mcount_ptr = _gprof_mcount;
342:
343: } else if (type == PROFILE_PROF) {
344: pv->md.save_mcount_ptr = _prof_mcount;
345:
346: } else {
347: pv->md.save_mcount_ptr = _dummy_mcount;
348: }
349:
350: pv->vars_size = sizeof(struct profile_vars);
351: pv->plist_size = sizeof(struct page_list);
352: pv->acontext_size = sizeof(struct alloc_context);
353: pv->callback_size = sizeof(struct callback);
354: pv->major_version = PROFILE_MAJOR_VERSION;
355: pv->minor_version = PROFILE_MINOR_VERSION;
356: pv->type = type;
357: pv->do_profile = 1;
358: pv->use_dci = 1;
359: pv->use_profil = 1;
360: pv->output_uarea = 1;
361: pv->output_stats = (prof_flag_t) _profile_do_stats;
362: pv->output_clock = 1;
363: pv->multiple_sections = 1;
364: pv->init_format = 0;
365: pv->bogus_func = _bogus_function;
366:
367: #ifdef DEBUG_PROFILE
368: pv->debug = 1;
369: #endif
370:
371: if (!pv->error_msg) {
372: pv->error_msg = "error in profiling";
373: }
374:
375: if (!pv->page_size) {
376: pv->page_size = 4096;
377: }
378:
379: pv->stats.stats_size = sizeof(struct profile_stats);
380: pv->stats.major_version = PROFILE_MAJOR_VERSION;
381: pv->stats.minor_version = PROFILE_MINOR_VERSION;
382:
383: pv->md.md_size = sizeof(struct profile_md);
384: pv->md.major_version = PROFILE_MAJOR_VERSION;
385: pv->md.minor_version = PROFILE_MINOR_VERSION;
386: pv->md.hash_size = _profile_hash_size;
387: pv->md.num_cache = MAX_CACHE;
388: pv->md.mcount_ptr_ptr = &_mcount_ptr;
389: pv->md.dummy_ptr = &_gprof_dummy;
390: pv->md.alloc_pages = _profile_alloc_pages;
391:
392: /* zero out all allocation context blocks */
393: for (ac = ACONTEXT_FIRST; ac < ACONTEXT_MAX; ac++) {
394: pv->acontext[ac] = (struct alloc_context *)0;
395: }
396:
397: /* Don't allocate memory if not desired */
398: if (!alloc_mem) {
399: return;
400: }
401:
402: /* Allocate some space for the initial allocations */
403: switch (type) {
404: default:
405: misc_size = page_size;
406: ptr = _profile_alloc_pages(misc_size + callback_size);
407: ptr = _profile_md_acontext(pv, ptr, misc_size, ACONTEXT_MISC);
408: ptr = _profile_md_acontext(pv, ptr, callback_size, ACONTEXT_CALLBACK);
409: break;
410:
411: case PROFILE_GPROF:
412:
413: #if defined(MACH_KERNEL) || defined(_KERNEL)
414: /*
415: * For the MK & server allocate some slop space now for the
416: * secondary context blocks in case allocations are done at
417: * interrupt level when another allocation is being done. This
418: * is done before the main allocation blocks and will be pushed
419: * so that it will only be used when the main allocation block
420: * is locked.
421: */
422: extra_arc_size = 4*page_size;
423: extra_func_size = 2*page_size;
424: #else
425: extra_arc_size = extra_func_size = 0;
426: #endif
427:
428: /* Set up allocation areas */
429: arc_size = ROUNDUP(PROFILE_NUM_ARCS * sizeof(struct hasharc), page_size);
430: func_size = ROUNDUP(PROFILE_NUM_FUNCS * sizeof(struct gfuncs), page_size);
431: hash_size = _profile_hash_size * sizeof (struct hasharc *);
432: misc_size = ROUNDUP(hash_size + page_size, page_size);
433:
434: ptr = _profile_alloc_pages(arc_size
435: + func_size
436: + misc_size
437: + callback_size
438: + extra_arc_size
439: + extra_func_size);
440:
441: #if defined(MACH_KERNEL) || defined(_KERNEL)
442: ptr = _profile_md_acontext(pv, ptr, extra_arc_size, ACONTEXT_GPROF);
443: ptr = _profile_md_acontext(pv, ptr, extra_func_size, ACONTEXT_GFUNC);
444: #endif
445: ptr = _profile_md_acontext(pv, ptr, arc_size, ACONTEXT_GPROF);
446: ptr = _profile_md_acontext(pv, ptr, func_size, ACONTEXT_GFUNC);
447: ptr = _profile_md_acontext(pv, ptr, misc_size, ACONTEXT_MISC);
448: ptr = _profile_md_acontext(pv, ptr, callback_size, ACONTEXT_CALLBACK);
449:
450: /* Allocate hash table */
451: pv->md.hash_ptr = (struct hasharc **) _profile_alloc(pv, hash_size, ACONTEXT_MISC);
452: break;
453:
454: case PROFILE_PROF:
455: /* Set up allocation areas */
456: func_size = ROUNDUP(PROFILE_NUM_FUNCS * sizeof(struct prof_ext), page_size);
457: misc_size = page_size;
458:
459: ptr = _profile_alloc_pages(func_size
460: + misc_size
461: + callback_size);
462:
463: ptr = _profile_md_acontext(pv, ptr, func_size, ACONTEXT_PROF);
464: ptr = _profile_md_acontext(pv, ptr, misc_size, ACONTEXT_MISC);
465: ptr = _profile_md_acontext(pv, ptr, callback_size, ACONTEXT_CALLBACK);
466: break;
467: }
468: }
469:
470:
471: /*
472: * Machine dependent functions to start and stop profiling.
473: */
474:
475: int
476: _profile_md_start(void)
477: {
478: _mcount_ptr = _profile_vars.md.save_mcount_ptr;
479: return 0;
480: }
481:
482: int
483: _profile_md_stop(void)
484: {
485: _mcount_ptr = _dummy_mcount;
486: return 0;
487: }
488:
489:
490: /*
491: * Free up all memory in a memory context block.
492: */
493:
494: static void
495: _profile_reset_alloc(struct profile_vars *pv, acontext_type_t ac)
496: {
497: struct alloc_context *aptr;
498: struct page_list *plist;
499:
500: for (aptr = pv->acontext[ac];
501: aptr != (struct alloc_context *)0;
502: aptr = aptr->next) {
503:
504: for (plist = aptr->plist;
505: plist != (struct page_list *)0;
506: plist = plist->next) {
507:
508: plist->ptr = plist->first;
509: plist->bytes_free += plist->bytes_allocated;
510: plist->bytes_allocated = 0;
511: plist->num_allocations = 0;
512: memset(plist->first, '\0', plist->bytes_allocated);
513: }
514: }
515: }
516:
517:
518: /*
519: * Reset profiling. Since the only user of this function is the kernel
520: * and the server, we don't have to worry about other stuff than gprof.
521: */
522:
523: void
524: _profile_reset(struct profile_vars *pv)
525: {
526: struct alloc_context *aptr;
527: struct page_list *plist;
528: struct gfuncs *gfunc;
529:
530: if (pv->active) {
531: _profile_md_stop();
532: }
533:
534: /* Reset all function unique pointers back to 0 */
535: for (aptr = pv->acontext[ACONTEXT_GFUNC];
536: aptr != (struct alloc_context *)0;
537: aptr = aptr->next) {
538:
539: for (plist = aptr->plist;
540: plist != (struct page_list *)0;
541: plist = plist->next) {
542:
543: for (gfunc = (struct gfuncs *)plist->first;
544: gfunc < (struct gfuncs *)plist->ptr;
545: gfunc++) {
546:
547: *(gfunc->unique_ptr) = (struct hasharc *)0;
548: }
549: }
550: }
551:
552: /* Release memory */
553: _profile_reset_alloc(pv, ACONTEXT_GPROF);
554: _profile_reset_alloc(pv, ACONTEXT_GFUNC);
555: _profile_reset_alloc(pv, ACONTEXT_PROF);
556:
557: memset((void *)pv->profil_buf, '\0', pv->profil_info.profil_len);
558: memset((void *)pv->md.hash_ptr, '\0', pv->md.hash_size * sizeof(struct hasharc *));
559: memset((void *)&pv->stats, '\0', sizeof(pv->stats));
560:
561: pv->stats.stats_size = sizeof(struct profile_stats);
562: pv->stats.major_version = PROFILE_MAJOR_VERSION;
563: pv->stats.minor_version = PROFILE_MINOR_VERSION;
564:
565: if (pv->active) {
566: _profile_md_start();
567: }
568: }
569:
570:
571: /*
572: * Machine dependent function to write out gprof records.
573: */
574:
575: size_t
576: _gprof_write(struct profile_vars *pv, struct callback *callback_ptr)
577: {
578: struct alloc_context *aptr;
579: struct page_list *plist;
580: size_t bytes = 0;
581: struct hasharc *hptr;
582: int i;
583:
584: for (aptr = pv->acontext[ACONTEXT_GPROF];
585: aptr != (struct alloc_context *)0;
586: aptr = aptr->next) {
587:
588: for (plist = aptr->plist; plist != (struct page_list *)0; plist = plist->next) {
589: hptr = (struct hasharc *)plist->first;
590: for (i = 0; i < plist->num_allocations; (i++, hptr++)) {
591:
592: struct gprof_arc arc = hptr->arc;
593: int nrecs = 1 + (hptr->overflow * 2);
594: int j;
595:
596: if (pv->check_funcs) {
597: if (arc.frompc < pv->profil_info.lowpc ||
598: arc.frompc > pv->profil_info.highpc) {
599:
600: arc.frompc = (prof_uptrint_t)pv->bogus_func;
601: }
602:
603: if (arc.selfpc < pv->profil_info.lowpc ||
604: arc.selfpc > pv->profil_info.highpc) {
605:
606: arc.selfpc = (prof_uptrint_t)pv->bogus_func;
607: }
608: }
609:
610: /* For each overflow, emit 2 extra records with the count
611: set to 0x80000000 */
612: for (j = 0; j < nrecs; j++) {
613: bytes += sizeof (arc);
614: if ((*pv->fwrite_func)((void *)&arc,
615: sizeof(arc),
616: 1,
617: pv->stream) != 1) {
618:
619: _profile_error(pv);
620: }
621:
622: arc.count = 0x80000000;
623: }
624: }
625: }
626: }
627:
628: return bytes;
629: }
630:
631:
632: /*
633: * Machine dependent function to write out prof records.
634: */
635:
636: size_t
637: _prof_write(struct profile_vars *pv, struct callback *callback_ptr)
638: {
639: struct alloc_context *aptr;
640: struct page_list *plist;
641: size_t bytes = 0;
642: struct prof_ext prof_st;
643: struct prof_int *pptr;
644: struct gfuncs *gptr;
645: int nrecs;
646: int i, j;
647:
648: /* Write out information prof_mcount collects */
649: for (aptr = pv->acontext[ACONTEXT_PROF];
650: aptr != (struct alloc_context *)0;
651: aptr = aptr->next) {
652:
653: for (plist = aptr->plist; plist != (struct page_list *)0; plist = plist->next) {
654: pptr = (struct prof_int *)plist->first;
655:
656: for (i = 0; i < plist->num_allocations; (i++, pptr++)) {
657:
658: /* Write out 2 records for each overflow, each with a
659: count of 0x80000000 + the normal record */
660: prof_st = pptr->prof;
661: nrecs = 1 + (pptr->overflow * 2);
662:
663: for (j = 0; j < nrecs; j++) {
664: bytes += sizeof (struct prof_ext);
665: if ((*pv->fwrite_func)((void *)&prof_st,
666: sizeof(prof_st),
667: 1,
668: pv->stream) != 1) {
669:
670: _profile_error(pv);
671: }
672:
673: prof_st.cncall = 0x80000000;
674: }
675: }
676: }
677: }
678:
679: /* Now write out the prof information that gprof collects */
680: for (aptr = pv->acontext[ACONTEXT_GFUNC];
681: aptr != (struct alloc_context *)0;
682: aptr = aptr->next) {
683:
684: for (plist = aptr->plist; plist != (struct page_list *)0; plist = plist->next) {
685: gptr = (struct gfuncs *)plist->first;
686:
687: for (i = 0; i < plist->num_allocations; (i++, gptr++)) {
688:
689: /* Write out 2 records for each overflow, each with a
690: count of 0x80000000 + the normal record */
691: prof_st = gptr->prof.prof;
692: nrecs = 1 + (gptr->prof.overflow * 2);
693:
694: for (j = 0; j < nrecs; j++) {
695: bytes += sizeof (struct prof_ext);
696: if ((*pv->fwrite_func)((void *)&prof_st,
697: sizeof(prof_st),
698: 1,
699: pv->stream) != 1) {
700:
701: _profile_error(pv);
702: }
703:
704: prof_st.cncall = 0x80000000;
705: }
706: }
707: }
708: }
709:
710: return bytes;
711: }
712:
713:
714: /*
715: * Update any statistics. For the 386, calculate the hash table loading factor.
716: * Also figure out how many overflows occured.
717: */
718:
719: void
720: _profile_update_stats(struct profile_vars *pv)
721: {
722: struct alloc_context *aptr;
723: struct page_list *plist;
724: struct hasharc *hptr;
725: struct prof_int *pptr;
726: struct gfuncs *fptr;
727: LHISTCOUNTER *lptr;
728: int i;
729:
730: for(i = 0; i < MAX_BUCKETS+1; i++) {
731: pv->stats.buckets[i] = 0;
732: }
733:
734: pv->stats.hash_buckets = 0;
735:
736: if (pv->md.hash_ptr) {
737: for (i = 0; i < pv->md.hash_size; i++) {
738: long nbuckets = 0;
739: struct hasharc *hptr;
740:
741: for (hptr = pv->md.hash_ptr[i]; hptr; hptr = hptr->next) {
742: nbuckets++;
743: }
744:
745: pv->stats.buckets[ (nbuckets < MAX_BUCKETS) ? nbuckets : MAX_BUCKETS ]++;
746: if (pv->stats.hash_buckets < nbuckets) {
747: pv->stats.hash_buckets = nbuckets;
748: }
749: }
750: }
751:
752: /* Count how many times functions are out of bounds */
753: if (pv->check_funcs) {
754: pv->stats.bogus_count = 0;
755:
756: for (aptr = pv->acontext[ACONTEXT_GPROF];
757: aptr != (struct alloc_context *)0;
758: aptr = aptr->next) {
759:
760: for (plist = aptr->plist;
761: plist != (struct page_list *)0;
762: plist = plist->next) {
763:
764: hptr = (struct hasharc *)plist->first;
765: for (i = 0; i < plist->num_allocations; (i++, hptr++)) {
766:
767: if (hptr->arc.frompc < pv->profil_info.lowpc ||
768: hptr->arc.frompc > pv->profil_info.highpc) {
769: pv->stats.bogus_count++;
770: }
771:
772: if (hptr->arc.selfpc < pv->profil_info.lowpc ||
773: hptr->arc.selfpc > pv->profil_info.highpc) {
774: pv->stats.bogus_count++;
775: }
776: }
777: }
778: }
779: }
780:
781: /* Figure out how many overflows occurred */
782: PROF_ULONG_TO_CNT(pv->stats.prof_overflow, 0);
783: PROF_ULONG_TO_CNT(pv->stats.gprof_overflow, 0);
784:
785: for (aptr = pv->acontext[ACONTEXT_GPROF];
786: aptr != (struct alloc_context *)0;
787: aptr = aptr->next) {
788:
789: for (plist = aptr->plist;
790: plist != (struct page_list *)0;
791: plist = plist->next) {
792:
793: hptr = (struct hasharc *)plist->first;
794: for (i = 0; i < plist->num_allocations; (i++, hptr++)) {
795: PROF_CNT_ADD(pv->stats.gprof_overflow, hptr->overflow);
796: }
797: }
798: }
799:
800: for (aptr = pv->acontext[ACONTEXT_PROF];
801: aptr != (struct alloc_context *)0;
802: aptr = aptr->next) {
803:
804: for (plist = aptr->plist;
805: plist != (struct page_list *)0;
806: plist = plist->next) {
807:
808: pptr = (struct prof_int *)plist->first;
809: for (i = 0; i < plist->num_allocations; (i++, pptr++)) {
810: PROF_CNT_ADD(pv->stats.prof_overflow, pptr->overflow);
811: }
812: }
813: }
814:
815: for (aptr = pv->acontext[ACONTEXT_GFUNC];
816: aptr != (struct alloc_context *)0;
817: aptr = aptr->next) {
818:
819: for (plist = aptr->plist;
820: plist != (struct page_list *)0;
821: plist = plist->next) {
822:
823: fptr = (struct gfuncs *)plist->first;
824: for (i = 0; i < plist->num_allocations; (i++, fptr++)) {
825: PROF_CNT_ADD(pv->stats.prof_overflow, fptr->prof.overflow);
826: }
827: }
828: }
829:
830: /* Now go through & count how many times the LHISTCOUNTER overflowed into a 2nd word */
831: lptr = (LHISTCOUNTER *)pv->profil_buf;
832:
833: if (pv->use_profil &&
834: pv->profil_info.counter_size == sizeof(LHISTCOUNTER) &&
835: lptr != (LHISTCOUNTER *)0) {
836:
837: PROF_ULONG_TO_CNT(pv->stats.overflow_ticks, 0);
838: for (i = 0; i < pv->stats.profil_buckets; i++) {
839: PROF_CNT_ADD(pv->stats.overflow_ticks, lptr[i].high);
840: }
841: }
842: }
843:
844: #if !defined(_KERNEL) && !defined(MACH_KERNEL)
845:
846: /*
847: * Routine callable from the debugger that prints the statistics.
848: */
849:
850: int _profile_debug(void)
851: {
852: _profile_update_stats(&_profile_vars);
853: _profile_print_stats(stderr, &_profile_vars.stats, &_profile_vars.profil_info);
854: return 0;
855: }
856:
857: /*
858: * Print the statistics structure in a meaningful way.
859: */
860:
861: void _profile_print_stats(FILE *stream,
862: const struct profile_stats *stats,
863: const struct profile_profil *pinfo)
864: {
865: int i;
866: prof_cnt_t total_hits;
867: acontext_type_t ac;
868: int width_cname = 0;
869: int width_alloc = 0;
870: int width_wasted = 0;
871: int width_overhead = 0;
872: int width_context = 0;
873: static const char *cname[ACONTEXT_MAX] = ACONTEXT_NAMES;
874: char buf[20];
875:
876: if (!stats) {
877: return;
878: }
879:
880: if (!stream) {
881: stream = stdout;
882: }
883:
884: sprintf(buf, "%ld.%ld", (long)stats->major_version, (long)stats->minor_version);
885: fprintf(stream, "%12s profiling version number\n", buf);
886: fprintf(stream, "%12lu size of profile_vars\n", (long unsigned)sizeof(struct profile_vars));
887: fprintf(stream, "%12lu size of profile_stats\n", (long unsigned)sizeof(struct profile_stats));
888: fprintf(stream, "%12lu size of profile_md\n", (long unsigned)sizeof(struct profile_md));
889: fprintf(stream, "%12s calls to _{,g}prof_mcount\n", PROF_CNT_TO_DECIMAL((char *)0, stats->cnt));
890: fprintf(stream, "%12s calls to old mcount\n", PROF_CNT_TO_DECIMAL((char *)0, stats->old_mcount));
891: fprintf(stream, "%12s calls to _dummy_mcount\n", PROF_CNT_TO_DECIMAL((char *)0, stats->dummy));
892: fprintf(stream, "%12lu functions profiled\n", (long unsigned)stats->prof_records);
893: fprintf(stream, "%12lu gprof arcs\n", (long unsigned)stats->gprof_records);
894:
895: if (pinfo) {
896: fprintf(stream, "%12lu profil buckets\n", (long unsigned)stats->profil_buckets);
897: fprintf(stream, "%12lu profil lowpc [0x%lx]\n",
898: (long unsigned)pinfo->lowpc,
899: (long unsigned)pinfo->lowpc);
900:
901: fprintf(stream, "%12lu profil highpc [0x%lx]\n",
902: (long unsigned)pinfo->highpc,
903: (long unsigned)pinfo->highpc);
904:
905: fprintf(stream, "%12lu profil highpc-lowpc\n", (long unsigned)(pinfo->highpc - pinfo->lowpc));
906: fprintf(stream, "%12lu profil buffer length\n", (long unsigned)pinfo->profil_len);
907: fprintf(stream, "%12lu profil sizeof counters\n", (long unsigned)pinfo->counter_size);
908: fprintf(stream, "%12lu profil scale (%g)\n",
909: (long unsigned)pinfo->scale,
910: ((double)pinfo->scale) / ((double) 0x10000));
911:
912:
913: for (i = 0; i < sizeof (pinfo->profil_unused) / sizeof (pinfo->profil_unused[0]); i++) {
914: if (pinfo->profil_unused[i]) {
915: fprintf(stream, "%12lu profil unused[%2d] {0x%.8lx}\n",
916: (long unsigned)pinfo->profil_unused[i],
917: i,
918: (long unsigned)pinfo->profil_unused[i]);
919: }
920: }
921: }
922:
923: if (stats->max_cpu) {
924: fprintf(stream, "%12lu current cpu/thread\n", (long unsigned)stats->my_cpu);
925: fprintf(stream, "%12lu max cpu/thread+1\n", (long unsigned)stats->max_cpu);
926: }
927:
928: if (stats->bogus_count != 0) {
929: fprintf(stream,
930: "%12lu gprof functions found outside of range\n",
931: (long unsigned)stats->bogus_count);
932: }
933:
934: if (PROF_CNT_NE_0(stats->too_low)) {
935: fprintf(stream,
936: "%12s histogram ticks were too low\n",
937: PROF_CNT_TO_DECIMAL((char *)0, stats->too_low));
938: }
939:
940: if (PROF_CNT_NE_0(stats->too_high)) {
941: fprintf(stream,
942: "%12s histogram ticks were too high\n",
943: PROF_CNT_TO_DECIMAL((char *)0, stats->too_high));
944: }
945:
946: if (PROF_CNT_NE_0(stats->acontext_locked)) {
947: fprintf(stream,
948: "%12s times an allocation context was locked\n",
949: PROF_CNT_TO_DECIMAL((char *)0, stats->acontext_locked));
950: }
951:
952: if (PROF_CNT_NE_0(stats->kernel_ticks)
953: || PROF_CNT_NE_0(stats->user_ticks)
954: || PROF_CNT_NE_0(stats->idle_ticks)) {
955:
956: prof_cnt_t total_ticks;
957: long double total_ticks_dbl;
958:
959: total_ticks = stats->kernel_ticks;
960: PROF_CNT_LADD(total_ticks, stats->user_ticks);
961: PROF_CNT_LADD(total_ticks, stats->idle_ticks);
962: total_ticks_dbl = PROF_CNT_TO_LDOUBLE(total_ticks);
963:
964: fprintf(stream,
965: "%12s total ticks\n",
966: PROF_CNT_TO_DECIMAL((char *)0, total_ticks));
967:
968: fprintf(stream,
969: "%12s ticks within the kernel (%5.2Lf%%)\n",
970: PROF_CNT_TO_DECIMAL((char *)0, stats->kernel_ticks),
971: 100.0L * (PROF_CNT_TO_LDOUBLE(stats->kernel_ticks) / total_ticks_dbl));
972:
973: fprintf(stream,
974: "%12s ticks within user space (%5.2Lf%%)\n",
975: PROF_CNT_TO_DECIMAL((char *)0, stats->user_ticks),
976: 100.0L * (PROF_CNT_TO_LDOUBLE(stats->user_ticks) / total_ticks_dbl));
977:
978: fprintf(stream,
979: "%12s ticks idle (%5.2Lf%%)\n",
980: PROF_CNT_TO_DECIMAL((char *)0, stats->idle_ticks),
981: 100.0L * (PROF_CNT_TO_LDOUBLE(stats->idle_ticks) / total_ticks_dbl));
982: }
983:
984: if (PROF_CNT_NE_0(stats->overflow_ticks)) {
985: fprintf(stream, "%12s times a HISTCOUNTER counter would have overflowed\n",
986: PROF_CNT_TO_DECIMAL((char *)0, stats->overflow_ticks));
987: }
988:
989: if (PROF_CNT_NE_0(stats->hash_num)) {
990: long double total_buckets = 0.0L;
991:
992: for (i = 0; i <= MAX_BUCKETS; i++) {
993: total_buckets += (long double)stats->buckets[i];
994: }
995:
996: fprintf(stream, "%12lu max bucket(s) on hash chain.\n", (long unsigned)stats->hash_buckets);
997: for (i = 0; i < MAX_BUCKETS; i++) {
998: if (stats->buckets[i] != 0) {
999: fprintf(stream, "%12lu bucket(s) had %d entries (%5.2Lf%%)\n",
1000: (long unsigned)stats->buckets[i], i,
1001: 100.0L * ((long double)stats->buckets[i] / total_buckets));
1002: }
1003: }
1004:
1005: if (stats->buckets[MAX_BUCKETS] != 0) {
1006: fprintf(stream, "%12lu bucket(s) had more than %d entries (%5.2Lf%%)\n",
1007: (long unsigned)stats->buckets[MAX_BUCKETS], MAX_BUCKETS,
1008: 100.0L * ((long double)stats->buckets[MAX_BUCKETS] / total_buckets));
1009: }
1010: }
1011:
1012: PROF_ULONG_TO_CNT(total_hits, 0);
1013: for (i = 0; i < MAX_CACHE; i++) {
1014: PROF_CNT_LADD(total_hits, stats->cache_hits[i]);
1015: }
1016:
1017: if (PROF_CNT_NE_0(total_hits)) {
1018: long double total = PROF_CNT_TO_LDOUBLE(stats->cnt);
1019: long double total_hits_dbl = PROF_CNT_TO_LDOUBLE(total_hits);
1020:
1021: fprintf(stream,
1022: "%12s cache hits (%.2Lf%%)\n",
1023: PROF_CNT_TO_DECIMAL((char *)0, total_hits),
1024: 100.0L * (total_hits_dbl / total));
1025:
1026: for (i = 0; i < MAX_CACHE; i++) {
1027: if (PROF_CNT_NE_0(stats->cache_hits[i])) {
1028: fprintf(stream,
1029: "%12s times cache#%d matched (%5.2Lf%% of cache hits, %5.2Lf%% total)\n",
1030: PROF_CNT_TO_DECIMAL((char *)0, stats->cache_hits[i]),
1031: i+1,
1032: 100.0L * (PROF_CNT_TO_LDOUBLE(stats->cache_hits[i]) / total_hits_dbl),
1033: 100.0L * (PROF_CNT_TO_LDOUBLE(stats->cache_hits[i]) / total));
1034: }
1035: }
1036:
1037: if (PROF_CNT_NE_0(stats->hash_num)) {
1038: fprintf(stream, "%12s times hash table searched\n", PROF_CNT_TO_DECIMAL((char *)0, stats->hash_num));
1039: fprintf(stream, "%12s hash buckets searched\n", PROF_CNT_TO_DECIMAL((char *)0, stats->hash_search));
1040: fprintf(stream, "%12.4Lf average buckets searched\n",
1041: PROF_CNT_TO_LDOUBLE(stats->hash_search) / PROF_CNT_TO_LDOUBLE(stats->hash_num));
1042: }
1043: }
1044:
1045: for (i = 0; i < sizeof (stats->stats_unused) / sizeof (stats->stats_unused[0]); i++) {
1046: if (PROF_CNT_NE_0(stats->stats_unused[i])) {
1047: fprintf(stream, "%12s unused[%2d] {0x%.8lx 0x%.8lx}\n",
1048: PROF_CNT_TO_DECIMAL((char *)0, stats->stats_unused[i]),
1049: i,
1050: (unsigned long)stats->stats_unused[i].high,
1051: (unsigned long)stats->stats_unused[i].low);
1052: }
1053: }
1054:
1055: /* Get the width for the allocation contexts */
1056: for (ac = ACONTEXT_FIRST; ac < ACONTEXT_MAX; ac++) {
1057: int len;
1058:
1059: if (stats->num_context[ac] == 0) {
1060: continue;
1061: }
1062:
1063: len = strlen (cname[ac]);
1064: if (len > width_cname)
1065: width_cname = len;
1066:
1067: len = sprintf (buf, "%lu", (long unsigned)stats->num_alloc[ac]);
1068: if (len > width_alloc)
1069: width_alloc = len;
1070:
1071: len = sprintf (buf, "%lu", (long unsigned)stats->wasted[ac]);
1072: if (len > width_wasted)
1073: width_wasted = len;
1074:
1075: len = sprintf (buf, "%lu", (long unsigned)stats->overhead[ac]);
1076: if (len > width_overhead)
1077: width_overhead = len;
1078:
1079: len = sprintf (buf, "%lu", (long unsigned)stats->num_context[ac]);
1080: if (len > width_context)
1081: width_context = len;
1082: }
1083:
1084: /* Print info about allocation contexts */
1085: for (ac = ACONTEXT_FIRST; ac < ACONTEXT_MAX; ac++) {
1086: if (stats->num_context[ac] == 0) {
1087: continue;
1088: }
1089:
1090: fprintf (stream,
1091: "%12lu bytes in %-*s %*lu alloc, %*lu unused, %*lu over, %*lu context\n",
1092: (long unsigned)stats->bytes_alloc[ac],
1093: width_cname, cname[ac],
1094: width_alloc, (long unsigned)stats->num_alloc[ac],
1095: width_wasted, (long unsigned)stats->wasted[ac],
1096: width_overhead, (long unsigned)stats->overhead[ac],
1097: width_context, (long unsigned)stats->num_context[ac]);
1098: }
1099: }
1100:
1101:
1102: /*
1103: * Merge a new statistics field into an old one.
1104: */
1105:
1106: void _profile_merge_stats(struct profile_stats *old_stats, const struct profile_stats *new_stats)
1107: {
1108: int i;
1109:
1110: /* If nothing passed, just return */
1111: if (!old_stats || !new_stats)
1112: return;
1113:
1114: /* If the old_stats has not been initialized, just copy in the new stats */
1115: if (old_stats->major_version == 0) {
1116: *old_stats = *new_stats;
1117:
1118: /* Otherwise, update stats, field by field */
1119: } else {
1120: if (old_stats->prof_records < new_stats->prof_records)
1121: old_stats->prof_records = new_stats->prof_records;
1122:
1123: if (old_stats->gprof_records < new_stats->gprof_records)
1124: old_stats->gprof_records = new_stats->gprof_records;
1125:
1126: if (old_stats->hash_buckets < new_stats->hash_buckets)
1127: old_stats->hash_buckets = new_stats->hash_buckets;
1128:
1129: if (old_stats->bogus_count < new_stats->bogus_count)
1130: old_stats->bogus_count = new_stats->bogus_count;
1131:
1132: PROF_CNT_LADD(old_stats->cnt, new_stats->cnt);
1133: PROF_CNT_LADD(old_stats->dummy, new_stats->dummy);
1134: PROF_CNT_LADD(old_stats->old_mcount, new_stats->old_mcount);
1135: PROF_CNT_LADD(old_stats->hash_search, new_stats->hash_search);
1136: PROF_CNT_LADD(old_stats->hash_num, new_stats->hash_num);
1137: PROF_CNT_LADD(old_stats->user_ticks, new_stats->user_ticks);
1138: PROF_CNT_LADD(old_stats->kernel_ticks, new_stats->kernel_ticks);
1139: PROF_CNT_LADD(old_stats->idle_ticks, new_stats->idle_ticks);
1140: PROF_CNT_LADD(old_stats->overflow_ticks, new_stats->overflow_ticks);
1141: PROF_CNT_LADD(old_stats->acontext_locked, new_stats->acontext_locked);
1142: PROF_CNT_LADD(old_stats->too_low, new_stats->too_low);
1143: PROF_CNT_LADD(old_stats->too_high, new_stats->too_high);
1144: PROF_CNT_LADD(old_stats->prof_overflow, new_stats->prof_overflow);
1145: PROF_CNT_LADD(old_stats->gprof_overflow, new_stats->gprof_overflow);
1146:
1147: for (i = 0; i < (int)ACONTEXT_MAX; i++) {
1148: if (old_stats->num_alloc[i] < new_stats->num_alloc[i])
1149: old_stats->num_alloc[i] = new_stats->num_alloc[i];
1150:
1151: if (old_stats->bytes_alloc[i] < new_stats->bytes_alloc[i])
1152: old_stats->bytes_alloc[i] = new_stats->bytes_alloc[i];
1153:
1154: if (old_stats->num_context[i] < new_stats->num_context[i])
1155: old_stats->num_context[i] = new_stats->num_context[i];
1156:
1157: if (old_stats->wasted[i] < new_stats->wasted[i])
1158: old_stats->wasted[i] = new_stats->wasted[i];
1159:
1160: if (old_stats->overhead[i] < new_stats->overhead[i])
1161: old_stats->overhead[i] = new_stats->overhead[i];
1162:
1163: }
1164:
1165: for (i = 0; i < MAX_BUCKETS+1; i++) {
1166: if (old_stats->buckets[i] < new_stats->buckets[i])
1167: old_stats->buckets[i] = new_stats->buckets[i];
1168: }
1169:
1170: for (i = 0; i < MAX_CACHE; i++) {
1171: PROF_CNT_LADD(old_stats->cache_hits[i], new_stats->cache_hits[i]);
1172: }
1173:
1174: for (i = 0; i < sizeof(old_stats->stats_unused) / sizeof(old_stats->stats_unused[0]); i++) {
1175: PROF_CNT_LADD(old_stats->stats_unused[i], new_stats->stats_unused[i]);
1176: }
1177: }
1178: }
1179:
1180: #endif
1181:
1182:
1183: /*
1184: * Invalid function address used when checking of function addresses is
1185: * desired for gprof arcs, and we discover an address out of bounds.
1186: * There should be no callers of this function.
1187: */
1188:
1189: void
1190: _bogus_function(void)
1191: {
1192: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.