blob: d004ef49322b8d3f71b7953c76c0d39b41431974 [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"
njn24a6efb2005-06-20 03:36:51 +000033#include "pub_core_debuginfo.h" // For VG_(get_fnname_w_offset)()
njn97405b22005-06-02 03:39:33 +000034#include "pub_core_libcbase.h"
njn132bfcc2005-06-04 19:16:06 +000035#include "pub_core_libcassert.h"
njn24a6efb2005-06-20 03:36:51 +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"
njn24a6efb2005-06-20 03:36:51 +000039#include "pub_core_tooliface.h" // For VG_(details).avg_translation_sizeB
njn8bddf582005-05-13 23:40:55 +000040// XXX: this module should not depend on m_translate!
njn24a6efb2005-06-20 03:36:51 +000041#include "pub_core_translate.h" // For VG_(translate)()
njn8bddf582005-05-13 23:40:55 +000042#include "pub_core_transtab.h"
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. */
60#define N_TTES_PER_SECTOR /*30011*/ 40009
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. */
65#define SECTOR_TT_LIMIT_PERCENT 60
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
230/* Number/osize/tsize of translations entered. */
231ULong n_in_count = 0;
232ULong n_in_osize = 0;
233ULong n_in_tsize = 0;
234
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
cerion85665ca2005-06-20 15:51:07 +0000330#if defined(VGA_ppc32)
331static void invalidate_icache(void *ptr, int nbytes)
332{
333 unsigned long startaddr = (unsigned long) ptr;
334 unsigned long endaddr = startaddr + nbytes;
335 unsigned long addr;
336 unsigned long cls = 16; //VG_(cache_line_size);
337
338 startaddr &= ~(cls - 1);
339 for (addr = startaddr; addr < endaddr; addr += cls)
340 asm volatile("dcbst 0,%0" : : "r" (addr));
341 asm volatile("sync");
342 for (addr = startaddr; addr < endaddr; addr += cls)
343 asm volatile("icbi 0,%0" : : "r" (addr));
344 asm volatile("sync; isync");
345}
346#endif
347
sewardj6c3769f2002-11-29 01:02:45 +0000348
sewardjfa8ec112005-01-19 11:55:34 +0000349/* Add a translation of vge to TT/TC. The translation is temporarily
350 in code[0 .. code_len-1].
351
352 pre: youngest_sector points to a valid (although possibly full)
353 sector.
354*/
njn8bddf582005-05-13 23:40:55 +0000355void VG_(add_to_transtab)( VexGuestExtents* vge,
356 Addr64 entry,
357 AddrH code,
358 UInt code_len )
sewardj6c3769f2002-11-29 01:02:45 +0000359{
sewardjfa8ec112005-01-19 11:55:34 +0000360 Int tcAvailQ, reqdQ, y, i;
361 ULong *tce, *tce2;
362 UChar* srcP;
363 UChar* dstP;
364
sewardj663a1bd2005-04-24 11:22:44 +0000365 vg_assert(init_done);
sewardjfa8ec112005-01-19 11:55:34 +0000366 vg_assert(vge->n_used >= 1 && vge->n_used <= 3);
367 vg_assert(code_len > 0 && code_len < 20000);
368
369 if (0)
njn8bddf582005-05-13 23:40:55 +0000370 VG_(printf)("add_to_transtab(entry = 0x%llx, len = %d)\n",
sewardjfa8ec112005-01-19 11:55:34 +0000371 entry, code_len);
372
373 n_in_count++;
374 n_in_tsize += code_len;
375 n_in_osize += vge_osize(vge);
376
377 y = youngest_sector;
378 vg_assert(isValidSector(y));
379
380 if (sectors[y].tc == NULL)
381 initialiseSector(y);
382
383 /* Try putting the translation in this sector. */
384 reqdQ = 1 + ((code_len + 7) >> 3);
385
386 /* Will it fit in tc? */
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
392 if (tcAvailQ < reqdQ
393 || sectors[y].tt_n_inuse >= N_TTES_PER_SECTOR_USABLE) {
394 /* No. So move on to the next sector. Either it's never been
395 used before, in which case it will get its tt/tc allocated
396 now, or it has been used before, in which case it is set to be
397 empty, hence throwing out the oldest sector. */
398 youngest_sector++;
399 if (youngest_sector >= N_SECTORS)
400 youngest_sector = 0;
401 y = youngest_sector;
402 initialiseSector(y);
403 }
404
405 /* Be sure ... */
406 tcAvailQ = ((ULong*)(&sectors[y].tc[tc_sector_szQ]))
407 - ((ULong*)(sectors[y].tc_next));
408 vg_assert(tcAvailQ >= 0);
409 vg_assert(tcAvailQ <= tc_sector_szQ);
410 vg_assert(tcAvailQ >= reqdQ);
411 vg_assert(sectors[y].tt_n_inuse < N_TTES_PER_SECTOR_USABLE);
412 vg_assert(sectors[y].tt_n_inuse >= 0);
413
414 /* Copy into tc. */
415 tce = sectors[y].tc_next;
416 vg_assert(tce >= &sectors[y].tc[0]);
417 vg_assert(tce <= &sectors[y].tc[tc_sector_szQ]);
418
419 tce[0] = entry;
420 dstP = (UChar*)(&tce[1]);
421 srcP = (UChar*)code;
422 for (i = 0; i < code_len; i++)
423 dstP[i] = srcP[i];
424 sectors[y].tc_next += reqdQ;
425 sectors[y].tt_n_inuse++;
426
cerion85665ca2005-06-20 15:51:07 +0000427#if defined(VGA_ppc32)
428 invalidate_icache( dstP, code_len );
429#endif
430
sewardjfa8ec112005-01-19 11:55:34 +0000431 /* more paranoia */
432 tce2 = sectors[y].tc_next;
433 vg_assert(tce2 >= &sectors[y].tc[0]);
434 vg_assert(tce2 <= &sectors[y].tc[tc_sector_szQ]);
435
436 /* Find an empty tt slot, and use it. There must be such a slot
437 since tt is never allowed to get completely full. */
438 i = HASH_TT(entry);
439 vg_assert(i >= 0 && i < N_TTES_PER_SECTOR);
sewardj6c3769f2002-11-29 01:02:45 +0000440 while (True) {
sewardjfa8ec112005-01-19 11:55:34 +0000441 if (sectors[y].tt[i].status == Empty
442 || sectors[y].tt[i].status == Deleted)
sewardj6c3769f2002-11-29 01:02:45 +0000443 break;
444 i++;
sewardjfa8ec112005-01-19 11:55:34 +0000445 if (i >= N_TTES_PER_SECTOR)
sewardj6c3769f2002-11-29 01:02:45 +0000446 i = 0;
447 }
sewardj22854b92002-11-30 14:00:47 +0000448
sewardjfa8ec112005-01-19 11:55:34 +0000449 sectors[y].tt[i].status = InUse;
450 sectors[y].tt[i].tce = tce;
451 sectors[y].tt[i].count = 0;
452 sectors[y].tt[i].weight = 1;
453 sectors[y].tt[i].vge = *vge;
454 sectors[y].tt[i].entry = entry;
455
456 setFastCacheEntry( entry, tce, &sectors[y].tt[i].count );
sewardj6c3769f2002-11-29 01:02:45 +0000457}
458
459
sewardjfa8ec112005-01-19 11:55:34 +0000460/* Search for the translation of the given guest address. If
461 requested, a successful search can also cause the fast-caches to be
462 updated.
sewardj6c3769f2002-11-29 01:02:45 +0000463*/
sewardjfa8ec112005-01-19 11:55:34 +0000464Bool VG_(search_transtab) ( /*OUT*/AddrH* result,
465 Addr64 guest_addr,
466 Bool upd_cache )
sewardj6c3769f2002-11-29 01:02:45 +0000467{
sewardjfa8ec112005-01-19 11:55:34 +0000468 Int i, j, k, kstart, sno;
sewardj663a1bd2005-04-24 11:22:44 +0000469
470 vg_assert(init_done);
sewardjfa8ec112005-01-19 11:55:34 +0000471 /* Find the initial probe point just once. It will be the same in
472 all sectors and avoids multiple expensive % operations. */
473 n_full_lookups++;
474 k = -1;
475 kstart = HASH_TT(guest_addr);
476 vg_assert(kstart >= 0 && kstart < N_TTES_PER_SECTOR);
sewardj6c3769f2002-11-29 01:02:45 +0000477
sewardjfa8ec112005-01-19 11:55:34 +0000478 /* Search in all the sectors. Although the order should not matter,
479 it might be most efficient to search in the order youngest to
480 oldest. */
481 sno = youngest_sector;
482 for (i = 0; i < N_SECTORS; i++) {
sewardj6c3769f2002-11-29 01:02:45 +0000483
sewardjfa8ec112005-01-19 11:55:34 +0000484 if (sectors[sno].tc == NULL)
485 goto notfound; /* sector not in use. */
sewardj6c3769f2002-11-29 01:02:45 +0000486
sewardjfa8ec112005-01-19 11:55:34 +0000487 k = kstart;
488 for (j = 0; j < N_TTES_PER_SECTOR; j++) {
489 n_lookup_probes++;
490 if (sectors[sno].tt[k].status == InUse
491 && sectors[sno].tt[k].entry == guest_addr) {
492 /* found it */
493 if (upd_cache)
494 setFastCacheEntry(
495 guest_addr, sectors[sno].tt[k].tce,
496 &sectors[sno].tt[k].count );
497 if (result)
498 *result = sizeof(Addr64) + (AddrH)sectors[sno].tt[k].tce;
499 return True;
500 }
501 if (sectors[sno].tt[k].status == Empty)
502 break; /* not found in this sector */
503 k++;
504 if (k == N_TTES_PER_SECTOR)
505 k = 0;
sewardj6c3769f2002-11-29 01:02:45 +0000506 }
sewardjfa8ec112005-01-19 11:55:34 +0000507
508 /* If we fall off the end, all entries are InUse and not
509 matching, or Deleted. In any case we did not find it in this
510 sector. */
511
512 notfound:
513 /* move to the next oldest sector */
514 sno = sno==0 ? (N_SECTORS-1) : (sno-1);
sewardj6c3769f2002-11-29 01:02:45 +0000515 }
sewardjfa8ec112005-01-19 11:55:34 +0000516
517 /* Not found in any sector. */
518 return False;
sewardj6c3769f2002-11-29 01:02:45 +0000519}
520
521
sewardjfa8ec112005-01-19 11:55:34 +0000522/* Delete all translations which intersect with any part of the
523 specified guest address range. Note, this is SLOW.
sewardjde4a1d02002-03-22 01:27:54 +0000524*/
sewardjfa8ec112005-01-19 11:55:34 +0000525
526static inline
527Bool overlap1 ( Addr64 s1, UInt r1, Addr64 s2, UInt r2 )
528{
529 Addr64 e1 = s1 + (ULong)r1 - 1ULL;
530 Addr64 e2 = s2 + (ULong)r1 - 1ULL;
531 if (e1 < s2 || e2 < s1)
532 return False;
533 return True;
534}
535
536static inline
537Bool overlaps ( Addr64 start, UInt range, VexGuestExtents* vge )
538{
539 if (overlap1(start, range, vge->base[0], (UInt)vge->len[0]))
540 return True;
541 if (vge->n_used < 2)
542 return False;
543 if (overlap1(start, range, vge->base[1], (UInt)vge->len[1]))
544 return True;
545 if (vge->n_used < 3)
546 return False;
547 if (overlap1(start, range, vge->base[2], (UInt)vge->len[2]))
548 return True;
549 return False;
550}
551
552
553void VG_(discard_translations) ( Addr64 guest_start, UInt range )
554{
555 Int sno, i;
556 Bool anyDeleted = False;
557
sewardj663a1bd2005-04-24 11:22:44 +0000558 vg_assert(init_done);
559
sewardjfa8ec112005-01-19 11:55:34 +0000560 for (sno = 0; sno < N_SECTORS; sno++) {
561 if (sectors[sno].tc == NULL)
562 continue;
563 for (i = 0; i < N_TTES_PER_SECTOR; i++) {
564 if (sectors[sno].tt[i].status == InUse
565 && overlaps( guest_start, range, &sectors[sno].tt[i].vge )) {
566 sectors[sno].tt[i].status = Deleted;
567 sectors[sno].tt_n_inuse--;
568 anyDeleted = True;
569 n_disc_count++;
570 n_disc_osize += vge_osize(&sectors[sno].tt[i].vge);
571 }
572 }
573 }
574
575 if (anyDeleted)
576 invalidateFastCache();
577}
578
579
580/*------------------------------------------------------------*/
581/*--- Sanity checking ---*/
582/*------------------------------------------------------------*/
583
sewardj4ccf7072004-11-28 16:58:05 +0000584void VG_(sanity_check_tt_tc) ( Char* who )
sewardjde4a1d02002-03-22 01:27:54 +0000585{
sewardjde4a1d02002-03-22 01:27:54 +0000586}
587
588
589/*------------------------------------------------------------*/
590/*--- Initialisation. ---*/
591/*------------------------------------------------------------*/
592
sewardjc0d8f682002-11-30 00:49:43 +0000593void VG_(init_tt_tc) ( void )
594{
sewardjfa8ec112005-01-19 11:55:34 +0000595 Int i, avg_codeszQ;
sewardjc0d8f682002-11-30 00:49:43 +0000596
sewardj663a1bd2005-04-24 11:22:44 +0000597 vg_assert(!init_done);
598 init_done = True;
599
sewardj4ccf7072004-11-28 16:58:05 +0000600 /* Otherwise lots of things go wrong... */
sewardjfa8ec112005-01-19 11:55:34 +0000601 vg_assert(sizeof(ULong) == 8);
602 vg_assert(sizeof(Addr64) == 8);
603
604 if (VG_(clo_verbosity) > 2)
605 VG_(message)(Vg_DebugMsg,
606 "TT/TC: VG_(init_tt_tc) "
607 "(startup of code management)");
608
609 /* Figure out how big each tc area should be. */
njn43b9a8a2005-05-10 04:37:01 +0000610 avg_codeszQ = (VG_(details).avg_translation_sizeB + 7) / 8;
611 tc_sector_szQ = N_TTES_PER_SECTOR_USABLE * (1 + avg_codeszQ);
sewardjfa8ec112005-01-19 11:55:34 +0000612
sewardjc0d8f682002-11-30 00:49:43 +0000613 /* Ensure the calculated value is not way crazy. */
sewardjfa8ec112005-01-19 11:55:34 +0000614 vg_assert(tc_sector_szQ >= 2 * N_TTES_PER_SECTOR_USABLE);
615 vg_assert(tc_sector_szQ <= 50 * N_TTES_PER_SECTOR_USABLE);
sewardjc0d8f682002-11-30 00:49:43 +0000616
sewardjfa8ec112005-01-19 11:55:34 +0000617 /* Initialise the sectors */
618 youngest_sector = 0;
619 for (i = 0; i < N_SECTORS; i++) {
620 sectors[i].tc = NULL;
621 sectors[i].tt = NULL;
622 sectors[i].tc_next = NULL;
623 sectors[i].tt_n_inuse = 0;
sewardjc0d8f682002-11-30 00:49:43 +0000624 }
sewardjc0d8f682002-11-30 00:49:43 +0000625
sewardjfa8ec112005-01-19 11:55:34 +0000626 /* and the fast caches. */
627 invalidateFastCache();
sewardjc0d8f682002-11-30 00:49:43 +0000628
629 if (VG_(clo_verbosity) > 2) {
630 VG_(message)(Vg_DebugMsg,
sewardjfa8ec112005-01-19 11:55:34 +0000631 "TT/TC: cache: %d sectors of %d bytes each = %d total",
632 N_SECTORS, 8 * tc_sector_szQ,
633 N_SECTORS * 8 * tc_sector_szQ );
sewardjc0d8f682002-11-30 00:49:43 +0000634 VG_(message)(Vg_DebugMsg,
sewardjfa8ec112005-01-19 11:55:34 +0000635 "TT/TC: table: %d total entries, max occupancy %d (%d%%)",
636 N_SECTORS * N_TTES_PER_SECTOR,
637 N_SECTORS * N_TTES_PER_SECTOR_USABLE,
638 SECTOR_TT_LIMIT_PERCENT );
639 }
640}
641
642
643/*------------------------------------------------------------*/
644/*--- Printing out statistics. ---*/
645/*------------------------------------------------------------*/
646
647static ULong safe_idiv( ULong a, ULong b )
648{
649 return (b == 0 ? 0 : a / b);
650}
651
652UInt VG_(get_bbs_translated) ( void )
653{
654 return n_in_count;
655}
656
657void VG_(print_tt_tc_stats) ( void )
658{
659 VG_(message)(Vg_DebugMsg,
660 " tt/tc: %llu tt lookups requiring %llu probes",
661 n_full_lookups, n_lookup_probes );
662 VG_(message)(Vg_DebugMsg,
663 " tt/tc: %llu fast-cache updates, %llu flushes",
664 n_fast_updates, n_fast_flushes );
665
666 VG_(message)(Vg_DebugMsg,
667 "translate: new %lld (%lld -> %lld; ratio %lld:10)",
668 n_in_count, n_in_osize, n_in_tsize,
669 safe_idiv(10*n_in_tsize, n_in_osize));
670 VG_(message)(Vg_DebugMsg,
671 "translate: dumped %lld (%lld -> ?" "?)",
672 n_dump_count, n_dump_osize );
673 VG_(message)(Vg_DebugMsg,
674 "translate: discarded %lld (%lld -> ?" "?)",
675 n_disc_count, n_disc_osize );
676}
677
678/*------------------------------------------------------------*/
679/*--- Printing out of profiling results. ---*/
680/*------------------------------------------------------------*/
681
682/* Only the top N_MAX bbs will be displayed. */
sewardjf9f19492005-01-24 10:42:46 +0000683#define N_MAX 200
sewardjfa8ec112005-01-19 11:55:34 +0000684
685static TTEntry* tops[N_MAX];
686
687static ULong score ( TTEntry* tte )
688{
689 return ((ULong)tte->weight) * ((ULong)tte->count);
690}
691
692static Bool heavier ( TTEntry* t1, TTEntry* t2 )
693{
694 return score(t1) > score(t2);
695}
696
697/* Print n/m in form xx.yy% */
698static
699void percentify ( ULong n, ULong m, Int field_width, Char* buf)
700{
701 Int i, len, space;
702 ULong lo, hi;
703 if (m == 0) m = 1; /* stay sane */
704 hi = (n * 100) / m;
705 lo = (((n * 100) - hi * m) * 100) / m;
706 vg_assert(lo < 100);
707 if (lo < 10)
708 VG_(sprintf)(buf, "%lld.0%lld%%", hi, lo);
709 else
710 VG_(sprintf)(buf, "%lld.%lld%%", hi, lo);
711
712 len = VG_(strlen)(buf);
713 space = field_width - len;
714 if (space < 0) space = 0; /* Allow for v. small field_width */
715 i = len;
716
717 /* Right justify in field */
718 for ( ; i >= 0; i--) buf[i + space] = buf[i];
719 for (i = 0; i < space; i++) buf[i] = ' ';
720}
721
722
723void VG_(show_BB_profile) ( void )
724{
725 Char name[64];
726 Int sno, i, r, s;
727 ULong score_total, score_cumul, score_here;
728 Char buf_cumul[10];
729 Char buf_here[10];
730
731 /* First, compute the total weighted count, and find the top N
732 ttes. tops contains pointers to the most-used N_MAX blocks, in
733 descending order (viz, tops[0] is the highest scorer). */
734 for (i = 0; i < N_MAX; i++)
735 tops[i] = NULL;
736
737 score_total = 0;
738
739 for (sno = 0; sno < N_SECTORS; sno++) {
740 if (sectors[sno].tc == NULL)
741 continue;
742 for (i = 0; i < N_TTES_PER_SECTOR; i++) {
743 if (sectors[sno].tt[i].status != InUse)
744 continue;
745 score_total += score(&sectors[sno].tt[i]);
746 /* Find the rank for sectors[sno].tt[i]. */
747 r = N_MAX-1;
748 while (True) {
749 if (r == -1)
750 break;
751 if (tops[r] == NULL) {
752 r--;
753 continue;
754 }
755 if (heavier(&sectors[sno].tt[i], tops[r])) {
756 r--;
757 continue;
758 }
759 break;
760 }
761 r++;
762 vg_assert(r >= 0 && r <= N_MAX);
763 /* This bb should be placed at r, and bbs above it shifted
764 upwards one slot. */
765 if (r < N_MAX) {
766 for (s = N_MAX-1; s > r; s--)
767 tops[s] = tops[s-1];
768 tops[r] = &sectors[sno].tt[i];
769 }
770 }
sewardjc0d8f682002-11-30 00:49:43 +0000771 }
772
sewardjfa8ec112005-01-19 11:55:34 +0000773 VG_(printf)("\n");
774 VG_(printf)("------------------------------------------------------------\n");
775 VG_(printf)("--- BEGIN BB Profile (summary of scores) ---\n");
776 VG_(printf)("------------------------------------------------------------\n");
777 VG_(printf)("\n");
778
779 VG_(printf)("Total score = %lld\n\n", score_total);
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)("%3d: (%9lld %s) %9lld %s 0x%llx %s\n",
793 r,
794 score_cumul, buf_cumul,
795 score_here, buf_here, tops[r]->entry, name );
796 }
797
798 VG_(printf)("\n");
799 VG_(printf)("------------------------------------------------------------\n");
800 VG_(printf)("--- BB Profile (BB details) ---\n");
801 VG_(printf)("------------------------------------------------------------\n");
802 VG_(printf)("\n");
803
804 score_cumul = 0;
805 for (r = 0; r < N_MAX; r++) {
806 if (tops[r] == NULL)
807 continue;
808 name[0] = 0;
809 VG_(get_fnname_w_offset)(tops[r]->entry, name, 64);
810 name[63] = 0;
811 score_here = score(tops[r]);
812 score_cumul += score_here;
813 percentify(score_cumul, score_total, 6, buf_cumul);
814 percentify(score_here, score_total, 6, buf_here);
815 VG_(printf)("\n");
816 VG_(printf)("=-=-=-=-=-=-=-=-=-=-=-=-=-= begin BB rank %d "
817 "=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n", r);
818 VG_(printf)("%3d: (%9lld %s) %9lld %s 0x%llx %s\n",
819 r,
820 score_cumul, buf_cumul,
821 score_here, buf_here, tops[r]->entry, name );
822 VG_(printf)("\n");
njn394213a2005-06-19 18:38:24 +0000823 VG_(translate)(0, tops[r]->entry, True, VG_(clo_profile_flags), 0);
sewardjfa8ec112005-01-19 11:55:34 +0000824 VG_(printf)("=-=-=-=-=-=-=-=-=-=-=-=-=-= end BB rank %d "
825 "=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n", r);
826 }
827
828 VG_(printf)("\n");
829 VG_(printf)("------------------------------------------------------------\n");
830 VG_(printf)("--- END BB Profile ---\n");
831 VG_(printf)("------------------------------------------------------------\n");
832 VG_(printf)("\n");
sewardjc0d8f682002-11-30 00:49:43 +0000833}
834
sewardjfa8ec112005-01-19 11:55:34 +0000835
sewardjde4a1d02002-03-22 01:27:54 +0000836/*--------------------------------------------------------------------*/
njn8bddf582005-05-13 23:40:55 +0000837/*--- end ---*/
sewardjde4a1d02002-03-22 01:27:54 +0000838/*--------------------------------------------------------------------*/