2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
34 * Revision 1.1.1.1 1998/09/22 21:05:49 wsanchez
35 * Import of Mac OS X kernel (~semeria)
37 * Revision 1.1.1.1 1998/03/07 02:26:08 wsanchez
38 * Import of OSF Mach kernel (~mburg)
40 * Revision 1.1.5.1 1995/01/06 19:53:45 devrcs
41 * mk6 CR668 - 1.3b26 merge
43 * [1994/10/12 22:25:24 dwm]
45 * Revision 1.1.2.2 1994/05/16 19:19:22 meissner
46 * Protect against hash_ptr being null in _profile_update_stats.
47 * [1994/05/16 17:23:53 meissner]
49 * Remove _profile_cnt_to_hex, _profile_strbuffer.
50 * _profile_print_stats now takes const pointers.
51 * Use the new 64-bit arithmetic support instead of converting to double.
52 * Add _profile_merge_stats to merge statistics.
53 * [1994/04/28 21:45:04 meissner]
55 * If MACH_ASSERT is on in server or kernel, turn on profiling printfs.
56 * Print out fractional digits for average # of hash searches in stats.
57 * Update overflow_ticks for # times the lprofil counter overflows into high word.
58 * Don't make sizes of C/asm structures a const array, since it has pointers in it.
59 * Add support for converting 64 bit ints to a string.
60 * Use PROF_CNT_TO_DECIMAL where possible instead of PROF_CNT_TO_LDOUBLE.
61 * [1994/04/20 15:47:02 meissner]
63 * Revision 1.1.2.1 1994/04/08 17:51:51 meissner
65 * [1994/04/08 02:11:40 meissner]
67 * Make most stats 64 bits, except for things like memory allocation.
68 * [1994/04/02 14:58:28 meissner]
70 * Add some printfs under #idef DEBUG_PROFILE.
71 * [1994/03/29 21:00:11 meissner]
73 * Further changes for gprof/prof overflow support.
74 * Add overflow support for {gprof,prof,old,dummy}_mcount counters.
75 * [1994/03/17 20:13:31 meissner]
77 * Add gprof/prof overflow support
78 * [1994/03/17 14:56:51 meissner]
80 * Use memset instead of bzero.
81 * [1994/02/28 23:56:10 meissner]
83 * Add size of histogram counters & unused fields to profile_profil struct
84 * [1994/02/17 21:41:50 meissner]
86 * Allocate slop space for server in addition to microkernel.
87 * Add 3rd argument to _profile_print_stats for profil info.
88 * Print # histogram ticks too low/too high for server/mk.
89 * [1994/02/16 22:38:18 meissner]
91 * Calculate percentages for # of hash buckets.
92 * [1994/02/11 16:52:04 meissner]
94 * Print stats as an unsigned number.
95 * [1994/02/07 18:47:05 meissner]
97 * For kernel and server, include <kern/assert.h> not <assert.h>.
98 * Always do assert on comparing asm vs. C structure sizes.
99 * Add _profile_reset to reset profiling information.
100 * Add _profile_update_stats to update the statistics.
101 * Move _gprof_write code that updates hash stats to _profile_update_stats.
102 * Don't allocate space for basic block support just yet.
103 * Add support for range checking the gprof arc {from,self}pc addresses.
104 * _profile_debug now calls _profile_update_stats.
105 * Print how many times the acontext was locked.
106 * If DEBUG_PROFILE is defined, set pv->debug to 1.
108 * [1994/02/07 12:41:03 meissner]
110 * Keep track of the number of times the kernel overflows the HISTCOUNTER counter.
111 * [1994/02/03 20:13:28 meissner]
113 * Add stats for {user,kernel,idle} mode in the kernel.
114 * [1994/02/03 15:17:31 meissner]
116 * Print unused stats in hex as well as decimal.
117 * [1994/02/03 14:52:20 meissner]
119 * _profile_print_stats no longer takes profile_{vars,md} pointer arguments.
120 * If stream is NULL, _profile_print_stats will use stdout.
121 * Separate _profile_update_stats from _gprof_write.
122 * [1994/02/03 00:58:55 meissner]
124 * Combine _profile_{vars,stats,md}; Allow more than one _profile_vars.
125 * [1994/02/01 12:04:01 meissner]
127 * Add allocation flag to _profile_md_init.
128 * Fix core dumps in _profile_print_stats if no profile_vars ptr passed.
129 * Print numbers in 12 columns, not 8.
130 * Print my_cpu/max_cpu if max_cpu != 0.
131 * Make allocations print like other stats.
132 * Use ACONTEXT_FIRST to start loop on, not ACONTEXT_PROF.
133 * [1994/01/28 23:33:26 meissner]
135 * Move callback pointers into separate allocation context.
136 * Add size fields for other structures to profile-vars.
137 * [1994/01/26 20:23:37 meissner]
139 * Allocate initial memory at startup.
140 * Print structure sizes and version number when printing stats.
141 * Initialize size fields and version numbers.
142 * Allocation context pointers moved to _profile_vars.
143 * [1994/01/25 01:46:04 meissner]
145 * Move init code here from assembly language.
146 * [1994/01/22 01:13:21 meissner]
148 * Include <profile/profile-internal.h> instead of "profile-md.h".
149 * [1994/01/20 20:56:49 meissner]
152 * [1994/01/18 23:08:02 meissner]
154 * Rename profile.h -> profile-md.h.
155 * [1994/01/18 19:44:57 meissner]
157 * Write out stats unused fields.
158 * Make _prof_write write out the prof stats gprof collects.
159 * [1994/01/15 18:40:37 meissner]
161 * Remove debug code called from profile-asm.s.
162 * Always print out the # of profil buckets.
163 * [1994/01/15 00:59:06 meissner]
166 * [1994/01/04 16:34:46 meissner]
168 * Move max hash bucket calculation into _gprof_write & put info in stats structure.
169 * [1994/01/04 16:15:17 meissner]
171 * Use _profile_printf to write diagnostics; add diag_stream to hold stream to write to.
172 * [1994/01/04 15:37:46 meissner]
174 * Correctly handle case where more than one allocation context was
175 * allocated due to multiple threads.
176 * Cast stats to long for output.
177 * Print number of profil buckets field in _profile_stats.
178 * Add support for GFUNC allocation context.
179 * [1994/01/04 14:26:00 meissner]
181 * CR 10198 - Initial version.
182 * [1994/01/01 22:44:10 meissne
187 #include <profiling/profile-internal.h>
188 #include <vm/vm_kern.h>
192 #if defined(MACH_KERNEL) || defined(_KERNEL)
194 #include <mach_assert.h>
195 #if MACH_ASSERT && !defined(DEBUG_PROFILE)
196 #define DEBUG_PROFILE 1
201 #define panic(str) exit(1)
204 #ifndef PROFILE_NUM_FUNCS
205 #define PROFILE_NUM_FUNCS 2000
208 #ifndef PROFILE_NUM_ARCS
209 #define PROFILE_NUM_ARCS 8000
213 * Information passed on from profile-asm.s
216 extern int _profile_do_stats
;
217 extern size_t _profile_size
;
218 extern size_t _profile_stats_size
;
219 extern size_t _profile_md_size
;
220 extern size_t _profile_profil_size
;
221 extern size_t _profile_hash_size
;
224 * All profiling variables, and a dummy gprof record.
227 struct profile_vars _profile_vars
= { 0 };
228 struct hasharc _gprof_dummy
= { 0 };
231 * Forward references.
234 static void *_profile_md_acontext(struct profile_vars
*pv
,
237 acontext_type_t type
);
239 static void _profile_reset_alloc(struct profile_vars
*,
242 extern void _bogus_function(void);
246 struct profile_vars
*_profile_vars_cpus
[NCPUS
] = { &_profile_vars
};
247 struct profile_vars _profile_vars_aux
[NCPUS
-1];
248 #define PROFILE_VARS(cpu) (_profile_vars_cpus[(cpu)])
250 #define PROFILE_VARS(cpu) (&_profile_vars)
254 _profile_alloc_pages (size_t size
)
259 * For the MK, we can't support allocating pages at runtime, because we
260 * might be at interrupt level, so abort if we didn't size the table
264 if (PROFILE_VARS(0)->active
) {
265 panic("Call to _profile_alloc_pages while profiling is running.");
268 if (kmem_alloc(kernel_map
, &addr
, size
)) {
269 panic("Could not allocate memory for profiling");
272 memset((void *)addr
, '\0', size
);
273 if (PROFILE_VARS(0)->debug
) {
274 printf("Allocated %d bytes for profiling, address 0x%x\n", (int)size
, (int)addr
);
277 return((caddr_t
)addr
);
281 _profile_free_pages(void *addr
, size_t size
)
283 if (PROFILE_VARS(0)->debug
) {
284 printf("Freed %d bytes for profiling, address 0x%x\n", (int)size
, (int)addr
);
287 kmem_free(kernel_map
, (vm_offset_t
)addr
, size
);
291 void _profile_error(struct profile_vars
*pv
)
293 panic("Fatal error in profiling");
298 * Function to set up the initial allocation for a context block.
302 _profile_md_acontext(struct profile_vars
*pv
,
305 acontext_type_t type
)
308 struct alloc_context context
;
309 struct page_list plist
;
313 struct memory
*mptr
= (struct memory
*)ptr
;
314 struct alloc_context
*context
= &mptr
->context
;
315 struct page_list
*plist
= &mptr
->plist
;
318 _profile_printf("_profile_md_acontext: pv= 0x%lx, ptr= 0x%lx, len= %6ld, type= %d\n",
325 /* Fill in context block header */
326 context
->next
= pv
->acontext
[type
];
327 context
->plist
= plist
;
330 /* Fill in first page list information */
331 plist
->ptr
= plist
->first
= (void *)&mptr
->data
[0];
332 plist
->next
= (struct page_list
*)0;
333 plist
->bytes_free
= len
- ((char *)plist
->ptr
- (char *)ptr
);
334 plist
->bytes_allocated
= 0;
335 plist
->num_allocations
= 0;
337 /* Update statistics */
338 pv
->stats
.num_context
[type
]++;
339 pv
->stats
.wasted
[type
] += plist
->bytes_free
;
340 pv
->stats
.overhead
[type
] += len
- plist
->bytes_free
;
342 /* And setup context block */
343 pv
->acontext
[type
] = context
;
345 return (void *)((char *)ptr
+len
);
350 * Machine dependent function to initialize things.
354 _profile_md_init(struct profile_vars
*pv
,
356 profile_alloc_mem_t alloc_mem
)
358 size_t page_size
= pv
->page_size
;
363 size_t extra_arc_size
;
364 size_t extra_func_size
;
365 size_t callback_size
= page_size
;
370 size_t c_size
; /* size C thinks structure is */
371 size_t *asm_size_ptr
; /* pointer to size asm thinks struct is */
372 const char *name
; /* structure name */
374 { sizeof(struct profile_profil
), &_profile_profil_size
, "profile_profil" },
375 { sizeof(struct profile_stats
), &_profile_stats_size
, "profile_stats" },
376 { sizeof(struct profile_md
), &_profile_md_size
, "profile_md" },
377 { sizeof(struct profile_vars
), &_profile_size
, "profile_vars" }};
380 _profile_printf("_profile_md_init: pv = 0x%lx, type = %d, alloc = %d\n",
386 for (i
= 0; i
< sizeof (sizes
) / sizeof(sizes
[0]); i
++) {
387 if (sizes
[i
].c_size
!= *sizes
[i
].asm_size_ptr
) {
388 _profile_printf("C thinks struct %s is %ld bytes, asm thinks it is %ld bytes\n",
390 (long)sizes
[i
].c_size
,
391 (long)*sizes
[i
].asm_size_ptr
);
393 panic(sizes
[i
].name
);
397 /* Figure out which function will handle compiler generated profiling */
398 if (type
== PROFILE_GPROF
) {
399 pv
->md
.save_mcount_ptr
= _gprof_mcount
;
401 } else if (type
== PROFILE_PROF
) {
402 pv
->md
.save_mcount_ptr
= _prof_mcount
;
405 pv
->md
.save_mcount_ptr
= _dummy_mcount
;
408 pv
->vars_size
= sizeof(struct profile_vars
);
409 pv
->plist_size
= sizeof(struct page_list
);
410 pv
->acontext_size
= sizeof(struct alloc_context
);
411 pv
->callback_size
= sizeof(struct callback
);
412 pv
->major_version
= PROFILE_MAJOR_VERSION
;
413 pv
->minor_version
= PROFILE_MINOR_VERSION
;
418 pv
->output_uarea
= 1;
419 pv
->output_stats
= (prof_flag_t
) _profile_do_stats
;
420 pv
->output_clock
= 1;
421 pv
->multiple_sections
= 1;
423 pv
->bogus_func
= _bogus_function
;
429 if (!pv
->error_msg
) {
430 pv
->error_msg
= "error in profiling";
433 if (!pv
->page_size
) {
434 pv
->page_size
= 4096;
437 pv
->stats
.stats_size
= sizeof(struct profile_stats
);
438 pv
->stats
.major_version
= PROFILE_MAJOR_VERSION
;
439 pv
->stats
.minor_version
= PROFILE_MINOR_VERSION
;
441 pv
->md
.md_size
= sizeof(struct profile_md
);
442 pv
->md
.major_version
= PROFILE_MAJOR_VERSION
;
443 pv
->md
.minor_version
= PROFILE_MINOR_VERSION
;
444 pv
->md
.hash_size
= _profile_hash_size
;
445 pv
->md
.num_cache
= MAX_CACHE
;
446 pv
->md
.mcount_ptr_ptr
= &_mcount_ptr
;
447 pv
->md
.dummy_ptr
= &_gprof_dummy
;
448 pv
->md
.alloc_pages
= _profile_alloc_pages
;
450 /* zero out all allocation context blocks */
451 for (ac
= ACONTEXT_FIRST
; ac
< ACONTEXT_MAX
; ac
++) {
452 pv
->acontext
[ac
] = (struct alloc_context
*)0;
455 /* Don't allocate memory if not desired */
460 /* Allocate some space for the initial allocations */
463 misc_size
= page_size
;
464 ptr
= _profile_alloc_pages(misc_size
+ callback_size
);
465 ptr
= _profile_md_acontext(pv
, ptr
, misc_size
, ACONTEXT_MISC
);
466 ptr
= _profile_md_acontext(pv
, ptr
, callback_size
, ACONTEXT_CALLBACK
);
471 #if defined(MACH_KERNEL) || defined(_KERNEL)
473 * For the MK & server allocate some slop space now for the
474 * secondary context blocks in case allocations are done at
475 * interrupt level when another allocation is being done. This
476 * is done before the main allocation blocks and will be pushed
477 * so that it will only be used when the main allocation block
480 extra_arc_size
= 4*page_size
;
481 extra_func_size
= 2*page_size
;
483 extra_arc_size
= extra_func_size
= 0;
486 /* Set up allocation areas */
487 arc_size
= ROUNDUP(PROFILE_NUM_ARCS
* sizeof(struct hasharc
), page_size
);
488 func_size
= ROUNDUP(PROFILE_NUM_FUNCS
* sizeof(struct gfuncs
), page_size
);
489 hash_size
= _profile_hash_size
* sizeof (struct hasharc
*);
490 misc_size
= ROUNDUP(hash_size
+ page_size
, page_size
);
492 ptr
= _profile_alloc_pages(arc_size
499 #if defined(MACH_KERNEL) || defined(_KERNEL)
500 ptr
= _profile_md_acontext(pv
, ptr
, extra_arc_size
, ACONTEXT_GPROF
);
501 ptr
= _profile_md_acontext(pv
, ptr
, extra_func_size
, ACONTEXT_GFUNC
);
503 ptr
= _profile_md_acontext(pv
, ptr
, arc_size
, ACONTEXT_GPROF
);
504 ptr
= _profile_md_acontext(pv
, ptr
, func_size
, ACONTEXT_GFUNC
);
505 ptr
= _profile_md_acontext(pv
, ptr
, misc_size
, ACONTEXT_MISC
);
506 ptr
= _profile_md_acontext(pv
, ptr
, callback_size
, ACONTEXT_CALLBACK
);
508 /* Allocate hash table */
509 pv
->md
.hash_ptr
= (struct hasharc
**) _profile_alloc(pv
, hash_size
, ACONTEXT_MISC
);
513 /* Set up allocation areas */
514 func_size
= ROUNDUP(PROFILE_NUM_FUNCS
* sizeof(struct prof_ext
), page_size
);
515 misc_size
= page_size
;
517 ptr
= _profile_alloc_pages(func_size
521 ptr
= _profile_md_acontext(pv
, ptr
, func_size
, ACONTEXT_PROF
);
522 ptr
= _profile_md_acontext(pv
, ptr
, misc_size
, ACONTEXT_MISC
);
523 ptr
= _profile_md_acontext(pv
, ptr
, callback_size
, ACONTEXT_CALLBACK
);
530 * Machine dependent functions to start and stop profiling.
534 _profile_md_start(void)
536 _mcount_ptr
= _profile_vars
.md
.save_mcount_ptr
;
541 _profile_md_stop(void)
543 _mcount_ptr
= _dummy_mcount
;
549 * Free up all memory in a memory context block.
553 _profile_reset_alloc(struct profile_vars
*pv
, acontext_type_t ac
)
555 struct alloc_context
*aptr
;
556 struct page_list
*plist
;
558 for (aptr
= pv
->acontext
[ac
];
559 aptr
!= (struct alloc_context
*)0;
562 for (plist
= aptr
->plist
;
563 plist
!= (struct page_list
*)0;
564 plist
= plist
->next
) {
566 plist
->ptr
= plist
->first
;
567 plist
->bytes_free
+= plist
->bytes_allocated
;
568 plist
->bytes_allocated
= 0;
569 plist
->num_allocations
= 0;
570 memset(plist
->first
, '\0', plist
->bytes_allocated
);
577 * Reset profiling. Since the only user of this function is the kernel
578 * and the server, we don't have to worry about other stuff than gprof.
582 _profile_reset(struct profile_vars
*pv
)
584 struct alloc_context
*aptr
;
585 struct page_list
*plist
;
586 struct gfuncs
*gfunc
;
592 /* Reset all function unique pointers back to 0 */
593 for (aptr
= pv
->acontext
[ACONTEXT_GFUNC
];
594 aptr
!= (struct alloc_context
*)0;
597 for (plist
= aptr
->plist
;
598 plist
!= (struct page_list
*)0;
599 plist
= plist
->next
) {
601 for (gfunc
= (struct gfuncs
*)plist
->first
;
602 gfunc
< (struct gfuncs
*)plist
->ptr
;
605 *(gfunc
->unique_ptr
) = (struct hasharc
*)0;
611 _profile_reset_alloc(pv
, ACONTEXT_GPROF
);
612 _profile_reset_alloc(pv
, ACONTEXT_GFUNC
);
613 _profile_reset_alloc(pv
, ACONTEXT_PROF
);
615 memset((void *)pv
->profil_buf
, '\0', pv
->profil_info
.profil_len
);
616 memset((void *)pv
->md
.hash_ptr
, '\0', pv
->md
.hash_size
* sizeof(struct hasharc
*));
617 memset((void *)&pv
->stats
, '\0', sizeof(pv
->stats
));
619 pv
->stats
.stats_size
= sizeof(struct profile_stats
);
620 pv
->stats
.major_version
= PROFILE_MAJOR_VERSION
;
621 pv
->stats
.minor_version
= PROFILE_MINOR_VERSION
;
630 * Machine dependent function to write out gprof records.
634 _gprof_write(struct profile_vars
*pv
, struct callback
*callback_ptr
)
636 struct alloc_context
*aptr
;
637 struct page_list
*plist
;
639 struct hasharc
*hptr
;
642 for (aptr
= pv
->acontext
[ACONTEXT_GPROF
];
643 aptr
!= (struct alloc_context
*)0;
646 for (plist
= aptr
->plist
; plist
!= (struct page_list
*)0; plist
= plist
->next
) {
647 hptr
= (struct hasharc
*)plist
->first
;
648 for (i
= 0; i
< plist
->num_allocations
; (i
++, hptr
++)) {
650 struct gprof_arc arc
= hptr
->arc
;
651 int nrecs
= 1 + (hptr
->overflow
* 2);
654 if (pv
->check_funcs
) {
655 if (arc
.frompc
< pv
->profil_info
.lowpc
||
656 arc
.frompc
> pv
->profil_info
.highpc
) {
658 arc
.frompc
= (prof_uptrint_t
)pv
->bogus_func
;
661 if (arc
.selfpc
< pv
->profil_info
.lowpc
||
662 arc
.selfpc
> pv
->profil_info
.highpc
) {
664 arc
.selfpc
= (prof_uptrint_t
)pv
->bogus_func
;
668 /* For each overflow, emit 2 extra records with the count
670 for (j
= 0; j
< nrecs
; j
++) {
671 bytes
+= sizeof (arc
);
672 if ((*pv
->fwrite_func
)((void *)&arc
,
680 arc
.count
= 0x80000000;
691 * Machine dependent function to write out prof records.
695 _prof_write(struct profile_vars
*pv
, struct callback
*callback_ptr
)
697 struct alloc_context
*aptr
;
698 struct page_list
*plist
;
700 struct prof_ext prof_st
;
701 struct prof_int
*pptr
;
706 /* Write out information prof_mcount collects */
707 for (aptr
= pv
->acontext
[ACONTEXT_PROF
];
708 aptr
!= (struct alloc_context
*)0;
711 for (plist
= aptr
->plist
; plist
!= (struct page_list
*)0; plist
= plist
->next
) {
712 pptr
= (struct prof_int
*)plist
->first
;
714 for (i
= 0; i
< plist
->num_allocations
; (i
++, pptr
++)) {
716 /* Write out 2 records for each overflow, each with a
717 count of 0x80000000 + the normal record */
718 prof_st
= pptr
->prof
;
719 nrecs
= 1 + (pptr
->overflow
* 2);
721 for (j
= 0; j
< nrecs
; j
++) {
722 bytes
+= sizeof (struct prof_ext
);
723 if ((*pv
->fwrite_func
)((void *)&prof_st
,
731 prof_st
.cncall
= 0x80000000;
737 /* Now write out the prof information that gprof collects */
738 for (aptr
= pv
->acontext
[ACONTEXT_GFUNC
];
739 aptr
!= (struct alloc_context
*)0;
742 for (plist
= aptr
->plist
; plist
!= (struct page_list
*)0; plist
= plist
->next
) {
743 gptr
= (struct gfuncs
*)plist
->first
;
745 for (i
= 0; i
< plist
->num_allocations
; (i
++, gptr
++)) {
747 /* Write out 2 records for each overflow, each with a
748 count of 0x80000000 + the normal record */
749 prof_st
= gptr
->prof
.prof
;
750 nrecs
= 1 + (gptr
->prof
.overflow
* 2);
752 for (j
= 0; j
< nrecs
; j
++) {
753 bytes
+= sizeof (struct prof_ext
);
754 if ((*pv
->fwrite_func
)((void *)&prof_st
,
762 prof_st
.cncall
= 0x80000000;
773 * Update any statistics. For the 386, calculate the hash table loading factor.
774 * Also figure out how many overflows occurred.
778 _profile_update_stats(struct profile_vars
*pv
)
780 struct alloc_context
*aptr
;
781 struct page_list
*plist
;
782 struct hasharc
*hptr
;
783 struct prof_int
*pptr
;
788 for(i
= 0; i
< MAX_BUCKETS
+1; i
++) {
789 pv
->stats
.buckets
[i
] = 0;
792 pv
->stats
.hash_buckets
= 0;
794 if (pv
->md
.hash_ptr
) {
795 for (i
= 0; i
< pv
->md
.hash_size
; i
++) {
797 struct hasharc
*hptr
;
799 for (hptr
= pv
->md
.hash_ptr
[i
]; hptr
; hptr
= hptr
->next
) {
803 pv
->stats
.buckets
[ (nbuckets
< MAX_BUCKETS
) ? nbuckets
: MAX_BUCKETS
]++;
804 if (pv
->stats
.hash_buckets
< nbuckets
) {
805 pv
->stats
.hash_buckets
= nbuckets
;
810 /* Count how many times functions are out of bounds */
811 if (pv
->check_funcs
) {
812 pv
->stats
.bogus_count
= 0;
814 for (aptr
= pv
->acontext
[ACONTEXT_GPROF
];
815 aptr
!= (struct alloc_context
*)0;
818 for (plist
= aptr
->plist
;
819 plist
!= (struct page_list
*)0;
820 plist
= plist
->next
) {
822 hptr
= (struct hasharc
*)plist
->first
;
823 for (i
= 0; i
< plist
->num_allocations
; (i
++, hptr
++)) {
825 if (hptr
->arc
.frompc
< pv
->profil_info
.lowpc
||
826 hptr
->arc
.frompc
> pv
->profil_info
.highpc
) {
827 pv
->stats
.bogus_count
++;
830 if (hptr
->arc
.selfpc
< pv
->profil_info
.lowpc
||
831 hptr
->arc
.selfpc
> pv
->profil_info
.highpc
) {
832 pv
->stats
.bogus_count
++;
839 /* Figure out how many overflows occurred */
840 PROF_ULONG_TO_CNT(pv
->stats
.prof_overflow
, 0);
841 PROF_ULONG_TO_CNT(pv
->stats
.gprof_overflow
, 0);
843 for (aptr
= pv
->acontext
[ACONTEXT_GPROF
];
844 aptr
!= (struct alloc_context
*)0;
847 for (plist
= aptr
->plist
;
848 plist
!= (struct page_list
*)0;
849 plist
= plist
->next
) {
851 hptr
= (struct hasharc
*)plist
->first
;
852 for (i
= 0; i
< plist
->num_allocations
; (i
++, hptr
++)) {
853 PROF_CNT_ADD(pv
->stats
.gprof_overflow
, hptr
->overflow
);
858 for (aptr
= pv
->acontext
[ACONTEXT_PROF
];
859 aptr
!= (struct alloc_context
*)0;
862 for (plist
= aptr
->plist
;
863 plist
!= (struct page_list
*)0;
864 plist
= plist
->next
) {
866 pptr
= (struct prof_int
*)plist
->first
;
867 for (i
= 0; i
< plist
->num_allocations
; (i
++, pptr
++)) {
868 PROF_CNT_ADD(pv
->stats
.prof_overflow
, pptr
->overflow
);
873 for (aptr
= pv
->acontext
[ACONTEXT_GFUNC
];
874 aptr
!= (struct alloc_context
*)0;
877 for (plist
= aptr
->plist
;
878 plist
!= (struct page_list
*)0;
879 plist
= plist
->next
) {
881 fptr
= (struct gfuncs
*)plist
->first
;
882 for (i
= 0; i
< plist
->num_allocations
; (i
++, fptr
++)) {
883 PROF_CNT_ADD(pv
->stats
.prof_overflow
, fptr
->prof
.overflow
);
888 /* Now go through & count how many times the LHISTCOUNTER overflowed into a 2nd word */
889 lptr
= (LHISTCOUNTER
*)pv
->profil_buf
;
891 if (pv
->use_profil
&&
892 pv
->profil_info
.counter_size
== sizeof(LHISTCOUNTER
) &&
893 lptr
!= (LHISTCOUNTER
*)0) {
895 PROF_ULONG_TO_CNT(pv
->stats
.overflow_ticks
, 0);
896 for (i
= 0; i
< pv
->stats
.profil_buckets
; i
++) {
897 PROF_CNT_ADD(pv
->stats
.overflow_ticks
, lptr
[i
].high
);
902 #if !defined(_KERNEL) && !defined(MACH_KERNEL)
905 * Routine callable from the debugger that prints the statistics.
908 int _profile_debug(void)
910 _profile_update_stats(&_profile_vars
);
911 _profile_print_stats(stderr
, &_profile_vars
.stats
, &_profile_vars
.profil_info
);
916 * Print the statistics structure in a meaningful way.
919 void _profile_print_stats(FILE *stream
,
920 const struct profile_stats
*stats
,
921 const struct profile_profil
*pinfo
)
924 prof_cnt_t total_hits
;
928 int width_wasted
= 0;
929 int width_overhead
= 0;
930 int width_context
= 0;
931 static const char *cname
[ACONTEXT_MAX
] = ACONTEXT_NAMES
;
942 sprintf(buf
, "%ld.%ld", (long)stats
->major_version
, (long)stats
->minor_version
);
943 fprintf(stream
, "%12s profiling version number\n", buf
);
944 fprintf(stream
, "%12lu size of profile_vars\n", (long unsigned)sizeof(struct profile_vars
));
945 fprintf(stream
, "%12lu size of profile_stats\n", (long unsigned)sizeof(struct profile_stats
));
946 fprintf(stream
, "%12lu size of profile_md\n", (long unsigned)sizeof(struct profile_md
));
947 fprintf(stream
, "%12s calls to _{,g}prof_mcount\n", PROF_CNT_TO_DECIMAL((char *)0, stats
->cnt
));
948 fprintf(stream
, "%12s calls to old mcount\n", PROF_CNT_TO_DECIMAL((char *)0, stats
->old_mcount
));
949 fprintf(stream
, "%12s calls to _dummy_mcount\n", PROF_CNT_TO_DECIMAL((char *)0, stats
->dummy
));
950 fprintf(stream
, "%12lu functions profiled\n", (long unsigned)stats
->prof_records
);
951 fprintf(stream
, "%12lu gprof arcs\n", (long unsigned)stats
->gprof_records
);
954 fprintf(stream
, "%12lu profil buckets\n", (long unsigned)stats
->profil_buckets
);
955 fprintf(stream
, "%12lu profil lowpc [0x%lx]\n",
956 (long unsigned)pinfo
->lowpc
,
957 (long unsigned)pinfo
->lowpc
);
959 fprintf(stream
, "%12lu profil highpc [0x%lx]\n",
960 (long unsigned)pinfo
->highpc
,
961 (long unsigned)pinfo
->highpc
);
963 fprintf(stream
, "%12lu profil highpc-lowpc\n", (long unsigned)(pinfo
->highpc
- pinfo
->lowpc
));
964 fprintf(stream
, "%12lu profil buffer length\n", (long unsigned)pinfo
->profil_len
);
965 fprintf(stream
, "%12lu profil sizeof counters\n", (long unsigned)pinfo
->counter_size
);
966 fprintf(stream
, "%12lu profil scale (%g)\n",
967 (long unsigned)pinfo
->scale
,
968 ((double)pinfo
->scale
) / ((double) 0x10000));
971 for (i
= 0; i
< sizeof (pinfo
->profil_unused
) / sizeof (pinfo
->profil_unused
[0]); i
++) {
972 if (pinfo
->profil_unused
[i
]) {
973 fprintf(stream
, "%12lu profil unused[%2d] {0x%.8lx}\n",
974 (long unsigned)pinfo
->profil_unused
[i
],
976 (long unsigned)pinfo
->profil_unused
[i
]);
981 if (stats
->max_cpu
) {
982 fprintf(stream
, "%12lu current cpu/thread\n", (long unsigned)stats
->my_cpu
);
983 fprintf(stream
, "%12lu max cpu/thread+1\n", (long unsigned)stats
->max_cpu
);
986 if (stats
->bogus_count
!= 0) {
988 "%12lu gprof functions found outside of range\n",
989 (long unsigned)stats
->bogus_count
);
992 if (PROF_CNT_NE_0(stats
->too_low
)) {
994 "%12s histogram ticks were too low\n",
995 PROF_CNT_TO_DECIMAL((char *)0, stats
->too_low
));
998 if (PROF_CNT_NE_0(stats
->too_high
)) {
1000 "%12s histogram ticks were too high\n",
1001 PROF_CNT_TO_DECIMAL((char *)0, stats
->too_high
));
1004 if (PROF_CNT_NE_0(stats
->acontext_locked
)) {
1006 "%12s times an allocation context was locked\n",
1007 PROF_CNT_TO_DECIMAL((char *)0, stats
->acontext_locked
));
1010 if (PROF_CNT_NE_0(stats
->kernel_ticks
)
1011 || PROF_CNT_NE_0(stats
->user_ticks
)
1012 || PROF_CNT_NE_0(stats
->idle_ticks
)) {
1014 prof_cnt_t total_ticks
;
1015 long double total_ticks_dbl
;
1017 total_ticks
= stats
->kernel_ticks
;
1018 PROF_CNT_LADD(total_ticks
, stats
->user_ticks
);
1019 PROF_CNT_LADD(total_ticks
, stats
->idle_ticks
);
1020 total_ticks_dbl
= PROF_CNT_TO_LDOUBLE(total_ticks
);
1023 "%12s total ticks\n",
1024 PROF_CNT_TO_DECIMAL((char *)0, total_ticks
));
1027 "%12s ticks within the kernel (%5.2Lf%%)\n",
1028 PROF_CNT_TO_DECIMAL((char *)0, stats
->kernel_ticks
),
1029 100.0L * (PROF_CNT_TO_LDOUBLE(stats
->kernel_ticks
) / total_ticks_dbl
));
1032 "%12s ticks within user space (%5.2Lf%%)\n",
1033 PROF_CNT_TO_DECIMAL((char *)0, stats
->user_ticks
),
1034 100.0L * (PROF_CNT_TO_LDOUBLE(stats
->user_ticks
) / total_ticks_dbl
));
1037 "%12s ticks idle (%5.2Lf%%)\n",
1038 PROF_CNT_TO_DECIMAL((char *)0, stats
->idle_ticks
),
1039 100.0L * (PROF_CNT_TO_LDOUBLE(stats
->idle_ticks
) / total_ticks_dbl
));
1042 if (PROF_CNT_NE_0(stats
->overflow_ticks
)) {
1043 fprintf(stream
, "%12s times a HISTCOUNTER counter would have overflowed\n",
1044 PROF_CNT_TO_DECIMAL((char *)0, stats
->overflow_ticks
));
1047 if (PROF_CNT_NE_0(stats
->hash_num
)) {
1048 long double total_buckets
= 0.0L;
1050 for (i
= 0; i
<= MAX_BUCKETS
; i
++) {
1051 total_buckets
+= (long double)stats
->buckets
[i
];
1054 fprintf(stream
, "%12lu max bucket(s) on hash chain.\n", (long unsigned)stats
->hash_buckets
);
1055 for (i
= 0; i
< MAX_BUCKETS
; i
++) {
1056 if (stats
->buckets
[i
] != 0) {
1057 fprintf(stream
, "%12lu bucket(s) had %d entries (%5.2Lf%%)\n",
1058 (long unsigned)stats
->buckets
[i
], i
,
1059 100.0L * ((long double)stats
->buckets
[i
] / total_buckets
));
1063 if (stats
->buckets
[MAX_BUCKETS
] != 0) {
1064 fprintf(stream
, "%12lu bucket(s) had more than %d entries (%5.2Lf%%)\n",
1065 (long unsigned)stats
->buckets
[MAX_BUCKETS
], MAX_BUCKETS
,
1066 100.0L * ((long double)stats
->buckets
[MAX_BUCKETS
] / total_buckets
));
1070 PROF_ULONG_TO_CNT(total_hits
, 0);
1071 for (i
= 0; i
< MAX_CACHE
; i
++) {
1072 PROF_CNT_LADD(total_hits
, stats
->cache_hits
[i
]);
1075 if (PROF_CNT_NE_0(total_hits
)) {
1076 long double total
= PROF_CNT_TO_LDOUBLE(stats
->cnt
);
1077 long double total_hits_dbl
= PROF_CNT_TO_LDOUBLE(total_hits
);
1080 "%12s cache hits (%.2Lf%%)\n",
1081 PROF_CNT_TO_DECIMAL((char *)0, total_hits
),
1082 100.0L * (total_hits_dbl
/ total
));
1084 for (i
= 0; i
< MAX_CACHE
; i
++) {
1085 if (PROF_CNT_NE_0(stats
->cache_hits
[i
])) {
1087 "%12s times cache#%d matched (%5.2Lf%% of cache hits, %5.2Lf%% total)\n",
1088 PROF_CNT_TO_DECIMAL((char *)0, stats
->cache_hits
[i
]),
1090 100.0L * (PROF_CNT_TO_LDOUBLE(stats
->cache_hits
[i
]) / total_hits_dbl
),
1091 100.0L * (PROF_CNT_TO_LDOUBLE(stats
->cache_hits
[i
]) / total
));
1095 if (PROF_CNT_NE_0(stats
->hash_num
)) {
1096 fprintf(stream
, "%12s times hash table searched\n", PROF_CNT_TO_DECIMAL((char *)0, stats
->hash_num
));
1097 fprintf(stream
, "%12s hash buckets searched\n", PROF_CNT_TO_DECIMAL((char *)0, stats
->hash_search
));
1098 fprintf(stream
, "%12.4Lf average buckets searched\n",
1099 PROF_CNT_TO_LDOUBLE(stats
->hash_search
) / PROF_CNT_TO_LDOUBLE(stats
->hash_num
));
1103 for (i
= 0; i
< sizeof (stats
->stats_unused
) / sizeof (stats
->stats_unused
[0]); i
++) {
1104 if (PROF_CNT_NE_0(stats
->stats_unused
[i
])) {
1105 fprintf(stream
, "%12s unused[%2d] {0x%.8lx 0x%.8lx}\n",
1106 PROF_CNT_TO_DECIMAL((char *)0, stats
->stats_unused
[i
]),
1108 (unsigned long)stats
->stats_unused
[i
].high
,
1109 (unsigned long)stats
->stats_unused
[i
].low
);
1113 /* Get the width for the allocation contexts */
1114 for (ac
= ACONTEXT_FIRST
; ac
< ACONTEXT_MAX
; ac
++) {
1117 if (stats
->num_context
[ac
] == 0) {
1121 len
= strlen (cname
[ac
]);
1122 if (len
> width_cname
)
1125 len
= sprintf (buf
, "%lu", (long unsigned)stats
->num_alloc
[ac
]);
1126 if (len
> width_alloc
)
1129 len
= sprintf (buf
, "%lu", (long unsigned)stats
->wasted
[ac
]);
1130 if (len
> width_wasted
)
1133 len
= sprintf (buf
, "%lu", (long unsigned)stats
->overhead
[ac
]);
1134 if (len
> width_overhead
)
1135 width_overhead
= len
;
1137 len
= sprintf (buf
, "%lu", (long unsigned)stats
->num_context
[ac
]);
1138 if (len
> width_context
)
1139 width_context
= len
;
1142 /* Print info about allocation contexts */
1143 for (ac
= ACONTEXT_FIRST
; ac
< ACONTEXT_MAX
; ac
++) {
1144 if (stats
->num_context
[ac
] == 0) {
1149 "%12lu bytes in %-*s %*lu alloc, %*lu unused, %*lu over, %*lu context\n",
1150 (long unsigned)stats
->bytes_alloc
[ac
],
1151 width_cname
, cname
[ac
],
1152 width_alloc
, (long unsigned)stats
->num_alloc
[ac
],
1153 width_wasted
, (long unsigned)stats
->wasted
[ac
],
1154 width_overhead
, (long unsigned)stats
->overhead
[ac
],
1155 width_context
, (long unsigned)stats
->num_context
[ac
]);
1161 * Merge a new statistics field into an old one.
1164 void _profile_merge_stats(struct profile_stats
*old_stats
, const struct profile_stats
*new_stats
)
1168 /* If nothing passed, just return */
1169 if (!old_stats
|| !new_stats
)
1172 /* If the old_stats has not been initialized, just copy in the new stats */
1173 if (old_stats
->major_version
== 0) {
1174 *old_stats
= *new_stats
;
1176 /* Otherwise, update stats, field by field */
1178 if (old_stats
->prof_records
< new_stats
->prof_records
)
1179 old_stats
->prof_records
= new_stats
->prof_records
;
1181 if (old_stats
->gprof_records
< new_stats
->gprof_records
)
1182 old_stats
->gprof_records
= new_stats
->gprof_records
;
1184 if (old_stats
->hash_buckets
< new_stats
->hash_buckets
)
1185 old_stats
->hash_buckets
= new_stats
->hash_buckets
;
1187 if (old_stats
->bogus_count
< new_stats
->bogus_count
)
1188 old_stats
->bogus_count
= new_stats
->bogus_count
;
1190 PROF_CNT_LADD(old_stats
->cnt
, new_stats
->cnt
);
1191 PROF_CNT_LADD(old_stats
->dummy
, new_stats
->dummy
);
1192 PROF_CNT_LADD(old_stats
->old_mcount
, new_stats
->old_mcount
);
1193 PROF_CNT_LADD(old_stats
->hash_search
, new_stats
->hash_search
);
1194 PROF_CNT_LADD(old_stats
->hash_num
, new_stats
->hash_num
);
1195 PROF_CNT_LADD(old_stats
->user_ticks
, new_stats
->user_ticks
);
1196 PROF_CNT_LADD(old_stats
->kernel_ticks
, new_stats
->kernel_ticks
);
1197 PROF_CNT_LADD(old_stats
->idle_ticks
, new_stats
->idle_ticks
);
1198 PROF_CNT_LADD(old_stats
->overflow_ticks
, new_stats
->overflow_ticks
);
1199 PROF_CNT_LADD(old_stats
->acontext_locked
, new_stats
->acontext_locked
);
1200 PROF_CNT_LADD(old_stats
->too_low
, new_stats
->too_low
);
1201 PROF_CNT_LADD(old_stats
->too_high
, new_stats
->too_high
);
1202 PROF_CNT_LADD(old_stats
->prof_overflow
, new_stats
->prof_overflow
);
1203 PROF_CNT_LADD(old_stats
->gprof_overflow
, new_stats
->gprof_overflow
);
1205 for (i
= 0; i
< (int)ACONTEXT_MAX
; i
++) {
1206 if (old_stats
->num_alloc
[i
] < new_stats
->num_alloc
[i
])
1207 old_stats
->num_alloc
[i
] = new_stats
->num_alloc
[i
];
1209 if (old_stats
->bytes_alloc
[i
] < new_stats
->bytes_alloc
[i
])
1210 old_stats
->bytes_alloc
[i
] = new_stats
->bytes_alloc
[i
];
1212 if (old_stats
->num_context
[i
] < new_stats
->num_context
[i
])
1213 old_stats
->num_context
[i
] = new_stats
->num_context
[i
];
1215 if (old_stats
->wasted
[i
] < new_stats
->wasted
[i
])
1216 old_stats
->wasted
[i
] = new_stats
->wasted
[i
];
1218 if (old_stats
->overhead
[i
] < new_stats
->overhead
[i
])
1219 old_stats
->overhead
[i
] = new_stats
->overhead
[i
];
1223 for (i
= 0; i
< MAX_BUCKETS
+1; i
++) {
1224 if (old_stats
->buckets
[i
] < new_stats
->buckets
[i
])
1225 old_stats
->buckets
[i
] = new_stats
->buckets
[i
];
1228 for (i
= 0; i
< MAX_CACHE
; i
++) {
1229 PROF_CNT_LADD(old_stats
->cache_hits
[i
], new_stats
->cache_hits
[i
]);
1232 for (i
= 0; i
< sizeof(old_stats
->stats_unused
) / sizeof(old_stats
->stats_unused
[0]); i
++) {
1233 PROF_CNT_LADD(old_stats
->stats_unused
[i
], new_stats
->stats_unused
[i
]);
1242 * Invalid function address used when checking of function addresses is
1243 * desired for gprof arcs, and we discover an address out of bounds.
1244 * There should be no callers of this function.
1248 _bogus_function(void)