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#define PROFILE_VARS(cpu) (&_profile_vars)
246
247void *
248_profile_alloc_pages (size_t size)
249{
250	vm_offset_t addr;
251
252	/*
253	 * For the MK, we can't support allocating pages at runtime, because we
254	 * might be at interrupt level, so abort if we didn't size the table
255	 * properly.
256	 */
257
258	if (PROFILE_VARS(0)->active) {
259		panic("Call to _profile_alloc_pages while profiling is running.");
260	}
261
262	if (kmem_alloc(kernel_map, &addr, size)) {
263		panic("Could not allocate memory for profiling");
264	}
265
266	memset((void *)addr, '\0', size);
267	if (PROFILE_VARS(0)->debug) {
268		printf("Allocated %d bytes for profiling, address 0x%x\n", (int)size, (int)addr);
269	}
270
271	return((caddr_t)addr);
272}
273
274void
275_profile_free_pages(void *addr, size_t size)
276{
277	if (PROFILE_VARS(0)->debug) {
278		printf("Freed %d bytes for profiling, address 0x%x\n", (int)size, (int)addr);
279	}
280
281	kmem_free(kernel_map, (vm_offset_t)addr, size);
282	return;
283}
284
285void _profile_error(struct profile_vars *pv)
286{
287	panic("Fatal error in profiling");
288}
289
290
291/*
292 * Function to set up the initial allocation for a context block.
293 */
294
295static void *
296_profile_md_acontext(struct profile_vars *pv,
297		     void *ptr,
298		     size_t len,
299		     acontext_type_t type)
300{
301	struct memory {
302		struct alloc_context context;
303		struct page_list plist;
304		int data[1];
305	};
306
307	struct memory *mptr = (struct memory *)ptr;
308	struct alloc_context *context = &mptr->context;
309	struct page_list *plist = &mptr->plist;
310
311#ifdef DEBUG_PROFILE
312	_profile_printf("_profile_md_acontext: pv= 0x%lx, ptr= 0x%lx, len= %6ld, type= %d\n",
313			(long)pv,
314			(long)ptr,
315			(long)len,
316			(int)type);
317#endif
318
319	/* Fill in context block header */
320	context->next = pv->acontext[type];
321	context->plist = plist;
322	context->lock = 0;
323
324	/* Fill in first page list information */
325	plist->ptr = plist->first = (void *)&mptr->data[0];
326	plist->next = (struct page_list *)0;
327	plist->bytes_free = len - ((char *)plist->ptr - (char *)ptr);
328	plist->bytes_allocated = 0;
329	plist->num_allocations = 0;
330
331	/* Update statistics */
332	pv->stats.num_context[type]++;
333	pv->stats.wasted[type] += plist->bytes_free;
334	pv->stats.overhead[type] += len - plist->bytes_free;
335
336	/* And setup context block */
337	pv->acontext[type] = context;
338
339	return (void *)((char *)ptr+len);
340}
341
342
343/*
344 * Machine dependent function to initialize things.
345 */
346
347void
348_profile_md_init(struct profile_vars *pv,
349		 profile_type_t type,
350		 profile_alloc_mem_t alloc_mem)
351{
352	size_t page_size = pv->page_size;
353	size_t arc_size;
354	size_t func_size;
355	size_t misc_size;
356	size_t hash_size;
357	size_t extra_arc_size;
358	size_t extra_func_size;
359	size_t callback_size = page_size;
360	void *ptr;
361	acontext_type_t ac;
362	int i;
363	static struct {
364		size_t	    c_size;		/* size C thinks structure is */
365		size_t	   *asm_size_ptr;	/* pointer to size asm thinks struct is */
366		const char *name;		/* structure name */
367	} sizes[] = {
368		{ sizeof(struct profile_profil), &_profile_profil_size,	"profile_profil" },
369		{ sizeof(struct profile_stats),	 &_profile_stats_size,	"profile_stats" },
370		{ sizeof(struct profile_md),	 &_profile_md_size,	"profile_md" },
371		{ sizeof(struct profile_vars),	 &_profile_size,	"profile_vars" }};
372
373#ifdef DEBUG_PROFILE
374	_profile_printf("_profile_md_init: pv = 0x%lx, type = %d, alloc = %d\n",
375			(long) pv,
376			(int)type,
377			(int)alloc_mem);
378#endif
379
380	for (i = 0; i < sizeof (sizes) / sizeof(sizes[0]); i++) {
381		if (sizes[i].c_size != *sizes[i].asm_size_ptr) {
382			_profile_printf("C thinks struct %s is %ld bytes, asm thinks it is %ld bytes\n",
383					sizes[i].name,
384					(long)sizes[i].c_size,
385					(long)*sizes[i].asm_size_ptr);
386
387			panic(sizes[i].name);
388		}
389	}
390
391	/* Figure out which function will handle compiler generated profiling */
392	if (type == PROFILE_GPROF) {
393		pv->md.save_mcount_ptr = _gprof_mcount;
394
395	} else if (type == PROFILE_PROF) {
396		pv->md.save_mcount_ptr = _prof_mcount;
397
398	} else {
399		pv->md.save_mcount_ptr = _dummy_mcount;
400	}
401
402	pv->vars_size         = sizeof(struct profile_vars);
403	pv->plist_size        = sizeof(struct page_list);
404	pv->acontext_size     = sizeof(struct alloc_context);
405	pv->callback_size     = sizeof(struct callback);
406	pv->major_version     = PROFILE_MAJOR_VERSION;
407	pv->minor_version     = PROFILE_MINOR_VERSION;
408	pv->type              = type;
409	pv->do_profile        = 1;
410	pv->use_dci	      = 1;
411	pv->use_profil	      = 1;
412	pv->output_uarea      = 1;
413	pv->output_stats      = (prof_flag_t) _profile_do_stats;
414	pv->output_clock      = 1;
415	pv->multiple_sections = 1;
416	pv->init_format	      = 0;
417	pv->bogus_func	      = _bogus_function;
418
419#ifdef DEBUG_PROFILE
420	pv->debug	      = 1;
421#endif
422
423	if (!pv->error_msg) {
424		pv->error_msg = "error in profiling";
425	}
426
427	if (!pv->page_size) {
428		pv->page_size = 4096;
429	}
430
431	pv->stats.stats_size    = sizeof(struct profile_stats);
432	pv->stats.major_version = PROFILE_MAJOR_VERSION;
433	pv->stats.minor_version = PROFILE_MINOR_VERSION;
434
435	pv->md.md_size	       = sizeof(struct profile_md);
436	pv->md.major_version   = PROFILE_MAJOR_VERSION;
437	pv->md.minor_version   = PROFILE_MINOR_VERSION;
438	pv->md.hash_size       = _profile_hash_size;
439	pv->md.num_cache       = MAX_CACHE;
440	pv->md.mcount_ptr_ptr  = &_mcount_ptr;
441	pv->md.dummy_ptr       = &_gprof_dummy;
442	pv->md.alloc_pages     = _profile_alloc_pages;
443
444	/* zero out all allocation context blocks */
445	for (ac = ACONTEXT_FIRST; ac < ACONTEXT_MAX; ac++) {
446		pv->acontext[ac] = (struct alloc_context *)0;
447	}
448
449	/* Don't allocate memory if not desired */
450	if (!alloc_mem) {
451		return;
452	}
453
454	/* Allocate some space for the initial allocations */
455	switch (type) {
456	default:
457		misc_size = page_size;
458		ptr = _profile_alloc_pages(misc_size + callback_size);
459		ptr = _profile_md_acontext(pv, ptr, misc_size, ACONTEXT_MISC);
460		ptr = _profile_md_acontext(pv, ptr, callback_size, ACONTEXT_CALLBACK);
461		break;
462
463	case PROFILE_GPROF:
464
465#if defined(MACH_KERNEL) || defined(_KERNEL)
466		/*
467		 * For the MK & server allocate some slop space now for the
468		 * secondary context blocks in case allocations are done at
469		 * interrupt level when another allocation is being done.  This
470		 * is done before the main allocation blocks and will be pushed
471		 * so that it will only be used when the main allocation block
472		 * is locked.
473		 */
474		extra_arc_size = 4*page_size;
475		extra_func_size = 2*page_size;
476#else
477		extra_arc_size = extra_func_size = 0;
478#endif
479
480		/* Set up allocation areas */
481		arc_size = ROUNDUP(PROFILE_NUM_ARCS * sizeof(struct hasharc), page_size);
482		func_size = ROUNDUP(PROFILE_NUM_FUNCS * sizeof(struct gfuncs), page_size);
483		hash_size = _profile_hash_size * sizeof (struct hasharc *);
484		misc_size = ROUNDUP(hash_size + page_size, page_size);
485
486		ptr = _profile_alloc_pages(arc_size
487					   + func_size
488					   + misc_size
489					   + callback_size
490					   + extra_arc_size
491					   + extra_func_size);
492
493#if defined(MACH_KERNEL) || defined(_KERNEL)
494		ptr = _profile_md_acontext(pv, ptr, extra_arc_size, ACONTEXT_GPROF);
495		ptr = _profile_md_acontext(pv, ptr, extra_func_size, ACONTEXT_GFUNC);
496#endif
497		ptr = _profile_md_acontext(pv, ptr, arc_size, ACONTEXT_GPROF);
498		ptr = _profile_md_acontext(pv, ptr, func_size, ACONTEXT_GFUNC);
499		ptr = _profile_md_acontext(pv, ptr, misc_size, ACONTEXT_MISC);
500		ptr = _profile_md_acontext(pv, ptr, callback_size, ACONTEXT_CALLBACK);
501
502		/* Allocate hash table */
503		pv->md.hash_ptr = (struct hasharc **) _profile_alloc(pv, hash_size, ACONTEXT_MISC);
504		break;
505
506	case PROFILE_PROF:
507		/* Set up allocation areas */
508		func_size = ROUNDUP(PROFILE_NUM_FUNCS * sizeof(struct prof_ext), page_size);
509		misc_size = page_size;
510
511		ptr = _profile_alloc_pages(func_size
512					   + misc_size
513					   + callback_size);
514
515		ptr = _profile_md_acontext(pv, ptr, func_size, ACONTEXT_PROF);
516		ptr = _profile_md_acontext(pv, ptr, misc_size, ACONTEXT_MISC);
517		ptr = _profile_md_acontext(pv, ptr, callback_size, ACONTEXT_CALLBACK);
518		break;
519	}
520}
521
522
523/*
524 * Machine dependent functions to start and stop profiling.
525 */
526
527int
528_profile_md_start(void)
529{
530	_mcount_ptr = _profile_vars.md.save_mcount_ptr;
531	return 0;
532}
533
534int
535_profile_md_stop(void)
536{
537	_mcount_ptr = _dummy_mcount;
538	return 0;
539}
540
541
542/*
543 * Free up all memory in a memory context block.
544 */
545
546static void
547_profile_reset_alloc(struct profile_vars *pv, acontext_type_t ac)
548{
549	struct alloc_context *aptr;
550	struct page_list *plist;
551
552	for (aptr = pv->acontext[ac];
553	     aptr != (struct alloc_context *)0;
554	     aptr = aptr->next) {
555
556		for (plist = aptr->plist;
557		     plist != (struct page_list *)0;
558		     plist = plist->next) {
559
560			plist->ptr = plist->first;
561			plist->bytes_free += plist->bytes_allocated;
562			plist->bytes_allocated = 0;
563			plist->num_allocations = 0;
564			memset(plist->first, '\0', plist->bytes_allocated);
565		}
566	}
567}
568
569
570/*
571 * Reset profiling.  Since the only user of this function is the kernel
572 * and the server, we don't have to worry about other stuff than gprof.
573 */
574
575void
576_profile_reset(struct profile_vars *pv)
577{
578	struct alloc_context *aptr;
579	struct page_list *plist;
580	struct gfuncs *gfunc;
581
582	if (pv->active) {
583		_profile_md_stop();
584	}
585
586	/* Reset all function unique pointers back to 0 */
587	for (aptr = pv->acontext[ACONTEXT_GFUNC];
588	     aptr != (struct alloc_context *)0;
589	     aptr = aptr->next) {
590
591		for (plist = aptr->plist;
592		     plist != (struct page_list *)0;
593		     plist = plist->next) {
594
595			for (gfunc = (struct gfuncs *)plist->first;
596			     gfunc < (struct gfuncs *)plist->ptr;
597			     gfunc++) {
598
599				*(gfunc->unique_ptr) = (struct hasharc *)0;
600			}
601		}
602	}
603
604	/* Release memory */
605	_profile_reset_alloc(pv, ACONTEXT_GPROF);
606	_profile_reset_alloc(pv, ACONTEXT_GFUNC);
607	_profile_reset_alloc(pv, ACONTEXT_PROF);
608
609	memset((void *)pv->profil_buf, '\0', pv->profil_info.profil_len);
610	memset((void *)pv->md.hash_ptr, '\0', pv->md.hash_size * sizeof(struct hasharc *));
611	memset((void *)&pv->stats, '\0', sizeof(pv->stats));
612
613	pv->stats.stats_size    = sizeof(struct profile_stats);
614	pv->stats.major_version = PROFILE_MAJOR_VERSION;
615	pv->stats.minor_version = PROFILE_MINOR_VERSION;
616
617	if (pv->active) {
618		_profile_md_start();
619	}
620}
621
622
623/*
624 * Machine dependent function to write out gprof records.
625 */
626
627size_t
628_gprof_write(struct profile_vars *pv, struct callback *callback_ptr)
629{
630	struct alloc_context *aptr;
631	struct page_list *plist;
632	size_t bytes = 0;
633	struct hasharc *hptr;
634	int i;
635
636	for (aptr = pv->acontext[ACONTEXT_GPROF];
637	     aptr != (struct alloc_context *)0;
638	     aptr = aptr->next) {
639
640		for (plist = aptr->plist; plist != (struct page_list *)0; plist = plist->next) {
641			hptr = (struct hasharc *)plist->first;
642			for (i = 0; i < plist->num_allocations; (i++, hptr++)) {
643
644				struct gprof_arc arc = hptr->arc;
645				int nrecs = 1 + (hptr->overflow * 2);
646				int j;
647
648				if (pv->check_funcs) {
649					if (arc.frompc < pv->profil_info.lowpc ||
650					    arc.frompc > pv->profil_info.highpc) {
651
652						arc.frompc = (prof_uptrint_t)pv->bogus_func;
653					}
654
655					if (arc.selfpc < pv->profil_info.lowpc ||
656					    arc.selfpc > pv->profil_info.highpc) {
657
658						arc.selfpc = (prof_uptrint_t)pv->bogus_func;
659					}
660				}
661
662				/* For each overflow, emit 2 extra records with the count
663				   set to 0x80000000 */
664				for (j = 0; j < nrecs; j++) {
665					bytes += sizeof (arc);
666					if ((*pv->fwrite_func)((void *)&arc,
667							       sizeof(arc),
668							       1,
669							       pv->stream) != 1) {
670
671						_profile_error(pv);
672					}
673
674					arc.count = 0x80000000;
675				}
676			}
677		}
678	}
679
680	return bytes;
681}
682
683
684/*
685 * Machine dependent function to write out prof records.
686 */
687
688size_t
689_prof_write(struct profile_vars *pv, struct callback *callback_ptr)
690{
691	struct alloc_context *aptr;
692	struct page_list *plist;
693	size_t bytes = 0;
694	struct prof_ext prof_st;
695	struct prof_int *pptr;
696	struct gfuncs *gptr;
697	int nrecs;
698	int i, j;
699
700	/* Write out information prof_mcount collects */
701	for (aptr = pv->acontext[ACONTEXT_PROF];
702	     aptr != (struct alloc_context *)0;
703	     aptr = aptr->next) {
704
705		for (plist = aptr->plist; plist != (struct page_list *)0; plist = plist->next) {
706			pptr = (struct prof_int *)plist->first;
707
708			for (i = 0; i < plist->num_allocations; (i++, pptr++)) {
709
710				/* Write out 2 records for each overflow, each with a
711				   count of 0x80000000 + the normal record */
712				prof_st = pptr->prof;
713				nrecs = 1 + (pptr->overflow * 2);
714
715				for (j = 0; j < nrecs; j++) {
716					bytes += sizeof (struct prof_ext);
717					if ((*pv->fwrite_func)((void *)&prof_st,
718							       sizeof(prof_st),
719							       1,
720							       pv->stream) != 1) {
721
722						_profile_error(pv);
723					}
724
725					prof_st.cncall = 0x80000000;
726				}
727			}
728		}
729	}
730
731	/* Now write out the prof information that gprof collects */
732	for (aptr = pv->acontext[ACONTEXT_GFUNC];
733	     aptr != (struct alloc_context *)0;
734	     aptr = aptr->next) {
735
736		for (plist = aptr->plist; plist != (struct page_list *)0; plist = plist->next) {
737			gptr = (struct gfuncs *)plist->first;
738
739			for (i = 0; i < plist->num_allocations; (i++, gptr++)) {
740
741				/* Write out 2 records for each overflow, each with a
742				   count of 0x80000000 + the normal record */
743				prof_st = gptr->prof.prof;
744				nrecs = 1 + (gptr->prof.overflow * 2);
745
746				for (j = 0; j < nrecs; j++) {
747					bytes += sizeof (struct prof_ext);
748					if ((*pv->fwrite_func)((void *)&prof_st,
749							       sizeof(prof_st),
750							       1,
751							       pv->stream) != 1) {
752
753						_profile_error(pv);
754					}
755
756					prof_st.cncall = 0x80000000;
757				}
758			}
759		}
760	}
761
762	return bytes;
763}
764
765
766/*
767 * Update any statistics.  For the 386, calculate the hash table loading factor.
768 * Also figure out how many overflows occurred.
769 */
770
771void
772_profile_update_stats(struct profile_vars *pv)
773{
774	struct alloc_context *aptr;
775	struct page_list *plist;
776	struct hasharc *hptr;
777	struct prof_int *pptr;
778	struct gfuncs *fptr;
779	LHISTCOUNTER *lptr;
780	int i;
781
782	for(i = 0; i < MAX_BUCKETS+1; i++) {
783		pv->stats.buckets[i] = 0;
784	}
785
786	pv->stats.hash_buckets = 0;
787
788	if (pv->md.hash_ptr) {
789		for (i = 0; i < pv->md.hash_size; i++) {
790			long nbuckets = 0;
791			struct hasharc *hptr;
792
793			for (hptr = pv->md.hash_ptr[i]; hptr; hptr = hptr->next) {
794				nbuckets++;
795			}
796
797			pv->stats.buckets[ (nbuckets < MAX_BUCKETS) ? nbuckets : MAX_BUCKETS ]++;
798			if (pv->stats.hash_buckets < nbuckets) {
799				pv->stats.hash_buckets = nbuckets;
800			}
801		}
802	}
803
804	/* Count how many times functions are out of bounds */
805	if (pv->check_funcs) {
806		pv->stats.bogus_count = 0;
807
808		for (aptr = pv->acontext[ACONTEXT_GPROF];
809		     aptr != (struct alloc_context *)0;
810		     aptr = aptr->next) {
811
812			for (plist = aptr->plist;
813			     plist != (struct page_list *)0;
814			     plist = plist->next) {
815
816				hptr = (struct hasharc *)plist->first;
817				for (i = 0; i < plist->num_allocations; (i++, hptr++)) {
818
819					if (hptr->arc.frompc < pv->profil_info.lowpc ||
820					    hptr->arc.frompc > pv->profil_info.highpc) {
821						pv->stats.bogus_count++;
822					}
823
824					if (hptr->arc.selfpc < pv->profil_info.lowpc ||
825					    hptr->arc.selfpc > pv->profil_info.highpc) {
826						pv->stats.bogus_count++;
827					}
828				}
829			}
830		}
831	}
832
833	/* Figure out how many overflows occurred */
834	PROF_ULONG_TO_CNT(pv->stats.prof_overflow, 0);
835	PROF_ULONG_TO_CNT(pv->stats.gprof_overflow, 0);
836
837	for (aptr = pv->acontext[ACONTEXT_GPROF];
838	     aptr != (struct alloc_context *)0;
839	     aptr = aptr->next) {
840
841		for (plist = aptr->plist;
842		     plist != (struct page_list *)0;
843		     plist = plist->next) {
844
845			hptr = (struct hasharc *)plist->first;
846			for (i = 0; i < plist->num_allocations; (i++, hptr++)) {
847				PROF_CNT_ADD(pv->stats.gprof_overflow, hptr->overflow);
848			}
849		}
850	}
851
852	for (aptr = pv->acontext[ACONTEXT_PROF];
853	     aptr != (struct alloc_context *)0;
854	     aptr = aptr->next) {
855
856		for (plist = aptr->plist;
857		     plist != (struct page_list *)0;
858		     plist = plist->next) {
859
860			pptr = (struct prof_int *)plist->first;
861			for (i = 0; i < plist->num_allocations; (i++, pptr++)) {
862				PROF_CNT_ADD(pv->stats.prof_overflow, pptr->overflow);
863			}
864		}
865	}
866
867	for (aptr = pv->acontext[ACONTEXT_GFUNC];
868	     aptr != (struct alloc_context *)0;
869	     aptr = aptr->next) {
870
871		for (plist = aptr->plist;
872		     plist != (struct page_list *)0;
873		     plist = plist->next) {
874
875			fptr = (struct gfuncs *)plist->first;
876			for (i = 0; i < plist->num_allocations; (i++, fptr++)) {
877				PROF_CNT_ADD(pv->stats.prof_overflow, fptr->prof.overflow);
878			}
879		}
880	}
881
882	/* Now go through & count how many times the LHISTCOUNTER overflowed into a 2nd word */
883	lptr = (LHISTCOUNTER *)pv->profil_buf;
884
885	if (pv->use_profil &&
886	    pv->profil_info.counter_size == sizeof(LHISTCOUNTER) &&
887	    lptr != (LHISTCOUNTER *)0) {
888
889		PROF_ULONG_TO_CNT(pv->stats.overflow_ticks, 0);
890		for (i = 0; i < pv->stats.profil_buckets; i++) {
891			PROF_CNT_ADD(pv->stats.overflow_ticks, lptr[i].high);
892		}
893	}
894}
895
896#if !defined(_KERNEL) && !defined(MACH_KERNEL)
897
898/*
899 * Routine callable from the debugger that prints the statistics.
900 */
901
902int _profile_debug(void)
903{
904	_profile_update_stats(&_profile_vars);
905	_profile_print_stats(stderr, &_profile_vars.stats, &_profile_vars.profil_info);
906	return 0;
907}
908
909/*
910 * Print the statistics structure in a meaningful way.
911 */
912
913void _profile_print_stats(FILE *stream,
914			  const struct profile_stats *stats,
915			  const struct profile_profil *pinfo)
916{
917	int i;
918	prof_cnt_t total_hits;
919	acontext_type_t ac;
920	int width_cname = 0;
921	int width_alloc = 0;
922	int width_wasted = 0;
923	int width_overhead = 0;
924	int width_context = 0;
925	static const char *cname[ACONTEXT_MAX] = ACONTEXT_NAMES;
926	char buf[20];
927
928	if (!stats) {
929		return;
930	}
931
932	if (!stream) {
933		stream = stdout;
934	}
935
936	sprintf(buf, "%ld.%ld", (long)stats->major_version, (long)stats->minor_version);
937	fprintf(stream, "%12s profiling version number\n", buf);
938	fprintf(stream, "%12lu size of profile_vars\n", (long unsigned)sizeof(struct profile_vars));
939	fprintf(stream, "%12lu size of profile_stats\n", (long unsigned)sizeof(struct profile_stats));
940	fprintf(stream, "%12lu size of profile_md\n", (long unsigned)sizeof(struct profile_md));
941	fprintf(stream, "%12s calls to _{,g}prof_mcount\n", PROF_CNT_TO_DECIMAL((char *)0, stats->cnt));
942	fprintf(stream, "%12s calls to old mcount\n", PROF_CNT_TO_DECIMAL((char *)0, stats->old_mcount));
943	fprintf(stream, "%12s calls to _dummy_mcount\n", PROF_CNT_TO_DECIMAL((char *)0, stats->dummy));
944	fprintf(stream, "%12lu functions profiled\n", (long unsigned)stats->prof_records);
945	fprintf(stream, "%12lu gprof arcs\n", (long unsigned)stats->gprof_records);
946
947	if (pinfo) {
948		fprintf(stream, "%12lu profil buckets\n", (long unsigned)stats->profil_buckets);
949		fprintf(stream, "%12lu profil lowpc  [0x%lx]\n",
950			(long unsigned)pinfo->lowpc,
951			(long unsigned)pinfo->lowpc);
952
953		fprintf(stream, "%12lu profil highpc [0x%lx]\n",
954			(long unsigned)pinfo->highpc,
955			(long unsigned)pinfo->highpc);
956
957		fprintf(stream, "%12lu profil highpc-lowpc\n", (long unsigned)(pinfo->highpc - pinfo->lowpc));
958		fprintf(stream, "%12lu profil buffer length\n", (long unsigned)pinfo->profil_len);
959		fprintf(stream, "%12lu profil sizeof counters\n", (long unsigned)pinfo->counter_size);
960		fprintf(stream, "%12lu profil scale (%g)\n",
961			(long unsigned)pinfo->scale,
962			((double)pinfo->scale) / ((double) 0x10000));
963
964
965		for (i = 0; i < sizeof (pinfo->profil_unused) / sizeof (pinfo->profil_unused[0]); i++) {
966			if (pinfo->profil_unused[i]) {
967				fprintf(stream, "%12lu profil unused[%2d] {0x%.8lx}\n",
968					(long unsigned)pinfo->profil_unused[i],
969					i,
970					(long unsigned)pinfo->profil_unused[i]);
971			}
972		}
973	}
974
975	if (stats->max_cpu) {
976		fprintf(stream, "%12lu current cpu/thread\n", (long unsigned)stats->my_cpu);
977		fprintf(stream, "%12lu max cpu/thread+1\n", (long unsigned)stats->max_cpu);
978	}
979
980	if (stats->bogus_count != 0) {
981		fprintf(stream,
982			"%12lu gprof functions found outside of range\n",
983			(long unsigned)stats->bogus_count);
984	}
985
986	if (PROF_CNT_NE_0(stats->too_low)) {
987		fprintf(stream,
988			"%12s histogram ticks were too low\n",
989			PROF_CNT_TO_DECIMAL((char *)0, stats->too_low));
990	}
991
992	if (PROF_CNT_NE_0(stats->too_high)) {
993		fprintf(stream,
994			"%12s histogram ticks were too high\n",
995			PROF_CNT_TO_DECIMAL((char *)0, stats->too_high));
996	}
997
998	if (PROF_CNT_NE_0(stats->acontext_locked)) {
999		fprintf(stream,
1000			"%12s times an allocation context was locked\n",
1001			PROF_CNT_TO_DECIMAL((char *)0, stats->acontext_locked));
1002	}
1003
1004	if (PROF_CNT_NE_0(stats->kernel_ticks)
1005	    || PROF_CNT_NE_0(stats->user_ticks)
1006	    || PROF_CNT_NE_0(stats->idle_ticks)) {
1007
1008		prof_cnt_t total_ticks;
1009		long double total_ticks_dbl;
1010
1011		total_ticks = stats->kernel_ticks;
1012		PROF_CNT_LADD(total_ticks, stats->user_ticks);
1013		PROF_CNT_LADD(total_ticks, stats->idle_ticks);
1014		total_ticks_dbl = PROF_CNT_TO_LDOUBLE(total_ticks);
1015
1016		fprintf(stream,
1017			"%12s total ticks\n",
1018			PROF_CNT_TO_DECIMAL((char *)0, total_ticks));
1019
1020		fprintf(stream,
1021			"%12s ticks within the kernel (%5.2Lf%%)\n",
1022			PROF_CNT_TO_DECIMAL((char *)0, stats->kernel_ticks),
1023			100.0L * (PROF_CNT_TO_LDOUBLE(stats->kernel_ticks) / total_ticks_dbl));
1024
1025		fprintf(stream,
1026			"%12s ticks within user space (%5.2Lf%%)\n",
1027			PROF_CNT_TO_DECIMAL((char *)0, stats->user_ticks),
1028			100.0L * (PROF_CNT_TO_LDOUBLE(stats->user_ticks) / total_ticks_dbl));
1029
1030		fprintf(stream,
1031			"%12s ticks idle              (%5.2Lf%%)\n",
1032			PROF_CNT_TO_DECIMAL((char *)0, stats->idle_ticks),
1033			100.0L * (PROF_CNT_TO_LDOUBLE(stats->idle_ticks) / total_ticks_dbl));
1034	}
1035
1036	if (PROF_CNT_NE_0(stats->overflow_ticks)) {
1037		fprintf(stream, "%12s times a HISTCOUNTER counter would have overflowed\n",
1038			PROF_CNT_TO_DECIMAL((char *)0, stats->overflow_ticks));
1039	}
1040
1041	if (PROF_CNT_NE_0(stats->hash_num)) {
1042		long double total_buckets = 0.0L;
1043
1044		for (i = 0; i <= MAX_BUCKETS; i++) {
1045			total_buckets += (long double)stats->buckets[i];
1046		}
1047
1048		fprintf(stream, "%12lu max bucket(s) on hash chain.\n", (long unsigned)stats->hash_buckets);
1049		for (i = 0; i < MAX_BUCKETS; i++) {
1050			if (stats->buckets[i] != 0) {
1051				fprintf(stream, "%12lu bucket(s) had %d entries (%5.2Lf%%)\n",
1052					(long unsigned)stats->buckets[i], i,
1053					100.0L * ((long double)stats->buckets[i] / total_buckets));
1054			}
1055		}
1056
1057		if (stats->buckets[MAX_BUCKETS] != 0) {
1058			fprintf(stream, "%12lu bucket(s) had more than %d entries (%5.2Lf%%)\n",
1059				(long unsigned)stats->buckets[MAX_BUCKETS], MAX_BUCKETS,
1060				100.0L * ((long double)stats->buckets[MAX_BUCKETS] / total_buckets));
1061		}
1062	}
1063
1064	PROF_ULONG_TO_CNT(total_hits, 0);
1065	for (i = 0; i < MAX_CACHE; i++) {
1066		PROF_CNT_LADD(total_hits, stats->cache_hits[i]);
1067	}
1068
1069	if (PROF_CNT_NE_0(total_hits)) {
1070		long double total		= PROF_CNT_TO_LDOUBLE(stats->cnt);
1071		long double total_hits_dbl	= PROF_CNT_TO_LDOUBLE(total_hits);
1072
1073		fprintf(stream,
1074			"%12s cache hits (%.2Lf%%)\n",
1075			PROF_CNT_TO_DECIMAL((char *)0, total_hits),
1076			100.0L * (total_hits_dbl / total));
1077
1078		for (i = 0; i < MAX_CACHE; i++) {
1079			if (PROF_CNT_NE_0(stats->cache_hits[i])) {
1080				fprintf(stream,
1081					"%12s times cache#%d matched (%5.2Lf%% of cache hits, %5.2Lf%% total)\n",
1082					PROF_CNT_TO_DECIMAL((char *)0, stats->cache_hits[i]),
1083					i+1,
1084					100.0L * (PROF_CNT_TO_LDOUBLE(stats->cache_hits[i]) / total_hits_dbl),
1085					100.0L * (PROF_CNT_TO_LDOUBLE(stats->cache_hits[i]) / total));
1086			}
1087		}
1088
1089		if (PROF_CNT_NE_0(stats->hash_num)) {
1090			fprintf(stream, "%12s times hash table searched\n", PROF_CNT_TO_DECIMAL((char *)0, stats->hash_num));
1091			fprintf(stream, "%12s hash buckets searched\n", PROF_CNT_TO_DECIMAL((char *)0, stats->hash_search));
1092			fprintf(stream, "%12.4Lf average buckets searched\n",
1093				PROF_CNT_TO_LDOUBLE(stats->hash_search) / PROF_CNT_TO_LDOUBLE(stats->hash_num));
1094		}
1095	}
1096
1097	for (i = 0; i < sizeof (stats->stats_unused) / sizeof (stats->stats_unused[0]); i++) {
1098		if (PROF_CNT_NE_0(stats->stats_unused[i])) {
1099			fprintf(stream, "%12s unused[%2d] {0x%.8lx 0x%.8lx}\n",
1100				PROF_CNT_TO_DECIMAL((char *)0, stats->stats_unused[i]),
1101				i,
1102				(unsigned long)stats->stats_unused[i].high,
1103				(unsigned long)stats->stats_unused[i].low);
1104		}
1105	}
1106
1107	/* Get the width for the allocation contexts */
1108	for (ac = ACONTEXT_FIRST; ac < ACONTEXT_MAX; ac++) {
1109		int len;
1110
1111		if (stats->num_context[ac] == 0) {
1112			continue;
1113		}
1114
1115		len = strlen (cname[ac]);
1116		if (len > width_cname)
1117			width_cname = len;
1118
1119		len = sprintf (buf, "%lu", (long unsigned)stats->num_alloc[ac]);
1120		if (len > width_alloc)
1121			width_alloc = len;
1122
1123		len = sprintf (buf, "%lu", (long unsigned)stats->wasted[ac]);
1124		if (len > width_wasted)
1125			width_wasted = len;
1126
1127		len = sprintf (buf, "%lu", (long unsigned)stats->overhead[ac]);
1128		if (len > width_overhead)
1129			width_overhead = len;
1130
1131		len = sprintf (buf, "%lu", (long unsigned)stats->num_context[ac]);
1132		if (len > width_context)
1133			width_context = len;
1134	}
1135
1136	/* Print info about allocation contexts */
1137	for (ac = ACONTEXT_FIRST; ac < ACONTEXT_MAX; ac++) {
1138		if (stats->num_context[ac] == 0) {
1139			continue;
1140		}
1141
1142		fprintf (stream,
1143			 "%12lu bytes in %-*s %*lu alloc, %*lu unused, %*lu over, %*lu context\n",
1144			 (long unsigned)stats->bytes_alloc[ac],
1145			 width_cname,    cname[ac],
1146			 width_alloc,    (long unsigned)stats->num_alloc[ac],
1147			 width_wasted,   (long unsigned)stats->wasted[ac],
1148			 width_overhead, (long unsigned)stats->overhead[ac],
1149			 width_context,  (long unsigned)stats->num_context[ac]);
1150	}
1151}
1152
1153
1154/*
1155 * Merge a new statistics field into an old one.
1156 */
1157
1158void _profile_merge_stats(struct profile_stats  *old_stats, const struct profile_stats  *new_stats)
1159{
1160	int i;
1161
1162	/* If nothing passed, just return */
1163	if (!old_stats || !new_stats)
1164		return;
1165
1166	/* If the old_stats has not been initialized, just copy in the new stats */
1167	if (old_stats->major_version == 0) {
1168	    *old_stats = *new_stats;
1169
1170	/* Otherwise, update stats, field by field */
1171	} else {
1172		if (old_stats->prof_records < new_stats->prof_records)
1173			old_stats->prof_records = new_stats->prof_records;
1174
1175		if (old_stats->gprof_records < new_stats->gprof_records)
1176			old_stats->gprof_records = new_stats->gprof_records;
1177
1178		if (old_stats->hash_buckets < new_stats->hash_buckets)
1179			old_stats->hash_buckets = new_stats->hash_buckets;
1180
1181		if (old_stats->bogus_count < new_stats->bogus_count)
1182			old_stats->bogus_count = new_stats->bogus_count;
1183
1184		PROF_CNT_LADD(old_stats->cnt,		  new_stats->cnt);
1185		PROF_CNT_LADD(old_stats->dummy,		  new_stats->dummy);
1186		PROF_CNT_LADD(old_stats->old_mcount,	  new_stats->old_mcount);
1187		PROF_CNT_LADD(old_stats->hash_search,	  new_stats->hash_search);
1188		PROF_CNT_LADD(old_stats->hash_num,	  new_stats->hash_num);
1189		PROF_CNT_LADD(old_stats->user_ticks,	  new_stats->user_ticks);
1190		PROF_CNT_LADD(old_stats->kernel_ticks,	  new_stats->kernel_ticks);
1191		PROF_CNT_LADD(old_stats->idle_ticks,	  new_stats->idle_ticks);
1192		PROF_CNT_LADD(old_stats->overflow_ticks,  new_stats->overflow_ticks);
1193		PROF_CNT_LADD(old_stats->acontext_locked, new_stats->acontext_locked);
1194		PROF_CNT_LADD(old_stats->too_low,	  new_stats->too_low);
1195		PROF_CNT_LADD(old_stats->too_high,	  new_stats->too_high);
1196		PROF_CNT_LADD(old_stats->prof_overflow,	  new_stats->prof_overflow);
1197		PROF_CNT_LADD(old_stats->gprof_overflow,  new_stats->gprof_overflow);
1198
1199		for (i = 0; i < (int)ACONTEXT_MAX; i++) {
1200			if (old_stats->num_alloc[i] < new_stats->num_alloc[i])
1201				old_stats->num_alloc[i] = new_stats->num_alloc[i];
1202
1203			if (old_stats->bytes_alloc[i] < new_stats->bytes_alloc[i])
1204				old_stats->bytes_alloc[i] = new_stats->bytes_alloc[i];
1205
1206			if (old_stats->num_context[i] < new_stats->num_context[i])
1207				old_stats->num_context[i] = new_stats->num_context[i];
1208
1209			if (old_stats->wasted[i] < new_stats->wasted[i])
1210				old_stats->wasted[i] = new_stats->wasted[i];
1211
1212			if (old_stats->overhead[i] < new_stats->overhead[i])
1213				old_stats->overhead[i] = new_stats->overhead[i];
1214
1215		}
1216
1217		for (i = 0; i < MAX_BUCKETS+1; i++) {
1218			if (old_stats->buckets[i] < new_stats->buckets[i])
1219				old_stats->buckets[i] = new_stats->buckets[i];
1220		}
1221
1222		for (i = 0; i < MAX_CACHE; i++) {
1223			PROF_CNT_LADD(old_stats->cache_hits[i], new_stats->cache_hits[i]);
1224		}
1225
1226		for (i = 0; i < sizeof(old_stats->stats_unused) / sizeof(old_stats->stats_unused[0]); i++) {
1227			PROF_CNT_LADD(old_stats->stats_unused[i], new_stats->stats_unused[i]);
1228		}
1229	}
1230}
1231
1232#endif
1233
1234
1235/*
1236 * Invalid function address used when checking of function addresses is
1237 * desired for gprof arcs, and we discover an address out of bounds.
1238 * There should be no callers of this function.
1239 */
1240
1241void
1242_bogus_function(void)
1243{
1244}
1245