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#if NCPUS > 1 246struct profile_vars *_profile_vars_cpus[NCPUS] = { &_profile_vars }; 247struct 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 253void * 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 280void 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 291void _profile_error(struct profile_vars *pv) 292{ 293 panic("Fatal error in profiling"); 294} 295 296 297/* 298 * Function to set up the initial allocation for a context block. 299 */ 300 301static 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 349/* 350 * Machine dependent function to initialize things. 351 */ 352 353void 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 529/* 530 * Machine dependent functions to start and stop profiling. 531 */ 532 533int 534_profile_md_start(void) 535{ 536 _mcount_ptr = _profile_vars.md.save_mcount_ptr; 537 return 0; 538} 539 540int 541_profile_md_stop(void) 542{ 543 _mcount_ptr = _dummy_mcount; 544 return 0; 545} 546 547 548/* 549 * Free up all memory in a memory context block. 550 */ 551 552static 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 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 581void 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 629/* 630 * Machine dependent function to write out gprof records. 631 */ 632 633size_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 690/* 691 * Machine dependent function to write out prof records. 692 */ 693 694size_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 772/* 773 * Update any statistics. For the 386, calculate the hash table loading factor. 774 * Also figure out how many overflows occurred. 775 */ 776 777void 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 902#if !defined(_KERNEL) && !defined(MACH_KERNEL) 903 904/* 905 * Routine callable from the debugger that prints the statistics. 906 */ 907 908int _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 919void _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 1160/* 1161 * Merge a new statistics field into an old one. 1162 */ 1163 1164void _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 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 1247void 1248_bogus_function(void) 1249{ 1250} 1251