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