blob: d7461d7eecacc5fbd82e74dcfa4d4cffda8dc7b5 [file] [log] [blame]
sewardjde4a1d02002-03-22 01:27:54 +00001
2/*--------------------------------------------------------------------*/
3/*--- Management of the translation table and cache. ---*/
4/*--- vg_transtab.c ---*/
5/*--------------------------------------------------------------------*/
6
7/*
njnc9539842002-10-02 13:26:35 +00008 This file is part of Valgrind, an extensible x86 protected-mode
9 emulator for monitoring program execution on x86-Unixes.
sewardjde4a1d02002-03-22 01:27:54 +000010
nethercotebb1c9912004-01-04 16:43:23 +000011 Copyright (C) 2000-2004 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"
sewardjde4a1d02002-03-22 01:27:54 +000033
sewardj18d75132002-05-16 11:06:21 +000034/* #define DEBUG_TRANSTAB */
35
sewardjde4a1d02002-03-22 01:27:54 +000036
sewardj6c3769f2002-11-29 01:02:45 +000037/*-------------------------------------------------------------*/
38/*--- Management of the FIFO-based translation table+cache. ---*/
39/*-------------------------------------------------------------*/
sewardjde4a1d02002-03-22 01:27:54 +000040
sewardj6c3769f2002-11-29 01:02:45 +000041/*------------------ CONSTANTS ------------------*/
sewardjde4a1d02002-03-22 01:27:54 +000042
sewardj6c3769f2002-11-29 01:02:45 +000043/* Number of sectors the TC is divided into. */
44#define VG_TC_N_SECTORS 8
sewardjde4a1d02002-03-22 01:27:54 +000045
sewardjc0d8f682002-11-30 00:49:43 +000046/* Calculated once at startup and never changed. */
njn9b007f62003-04-07 14:40:25 +000047static /* const */ Int vg_tc_sector_szB = 0;
sewardjde4a1d02002-03-22 01:27:54 +000048
49/* Number of entries in the translation table. This must be a prime
50 number in order to make the hashing work properly. */
jseward3f409e82003-12-14 14:25:19 +000051#define VG_TT_SIZE /*5281*/ /*100129*/ /*200191*/ /*250829*/ 300007
sewardjde4a1d02002-03-22 01:27:54 +000052
53/* Do an LRU pass when the translation table becomes this full. */
54#define VG_TT_LIMIT_PERCENT /*67*/ 80
55
sewardj6c3769f2002-11-29 01:02:45 +000056#define VG_TT_LIMIT ((VG_TT_SIZE * VG_TT_LIMIT_PERCENT) / 100)
sewardjde4a1d02002-03-22 01:27:54 +000057
sewardjde4a1d02002-03-22 01:27:54 +000058
sewardj6c3769f2002-11-29 01:02:45 +000059/*------------------ TYPES ------------------*/
60
nethercotee0ce1ac2004-10-26 13:37:48 +000061#define CODE_ALIGNMENT sizeof(void*) // alignment of TCEntries
sewardj22854b92002-11-30 14:00:47 +000062#define CODE_ALIGN(a) (((a)+CODE_ALIGNMENT-1) & ~(CODE_ALIGNMENT-1))
63#define IS_ALIGNED(a) (((a) & (CODE_ALIGNMENT-1)) == 0)
64
nethercotee0ce1ac2004-10-26 13:37:48 +000065/* An entry in TC. Payload always is always padded out to a word-aligned
sewardj6c3769f2002-11-29 01:02:45 +000066 quantity so that these structs are always word-aligned. */
67typedef
68 struct {
nethercotee0ce1ac2004-10-26 13:37:48 +000069 /* 32-bit or 64-bit offsets */
70 /* +0 or 0 */ Addr orig_addr;
71 /* +4 or 8 */ UShort orig_size;
72 /* +6 or 10 */ UShort trans_size;
73 /* +8 or 12 */ UShort jump_sites[VG_MAX_JUMPS];
sewardj22854b92002-11-30 14:00:47 +000074 /* +VG_CODE_OFFSET */ UChar payload[0];
sewardj6c3769f2002-11-29 01:02:45 +000075 }
76 TCEntry;
77
78/* An entry in TT. */
79typedef
80 struct {
81 Addr orig_addr;
82 TCEntry* tcentry;
83 }
84 TTEntry;
85
86/* Denotes an empty TT slot, when TTEntry.orig_addr holds this
87 value. */
88#define VG_TTE_EMPTY ((Addr)1)
89
90/* Denotes an empty TT slot, when TTEntry.orig_addr holds this
91 value. */
sewardjde4a1d02002-03-22 01:27:54 +000092#define VG_TTE_DELETED ((Addr)3)
93
sewardj6c3769f2002-11-29 01:02:45 +000094/* A bogus TCEntry which hopefully does not match code from any valid
95 address. This is what all VG_(tt_fast) entries are made to point
96 at when we want to invalidate it. */
njn29a0abf2003-05-14 12:56:36 +000097static const TCEntry vg_tc_bogus_TCEntry = { ((Addr)5), 0, 0 };
sewardjde4a1d02002-03-22 01:27:54 +000098
sewardjde4a1d02002-03-22 01:27:54 +000099
sewardj6c3769f2002-11-29 01:02:45 +0000100/*------------------ DECLS ------------------*/
101
102/* The translation cache sectors. These are NULL until allocated
103 dynamically. */
104static UChar* vg_tc[VG_TC_N_SECTORS];
105
106/* Count of bytes used in each sector of the TC. */
107static Int vg_tc_used[VG_TC_N_SECTORS];
108
109/* The age of each sector, so we can find the oldest. We just use the
110 global count of translations made when the sector was brought into
111 use. Doesn't matter if this mechanism gets confused (wraps around
112 4G) once in a while. */
113static Int vg_tc_age[VG_TC_N_SECTORS];
114
115/* The number of the sector currently being allocated in. */
116static Int vg_tc_current;
117
sewardjc0d8f682002-11-30 00:49:43 +0000118/* Count of number of translations, orig and new bytes in each sector.
119 For stats purposes only. */
120static Int vg_tc_stats_count[VG_TC_N_SECTORS];
121static Int vg_tc_stats_osize[VG_TC_N_SECTORS];
122static Int vg_tc_stats_tsize[VG_TC_N_SECTORS];
123
nethercote92e7b7f2004-08-07 17:52:25 +0000124static UInt n_tt_fast_misses = 0; // number of lookups missing fast TT helper
125static UInt n_tc_discards = 0; // number of TT/TC discards
126
127// Number and total original/translated size of translations overall.
128static UInt overall_in_count = 0;
129static UInt overall_in_osize = 0;
130static UInt overall_in_tsize = 0;
131// Number and total original/t size of discards overall.
132static UInt overall_out_count = 0;
133static UInt overall_out_osize = 0;
134static UInt overall_out_tsize = 0;
135
136
sewardj6c3769f2002-11-29 01:02:45 +0000137
138/*------------------ TRANSLATION TABLE ------------------*/
139
140/* The translation table. An array of VG_TT_SIZE TTEntrys. */
sewardjde4a1d02002-03-22 01:27:54 +0000141static TTEntry* vg_tt = NULL;
142
sewardj18d75132002-05-16 11:06:21 +0000143/* Count of non-empty TT entries. This includes deleted ones. */
sewardjde4a1d02002-03-22 01:27:54 +0000144static Int vg_tt_used = 0;
145
146/* Fast helper for the TT. A direct-mapped cache which holds a
147 pointer to a TT entry which may or may not be the correct one, but
148 which we hope usually is. This array is referred to directly from
149 vg_dispatch.S. */
sewardj6c3769f2002-11-29 01:02:45 +0000150Addr /* TCEntry*, really */ VG_(tt_fast)[VG_TT_FAST_SIZE];
sewardj8aef1192002-07-24 09:36:36 +0000151
sewardj22854b92002-11-30 14:00:47 +0000152static void for_each_tc(Int sector, void (*fn)(TCEntry *));
153
154
155/*------------------ T-CHAINING HELPERS ------------------*/
sewardj8b635a42004-11-22 19:01:47 +0000156#if 0
sewardj22854b92002-11-30 14:00:47 +0000157static
158void for_each_jumpsite(TCEntry *tce, void (*fn)(Addr))
159{
160 Int i;
161 for(i = 0; i < VG_MAX_JUMPS; i++) {
162 Addr a;
163 UShort idx = tce->jump_sites[i];
164
165 if (idx == (UShort)-1)
166 continue;
167
168 a = (Addr)&tce->payload[idx];
169
170 (*fn)(a);
171 }
172}
173
174static inline
175void unchain_tce(TCEntry *tce)
176{
177 for_each_jumpsite(tce, VG_(unchain_jumpsite));
178}
179
180/* Unchain any jumps pointing to a sector we're about to free */
thughes4ad52d02004-06-27 17:37:21 +0000181static Addr sector_base;
182static Addr sector_len;
183
184static
185void unchain_site_for_sector(Addr a) {
186 Addr jmp = VG_(get_jmp_dest)(a);
187 if (jmp >= sector_base && jmp < (sector_base+sector_len))
188 VG_(unchain_jumpsite)(a);
189}
190
191static
192void unchain_tce_for_sector(TCEntry *tce) {
193 for_each_jumpsite(tce, unchain_site_for_sector);
194}
195
sewardj22854b92002-11-30 14:00:47 +0000196static
197void unchain_sector(Int s, Addr base, UInt len)
198{
thughes4ad52d02004-06-27 17:37:21 +0000199 sector_base = base;
200 sector_len = len;
sewardj22854b92002-11-30 14:00:47 +0000201
thughes4ad52d02004-06-27 17:37:21 +0000202 for_each_tc(s, unchain_tce_for_sector);
sewardj22854b92002-11-30 14:00:47 +0000203}
sewardj8b635a42004-11-22 19:01:47 +0000204#endif
sewardjde4a1d02002-03-22 01:27:54 +0000205
sewardj6c3769f2002-11-29 01:02:45 +0000206/*------------------ TT HELPERS ------------------*/
207
sewardjc0d8f682002-11-30 00:49:43 +0000208static
209void pp_tt_tc_status ( Char* submsg )
210{
211 Int tc_used, s;
212 if (VG_(clo_verbosity) <= 2)
213 return;
214 tc_used = 0;
215 for (s = 0; s < VG_TC_N_SECTORS; s++)
216 tc_used += vg_tc_used[s];
217
218 VG_(message)(Vg_DebugMsg,
njne0205ff2003-04-08 00:56:14 +0000219 "%lluk bbs: tt %d, tc %d, %s",
sewardjc0d8f682002-11-30 00:49:43 +0000220 VG_(bbs_done) / 1000,
221 vg_tt_used, tc_used, submsg );
222}
223
sewardj6c3769f2002-11-29 01:02:45 +0000224/* Invalidate the tt_fast cache, for whatever reason, by pointing all
225 entries at vg_tc_bogus_TCEntry. */
226static
227void vg_invalidate_tt_fast( void )
228{
229 Int j;
230 for (j = 0; j < VG_TT_FAST_SIZE; j++)
231 VG_(tt_fast)[j] = (Addr)&vg_tc_bogus_TCEntry;
232}
233
234
235static
236void add_tt_entry ( TCEntry* tce )
237{
238 UInt i;
239 /* VG_(printf)("add_TT_entry orig_addr %p\n", tce->orig_addr); */
240 /* Hash to get initial probe point. */
nethercote50397c22004-11-04 18:03:06 +0000241 i = tce->orig_addr % VG_TT_SIZE;
sewardj6c3769f2002-11-29 01:02:45 +0000242 while (True) {
243 if (vg_tt[i].orig_addr == tce->orig_addr)
244 VG_(core_panic)("add_TT_entry: duplicate");
245 if (vg_tt[i].orig_addr == VG_TTE_EMPTY)
246 break;
247 i++;
248 if (i == VG_TT_SIZE)
249 i = 0;
250 }
sewardj22854b92002-11-30 14:00:47 +0000251
sewardj6c3769f2002-11-29 01:02:45 +0000252 vg_tt[i].orig_addr = tce->orig_addr;
253 vg_tt[i].tcentry = tce;
254 vg_tt_used++;
255 /* sanity ... */
256 vg_assert(vg_tt_used < VG_TT_SIZE-1000);
257}
258
259
260/* Search TT to find the translated address of the supplied original,
261 or NULL if not found. This routine is used when we miss in
262 VG_(tt_fast).
263*/
264static __inline__
265TTEntry* search_tt ( Addr orig_addr )
266{
267 Int i;
268 /* Hash to get initial probe point. */
nethercote50397c22004-11-04 18:03:06 +0000269 i = orig_addr % VG_TT_SIZE;
sewardj6c3769f2002-11-29 01:02:45 +0000270 while (True) {
271 if (vg_tt[i].orig_addr == orig_addr)
272 return &vg_tt[i];
273 if (vg_tt[i].orig_addr == VG_TTE_EMPTY)
274 return NULL;
275 i++;
276 if (i == VG_TT_SIZE) i = 0;
277 }
278}
279
280
281static
282void initialise_tt ( void )
283{
284 Int i;
285 vg_tt_used = 0;
286 for (i = 0; i < VG_TT_SIZE; i++) {
287 vg_tt[i].orig_addr = VG_TTE_EMPTY;
288 }
289 vg_invalidate_tt_fast();
290}
291
292
293static
294void rebuild_TT ( void )
295{
296 Int s;
sewardj6c3769f2002-11-29 01:02:45 +0000297
298 /* Throw away TT. */
299 initialise_tt();
300
301 /* Rebuild TT from the remaining quarters. */
302 for (s = 0; s < VG_TC_N_SECTORS; s++) {
sewardj22854b92002-11-30 14:00:47 +0000303 for_each_tc(s, add_tt_entry);
sewardj6c3769f2002-11-29 01:02:45 +0000304 }
sewardjc0d8f682002-11-30 00:49:43 +0000305 pp_tt_tc_status ( "after rebuild of TC" );
sewardj6c3769f2002-11-29 01:02:45 +0000306}
307
308
309/*------------------ TC HELPERS ------------------*/
310
sewardj22854b92002-11-30 14:00:47 +0000311static
312void for_each_tc(Int s, void (*fn)(TCEntry *))
313{
314 UChar *pc;
315 UChar *pc_lim;
316 TCEntry *tce;
317
318 pc = &(vg_tc[s][0]);
319 pc_lim = &(vg_tc[s][vg_tc_used[s]]);
320 while (True) {
321 if (pc >= pc_lim) break;
322 tce = (TCEntry*)pc;
323 pc += sizeof(TCEntry) + tce->trans_size;
324 if (tce->orig_addr != VG_TTE_DELETED)
325 (*fn)(tce);
326 }
327}
328
sewardj6c3769f2002-11-29 01:02:45 +0000329/* Find the oldest non-NULL, non-empty sector, or -1 if none such. */
330static
331Int find_oldest_sector ( void )
332{
333 Int oldest_age, oldest, i;
334 oldest_age = 1000 * 1000 * 1000;
335 oldest = -1;
336 for (i = 0; i < VG_TC_N_SECTORS; i++) {
337 if (vg_tc[i] == NULL)
338 continue;
339 if (vg_tc_used[i] == 0)
340 continue;
341 if (vg_tc_age[i] < oldest_age) {
342 oldest = i;
343 oldest_age = vg_tc_age[i];
344 }
345 }
346 return oldest;
347}
348
349
350/* Discard the oldest sector, if any such exists. */
351static
352void discard_oldest_sector ( void )
353{
sewardjc0d8f682002-11-30 00:49:43 +0000354 Char msg[100];
sewardj6c3769f2002-11-29 01:02:45 +0000355 Int s = find_oldest_sector();
356 if (s != -1) {
sewardj8b635a42004-11-22 19:01:47 +0000357 //Int i;
sewardj22854b92002-11-30 14:00:47 +0000358
sewardj6c3769f2002-11-29 01:02:45 +0000359 vg_assert(s >= 0 && s < VG_TC_N_SECTORS);
sewardjc0d8f682002-11-30 00:49:43 +0000360 VG_(sprintf)(msg, "before discard of sector %d (%d bytes)",
361 s, vg_tc_used[s]);
sewardj22854b92002-11-30 14:00:47 +0000362
sewardj8b635a42004-11-22 19:01:47 +0000363 //for(i = 0; i < VG_TC_N_SECTORS; i++) {
364 // if (i != s && vg_tc[i] != NULL)
365 // unchain_sector(i, (Addr)vg_tc[s], vg_tc_used[s]);
366 // }
sewardj22854b92002-11-30 14:00:47 +0000367
sewardjc0d8f682002-11-30 00:49:43 +0000368 pp_tt_tc_status ( msg );
nethercote92e7b7f2004-08-07 17:52:25 +0000369 overall_out_count += vg_tc_stats_count[s];
370 overall_out_osize += vg_tc_stats_osize[s];
371 overall_out_tsize += vg_tc_stats_tsize[s];
sewardj6c3769f2002-11-29 01:02:45 +0000372 vg_tc_used[s] = 0;
sewardjc0d8f682002-11-30 00:49:43 +0000373 vg_tc_stats_count[s] = 0;
374 vg_tc_stats_osize[s] = 0;
375 vg_tc_stats_tsize[s] = 0;
nethercote92e7b7f2004-08-07 17:52:25 +0000376 n_tc_discards++;
sewardj6c3769f2002-11-29 01:02:45 +0000377 }
378}
379
380
381/* Find an empty sector and bring it into use. If there isn't one,
382 try and allocate one. If that fails, return -1. */
383static
384Int maybe_commission_sector ( void )
385{
sewardjc0d8f682002-11-30 00:49:43 +0000386 Char msg[100];
387 Int s;
sewardj6c3769f2002-11-29 01:02:45 +0000388 for (s = 0; s < VG_TC_N_SECTORS; s++) {
389 if (vg_tc[s] != NULL && vg_tc_used[s] == 0) {
nethercote92e7b7f2004-08-07 17:52:25 +0000390 vg_tc_age[s] = overall_in_count;
sewardjc0d8f682002-11-30 00:49:43 +0000391 VG_(sprintf)(msg, "after commission of sector %d "
392 "at time %d",
393 s, vg_tc_age[s]);
394 pp_tt_tc_status ( msg );
sewardj6c3769f2002-11-29 01:02:45 +0000395# ifdef DEBUG_TRANSTAB
nethercote92e7b7f2004-08-07 17:52:25 +0000396 VG_(sanity_check_tt_tc)();
sewardj6c3769f2002-11-29 01:02:45 +0000397# endif
398 return s;
399 }
400 }
401 for (s = 0; s < VG_TC_N_SECTORS; s++) {
402 if (vg_tc[s] == NULL) {
fitzhardinge98abfc72003-12-16 02:05:15 +0000403#if 1
sewardj6c3769f2002-11-29 01:02:45 +0000404 vg_tc[s] = VG_(get_memory_from_mmap)
sewardjc0d8f682002-11-30 00:49:43 +0000405 ( vg_tc_sector_szB, "trans-cache(sector)" );
fitzhardinge98abfc72003-12-16 02:05:15 +0000406#else
nethercote794a5912004-07-12 09:01:24 +0000407 // Alternative: put translations in an mmap'd file. The main
408 // reason is to help OProfile -- OProfile can assign time spent in
409 // translations to a particular file. The file format doesn't
410 // really matter, which is good because it's not really readable,
411 // being generated code but not a proper ELF file.
fitzhardinge98abfc72003-12-16 02:05:15 +0000412 Char buf[20];
413 static Int count = 0;
414 Int fd;
415
416 VG_(sprintf)(buf, ".transtab.%d", count++);
417
418 fd = VG_(open)(buf, VKI_O_RDWR|VKI_O_CREAT|VKI_O_TRUNC, 0700);
419 //VG_(unlink)(buf);
420 VG_(do_syscall)(__NR_ftruncate, fd, PGROUNDUP(vg_tc_sector_szB));
nethercote43f583e2004-07-11 18:16:36 +0000421 vg_tc[s] = VG_(mmap)(0, PGROUNDUP(vg_tc_sector_szB), VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC, VKI_MAP_SHARED, 0, fd, 0);
fitzhardinge98abfc72003-12-16 02:05:15 +0000422 VG_(close)(fd);
423#endif
sewardj6c3769f2002-11-29 01:02:45 +0000424 vg_tc_used[s] = 0;
nethercote794a5912004-07-12 09:01:24 +0000425 VG_(sprintf)(msg, "after allocation of sector %d (size %d)",
sewardjc0d8f682002-11-30 00:49:43 +0000426 s, vg_tc_sector_szB );
427 pp_tt_tc_status ( msg );
sewardj6c3769f2002-11-29 01:02:45 +0000428 return maybe_commission_sector();
429 }
430 }
431 return -1;
432}
433
sewardj6c3769f2002-11-29 01:02:45 +0000434
435static
436UChar* allocate ( Int nBytes )
437{
438 Int i;
439
sewardj22854b92002-11-30 14:00:47 +0000440 vg_assert(IS_ALIGNED(nBytes));
sewardj6c3769f2002-11-29 01:02:45 +0000441
442 /* Ensure the TT is still OK. */
443 while (vg_tt_used >= VG_TT_LIMIT) {
444 (void)discard_oldest_sector();
445 rebuild_TT();
446 vg_assert(vg_tt_used < VG_TT_LIMIT);
447 }
448
449 /* Can we get it into the current sector? */
450 if (vg_tc_current >= 0
451 && vg_tc_current < VG_TC_N_SECTORS
452 && vg_tc[vg_tc_current] != NULL
sewardjc0d8f682002-11-30 00:49:43 +0000453 && vg_tc_used[vg_tc_current] + nBytes <= vg_tc_sector_szB) {
sewardj6c3769f2002-11-29 01:02:45 +0000454 /* Yes. */
455 UChar* p = &(vg_tc[vg_tc_current][ vg_tc_used[vg_tc_current] ]);
456 vg_tc_used[vg_tc_current] += nBytes;
457 return p;
458 }
459
460 /* Perhaps we can bring a new sector into use, for the first
461 time. */
462 vg_tc_current = maybe_commission_sector();
463 if (vg_tc_current >= 0 && vg_tc_current < VG_TC_N_SECTORS)
464 return allocate(nBytes);
465
466 /* That didn't work. We'll have to dump the oldest. We take the
467 opportunity to dump the N oldest at once. */
468 for (i = 0; i < 1; i++)
469 (void)discard_oldest_sector();
470
471 rebuild_TT();
472 vg_tc_current = maybe_commission_sector();
473 vg_assert(vg_tc_current >= 0 && vg_tc_current < VG_TC_N_SECTORS);
474# ifdef DEBUG_TRANSTAB
nethercote92e7b7f2004-08-07 17:52:25 +0000475 VG_(sanity_check_tt_tc)();
sewardj6c3769f2002-11-29 01:02:45 +0000476# endif
477
478 return allocate(nBytes);
479}
sewardjde4a1d02002-03-22 01:27:54 +0000480
481
482/* Just so these counts can be queried without making them globally
483 visible. */
484void VG_(get_tt_tc_used) ( UInt* tt_used, UInt* tc_used )
485{
sewardj6c3769f2002-11-29 01:02:45 +0000486 Int s;
sewardjde4a1d02002-03-22 01:27:54 +0000487 *tt_used = vg_tt_used;
sewardj6c3769f2002-11-29 01:02:45 +0000488 *tc_used = 0;
489 for (s = 0; s < VG_TC_N_SECTORS; s++)
490 *tc_used += vg_tc_used[s];
sewardjde4a1d02002-03-22 01:27:54 +0000491}
492
493
494/* Do a sanity check on TT/TC.
495*/
nethercote92e7b7f2004-08-07 17:52:25 +0000496void VG_(sanity_check_tt_tc) ( void )
sewardjde4a1d02002-03-22 01:27:54 +0000497{
sewardjc0d8f682002-11-30 00:49:43 +0000498 Int i, s;
499 TTEntry* tte;
500 TCEntry* tce;
501 /* Checks:
502 - Each TT entry points to a valid and corresponding TC entry.
sewardj6c3769f2002-11-29 01:02:45 +0000503 */
504 for (i = 0; i < VG_TT_SIZE; i++) {
505 tte = &vg_tt[i];
506 /* empty slots are harmless. */
507 if (tte->orig_addr == VG_TTE_EMPTY) continue;
508 /* all others should agree with the TC entry. */
509 tce = tte->tcentry;
510 vg_assert(IS_ALIGNED4_ADDR(tce));
511 /* does this point into a valid TC sector? */
512 for (s = 0; s < VG_TC_N_SECTORS; s++)
513 if (vg_tc[s] != NULL
514 && ((Addr)tce) >= (Addr)&vg_tc[s][0]
515 && ((Addr)tce) < (Addr)&vg_tc[s][ vg_tc_used[s] ])
516 break;
517 vg_assert(s < VG_TC_N_SECTORS);
518 /* It should agree with the TC entry on the orig_addr. This may
519 be VG_TTE_DELETED, or a real orig addr. */
520 vg_assert(tte->orig_addr == tce->orig_addr);
521 }
sewardjde4a1d02002-03-22 01:27:54 +0000522}
523
nethercote92e7b7f2004-08-07 17:52:25 +0000524static __inline__ Int safe_idiv(Int a, Int b)
525{
526 return (b == 0 ? 0 : a / b);
527}
528
529void VG_(print_tt_tc_stats)(void)
530{
531 VG_(message)(Vg_DebugMsg,
532 " TT/TC: %d tc sectors discarded.",
533 n_tc_discards );
534 VG_(message)(Vg_DebugMsg,
535 " %d tt_fast misses.",
536 n_tt_fast_misses);
537 VG_(message)(Vg_DebugMsg,
538 "translate: new %d (%d -> %d; ratio %d:10)",
539 overall_in_count, overall_in_osize, overall_in_tsize,
540 safe_idiv(10*overall_in_tsize, overall_in_osize));
541 VG_(message)(Vg_DebugMsg,
542 " discard %d (%d -> %d; ratio %d:10).",
543 overall_out_count, overall_out_osize, overall_out_tsize,
544 safe_idiv(10*overall_out_tsize, overall_out_osize));
545}
546
547Int VG_(get_bbs_translated) ( void )
548{
549 return overall_in_count;
550}
sewardjde4a1d02002-03-22 01:27:54 +0000551
552/* Add this already-filled-in entry to the TT. Assumes that the
553 relevant code chunk has been placed in TC, along with a dummy back
sewardjc0d8f682002-11-30 00:49:43 +0000554 pointer, which is inserted here.
sewardjde4a1d02002-03-22 01:27:54 +0000555*/
sewardjc0d8f682002-11-30 00:49:43 +0000556void VG_(add_to_trans_tab) ( Addr orig_addr, Int orig_size,
sewardj8b635a42004-11-22 19:01:47 +0000557 Addr trans_addr, Int trans_size )
sewardjde4a1d02002-03-22 01:27:54 +0000558{
sewardj6c3769f2002-11-29 01:02:45 +0000559 Int i, nBytes, trans_size_aligned;
560 TCEntry* tce;
sewardjde4a1d02002-03-22 01:27:54 +0000561 /*
562 VG_(printf)("add_to_trans_tab(%d) %x %d %x %d\n",
563 vg_tt_used, tte->orig_addr, tte->orig_size,
564 tte->trans_addr, tte->trans_size);
565 */
sewardj6c3769f2002-11-29 01:02:45 +0000566
sewardj22854b92002-11-30 14:00:47 +0000567 vg_assert(offsetof(TCEntry, payload) == VG_CODE_OFFSET);
568
sewardj6c3769f2002-11-29 01:02:45 +0000569 /* figure out how many bytes we require. */
sewardj22854b92002-11-30 14:00:47 +0000570 nBytes = CODE_ALIGN(trans_size + sizeof(TCEntry));
571 trans_size_aligned = nBytes-sizeof(TCEntry);
572 vg_assert(IS_ALIGNED(nBytes));
sewardj6c3769f2002-11-29 01:02:45 +0000573
574 tce = (TCEntry*)allocate(nBytes);
sewardj78210aa2002-12-01 02:55:46 +0000575 /*
576 VG_(printf)("allocate returned %p (code start %p)\n",
577 tce, &tce->payload[0]);
578 */
sewardjc0d8f682002-11-30 00:49:43 +0000579 vg_assert(vg_tc_current >= 0 && vg_tc_current < VG_TC_N_SECTORS);
580
sewardj6c3769f2002-11-29 01:02:45 +0000581 tce->orig_addr = orig_addr;
582 tce->orig_size = (UShort)orig_size; /* what's the point of storing this? */
583 tce->trans_size = (UShort)trans_size_aligned;
584 for (i = 0; i < trans_size; i++) {
585 tce->payload[i] = ((UChar*)trans_addr)[i];
sewardjde4a1d02002-03-22 01:27:54 +0000586 }
sewardj22854b92002-11-30 14:00:47 +0000587
sewardj8b635a42004-11-22 19:01:47 +0000588 //unchain_tce(tce);
sewardj6c3769f2002-11-29 01:02:45 +0000589 add_tt_entry(tce);
sewardjc0d8f682002-11-30 00:49:43 +0000590
591 /* Update stats. */
nethercote92e7b7f2004-08-07 17:52:25 +0000592 overall_in_count ++;
593 overall_in_osize += orig_size;
594 overall_in_tsize += trans_size;
sewardjc0d8f682002-11-30 00:49:43 +0000595
596 vg_tc_stats_count[vg_tc_current] ++;
597 vg_tc_stats_osize[vg_tc_current] += orig_size;
598 vg_tc_stats_tsize[vg_tc_current] += trans_size;
sewardjde4a1d02002-03-22 01:27:54 +0000599}
600
601
602/* Find the translation address for a given (original) code address.
603 If found, update VG_(tt_fast) so subsequent lookups are fast. If
604 no translation can be found, return zero. This routine is (the
605 only one) called from vg_run_innerloop. */
606Addr VG_(search_transtab) ( Addr original_addr )
607{
608 TTEntry* tte;
609 VGP_PUSHCC(VgpSlowFindT);
sewardj6c3769f2002-11-29 01:02:45 +0000610 tte = search_tt ( original_addr );
sewardjde4a1d02002-03-22 01:27:54 +0000611 if (tte == NULL) {
612 /* We didn't find it. vg_run_innerloop will have to request a
613 translation. */
njn25e49d8e72002-09-23 09:36:25 +0000614 VGP_POPCC(VgpSlowFindT);
sewardjde4a1d02002-03-22 01:27:54 +0000615 return (Addr)0;
616 } else {
sewardj1f086892002-12-15 01:47:05 +0000617 /* Found it. Put the search result into the fast cache now. */
sewardjde4a1d02002-03-22 01:27:54 +0000618 UInt cno = (UInt)original_addr & VG_TT_FAST_MASK;
sewardj6c3769f2002-11-29 01:02:45 +0000619 VG_(tt_fast)[cno] = (Addr)(tte->tcentry);
nethercote92e7b7f2004-08-07 17:52:25 +0000620 n_tt_fast_misses++;
njn25e49d8e72002-09-23 09:36:25 +0000621 VGP_POPCC(VgpSlowFindT);
sewardj6c3769f2002-11-29 01:02:45 +0000622 return (Addr)&(tte->tcentry->payload[0]);
sewardjde4a1d02002-03-22 01:27:54 +0000623 }
624}
625
626
sewardj18d75132002-05-16 11:06:21 +0000627/* Invalidate translations of original code [start .. start + range - 1].
sewardj97ad5522003-05-04 12:32:56 +0000628 This is slow, so you *really* don't want to call it very often.
629 Set 'unchain_blocks' if the translation being invalidated may be chained
630 to by other local blocks (which are NOT being discarded).
sewardjde4a1d02002-03-22 01:27:54 +0000631*/
sewardj97ad5522003-05-04 12:32:56 +0000632void VG_(invalidate_translations) ( Addr start, UInt range, Bool unchain_blocks )
sewardjde4a1d02002-03-22 01:27:54 +0000633{
sewardj6c3769f2002-11-29 01:02:45 +0000634 Addr i_start, i_end, o_start, o_end;
635 UInt out_count, out_osize, out_tsize;
sewardj8b635a42004-11-22 19:01:47 +0000636 Int i; //, j;
sewardj6c3769f2002-11-29 01:02:45 +0000637 TCEntry* tce;
sewardj18d75132002-05-16 11:06:21 +0000638# ifdef DEBUG_TRANSTAB
nethercote92e7b7f2004-08-07 17:52:25 +0000639 VG_(sanity_check_tt_tc)();
sewardjde4a1d02002-03-22 01:27:54 +0000640# endif
sewardj18d75132002-05-16 11:06:21 +0000641 i_start = start;
642 i_end = start + range - 1;
643 out_count = out_osize = out_tsize = 0;
sewardjde4a1d02002-03-22 01:27:54 +0000644
sewardj18d75132002-05-16 11:06:21 +0000645 for (i = 0; i < VG_TT_SIZE; i++) {
646 if (vg_tt[i].orig_addr == VG_TTE_EMPTY
647 || vg_tt[i].orig_addr == VG_TTE_DELETED) continue;
sewardj6c3769f2002-11-29 01:02:45 +0000648 tce = vg_tt[i].tcentry;
649 o_start = tce->orig_addr;
650 o_end = o_start + tce->trans_size - 1;
sewardj18d75132002-05-16 11:06:21 +0000651 if (o_end < i_start || o_start > i_end)
652 continue;
njn25e49d8e72002-09-23 09:36:25 +0000653
654 if (VG_(needs).basic_block_discards)
njn26f02512004-11-22 18:33:15 +0000655 TL_(discard_basic_block_info)( tce->orig_addr,
sewardj6c3769f2002-11-29 01:02:45 +0000656 tce->orig_size );
njn25e49d8e72002-09-23 09:36:25 +0000657
sewardj18d75132002-05-16 11:06:21 +0000658 vg_tt[i].orig_addr = VG_TTE_DELETED;
sewardj6c3769f2002-11-29 01:02:45 +0000659 tce->orig_addr = VG_TTE_DELETED;
sewardj97ad5522003-05-04 12:32:56 +0000660
sewardj8b635a42004-11-22 19:01:47 +0000661 // if (unchain_blocks) {
662 // /* make sure no other blocks chain to the one we just discarded */
663 // for(j = 0; j < VG_TC_N_SECTORS; j++) {
664 // if (vg_tc[j] != NULL)
665 // unchain_sector(j, (Addr)tce->payload, tce->trans_size);
666 // }
667 // }
sewardj97ad5522003-05-04 12:32:56 +0000668
nethercote92e7b7f2004-08-07 17:52:25 +0000669 overall_out_count ++;
670 overall_out_osize += tce->orig_size;
671 overall_out_tsize += tce->trans_size;
sewardj18d75132002-05-16 11:06:21 +0000672 out_count ++;
sewardj6c3769f2002-11-29 01:02:45 +0000673 out_osize += tce->orig_size;
674 out_tsize += tce->trans_size;
sewardjde4a1d02002-03-22 01:27:54 +0000675 }
sewardjde4a1d02002-03-22 01:27:54 +0000676
sewardj18d75132002-05-16 11:06:21 +0000677 if (out_count > 0) {
sewardj6c3769f2002-11-29 01:02:45 +0000678 vg_invalidate_tt_fast();
nethercote92e7b7f2004-08-07 17:52:25 +0000679 VG_(sanity_check_tt_tc)();
sewardj18d75132002-05-16 11:06:21 +0000680# ifdef DEBUG_TRANSTAB
681 { Addr aa;
682 for (aa = i_start; aa <= i_end; aa++)
sewardj6c3769f2002-11-29 01:02:45 +0000683 vg_assert(search_tt ( aa ) == NULL);
sewardjde4a1d02002-03-22 01:27:54 +0000684 }
sewardj18d75132002-05-16 11:06:21 +0000685# endif
sewardjde4a1d02002-03-22 01:27:54 +0000686 }
687
sewardjc0d8f682002-11-30 00:49:43 +0000688 if (VG_(clo_verbosity) > 2)
sewardj18d75132002-05-16 11:06:21 +0000689 VG_(message)(Vg_UserMsg,
690 "discard %d (%d -> %d) translations in range %p .. %p",
691 out_count, out_osize, out_tsize, i_start, i_end );
sewardjde4a1d02002-03-22 01:27:54 +0000692}
693
694
695/*------------------------------------------------------------*/
696/*--- Initialisation. ---*/
697/*------------------------------------------------------------*/
698
sewardjc0d8f682002-11-30 00:49:43 +0000699void VG_(init_tt_tc) ( void )
700{
701 Int s;
702
sewardj22854b92002-11-30 14:00:47 +0000703 /* Otherwise we wind up with non-32-bit-aligned code in
704 TCEntries. */
705 vg_assert((VG_MAX_JUMPS % 2) == 0);
706
nethercote463c63c2004-10-26 11:18:32 +0000707 // Otherwise lots of things go wrong...
708 vg_assert(VG_CODE_OFFSET == sizeof(TCEntry));
709
sewardjc0d8f682002-11-30 00:49:43 +0000710 /* Figure out how big each sector should be. */
711 vg_tc_sector_szB
712 = (VG_TT_LIMIT /* max TT entries we expect */
sewardj78210aa2002-12-01 02:55:46 +0000713 * (VG_(details).avg_translation_sizeB
714 + sizeof(TCEntry)
715 + (CODE_ALIGNMENT/2) /* avg alignment loss */)
716 )
sewardjc0d8f682002-11-30 00:49:43 +0000717 / VG_TC_N_SECTORS;
718 /* Ensure the calculated value is not way crazy. */
719 vg_assert(vg_tc_sector_szB >= 200000);
jseward3f409e82003-12-14 14:25:19 +0000720 vg_assert(vg_tc_sector_szB <= 8000000);
sewardjc0d8f682002-11-30 00:49:43 +0000721
722 for (s = 0; s < VG_TC_N_SECTORS; s++) {
723 vg_tc[s] = NULL;
724 vg_tc_used[s] = 0;
725 vg_tc_age[s] = 0;
726 vg_tc_stats_count[s] = 0;
727 vg_tc_stats_osize[s] = 0;
728 vg_tc_stats_tsize[s] = 0;
729 }
730 vg_tc_current = 0;
731
732 vg_tt = VG_(get_memory_from_mmap) ( VG_TT_SIZE * sizeof(TTEntry),
733 "trans-table" );
734 /* The main translation table is empty. */
735 initialise_tt();
736
737 if (VG_(clo_verbosity) > 2) {
738 VG_(message)(Vg_DebugMsg,
739 "Translation Cache: using %d sectors of %d bytes each",
740 VG_TC_N_SECTORS, vg_tc_sector_szB );
741 VG_(message)(Vg_DebugMsg,
742 "Translation Table: %d total entries, max occupancy %d (%d%%)",
743 VG_TT_SIZE, VG_TT_LIMIT, VG_TT_LIMIT_PERCENT );
744 }
745
746# ifdef DEBUG_TRANSTAB
nethercote92e7b7f2004-08-07 17:52:25 +0000747 VG_(sanity_check_tt_tc)();
sewardjc0d8f682002-11-30 00:49:43 +0000748# endif
749}
750
sewardjde4a1d02002-03-22 01:27:54 +0000751/*--------------------------------------------------------------------*/
752/*--- end vg_transtab.c ---*/
753/*--------------------------------------------------------------------*/