1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31/*
32 * HISTORY
33 *
34 * Revision 1.1.1.1  1998/09/22 21:05:49  wsanchez
35 * Import of Mac OS X kernel (~semeria)
36 *
37 * Revision 1.1.1.1  1998/03/07 02:26:08  wsanchez
38 * Import of OSF Mach kernel (~mburg)
39 *
40 * Revision 1.1.5.1  1995/01/06  19:53:45  devrcs
41 * 	mk6 CR668 - 1.3b26 merge
42 * 	new file for mk6
43 * 	[1994/10/12  22:25:24  dwm]
44 *
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]
48 *
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]
54 *
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]
62 *
63 * Revision 1.1.2.1  1994/04/08  17:51:51  meissner
64 * 	no change
65 * 	[1994/04/08  02:11:40  meissner]
66 *
67 * 	Make most stats 64 bits, except for things like memory allocation.
68 * 	[1994/04/02  14:58:28  meissner]
69 *
70 * 	Add some printfs under #idef DEBUG_PROFILE.
71 * 	[1994/03/29  21:00:11  meissner]
72 *
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]
76 *
77 * 	Add gprof/prof overflow support
78 * 	[1994/03/17  14:56:51  meissner]
79 *
80 * 	Use memset instead of bzero.
81 * 	[1994/02/28  23:56:10  meissner]
82 *
83 * 	Add size of histogram counters & unused fields to profile_profil struct
84 * 	[1994/02/17  21:41:50  meissner]
85 *
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]
90 *
91 * 	Calculate percentages for # of hash buckets.
92 * 	[1994/02/11  16:52:04  meissner]
93 *
94 * 	Print stats as an unsigned number.
95 * 	[1994/02/07  18:47:05  meissner]
96 *
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.
107 * 	Expand copyright.
108 * 	[1994/02/07  12:41:03  meissner]
109 *
110 * 	Keep track of the number of times the kernel overflows the HISTCOUNTER counter.
111 * 	[1994/02/03  20:13:28  meissner]
112 *
113 * 	Add stats for {user,kernel,idle} mode in the kernel.
114 * 	[1994/02/03  15:17:31  meissner]
115 *
116 * 	Print unused stats in hex as well as decimal.
117 * 	[1994/02/03  14:52:20  meissner]
118 *
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]
123 *
124 * 	Combine _profile_{vars,stats,md}; Allow more than one _profile_vars.
125 * 	[1994/02/01  12:04:01  meissner]
126 *
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]
134 *
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]
138 *
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]
144 *
145 * 	Move init code here from assembly language.
146 * 	[1994/01/22  01:13:21  meissner]
147 *
148 * 	Include <profile/profile-internal.h> instead of "profile-md.h".
149 * 	[1994/01/20  20:56:49  meissner]
150 *
151 * 	Fixup copyright.
152 * 	[1994/01/18  23:08:02  meissner]
153 *
154 * 	Rename profile.h -> profile-md.h.
155 * 	[1994/01/18  19:44:57  meissner]
156 *
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]
160 *
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]
164 *
165 * 	Fix typo.
166 * 	[1994/01/04  16:34:46  meissner]
167 *
168 * 	Move max hash bucket calculation into _gprof_write & put info in stats structure.
169 * 	[1994/01/04  16:15:17  meissner]
170 *
171 * 	Use _profile_printf to write diagnostics; add diag_stream to hold stream to write to.
172 * 	[1994/01/04  15:37:46  meissner]
173 *
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]
180 *
181 * 	CR 10198 - Initial version.
182 * 	[1994/01/01  22:44:10  meissne
183 *
184 * $EndLog$
185 */
186
187#include <profiling/profile-internal.h>
188#include <vm/vm_kern.h>
189#include <stdlib.h>
190#include <string.h>
191
192#if defined(MACH_KERNEL) || defined(_KERNEL)
193
194#include <mach_assert.h>
195#if MACH_ASSERT && !defined(DEBUG_PROFILE)
196#define DEBUG_PROFILE 1
197#endif
198
199#else
200#include <assert.h>
201#define panic(str) exit(1)
202#endif
203
204#ifndef PROFILE_NUM_FUNCS
205#define	PROFILE_NUM_FUNCS	2000
206#endif
207
208#ifndef PROFILE_NUM_ARCS
209#define	PROFILE_NUM_ARCS	8000
210#endif
211
212/*
213 * Information passed on from profile-asm.s
214 */
215
216extern int _profile_do_stats;
217extern size_t _profile_size;
218extern size_t _profile_stats_size;
219extern size_t _profile_md_size;
220extern size_t _profile_profil_size;
221extern size_t _profile_hash_size;
222
223/*
224 * All profiling variables, and a dummy gprof record.
225 */
226
227struct profile_vars _profile_vars = { 0 };
228struct hasharc _gprof_dummy = { 0 };
229
230/*
231 * Forward references.
232 */
233
234static void *_profile_md_acontext(struct profile_vars *pv,
235				  void *ptr,
236				  size_t len,
237				  acontext_type_t type);
238
239static void _profile_reset_alloc(struct profile_vars *,
240				 acontext_type_t);
241
242extern void _bogus_function(void);
243
244
245#if NCPUS > 1
246struct profile_vars *_profile_vars_cpus[NCPUS] = { &_profile_vars };
247struct profile_vars _profile_vars_aux[NCPUS-1];
248#define PROFILE_VARS(cpu) (_profile_vars_cpus[(cpu)])
249#else
250#define PROFILE_VARS(cpu) (&_profile_vars)
251#endif
252
253void *
254_profile_alloc_pages (size_t size)
255{
256	vm_offset_t addr;
257
258	/*
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
261	 * properly.
262	 */
263
264	if (PROFILE_VARS(0)->active) {
265		panic("Call to _profile_alloc_pages while profiling is running.");
266	}
267
268	if (kmem_alloc(kernel_map, &addr, size)) {
269		panic("Could not allocate memory for profiling");
270	}
271
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);
275	}
276
277	return((caddr_t)addr);
278}
279
280void
281_profile_free_pages(void *addr, size_t size)
282{
283	if (PROFILE_VARS(0)->debug) {
284		printf("Freed %d bytes for profiling, address 0x%x\n", (int)size, (int)addr);
285	}
286
287	kmem_free(kernel_map, (vm_offset_t)addr, size);
288	return;
289}
290
291void _profile_error(struct profile_vars *pv)
292{
293	panic("Fatal error in profiling");
294}
295
296
297/*
298 * Function to set up the initial allocation for a context block.
299 */
300
301static void *
302_profile_md_acontext(struct profile_vars *pv,
303		     void *ptr,
304		     size_t len,
305		     acontext_type_t type)
306{
307	struct memory {
308		struct alloc_context context;
309		struct page_list plist;
310		int data[1];
311	};
312
313	struct memory *mptr = (struct memory *)ptr;
314	struct alloc_context *context = &mptr->context;
315	struct page_list *plist = &mptr->plist;
316
317#ifdef DEBUG_PROFILE
318	_profile_printf("_profile_md_acontext: pv= 0x%lx, ptr= 0x%lx, len= %6ld, type= %d\n",
319			(long)pv,
320			(long)ptr,
321			(long)len,
322			(int)type);
323#endif
324
325	/* Fill in context block header */
326	context->next = pv->acontext[type];
327	context->plist = plist;
328	context->lock = 0;
329
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;
336
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;
341
342	/* And setup context block */
343	pv->acontext[type] = context;
344
345	return (void *)((char *)ptr+len);
346}
347
348
349/*
350 * Machine dependent function to initialize things.
351 */
352
353void
354_profile_md_init(struct profile_vars *pv,
355		 profile_type_t type,
356		 profile_alloc_mem_t alloc_mem)
357{
358	size_t page_size = pv->page_size;
359	size_t arc_size;
360	size_t func_size;
361	size_t misc_size;
362	size_t hash_size;
363	size_t extra_arc_size;
364	size_t extra_func_size;
365	size_t callback_size = page_size;
366	void *ptr;
367	acontext_type_t ac;
368	int i;
369	static struct {
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 */
373	} sizes[] = {
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" }};
378
379#ifdef DEBUG_PROFILE
380	_profile_printf("_profile_md_init: pv = 0x%lx, type = %d, alloc = %d\n",
381			(long) pv,
382			(int)type,
383			(int)alloc_mem);
384#endif
385
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",
389					sizes[i].name,
390					(long)sizes[i].c_size,
391					(long)*sizes[i].asm_size_ptr);
392
393			panic(sizes[i].name);
394		}
395	}
396
397	/* Figure out which function will handle compiler generated profiling */
398	if (type == PROFILE_GPROF) {
399		pv->md.save_mcount_ptr = _gprof_mcount;
400
401	} else if (type == PROFILE_PROF) {
402		pv->md.save_mcount_ptr = _prof_mcount;
403
404	} else {
405		pv->md.save_mcount_ptr = _dummy_mcount;
406	}
407
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;
414	pv->type              = type;
415	pv->do_profile        = 1;
416	pv->use_dci	      = 1;
417	pv->use_profil	      = 1;
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;
422	pv->init_format	      = 0;
423	pv->bogus_func	      = _bogus_function;
424
425#ifdef DEBUG_PROFILE
426	pv->debug	      = 1;
427#endif
428
429	if (!pv->error_msg) {
430		pv->error_msg = "error in profiling";
431	}
432
433	if (!pv->page_size) {
434		pv->page_size = 4096;
435	}
436
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;
440
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;
449
450	/* zero out all allocation context blocks */
451	for (ac = ACONTEXT_FIRST; ac < ACONTEXT_MAX; ac++) {
452		pv->acontext[ac] = (struct alloc_context *)0;
453	}
454
455	/* Don't allocate memory if not desired */
456	if (!alloc_mem) {
457		return;
458	}
459
460	/* Allocate some space for the initial allocations */
461	switch (type) {
462	default:
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);
467		break;
468
469	case PROFILE_GPROF:
470
471#if defined(MACH_KERNEL) || defined(_KERNEL)
472		/*
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
478		 * is locked.
479		 */
480		extra_arc_size = 4*page_size;
481		extra_func_size = 2*page_size;
482#else
483		extra_arc_size = extra_func_size = 0;
484#endif
485
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);
491
492		ptr = _profile_alloc_pages(arc_size
493					   + func_size
494					   + misc_size
495					   + callback_size
496					   + extra_arc_size
497					   + extra_func_size);
498
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);
502#endif
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);
507
508		/* Allocate hash table */
509		pv->md.hash_ptr = (struct hasharc **) _profile_alloc(pv, hash_size, ACONTEXT_MISC);
510		break;
511
512	case PROFILE_PROF:
513		/* Set up allocation areas */
514		func_size = ROUNDUP(PROFILE_NUM_FUNCS * sizeof(struct prof_ext), page_size);
515		misc_size = page_size;
516
517		ptr = _profile_alloc_pages(func_size
518					   + misc_size
519					   + callback_size);
520
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);
524		break;
525	}
526}
527
528
529/*
530 * Machine dependent functions to start and stop profiling.
531 */
532
533int
534_profile_md_start(void)
535{
536	_mcount_ptr = _profile_vars.md.save_mcount_ptr;
537	return 0;
538}
539
540int
541_profile_md_stop(void)
542{
543	_mcount_ptr = _dummy_mcount;
544	return 0;
545}
546
547
548/*
549 * Free up all memory in a memory context block.
550 */
551
552static void
553_profile_reset_alloc(struct profile_vars *pv, acontext_type_t ac)
554{
555	struct alloc_context *aptr;
556	struct page_list *plist;
557
558	for (aptr = pv->acontext[ac];
559	     aptr != (struct alloc_context *)0;
560	     aptr = aptr->next) {
561
562		for (plist = aptr->plist;
563		     plist != (struct page_list *)0;
564		     plist = plist->next) {
565
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);
571		}
572	}
573}
574
575
576/*
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.
579 */
580
581void
582_profile_reset(struct profile_vars *pv)
583{
584	struct alloc_context *aptr;
585	struct page_list *plist;
586	struct gfuncs *gfunc;
587
588	if (pv->active) {
589		_profile_md_stop();
590	}
591
592	/* Reset all function unique pointers back to 0 */
593	for (aptr = pv->acontext[ACONTEXT_GFUNC];
594	     aptr != (struct alloc_context *)0;
595	     aptr = aptr->next) {
596
597		for (plist = aptr->plist;
598		     plist != (struct page_list *)0;
599		     plist = plist->next) {
600
601			for (gfunc = (struct gfuncs *)plist->first;
602			     gfunc < (struct gfuncs *)plist->ptr;
603			     gfunc++) {
604
605				*(gfunc->unique_ptr) = (struct hasharc *)0;
606			}
607		}
608	}
609
610	/* Release memory */
611	_profile_reset_alloc(pv, ACONTEXT_GPROF);
612	_profile_reset_alloc(pv, ACONTEXT_GFUNC);
613	_profile_reset_alloc(pv, ACONTEXT_PROF);
614
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));
618
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;
622
623	if (pv->active) {
624		_profile_md_start();
625	}
626}
627
628
629/*
630 * Machine dependent function to write out gprof records.
631 */
632
633size_t
634_gprof_write(struct profile_vars *pv, struct callback *callback_ptr)
635{
636	struct alloc_context *aptr;
637	struct page_list *plist;
638	size_t bytes = 0;
639	struct hasharc *hptr;
640	int i;
641
642	for (aptr = pv->acontext[ACONTEXT_GPROF];
643	     aptr != (struct alloc_context *)0;
644	     aptr = aptr->next) {
645
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++)) {
649
650				struct gprof_arc arc = hptr->arc;
651				int nrecs = 1 + (hptr->overflow * 2);
652				int j;
653
654				if (pv->check_funcs) {
655					if (arc.frompc < pv->profil_info.lowpc ||
656					    arc.frompc > pv->profil_info.highpc) {
657
658						arc.frompc = (prof_uptrint_t)pv->bogus_func;
659					}
660
661					if (arc.selfpc < pv->profil_info.lowpc ||
662					    arc.selfpc > pv->profil_info.highpc) {
663
664						arc.selfpc = (prof_uptrint_t)pv->bogus_func;
665					}
666				}
667
668				/* For each overflow, emit 2 extra records with the count
669				   set to 0x80000000 */
670				for (j = 0; j < nrecs; j++) {
671					bytes += sizeof (arc);
672					if ((*pv->fwrite_func)((void *)&arc,
673							       sizeof(arc),
674							       1,
675							       pv->stream) != 1) {
676
677						_profile_error(pv);
678					}
679
680					arc.count = 0x80000000;
681				}
682			}
683		}
684	}
685
686	return bytes;
687}
688
689
690/*
691 * Machine dependent function to write out prof records.
692 */
693
694size_t
695_prof_write(struct profile_vars *pv, struct callback *callback_ptr)
696{
697	struct alloc_context *aptr;
698	struct page_list *plist;
699	size_t bytes = 0;
700	struct prof_ext prof_st;
701	struct prof_int *pptr;
702	struct gfuncs *gptr;
703	int nrecs;
704	int i, j;
705
706	/* Write out information prof_mcount collects */
707	for (aptr = pv->acontext[ACONTEXT_PROF];
708	     aptr != (struct alloc_context *)0;
709	     aptr = aptr->next) {
710
711		for (plist = aptr->plist; plist != (struct page_list *)0; plist = plist->next) {
712			pptr = (struct prof_int *)plist->first;
713
714			for (i = 0; i < plist->num_allocations; (i++, pptr++)) {
715
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);
720
721				for (j = 0; j < nrecs; j++) {
722					bytes += sizeof (struct prof_ext);
723					if ((*pv->fwrite_func)((void *)&prof_st,
724							       sizeof(prof_st),
725							       1,
726							       pv->stream) != 1) {
727
728						_profile_error(pv);
729					}
730
731					prof_st.cncall = 0x80000000;
732				}
733			}
734		}
735	}
736
737	/* Now write out the prof information that gprof collects */
738	for (aptr = pv->acontext[ACONTEXT_GFUNC];
739	     aptr != (struct alloc_context *)0;
740	     aptr = aptr->next) {
741
742		for (plist = aptr->plist; plist != (struct page_list *)0; plist = plist->next) {
743			gptr = (struct gfuncs *)plist->first;
744
745			for (i = 0; i < plist->num_allocations; (i++, gptr++)) {
746
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);
751
752				for (j = 0; j < nrecs; j++) {
753					bytes += sizeof (struct prof_ext);
754					if ((*pv->fwrite_func)((void *)&prof_st,
755							       sizeof(prof_st),
756							       1,
757							       pv->stream) != 1) {
758
759						_profile_error(pv);
760					}
761
762					prof_st.cncall = 0x80000000;
763				}
764			}
765		}
766	}
767
768	return bytes;
769}
770
771
772/*
773 * Update any statistics.  For the 386, calculate the hash table loading factor.
774 * Also figure out how many overflows occurred.
775 */
776
777void
778_profile_update_stats(struct profile_vars *pv)
779{
780	struct alloc_context *aptr;
781	struct page_list *plist;
782	struct hasharc *hptr;
783	struct prof_int *pptr;
784	struct gfuncs *fptr;
785	LHISTCOUNTER *lptr;
786	int i;
787
788	for(i = 0; i < MAX_BUCKETS+1; i++) {
789		pv->stats.buckets[i] = 0;
790	}
791
792	pv->stats.hash_buckets = 0;
793
794	if (pv->md.hash_ptr) {
795		for (i = 0; i < pv->md.hash_size; i++) {
796			long nbuckets = 0;
797			struct hasharc *hptr;
798
799			for (hptr = pv->md.hash_ptr[i]; hptr; hptr = hptr->next) {
800				nbuckets++;
801			}
802
803			pv->stats.buckets[ (nbuckets < MAX_BUCKETS) ? nbuckets : MAX_BUCKETS ]++;
804			if (pv->stats.hash_buckets < nbuckets) {
805				pv->stats.hash_buckets = nbuckets;
806			}
807		}
808	}
809
810	/* Count how many times functions are out of bounds */
811	if (pv->check_funcs) {
812		pv->stats.bogus_count = 0;
813
814		for (aptr = pv->acontext[ACONTEXT_GPROF];
815		     aptr != (struct alloc_context *)0;
816		     aptr = aptr->next) {
817
818			for (plist = aptr->plist;
819			     plist != (struct page_list *)0;
820			     plist = plist->next) {
821
822				hptr = (struct hasharc *)plist->first;
823				for (i = 0; i < plist->num_allocations; (i++, hptr++)) {
824
825					if (hptr->arc.frompc < pv->profil_info.lowpc ||
826					    hptr->arc.frompc > pv->profil_info.highpc) {
827						pv->stats.bogus_count++;
828					}
829
830					if (hptr->arc.selfpc < pv->profil_info.lowpc ||
831					    hptr->arc.selfpc > pv->profil_info.highpc) {
832						pv->stats.bogus_count++;
833					}
834				}
835			}
836		}
837	}
838
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);
842
843	for (aptr = pv->acontext[ACONTEXT_GPROF];
844	     aptr != (struct alloc_context *)0;
845	     aptr = aptr->next) {
846
847		for (plist = aptr->plist;
848		     plist != (struct page_list *)0;
849		     plist = plist->next) {
850
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);
854			}
855		}
856	}
857
858	for (aptr = pv->acontext[ACONTEXT_PROF];
859	     aptr != (struct alloc_context *)0;
860	     aptr = aptr->next) {
861
862		for (plist = aptr->plist;
863		     plist != (struct page_list *)0;
864		     plist = plist->next) {
865
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);
869			}
870		}
871	}
872
873	for (aptr = pv->acontext[ACONTEXT_GFUNC];
874	     aptr != (struct alloc_context *)0;
875	     aptr = aptr->next) {
876
877		for (plist = aptr->plist;
878		     plist != (struct page_list *)0;
879		     plist = plist->next) {
880
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);
884			}
885		}
886	}
887
888	/* Now go through & count how many times the LHISTCOUNTER overflowed into a 2nd word */
889	lptr = (LHISTCOUNTER *)pv->profil_buf;
890
891	if (pv->use_profil &&
892	    pv->profil_info.counter_size == sizeof(LHISTCOUNTER) &&
893	    lptr != (LHISTCOUNTER *)0) {
894
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);
898		}
899	}
900}
901
902#if !defined(_KERNEL) && !defined(MACH_KERNEL)
903
904/*
905 * Routine callable from the debugger that prints the statistics.
906 */
907
908int _profile_debug(void)
909{
910	_profile_update_stats(&_profile_vars);
911	_profile_print_stats(stderr, &_profile_vars.stats, &_profile_vars.profil_info);
912	return 0;
913}
914
915/*
916 * Print the statistics structure in a meaningful way.
917 */
918
919void _profile_print_stats(FILE *stream,
920			  const struct profile_stats *stats,
921			  const struct profile_profil *pinfo)
922{
923	int i;
924	prof_cnt_t total_hits;
925	acontext_type_t ac;
926	int width_cname = 0;
927	int width_alloc = 0;
928	int width_wasted = 0;
929	int width_overhead = 0;
930	int width_context = 0;
931	static const char *cname[ACONTEXT_MAX] = ACONTEXT_NAMES;
932	char buf[20];
933
934	if (!stats) {
935		return;
936	}
937
938	if (!stream) {
939		stream = stdout;
940	}
941
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);
952
953	if (pinfo) {
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);
958
959		fprintf(stream, "%12lu profil highpc [0x%lx]\n",
960			(long unsigned)pinfo->highpc,
961			(long unsigned)pinfo->highpc);
962
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));
969
970
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],
975					i,
976					(long unsigned)pinfo->profil_unused[i]);
977			}
978		}
979	}
980
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);
984	}
985
986	if (stats->bogus_count != 0) {
987		fprintf(stream,
988			"%12lu gprof functions found outside of range\n",
989			(long unsigned)stats->bogus_count);
990	}
991
992	if (PROF_CNT_NE_0(stats->too_low)) {
993		fprintf(stream,
994			"%12s histogram ticks were too low\n",
995			PROF_CNT_TO_DECIMAL((char *)0, stats->too_low));
996	}
997
998	if (PROF_CNT_NE_0(stats->too_high)) {
999		fprintf(stream,
1000			"%12s histogram ticks were too high\n",
1001			PROF_CNT_TO_DECIMAL((char *)0, stats->too_high));
1002	}
1003
1004	if (PROF_CNT_NE_0(stats->acontext_locked)) {
1005		fprintf(stream,
1006			"%12s times an allocation context was locked\n",
1007			PROF_CNT_TO_DECIMAL((char *)0, stats->acontext_locked));
1008	}
1009
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)) {
1013
1014		prof_cnt_t total_ticks;
1015		long double total_ticks_dbl;
1016
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);
1021
1022		fprintf(stream,
1023			"%12s total ticks\n",
1024			PROF_CNT_TO_DECIMAL((char *)0, total_ticks));
1025
1026		fprintf(stream,
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));
1030
1031		fprintf(stream,
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));
1035
1036		fprintf(stream,
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));
1040	}
1041
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));
1045	}
1046
1047	if (PROF_CNT_NE_0(stats->hash_num)) {
1048		long double total_buckets = 0.0L;
1049
1050		for (i = 0; i <= MAX_BUCKETS; i++) {
1051			total_buckets += (long double)stats->buckets[i];
1052		}
1053
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));
1060			}
1061		}
1062
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));
1067		}
1068	}
1069
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]);
1073	}
1074
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);
1078
1079		fprintf(stream,
1080			"%12s cache hits (%.2Lf%%)\n",
1081			PROF_CNT_TO_DECIMAL((char *)0, total_hits),
1082			100.0L * (total_hits_dbl / total));
1083
1084		for (i = 0; i < MAX_CACHE; i++) {
1085			if (PROF_CNT_NE_0(stats->cache_hits[i])) {
1086				fprintf(stream,
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]),
1089					i+1,
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));
1092			}
1093		}
1094
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));
1100		}
1101	}
1102
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]),
1107				i,
1108				(unsigned long)stats->stats_unused[i].high,
1109				(unsigned long)stats->stats_unused[i].low);
1110		}
1111	}
1112
1113	/* Get the width for the allocation contexts */
1114	for (ac = ACONTEXT_FIRST; ac < ACONTEXT_MAX; ac++) {
1115		int len;
1116
1117		if (stats->num_context[ac] == 0) {
1118			continue;
1119		}
1120
1121		len = strlen (cname[ac]);
1122		if (len > width_cname)
1123			width_cname = len;
1124
1125		len = sprintf (buf, "%lu", (long unsigned)stats->num_alloc[ac]);
1126		if (len > width_alloc)
1127			width_alloc = len;
1128
1129		len = sprintf (buf, "%lu", (long unsigned)stats->wasted[ac]);
1130		if (len > width_wasted)
1131			width_wasted = len;
1132
1133		len = sprintf (buf, "%lu", (long unsigned)stats->overhead[ac]);
1134		if (len > width_overhead)
1135			width_overhead = len;
1136
1137		len = sprintf (buf, "%lu", (long unsigned)stats->num_context[ac]);
1138		if (len > width_context)
1139			width_context = len;
1140	}
1141
1142	/* Print info about allocation contexts */
1143	for (ac = ACONTEXT_FIRST; ac < ACONTEXT_MAX; ac++) {
1144		if (stats->num_context[ac] == 0) {
1145			continue;
1146		}
1147
1148		fprintf (stream,
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]);
1156	}
1157}
1158
1159
1160/*
1161 * Merge a new statistics field into an old one.
1162 */
1163
1164void _profile_merge_stats(struct profile_stats  *old_stats, const struct profile_stats  *new_stats)
1165{
1166	int i;
1167
1168	/* If nothing passed, just return */
1169	if (!old_stats || !new_stats)
1170		return;
1171
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;
1175
1176	/* Otherwise, update stats, field by field */
1177	} else {
1178		if (old_stats->prof_records < new_stats->prof_records)
1179			old_stats->prof_records = new_stats->prof_records;
1180
1181		if (old_stats->gprof_records < new_stats->gprof_records)
1182			old_stats->gprof_records = new_stats->gprof_records;
1183
1184		if (old_stats->hash_buckets < new_stats->hash_buckets)
1185			old_stats->hash_buckets = new_stats->hash_buckets;
1186
1187		if (old_stats->bogus_count < new_stats->bogus_count)
1188			old_stats->bogus_count = new_stats->bogus_count;
1189
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);
1204
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];
1208
1209			if (old_stats->bytes_alloc[i] < new_stats->bytes_alloc[i])
1210				old_stats->bytes_alloc[i] = new_stats->bytes_alloc[i];
1211
1212			if (old_stats->num_context[i] < new_stats->num_context[i])
1213				old_stats->num_context[i] = new_stats->num_context[i];
1214
1215			if (old_stats->wasted[i] < new_stats->wasted[i])
1216				old_stats->wasted[i] = new_stats->wasted[i];
1217
1218			if (old_stats->overhead[i] < new_stats->overhead[i])
1219				old_stats->overhead[i] = new_stats->overhead[i];
1220
1221		}
1222
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];
1226		}
1227
1228		for (i = 0; i < MAX_CACHE; i++) {
1229			PROF_CNT_LADD(old_stats->cache_hits[i], new_stats->cache_hits[i]);
1230		}
1231
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]);
1234		}
1235	}
1236}
1237
1238#endif
1239
1240
1241/*
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.
1245 */
1246
1247void
1248_bogus_function(void)
1249{
1250}
1251