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