m_transtab.c revision 663a1bd7a8def9d4ef4a10615732a15cc9b09171
1 2/*--------------------------------------------------------------------*/ 3/*--- Management of the translation table and cache. ---*/ 4/*--- vg_transtab.c ---*/ 5/*--------------------------------------------------------------------*/ 6 7/* 8 This file is part of Valgrind, a dynamic binary instrumentation 9 framework. 10 11 Copyright (C) 2000-2005 Julian Seward 12 jseward@acm.org 13 14 This program is free software; you can redistribute it and/or 15 modify it under the terms of the GNU General Public License as 16 published by the Free Software Foundation; either version 2 of the 17 License, or (at your option) any later version. 18 19 This program is distributed in the hope that it will be useful, but 20 WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 General Public License for more details. 23 24 You should have received a copy of the GNU General Public License 25 along with this program; if not, write to the Free Software 26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 27 02111-1307, USA. 28 29 The GNU General Public License is contained in the file COPYING. 30*/ 31 32#include "core.h" 33 34/* #define DEBUG_TRANSTAB */ 35 36 37/*-------------------------------------------------------------*/ 38/*--- Management of the FIFO-based translation table+cache. ---*/ 39/*-------------------------------------------------------------*/ 40 41/*------------------ CONSTANTS ------------------*/ 42 43/* Number of sectors the TC is divided into. If you need a larger 44 overall translation cache, increase this value. */ 45#define N_SECTORS 8 46 47/* Number of TC entries in each sector. This needs to be a prime 48 number to work properly, and it is strongly recommended not to 49 change this. */ 50#define N_TTES_PER_SECTOR /*30011*/ 40009 51 52/* Because each sector contains a hash table of TTEntries, we need to 53 specify the maximum allowable loading, after which the sector is 54 deemed full. */ 55#define SECTOR_TT_LIMIT_PERCENT 60 56 57/* The sector is deemed full when this many entries are in it. */ 58#define N_TTES_PER_SECTOR_USABLE \ 59 ((N_TTES_PER_SECTOR * SECTOR_TT_LIMIT_PERCENT) / 100) 60 61 62/*------------------ TYPES ------------------*/ 63 64/* A translation-cache entry is two parts: 65 - The guest address of the first (entry) bb in the translation, 66 as a 64-bit word. 67 - One or more 64-bit words containing the code. 68 It is supposed to be 64-bit aligned. 69*/ 70/* 71typedef 72 struct { 73 Addr64 orig_addr; 74 ULong code[0]; 75 } 76 TCEntry; 77*/ 78 79/* A translation-table entry. This indicates precisely which areas of 80 guest code are included in the translation, and contains all other 81 auxiliary info too. */ 82typedef 83 struct { 84 /* Profiling only: the count and weight (arbitrary meaning) for 85 this translation. Weight is a property of the translation 86 itself and computed once when the translation is created. 87 Count is an entry count for the translation and is 88 incremented by 1 every time the translation is used, if we 89 are profiling. */ 90 UInt count; 91 UShort weight; 92 93 /* Status of the slot. Note, we need to be able to do lazy 94 deletion, hence the Deleted state. */ 95 enum { InUse, Deleted, Empty } status; 96 97 /* Pointer to the corresponding TCEntry (must be in the same 98 sector!) */ 99 ULong* tce; 100 101 /* This is the original guest address that purportedly is the 102 entry point of the translation. You might think that .entry 103 should be the same as .vge->base[0], and most of the time it 104 is. However, when doing redirections, that is not the case. 105 .vge must always correctly describe the guest code sections 106 from which this translation was made. However, .entry may or 107 may not be a lie, depending on whether or not we're doing 108 redirection. */ 109 Addr64 entry; 110 111 /* This structure describes precisely what ranges of guest code 112 the translation covers, so we can decide whether or not to 113 delete it when translations of a given address range are 114 invalidated. */ 115 VexGuestExtents vge; 116 } 117 TTEntry; 118 119 120/* Finally, a sector itself. Each sector contains an array of 121 TCEntries, which hold code, and an array of TTEntries, containing 122 all required administrative info. Profiling is supported using the 123 TTEntry .count and .weight fields, if required. Each sector is 124 independent in that no cross-sector references are allowed. 125 126 If the sector is not in use, all three pointers are NULL and 127 tt_n_inuse is zero. 128*/ 129typedef 130 struct { 131 /* The TCEntry area. Size of this depends on the average 132 translation size. We try and size it so it becomes full 133 precisely when this sector's translation table (tt) reaches 134 its load limit (SECTOR_TT_LIMIT_PERCENT). */ 135 ULong* tc; 136 137 /* The TTEntry array. This is a fixed size, always containing 138 exactly N_TTES_PER_SECTOR entries. */ 139 TTEntry* tt; 140 141 /* This points to the current allocation point in tc. */ 142 ULong* tc_next; 143 144 /* The count of tt entries with state InUse. */ 145 Int tt_n_inuse; 146 } 147 Sector; 148 149 150/*------------------ DECLS ------------------*/ 151 152/* The root data structure is an array of sectors. The index of the 153 youngest sector is recorded, and new translations are put into that 154 sector. When it fills up, we move along to the next sector and 155 start to fill that up, wrapping around at the end of the array. 156 That way, once all N_TC_SECTORS have been bought into use for the 157 first time, and are full, we then re-use the oldest sector, 158 endlessly. 159 160 When running, youngest sector should be between >= 0 and < 161 N_TC_SECTORS. The initial -1 value indicates the TT/TC system is 162 not yet initialised. 163*/ 164static Sector sectors[N_SECTORS]; 165static Int youngest_sector = -1; 166 167/* The number of ULongs in each TCEntry area. This is computed once 168 at startup and does not change. */ 169static Int tc_sector_szQ; 170 171 172/* Fast helper for the TC. A direct-mapped cache which holds a 173 pointer to a TC entry which may or may not be the correct one, but 174 which we hope usually is. This array is referred to directly from 175 <arch>/dispatch.S. 176 177 Entries in tt_fast may point to any valid TC entry, regardless of 178 which sector it's in. Consequently we must be very careful to 179 invalidate this cache when TC entries are changed or disappear. 180 181 A special TCEntry -- bogus_tc_entry -- must be pointed at to cause 182 that cache entry to miss. This relies on the assumption that no 183 guest code actually has an address of 0x1. 184*/ 185/*global*/ ULong* VG_(tt_fast)[VG_TT_FAST_SIZE]; 186 187static ULong bogus_tc_entry = (Addr64)1; 188 189 190/* For profiling, we have a parallel array of pointers to .count 191 fields in TT entries. Again, these pointers must be invalidated 192 when translations disappear. A NULL pointer suffices to indicate 193 an unused slot. 194 195 tt_fast and tt_fastN change together: if tt_fast[i] points to 196 bogus_tc_entry then the corresponding tt_fastN[i] must be null. If 197 tt_fast[i] points to some TC entry somewhere, then tt_fastN[i] 198 *must* point to the .count field of the corresponding TT entry. 199 200 tt_fast and tt_fastN are referred to from assembly code 201 (dispatch.S). 202*/ 203/*global*/ UInt* VG_(tt_fastN)[VG_TT_FAST_SIZE]; 204 205 206/* Make sure we're not used before initialisation. */ 207static Bool init_done = False; 208 209 210/*------------------ STATS DECLS ------------------*/ 211 212/* Number of fast-cache updates and flushes done. */ 213ULong n_fast_flushes = 0; 214ULong n_fast_updates = 0; 215 216/* Number of full lookups done. */ 217ULong n_full_lookups = 0; 218ULong n_lookup_probes = 0; 219 220/* Number/osize/tsize of translations entered. */ 221ULong n_in_count = 0; 222ULong n_in_osize = 0; 223ULong n_in_tsize = 0; 224 225/* Number/osize of translations discarded due to lack of space. */ 226ULong n_dump_count = 0; 227ULong n_dump_osize = 0; 228 229/* Number/osize of translations discarded due to requests to do so. */ 230ULong n_disc_count = 0; 231ULong n_disc_osize = 0; 232 233 234 235/*-------------------------------------------------------------*/ 236/*--- Add/delete/find translations ---*/ 237/*-------------------------------------------------------------*/ 238 239static UInt vge_osize ( VexGuestExtents* vge ) 240{ 241 UInt i, n = 0; 242 for (i = 0; i < vge->n_used; i++) 243 n += (UInt)vge->len[i]; 244 return n; 245} 246 247static Bool isValidSector ( Int sector ) 248{ 249 if (sector < 0 || sector >= N_SECTORS) 250 return False; 251 return True; 252} 253 254static inline UInt HASH_TT ( Addr64 key ) 255{ 256 UInt kHi = (UInt)(key >> 32); 257 UInt kLo = (UInt)key; 258 return (kHi ^ kLo) % N_TTES_PER_SECTOR; 259} 260 261static void setFastCacheEntry ( Addr64 key, ULong* tce, UInt* count ) 262{ 263 UInt cno = ((UInt)key) & VG_TT_FAST_MASK; 264 VG_(tt_fast)[cno] = tce; 265 VG_(tt_fastN)[cno] = count; 266 n_fast_updates++; 267} 268 269static void invalidateFastCache ( void ) 270{ 271 UInt j; 272 for (j = 0; j < VG_TT_FAST_SIZE; j++) { 273 VG_(tt_fast)[j] = &bogus_tc_entry; 274 VG_(tt_fastN)[j] = NULL; 275 } 276 n_fast_flushes++; 277} 278 279static void initialiseSector ( Int sno ) 280{ 281 Int i; 282 vg_assert(isValidSector(sno)); 283 284 if (sectors[sno].tc == NULL) { 285 /* Sector has never been used before. Need to allocate tt and 286 tc. */ 287 vg_assert(sectors[sno].tt == NULL); 288 vg_assert(sectors[sno].tc_next == NULL); 289 vg_assert(sectors[sno].tt_n_inuse == 0); 290 sectors[sno].tc 291 = VG_(get_memory_from_mmap) 292 ( 8 * tc_sector_szQ, "sectors[sno].tc" ); 293 sectors[sno].tt 294 = VG_(get_memory_from_mmap) 295 ( N_TTES_PER_SECTOR * sizeof(TTEntry), "sectors[sno].tt" ); 296 if (VG_(clo_verbosity) > 2) 297 VG_(message)(Vg_DebugMsg, "TT/TC: initialise sector %d", sno); 298 } else { 299 /* Sector has been used before. */ 300 vg_assert(sectors[sno].tt != NULL); 301 vg_assert(sectors[sno].tc_next != NULL); 302 n_dump_count += sectors[sno].tt_n_inuse; 303 for (i = 0; i < N_TTES_PER_SECTOR; i++) { 304 if (sectors[sno].tt[i].status == InUse) { 305 n_dump_osize += vge_osize(§ors[sno].tt[i].vge); 306 } 307 } 308 if (VG_(clo_verbosity) > 2) 309 VG_(message)(Vg_DebugMsg, "TT/TC: recycle sector %d", sno); 310 } 311 312 sectors[sno].tc_next = sectors[sno].tc; 313 sectors[sno].tt_n_inuse = 0; 314 for (i = 0; i < N_TTES_PER_SECTOR; i++) 315 sectors[sno].tt[i].status = Empty; 316 317 invalidateFastCache(); 318} 319 320 321/* Add a translation of vge to TT/TC. The translation is temporarily 322 in code[0 .. code_len-1]. 323 324 pre: youngest_sector points to a valid (although possibly full) 325 sector. 326*/ 327void VG_(add_to_trans_tab)( VexGuestExtents* vge, 328 Addr64 entry, 329 AddrH code, 330 UInt code_len ) 331{ 332 Int tcAvailQ, reqdQ, y, i; 333 ULong *tce, *tce2; 334 UChar* srcP; 335 UChar* dstP; 336 337 vg_assert(init_done); 338 vg_assert(vge->n_used >= 1 && vge->n_used <= 3); 339 vg_assert(code_len > 0 && code_len < 20000); 340 341 if (0) 342 VG_(printf)("add_to_trans_tab(entry = 0x%llx, len = %d)\n", 343 entry, code_len); 344 345 n_in_count++; 346 n_in_tsize += code_len; 347 n_in_osize += vge_osize(vge); 348 349 y = youngest_sector; 350 vg_assert(isValidSector(y)); 351 352 if (sectors[y].tc == NULL) 353 initialiseSector(y); 354 355 /* Try putting the translation in this sector. */ 356 reqdQ = 1 + ((code_len + 7) >> 3); 357 358 /* Will it fit in tc? */ 359 tcAvailQ = ((ULong*)(§ors[y].tc[tc_sector_szQ])) 360 - ((ULong*)(sectors[y].tc_next)); 361 vg_assert(tcAvailQ >= 0); 362 vg_assert(tcAvailQ <= tc_sector_szQ); 363 364 if (tcAvailQ < reqdQ 365 || sectors[y].tt_n_inuse >= N_TTES_PER_SECTOR_USABLE) { 366 /* No. So move on to the next sector. Either it's never been 367 used before, in which case it will get its tt/tc allocated 368 now, or it has been used before, in which case it is set to be 369 empty, hence throwing out the oldest sector. */ 370 youngest_sector++; 371 if (youngest_sector >= N_SECTORS) 372 youngest_sector = 0; 373 y = youngest_sector; 374 initialiseSector(y); 375 } 376 377 /* Be sure ... */ 378 tcAvailQ = ((ULong*)(§ors[y].tc[tc_sector_szQ])) 379 - ((ULong*)(sectors[y].tc_next)); 380 vg_assert(tcAvailQ >= 0); 381 vg_assert(tcAvailQ <= tc_sector_szQ); 382 vg_assert(tcAvailQ >= reqdQ); 383 vg_assert(sectors[y].tt_n_inuse < N_TTES_PER_SECTOR_USABLE); 384 vg_assert(sectors[y].tt_n_inuse >= 0); 385 386 /* Copy into tc. */ 387 tce = sectors[y].tc_next; 388 vg_assert(tce >= §ors[y].tc[0]); 389 vg_assert(tce <= §ors[y].tc[tc_sector_szQ]); 390 391 tce[0] = entry; 392 dstP = (UChar*)(&tce[1]); 393 srcP = (UChar*)code; 394 for (i = 0; i < code_len; i++) 395 dstP[i] = srcP[i]; 396 sectors[y].tc_next += reqdQ; 397 sectors[y].tt_n_inuse++; 398 399 /* more paranoia */ 400 tce2 = sectors[y].tc_next; 401 vg_assert(tce2 >= §ors[y].tc[0]); 402 vg_assert(tce2 <= §ors[y].tc[tc_sector_szQ]); 403 404 /* Find an empty tt slot, and use it. There must be such a slot 405 since tt is never allowed to get completely full. */ 406 i = HASH_TT(entry); 407 vg_assert(i >= 0 && i < N_TTES_PER_SECTOR); 408 while (True) { 409 if (sectors[y].tt[i].status == Empty 410 || sectors[y].tt[i].status == Deleted) 411 break; 412 i++; 413 if (i >= N_TTES_PER_SECTOR) 414 i = 0; 415 } 416 417 sectors[y].tt[i].status = InUse; 418 sectors[y].tt[i].tce = tce; 419 sectors[y].tt[i].count = 0; 420 sectors[y].tt[i].weight = 1; 421 sectors[y].tt[i].vge = *vge; 422 sectors[y].tt[i].entry = entry; 423 424 setFastCacheEntry( entry, tce, §ors[y].tt[i].count ); 425} 426 427 428/* Search for the translation of the given guest address. If 429 requested, a successful search can also cause the fast-caches to be 430 updated. 431*/ 432Bool VG_(search_transtab) ( /*OUT*/AddrH* result, 433 Addr64 guest_addr, 434 Bool upd_cache ) 435{ 436 Int i, j, k, kstart, sno; 437 438 vg_assert(init_done); 439 /* Find the initial probe point just once. It will be the same in 440 all sectors and avoids multiple expensive % operations. */ 441 n_full_lookups++; 442 k = -1; 443 kstart = HASH_TT(guest_addr); 444 vg_assert(kstart >= 0 && kstart < N_TTES_PER_SECTOR); 445 446 /* Search in all the sectors. Although the order should not matter, 447 it might be most efficient to search in the order youngest to 448 oldest. */ 449 sno = youngest_sector; 450 for (i = 0; i < N_SECTORS; i++) { 451 452 if (sectors[sno].tc == NULL) 453 goto notfound; /* sector not in use. */ 454 455 k = kstart; 456 for (j = 0; j < N_TTES_PER_SECTOR; j++) { 457 n_lookup_probes++; 458 if (sectors[sno].tt[k].status == InUse 459 && sectors[sno].tt[k].entry == guest_addr) { 460 /* found it */ 461 if (upd_cache) 462 setFastCacheEntry( 463 guest_addr, sectors[sno].tt[k].tce, 464 §ors[sno].tt[k].count ); 465 if (result) 466 *result = sizeof(Addr64) + (AddrH)sectors[sno].tt[k].tce; 467 return True; 468 } 469 if (sectors[sno].tt[k].status == Empty) 470 break; /* not found in this sector */ 471 k++; 472 if (k == N_TTES_PER_SECTOR) 473 k = 0; 474 } 475 476 /* If we fall off the end, all entries are InUse and not 477 matching, or Deleted. In any case we did not find it in this 478 sector. */ 479 480 notfound: 481 /* move to the next oldest sector */ 482 sno = sno==0 ? (N_SECTORS-1) : (sno-1); 483 } 484 485 /* Not found in any sector. */ 486 return False; 487} 488 489 490/* Delete all translations which intersect with any part of the 491 specified guest address range. Note, this is SLOW. 492*/ 493 494static inline 495Bool overlap1 ( Addr64 s1, UInt r1, Addr64 s2, UInt r2 ) 496{ 497 Addr64 e1 = s1 + (ULong)r1 - 1ULL; 498 Addr64 e2 = s2 + (ULong)r1 - 1ULL; 499 if (e1 < s2 || e2 < s1) 500 return False; 501 return True; 502} 503 504static inline 505Bool overlaps ( Addr64 start, UInt range, VexGuestExtents* vge ) 506{ 507 if (overlap1(start, range, vge->base[0], (UInt)vge->len[0])) 508 return True; 509 if (vge->n_used < 2) 510 return False; 511 if (overlap1(start, range, vge->base[1], (UInt)vge->len[1])) 512 return True; 513 if (vge->n_used < 3) 514 return False; 515 if (overlap1(start, range, vge->base[2], (UInt)vge->len[2])) 516 return True; 517 return False; 518} 519 520 521void VG_(discard_translations) ( Addr64 guest_start, UInt range ) 522{ 523 Int sno, i; 524 Bool anyDeleted = False; 525 526 vg_assert(init_done); 527 528 for (sno = 0; sno < N_SECTORS; sno++) { 529 if (sectors[sno].tc == NULL) 530 continue; 531 for (i = 0; i < N_TTES_PER_SECTOR; i++) { 532 if (sectors[sno].tt[i].status == InUse 533 && overlaps( guest_start, range, §ors[sno].tt[i].vge )) { 534 sectors[sno].tt[i].status = Deleted; 535 sectors[sno].tt_n_inuse--; 536 anyDeleted = True; 537 n_disc_count++; 538 n_disc_osize += vge_osize(§ors[sno].tt[i].vge); 539 } 540 } 541 } 542 543 if (anyDeleted) 544 invalidateFastCache(); 545} 546 547 548/*------------------------------------------------------------*/ 549/*--- Sanity checking ---*/ 550/*------------------------------------------------------------*/ 551 552void VG_(sanity_check_tt_tc) ( Char* who ) 553{ 554} 555 556 557/*------------------------------------------------------------*/ 558/*--- Initialisation. ---*/ 559/*------------------------------------------------------------*/ 560 561void VG_(init_tt_tc) ( void ) 562{ 563 Int i, avg_codeszQ; 564 565 vg_assert(!init_done); 566 init_done = True; 567 568 /* Otherwise lots of things go wrong... */ 569 vg_assert(sizeof(ULong) == 8); 570 vg_assert(sizeof(Addr64) == 8); 571 572 if (VG_(clo_verbosity) > 2) 573 VG_(message)(Vg_DebugMsg, 574 "TT/TC: VG_(init_tt_tc) " 575 "(startup of code management)"); 576 577 /* Figure out how big each tc area should be. */ 578 avg_codeszQ 579 = (VG_(details).avg_translation_sizeB + 7) / 8; 580 581 tc_sector_szQ 582 = N_TTES_PER_SECTOR_USABLE * (1 + avg_codeszQ); 583 584 /* Ensure the calculated value is not way crazy. */ 585 vg_assert(tc_sector_szQ >= 2 * N_TTES_PER_SECTOR_USABLE); 586 vg_assert(tc_sector_szQ <= 50 * N_TTES_PER_SECTOR_USABLE); 587 588 /* Initialise the sectors */ 589 youngest_sector = 0; 590 for (i = 0; i < N_SECTORS; i++) { 591 sectors[i].tc = NULL; 592 sectors[i].tt = NULL; 593 sectors[i].tc_next = NULL; 594 sectors[i].tt_n_inuse = 0; 595 } 596 597 /* and the fast caches. */ 598 invalidateFastCache(); 599 600 if (VG_(clo_verbosity) > 2) { 601 VG_(message)(Vg_DebugMsg, 602 "TT/TC: cache: %d sectors of %d bytes each = %d total", 603 N_SECTORS, 8 * tc_sector_szQ, 604 N_SECTORS * 8 * tc_sector_szQ ); 605 VG_(message)(Vg_DebugMsg, 606 "TT/TC: table: %d total entries, max occupancy %d (%d%%)", 607 N_SECTORS * N_TTES_PER_SECTOR, 608 N_SECTORS * N_TTES_PER_SECTOR_USABLE, 609 SECTOR_TT_LIMIT_PERCENT ); 610 } 611} 612 613 614/*------------------------------------------------------------*/ 615/*--- Printing out statistics. ---*/ 616/*------------------------------------------------------------*/ 617 618static ULong safe_idiv( ULong a, ULong b ) 619{ 620 return (b == 0 ? 0 : a / b); 621} 622 623UInt VG_(get_bbs_translated) ( void ) 624{ 625 return n_in_count; 626} 627 628void VG_(print_tt_tc_stats) ( void ) 629{ 630 VG_(message)(Vg_DebugMsg, 631 " tt/tc: %llu tt lookups requiring %llu probes", 632 n_full_lookups, n_lookup_probes ); 633 VG_(message)(Vg_DebugMsg, 634 " tt/tc: %llu fast-cache updates, %llu flushes", 635 n_fast_updates, n_fast_flushes ); 636 637 VG_(message)(Vg_DebugMsg, 638 "translate: new %lld (%lld -> %lld; ratio %lld:10)", 639 n_in_count, n_in_osize, n_in_tsize, 640 safe_idiv(10*n_in_tsize, n_in_osize)); 641 VG_(message)(Vg_DebugMsg, 642 "translate: dumped %lld (%lld -> ?" "?)", 643 n_dump_count, n_dump_osize ); 644 VG_(message)(Vg_DebugMsg, 645 "translate: discarded %lld (%lld -> ?" "?)", 646 n_disc_count, n_disc_osize ); 647} 648 649/*------------------------------------------------------------*/ 650/*--- Printing out of profiling results. ---*/ 651/*------------------------------------------------------------*/ 652 653/* Only the top N_MAX bbs will be displayed. */ 654#define N_MAX 200 655 656static TTEntry* tops[N_MAX]; 657 658static ULong score ( TTEntry* tte ) 659{ 660 return ((ULong)tte->weight) * ((ULong)tte->count); 661} 662 663static Bool heavier ( TTEntry* t1, TTEntry* t2 ) 664{ 665 return score(t1) > score(t2); 666} 667 668/* Print n/m in form xx.yy% */ 669static 670void percentify ( ULong n, ULong m, Int field_width, Char* buf) 671{ 672 Int i, len, space; 673 ULong lo, hi; 674 if (m == 0) m = 1; /* stay sane */ 675 hi = (n * 100) / m; 676 lo = (((n * 100) - hi * m) * 100) / m; 677 vg_assert(lo < 100); 678 if (lo < 10) 679 VG_(sprintf)(buf, "%lld.0%lld%%", hi, lo); 680 else 681 VG_(sprintf)(buf, "%lld.%lld%%", hi, lo); 682 683 len = VG_(strlen)(buf); 684 space = field_width - len; 685 if (space < 0) space = 0; /* Allow for v. small field_width */ 686 i = len; 687 688 /* Right justify in field */ 689 for ( ; i >= 0; i--) buf[i + space] = buf[i]; 690 for (i = 0; i < space; i++) buf[i] = ' '; 691} 692 693 694void VG_(show_BB_profile) ( void ) 695{ 696 Char name[64]; 697 Int sno, i, r, s; 698 ULong score_total, score_cumul, score_here; 699 Char buf_cumul[10]; 700 Char buf_here[10]; 701 702 /* First, compute the total weighted count, and find the top N 703 ttes. tops contains pointers to the most-used N_MAX blocks, in 704 descending order (viz, tops[0] is the highest scorer). */ 705 for (i = 0; i < N_MAX; i++) 706 tops[i] = NULL; 707 708 score_total = 0; 709 710 for (sno = 0; sno < N_SECTORS; sno++) { 711 if (sectors[sno].tc == NULL) 712 continue; 713 for (i = 0; i < N_TTES_PER_SECTOR; i++) { 714 if (sectors[sno].tt[i].status != InUse) 715 continue; 716 score_total += score(§ors[sno].tt[i]); 717 /* Find the rank for sectors[sno].tt[i]. */ 718 r = N_MAX-1; 719 while (True) { 720 if (r == -1) 721 break; 722 if (tops[r] == NULL) { 723 r--; 724 continue; 725 } 726 if (heavier(§ors[sno].tt[i], tops[r])) { 727 r--; 728 continue; 729 } 730 break; 731 } 732 r++; 733 vg_assert(r >= 0 && r <= N_MAX); 734 /* This bb should be placed at r, and bbs above it shifted 735 upwards one slot. */ 736 if (r < N_MAX) { 737 for (s = N_MAX-1; s > r; s--) 738 tops[s] = tops[s-1]; 739 tops[r] = §ors[sno].tt[i]; 740 } 741 } 742 } 743 744 VG_(printf)("\n"); 745 VG_(printf)("------------------------------------------------------------\n"); 746 VG_(printf)("--- BEGIN BB Profile (summary of scores) ---\n"); 747 VG_(printf)("------------------------------------------------------------\n"); 748 VG_(printf)("\n"); 749 750 VG_(printf)("Total score = %lld\n\n", score_total); 751 752 score_cumul = 0; 753 for (r = 0; r < N_MAX; r++) { 754 if (tops[r] == NULL) 755 continue; 756 name[0] = 0; 757 VG_(get_fnname_w_offset)(tops[r]->entry, name, 64); 758 name[63] = 0; 759 score_here = score(tops[r]); 760 score_cumul += score_here; 761 percentify(score_cumul, score_total, 6, buf_cumul); 762 percentify(score_here, score_total, 6, buf_here); 763 VG_(printf)("%3d: (%9lld %s) %9lld %s 0x%llx %s\n", 764 r, 765 score_cumul, buf_cumul, 766 score_here, buf_here, tops[r]->entry, name ); 767 } 768 769 VG_(printf)("\n"); 770 VG_(printf)("------------------------------------------------------------\n"); 771 VG_(printf)("--- BB Profile (BB details) ---\n"); 772 VG_(printf)("------------------------------------------------------------\n"); 773 VG_(printf)("\n"); 774 775 score_cumul = 0; 776 for (r = 0; r < N_MAX; r++) { 777 if (tops[r] == NULL) 778 continue; 779 name[0] = 0; 780 VG_(get_fnname_w_offset)(tops[r]->entry, name, 64); 781 name[63] = 0; 782 score_here = score(tops[r]); 783 score_cumul += score_here; 784 percentify(score_cumul, score_total, 6, buf_cumul); 785 percentify(score_here, score_total, 6, buf_here); 786 VG_(printf)("\n"); 787 VG_(printf)("=-=-=-=-=-=-=-=-=-=-=-=-=-= begin BB rank %d " 788 "=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n", r); 789 VG_(printf)("%3d: (%9lld %s) %9lld %s 0x%llx %s\n", 790 r, 791 score_cumul, buf_cumul, 792 score_here, buf_here, tops[r]->entry, name ); 793 VG_(printf)("\n"); 794 VG_(translate)(0, tops[r]->entry, True, VG_(clo_profile_flags)); 795 VG_(printf)("=-=-=-=-=-=-=-=-=-=-=-=-=-= end BB rank %d " 796 "=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n", r); 797 } 798 799 VG_(printf)("\n"); 800 VG_(printf)("------------------------------------------------------------\n"); 801 VG_(printf)("--- END BB Profile ---\n"); 802 VG_(printf)("------------------------------------------------------------\n"); 803 VG_(printf)("\n"); 804} 805 806 807/*--------------------------------------------------------------------*/ 808/*--- end vg_transtab.c ---*/ 809/*--------------------------------------------------------------------*/ 810