]> git.saurik.com Git - apple/xnu.git/blob - osfmk/profiling/i386/profile-md.c
ff5c91d1b56d5a51062af530bf2d12806f5d3d71
[apple/xnu.git] / osfmk / profiling / i386 / profile-md.c
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
216 extern int _profile_do_stats;
217 extern size_t _profile_size;
218 extern size_t _profile_stats_size;
219 extern size_t _profile_md_size;
220 extern size_t _profile_profil_size;
221 extern size_t _profile_hash_size;
222
223 /*
224 * All profiling variables, and a dummy gprof record.
225 */
226
227 struct profile_vars _profile_vars = { 0 };
228 struct hasharc _gprof_dummy = { 0 };
229
230 /*
231 * Forward references.
232 */
233
234 static void *_profile_md_acontext(struct profile_vars *pv,
235 void *ptr,
236 size_t len,
237 acontext_type_t type);
238
239 static void _profile_reset_alloc(struct profile_vars *,
240 acontext_type_t);
241
242 extern void _bogus_function(void);
243 \f
244
245 #define PROFILE_VARS(cpu) (&_profile_vars)
246
247 void *
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
274 void
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
285 void _profile_error(struct profile_vars *pv)
286 {
287 panic("Fatal error in profiling");
288 }
289
290 \f
291 /*
292 * Function to set up the initial allocation for a context block.
293 */
294
295 static 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 \f
343 /*
344 * Machine dependent function to initialize things.
345 */
346
347 void
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 \f
523 /*
524 * Machine dependent functions to start and stop profiling.
525 */
526
527 int
528 _profile_md_start(void)
529 {
530 _mcount_ptr = _profile_vars.md.save_mcount_ptr;
531 return 0;
532 }
533
534 int
535 _profile_md_stop(void)
536 {
537 _mcount_ptr = _dummy_mcount;
538 return 0;
539 }
540
541 \f
542 /*
543 * Free up all memory in a memory context block.
544 */
545
546 static 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 \f
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
575 void
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 \f
623 /*
624 * Machine dependent function to write out gprof records.
625 */
626
627 size_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 \f
684 /*
685 * Machine dependent function to write out prof records.
686 */
687
688 size_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 \f
766 /*
767 * Update any statistics. For the 386, calculate the hash table loading factor.
768 * Also figure out how many overflows occurred.
769 */
770
771 void
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 \f
896 #if !defined(_KERNEL) && !defined(MACH_KERNEL)
897
898 /*
899 * Routine callable from the debugger that prints the statistics.
900 */
901
902 int _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
913 void _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 \f
1154 /*
1155 * Merge a new statistics field into an old one.
1156 */
1157
1158 void _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 \f
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
1241 void
1242 _bogus_function(void)
1243 {
1244 }