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