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