blob: 2c94b7379790febf2dcbd15a054c1486312d05f1 [file] [log] [blame]
sewardjde4a1d02002-03-22 01:27:54 +00001
2/*--------------------------------------------------------------------*/
3/*--- Management of the translation table and cache. ---*/
4/*--- vg_transtab.c ---*/
5/*--------------------------------------------------------------------*/
6
7/*
njnb9c427c2004-12-01 14:14:42 +00008 This file is part of Valgrind, a dynamic binary instrumentation
9 framework.
sewardjde4a1d02002-03-22 01:27:54 +000010
sewardjfa8ec112005-01-19 11:55:34 +000011 Copyright (C) 2000-2005 Julian Seward
sewardjde4a1d02002-03-22 01:27:54 +000012 jseward@acm.org
sewardjde4a1d02002-03-22 01:27:54 +000013
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
njn25e49d8e72002-09-23 09:36:25 +000029 The GNU General Public License is contained in the file COPYING.
sewardjde4a1d02002-03-22 01:27:54 +000030*/
31
nethercotef1e5e152004-09-01 23:58:16 +000032#include "core.h"
sewardjde4a1d02002-03-22 01:27:54 +000033
sewardj18d75132002-05-16 11:06:21 +000034/* #define DEBUG_TRANSTAB */
35
sewardjde4a1d02002-03-22 01:27:54 +000036
sewardj6c3769f2002-11-29 01:02:45 +000037/*-------------------------------------------------------------*/
38/*--- Management of the FIFO-based translation table+cache. ---*/
39/*-------------------------------------------------------------*/
sewardjde4a1d02002-03-22 01:27:54 +000040
sewardj6c3769f2002-11-29 01:02:45 +000041/*------------------ CONSTANTS ------------------*/
sewardjde4a1d02002-03-22 01:27:54 +000042
sewardjfa8ec112005-01-19 11:55:34 +000043/* 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
sewardjde4a1d02002-03-22 01:27:54 +000046
sewardjfa8ec112005-01-19 11:55:34 +000047/* 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
sewardjde4a1d02002-03-22 01:27:54 +000051
sewardjfa8ec112005-01-19 11:55:34 +000052/* 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
sewardjde4a1d02002-03-22 01:27:54 +000056
sewardjfa8ec112005-01-19 11:55:34 +000057/* 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)
sewardjde4a1d02002-03-22 01:27:54 +000060
sewardjde4a1d02002-03-22 01:27:54 +000061
sewardj6c3769f2002-11-29 01:02:45 +000062/*------------------ TYPES ------------------*/
63
sewardjfa8ec112005-01-19 11:55:34 +000064/* 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/*
sewardj6c3769f2002-11-29 01:02:45 +000071typedef
72 struct {
sewardjfa8ec112005-01-19 11:55:34 +000073 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;
sewardj6c3769f2002-11-29 01:02:45 +0000116 }
117 TTEntry;
118
sewardj4ccf7072004-11-28 16:58:05 +0000119
sewardjfa8ec112005-01-19 11:55:34 +0000120/* 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.
sewardj4ccf7072004-11-28 16:58:05 +0000125
sewardjfa8ec112005-01-19 11:55:34 +0000126 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;
sewardj4ccf7072004-11-28 16:58:05 +0000136
sewardjfa8ec112005-01-19 11:55:34 +0000137 /* The TTEntry array. This is a fixed size, always containing
138 exactly N_TTES_PER_SECTOR entries. */
139 TTEntry* tt;
sewardj4ccf7072004-11-28 16:58:05 +0000140
sewardjfa8ec112005-01-19 11:55:34 +0000141 /* This points to the current allocation point in tc. */
142 ULong* tc_next;
sewardj6c3769f2002-11-29 01:02:45 +0000143
sewardjfa8ec112005-01-19 11:55:34 +0000144 /* The count of tt entries with state InUse. */
145 Int tt_n_inuse;
146 }
147 Sector;
sewardjde4a1d02002-03-22 01:27:54 +0000148
sewardjde4a1d02002-03-22 01:27:54 +0000149
sewardj6c3769f2002-11-29 01:02:45 +0000150/*------------------ DECLS ------------------*/
151
sewardjfa8ec112005-01-19 11:55:34 +0000152/* 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.
sewardj6c3769f2002-11-29 01:02:45 +0000159
sewardjfa8ec112005-01-19 11:55:34 +0000160 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;
sewardj6c3769f2002-11-29 01:02:45 +0000166
sewardjfa8ec112005-01-19 11:55:34 +0000167/* The number of ULongs in each TCEntry area. This is computed once
168 at startup and does not change. */
169static Int tc_sector_szQ;
nethercote92e7b7f2004-08-07 17:52:25 +0000170
171
sewardjfa8ec112005-01-19 11:55:34 +0000172/* Fast helper for the TC. A direct-mapped cache which holds a
sewardjc54ee502004-11-29 19:46:47 +0000173 pointer to a TC entry which may or may not be the correct one, but
sewardjde4a1d02002-03-22 01:27:54 +0000174 which we hope usually is. This array is referred to directly from
sewardjfa8ec112005-01-19 11:55:34 +0000175 <arch>/dispatch.S.
sewardj8aef1192002-07-24 09:36:36 +0000176
sewardjfa8ec112005-01-19 11:55:34 +0000177 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;
sewardj22854b92002-11-30 14:00:47 +0000188
189
sewardjfa8ec112005-01-19 11:55:34 +0000190/* 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.
sewardj6c3769f2002-11-29 01:02:45 +0000194
sewardjfa8ec112005-01-19 11:55:34 +0000195 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/*------------------ STATS DECLS ------------------*/
207
208/* Number of fast-cache updates and flushes done. */
209ULong n_fast_flushes = 0;
210ULong n_fast_updates = 0;
211
212/* Number of full lookups done. */
213ULong n_full_lookups = 0;
214ULong n_lookup_probes = 0;
215
216/* Number/osize/tsize of translations entered. */
217ULong n_in_count = 0;
218ULong n_in_osize = 0;
219ULong n_in_tsize = 0;
220
221/* Number/osize of translations discarded due to lack of space. */
222ULong n_dump_count = 0;
223ULong n_dump_osize = 0;
224
225/* Number/osize of translations discarded due to requests to do so. */
226ULong n_disc_count = 0;
227ULong n_disc_osize = 0;
228
229
230
231/*-------------------------------------------------------------*/
232/*--- Add/delete/find translations ---*/
233/*-------------------------------------------------------------*/
234
235static UInt vge_osize ( VexGuestExtents* vge )
sewardjc0d8f682002-11-30 00:49:43 +0000236{
sewardjfa8ec112005-01-19 11:55:34 +0000237 UInt i, n = 0;
238 for (i = 0; i < vge->n_used; i++)
239 n += (UInt)vge->len[i];
240 return n;
sewardjc0d8f682002-11-30 00:49:43 +0000241}
242
sewardjfa8ec112005-01-19 11:55:34 +0000243static Bool isValidSector ( Int sector )
sewardj6c3769f2002-11-29 01:02:45 +0000244{
sewardjfa8ec112005-01-19 11:55:34 +0000245 if (sector < 0 || sector >= N_SECTORS)
246 return False;
247 return True;
248}
249
250static inline UInt HASH_TT ( Addr64 key )
251{
252 UInt kHi = (UInt)(key >> 32);
253 UInt kLo = (UInt)key;
254 return (kHi ^ kLo) % N_TTES_PER_SECTOR;
255}
256
257static void setFastCacheEntry ( Addr64 key, ULong* tce, UInt* count )
258{
259 UInt cno = ((UInt)key) & VG_TT_FAST_MASK;
260 VG_(tt_fast)[cno] = tce;
261 VG_(tt_fastN)[cno] = count;
262 n_fast_updates++;
263}
264
265static void invalidateFastCache ( void )
266{
267 UInt j;
268 for (j = 0; j < VG_TT_FAST_SIZE; j++) {
269 VG_(tt_fast)[j] = &bogus_tc_entry;
270 VG_(tt_fastN)[j] = NULL;
271 }
272 n_fast_flushes++;
273}
274
275static void initialiseSector ( Int sno )
276{
277 Int i;
278 vg_assert(isValidSector(sno));
279
280 if (sectors[sno].tc == NULL) {
281 /* Sector has never been used before. Need to allocate tt and
282 tc. */
283 vg_assert(sectors[sno].tt == NULL);
284 vg_assert(sectors[sno].tc_next == NULL);
285 vg_assert(sectors[sno].tt_n_inuse == 0);
286 sectors[sno].tc
287 = VG_(get_memory_from_mmap)
288 ( 8 * tc_sector_szQ, "sectors[sno].tc" );
289 sectors[sno].tt
290 = VG_(get_memory_from_mmap)
291 ( N_TTES_PER_SECTOR * sizeof(TTEntry), "sectors[sno].tt" );
292 if (VG_(clo_verbosity) > 2)
293 VG_(message)(Vg_DebugMsg, "TT/TC: initialise sector %d", sno);
294 } else {
295 /* Sector has been used before. */
296 vg_assert(sectors[sno].tt != NULL);
297 vg_assert(sectors[sno].tc_next != NULL);
298 n_dump_count += sectors[sno].tt_n_inuse;
299 for (i = 0; i < N_TTES_PER_SECTOR; i++) {
300 if (sectors[sno].tt[i].status == InUse) {
301 n_dump_osize += vge_osize(&sectors[sno].tt[i].vge);
302 }
303 }
304 if (VG_(clo_verbosity) > 2)
305 VG_(message)(Vg_DebugMsg, "TT/TC: recycle sector %d", sno);
306 }
307
308 sectors[sno].tc_next = sectors[sno].tc;
309 sectors[sno].tt_n_inuse = 0;
310 for (i = 0; i < N_TTES_PER_SECTOR; i++)
311 sectors[sno].tt[i].status = Empty;
312
313 invalidateFastCache();
sewardj6c3769f2002-11-29 01:02:45 +0000314}
315
316
sewardjfa8ec112005-01-19 11:55:34 +0000317/* Add a translation of vge to TT/TC. The translation is temporarily
318 in code[0 .. code_len-1].
319
320 pre: youngest_sector points to a valid (although possibly full)
321 sector.
322*/
323void VG_(add_to_trans_tab)( VexGuestExtents* vge,
324 Addr64 entry,
325 AddrH code,
326 UInt code_len )
sewardj6c3769f2002-11-29 01:02:45 +0000327{
sewardjfa8ec112005-01-19 11:55:34 +0000328 Int tcAvailQ, reqdQ, y, i;
329 ULong *tce, *tce2;
330 UChar* srcP;
331 UChar* dstP;
332
333 vg_assert(vge->n_used >= 1 && vge->n_used <= 3);
334 vg_assert(code_len > 0 && code_len < 20000);
335
336 if (0)
337 VG_(printf)("add_to_trans_tab(entry = 0x%llx, len = %d)\n",
338 entry, code_len);
339
340 n_in_count++;
341 n_in_tsize += code_len;
342 n_in_osize += vge_osize(vge);
343
344 y = youngest_sector;
345 vg_assert(isValidSector(y));
346
347 if (sectors[y].tc == NULL)
348 initialiseSector(y);
349
350 /* Try putting the translation in this sector. */
351 reqdQ = 1 + ((code_len + 7) >> 3);
352
353 /* Will it fit in tc? */
354 tcAvailQ = ((ULong*)(&sectors[y].tc[tc_sector_szQ]))
355 - ((ULong*)(sectors[y].tc_next));
356 vg_assert(tcAvailQ >= 0);
357 vg_assert(tcAvailQ <= tc_sector_szQ);
358
359 if (tcAvailQ < reqdQ
360 || sectors[y].tt_n_inuse >= N_TTES_PER_SECTOR_USABLE) {
361 /* No. So move on to the next sector. Either it's never been
362 used before, in which case it will get its tt/tc allocated
363 now, or it has been used before, in which case it is set to be
364 empty, hence throwing out the oldest sector. */
365 youngest_sector++;
366 if (youngest_sector >= N_SECTORS)
367 youngest_sector = 0;
368 y = youngest_sector;
369 initialiseSector(y);
370 }
371
372 /* Be sure ... */
373 tcAvailQ = ((ULong*)(&sectors[y].tc[tc_sector_szQ]))
374 - ((ULong*)(sectors[y].tc_next));
375 vg_assert(tcAvailQ >= 0);
376 vg_assert(tcAvailQ <= tc_sector_szQ);
377 vg_assert(tcAvailQ >= reqdQ);
378 vg_assert(sectors[y].tt_n_inuse < N_TTES_PER_SECTOR_USABLE);
379 vg_assert(sectors[y].tt_n_inuse >= 0);
380
381 /* Copy into tc. */
382 tce = sectors[y].tc_next;
383 vg_assert(tce >= &sectors[y].tc[0]);
384 vg_assert(tce <= &sectors[y].tc[tc_sector_szQ]);
385
386 tce[0] = entry;
387 dstP = (UChar*)(&tce[1]);
388 srcP = (UChar*)code;
389 for (i = 0; i < code_len; i++)
390 dstP[i] = srcP[i];
391 sectors[y].tc_next += reqdQ;
392 sectors[y].tt_n_inuse++;
393
394 /* more paranoia */
395 tce2 = sectors[y].tc_next;
396 vg_assert(tce2 >= &sectors[y].tc[0]);
397 vg_assert(tce2 <= &sectors[y].tc[tc_sector_szQ]);
398
399 /* Find an empty tt slot, and use it. There must be such a slot
400 since tt is never allowed to get completely full. */
401 i = HASH_TT(entry);
402 vg_assert(i >= 0 && i < N_TTES_PER_SECTOR);
sewardj6c3769f2002-11-29 01:02:45 +0000403 while (True) {
sewardjfa8ec112005-01-19 11:55:34 +0000404 if (sectors[y].tt[i].status == Empty
405 || sectors[y].tt[i].status == Deleted)
sewardj6c3769f2002-11-29 01:02:45 +0000406 break;
407 i++;
sewardjfa8ec112005-01-19 11:55:34 +0000408 if (i >= N_TTES_PER_SECTOR)
sewardj6c3769f2002-11-29 01:02:45 +0000409 i = 0;
410 }
sewardj22854b92002-11-30 14:00:47 +0000411
sewardjfa8ec112005-01-19 11:55:34 +0000412 sectors[y].tt[i].status = InUse;
413 sectors[y].tt[i].tce = tce;
414 sectors[y].tt[i].count = 0;
415 sectors[y].tt[i].weight = 1;
416 sectors[y].tt[i].vge = *vge;
417 sectors[y].tt[i].entry = entry;
418
419 setFastCacheEntry( entry, tce, &sectors[y].tt[i].count );
sewardj6c3769f2002-11-29 01:02:45 +0000420}
421
422
sewardjfa8ec112005-01-19 11:55:34 +0000423/* Search for the translation of the given guest address. If
424 requested, a successful search can also cause the fast-caches to be
425 updated.
sewardj6c3769f2002-11-29 01:02:45 +0000426*/
sewardjfa8ec112005-01-19 11:55:34 +0000427Bool VG_(search_transtab) ( /*OUT*/AddrH* result,
428 Addr64 guest_addr,
429 Bool upd_cache )
sewardj6c3769f2002-11-29 01:02:45 +0000430{
sewardjfa8ec112005-01-19 11:55:34 +0000431 Int i, j, k, kstart, sno;
432 /* Find the initial probe point just once. It will be the same in
433 all sectors and avoids multiple expensive % operations. */
434 n_full_lookups++;
435 k = -1;
436 kstart = HASH_TT(guest_addr);
437 vg_assert(kstart >= 0 && kstart < N_TTES_PER_SECTOR);
sewardj6c3769f2002-11-29 01:02:45 +0000438
sewardjfa8ec112005-01-19 11:55:34 +0000439 /* Search in all the sectors. Although the order should not matter,
440 it might be most efficient to search in the order youngest to
441 oldest. */
442 sno = youngest_sector;
443 for (i = 0; i < N_SECTORS; i++) {
sewardj6c3769f2002-11-29 01:02:45 +0000444
sewardjfa8ec112005-01-19 11:55:34 +0000445 if (sectors[sno].tc == NULL)
446 goto notfound; /* sector not in use. */
sewardj6c3769f2002-11-29 01:02:45 +0000447
sewardjfa8ec112005-01-19 11:55:34 +0000448 k = kstart;
449 for (j = 0; j < N_TTES_PER_SECTOR; j++) {
450 n_lookup_probes++;
451 if (sectors[sno].tt[k].status == InUse
452 && sectors[sno].tt[k].entry == guest_addr) {
453 /* found it */
454 if (upd_cache)
455 setFastCacheEntry(
456 guest_addr, sectors[sno].tt[k].tce,
457 &sectors[sno].tt[k].count );
458 if (result)
459 *result = sizeof(Addr64) + (AddrH)sectors[sno].tt[k].tce;
460 return True;
461 }
462 if (sectors[sno].tt[k].status == Empty)
463 break; /* not found in this sector */
464 k++;
465 if (k == N_TTES_PER_SECTOR)
466 k = 0;
sewardj6c3769f2002-11-29 01:02:45 +0000467 }
sewardjfa8ec112005-01-19 11:55:34 +0000468
469 /* If we fall off the end, all entries are InUse and not
470 matching, or Deleted. In any case we did not find it in this
471 sector. */
472
473 notfound:
474 /* move to the next oldest sector */
475 sno = sno==0 ? (N_SECTORS-1) : (sno-1);
sewardj6c3769f2002-11-29 01:02:45 +0000476 }
sewardjfa8ec112005-01-19 11:55:34 +0000477
478 /* Not found in any sector. */
479 return False;
sewardj6c3769f2002-11-29 01:02:45 +0000480}
481
482
sewardjfa8ec112005-01-19 11:55:34 +0000483/* Delete all translations which intersect with any part of the
484 specified guest address range. Note, this is SLOW.
sewardjde4a1d02002-03-22 01:27:54 +0000485*/
sewardjfa8ec112005-01-19 11:55:34 +0000486
487static inline
488Bool overlap1 ( Addr64 s1, UInt r1, Addr64 s2, UInt r2 )
489{
490 Addr64 e1 = s1 + (ULong)r1 - 1ULL;
491 Addr64 e2 = s2 + (ULong)r1 - 1ULL;
492 if (e1 < s2 || e2 < s1)
493 return False;
494 return True;
495}
496
497static inline
498Bool overlaps ( Addr64 start, UInt range, VexGuestExtents* vge )
499{
500 if (overlap1(start, range, vge->base[0], (UInt)vge->len[0]))
501 return True;
502 if (vge->n_used < 2)
503 return False;
504 if (overlap1(start, range, vge->base[1], (UInt)vge->len[1]))
505 return True;
506 if (vge->n_used < 3)
507 return False;
508 if (overlap1(start, range, vge->base[2], (UInt)vge->len[2]))
509 return True;
510 return False;
511}
512
513
514void VG_(discard_translations) ( Addr64 guest_start, UInt range )
515{
516 Int sno, i;
517 Bool anyDeleted = False;
518
519 for (sno = 0; sno < N_SECTORS; sno++) {
520 if (sectors[sno].tc == NULL)
521 continue;
522 for (i = 0; i < N_TTES_PER_SECTOR; i++) {
523 if (sectors[sno].tt[i].status == InUse
524 && overlaps( guest_start, range, &sectors[sno].tt[i].vge )) {
525 sectors[sno].tt[i].status = Deleted;
526 sectors[sno].tt_n_inuse--;
527 anyDeleted = True;
528 n_disc_count++;
529 n_disc_osize += vge_osize(&sectors[sno].tt[i].vge);
530 }
531 }
532 }
533
534 if (anyDeleted)
535 invalidateFastCache();
536}
537
538
539/*------------------------------------------------------------*/
540/*--- Sanity checking ---*/
541/*------------------------------------------------------------*/
542
sewardj4ccf7072004-11-28 16:58:05 +0000543void VG_(sanity_check_tt_tc) ( Char* who )
sewardjde4a1d02002-03-22 01:27:54 +0000544{
sewardjde4a1d02002-03-22 01:27:54 +0000545}
546
547
548/*------------------------------------------------------------*/
549/*--- Initialisation. ---*/
550/*------------------------------------------------------------*/
551
sewardjc0d8f682002-11-30 00:49:43 +0000552void VG_(init_tt_tc) ( void )
553{
sewardjfa8ec112005-01-19 11:55:34 +0000554 Int i, avg_codeszQ;
sewardjc0d8f682002-11-30 00:49:43 +0000555
sewardj4ccf7072004-11-28 16:58:05 +0000556 /* Otherwise lots of things go wrong... */
sewardjfa8ec112005-01-19 11:55:34 +0000557 vg_assert(sizeof(ULong) == 8);
558 vg_assert(sizeof(Addr64) == 8);
559
560 if (VG_(clo_verbosity) > 2)
561 VG_(message)(Vg_DebugMsg,
562 "TT/TC: VG_(init_tt_tc) "
563 "(startup of code management)");
564
565 /* Figure out how big each tc area should be. */
566 avg_codeszQ
567 = (VG_(details).avg_translation_sizeB + 7) / 8;
568
569 tc_sector_szQ
570 = N_TTES_PER_SECTOR_USABLE * (1 + avg_codeszQ);
571
sewardjc0d8f682002-11-30 00:49:43 +0000572 /* Ensure the calculated value is not way crazy. */
sewardjfa8ec112005-01-19 11:55:34 +0000573 vg_assert(tc_sector_szQ >= 2 * N_TTES_PER_SECTOR_USABLE);
574 vg_assert(tc_sector_szQ <= 50 * N_TTES_PER_SECTOR_USABLE);
sewardjc0d8f682002-11-30 00:49:43 +0000575
sewardjfa8ec112005-01-19 11:55:34 +0000576 /* Initialise the sectors */
577 youngest_sector = 0;
578 for (i = 0; i < N_SECTORS; i++) {
579 sectors[i].tc = NULL;
580 sectors[i].tt = NULL;
581 sectors[i].tc_next = NULL;
582 sectors[i].tt_n_inuse = 0;
sewardjc0d8f682002-11-30 00:49:43 +0000583 }
sewardjc0d8f682002-11-30 00:49:43 +0000584
sewardjfa8ec112005-01-19 11:55:34 +0000585 /* and the fast caches. */
586 invalidateFastCache();
sewardjc0d8f682002-11-30 00:49:43 +0000587
588 if (VG_(clo_verbosity) > 2) {
589 VG_(message)(Vg_DebugMsg,
sewardjfa8ec112005-01-19 11:55:34 +0000590 "TT/TC: cache: %d sectors of %d bytes each = %d total",
591 N_SECTORS, 8 * tc_sector_szQ,
592 N_SECTORS * 8 * tc_sector_szQ );
sewardjc0d8f682002-11-30 00:49:43 +0000593 VG_(message)(Vg_DebugMsg,
sewardjfa8ec112005-01-19 11:55:34 +0000594 "TT/TC: table: %d total entries, max occupancy %d (%d%%)",
595 N_SECTORS * N_TTES_PER_SECTOR,
596 N_SECTORS * N_TTES_PER_SECTOR_USABLE,
597 SECTOR_TT_LIMIT_PERCENT );
598 }
599}
600
601
602/*------------------------------------------------------------*/
603/*--- Printing out statistics. ---*/
604/*------------------------------------------------------------*/
605
606static ULong safe_idiv( ULong a, ULong b )
607{
608 return (b == 0 ? 0 : a / b);
609}
610
611UInt VG_(get_bbs_translated) ( void )
612{
613 return n_in_count;
614}
615
616void VG_(print_tt_tc_stats) ( void )
617{
618 VG_(message)(Vg_DebugMsg,
619 " tt/tc: %llu tt lookups requiring %llu probes",
620 n_full_lookups, n_lookup_probes );
621 VG_(message)(Vg_DebugMsg,
622 " tt/tc: %llu fast-cache updates, %llu flushes",
623 n_fast_updates, n_fast_flushes );
624
625 VG_(message)(Vg_DebugMsg,
626 "translate: new %lld (%lld -> %lld; ratio %lld:10)",
627 n_in_count, n_in_osize, n_in_tsize,
628 safe_idiv(10*n_in_tsize, n_in_osize));
629 VG_(message)(Vg_DebugMsg,
630 "translate: dumped %lld (%lld -> ?" "?)",
631 n_dump_count, n_dump_osize );
632 VG_(message)(Vg_DebugMsg,
633 "translate: discarded %lld (%lld -> ?" "?)",
634 n_disc_count, n_disc_osize );
635}
636
637/*------------------------------------------------------------*/
638/*--- Printing out of profiling results. ---*/
639/*------------------------------------------------------------*/
640
641/* Only the top N_MAX bbs will be displayed. */
sewardjf9f19492005-01-24 10:42:46 +0000642#define N_MAX 200
sewardjfa8ec112005-01-19 11:55:34 +0000643
644static TTEntry* tops[N_MAX];
645
646static ULong score ( TTEntry* tte )
647{
648 return ((ULong)tte->weight) * ((ULong)tte->count);
649}
650
651static Bool heavier ( TTEntry* t1, TTEntry* t2 )
652{
653 return score(t1) > score(t2);
654}
655
656/* Print n/m in form xx.yy% */
657static
658void percentify ( ULong n, ULong m, Int field_width, Char* buf)
659{
660 Int i, len, space;
661 ULong lo, hi;
662 if (m == 0) m = 1; /* stay sane */
663 hi = (n * 100) / m;
664 lo = (((n * 100) - hi * m) * 100) / m;
665 vg_assert(lo < 100);
666 if (lo < 10)
667 VG_(sprintf)(buf, "%lld.0%lld%%", hi, lo);
668 else
669 VG_(sprintf)(buf, "%lld.%lld%%", hi, lo);
670
671 len = VG_(strlen)(buf);
672 space = field_width - len;
673 if (space < 0) space = 0; /* Allow for v. small field_width */
674 i = len;
675
676 /* Right justify in field */
677 for ( ; i >= 0; i--) buf[i + space] = buf[i];
678 for (i = 0; i < space; i++) buf[i] = ' ';
679}
680
681
682void VG_(show_BB_profile) ( void )
683{
684 Char name[64];
685 Int sno, i, r, s;
686 ULong score_total, score_cumul, score_here;
687 Char buf_cumul[10];
688 Char buf_here[10];
689
690 /* First, compute the total weighted count, and find the top N
691 ttes. tops contains pointers to the most-used N_MAX blocks, in
692 descending order (viz, tops[0] is the highest scorer). */
693 for (i = 0; i < N_MAX; i++)
694 tops[i] = NULL;
695
696 score_total = 0;
697
698 for (sno = 0; sno < N_SECTORS; sno++) {
699 if (sectors[sno].tc == NULL)
700 continue;
701 for (i = 0; i < N_TTES_PER_SECTOR; i++) {
702 if (sectors[sno].tt[i].status != InUse)
703 continue;
704 score_total += score(&sectors[sno].tt[i]);
705 /* Find the rank for sectors[sno].tt[i]. */
706 r = N_MAX-1;
707 while (True) {
708 if (r == -1)
709 break;
710 if (tops[r] == NULL) {
711 r--;
712 continue;
713 }
714 if (heavier(&sectors[sno].tt[i], tops[r])) {
715 r--;
716 continue;
717 }
718 break;
719 }
720 r++;
721 vg_assert(r >= 0 && r <= N_MAX);
722 /* This bb should be placed at r, and bbs above it shifted
723 upwards one slot. */
724 if (r < N_MAX) {
725 for (s = N_MAX-1; s > r; s--)
726 tops[s] = tops[s-1];
727 tops[r] = &sectors[sno].tt[i];
728 }
729 }
sewardjc0d8f682002-11-30 00:49:43 +0000730 }
731
sewardjfa8ec112005-01-19 11:55:34 +0000732 VG_(printf)("\n");
733 VG_(printf)("------------------------------------------------------------\n");
734 VG_(printf)("--- BEGIN BB Profile (summary of scores) ---\n");
735 VG_(printf)("------------------------------------------------------------\n");
736 VG_(printf)("\n");
737
738 VG_(printf)("Total score = %lld\n\n", score_total);
739
740 score_cumul = 0;
741 for (r = 0; r < N_MAX; r++) {
742 if (tops[r] == NULL)
743 continue;
744 name[0] = 0;
745 VG_(get_fnname_w_offset)(tops[r]->entry, name, 64);
746 name[63] = 0;
747 score_here = score(tops[r]);
748 score_cumul += score_here;
749 percentify(score_cumul, score_total, 6, buf_cumul);
750 percentify(score_here, score_total, 6, buf_here);
751 VG_(printf)("%3d: (%9lld %s) %9lld %s 0x%llx %s\n",
752 r,
753 score_cumul, buf_cumul,
754 score_here, buf_here, tops[r]->entry, name );
755 }
756
757 VG_(printf)("\n");
758 VG_(printf)("------------------------------------------------------------\n");
759 VG_(printf)("--- BB Profile (BB details) ---\n");
760 VG_(printf)("------------------------------------------------------------\n");
761 VG_(printf)("\n");
762
763 score_cumul = 0;
764 for (r = 0; r < N_MAX; r++) {
765 if (tops[r] == NULL)
766 continue;
767 name[0] = 0;
768 VG_(get_fnname_w_offset)(tops[r]->entry, name, 64);
769 name[63] = 0;
770 score_here = score(tops[r]);
771 score_cumul += score_here;
772 percentify(score_cumul, score_total, 6, buf_cumul);
773 percentify(score_here, score_total, 6, buf_here);
774 VG_(printf)("\n");
775 VG_(printf)("=-=-=-=-=-=-=-=-=-=-=-=-=-= begin BB rank %d "
776 "=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n", r);
777 VG_(printf)("%3d: (%9lld %s) %9lld %s 0x%llx %s\n",
778 r,
779 score_cumul, buf_cumul,
780 score_here, buf_here, tops[r]->entry, name );
781 VG_(printf)("\n");
782 VG_(translate)(0, tops[r]->entry, True, VG_(clo_profile_flags));
783 VG_(printf)("=-=-=-=-=-=-=-=-=-=-=-=-=-= end BB rank %d "
784 "=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n", r);
785 }
786
787 VG_(printf)("\n");
788 VG_(printf)("------------------------------------------------------------\n");
789 VG_(printf)("--- END BB Profile ---\n");
790 VG_(printf)("------------------------------------------------------------\n");
791 VG_(printf)("\n");
sewardjc0d8f682002-11-30 00:49:43 +0000792}
793
sewardjfa8ec112005-01-19 11:55:34 +0000794
sewardjde4a1d02002-03-22 01:27:54 +0000795/*--------------------------------------------------------------------*/
796/*--- end vg_transtab.c ---*/
797/*--------------------------------------------------------------------*/