blob: f83ea28bcf971a4f66f688a18541a30b8b8cf59e [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"
sewardj10f08cf2005-06-29 10:16:14 +000033#include "pub_core_machine.h" // ppc32: VG_(cache_line_size_ppc32)
njn97405b22005-06-02 03:39:33 +000034#include "pub_core_libcbase.h"
njn132bfcc2005-06-04 19:16:06 +000035#include "pub_core_libcassert.h"
sewardj10f08cf2005-06-29 10:16:14 +000036#include "pub_core_libcmman.h" // For VG_(get_memory_from_mmap)()
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"
sewardjde4a1d02002-03-22 01:27:54 +000041
sewardj18d75132002-05-16 11:06:21 +000042/* #define DEBUG_TRANSTAB */
43
sewardjde4a1d02002-03-22 01:27:54 +000044
sewardj6c3769f2002-11-29 01:02:45 +000045/*-------------------------------------------------------------*/
46/*--- Management of the FIFO-based translation table+cache. ---*/
47/*-------------------------------------------------------------*/
sewardjde4a1d02002-03-22 01:27:54 +000048
sewardj6c3769f2002-11-29 01:02:45 +000049/*------------------ CONSTANTS ------------------*/
sewardjde4a1d02002-03-22 01:27:54 +000050
sewardjfa8ec112005-01-19 11:55:34 +000051/* Number of sectors the TC is divided into. If you need a larger
52 overall translation cache, increase this value. */
53#define N_SECTORS 8
sewardjde4a1d02002-03-22 01:27:54 +000054
sewardjfa8ec112005-01-19 11:55:34 +000055/* Number of TC entries in each sector. This needs to be a prime
56 number to work properly, and it is strongly recommended not to
57 change this. */
58#define N_TTES_PER_SECTOR /*30011*/ 40009
sewardjde4a1d02002-03-22 01:27:54 +000059
sewardjfa8ec112005-01-19 11:55:34 +000060/* Because each sector contains a hash table of TTEntries, we need to
61 specify the maximum allowable loading, after which the sector is
62 deemed full. */
63#define SECTOR_TT_LIMIT_PERCENT 60
sewardjde4a1d02002-03-22 01:27:54 +000064
sewardjfa8ec112005-01-19 11:55:34 +000065/* The sector is deemed full when this many entries are in it. */
66#define N_TTES_PER_SECTOR_USABLE \
67 ((N_TTES_PER_SECTOR * SECTOR_TT_LIMIT_PERCENT) / 100)
sewardjde4a1d02002-03-22 01:27:54 +000068
sewardjde4a1d02002-03-22 01:27:54 +000069
sewardj6c3769f2002-11-29 01:02:45 +000070/*------------------ TYPES ------------------*/
71
sewardjfa8ec112005-01-19 11:55:34 +000072/* A translation-cache entry is two parts:
73 - The guest address of the first (entry) bb in the translation,
74 as a 64-bit word.
75 - One or more 64-bit words containing the code.
76 It is supposed to be 64-bit aligned.
77*/
78/*
sewardj6c3769f2002-11-29 01:02:45 +000079typedef
80 struct {
sewardjfa8ec112005-01-19 11:55:34 +000081 Addr64 orig_addr;
82 ULong code[0];
83 }
84 TCEntry;
85*/
86
87/* A translation-table entry. This indicates precisely which areas of
88 guest code are included in the translation, and contains all other
89 auxiliary info too. */
90typedef
91 struct {
92 /* Profiling only: the count and weight (arbitrary meaning) for
93 this translation. Weight is a property of the translation
94 itself and computed once when the translation is created.
95 Count is an entry count for the translation and is
96 incremented by 1 every time the translation is used, if we
97 are profiling. */
98 UInt count;
99 UShort weight;
100
101 /* Status of the slot. Note, we need to be able to do lazy
102 deletion, hence the Deleted state. */
103 enum { InUse, Deleted, Empty } status;
104
105 /* Pointer to the corresponding TCEntry (must be in the same
106 sector!) */
107 ULong* tce;
108
109 /* This is the original guest address that purportedly is the
110 entry point of the translation. You might think that .entry
111 should be the same as .vge->base[0], and most of the time it
112 is. However, when doing redirections, that is not the case.
113 .vge must always correctly describe the guest code sections
114 from which this translation was made. However, .entry may or
115 may not be a lie, depending on whether or not we're doing
116 redirection. */
117 Addr64 entry;
118
119 /* This structure describes precisely what ranges of guest code
120 the translation covers, so we can decide whether or not to
121 delete it when translations of a given address range are
122 invalidated. */
123 VexGuestExtents vge;
sewardj6c3769f2002-11-29 01:02:45 +0000124 }
125 TTEntry;
126
sewardj4ccf7072004-11-28 16:58:05 +0000127
sewardjfa8ec112005-01-19 11:55:34 +0000128/* Finally, a sector itself. Each sector contains an array of
129 TCEntries, which hold code, and an array of TTEntries, containing
130 all required administrative info. Profiling is supported using the
131 TTEntry .count and .weight fields, if required. Each sector is
132 independent in that no cross-sector references are allowed.
sewardj4ccf7072004-11-28 16:58:05 +0000133
sewardjfa8ec112005-01-19 11:55:34 +0000134 If the sector is not in use, all three pointers are NULL and
135 tt_n_inuse is zero.
136*/
137typedef
138 struct {
139 /* The TCEntry area. Size of this depends on the average
140 translation size. We try and size it so it becomes full
141 precisely when this sector's translation table (tt) reaches
142 its load limit (SECTOR_TT_LIMIT_PERCENT). */
143 ULong* tc;
sewardj4ccf7072004-11-28 16:58:05 +0000144
sewardjfa8ec112005-01-19 11:55:34 +0000145 /* The TTEntry array. This is a fixed size, always containing
146 exactly N_TTES_PER_SECTOR entries. */
147 TTEntry* tt;
sewardj4ccf7072004-11-28 16:58:05 +0000148
sewardjfa8ec112005-01-19 11:55:34 +0000149 /* This points to the current allocation point in tc. */
150 ULong* tc_next;
sewardj6c3769f2002-11-29 01:02:45 +0000151
sewardjfa8ec112005-01-19 11:55:34 +0000152 /* The count of tt entries with state InUse. */
153 Int tt_n_inuse;
154 }
155 Sector;
sewardjde4a1d02002-03-22 01:27:54 +0000156
sewardjde4a1d02002-03-22 01:27:54 +0000157
sewardj6c3769f2002-11-29 01:02:45 +0000158/*------------------ DECLS ------------------*/
159
sewardjfa8ec112005-01-19 11:55:34 +0000160/* The root data structure is an array of sectors. The index of the
161 youngest sector is recorded, and new translations are put into that
162 sector. When it fills up, we move along to the next sector and
163 start to fill that up, wrapping around at the end of the array.
164 That way, once all N_TC_SECTORS have been bought into use for the
165 first time, and are full, we then re-use the oldest sector,
166 endlessly.
sewardj6c3769f2002-11-29 01:02:45 +0000167
sewardjfa8ec112005-01-19 11:55:34 +0000168 When running, youngest sector should be between >= 0 and <
169 N_TC_SECTORS. The initial -1 value indicates the TT/TC system is
170 not yet initialised.
171*/
172static Sector sectors[N_SECTORS];
173static Int youngest_sector = -1;
sewardj6c3769f2002-11-29 01:02:45 +0000174
sewardjfa8ec112005-01-19 11:55:34 +0000175/* The number of ULongs in each TCEntry area. This is computed once
176 at startup and does not change. */
177static Int tc_sector_szQ;
nethercote92e7b7f2004-08-07 17:52:25 +0000178
179
sewardjfa8ec112005-01-19 11:55:34 +0000180/* Fast helper for the TC. A direct-mapped cache which holds a
sewardjc54ee502004-11-29 19:46:47 +0000181 pointer to a TC entry which may or may not be the correct one, but
sewardjde4a1d02002-03-22 01:27:54 +0000182 which we hope usually is. This array is referred to directly from
sewardjfa8ec112005-01-19 11:55:34 +0000183 <arch>/dispatch.S.
sewardj8aef1192002-07-24 09:36:36 +0000184
sewardjfa8ec112005-01-19 11:55:34 +0000185 Entries in tt_fast may point to any valid TC entry, regardless of
186 which sector it's in. Consequently we must be very careful to
187 invalidate this cache when TC entries are changed or disappear.
188
189 A special TCEntry -- bogus_tc_entry -- must be pointed at to cause
190 that cache entry to miss. This relies on the assumption that no
191 guest code actually has an address of 0x1.
192*/
193/*global*/ ULong* VG_(tt_fast)[VG_TT_FAST_SIZE];
194
195static ULong bogus_tc_entry = (Addr64)1;
sewardj22854b92002-11-30 14:00:47 +0000196
197
sewardjfa8ec112005-01-19 11:55:34 +0000198/* For profiling, we have a parallel array of pointers to .count
199 fields in TT entries. Again, these pointers must be invalidated
200 when translations disappear. A NULL pointer suffices to indicate
201 an unused slot.
sewardj6c3769f2002-11-29 01:02:45 +0000202
sewardjfa8ec112005-01-19 11:55:34 +0000203 tt_fast and tt_fastN change together: if tt_fast[i] points to
204 bogus_tc_entry then the corresponding tt_fastN[i] must be null. If
205 tt_fast[i] points to some TC entry somewhere, then tt_fastN[i]
206 *must* point to the .count field of the corresponding TT entry.
207
208 tt_fast and tt_fastN are referred to from assembly code
209 (dispatch.S).
210*/
211/*global*/ UInt* VG_(tt_fastN)[VG_TT_FAST_SIZE];
212
213
sewardj663a1bd2005-04-24 11:22:44 +0000214/* Make sure we're not used before initialisation. */
215static Bool init_done = False;
216
217
sewardjfa8ec112005-01-19 11:55:34 +0000218/*------------------ STATS DECLS ------------------*/
219
220/* Number of fast-cache updates and flushes done. */
221ULong n_fast_flushes = 0;
222ULong n_fast_updates = 0;
223
224/* Number of full lookups done. */
225ULong n_full_lookups = 0;
226ULong n_lookup_probes = 0;
227
sewardj26412bd2005-07-07 10:05:05 +0000228/* Number/osize/tsize of translations entered; also the number of
229 those for which self-checking was requested. */
230ULong n_in_count = 0;
231ULong n_in_osize = 0;
232ULong n_in_tsize = 0;
233ULong n_in_sc_count = 0;
sewardjfa8ec112005-01-19 11:55:34 +0000234
235/* Number/osize of translations discarded due to lack of space. */
236ULong n_dump_count = 0;
237ULong n_dump_osize = 0;
238
239/* Number/osize of translations discarded due to requests to do so. */
240ULong n_disc_count = 0;
241ULong n_disc_osize = 0;
242
243
244
245/*-------------------------------------------------------------*/
246/*--- Add/delete/find translations ---*/
247/*-------------------------------------------------------------*/
248
249static UInt vge_osize ( VexGuestExtents* vge )
sewardjc0d8f682002-11-30 00:49:43 +0000250{
sewardjfa8ec112005-01-19 11:55:34 +0000251 UInt i, n = 0;
252 for (i = 0; i < vge->n_used; i++)
253 n += (UInt)vge->len[i];
254 return n;
sewardjc0d8f682002-11-30 00:49:43 +0000255}
256
sewardjfa8ec112005-01-19 11:55:34 +0000257static Bool isValidSector ( Int sector )
sewardj6c3769f2002-11-29 01:02:45 +0000258{
sewardjfa8ec112005-01-19 11:55:34 +0000259 if (sector < 0 || sector >= N_SECTORS)
260 return False;
261 return True;
262}
263
264static inline UInt HASH_TT ( Addr64 key )
265{
266 UInt kHi = (UInt)(key >> 32);
267 UInt kLo = (UInt)key;
268 return (kHi ^ kLo) % N_TTES_PER_SECTOR;
269}
270
271static void setFastCacheEntry ( Addr64 key, ULong* tce, UInt* count )
272{
273 UInt cno = ((UInt)key) & VG_TT_FAST_MASK;
274 VG_(tt_fast)[cno] = tce;
275 VG_(tt_fastN)[cno] = count;
276 n_fast_updates++;
277}
278
279static void invalidateFastCache ( void )
280{
281 UInt j;
282 for (j = 0; j < VG_TT_FAST_SIZE; j++) {
283 VG_(tt_fast)[j] = &bogus_tc_entry;
284 VG_(tt_fastN)[j] = NULL;
285 }
286 n_fast_flushes++;
287}
288
289static void initialiseSector ( Int sno )
290{
291 Int i;
292 vg_assert(isValidSector(sno));
293
294 if (sectors[sno].tc == NULL) {
295 /* Sector has never been used before. Need to allocate tt and
296 tc. */
297 vg_assert(sectors[sno].tt == NULL);
298 vg_assert(sectors[sno].tc_next == NULL);
299 vg_assert(sectors[sno].tt_n_inuse == 0);
300 sectors[sno].tc
301 = VG_(get_memory_from_mmap)
302 ( 8 * tc_sector_szQ, "sectors[sno].tc" );
303 sectors[sno].tt
304 = VG_(get_memory_from_mmap)
305 ( N_TTES_PER_SECTOR * sizeof(TTEntry), "sectors[sno].tt" );
306 if (VG_(clo_verbosity) > 2)
307 VG_(message)(Vg_DebugMsg, "TT/TC: initialise sector %d", sno);
308 } else {
309 /* Sector has been used before. */
310 vg_assert(sectors[sno].tt != NULL);
311 vg_assert(sectors[sno].tc_next != NULL);
312 n_dump_count += sectors[sno].tt_n_inuse;
313 for (i = 0; i < N_TTES_PER_SECTOR; i++) {
314 if (sectors[sno].tt[i].status == InUse) {
315 n_dump_osize += vge_osize(&sectors[sno].tt[i].vge);
316 }
317 }
318 if (VG_(clo_verbosity) > 2)
319 VG_(message)(Vg_DebugMsg, "TT/TC: recycle sector %d", sno);
320 }
321
322 sectors[sno].tc_next = sectors[sno].tc;
323 sectors[sno].tt_n_inuse = 0;
324 for (i = 0; i < N_TTES_PER_SECTOR; i++)
325 sectors[sno].tt[i].status = Empty;
326
327 invalidateFastCache();
sewardj6c3769f2002-11-29 01:02:45 +0000328}
329
sewardj10f08cf2005-06-29 10:16:14 +0000330static void invalidate_icache ( void *ptr, Int nbytes )
cerion85665ca2005-06-20 15:51:07 +0000331{
sewardj10f08cf2005-06-29 10:16:14 +0000332# if defined(VGA_ppc32)
333 Addr startaddr = (Addr) ptr;
334 Addr endaddr = startaddr + nbytes;
335 Addr cls = VG_(cache_line_size_ppc32);
336 Addr addr;
337
sewardj2bf6ba52005-06-30 12:11:19 +0000338 /* Stay sane .. */
339 vg_assert(cls == 32 || cls == 128);
cerion85665ca2005-06-20 15:51:07 +0000340
341 startaddr &= ~(cls - 1);
342 for (addr = startaddr; addr < endaddr; addr += cls)
343 asm volatile("dcbst 0,%0" : : "r" (addr));
344 asm volatile("sync");
345 for (addr = startaddr; addr < endaddr; addr += cls)
346 asm volatile("icbi 0,%0" : : "r" (addr));
347 asm volatile("sync; isync");
sewardj10f08cf2005-06-29 10:16:14 +0000348
349# elif defined(VGA_x86)
350 /* no need to do anything, hardware provides coherence */
351
352# elif defined(VGA_amd64)
353 /* no need to do anything, hardware provides coherence */
354
355# else
356# error "Unknown ARCH"
357# endif
cerion85665ca2005-06-20 15:51:07 +0000358}
cerion85665ca2005-06-20 15:51:07 +0000359
sewardj6c3769f2002-11-29 01:02:45 +0000360
sewardjfa8ec112005-01-19 11:55:34 +0000361/* Add a translation of vge to TT/TC. The translation is temporarily
362 in code[0 .. code_len-1].
363
364 pre: youngest_sector points to a valid (although possibly full)
365 sector.
366*/
njn8bddf582005-05-13 23:40:55 +0000367void VG_(add_to_transtab)( VexGuestExtents* vge,
368 Addr64 entry,
369 AddrH code,
sewardj26412bd2005-07-07 10:05:05 +0000370 UInt code_len,
371 Bool is_self_checking )
sewardj6c3769f2002-11-29 01:02:45 +0000372{
sewardjfa8ec112005-01-19 11:55:34 +0000373 Int tcAvailQ, reqdQ, y, i;
374 ULong *tce, *tce2;
375 UChar* srcP;
376 UChar* dstP;
377
sewardj663a1bd2005-04-24 11:22:44 +0000378 vg_assert(init_done);
sewardjfa8ec112005-01-19 11:55:34 +0000379 vg_assert(vge->n_used >= 1 && vge->n_used <= 3);
380 vg_assert(code_len > 0 && code_len < 20000);
381
382 if (0)
njn8bddf582005-05-13 23:40:55 +0000383 VG_(printf)("add_to_transtab(entry = 0x%llx, len = %d)\n",
sewardjfa8ec112005-01-19 11:55:34 +0000384 entry, code_len);
385
386 n_in_count++;
387 n_in_tsize += code_len;
388 n_in_osize += vge_osize(vge);
sewardj26412bd2005-07-07 10:05:05 +0000389 if (is_self_checking)
390 n_in_sc_count++;
sewardjfa8ec112005-01-19 11:55:34 +0000391
392 y = youngest_sector;
393 vg_assert(isValidSector(y));
394
395 if (sectors[y].tc == NULL)
396 initialiseSector(y);
397
398 /* Try putting the translation in this sector. */
399 reqdQ = 1 + ((code_len + 7) >> 3);
400
401 /* Will it fit in tc? */
402 tcAvailQ = ((ULong*)(&sectors[y].tc[tc_sector_szQ]))
403 - ((ULong*)(sectors[y].tc_next));
404 vg_assert(tcAvailQ >= 0);
405 vg_assert(tcAvailQ <= tc_sector_szQ);
406
407 if (tcAvailQ < reqdQ
408 || sectors[y].tt_n_inuse >= N_TTES_PER_SECTOR_USABLE) {
409 /* No. So move on to the next sector. Either it's never been
410 used before, in which case it will get its tt/tc allocated
411 now, or it has been used before, in which case it is set to be
412 empty, hence throwing out the oldest sector. */
413 youngest_sector++;
414 if (youngest_sector >= N_SECTORS)
415 youngest_sector = 0;
416 y = youngest_sector;
417 initialiseSector(y);
418 }
419
420 /* Be sure ... */
421 tcAvailQ = ((ULong*)(&sectors[y].tc[tc_sector_szQ]))
422 - ((ULong*)(sectors[y].tc_next));
423 vg_assert(tcAvailQ >= 0);
424 vg_assert(tcAvailQ <= tc_sector_szQ);
425 vg_assert(tcAvailQ >= reqdQ);
426 vg_assert(sectors[y].tt_n_inuse < N_TTES_PER_SECTOR_USABLE);
427 vg_assert(sectors[y].tt_n_inuse >= 0);
428
429 /* Copy into tc. */
430 tce = sectors[y].tc_next;
431 vg_assert(tce >= &sectors[y].tc[0]);
432 vg_assert(tce <= &sectors[y].tc[tc_sector_szQ]);
433
434 tce[0] = entry;
435 dstP = (UChar*)(&tce[1]);
436 srcP = (UChar*)code;
437 for (i = 0; i < code_len; i++)
438 dstP[i] = srcP[i];
439 sectors[y].tc_next += reqdQ;
440 sectors[y].tt_n_inuse++;
441
cerion85665ca2005-06-20 15:51:07 +0000442 invalidate_icache( dstP, code_len );
cerion85665ca2005-06-20 15:51:07 +0000443
sewardjfa8ec112005-01-19 11:55:34 +0000444 /* more paranoia */
445 tce2 = sectors[y].tc_next;
446 vg_assert(tce2 >= &sectors[y].tc[0]);
447 vg_assert(tce2 <= &sectors[y].tc[tc_sector_szQ]);
448
449 /* Find an empty tt slot, and use it. There must be such a slot
450 since tt is never allowed to get completely full. */
451 i = HASH_TT(entry);
452 vg_assert(i >= 0 && i < N_TTES_PER_SECTOR);
sewardj6c3769f2002-11-29 01:02:45 +0000453 while (True) {
sewardjfa8ec112005-01-19 11:55:34 +0000454 if (sectors[y].tt[i].status == Empty
455 || sectors[y].tt[i].status == Deleted)
sewardj6c3769f2002-11-29 01:02:45 +0000456 break;
457 i++;
sewardjfa8ec112005-01-19 11:55:34 +0000458 if (i >= N_TTES_PER_SECTOR)
sewardj6c3769f2002-11-29 01:02:45 +0000459 i = 0;
460 }
sewardj22854b92002-11-30 14:00:47 +0000461
sewardjfa8ec112005-01-19 11:55:34 +0000462 sectors[y].tt[i].status = InUse;
463 sectors[y].tt[i].tce = tce;
464 sectors[y].tt[i].count = 0;
465 sectors[y].tt[i].weight = 1;
466 sectors[y].tt[i].vge = *vge;
467 sectors[y].tt[i].entry = entry;
468
469 setFastCacheEntry( entry, tce, &sectors[y].tt[i].count );
sewardj6c3769f2002-11-29 01:02:45 +0000470}
471
472
sewardjfa8ec112005-01-19 11:55:34 +0000473/* Search for the translation of the given guest address. If
474 requested, a successful search can also cause the fast-caches to be
475 updated.
sewardj6c3769f2002-11-29 01:02:45 +0000476*/
sewardjfa8ec112005-01-19 11:55:34 +0000477Bool VG_(search_transtab) ( /*OUT*/AddrH* result,
478 Addr64 guest_addr,
479 Bool upd_cache )
sewardj6c3769f2002-11-29 01:02:45 +0000480{
sewardjfa8ec112005-01-19 11:55:34 +0000481 Int i, j, k, kstart, sno;
sewardj663a1bd2005-04-24 11:22:44 +0000482
483 vg_assert(init_done);
sewardjfa8ec112005-01-19 11:55:34 +0000484 /* Find the initial probe point just once. It will be the same in
485 all sectors and avoids multiple expensive % operations. */
486 n_full_lookups++;
487 k = -1;
488 kstart = HASH_TT(guest_addr);
489 vg_assert(kstart >= 0 && kstart < N_TTES_PER_SECTOR);
sewardj6c3769f2002-11-29 01:02:45 +0000490
sewardjfa8ec112005-01-19 11:55:34 +0000491 /* Search in all the sectors. Although the order should not matter,
492 it might be most efficient to search in the order youngest to
493 oldest. */
494 sno = youngest_sector;
495 for (i = 0; i < N_SECTORS; i++) {
sewardj6c3769f2002-11-29 01:02:45 +0000496
sewardjfa8ec112005-01-19 11:55:34 +0000497 if (sectors[sno].tc == NULL)
498 goto notfound; /* sector not in use. */
sewardj6c3769f2002-11-29 01:02:45 +0000499
sewardjfa8ec112005-01-19 11:55:34 +0000500 k = kstart;
501 for (j = 0; j < N_TTES_PER_SECTOR; j++) {
502 n_lookup_probes++;
503 if (sectors[sno].tt[k].status == InUse
504 && sectors[sno].tt[k].entry == guest_addr) {
505 /* found it */
506 if (upd_cache)
507 setFastCacheEntry(
508 guest_addr, sectors[sno].tt[k].tce,
509 &sectors[sno].tt[k].count );
510 if (result)
511 *result = sizeof(Addr64) + (AddrH)sectors[sno].tt[k].tce;
512 return True;
513 }
514 if (sectors[sno].tt[k].status == Empty)
515 break; /* not found in this sector */
516 k++;
517 if (k == N_TTES_PER_SECTOR)
518 k = 0;
sewardj6c3769f2002-11-29 01:02:45 +0000519 }
sewardjfa8ec112005-01-19 11:55:34 +0000520
521 /* If we fall off the end, all entries are InUse and not
522 matching, or Deleted. In any case we did not find it in this
523 sector. */
524
525 notfound:
526 /* move to the next oldest sector */
527 sno = sno==0 ? (N_SECTORS-1) : (sno-1);
sewardj6c3769f2002-11-29 01:02:45 +0000528 }
sewardjfa8ec112005-01-19 11:55:34 +0000529
530 /* Not found in any sector. */
531 return False;
sewardj6c3769f2002-11-29 01:02:45 +0000532}
533
534
sewardjfa8ec112005-01-19 11:55:34 +0000535/* Delete all translations which intersect with any part of the
536 specified guest address range. Note, this is SLOW.
sewardjde4a1d02002-03-22 01:27:54 +0000537*/
sewardjfa8ec112005-01-19 11:55:34 +0000538
539static inline
sewardja3054502005-07-26 23:04:25 +0000540Bool overlap1 ( Addr64 s1, ULong r1, Addr64 s2, ULong r2 )
sewardjfa8ec112005-01-19 11:55:34 +0000541{
sewardja3054502005-07-26 23:04:25 +0000542 Addr64 e1 = s1 + r1 - 1ULL;
543 Addr64 e2 = s2 + r2 - 1ULL;
sewardjfa8ec112005-01-19 11:55:34 +0000544 if (e1 < s2 || e2 < s1)
545 return False;
546 return True;
547}
548
549static inline
sewardja3054502005-07-26 23:04:25 +0000550Bool overlaps ( Addr64 start, ULong range, VexGuestExtents* vge )
sewardjfa8ec112005-01-19 11:55:34 +0000551{
552 if (overlap1(start, range, vge->base[0], (UInt)vge->len[0]))
553 return True;
554 if (vge->n_used < 2)
555 return False;
556 if (overlap1(start, range, vge->base[1], (UInt)vge->len[1]))
557 return True;
558 if (vge->n_used < 3)
559 return False;
560 if (overlap1(start, range, vge->base[2], (UInt)vge->len[2]))
561 return True;
562 return False;
563}
564
565
sewardja3054502005-07-26 23:04:25 +0000566void VG_(discard_translations) ( Addr64 guest_start, ULong range )
sewardjfa8ec112005-01-19 11:55:34 +0000567{
568 Int sno, i;
569 Bool anyDeleted = False;
570
sewardj663a1bd2005-04-24 11:22:44 +0000571 vg_assert(init_done);
572
sewardjfa8ec112005-01-19 11:55:34 +0000573 for (sno = 0; sno < N_SECTORS; sno++) {
574 if (sectors[sno].tc == NULL)
575 continue;
576 for (i = 0; i < N_TTES_PER_SECTOR; i++) {
577 if (sectors[sno].tt[i].status == InUse
578 && overlaps( guest_start, range, &sectors[sno].tt[i].vge )) {
579 sectors[sno].tt[i].status = Deleted;
580 sectors[sno].tt_n_inuse--;
581 anyDeleted = True;
582 n_disc_count++;
583 n_disc_osize += vge_osize(&sectors[sno].tt[i].vge);
584 }
585 }
586 }
587
588 if (anyDeleted)
589 invalidateFastCache();
590}
591
592
593/*------------------------------------------------------------*/
sewardjde4a1d02002-03-22 01:27:54 +0000594/*--- Initialisation. ---*/
595/*------------------------------------------------------------*/
596
sewardjc0d8f682002-11-30 00:49:43 +0000597void VG_(init_tt_tc) ( void )
598{
sewardjfa8ec112005-01-19 11:55:34 +0000599 Int i, avg_codeszQ;
sewardjc0d8f682002-11-30 00:49:43 +0000600
sewardj663a1bd2005-04-24 11:22:44 +0000601 vg_assert(!init_done);
602 init_done = True;
603
sewardj4ccf7072004-11-28 16:58:05 +0000604 /* Otherwise lots of things go wrong... */
sewardjfa8ec112005-01-19 11:55:34 +0000605 vg_assert(sizeof(ULong) == 8);
606 vg_assert(sizeof(Addr64) == 8);
607
608 if (VG_(clo_verbosity) > 2)
609 VG_(message)(Vg_DebugMsg,
610 "TT/TC: VG_(init_tt_tc) "
611 "(startup of code management)");
612
613 /* Figure out how big each tc area should be. */
njn43b9a8a2005-05-10 04:37:01 +0000614 avg_codeszQ = (VG_(details).avg_translation_sizeB + 7) / 8;
615 tc_sector_szQ = N_TTES_PER_SECTOR_USABLE * (1 + avg_codeszQ);
sewardjfa8ec112005-01-19 11:55:34 +0000616
sewardjc0d8f682002-11-30 00:49:43 +0000617 /* Ensure the calculated value is not way crazy. */
sewardjfa8ec112005-01-19 11:55:34 +0000618 vg_assert(tc_sector_szQ >= 2 * N_TTES_PER_SECTOR_USABLE);
619 vg_assert(tc_sector_szQ <= 50 * N_TTES_PER_SECTOR_USABLE);
sewardjc0d8f682002-11-30 00:49:43 +0000620
sewardjfa8ec112005-01-19 11:55:34 +0000621 /* Initialise the sectors */
622 youngest_sector = 0;
623 for (i = 0; i < N_SECTORS; i++) {
624 sectors[i].tc = NULL;
625 sectors[i].tt = NULL;
626 sectors[i].tc_next = NULL;
627 sectors[i].tt_n_inuse = 0;
sewardjc0d8f682002-11-30 00:49:43 +0000628 }
sewardjc0d8f682002-11-30 00:49:43 +0000629
sewardjfa8ec112005-01-19 11:55:34 +0000630 /* and the fast caches. */
631 invalidateFastCache();
sewardjc0d8f682002-11-30 00:49:43 +0000632
633 if (VG_(clo_verbosity) > 2) {
634 VG_(message)(Vg_DebugMsg,
sewardjfa8ec112005-01-19 11:55:34 +0000635 "TT/TC: cache: %d sectors of %d bytes each = %d total",
636 N_SECTORS, 8 * tc_sector_szQ,
637 N_SECTORS * 8 * tc_sector_szQ );
sewardjc0d8f682002-11-30 00:49:43 +0000638 VG_(message)(Vg_DebugMsg,
sewardjfa8ec112005-01-19 11:55:34 +0000639 "TT/TC: table: %d total entries, max occupancy %d (%d%%)",
640 N_SECTORS * N_TTES_PER_SECTOR,
641 N_SECTORS * N_TTES_PER_SECTOR_USABLE,
642 SECTOR_TT_LIMIT_PERCENT );
643 }
644}
645
646
647/*------------------------------------------------------------*/
648/*--- Printing out statistics. ---*/
649/*------------------------------------------------------------*/
650
651static ULong safe_idiv( ULong a, ULong b )
652{
653 return (b == 0 ? 0 : a / b);
654}
655
656UInt VG_(get_bbs_translated) ( void )
657{
658 return n_in_count;
659}
660
661void VG_(print_tt_tc_stats) ( void )
662{
663 VG_(message)(Vg_DebugMsg,
664 " tt/tc: %llu tt lookups requiring %llu probes",
665 n_full_lookups, n_lookup_probes );
666 VG_(message)(Vg_DebugMsg,
667 " tt/tc: %llu fast-cache updates, %llu flushes",
668 n_fast_updates, n_fast_flushes );
669
670 VG_(message)(Vg_DebugMsg,
sewardj26412bd2005-07-07 10:05:05 +0000671 "translate: new %lld "
672 "(%lld -> %lld; ratio %lld:10) [%lld scs]",
sewardjfa8ec112005-01-19 11:55:34 +0000673 n_in_count, n_in_osize, n_in_tsize,
sewardj26412bd2005-07-07 10:05:05 +0000674 safe_idiv(10*n_in_tsize, n_in_osize),
675 n_in_sc_count);
sewardjfa8ec112005-01-19 11:55:34 +0000676 VG_(message)(Vg_DebugMsg,
677 "translate: dumped %lld (%lld -> ?" "?)",
678 n_dump_count, n_dump_osize );
679 VG_(message)(Vg_DebugMsg,
680 "translate: discarded %lld (%lld -> ?" "?)",
681 n_disc_count, n_disc_osize );
682}
683
684/*------------------------------------------------------------*/
685/*--- Printing out of profiling results. ---*/
686/*------------------------------------------------------------*/
687
sewardjfa8ec112005-01-19 11:55:34 +0000688static ULong score ( TTEntry* tte )
689{
690 return ((ULong)tte->weight) * ((ULong)tte->count);
691}
692
njn2025cf92005-06-26 20:44:48 +0000693ULong VG_(get_BB_profile) ( BBProfEntry tops[], UInt n_tops )
sewardjfa8ec112005-01-19 11:55:34 +0000694{
sewardjfa8ec112005-01-19 11:55:34 +0000695 Int sno, i, r, s;
njn2025cf92005-06-26 20:44:48 +0000696 ULong score_total;
sewardjfa8ec112005-01-19 11:55:34 +0000697
698 /* First, compute the total weighted count, and find the top N
njn2025cf92005-06-26 20:44:48 +0000699 ttes. tops contains pointers to the most-used n_tops blocks, in
sewardjfa8ec112005-01-19 11:55:34 +0000700 descending order (viz, tops[0] is the highest scorer). */
njn2025cf92005-06-26 20:44:48 +0000701 for (i = 0; i < n_tops; i++) {
702 tops[i].addr = 0;
703 tops[i].score = 0;
704 }
sewardjfa8ec112005-01-19 11:55:34 +0000705
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]. */
njn2025cf92005-06-26 20:44:48 +0000716 r = n_tops-1;
sewardjfa8ec112005-01-19 11:55:34 +0000717 while (True) {
718 if (r == -1)
719 break;
njn2025cf92005-06-26 20:44:48 +0000720 if (tops[r].addr == 0) {
sewardjfa8ec112005-01-19 11:55:34 +0000721 r--;
722 continue;
723 }
njn2025cf92005-06-26 20:44:48 +0000724 if ( score(&sectors[sno].tt[i]) > tops[r].score ) {
sewardjfa8ec112005-01-19 11:55:34 +0000725 r--;
726 continue;
727 }
728 break;
729 }
730 r++;
njn2025cf92005-06-26 20:44:48 +0000731 vg_assert(r >= 0 && r <= n_tops);
sewardjfa8ec112005-01-19 11:55:34 +0000732 /* This bb should be placed at r, and bbs above it shifted
733 upwards one slot. */
njn2025cf92005-06-26 20:44:48 +0000734 if (r < n_tops) {
735 for (s = n_tops-1; s > r; s--)
sewardjfa8ec112005-01-19 11:55:34 +0000736 tops[s] = tops[s-1];
njn2025cf92005-06-26 20:44:48 +0000737 tops[r].addr = sectors[sno].tt[i].entry;
738 tops[r].score = score( &sectors[sno].tt[i] );
sewardjfa8ec112005-01-19 11:55:34 +0000739 }
740 }
sewardjc0d8f682002-11-30 00:49:43 +0000741 }
742
njn2025cf92005-06-26 20:44:48 +0000743 return score_total;
sewardjc0d8f682002-11-30 00:49:43 +0000744}
745
sewardjde4a1d02002-03-22 01:27:54 +0000746/*--------------------------------------------------------------------*/
njn8bddf582005-05-13 23:40:55 +0000747/*--- end ---*/
sewardjde4a1d02002-03-22 01:27:54 +0000748/*--------------------------------------------------------------------*/