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