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