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