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(&sectors[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*)(&sectors[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*)(&sectors[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 >= &sectors[y].tc[0]);
394   vg_assert(tce <= &sectors[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 >= &sectors[y].tc[0]);
407   vg_assert(tce2 <= &sectors[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, &sectors[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                              &sectors[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, &sectors[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(&sectors[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(&sectors[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(&sectors[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] = &sectors[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