blob: 3c87213e1b2c94264ee2c121258754ee21e18cda [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
njnc7561b92005-06-19 01:24:32 +000032#include "pub_core_basics.h"
sewardj45f4e7c2005-09-27 19:20:21 +000033#include "pub_core_debuglog.h"
sewardj10f08cf2005-06-29 10:16:14 +000034#include "pub_core_machine.h" // ppc32: VG_(cache_line_size_ppc32)
njn97405b22005-06-02 03:39:33 +000035#include "pub_core_libcbase.h"
njn132bfcc2005-06-04 19:16:06 +000036#include "pub_core_libcassert.h"
njn36a20fa2005-06-03 03:08:39 +000037#include "pub_core_libcprint.h"
njn20242342005-05-16 23:31:24 +000038#include "pub_core_options.h"
sewardj10f08cf2005-06-29 10:16:14 +000039#include "pub_core_tooliface.h" // For VG_(details).avg_translation_sizeB
njn8bddf582005-05-13 23:40:55 +000040#include "pub_core_transtab.h"
sewardj45f4e7c2005-09-27 19:20:21 +000041#include "pub_core_aspacemgr.h"
42#include "pub_core_mallocfree.h" // VG_(out_of_memory_NORETURN)
sewardjde4a1d02002-03-22 01:27:54 +000043
sewardj18d75132002-05-16 11:06:21 +000044/* #define DEBUG_TRANSTAB */
45
sewardjde4a1d02002-03-22 01:27:54 +000046
sewardj6c3769f2002-11-29 01:02:45 +000047/*-------------------------------------------------------------*/
48/*--- Management of the FIFO-based translation table+cache. ---*/
49/*-------------------------------------------------------------*/
sewardjde4a1d02002-03-22 01:27:54 +000050
sewardj6c3769f2002-11-29 01:02:45 +000051/*------------------ CONSTANTS ------------------*/
sewardjde4a1d02002-03-22 01:27:54 +000052
sewardjfa8ec112005-01-19 11:55:34 +000053/* Number of sectors the TC is divided into. If you need a larger
54 overall translation cache, increase this value. */
55#define N_SECTORS 8
sewardjde4a1d02002-03-22 01:27:54 +000056
sewardjfa8ec112005-01-19 11:55:34 +000057/* Number of TC entries in each sector. This needs to be a prime
58 number to work properly, and it is strongly recommended not to
59 change this. */
sewardj34483bc2005-09-28 11:50:20 +000060#define N_TTES_PER_SECTOR /*30011*/ /*40009*/ 80021
sewardjde4a1d02002-03-22 01:27:54 +000061
sewardjfa8ec112005-01-19 11:55:34 +000062/* Because each sector contains a hash table of TTEntries, we need to
63 specify the maximum allowable loading, after which the sector is
64 deemed full. */
sewardj34483bc2005-09-28 11:50:20 +000065#define SECTOR_TT_LIMIT_PERCENT 66
sewardjde4a1d02002-03-22 01:27:54 +000066
sewardjfa8ec112005-01-19 11:55:34 +000067/* The sector is deemed full when this many entries are in it. */
68#define N_TTES_PER_SECTOR_USABLE \
69 ((N_TTES_PER_SECTOR * SECTOR_TT_LIMIT_PERCENT) / 100)
sewardjde4a1d02002-03-22 01:27:54 +000070
sewardjde4a1d02002-03-22 01:27:54 +000071
sewardj6c3769f2002-11-29 01:02:45 +000072/*------------------ TYPES ------------------*/
73
sewardjfa8ec112005-01-19 11:55:34 +000074/* A translation-cache entry is two parts:
75 - The guest address of the first (entry) bb in the translation,
76 as a 64-bit word.
77 - One or more 64-bit words containing the code.
78 It is supposed to be 64-bit aligned.
79*/
80/*
sewardj6c3769f2002-11-29 01:02:45 +000081typedef
82 struct {
sewardjfa8ec112005-01-19 11:55:34 +000083 Addr64 orig_addr;
84 ULong code[0];
85 }
86 TCEntry;
87*/
88
89/* A translation-table entry. This indicates precisely which areas of
90 guest code are included in the translation, and contains all other
91 auxiliary info too. */
92typedef
93 struct {
94 /* Profiling only: the count and weight (arbitrary meaning) for
95 this translation. Weight is a property of the translation
96 itself and computed once when the translation is created.
97 Count is an entry count for the translation and is
98 incremented by 1 every time the translation is used, if we
99 are profiling. */
100 UInt count;
101 UShort weight;
102
103 /* Status of the slot. Note, we need to be able to do lazy
104 deletion, hence the Deleted state. */
105 enum { InUse, Deleted, Empty } status;
106
107 /* Pointer to the corresponding TCEntry (must be in the same
108 sector!) */
109 ULong* tce;
110
111 /* This is the original guest address that purportedly is the
112 entry point of the translation. You might think that .entry
113 should be the same as .vge->base[0], and most of the time it
114 is. However, when doing redirections, that is not the case.
115 .vge must always correctly describe the guest code sections
116 from which this translation was made. However, .entry may or
117 may not be a lie, depending on whether or not we're doing
118 redirection. */
119 Addr64 entry;
120
121 /* This structure describes precisely what ranges of guest code
122 the translation covers, so we can decide whether or not to
123 delete it when translations of a given address range are
124 invalidated. */
125 VexGuestExtents vge;
sewardj6c3769f2002-11-29 01:02:45 +0000126 }
127 TTEntry;
128
sewardj4ccf7072004-11-28 16:58:05 +0000129
sewardjfa8ec112005-01-19 11:55:34 +0000130/* Finally, a sector itself. Each sector contains an array of
131 TCEntries, which hold code, and an array of TTEntries, containing
132 all required administrative info. Profiling is supported using the
133 TTEntry .count and .weight fields, if required. Each sector is
134 independent in that no cross-sector references are allowed.
sewardj4ccf7072004-11-28 16:58:05 +0000135
sewardjfa8ec112005-01-19 11:55:34 +0000136 If the sector is not in use, all three pointers are NULL and
137 tt_n_inuse is zero.
138*/
139typedef
140 struct {
141 /* The TCEntry area. Size of this depends on the average
142 translation size. We try and size it so it becomes full
143 precisely when this sector's translation table (tt) reaches
144 its load limit (SECTOR_TT_LIMIT_PERCENT). */
145 ULong* tc;
sewardj4ccf7072004-11-28 16:58:05 +0000146
sewardjfa8ec112005-01-19 11:55:34 +0000147 /* The TTEntry array. This is a fixed size, always containing
148 exactly N_TTES_PER_SECTOR entries. */
149 TTEntry* tt;
sewardj4ccf7072004-11-28 16:58:05 +0000150
sewardjfa8ec112005-01-19 11:55:34 +0000151 /* This points to the current allocation point in tc. */
152 ULong* tc_next;
sewardj6c3769f2002-11-29 01:02:45 +0000153
sewardjfa8ec112005-01-19 11:55:34 +0000154 /* The count of tt entries with state InUse. */
155 Int tt_n_inuse;
156 }
157 Sector;
sewardjde4a1d02002-03-22 01:27:54 +0000158
sewardjde4a1d02002-03-22 01:27:54 +0000159
sewardj6c3769f2002-11-29 01:02:45 +0000160/*------------------ DECLS ------------------*/
161
sewardjfa8ec112005-01-19 11:55:34 +0000162/* The root data structure is an array of sectors. The index of the
163 youngest sector is recorded, and new translations are put into that
164 sector. When it fills up, we move along to the next sector and
165 start to fill that up, wrapping around at the end of the array.
166 That way, once all N_TC_SECTORS have been bought into use for the
167 first time, and are full, we then re-use the oldest sector,
168 endlessly.
sewardj6c3769f2002-11-29 01:02:45 +0000169
sewardjfa8ec112005-01-19 11:55:34 +0000170 When running, youngest sector should be between >= 0 and <
171 N_TC_SECTORS. The initial -1 value indicates the TT/TC system is
172 not yet initialised.
173*/
174static Sector sectors[N_SECTORS];
175static Int youngest_sector = -1;
sewardj6c3769f2002-11-29 01:02:45 +0000176
sewardjfa8ec112005-01-19 11:55:34 +0000177/* The number of ULongs in each TCEntry area. This is computed once
178 at startup and does not change. */
179static Int tc_sector_szQ;
nethercote92e7b7f2004-08-07 17:52:25 +0000180
181
sewardjfa8ec112005-01-19 11:55:34 +0000182/* Fast helper for the TC. A direct-mapped cache which holds a
sewardjc54ee502004-11-29 19:46:47 +0000183 pointer to a TC entry which may or may not be the correct one, but
sewardjde4a1d02002-03-22 01:27:54 +0000184 which we hope usually is. This array is referred to directly from
sewardjfa8ec112005-01-19 11:55:34 +0000185 <arch>/dispatch.S.
sewardj8aef1192002-07-24 09:36:36 +0000186
sewardjfa8ec112005-01-19 11:55:34 +0000187 Entries in tt_fast may point to any valid TC entry, regardless of
188 which sector it's in. Consequently we must be very careful to
189 invalidate this cache when TC entries are changed or disappear.
190
191 A special TCEntry -- bogus_tc_entry -- must be pointed at to cause
192 that cache entry to miss. This relies on the assumption that no
193 guest code actually has an address of 0x1.
194*/
195/*global*/ ULong* VG_(tt_fast)[VG_TT_FAST_SIZE];
196
197static ULong bogus_tc_entry = (Addr64)1;
sewardj22854b92002-11-30 14:00:47 +0000198
199
sewardjfa8ec112005-01-19 11:55:34 +0000200/* For profiling, we have a parallel array of pointers to .count
201 fields in TT entries. Again, these pointers must be invalidated
202 when translations disappear. A NULL pointer suffices to indicate
203 an unused slot.
sewardj6c3769f2002-11-29 01:02:45 +0000204
sewardjfa8ec112005-01-19 11:55:34 +0000205 tt_fast and tt_fastN change together: if tt_fast[i] points to
206 bogus_tc_entry then the corresponding tt_fastN[i] must be null. If
207 tt_fast[i] points to some TC entry somewhere, then tt_fastN[i]
208 *must* point to the .count field of the corresponding TT entry.
209
210 tt_fast and tt_fastN are referred to from assembly code
211 (dispatch.S).
212*/
213/*global*/ UInt* VG_(tt_fastN)[VG_TT_FAST_SIZE];
214
215
sewardj663a1bd2005-04-24 11:22:44 +0000216/* Make sure we're not used before initialisation. */
217static Bool init_done = False;
218
219
sewardjfa8ec112005-01-19 11:55:34 +0000220/*------------------ STATS DECLS ------------------*/
221
222/* Number of fast-cache updates and flushes done. */
223ULong n_fast_flushes = 0;
224ULong n_fast_updates = 0;
225
226/* Number of full lookups done. */
227ULong n_full_lookups = 0;
228ULong n_lookup_probes = 0;
229
sewardj26412bd2005-07-07 10:05:05 +0000230/* Number/osize/tsize of translations entered; also the number of
231 those for which self-checking was requested. */
232ULong n_in_count = 0;
233ULong n_in_osize = 0;
234ULong n_in_tsize = 0;
235ULong n_in_sc_count = 0;
sewardjfa8ec112005-01-19 11:55:34 +0000236
237/* Number/osize of translations discarded due to lack of space. */
238ULong n_dump_count = 0;
239ULong n_dump_osize = 0;
240
241/* Number/osize of translations discarded due to requests to do so. */
242ULong n_disc_count = 0;
243ULong n_disc_osize = 0;
244
245
246
247/*-------------------------------------------------------------*/
248/*--- Add/delete/find translations ---*/
249/*-------------------------------------------------------------*/
250
251static UInt vge_osize ( VexGuestExtents* vge )
sewardjc0d8f682002-11-30 00:49:43 +0000252{
sewardjfa8ec112005-01-19 11:55:34 +0000253 UInt i, n = 0;
254 for (i = 0; i < vge->n_used; i++)
255 n += (UInt)vge->len[i];
256 return n;
sewardjc0d8f682002-11-30 00:49:43 +0000257}
258
sewardjfa8ec112005-01-19 11:55:34 +0000259static Bool isValidSector ( Int sector )
sewardj6c3769f2002-11-29 01:02:45 +0000260{
sewardjfa8ec112005-01-19 11:55:34 +0000261 if (sector < 0 || sector >= N_SECTORS)
262 return False;
263 return True;
264}
265
266static inline UInt HASH_TT ( Addr64 key )
267{
268 UInt kHi = (UInt)(key >> 32);
269 UInt kLo = (UInt)key;
270 return (kHi ^ kLo) % N_TTES_PER_SECTOR;
271}
272
273static void setFastCacheEntry ( Addr64 key, ULong* tce, UInt* count )
274{
275 UInt cno = ((UInt)key) & VG_TT_FAST_MASK;
276 VG_(tt_fast)[cno] = tce;
277 VG_(tt_fastN)[cno] = count;
278 n_fast_updates++;
279}
280
281static void invalidateFastCache ( void )
282{
283 UInt j;
284 for (j = 0; j < VG_TT_FAST_SIZE; j++) {
285 VG_(tt_fast)[j] = &bogus_tc_entry;
286 VG_(tt_fastN)[j] = NULL;
287 }
288 n_fast_flushes++;
289}
290
291static void initialiseSector ( Int sno )
292{
sewardj45f4e7c2005-09-27 19:20:21 +0000293 Int i;
294 SysRes sres;
sewardjfa8ec112005-01-19 11:55:34 +0000295 vg_assert(isValidSector(sno));
296
297 if (sectors[sno].tc == NULL) {
298 /* Sector has never been used before. Need to allocate tt and
299 tc. */
300 vg_assert(sectors[sno].tt == NULL);
301 vg_assert(sectors[sno].tc_next == NULL);
302 vg_assert(sectors[sno].tt_n_inuse == 0);
sewardj45f4e7c2005-09-27 19:20:21 +0000303
304 VG_(debugLog)(1,"transtab", "allocate sector %d\n", sno);
305
306 sres = VG_(am_mmap_anon_float_valgrind)( 8 * tc_sector_szQ );
307 if (sres.isError) {
308 VG_(out_of_memory_NORETURN)("initialiseSector(TC)",
309 8 * tc_sector_szQ );
310 /*NOTREACHED*/
311 }
312 sectors[sno].tc = (ULong*)sres.val;
313
314 sres = VG_(am_mmap_anon_float_valgrind)
315 ( N_TTES_PER_SECTOR * sizeof(TTEntry) );
316 if (sres.isError) {
317 VG_(out_of_memory_NORETURN)("initialiseSector(TT)",
318 N_TTES_PER_SECTOR * sizeof(TTEntry) );
319 /*NOTREACHED*/
320 }
321 sectors[sno].tt = (TTEntry*)sres.val;
322
sewardjfa8ec112005-01-19 11:55:34 +0000323 if (VG_(clo_verbosity) > 2)
324 VG_(message)(Vg_DebugMsg, "TT/TC: initialise sector %d", sno);
325 } else {
326 /* Sector has been used before. */
sewardj45f4e7c2005-09-27 19:20:21 +0000327 VG_(debugLog)(1,"transtab", "recycle sector %d\n", sno);
sewardjfa8ec112005-01-19 11:55:34 +0000328 vg_assert(sectors[sno].tt != NULL);
329 vg_assert(sectors[sno].tc_next != NULL);
330 n_dump_count += sectors[sno].tt_n_inuse;
331 for (i = 0; i < N_TTES_PER_SECTOR; i++) {
332 if (sectors[sno].tt[i].status == InUse) {
333 n_dump_osize += vge_osize(&sectors[sno].tt[i].vge);
334 }
335 }
336 if (VG_(clo_verbosity) > 2)
337 VG_(message)(Vg_DebugMsg, "TT/TC: recycle sector %d", sno);
338 }
339
340 sectors[sno].tc_next = sectors[sno].tc;
341 sectors[sno].tt_n_inuse = 0;
342 for (i = 0; i < N_TTES_PER_SECTOR; i++)
343 sectors[sno].tt[i].status = Empty;
344
345 invalidateFastCache();
sewardj6c3769f2002-11-29 01:02:45 +0000346}
347
sewardj10f08cf2005-06-29 10:16:14 +0000348static void invalidate_icache ( void *ptr, Int nbytes )
cerion85665ca2005-06-20 15:51:07 +0000349{
sewardj10f08cf2005-06-29 10:16:14 +0000350# if defined(VGA_ppc32)
351 Addr startaddr = (Addr) ptr;
352 Addr endaddr = startaddr + nbytes;
353 Addr cls = VG_(cache_line_size_ppc32);
354 Addr addr;
355
sewardj2bf6ba52005-06-30 12:11:19 +0000356 /* Stay sane .. */
357 vg_assert(cls == 32 || cls == 128);
cerion85665ca2005-06-20 15:51:07 +0000358
359 startaddr &= ~(cls - 1);
360 for (addr = startaddr; addr < endaddr; addr += cls)
361 asm volatile("dcbst 0,%0" : : "r" (addr));
362 asm volatile("sync");
363 for (addr = startaddr; addr < endaddr; addr += cls)
364 asm volatile("icbi 0,%0" : : "r" (addr));
365 asm volatile("sync; isync");
sewardj10f08cf2005-06-29 10:16:14 +0000366
367# elif defined(VGA_x86)
368 /* no need to do anything, hardware provides coherence */
369
370# elif defined(VGA_amd64)
371 /* no need to do anything, hardware provides coherence */
372
373# else
374# error "Unknown ARCH"
375# endif
cerion85665ca2005-06-20 15:51:07 +0000376}
cerion85665ca2005-06-20 15:51:07 +0000377
sewardj6c3769f2002-11-29 01:02:45 +0000378
sewardjfa8ec112005-01-19 11:55:34 +0000379/* Add a translation of vge to TT/TC. The translation is temporarily
380 in code[0 .. code_len-1].
381
382 pre: youngest_sector points to a valid (although possibly full)
383 sector.
384*/
njn8bddf582005-05-13 23:40:55 +0000385void VG_(add_to_transtab)( VexGuestExtents* vge,
386 Addr64 entry,
387 AddrH code,
sewardj26412bd2005-07-07 10:05:05 +0000388 UInt code_len,
389 Bool is_self_checking )
sewardj6c3769f2002-11-29 01:02:45 +0000390{
sewardjfa8ec112005-01-19 11:55:34 +0000391 Int tcAvailQ, reqdQ, y, i;
392 ULong *tce, *tce2;
393 UChar* srcP;
394 UChar* dstP;
395
sewardj663a1bd2005-04-24 11:22:44 +0000396 vg_assert(init_done);
sewardjfa8ec112005-01-19 11:55:34 +0000397 vg_assert(vge->n_used >= 1 && vge->n_used <= 3);
398 vg_assert(code_len > 0 && code_len < 20000);
399
400 if (0)
njn8bddf582005-05-13 23:40:55 +0000401 VG_(printf)("add_to_transtab(entry = 0x%llx, len = %d)\n",
sewardjfa8ec112005-01-19 11:55:34 +0000402 entry, code_len);
403
404 n_in_count++;
405 n_in_tsize += code_len;
406 n_in_osize += vge_osize(vge);
sewardj26412bd2005-07-07 10:05:05 +0000407 if (is_self_checking)
408 n_in_sc_count++;
sewardjfa8ec112005-01-19 11:55:34 +0000409
410 y = youngest_sector;
411 vg_assert(isValidSector(y));
412
413 if (sectors[y].tc == NULL)
414 initialiseSector(y);
415
416 /* Try putting the translation in this sector. */
417 reqdQ = 1 + ((code_len + 7) >> 3);
418
419 /* Will it fit in tc? */
420 tcAvailQ = ((ULong*)(&sectors[y].tc[tc_sector_szQ]))
421 - ((ULong*)(sectors[y].tc_next));
422 vg_assert(tcAvailQ >= 0);
423 vg_assert(tcAvailQ <= tc_sector_szQ);
424
425 if (tcAvailQ < reqdQ
426 || sectors[y].tt_n_inuse >= N_TTES_PER_SECTOR_USABLE) {
427 /* No. So move on to the next sector. Either it's never been
428 used before, in which case it will get its tt/tc allocated
429 now, or it has been used before, in which case it is set to be
430 empty, hence throwing out the oldest sector. */
sewardja16ea0a2005-09-30 10:34:06 +0000431 vg_assert(tc_sector_szQ > 0);
432 VG_(debugLog)(1,"transtab",
433 "declare sector %d full "
434 "(TT loading %2d%%, TC loading %2d%%)\n",
435 y,
436 (100 * sectors[y].tt_n_inuse)
437 / N_TTES_PER_SECTOR,
438 (100 * (tc_sector_szQ - tcAvailQ))
439 / tc_sector_szQ);
sewardjfa8ec112005-01-19 11:55:34 +0000440 youngest_sector++;
441 if (youngest_sector >= N_SECTORS)
442 youngest_sector = 0;
443 y = youngest_sector;
444 initialiseSector(y);
445 }
446
447 /* Be sure ... */
448 tcAvailQ = ((ULong*)(&sectors[y].tc[tc_sector_szQ]))
449 - ((ULong*)(sectors[y].tc_next));
450 vg_assert(tcAvailQ >= 0);
451 vg_assert(tcAvailQ <= tc_sector_szQ);
452 vg_assert(tcAvailQ >= reqdQ);
453 vg_assert(sectors[y].tt_n_inuse < N_TTES_PER_SECTOR_USABLE);
454 vg_assert(sectors[y].tt_n_inuse >= 0);
455
456 /* Copy into tc. */
457 tce = sectors[y].tc_next;
458 vg_assert(tce >= &sectors[y].tc[0]);
459 vg_assert(tce <= &sectors[y].tc[tc_sector_szQ]);
460
461 tce[0] = entry;
462 dstP = (UChar*)(&tce[1]);
463 srcP = (UChar*)code;
464 for (i = 0; i < code_len; i++)
465 dstP[i] = srcP[i];
466 sectors[y].tc_next += reqdQ;
467 sectors[y].tt_n_inuse++;
468
cerion85665ca2005-06-20 15:51:07 +0000469 invalidate_icache( dstP, code_len );
cerion85665ca2005-06-20 15:51:07 +0000470
sewardjfa8ec112005-01-19 11:55:34 +0000471 /* more paranoia */
472 tce2 = sectors[y].tc_next;
473 vg_assert(tce2 >= &sectors[y].tc[0]);
474 vg_assert(tce2 <= &sectors[y].tc[tc_sector_szQ]);
475
476 /* Find an empty tt slot, and use it. There must be such a slot
477 since tt is never allowed to get completely full. */
478 i = HASH_TT(entry);
479 vg_assert(i >= 0 && i < N_TTES_PER_SECTOR);
sewardj6c3769f2002-11-29 01:02:45 +0000480 while (True) {
sewardjfa8ec112005-01-19 11:55:34 +0000481 if (sectors[y].tt[i].status == Empty
482 || sectors[y].tt[i].status == Deleted)
sewardj6c3769f2002-11-29 01:02:45 +0000483 break;
484 i++;
sewardjfa8ec112005-01-19 11:55:34 +0000485 if (i >= N_TTES_PER_SECTOR)
sewardj6c3769f2002-11-29 01:02:45 +0000486 i = 0;
487 }
sewardj22854b92002-11-30 14:00:47 +0000488
sewardjfa8ec112005-01-19 11:55:34 +0000489 sectors[y].tt[i].status = InUse;
490 sectors[y].tt[i].tce = tce;
491 sectors[y].tt[i].count = 0;
492 sectors[y].tt[i].weight = 1;
493 sectors[y].tt[i].vge = *vge;
494 sectors[y].tt[i].entry = entry;
495
496 setFastCacheEntry( entry, tce, &sectors[y].tt[i].count );
sewardj6c3769f2002-11-29 01:02:45 +0000497}
498
499
sewardjfa8ec112005-01-19 11:55:34 +0000500/* Search for the translation of the given guest address. If
501 requested, a successful search can also cause the fast-caches to be
502 updated.
sewardj6c3769f2002-11-29 01:02:45 +0000503*/
sewardjfa8ec112005-01-19 11:55:34 +0000504Bool VG_(search_transtab) ( /*OUT*/AddrH* result,
505 Addr64 guest_addr,
506 Bool upd_cache )
sewardj6c3769f2002-11-29 01:02:45 +0000507{
sewardjfa8ec112005-01-19 11:55:34 +0000508 Int i, j, k, kstart, sno;
sewardj663a1bd2005-04-24 11:22:44 +0000509
510 vg_assert(init_done);
sewardjfa8ec112005-01-19 11:55:34 +0000511 /* Find the initial probe point just once. It will be the same in
512 all sectors and avoids multiple expensive % operations. */
513 n_full_lookups++;
514 k = -1;
515 kstart = HASH_TT(guest_addr);
516 vg_assert(kstart >= 0 && kstart < N_TTES_PER_SECTOR);
sewardj6c3769f2002-11-29 01:02:45 +0000517
sewardjfa8ec112005-01-19 11:55:34 +0000518 /* Search in all the sectors. Although the order should not matter,
519 it might be most efficient to search in the order youngest to
520 oldest. */
521 sno = youngest_sector;
522 for (i = 0; i < N_SECTORS; i++) {
sewardj6c3769f2002-11-29 01:02:45 +0000523
sewardjfa8ec112005-01-19 11:55:34 +0000524 if (sectors[sno].tc == NULL)
525 goto notfound; /* sector not in use. */
sewardj6c3769f2002-11-29 01:02:45 +0000526
sewardjfa8ec112005-01-19 11:55:34 +0000527 k = kstart;
528 for (j = 0; j < N_TTES_PER_SECTOR; j++) {
529 n_lookup_probes++;
530 if (sectors[sno].tt[k].status == InUse
531 && sectors[sno].tt[k].entry == guest_addr) {
532 /* found it */
533 if (upd_cache)
534 setFastCacheEntry(
535 guest_addr, sectors[sno].tt[k].tce,
536 &sectors[sno].tt[k].count );
537 if (result)
538 *result = sizeof(Addr64) + (AddrH)sectors[sno].tt[k].tce;
539 return True;
540 }
541 if (sectors[sno].tt[k].status == Empty)
542 break; /* not found in this sector */
543 k++;
544 if (k == N_TTES_PER_SECTOR)
545 k = 0;
sewardj6c3769f2002-11-29 01:02:45 +0000546 }
sewardjfa8ec112005-01-19 11:55:34 +0000547
548 /* If we fall off the end, all entries are InUse and not
549 matching, or Deleted. In any case we did not find it in this
550 sector. */
551
552 notfound:
553 /* move to the next oldest sector */
554 sno = sno==0 ? (N_SECTORS-1) : (sno-1);
sewardj6c3769f2002-11-29 01:02:45 +0000555 }
sewardjfa8ec112005-01-19 11:55:34 +0000556
557 /* Not found in any sector. */
558 return False;
sewardj6c3769f2002-11-29 01:02:45 +0000559}
560
561
sewardjfa8ec112005-01-19 11:55:34 +0000562/* Delete all translations which intersect with any part of the
563 specified guest address range. Note, this is SLOW.
sewardjde4a1d02002-03-22 01:27:54 +0000564*/
sewardjfa8ec112005-01-19 11:55:34 +0000565
566static inline
sewardja3054502005-07-26 23:04:25 +0000567Bool overlap1 ( Addr64 s1, ULong r1, Addr64 s2, ULong r2 )
sewardjfa8ec112005-01-19 11:55:34 +0000568{
sewardja3054502005-07-26 23:04:25 +0000569 Addr64 e1 = s1 + r1 - 1ULL;
570 Addr64 e2 = s2 + r2 - 1ULL;
sewardjfa8ec112005-01-19 11:55:34 +0000571 if (e1 < s2 || e2 < s1)
572 return False;
573 return True;
574}
575
576static inline
sewardja3054502005-07-26 23:04:25 +0000577Bool overlaps ( Addr64 start, ULong range, VexGuestExtents* vge )
sewardjfa8ec112005-01-19 11:55:34 +0000578{
579 if (overlap1(start, range, vge->base[0], (UInt)vge->len[0]))
580 return True;
581 if (vge->n_used < 2)
582 return False;
583 if (overlap1(start, range, vge->base[1], (UInt)vge->len[1]))
584 return True;
585 if (vge->n_used < 3)
586 return False;
587 if (overlap1(start, range, vge->base[2], (UInt)vge->len[2]))
588 return True;
589 return False;
590}
591
592
sewardj45f4e7c2005-09-27 19:20:21 +0000593void VG_(discard_translations) ( Addr64 guest_start, ULong range,
594 HChar* who )
sewardjfa8ec112005-01-19 11:55:34 +0000595{
596 Int sno, i;
597 Bool anyDeleted = False;
598
sewardj663a1bd2005-04-24 11:22:44 +0000599 vg_assert(init_done);
600
sewardja16ea0a2005-09-30 10:34:06 +0000601 VG_(debugLog)(2, "transtab",
sewardj45f4e7c2005-09-27 19:20:21 +0000602 "discard_translations(0x%llx, %lld) req by %s\n",
603 guest_start, range, who );
604
sewardjfa8ec112005-01-19 11:55:34 +0000605 for (sno = 0; sno < N_SECTORS; sno++) {
606 if (sectors[sno].tc == NULL)
607 continue;
608 for (i = 0; i < N_TTES_PER_SECTOR; i++) {
609 if (sectors[sno].tt[i].status == InUse
610 && overlaps( guest_start, range, &sectors[sno].tt[i].vge )) {
611 sectors[sno].tt[i].status = Deleted;
612 sectors[sno].tt_n_inuse--;
613 anyDeleted = True;
614 n_disc_count++;
615 n_disc_osize += vge_osize(&sectors[sno].tt[i].vge);
616 }
617 }
618 }
619
620 if (anyDeleted)
621 invalidateFastCache();
622}
623
624
625/*------------------------------------------------------------*/
sewardjde4a1d02002-03-22 01:27:54 +0000626/*--- Initialisation. ---*/
627/*------------------------------------------------------------*/
628
sewardjc0d8f682002-11-30 00:49:43 +0000629void VG_(init_tt_tc) ( void )
630{
sewardjfa8ec112005-01-19 11:55:34 +0000631 Int i, avg_codeszQ;
sewardjc0d8f682002-11-30 00:49:43 +0000632
sewardj663a1bd2005-04-24 11:22:44 +0000633 vg_assert(!init_done);
634 init_done = True;
635
sewardj4ccf7072004-11-28 16:58:05 +0000636 /* Otherwise lots of things go wrong... */
sewardjfa8ec112005-01-19 11:55:34 +0000637 vg_assert(sizeof(ULong) == 8);
638 vg_assert(sizeof(Addr64) == 8);
639
640 if (VG_(clo_verbosity) > 2)
641 VG_(message)(Vg_DebugMsg,
642 "TT/TC: VG_(init_tt_tc) "
643 "(startup of code management)");
644
645 /* Figure out how big each tc area should be. */
njn43b9a8a2005-05-10 04:37:01 +0000646 avg_codeszQ = (VG_(details).avg_translation_sizeB + 7) / 8;
647 tc_sector_szQ = N_TTES_PER_SECTOR_USABLE * (1 + avg_codeszQ);
sewardjfa8ec112005-01-19 11:55:34 +0000648
sewardjc0d8f682002-11-30 00:49:43 +0000649 /* Ensure the calculated value is not way crazy. */
sewardjfa8ec112005-01-19 11:55:34 +0000650 vg_assert(tc_sector_szQ >= 2 * N_TTES_PER_SECTOR_USABLE);
651 vg_assert(tc_sector_szQ <= 50 * N_TTES_PER_SECTOR_USABLE);
sewardjc0d8f682002-11-30 00:49:43 +0000652
sewardjfa8ec112005-01-19 11:55:34 +0000653 /* Initialise the sectors */
654 youngest_sector = 0;
655 for (i = 0; i < N_SECTORS; i++) {
656 sectors[i].tc = NULL;
657 sectors[i].tt = NULL;
658 sectors[i].tc_next = NULL;
659 sectors[i].tt_n_inuse = 0;
sewardjc0d8f682002-11-30 00:49:43 +0000660 }
sewardjc0d8f682002-11-30 00:49:43 +0000661
sewardjfa8ec112005-01-19 11:55:34 +0000662 /* and the fast caches. */
663 invalidateFastCache();
sewardjc0d8f682002-11-30 00:49:43 +0000664
665 if (VG_(clo_verbosity) > 2) {
666 VG_(message)(Vg_DebugMsg,
sewardjfa8ec112005-01-19 11:55:34 +0000667 "TT/TC: cache: %d sectors of %d bytes each = %d total",
668 N_SECTORS, 8 * tc_sector_szQ,
669 N_SECTORS * 8 * tc_sector_szQ );
sewardjc0d8f682002-11-30 00:49:43 +0000670 VG_(message)(Vg_DebugMsg,
sewardjfa8ec112005-01-19 11:55:34 +0000671 "TT/TC: table: %d total entries, max occupancy %d (%d%%)",
672 N_SECTORS * N_TTES_PER_SECTOR,
673 N_SECTORS * N_TTES_PER_SECTOR_USABLE,
674 SECTOR_TT_LIMIT_PERCENT );
675 }
sewardj45f4e7c2005-09-27 19:20:21 +0000676
677 VG_(debugLog)(2, "transtab",
678 "cache: %d sectors of %d bytes each = %d total\n",
679 N_SECTORS, 8 * tc_sector_szQ,
680 N_SECTORS * 8 * tc_sector_szQ );
681 VG_(debugLog)(2, "transtab",
682 "table: %d total entries, max occupancy %d (%d%%)\n",
683 N_SECTORS * N_TTES_PER_SECTOR,
684 N_SECTORS * N_TTES_PER_SECTOR_USABLE,
685 SECTOR_TT_LIMIT_PERCENT );
sewardjfa8ec112005-01-19 11:55:34 +0000686}
687
688
689/*------------------------------------------------------------*/
690/*--- Printing out statistics. ---*/
691/*------------------------------------------------------------*/
692
693static ULong safe_idiv( ULong a, ULong b )
694{
695 return (b == 0 ? 0 : a / b);
696}
697
698UInt VG_(get_bbs_translated) ( void )
699{
700 return n_in_count;
701}
702
703void VG_(print_tt_tc_stats) ( void )
704{
705 VG_(message)(Vg_DebugMsg,
njn0fd92f42005-10-06 03:32:42 +0000706 " tt/tc: %,llu tt lookups requiring %,llu probes",
sewardjfa8ec112005-01-19 11:55:34 +0000707 n_full_lookups, n_lookup_probes );
708 VG_(message)(Vg_DebugMsg,
njn0fd92f42005-10-06 03:32:42 +0000709 " tt/tc: %,llu fast-cache updates, %,llu flushes",
sewardjfa8ec112005-01-19 11:55:34 +0000710 n_fast_updates, n_fast_flushes );
711
712 VG_(message)(Vg_DebugMsg,
njn0fd92f42005-10-06 03:32:42 +0000713 "translate: new %,lld "
714 "(%,llu -> %,llu; ratio %,llu:10) [%,llu scs]",
sewardjfa8ec112005-01-19 11:55:34 +0000715 n_in_count, n_in_osize, n_in_tsize,
sewardj26412bd2005-07-07 10:05:05 +0000716 safe_idiv(10*n_in_tsize, n_in_osize),
717 n_in_sc_count);
sewardjfa8ec112005-01-19 11:55:34 +0000718 VG_(message)(Vg_DebugMsg,
njn0fd92f42005-10-06 03:32:42 +0000719 "translate: dumped %,llu (%,llu -> ?" "?)",
sewardjfa8ec112005-01-19 11:55:34 +0000720 n_dump_count, n_dump_osize );
721 VG_(message)(Vg_DebugMsg,
njn0fd92f42005-10-06 03:32:42 +0000722 "translate: discarded %,llu (%,llu -> ?" "?)",
sewardjfa8ec112005-01-19 11:55:34 +0000723 n_disc_count, n_disc_osize );
724}
725
726/*------------------------------------------------------------*/
727/*--- Printing out of profiling results. ---*/
728/*------------------------------------------------------------*/
729
sewardjfa8ec112005-01-19 11:55:34 +0000730static ULong score ( TTEntry* tte )
731{
732 return ((ULong)tte->weight) * ((ULong)tte->count);
733}
734
njn2025cf92005-06-26 20:44:48 +0000735ULong VG_(get_BB_profile) ( BBProfEntry tops[], UInt n_tops )
sewardjfa8ec112005-01-19 11:55:34 +0000736{
sewardjfa8ec112005-01-19 11:55:34 +0000737 Int sno, i, r, s;
njn2025cf92005-06-26 20:44:48 +0000738 ULong score_total;
sewardjfa8ec112005-01-19 11:55:34 +0000739
740 /* First, compute the total weighted count, and find the top N
njn2025cf92005-06-26 20:44:48 +0000741 ttes. tops contains pointers to the most-used n_tops blocks, in
sewardjfa8ec112005-01-19 11:55:34 +0000742 descending order (viz, tops[0] is the highest scorer). */
njn2025cf92005-06-26 20:44:48 +0000743 for (i = 0; i < n_tops; i++) {
744 tops[i].addr = 0;
745 tops[i].score = 0;
746 }
sewardjfa8ec112005-01-19 11:55:34 +0000747
748 score_total = 0;
749
750 for (sno = 0; sno < N_SECTORS; sno++) {
751 if (sectors[sno].tc == NULL)
752 continue;
753 for (i = 0; i < N_TTES_PER_SECTOR; i++) {
754 if (sectors[sno].tt[i].status != InUse)
755 continue;
756 score_total += score(&sectors[sno].tt[i]);
757 /* Find the rank for sectors[sno].tt[i]. */
njn2025cf92005-06-26 20:44:48 +0000758 r = n_tops-1;
sewardjfa8ec112005-01-19 11:55:34 +0000759 while (True) {
760 if (r == -1)
761 break;
njn2025cf92005-06-26 20:44:48 +0000762 if (tops[r].addr == 0) {
sewardjfa8ec112005-01-19 11:55:34 +0000763 r--;
764 continue;
765 }
njn2025cf92005-06-26 20:44:48 +0000766 if ( score(&sectors[sno].tt[i]) > tops[r].score ) {
sewardjfa8ec112005-01-19 11:55:34 +0000767 r--;
768 continue;
769 }
770 break;
771 }
772 r++;
njn2025cf92005-06-26 20:44:48 +0000773 vg_assert(r >= 0 && r <= n_tops);
sewardjfa8ec112005-01-19 11:55:34 +0000774 /* This bb should be placed at r, and bbs above it shifted
775 upwards one slot. */
njn2025cf92005-06-26 20:44:48 +0000776 if (r < n_tops) {
777 for (s = n_tops-1; s > r; s--)
sewardjfa8ec112005-01-19 11:55:34 +0000778 tops[s] = tops[s-1];
njn2025cf92005-06-26 20:44:48 +0000779 tops[r].addr = sectors[sno].tt[i].entry;
780 tops[r].score = score( &sectors[sno].tt[i] );
sewardjfa8ec112005-01-19 11:55:34 +0000781 }
782 }
sewardjc0d8f682002-11-30 00:49:43 +0000783 }
784
njn2025cf92005-06-26 20:44:48 +0000785 return score_total;
sewardjc0d8f682002-11-30 00:49:43 +0000786}
787
sewardjde4a1d02002-03-22 01:27:54 +0000788/*--------------------------------------------------------------------*/
njn8bddf582005-05-13 23:40:55 +0000789/*--- end ---*/
sewardjde4a1d02002-03-22 01:27:54 +0000790/*--------------------------------------------------------------------*/