2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
31 * Revision 1.1.1.1 1998/09/22 21:05:49 wsanchez
32 * Import of Mac OS X kernel (~semeria)
34 * Revision 1.1.1.1 1998/03/07 02:26:08 wsanchez
35 * Import of OSF Mach kernel (~mburg)
37 * Revision 1.1.5.1 1995/01/06 19:53:45 devrcs
38 * mk6 CR668 - 1.3b26 merge
40 * [1994/10/12 22:25:24 dwm]
42 * Revision 1.1.2.2 1994/05/16 19:19:22 meissner
43 * Protect against hash_ptr being null in _profile_update_stats.
44 * [1994/05/16 17:23:53 meissner]
46 * Remove _profile_cnt_to_hex, _profile_strbuffer.
47 * _profile_print_stats now takes const pointers.
48 * Use the new 64-bit arithmetic support instead of converting to double.
49 * Add _profile_merge_stats to merge statistics.
50 * [1994/04/28 21:45:04 meissner]
52 * If MACH_ASSERT is on in server or kernel, turn on profiling printfs.
53 * Print out fractional digits for average # of hash searches in stats.
54 * Update overflow_ticks for # times the lprofil counter overflows into high word.
55 * Don't make sizes of C/asm structures a const array, since it has pointers in it.
56 * Add support for converting 64 bit ints to a string.
57 * Use PROF_CNT_TO_DECIMAL where possible instead of PROF_CNT_TO_LDOUBLE.
58 * [1994/04/20 15:47:02 meissner]
60 * Revision 1.1.2.1 1994/04/08 17:51:51 meissner
62 * [1994/04/08 02:11:40 meissner]
64 * Make most stats 64 bits, except for things like memory allocation.
65 * [1994/04/02 14:58:28 meissner]
67 * Add some printfs under #idef DEBUG_PROFILE.
68 * [1994/03/29 21:00:11 meissner]
70 * Further changes for gprof/prof overflow support.
71 * Add overflow support for {gprof,prof,old,dummy}_mcount counters.
72 * [1994/03/17 20:13:31 meissner]
74 * Add gprof/prof overflow support
75 * [1994/03/17 14:56:51 meissner]
77 * Use memset instead of bzero.
78 * [1994/02/28 23:56:10 meissner]
80 * Add size of histogram counters & unused fields to profile_profil struct
81 * [1994/02/17 21:41:50 meissner]
83 * Allocate slop space for server in addition to microkernel.
84 * Add 3rd argument to _profile_print_stats for profil info.
85 * Print # histogram ticks too low/too high for server/mk.
86 * [1994/02/16 22:38:18 meissner]
88 * Calculate percentages for # of hash buckets.
89 * [1994/02/11 16:52:04 meissner]
91 * Print stats as an unsigned number.
92 * [1994/02/07 18:47:05 meissner]
94 * For kernel and server, include <kern/assert.h> not <assert.h>.
95 * Always do assert on comparing asm vs. C structure sizes.
96 * Add _profile_reset to reset profiling information.
97 * Add _profile_update_stats to update the statistics.
98 * Move _gprof_write code that updates hash stats to _profile_update_stats.
99 * Don't allocate space for basic block support just yet.
100 * Add support for range checking the gprof arc {from,self}pc addresses.
101 * _profile_debug now calls _profile_update_stats.
102 * Print how many times the acontext was locked.
103 * If DEBUG_PROFILE is defined, set pv->debug to 1.
105 * [1994/02/07 12:41:03 meissner]
107 * Keep track of the number of times the kernel overflows the HISTCOUNTER counter.
108 * [1994/02/03 20:13:28 meissner]
110 * Add stats for {user,kernel,idle} mode in the kernel.
111 * [1994/02/03 15:17:31 meissner]
113 * Print unused stats in hex as well as decimal.
114 * [1994/02/03 14:52:20 meissner]
116 * _profile_print_stats no longer takes profile_{vars,md} pointer arguments.
117 * If stream is NULL, _profile_print_stats will use stdout.
118 * Separate _profile_update_stats from _gprof_write.
119 * [1994/02/03 00:58:55 meissner]
121 * Combine _profile_{vars,stats,md}; Allow more than one _profile_vars.
122 * [1994/02/01 12:04:01 meissner]
124 * Add allocation flag to _profile_md_init.
125 * Fix core dumps in _profile_print_stats if no profile_vars ptr passed.
126 * Print numbers in 12 columns, not 8.
127 * Print my_cpu/max_cpu if max_cpu != 0.
128 * Make allocations print like other stats.
129 * Use ACONTEXT_FIRST to start loop on, not ACONTEXT_PROF.
130 * [1994/01/28 23:33:26 meissner]
132 * Move callback pointers into separate allocation context.
133 * Add size fields for other structures to profile-vars.
134 * [1994/01/26 20:23:37 meissner]
136 * Allocate initial memory at startup.
137 * Print structure sizes and version number when printing stats.
138 * Initialize size fields and version numbers.
139 * Allocation context pointers moved to _profile_vars.
140 * [1994/01/25 01:46:04 meissner]
142 * Move init code here from assembly language.
143 * [1994/01/22 01:13:21 meissner]
145 * Include <profile/profile-internal.h> instead of "profile-md.h".
146 * [1994/01/20 20:56:49 meissner]
149 * [1994/01/18 23:08:02 meissner]
151 * Rename profile.h -> profile-md.h.
152 * [1994/01/18 19:44:57 meissner]
154 * Write out stats unused fields.
155 * Make _prof_write write out the prof stats gprof collects.
156 * [1994/01/15 18:40:37 meissner]
158 * Remove debug code called from profile-asm.s.
159 * Always print out the # of profil buckets.
160 * [1994/01/15 00:59:06 meissner]
163 * [1994/01/04 16:34:46 meissner]
165 * Move max hash bucket calculation into _gprof_write & put info in stats structure.
166 * [1994/01/04 16:15:17 meissner]
168 * Use _profile_printf to write diagnostics; add diag_stream to hold stream to write to.
169 * [1994/01/04 15:37:46 meissner]
171 * Correctly handle case where more than one allocation context was
172 * allocated due to multiple threads.
173 * Cast stats to long for output.
174 * Print number of profil buckets field in _profile_stats.
175 * Add support for GFUNC allocation context.
176 * [1994/01/04 14:26:00 meissner]
178 * CR 10198 - Initial version.
179 * [1994/01/01 22:44:10 meissne
184 #include <profiling/profile-internal.h>
188 #if defined(MACH_KERNEL) || defined(_KERNEL)
190 #include <mach_assert.h>
191 #if MACH_ASSERT && !defined(DEBUG_PROFILE)
192 #define DEBUG_PROFILE 1
195 extern int printf(const char *, ...);
196 extern void panic(const char *);
199 #define panic(str) exit(1)
202 #ifndef PROFILE_NUM_FUNCS
203 #define PROFILE_NUM_FUNCS 2000
206 #ifndef PROFILE_NUM_ARCS
207 #define PROFILE_NUM_ARCS 8000
211 * Information passed on from profile-asm.s
214 extern int _profile_do_stats
;
215 extern size_t _profile_size
;
216 extern size_t _profile_stats_size
;
217 extern size_t _profile_md_size
;
218 extern size_t _profile_profil_size
;
219 extern size_t _profile_hash_size
;
222 * All profiling variables, and a dummy gprof record.
225 struct profile_vars _profile_vars
= { 0 };
226 struct hasharc _gprof_dummy
= { 0 };
229 * Forward references.
232 static void *_profile_md_acontext(struct profile_vars
*pv
,
235 acontext_type_t type
);
237 static void _profile_reset_alloc(struct profile_vars
*,
240 extern void _bogus_function(void);
243 * Function to set up the initial allocation for a context block.
247 _profile_md_acontext(struct profile_vars
*pv
,
250 acontext_type_t type
)
253 struct alloc_context context
;
254 struct page_list plist
;
258 struct memory
*mptr
= (struct memory
*)ptr
;
259 struct alloc_context
*context
= &mptr
->context
;
260 struct page_list
*plist
= &mptr
->plist
;
263 _profile_printf("_profile_md_acontext: pv= 0x%lx, ptr= 0x%lx, len= %6ld, type= %d\n",
270 /* Fill in context block header */
271 context
->next
= pv
->acontext
[type
];
272 context
->plist
= plist
;
275 /* Fill in first page list information */
276 plist
->ptr
= plist
->first
= (void *)&mptr
->data
[0];
277 plist
->next
= (struct page_list
*)0;
278 plist
->bytes_free
= len
- ((char *)plist
->ptr
- (char *)ptr
);
279 plist
->bytes_allocated
= 0;
280 plist
->num_allocations
= 0;
282 /* Update statistics */
283 pv
->stats
.num_context
[type
]++;
284 pv
->stats
.wasted
[type
] += plist
->bytes_free
;
285 pv
->stats
.overhead
[type
] += len
- plist
->bytes_free
;
287 /* And setup context block */
288 pv
->acontext
[type
] = context
;
290 return (void *)((char *)ptr
+len
);
295 * Machine dependent function to initialize things.
299 _profile_md_init(struct profile_vars
*pv
,
301 profile_alloc_mem_t alloc_mem
)
303 size_t page_size
= pv
->page_size
;
308 size_t extra_arc_size
;
309 size_t extra_func_size
;
310 size_t callback_size
= page_size
;
315 size_t c_size
; /* size C thinks structure is */
316 size_t *asm_size_ptr
; /* pointer to size asm thinks struct is */
317 const char *name
; /* structure name */
319 { sizeof(struct profile_profil
), &_profile_profil_size
, "profile_profil" },
320 { sizeof(struct profile_stats
), &_profile_stats_size
, "profile_stats" },
321 { sizeof(struct profile_md
), &_profile_md_size
, "profile_md" },
322 { sizeof(struct profile_vars
), &_profile_size
, "profile_vars" }};
325 _profile_printf("_profile_md_init: pv = 0x%lx, type = %d, alloc = %d\n",
331 for (i
= 0; i
< sizeof (sizes
) / sizeof(sizes
[0]); i
++) {
332 if (sizes
[i
].c_size
!= *sizes
[i
].asm_size_ptr
) {
333 _profile_printf("C thinks struct %s is %ld bytes, asm thinks it is %ld bytes\n",
335 (long)sizes
[i
].c_size
,
336 (long)*sizes
[i
].asm_size_ptr
);
338 panic(sizes
[i
].name
);
342 /* Figure out which function will handle compiler generated profiling */
343 if (type
== PROFILE_GPROF
) {
344 pv
->md
.save_mcount_ptr
= _gprof_mcount
;
346 } else if (type
== PROFILE_PROF
) {
347 pv
->md
.save_mcount_ptr
= _prof_mcount
;
350 pv
->md
.save_mcount_ptr
= _dummy_mcount
;
353 pv
->vars_size
= sizeof(struct profile_vars
);
354 pv
->plist_size
= sizeof(struct page_list
);
355 pv
->acontext_size
= sizeof(struct alloc_context
);
356 pv
->callback_size
= sizeof(struct callback
);
357 pv
->major_version
= PROFILE_MAJOR_VERSION
;
358 pv
->minor_version
= PROFILE_MINOR_VERSION
;
363 pv
->output_uarea
= 1;
364 pv
->output_stats
= (prof_flag_t
) _profile_do_stats
;
365 pv
->output_clock
= 1;
366 pv
->multiple_sections
= 1;
368 pv
->bogus_func
= _bogus_function
;
374 if (!pv
->error_msg
) {
375 pv
->error_msg
= "error in profiling";
378 if (!pv
->page_size
) {
379 pv
->page_size
= 4096;
382 pv
->stats
.stats_size
= sizeof(struct profile_stats
);
383 pv
->stats
.major_version
= PROFILE_MAJOR_VERSION
;
384 pv
->stats
.minor_version
= PROFILE_MINOR_VERSION
;
386 pv
->md
.md_size
= sizeof(struct profile_md
);
387 pv
->md
.major_version
= PROFILE_MAJOR_VERSION
;
388 pv
->md
.minor_version
= PROFILE_MINOR_VERSION
;
389 pv
->md
.hash_size
= _profile_hash_size
;
390 pv
->md
.num_cache
= MAX_CACHE
;
391 pv
->md
.mcount_ptr_ptr
= &_mcount_ptr
;
392 pv
->md
.dummy_ptr
= &_gprof_dummy
;
393 pv
->md
.alloc_pages
= _profile_alloc_pages
;
395 /* zero out all allocation context blocks */
396 for (ac
= ACONTEXT_FIRST
; ac
< ACONTEXT_MAX
; ac
++) {
397 pv
->acontext
[ac
] = (struct alloc_context
*)0;
400 /* Don't allocate memory if not desired */
405 /* Allocate some space for the initial allocations */
408 misc_size
= page_size
;
409 ptr
= _profile_alloc_pages(misc_size
+ callback_size
);
410 ptr
= _profile_md_acontext(pv
, ptr
, misc_size
, ACONTEXT_MISC
);
411 ptr
= _profile_md_acontext(pv
, ptr
, callback_size
, ACONTEXT_CALLBACK
);
416 #if defined(MACH_KERNEL) || defined(_KERNEL)
418 * For the MK & server allocate some slop space now for the
419 * secondary context blocks in case allocations are done at
420 * interrupt level when another allocation is being done. This
421 * is done before the main allocation blocks and will be pushed
422 * so that it will only be used when the main allocation block
425 extra_arc_size
= 4*page_size
;
426 extra_func_size
= 2*page_size
;
428 extra_arc_size
= extra_func_size
= 0;
431 /* Set up allocation areas */
432 arc_size
= ROUNDUP(PROFILE_NUM_ARCS
* sizeof(struct hasharc
), page_size
);
433 func_size
= ROUNDUP(PROFILE_NUM_FUNCS
* sizeof(struct gfuncs
), page_size
);
434 hash_size
= _profile_hash_size
* sizeof (struct hasharc
*);
435 misc_size
= ROUNDUP(hash_size
+ page_size
, page_size
);
437 ptr
= _profile_alloc_pages(arc_size
444 #if defined(MACH_KERNEL) || defined(_KERNEL)
445 ptr
= _profile_md_acontext(pv
, ptr
, extra_arc_size
, ACONTEXT_GPROF
);
446 ptr
= _profile_md_acontext(pv
, ptr
, extra_func_size
, ACONTEXT_GFUNC
);
448 ptr
= _profile_md_acontext(pv
, ptr
, arc_size
, ACONTEXT_GPROF
);
449 ptr
= _profile_md_acontext(pv
, ptr
, func_size
, ACONTEXT_GFUNC
);
450 ptr
= _profile_md_acontext(pv
, ptr
, misc_size
, ACONTEXT_MISC
);
451 ptr
= _profile_md_acontext(pv
, ptr
, callback_size
, ACONTEXT_CALLBACK
);
453 /* Allocate hash table */
454 pv
->md
.hash_ptr
= (struct hasharc
**) _profile_alloc(pv
, hash_size
, ACONTEXT_MISC
);
458 /* Set up allocation areas */
459 func_size
= ROUNDUP(PROFILE_NUM_FUNCS
* sizeof(struct prof_ext
), page_size
);
460 misc_size
= page_size
;
462 ptr
= _profile_alloc_pages(func_size
466 ptr
= _profile_md_acontext(pv
, ptr
, func_size
, ACONTEXT_PROF
);
467 ptr
= _profile_md_acontext(pv
, ptr
, misc_size
, ACONTEXT_MISC
);
468 ptr
= _profile_md_acontext(pv
, ptr
, callback_size
, ACONTEXT_CALLBACK
);
475 * Machine dependent functions to start and stop profiling.
479 _profile_md_start(void)
481 _mcount_ptr
= _profile_vars
.md
.save_mcount_ptr
;
486 _profile_md_stop(void)
488 _mcount_ptr
= _dummy_mcount
;
494 * Free up all memory in a memory context block.
498 _profile_reset_alloc(struct profile_vars
*pv
, acontext_type_t ac
)
500 struct alloc_context
*aptr
;
501 struct page_list
*plist
;
503 for (aptr
= pv
->acontext
[ac
];
504 aptr
!= (struct alloc_context
*)0;
507 for (plist
= aptr
->plist
;
508 plist
!= (struct page_list
*)0;
509 plist
= plist
->next
) {
511 plist
->ptr
= plist
->first
;
512 plist
->bytes_free
+= plist
->bytes_allocated
;
513 plist
->bytes_allocated
= 0;
514 plist
->num_allocations
= 0;
515 memset(plist
->first
, '\0', plist
->bytes_allocated
);
522 * Reset profiling. Since the only user of this function is the kernel
523 * and the server, we don't have to worry about other stuff than gprof.
527 _profile_reset(struct profile_vars
*pv
)
529 struct alloc_context
*aptr
;
530 struct page_list
*plist
;
531 struct gfuncs
*gfunc
;
537 /* Reset all function unique pointers back to 0 */
538 for (aptr
= pv
->acontext
[ACONTEXT_GFUNC
];
539 aptr
!= (struct alloc_context
*)0;
542 for (plist
= aptr
->plist
;
543 plist
!= (struct page_list
*)0;
544 plist
= plist
->next
) {
546 for (gfunc
= (struct gfuncs
*)plist
->first
;
547 gfunc
< (struct gfuncs
*)plist
->ptr
;
550 *(gfunc
->unique_ptr
) = (struct hasharc
*)0;
556 _profile_reset_alloc(pv
, ACONTEXT_GPROF
);
557 _profile_reset_alloc(pv
, ACONTEXT_GFUNC
);
558 _profile_reset_alloc(pv
, ACONTEXT_PROF
);
560 memset((void *)pv
->profil_buf
, '\0', pv
->profil_info
.profil_len
);
561 memset((void *)pv
->md
.hash_ptr
, '\0', pv
->md
.hash_size
* sizeof(struct hasharc
*));
562 memset((void *)&pv
->stats
, '\0', sizeof(pv
->stats
));
564 pv
->stats
.stats_size
= sizeof(struct profile_stats
);
565 pv
->stats
.major_version
= PROFILE_MAJOR_VERSION
;
566 pv
->stats
.minor_version
= PROFILE_MINOR_VERSION
;
575 * Machine dependent function to write out gprof records.
579 _gprof_write(struct profile_vars
*pv
, struct callback
*callback_ptr
)
581 struct alloc_context
*aptr
;
582 struct page_list
*plist
;
584 struct hasharc
*hptr
;
587 for (aptr
= pv
->acontext
[ACONTEXT_GPROF
];
588 aptr
!= (struct alloc_context
*)0;
591 for (plist
= aptr
->plist
; plist
!= (struct page_list
*)0; plist
= plist
->next
) {
592 hptr
= (struct hasharc
*)plist
->first
;
593 for (i
= 0; i
< plist
->num_allocations
; (i
++, hptr
++)) {
595 struct gprof_arc arc
= hptr
->arc
;
596 int nrecs
= 1 + (hptr
->overflow
* 2);
599 if (pv
->check_funcs
) {
600 if (arc
.frompc
< pv
->profil_info
.lowpc
||
601 arc
.frompc
> pv
->profil_info
.highpc
) {
603 arc
.frompc
= (prof_uptrint_t
)pv
->bogus_func
;
606 if (arc
.selfpc
< pv
->profil_info
.lowpc
||
607 arc
.selfpc
> pv
->profil_info
.highpc
) {
609 arc
.selfpc
= (prof_uptrint_t
)pv
->bogus_func
;
613 /* For each overflow, emit 2 extra records with the count
615 for (j
= 0; j
< nrecs
; j
++) {
616 bytes
+= sizeof (arc
);
617 if ((*pv
->fwrite_func
)((void *)&arc
,
625 arc
.count
= 0x80000000;
636 * Machine dependent function to write out prof records.
640 _prof_write(struct profile_vars
*pv
, struct callback
*callback_ptr
)
642 struct alloc_context
*aptr
;
643 struct page_list
*plist
;
645 struct prof_ext prof_st
;
646 struct prof_int
*pptr
;
651 /* Write out information prof_mcount collects */
652 for (aptr
= pv
->acontext
[ACONTEXT_PROF
];
653 aptr
!= (struct alloc_context
*)0;
656 for (plist
= aptr
->plist
; plist
!= (struct page_list
*)0; plist
= plist
->next
) {
657 pptr
= (struct prof_int
*)plist
->first
;
659 for (i
= 0; i
< plist
->num_allocations
; (i
++, pptr
++)) {
661 /* Write out 2 records for each overflow, each with a
662 count of 0x80000000 + the normal record */
663 prof_st
= pptr
->prof
;
664 nrecs
= 1 + (pptr
->overflow
* 2);
666 for (j
= 0; j
< nrecs
; j
++) {
667 bytes
+= sizeof (struct prof_ext
);
668 if ((*pv
->fwrite_func
)((void *)&prof_st
,
676 prof_st
.cncall
= 0x80000000;
682 /* Now write out the prof information that gprof collects */
683 for (aptr
= pv
->acontext
[ACONTEXT_GFUNC
];
684 aptr
!= (struct alloc_context
*)0;
687 for (plist
= aptr
->plist
; plist
!= (struct page_list
*)0; plist
= plist
->next
) {
688 gptr
= (struct gfuncs
*)plist
->first
;
690 for (i
= 0; i
< plist
->num_allocations
; (i
++, gptr
++)) {
692 /* Write out 2 records for each overflow, each with a
693 count of 0x80000000 + the normal record */
694 prof_st
= gptr
->prof
.prof
;
695 nrecs
= 1 + (gptr
->prof
.overflow
* 2);
697 for (j
= 0; j
< nrecs
; j
++) {
698 bytes
+= sizeof (struct prof_ext
);
699 if ((*pv
->fwrite_func
)((void *)&prof_st
,
707 prof_st
.cncall
= 0x80000000;
718 * Update any statistics. For the 386, calculate the hash table loading factor.
719 * Also figure out how many overflows occured.
723 _profile_update_stats(struct profile_vars
*pv
)
725 struct alloc_context
*aptr
;
726 struct page_list
*plist
;
727 struct hasharc
*hptr
;
728 struct prof_int
*pptr
;
733 for(i
= 0; i
< MAX_BUCKETS
+1; i
++) {
734 pv
->stats
.buckets
[i
] = 0;
737 pv
->stats
.hash_buckets
= 0;
739 if (pv
->md
.hash_ptr
) {
740 for (i
= 0; i
< pv
->md
.hash_size
; i
++) {
742 struct hasharc
*hptr
;
744 for (hptr
= pv
->md
.hash_ptr
[i
]; hptr
; hptr
= hptr
->next
) {
748 pv
->stats
.buckets
[ (nbuckets
< MAX_BUCKETS
) ? nbuckets
: MAX_BUCKETS
]++;
749 if (pv
->stats
.hash_buckets
< nbuckets
) {
750 pv
->stats
.hash_buckets
= nbuckets
;
755 /* Count how many times functions are out of bounds */
756 if (pv
->check_funcs
) {
757 pv
->stats
.bogus_count
= 0;
759 for (aptr
= pv
->acontext
[ACONTEXT_GPROF
];
760 aptr
!= (struct alloc_context
*)0;
763 for (plist
= aptr
->plist
;
764 plist
!= (struct page_list
*)0;
765 plist
= plist
->next
) {
767 hptr
= (struct hasharc
*)plist
->first
;
768 for (i
= 0; i
< plist
->num_allocations
; (i
++, hptr
++)) {
770 if (hptr
->arc
.frompc
< pv
->profil_info
.lowpc
||
771 hptr
->arc
.frompc
> pv
->profil_info
.highpc
) {
772 pv
->stats
.bogus_count
++;
775 if (hptr
->arc
.selfpc
< pv
->profil_info
.lowpc
||
776 hptr
->arc
.selfpc
> pv
->profil_info
.highpc
) {
777 pv
->stats
.bogus_count
++;
784 /* Figure out how many overflows occurred */
785 PROF_ULONG_TO_CNT(pv
->stats
.prof_overflow
, 0);
786 PROF_ULONG_TO_CNT(pv
->stats
.gprof_overflow
, 0);
788 for (aptr
= pv
->acontext
[ACONTEXT_GPROF
];
789 aptr
!= (struct alloc_context
*)0;
792 for (plist
= aptr
->plist
;
793 plist
!= (struct page_list
*)0;
794 plist
= plist
->next
) {
796 hptr
= (struct hasharc
*)plist
->first
;
797 for (i
= 0; i
< plist
->num_allocations
; (i
++, hptr
++)) {
798 PROF_CNT_ADD(pv
->stats
.gprof_overflow
, hptr
->overflow
);
803 for (aptr
= pv
->acontext
[ACONTEXT_PROF
];
804 aptr
!= (struct alloc_context
*)0;
807 for (plist
= aptr
->plist
;
808 plist
!= (struct page_list
*)0;
809 plist
= plist
->next
) {
811 pptr
= (struct prof_int
*)plist
->first
;
812 for (i
= 0; i
< plist
->num_allocations
; (i
++, pptr
++)) {
813 PROF_CNT_ADD(pv
->stats
.prof_overflow
, pptr
->overflow
);
818 for (aptr
= pv
->acontext
[ACONTEXT_GFUNC
];
819 aptr
!= (struct alloc_context
*)0;
822 for (plist
= aptr
->plist
;
823 plist
!= (struct page_list
*)0;
824 plist
= plist
->next
) {
826 fptr
= (struct gfuncs
*)plist
->first
;
827 for (i
= 0; i
< plist
->num_allocations
; (i
++, fptr
++)) {
828 PROF_CNT_ADD(pv
->stats
.prof_overflow
, fptr
->prof
.overflow
);
833 /* Now go through & count how many times the LHISTCOUNTER overflowed into a 2nd word */
834 lptr
= (LHISTCOUNTER
*)pv
->profil_buf
;
836 if (pv
->use_profil
&&
837 pv
->profil_info
.counter_size
== sizeof(LHISTCOUNTER
) &&
838 lptr
!= (LHISTCOUNTER
*)0) {
840 PROF_ULONG_TO_CNT(pv
->stats
.overflow_ticks
, 0);
841 for (i
= 0; i
< pv
->stats
.profil_buckets
; i
++) {
842 PROF_CNT_ADD(pv
->stats
.overflow_ticks
, lptr
[i
].high
);
847 #if !defined(_KERNEL) && !defined(MACH_KERNEL)
850 * Routine callable from the debugger that prints the statistics.
853 int _profile_debug(void)
855 _profile_update_stats(&_profile_vars
);
856 _profile_print_stats(stderr
, &_profile_vars
.stats
, &_profile_vars
.profil_info
);
861 * Print the statistics structure in a meaningful way.
864 void _profile_print_stats(FILE *stream
,
865 const struct profile_stats
*stats
,
866 const struct profile_profil
*pinfo
)
869 prof_cnt_t total_hits
;
873 int width_wasted
= 0;
874 int width_overhead
= 0;
875 int width_context
= 0;
876 static const char *cname
[ACONTEXT_MAX
] = ACONTEXT_NAMES
;
887 sprintf(buf
, "%ld.%ld", (long)stats
->major_version
, (long)stats
->minor_version
);
888 fprintf(stream
, "%12s profiling version number\n", buf
);
889 fprintf(stream
, "%12lu size of profile_vars\n", (long unsigned)sizeof(struct profile_vars
));
890 fprintf(stream
, "%12lu size of profile_stats\n", (long unsigned)sizeof(struct profile_stats
));
891 fprintf(stream
, "%12lu size of profile_md\n", (long unsigned)sizeof(struct profile_md
));
892 fprintf(stream
, "%12s calls to _{,g}prof_mcount\n", PROF_CNT_TO_DECIMAL((char *)0, stats
->cnt
));
893 fprintf(stream
, "%12s calls to old mcount\n", PROF_CNT_TO_DECIMAL((char *)0, stats
->old_mcount
));
894 fprintf(stream
, "%12s calls to _dummy_mcount\n", PROF_CNT_TO_DECIMAL((char *)0, stats
->dummy
));
895 fprintf(stream
, "%12lu functions profiled\n", (long unsigned)stats
->prof_records
);
896 fprintf(stream
, "%12lu gprof arcs\n", (long unsigned)stats
->gprof_records
);
899 fprintf(stream
, "%12lu profil buckets\n", (long unsigned)stats
->profil_buckets
);
900 fprintf(stream
, "%12lu profil lowpc [0x%lx]\n",
901 (long unsigned)pinfo
->lowpc
,
902 (long unsigned)pinfo
->lowpc
);
904 fprintf(stream
, "%12lu profil highpc [0x%lx]\n",
905 (long unsigned)pinfo
->highpc
,
906 (long unsigned)pinfo
->highpc
);
908 fprintf(stream
, "%12lu profil highpc-lowpc\n", (long unsigned)(pinfo
->highpc
- pinfo
->lowpc
));
909 fprintf(stream
, "%12lu profil buffer length\n", (long unsigned)pinfo
->profil_len
);
910 fprintf(stream
, "%12lu profil sizeof counters\n", (long unsigned)pinfo
->counter_size
);
911 fprintf(stream
, "%12lu profil scale (%g)\n",
912 (long unsigned)pinfo
->scale
,
913 ((double)pinfo
->scale
) / ((double) 0x10000));
916 for (i
= 0; i
< sizeof (pinfo
->profil_unused
) / sizeof (pinfo
->profil_unused
[0]); i
++) {
917 if (pinfo
->profil_unused
[i
]) {
918 fprintf(stream
, "%12lu profil unused[%2d] {0x%.8lx}\n",
919 (long unsigned)pinfo
->profil_unused
[i
],
921 (long unsigned)pinfo
->profil_unused
[i
]);
926 if (stats
->max_cpu
) {
927 fprintf(stream
, "%12lu current cpu/thread\n", (long unsigned)stats
->my_cpu
);
928 fprintf(stream
, "%12lu max cpu/thread+1\n", (long unsigned)stats
->max_cpu
);
931 if (stats
->bogus_count
!= 0) {
933 "%12lu gprof functions found outside of range\n",
934 (long unsigned)stats
->bogus_count
);
937 if (PROF_CNT_NE_0(stats
->too_low
)) {
939 "%12s histogram ticks were too low\n",
940 PROF_CNT_TO_DECIMAL((char *)0, stats
->too_low
));
943 if (PROF_CNT_NE_0(stats
->too_high
)) {
945 "%12s histogram ticks were too high\n",
946 PROF_CNT_TO_DECIMAL((char *)0, stats
->too_high
));
949 if (PROF_CNT_NE_0(stats
->acontext_locked
)) {
951 "%12s times an allocation context was locked\n",
952 PROF_CNT_TO_DECIMAL((char *)0, stats
->acontext_locked
));
955 if (PROF_CNT_NE_0(stats
->kernel_ticks
)
956 || PROF_CNT_NE_0(stats
->user_ticks
)
957 || PROF_CNT_NE_0(stats
->idle_ticks
)) {
959 prof_cnt_t total_ticks
;
960 long double total_ticks_dbl
;
962 total_ticks
= stats
->kernel_ticks
;
963 PROF_CNT_LADD(total_ticks
, stats
->user_ticks
);
964 PROF_CNT_LADD(total_ticks
, stats
->idle_ticks
);
965 total_ticks_dbl
= PROF_CNT_TO_LDOUBLE(total_ticks
);
968 "%12s total ticks\n",
969 PROF_CNT_TO_DECIMAL((char *)0, total_ticks
));
972 "%12s ticks within the kernel (%5.2Lf%%)\n",
973 PROF_CNT_TO_DECIMAL((char *)0, stats
->kernel_ticks
),
974 100.0L * (PROF_CNT_TO_LDOUBLE(stats
->kernel_ticks
) / total_ticks_dbl
));
977 "%12s ticks within user space (%5.2Lf%%)\n",
978 PROF_CNT_TO_DECIMAL((char *)0, stats
->user_ticks
),
979 100.0L * (PROF_CNT_TO_LDOUBLE(stats
->user_ticks
) / total_ticks_dbl
));
982 "%12s ticks idle (%5.2Lf%%)\n",
983 PROF_CNT_TO_DECIMAL((char *)0, stats
->idle_ticks
),
984 100.0L * (PROF_CNT_TO_LDOUBLE(stats
->idle_ticks
) / total_ticks_dbl
));
987 if (PROF_CNT_NE_0(stats
->overflow_ticks
)) {
988 fprintf(stream
, "%12s times a HISTCOUNTER counter would have overflowed\n",
989 PROF_CNT_TO_DECIMAL((char *)0, stats
->overflow_ticks
));
992 if (PROF_CNT_NE_0(stats
->hash_num
)) {
993 long double total_buckets
= 0.0L;
995 for (i
= 0; i
<= MAX_BUCKETS
; i
++) {
996 total_buckets
+= (long double)stats
->buckets
[i
];
999 fprintf(stream
, "%12lu max bucket(s) on hash chain.\n", (long unsigned)stats
->hash_buckets
);
1000 for (i
= 0; i
< MAX_BUCKETS
; i
++) {
1001 if (stats
->buckets
[i
] != 0) {
1002 fprintf(stream
, "%12lu bucket(s) had %d entries (%5.2Lf%%)\n",
1003 (long unsigned)stats
->buckets
[i
], i
,
1004 100.0L * ((long double)stats
->buckets
[i
] / total_buckets
));
1008 if (stats
->buckets
[MAX_BUCKETS
] != 0) {
1009 fprintf(stream
, "%12lu bucket(s) had more than %d entries (%5.2Lf%%)\n",
1010 (long unsigned)stats
->buckets
[MAX_BUCKETS
], MAX_BUCKETS
,
1011 100.0L * ((long double)stats
->buckets
[MAX_BUCKETS
] / total_buckets
));
1015 PROF_ULONG_TO_CNT(total_hits
, 0);
1016 for (i
= 0; i
< MAX_CACHE
; i
++) {
1017 PROF_CNT_LADD(total_hits
, stats
->cache_hits
[i
]);
1020 if (PROF_CNT_NE_0(total_hits
)) {
1021 long double total
= PROF_CNT_TO_LDOUBLE(stats
->cnt
);
1022 long double total_hits_dbl
= PROF_CNT_TO_LDOUBLE(total_hits
);
1025 "%12s cache hits (%.2Lf%%)\n",
1026 PROF_CNT_TO_DECIMAL((char *)0, total_hits
),
1027 100.0L * (total_hits_dbl
/ total
));
1029 for (i
= 0; i
< MAX_CACHE
; i
++) {
1030 if (PROF_CNT_NE_0(stats
->cache_hits
[i
])) {
1032 "%12s times cache#%d matched (%5.2Lf%% of cache hits, %5.2Lf%% total)\n",
1033 PROF_CNT_TO_DECIMAL((char *)0, stats
->cache_hits
[i
]),
1035 100.0L * (PROF_CNT_TO_LDOUBLE(stats
->cache_hits
[i
]) / total_hits_dbl
),
1036 100.0L * (PROF_CNT_TO_LDOUBLE(stats
->cache_hits
[i
]) / total
));
1040 if (PROF_CNT_NE_0(stats
->hash_num
)) {
1041 fprintf(stream
, "%12s times hash table searched\n", PROF_CNT_TO_DECIMAL((char *)0, stats
->hash_num
));
1042 fprintf(stream
, "%12s hash buckets searched\n", PROF_CNT_TO_DECIMAL((char *)0, stats
->hash_search
));
1043 fprintf(stream
, "%12.4Lf average buckets searched\n",
1044 PROF_CNT_TO_LDOUBLE(stats
->hash_search
) / PROF_CNT_TO_LDOUBLE(stats
->hash_num
));
1048 for (i
= 0; i
< sizeof (stats
->stats_unused
) / sizeof (stats
->stats_unused
[0]); i
++) {
1049 if (PROF_CNT_NE_0(stats
->stats_unused
[i
])) {
1050 fprintf(stream
, "%12s unused[%2d] {0x%.8lx 0x%.8lx}\n",
1051 PROF_CNT_TO_DECIMAL((char *)0, stats
->stats_unused
[i
]),
1053 (unsigned long)stats
->stats_unused
[i
].high
,
1054 (unsigned long)stats
->stats_unused
[i
].low
);
1058 /* Get the width for the allocation contexts */
1059 for (ac
= ACONTEXT_FIRST
; ac
< ACONTEXT_MAX
; ac
++) {
1062 if (stats
->num_context
[ac
] == 0) {
1066 len
= strlen (cname
[ac
]);
1067 if (len
> width_cname
)
1070 len
= sprintf (buf
, "%lu", (long unsigned)stats
->num_alloc
[ac
]);
1071 if (len
> width_alloc
)
1074 len
= sprintf (buf
, "%lu", (long unsigned)stats
->wasted
[ac
]);
1075 if (len
> width_wasted
)
1078 len
= sprintf (buf
, "%lu", (long unsigned)stats
->overhead
[ac
]);
1079 if (len
> width_overhead
)
1080 width_overhead
= len
;
1082 len
= sprintf (buf
, "%lu", (long unsigned)stats
->num_context
[ac
]);
1083 if (len
> width_context
)
1084 width_context
= len
;
1087 /* Print info about allocation contexts */
1088 for (ac
= ACONTEXT_FIRST
; ac
< ACONTEXT_MAX
; ac
++) {
1089 if (stats
->num_context
[ac
] == 0) {
1094 "%12lu bytes in %-*s %*lu alloc, %*lu unused, %*lu over, %*lu context\n",
1095 (long unsigned)stats
->bytes_alloc
[ac
],
1096 width_cname
, cname
[ac
],
1097 width_alloc
, (long unsigned)stats
->num_alloc
[ac
],
1098 width_wasted
, (long unsigned)stats
->wasted
[ac
],
1099 width_overhead
, (long unsigned)stats
->overhead
[ac
],
1100 width_context
, (long unsigned)stats
->num_context
[ac
]);
1106 * Merge a new statistics field into an old one.
1109 void _profile_merge_stats(struct profile_stats
*old_stats
, const struct profile_stats
*new_stats
)
1113 /* If nothing passed, just return */
1114 if (!old_stats
|| !new_stats
)
1117 /* If the old_stats has not been initialized, just copy in the new stats */
1118 if (old_stats
->major_version
== 0) {
1119 *old_stats
= *new_stats
;
1121 /* Otherwise, update stats, field by field */
1123 if (old_stats
->prof_records
< new_stats
->prof_records
)
1124 old_stats
->prof_records
= new_stats
->prof_records
;
1126 if (old_stats
->gprof_records
< new_stats
->gprof_records
)
1127 old_stats
->gprof_records
= new_stats
->gprof_records
;
1129 if (old_stats
->hash_buckets
< new_stats
->hash_buckets
)
1130 old_stats
->hash_buckets
= new_stats
->hash_buckets
;
1132 if (old_stats
->bogus_count
< new_stats
->bogus_count
)
1133 old_stats
->bogus_count
= new_stats
->bogus_count
;
1135 PROF_CNT_LADD(old_stats
->cnt
, new_stats
->cnt
);
1136 PROF_CNT_LADD(old_stats
->dummy
, new_stats
->dummy
);
1137 PROF_CNT_LADD(old_stats
->old_mcount
, new_stats
->old_mcount
);
1138 PROF_CNT_LADD(old_stats
->hash_search
, new_stats
->hash_search
);
1139 PROF_CNT_LADD(old_stats
->hash_num
, new_stats
->hash_num
);
1140 PROF_CNT_LADD(old_stats
->user_ticks
, new_stats
->user_ticks
);
1141 PROF_CNT_LADD(old_stats
->kernel_ticks
, new_stats
->kernel_ticks
);
1142 PROF_CNT_LADD(old_stats
->idle_ticks
, new_stats
->idle_ticks
);
1143 PROF_CNT_LADD(old_stats
->overflow_ticks
, new_stats
->overflow_ticks
);
1144 PROF_CNT_LADD(old_stats
->acontext_locked
, new_stats
->acontext_locked
);
1145 PROF_CNT_LADD(old_stats
->too_low
, new_stats
->too_low
);
1146 PROF_CNT_LADD(old_stats
->too_high
, new_stats
->too_high
);
1147 PROF_CNT_LADD(old_stats
->prof_overflow
, new_stats
->prof_overflow
);
1148 PROF_CNT_LADD(old_stats
->gprof_overflow
, new_stats
->gprof_overflow
);
1150 for (i
= 0; i
< (int)ACONTEXT_MAX
; i
++) {
1151 if (old_stats
->num_alloc
[i
] < new_stats
->num_alloc
[i
])
1152 old_stats
->num_alloc
[i
] = new_stats
->num_alloc
[i
];
1154 if (old_stats
->bytes_alloc
[i
] < new_stats
->bytes_alloc
[i
])
1155 old_stats
->bytes_alloc
[i
] = new_stats
->bytes_alloc
[i
];
1157 if (old_stats
->num_context
[i
] < new_stats
->num_context
[i
])
1158 old_stats
->num_context
[i
] = new_stats
->num_context
[i
];
1160 if (old_stats
->wasted
[i
] < new_stats
->wasted
[i
])
1161 old_stats
->wasted
[i
] = new_stats
->wasted
[i
];
1163 if (old_stats
->overhead
[i
] < new_stats
->overhead
[i
])
1164 old_stats
->overhead
[i
] = new_stats
->overhead
[i
];
1168 for (i
= 0; i
< MAX_BUCKETS
+1; i
++) {
1169 if (old_stats
->buckets
[i
] < new_stats
->buckets
[i
])
1170 old_stats
->buckets
[i
] = new_stats
->buckets
[i
];
1173 for (i
= 0; i
< MAX_CACHE
; i
++) {
1174 PROF_CNT_LADD(old_stats
->cache_hits
[i
], new_stats
->cache_hits
[i
]);
1177 for (i
= 0; i
< sizeof(old_stats
->stats_unused
) / sizeof(old_stats
->stats_unused
[0]); i
++) {
1178 PROF_CNT_LADD(old_stats
->stats_unused
[i
], new_stats
->stats_unused
[i
]);
1187 * Invalid function address used when checking of function addresses is
1188 * desired for gprof arcs, and we discover an address out of bounds.
1189 * There should be no callers of this function.
1193 _bogus_function(void)