blob: 8ef0f9f8b796c0b7e54a95d699de67e35a2346fa [file] [log] [blame]
sewardjde4a1d02002-03-22 01:27:54 +00001
2/*--------------------------------------------------------------------*/
3/*--- Management of the translation table and cache. ---*/
njn8bddf582005-05-13 23:40:55 +00004/*--- m_transtab.c ---*/
sewardjde4a1d02002-03-22 01:27:54 +00005/*--------------------------------------------------------------------*/
6
7/*
njnb9c427c2004-12-01 14:14:42 +00008 This file is part of Valgrind, a dynamic binary instrumentation
9 framework.
sewardjde4a1d02002-03-22 01:27:54 +000010
sewardjfa8ec112005-01-19 11:55:34 +000011 Copyright (C) 2000-2005 Julian Seward
sewardjde4a1d02002-03-22 01:27:54 +000012 jseward@acm.org
sewardjde4a1d02002-03-22 01:27:54 +000013
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of the
17 License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful, but
20 WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27 02111-1307, USA.
28
njn25e49d8e72002-09-23 09:36:25 +000029 The GNU General Public License is contained in the file COPYING.
sewardjde4a1d02002-03-22 01:27:54 +000030*/
31
nethercotef1e5e152004-09-01 23:58:16 +000032#include "core.h"
njnea27e462005-05-31 02:38:09 +000033#include "pub_core_debuginfo.h"
njn97405b22005-06-02 03:39:33 +000034#include "pub_core_libcbase.h"
njn132bfcc2005-06-04 19:16:06 +000035#include "pub_core_libcassert.h"
njne9befc62005-06-11 15:51:30 +000036#include "pub_core_libcmman.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"
njn43b9a8a2005-05-10 04:37:01 +000039#include "pub_core_tooliface.h"
njn8bddf582005-05-13 23:40:55 +000040// XXX: this module should not depend on m_translate!
njn3cbfbc12005-05-13 23:11:40 +000041#include "pub_core_translate.h"
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
330
sewardjfa8ec112005-01-19 11:55:34 +0000331/* Add a translation of vge to TT/TC. The translation is temporarily
332 in code[0 .. code_len-1].
333
334 pre: youngest_sector points to a valid (although possibly full)
335 sector.
336*/
njn8bddf582005-05-13 23:40:55 +0000337void VG_(add_to_transtab)( VexGuestExtents* vge,
338 Addr64 entry,
339 AddrH code,
340 UInt code_len )
sewardj6c3769f2002-11-29 01:02:45 +0000341{
sewardjfa8ec112005-01-19 11:55:34 +0000342 Int tcAvailQ, reqdQ, y, i;
343 ULong *tce, *tce2;
344 UChar* srcP;
345 UChar* dstP;
346
sewardj663a1bd2005-04-24 11:22:44 +0000347 vg_assert(init_done);
sewardjfa8ec112005-01-19 11:55:34 +0000348 vg_assert(vge->n_used >= 1 && vge->n_used <= 3);
349 vg_assert(code_len > 0 && code_len < 20000);
350
351 if (0)
njn8bddf582005-05-13 23:40:55 +0000352 VG_(printf)("add_to_transtab(entry = 0x%llx, len = %d)\n",
sewardjfa8ec112005-01-19 11:55:34 +0000353 entry, code_len);
354
355 n_in_count++;
356 n_in_tsize += code_len;
357 n_in_osize += vge_osize(vge);
358
359 y = youngest_sector;
360 vg_assert(isValidSector(y));
361
362 if (sectors[y].tc == NULL)
363 initialiseSector(y);
364
365 /* Try putting the translation in this sector. */
366 reqdQ = 1 + ((code_len + 7) >> 3);
367
368 /* Will it fit in tc? */
369 tcAvailQ = ((ULong*)(&sectors[y].tc[tc_sector_szQ]))
370 - ((ULong*)(sectors[y].tc_next));
371 vg_assert(tcAvailQ >= 0);
372 vg_assert(tcAvailQ <= tc_sector_szQ);
373
374 if (tcAvailQ < reqdQ
375 || sectors[y].tt_n_inuse >= N_TTES_PER_SECTOR_USABLE) {
376 /* No. So move on to the next sector. Either it's never been
377 used before, in which case it will get its tt/tc allocated
378 now, or it has been used before, in which case it is set to be
379 empty, hence throwing out the oldest sector. */
380 youngest_sector++;
381 if (youngest_sector >= N_SECTORS)
382 youngest_sector = 0;
383 y = youngest_sector;
384 initialiseSector(y);
385 }
386
387 /* Be sure ... */
388 tcAvailQ = ((ULong*)(&sectors[y].tc[tc_sector_szQ]))
389 - ((ULong*)(sectors[y].tc_next));
390 vg_assert(tcAvailQ >= 0);
391 vg_assert(tcAvailQ <= tc_sector_szQ);
392 vg_assert(tcAvailQ >= reqdQ);
393 vg_assert(sectors[y].tt_n_inuse < N_TTES_PER_SECTOR_USABLE);
394 vg_assert(sectors[y].tt_n_inuse >= 0);
395
396 /* Copy into tc. */
397 tce = sectors[y].tc_next;
398 vg_assert(tce >= &sectors[y].tc[0]);
399 vg_assert(tce <= &sectors[y].tc[tc_sector_szQ]);
400
401 tce[0] = entry;
402 dstP = (UChar*)(&tce[1]);
403 srcP = (UChar*)code;
404 for (i = 0; i < code_len; i++)
405 dstP[i] = srcP[i];
406 sectors[y].tc_next += reqdQ;
407 sectors[y].tt_n_inuse++;
408
409 /* more paranoia */
410 tce2 = sectors[y].tc_next;
411 vg_assert(tce2 >= &sectors[y].tc[0]);
412 vg_assert(tce2 <= &sectors[y].tc[tc_sector_szQ]);
413
414 /* Find an empty tt slot, and use it. There must be such a slot
415 since tt is never allowed to get completely full. */
416 i = HASH_TT(entry);
417 vg_assert(i >= 0 && i < N_TTES_PER_SECTOR);
sewardj6c3769f2002-11-29 01:02:45 +0000418 while (True) {
sewardjfa8ec112005-01-19 11:55:34 +0000419 if (sectors[y].tt[i].status == Empty
420 || sectors[y].tt[i].status == Deleted)
sewardj6c3769f2002-11-29 01:02:45 +0000421 break;
422 i++;
sewardjfa8ec112005-01-19 11:55:34 +0000423 if (i >= N_TTES_PER_SECTOR)
sewardj6c3769f2002-11-29 01:02:45 +0000424 i = 0;
425 }
sewardj22854b92002-11-30 14:00:47 +0000426
sewardjfa8ec112005-01-19 11:55:34 +0000427 sectors[y].tt[i].status = InUse;
428 sectors[y].tt[i].tce = tce;
429 sectors[y].tt[i].count = 0;
430 sectors[y].tt[i].weight = 1;
431 sectors[y].tt[i].vge = *vge;
432 sectors[y].tt[i].entry = entry;
433
434 setFastCacheEntry( entry, tce, &sectors[y].tt[i].count );
sewardj6c3769f2002-11-29 01:02:45 +0000435}
436
437
sewardjfa8ec112005-01-19 11:55:34 +0000438/* Search for the translation of the given guest address. If
439 requested, a successful search can also cause the fast-caches to be
440 updated.
sewardj6c3769f2002-11-29 01:02:45 +0000441*/
sewardjfa8ec112005-01-19 11:55:34 +0000442Bool VG_(search_transtab) ( /*OUT*/AddrH* result,
443 Addr64 guest_addr,
444 Bool upd_cache )
sewardj6c3769f2002-11-29 01:02:45 +0000445{
sewardjfa8ec112005-01-19 11:55:34 +0000446 Int i, j, k, kstart, sno;
sewardj663a1bd2005-04-24 11:22:44 +0000447
448 vg_assert(init_done);
sewardjfa8ec112005-01-19 11:55:34 +0000449 /* Find the initial probe point just once. It will be the same in
450 all sectors and avoids multiple expensive % operations. */
451 n_full_lookups++;
452 k = -1;
453 kstart = HASH_TT(guest_addr);
454 vg_assert(kstart >= 0 && kstart < N_TTES_PER_SECTOR);
sewardj6c3769f2002-11-29 01:02:45 +0000455
sewardjfa8ec112005-01-19 11:55:34 +0000456 /* Search in all the sectors. Although the order should not matter,
457 it might be most efficient to search in the order youngest to
458 oldest. */
459 sno = youngest_sector;
460 for (i = 0; i < N_SECTORS; i++) {
sewardj6c3769f2002-11-29 01:02:45 +0000461
sewardjfa8ec112005-01-19 11:55:34 +0000462 if (sectors[sno].tc == NULL)
463 goto notfound; /* sector not in use. */
sewardj6c3769f2002-11-29 01:02:45 +0000464
sewardjfa8ec112005-01-19 11:55:34 +0000465 k = kstart;
466 for (j = 0; j < N_TTES_PER_SECTOR; j++) {
467 n_lookup_probes++;
468 if (sectors[sno].tt[k].status == InUse
469 && sectors[sno].tt[k].entry == guest_addr) {
470 /* found it */
471 if (upd_cache)
472 setFastCacheEntry(
473 guest_addr, sectors[sno].tt[k].tce,
474 &sectors[sno].tt[k].count );
475 if (result)
476 *result = sizeof(Addr64) + (AddrH)sectors[sno].tt[k].tce;
477 return True;
478 }
479 if (sectors[sno].tt[k].status == Empty)
480 break; /* not found in this sector */
481 k++;
482 if (k == N_TTES_PER_SECTOR)
483 k = 0;
sewardj6c3769f2002-11-29 01:02:45 +0000484 }
sewardjfa8ec112005-01-19 11:55:34 +0000485
486 /* If we fall off the end, all entries are InUse and not
487 matching, or Deleted. In any case we did not find it in this
488 sector. */
489
490 notfound:
491 /* move to the next oldest sector */
492 sno = sno==0 ? (N_SECTORS-1) : (sno-1);
sewardj6c3769f2002-11-29 01:02:45 +0000493 }
sewardjfa8ec112005-01-19 11:55:34 +0000494
495 /* Not found in any sector. */
496 return False;
sewardj6c3769f2002-11-29 01:02:45 +0000497}
498
499
sewardjfa8ec112005-01-19 11:55:34 +0000500/* Delete all translations which intersect with any part of the
501 specified guest address range. Note, this is SLOW.
sewardjde4a1d02002-03-22 01:27:54 +0000502*/
sewardjfa8ec112005-01-19 11:55:34 +0000503
504static inline
505Bool overlap1 ( Addr64 s1, UInt r1, Addr64 s2, UInt r2 )
506{
507 Addr64 e1 = s1 + (ULong)r1 - 1ULL;
508 Addr64 e2 = s2 + (ULong)r1 - 1ULL;
509 if (e1 < s2 || e2 < s1)
510 return False;
511 return True;
512}
513
514static inline
515Bool overlaps ( Addr64 start, UInt range, VexGuestExtents* vge )
516{
517 if (overlap1(start, range, vge->base[0], (UInt)vge->len[0]))
518 return True;
519 if (vge->n_used < 2)
520 return False;
521 if (overlap1(start, range, vge->base[1], (UInt)vge->len[1]))
522 return True;
523 if (vge->n_used < 3)
524 return False;
525 if (overlap1(start, range, vge->base[2], (UInt)vge->len[2]))
526 return True;
527 return False;
528}
529
530
531void VG_(discard_translations) ( Addr64 guest_start, UInt range )
532{
533 Int sno, i;
534 Bool anyDeleted = False;
535
sewardj663a1bd2005-04-24 11:22:44 +0000536 vg_assert(init_done);
537
sewardjfa8ec112005-01-19 11:55:34 +0000538 for (sno = 0; sno < N_SECTORS; sno++) {
539 if (sectors[sno].tc == NULL)
540 continue;
541 for (i = 0; i < N_TTES_PER_SECTOR; i++) {
542 if (sectors[sno].tt[i].status == InUse
543 && overlaps( guest_start, range, &sectors[sno].tt[i].vge )) {
544 sectors[sno].tt[i].status = Deleted;
545 sectors[sno].tt_n_inuse--;
546 anyDeleted = True;
547 n_disc_count++;
548 n_disc_osize += vge_osize(&sectors[sno].tt[i].vge);
549 }
550 }
551 }
552
553 if (anyDeleted)
554 invalidateFastCache();
555}
556
557
558/*------------------------------------------------------------*/
559/*--- Sanity checking ---*/
560/*------------------------------------------------------------*/
561
sewardj4ccf7072004-11-28 16:58:05 +0000562void VG_(sanity_check_tt_tc) ( Char* who )
sewardjde4a1d02002-03-22 01:27:54 +0000563{
sewardjde4a1d02002-03-22 01:27:54 +0000564}
565
566
567/*------------------------------------------------------------*/
568/*--- Initialisation. ---*/
569/*------------------------------------------------------------*/
570
sewardjc0d8f682002-11-30 00:49:43 +0000571void VG_(init_tt_tc) ( void )
572{
sewardjfa8ec112005-01-19 11:55:34 +0000573 Int i, avg_codeszQ;
sewardjc0d8f682002-11-30 00:49:43 +0000574
sewardj663a1bd2005-04-24 11:22:44 +0000575 vg_assert(!init_done);
576 init_done = True;
577
sewardj4ccf7072004-11-28 16:58:05 +0000578 /* Otherwise lots of things go wrong... */
sewardjfa8ec112005-01-19 11:55:34 +0000579 vg_assert(sizeof(ULong) == 8);
580 vg_assert(sizeof(Addr64) == 8);
581
582 if (VG_(clo_verbosity) > 2)
583 VG_(message)(Vg_DebugMsg,
584 "TT/TC: VG_(init_tt_tc) "
585 "(startup of code management)");
586
587 /* Figure out how big each tc area should be. */
njn43b9a8a2005-05-10 04:37:01 +0000588 avg_codeszQ = (VG_(details).avg_translation_sizeB + 7) / 8;
589 tc_sector_szQ = N_TTES_PER_SECTOR_USABLE * (1 + avg_codeszQ);
sewardjfa8ec112005-01-19 11:55:34 +0000590
sewardjc0d8f682002-11-30 00:49:43 +0000591 /* Ensure the calculated value is not way crazy. */
sewardjfa8ec112005-01-19 11:55:34 +0000592 vg_assert(tc_sector_szQ >= 2 * N_TTES_PER_SECTOR_USABLE);
593 vg_assert(tc_sector_szQ <= 50 * N_TTES_PER_SECTOR_USABLE);
sewardjc0d8f682002-11-30 00:49:43 +0000594
sewardjfa8ec112005-01-19 11:55:34 +0000595 /* Initialise the sectors */
596 youngest_sector = 0;
597 for (i = 0; i < N_SECTORS; i++) {
598 sectors[i].tc = NULL;
599 sectors[i].tt = NULL;
600 sectors[i].tc_next = NULL;
601 sectors[i].tt_n_inuse = 0;
sewardjc0d8f682002-11-30 00:49:43 +0000602 }
sewardjc0d8f682002-11-30 00:49:43 +0000603
sewardjfa8ec112005-01-19 11:55:34 +0000604 /* and the fast caches. */
605 invalidateFastCache();
sewardjc0d8f682002-11-30 00:49:43 +0000606
607 if (VG_(clo_verbosity) > 2) {
608 VG_(message)(Vg_DebugMsg,
sewardjfa8ec112005-01-19 11:55:34 +0000609 "TT/TC: cache: %d sectors of %d bytes each = %d total",
610 N_SECTORS, 8 * tc_sector_szQ,
611 N_SECTORS * 8 * tc_sector_szQ );
sewardjc0d8f682002-11-30 00:49:43 +0000612 VG_(message)(Vg_DebugMsg,
sewardjfa8ec112005-01-19 11:55:34 +0000613 "TT/TC: table: %d total entries, max occupancy %d (%d%%)",
614 N_SECTORS * N_TTES_PER_SECTOR,
615 N_SECTORS * N_TTES_PER_SECTOR_USABLE,
616 SECTOR_TT_LIMIT_PERCENT );
617 }
618}
619
620
621/*------------------------------------------------------------*/
622/*--- Printing out statistics. ---*/
623/*------------------------------------------------------------*/
624
625static ULong safe_idiv( ULong a, ULong b )
626{
627 return (b == 0 ? 0 : a / b);
628}
629
630UInt VG_(get_bbs_translated) ( void )
631{
632 return n_in_count;
633}
634
635void VG_(print_tt_tc_stats) ( void )
636{
637 VG_(message)(Vg_DebugMsg,
638 " tt/tc: %llu tt lookups requiring %llu probes",
639 n_full_lookups, n_lookup_probes );
640 VG_(message)(Vg_DebugMsg,
641 " tt/tc: %llu fast-cache updates, %llu flushes",
642 n_fast_updates, n_fast_flushes );
643
644 VG_(message)(Vg_DebugMsg,
645 "translate: new %lld (%lld -> %lld; ratio %lld:10)",
646 n_in_count, n_in_osize, n_in_tsize,
647 safe_idiv(10*n_in_tsize, n_in_osize));
648 VG_(message)(Vg_DebugMsg,
649 "translate: dumped %lld (%lld -> ?" "?)",
650 n_dump_count, n_dump_osize );
651 VG_(message)(Vg_DebugMsg,
652 "translate: discarded %lld (%lld -> ?" "?)",
653 n_disc_count, n_disc_osize );
654}
655
656/*------------------------------------------------------------*/
657/*--- Printing out of profiling results. ---*/
658/*------------------------------------------------------------*/
659
660/* Only the top N_MAX bbs will be displayed. */
sewardjf9f19492005-01-24 10:42:46 +0000661#define N_MAX 200
sewardjfa8ec112005-01-19 11:55:34 +0000662
663static TTEntry* tops[N_MAX];
664
665static ULong score ( TTEntry* tte )
666{
667 return ((ULong)tte->weight) * ((ULong)tte->count);
668}
669
670static Bool heavier ( TTEntry* t1, TTEntry* t2 )
671{
672 return score(t1) > score(t2);
673}
674
675/* Print n/m in form xx.yy% */
676static
677void percentify ( ULong n, ULong m, Int field_width, Char* buf)
678{
679 Int i, len, space;
680 ULong lo, hi;
681 if (m == 0) m = 1; /* stay sane */
682 hi = (n * 100) / m;
683 lo = (((n * 100) - hi * m) * 100) / m;
684 vg_assert(lo < 100);
685 if (lo < 10)
686 VG_(sprintf)(buf, "%lld.0%lld%%", hi, lo);
687 else
688 VG_(sprintf)(buf, "%lld.%lld%%", hi, lo);
689
690 len = VG_(strlen)(buf);
691 space = field_width - len;
692 if (space < 0) space = 0; /* Allow for v. small field_width */
693 i = len;
694
695 /* Right justify in field */
696 for ( ; i >= 0; i--) buf[i + space] = buf[i];
697 for (i = 0; i < space; i++) buf[i] = ' ';
698}
699
700
701void VG_(show_BB_profile) ( void )
702{
703 Char name[64];
704 Int sno, i, r, s;
705 ULong score_total, score_cumul, score_here;
706 Char buf_cumul[10];
707 Char buf_here[10];
708
709 /* First, compute the total weighted count, and find the top N
710 ttes. tops contains pointers to the most-used N_MAX blocks, in
711 descending order (viz, tops[0] is the highest scorer). */
712 for (i = 0; i < N_MAX; i++)
713 tops[i] = NULL;
714
715 score_total = 0;
716
717 for (sno = 0; sno < N_SECTORS; sno++) {
718 if (sectors[sno].tc == NULL)
719 continue;
720 for (i = 0; i < N_TTES_PER_SECTOR; i++) {
721 if (sectors[sno].tt[i].status != InUse)
722 continue;
723 score_total += score(&sectors[sno].tt[i]);
724 /* Find the rank for sectors[sno].tt[i]. */
725 r = N_MAX-1;
726 while (True) {
727 if (r == -1)
728 break;
729 if (tops[r] == NULL) {
730 r--;
731 continue;
732 }
733 if (heavier(&sectors[sno].tt[i], tops[r])) {
734 r--;
735 continue;
736 }
737 break;
738 }
739 r++;
740 vg_assert(r >= 0 && r <= N_MAX);
741 /* This bb should be placed at r, and bbs above it shifted
742 upwards one slot. */
743 if (r < N_MAX) {
744 for (s = N_MAX-1; s > r; s--)
745 tops[s] = tops[s-1];
746 tops[r] = &sectors[sno].tt[i];
747 }
748 }
sewardjc0d8f682002-11-30 00:49:43 +0000749 }
750
sewardjfa8ec112005-01-19 11:55:34 +0000751 VG_(printf)("\n");
752 VG_(printf)("------------------------------------------------------------\n");
753 VG_(printf)("--- BEGIN BB Profile (summary of scores) ---\n");
754 VG_(printf)("------------------------------------------------------------\n");
755 VG_(printf)("\n");
756
757 VG_(printf)("Total score = %lld\n\n", score_total);
758
759 score_cumul = 0;
760 for (r = 0; r < N_MAX; r++) {
761 if (tops[r] == NULL)
762 continue;
763 name[0] = 0;
764 VG_(get_fnname_w_offset)(tops[r]->entry, name, 64);
765 name[63] = 0;
766 score_here = score(tops[r]);
767 score_cumul += score_here;
768 percentify(score_cumul, score_total, 6, buf_cumul);
769 percentify(score_here, score_total, 6, buf_here);
770 VG_(printf)("%3d: (%9lld %s) %9lld %s 0x%llx %s\n",
771 r,
772 score_cumul, buf_cumul,
773 score_here, buf_here, tops[r]->entry, name );
774 }
775
776 VG_(printf)("\n");
777 VG_(printf)("------------------------------------------------------------\n");
778 VG_(printf)("--- BB Profile (BB details) ---\n");
779 VG_(printf)("------------------------------------------------------------\n");
780 VG_(printf)("\n");
781
782 score_cumul = 0;
783 for (r = 0; r < N_MAX; r++) {
784 if (tops[r] == NULL)
785 continue;
786 name[0] = 0;
787 VG_(get_fnname_w_offset)(tops[r]->entry, name, 64);
788 name[63] = 0;
789 score_here = score(tops[r]);
790 score_cumul += score_here;
791 percentify(score_cumul, score_total, 6, buf_cumul);
792 percentify(score_here, score_total, 6, buf_here);
793 VG_(printf)("\n");
794 VG_(printf)("=-=-=-=-=-=-=-=-=-=-=-=-=-= begin BB rank %d "
795 "=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n", r);
796 VG_(printf)("%3d: (%9lld %s) %9lld %s 0x%llx %s\n",
797 r,
798 score_cumul, buf_cumul,
799 score_here, buf_here, tops[r]->entry, name );
800 VG_(printf)("\n");
801 VG_(translate)(0, tops[r]->entry, True, VG_(clo_profile_flags));
802 VG_(printf)("=-=-=-=-=-=-=-=-=-=-=-=-=-= end BB rank %d "
803 "=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n", r);
804 }
805
806 VG_(printf)("\n");
807 VG_(printf)("------------------------------------------------------------\n");
808 VG_(printf)("--- END BB Profile ---\n");
809 VG_(printf)("------------------------------------------------------------\n");
810 VG_(printf)("\n");
sewardjc0d8f682002-11-30 00:49:43 +0000811}
812
sewardjfa8ec112005-01-19 11:55:34 +0000813
sewardjde4a1d02002-03-22 01:27:54 +0000814/*--------------------------------------------------------------------*/
njn8bddf582005-05-13 23:40:55 +0000815/*--- end ---*/
sewardjde4a1d02002-03-22 01:27:54 +0000816/*--------------------------------------------------------------------*/