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