blob: d0f0eb1e2f06def4317de0db49d480e55774fa8e [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/*
8 This file is part of Valgrind, an x86 protected-mode emulator
9 designed for debugging and profiling binaries on x86-Unixes.
10
11 Copyright (C) 2000-2002 Julian Seward
12 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
29 The GNU General Public License is contained in the file LICENSE.
30*/
31
32#include "vg_include.h"
33#include "vg_constants.h"
34
35
36/*------------------------------------------------------------*/
37/*--- Management of the LRU-based translation table+cache. ---*/
38/*------------------------------------------------------------*/
39
40/* These sizes were set up so as to be able to debug large KDE 3
41 applications (are there any small ones?) without excessive amounts
42 of code retranslation. */
43
44/* Size of the translation cache, in bytes. */
sewardjec055642002-05-03 18:59:21 +000045#define VG_TC_SIZE /*16000000*/ 32000000 /*40000000*/
sewardjde4a1d02002-03-22 01:27:54 +000046
47/* Do a LRU pass when the translation cache becomes this full. */
sewardjec055642002-05-03 18:59:21 +000048#define VG_TC_LIMIT_PERCENT 98
sewardjde4a1d02002-03-22 01:27:54 +000049
50/* When doing an LRU pass, reduce TC fullness to this level. */
51#define VG_TC_TARGET_PERCENT 85
52
53/* Number of entries in the translation table. This must be a prime
54 number in order to make the hashing work properly. */
sewardjec055642002-05-03 18:59:21 +000055#define VG_TT_SIZE /*100129*/ 200191 /*250829*/
sewardjde4a1d02002-03-22 01:27:54 +000056
57/* Do an LRU pass when the translation table becomes this full. */
58#define VG_TT_LIMIT_PERCENT /*67*/ 80
59
60/* When doing an LRU pass, reduce TT fullness to this level. */
61#define VG_TT_TARGET_PERCENT /*60*/ 70
62
63/* The number of age steps we track. 0 means the current epoch,
64 N_EPOCHS-1 means used the epoch N_EPOCHS-1 or more ago. */
sewardjac680282002-03-25 02:02:52 +000065#define VG_N_EPOCHS /*2000*/ /*4000*/ 20000
sewardjde4a1d02002-03-22 01:27:54 +000066
67/* This TT entry is empty. */
68#define VG_TTE_EMPTY ((Addr)1)
69/* This TT entry has been deleted. */
70#define VG_TTE_DELETED ((Addr)3)
71
72/* The TC. This used to be statically allocated, but that forces many
73 SecMap arrays to be pointlessly allocated at startup, bloating the
74 process size by about 22M and making startup slow. So now we
75 dynamically allocate it at startup time.
76 was: static UChar vg_tc[VG_TC_SIZE];
77*/
78static UChar* vg_tc = NULL;
79
80/* Count of bytes used in the TC. */
81static Int vg_tc_used = 0;
82
83/* The TT. Like TC, for the same reason, is dynamically allocated at
84 startup.
85 was: static TTEntry vg_tt[VG_TT_SIZE];
86*/
87static TTEntry* vg_tt = NULL;
88
89/* Count of non-empty, non-deleted TT entries. */
90static Int vg_tt_used = 0;
91
92/* Fast helper for the TT. A direct-mapped cache which holds a
93 pointer to a TT entry which may or may not be the correct one, but
94 which we hope usually is. This array is referred to directly from
95 vg_dispatch.S. */
96Addr VG_(tt_fast)[VG_TT_FAST_SIZE];
97
98/* For reading/writing the misaligned TT-index word at immediately
99 preceding every translation in TC. */
100#define VG_READ_MISALIGNED_WORD(aaa) (*((UInt*)(aaa)))
101#define VG_WRITE_MISALIGNED_WORD(aaa,vvv) *((UInt*)(aaa)) = ((UInt)(vvv))
102
103/* Used for figuring out an age threshold for translations. */
104static Int vg_bytes_in_epoch[VG_N_EPOCHS];
105static Int vg_entries_in_epoch[VG_N_EPOCHS];
106
107
108/* Just so these counts can be queried without making them globally
109 visible. */
110void VG_(get_tt_tc_used) ( UInt* tt_used, UInt* tc_used )
111{
112 *tt_used = vg_tt_used;
113 *tc_used = vg_tc_used;
114}
115
116
117/* Do the LRU thing on TT/TC, clearing them back to the target limits
118 if they are over the threshold limits.
119*/
120void VG_(maybe_do_lru_pass) ( void )
121{
122 Int i, j, r, w, thresh, ttno;
123 TTEntry* tte;
124
sewardj215e7f02002-05-02 03:47:01 +0000125 const Int tc_limit = (Int)(((double)VG_TC_SIZE * (double)VG_TC_LIMIT_PERCENT)
126 / (double)100.0);
127 const Int tt_limit = (Int)(((double)VG_TT_SIZE * (double)VG_TT_LIMIT_PERCENT)
128 / (double)100.0);
129 const Int tc_target = (Int)(((double)VG_TC_SIZE * (double)VG_TC_TARGET_PERCENT)
130 / (double)100.0);
131 const Int tt_target = (Int)(((double)VG_TT_SIZE * (double)VG_TT_TARGET_PERCENT)
132 / (double)100.0);
sewardjde4a1d02002-03-22 01:27:54 +0000133
134 /* Decide quickly if we need to do an LRU pass ? */
135 if (vg_tc_used <= tc_limit && vg_tt_used <= tt_limit)
136 return;
137
138 VGP_PUSHCC(VgpDoLRU);
139 /*
140 VG_(printf)(
141 "limits: tc_limit %d, tt_limit %d, tc_target %d, tt_target %d\n",
142 tc_limit, tt_limit, tc_target, tt_target);
143 */
144
145 if (VG_(clo_verbosity) > 2)
146 VG_(printf)(" pre-LRU: tc %d (target %d), tt %d (target %d)\n",
147 vg_tc_used, tc_target, vg_tt_used, tt_target);
148
149 /* Yes we do. Figure out what threshold age is required in order to
150 shrink both the TC and TT occupancy below TC_TARGET_PERCENT and
151 TT_TARGET_PERCENT respectively. */
152
153 VG_(number_of_lrus)++;
154
155 /* Count the number of TC bytes and TT entries in each epoch. */
156 for (i = 0; i < VG_N_EPOCHS; i++)
157 vg_bytes_in_epoch[i] = vg_entries_in_epoch[i] = 0;
158
159 for (i = 0; i < VG_TT_SIZE; i++) {
160 if (vg_tt[i].orig_addr == VG_TTE_EMPTY ||
161 vg_tt[i].orig_addr == VG_TTE_DELETED) continue;
162 j = vg_tt[i].mru_epoch;
163 vg_assert(j <= VG_(current_epoch));
164 j = VG_(current_epoch) - j;
165 if (j >= VG_N_EPOCHS) j = VG_N_EPOCHS-1;
166 vg_assert(0 <= j && j < VG_N_EPOCHS);
167 /* Greater j now means older. */
168 vg_entries_in_epoch[j]++;
169 vg_bytes_in_epoch[j] += 4+vg_tt[i].trans_size;
170 }
171
172 /*
173 for (i = 0; i < VG_N_EPOCHS; i++)
174 VG_(printf)("epoch %d: ents %d, bytes %d\n",
175 i, vg_entries_in_epoch[i], vg_bytes_in_epoch[i]);
176 */
177
178 /* Cumulatise. Make vg_{bytes,entries}_in_epoch[n] contain the
179 counts for itself and all younger epochs. */
180 for (i = 1; i < VG_N_EPOCHS; i++) {
181 vg_entries_in_epoch[i] += vg_entries_in_epoch[i-1];
182 vg_bytes_in_epoch[i] += vg_bytes_in_epoch[i-1];
183 }
184
185 for (thresh = 0; thresh < VG_N_EPOCHS; thresh++) {
186 if (vg_entries_in_epoch[thresh] > tt_target
187 || vg_bytes_in_epoch[thresh] >= tc_target)
188 break;
189 }
190
191 if (VG_(clo_verbosity) > 2)
192 VG_(printf)(
193 " LRU: discard translations %d or more epochs since last use\n",
194 thresh
195 );
196
197 thresh = VG_(current_epoch) - thresh;
198
199 /* Ok, so we will hit our targets if we retain all entries most
200 recently used at most thresh epochs ago. Traverse the TT and
201 mark such entries as deleted. */
202 for (i = 0; i < VG_TT_SIZE; i++) {
203 if (vg_tt[i].orig_addr == VG_TTE_EMPTY ||
204 vg_tt[i].orig_addr == VG_TTE_DELETED) continue;
205 if (vg_tt[i].mru_epoch <= thresh) {
206 vg_tt[i].orig_addr = VG_TTE_DELETED;
207 vg_tt_used--;
208 VG_(this_epoch_out_count) ++;
209 VG_(this_epoch_out_osize) += vg_tt[i].orig_size;
210 VG_(this_epoch_out_tsize) += vg_tt[i].trans_size;
211 VG_(overall_out_count) ++;
212 VG_(overall_out_osize) += vg_tt[i].orig_size;
213 VG_(overall_out_tsize) += vg_tt[i].trans_size;
214 }
215 }
216
217 vg_assert(vg_tt_used >= 0);
218 vg_assert(vg_tt_used <= tt_target);
219
220 /* Now compact the TC, sliding live entries downwards to fill spaces
221 left by deleted entries. In this loop, r is the offset in TC of
222 the current translation under consideration, and w is the next
223 allocation point. */
224 r = w = 0;
225 while (True) {
226 if (r >= vg_tc_used) break;
227 /* The first four bytes of every translation contain the index
228 of its TT entry. The TT entry's .trans_addr field points at
229 the start of the code proper, not at this 4-byte index, so
230 that we don't constantly have to keep adding 4 in the main
231 lookup/dispatch loop. */
232 ttno = VG_READ_MISALIGNED_WORD(&vg_tc[r]);
233 vg_assert(ttno >= 0 && ttno < VG_TT_SIZE);
234 tte = & vg_tt[ ttno ];
235 vg_assert(tte->orig_addr != VG_TTE_EMPTY);
236 if (tte->orig_addr != VG_TTE_DELETED) {
237 /* We want to keep this one alive. */
238 /* Sanity check the pointer back to TC. */
239 vg_assert(tte->trans_addr == (Addr)&vg_tc[r+4]);
240 for (i = 0; i < 4+tte->trans_size; i++)
241 vg_tc[w+i] = vg_tc[r+i];
242 tte->trans_addr = (Addr)&vg_tc[w+4];
243 w += 4+tte->trans_size;
244 }
245 r += 4+tte->trans_size;
246 }
247 /* should have traversed an exact number of translations, with no
248 slop at the end. */
249 vg_assert(w <= r);
250 vg_assert(r == vg_tc_used);
251 vg_assert(w <= r);
252 vg_assert(w <= tc_target);
253 vg_tc_used = w;
254
255 /* Invalidate the fast cache, since it is now out of date. It will get
256 reconstructed incrementally when the client resumes. */
257 VG_(invalidate_tt_fast)();
258
259 if (VG_(clo_verbosity) > 2)
260 VG_(printf)("post-LRU: tc %d (target %d), tt %d (target %d)\n",
261 vg_tc_used, tc_target, vg_tt_used, tt_target);
262
263 if (VG_(clo_verbosity) > 1)
264 VG_(message)(Vg_UserMsg,
265 "epoch %d (bb %luk): thresh %d, "
266 "out %d (%dk -> %dk), new TT %d, TC %dk",
267 VG_(current_epoch),
268 VG_(bbs_done) / 1000,
269 VG_(current_epoch) - thresh,
270 VG_(this_epoch_out_count),
271 VG_(this_epoch_out_osize) / 1000,
272 VG_(this_epoch_out_tsize) / 1000,
273 vg_tt_used, vg_tc_used / 1000
274 );
275
276 /* Reconstruct the SMC detection structures. */
277
278 VGP_POPCC;
279}
280
281
282/* Do a sanity check on TT/TC.
283*/
284void VG_(sanity_check_tc_tt) ( void )
285{
286 Int i, counted_entries, counted_bytes;
287 TTEntry* tte;
288 counted_entries = 0;
289 counted_bytes = 0;
290 for (i = 0; i < VG_TT_SIZE; i++) {
291 tte = &vg_tt[i];
292 if (tte->orig_addr == VG_TTE_EMPTY) continue;
293 if (tte->orig_addr == VG_TTE_DELETED) continue;
294 vg_assert(tte->mru_epoch >= 0);
295 vg_assert(tte->mru_epoch <= VG_(current_epoch));
296 counted_entries++;
297 counted_bytes += 4+tte->trans_size;
298 vg_assert(tte->trans_addr >= (Addr)&vg_tc[4]);
299 vg_assert(tte->trans_addr < (Addr)&vg_tc[vg_tc_used]);
300 vg_assert(VG_READ_MISALIGNED_WORD(tte->trans_addr-4) == i);
301 }
302 vg_assert(counted_entries == vg_tt_used);
303 vg_assert(counted_bytes == vg_tc_used);
304}
305
306
307/* Add this already-filled-in entry to the TT. Assumes that the
308 relevant code chunk has been placed in TC, along with a dummy back
309 pointer, which is inserted here.
310*/
311extern void VG_(add_to_trans_tab) ( TTEntry* tte )
312{
313 Int i;
314 /*
315 VG_(printf)("add_to_trans_tab(%d) %x %d %x %d\n",
316 vg_tt_used, tte->orig_addr, tte->orig_size,
317 tte->trans_addr, tte->trans_size);
318 */
319 vg_assert(tte->orig_addr != VG_TTE_DELETED
320 && tte->orig_addr != VG_TTE_EMPTY);
321 /* Hash to get initial probe point. */
322 i = ((UInt)(tte->orig_addr)) % VG_TT_SIZE;
323 while (True) {
324 if (vg_tt[i].orig_addr == tte->orig_addr)
325 VG_(panic)("add_to_trans_tab: duplicate");
326 if (vg_tt[i].orig_addr == VG_TTE_DELETED ||
327 vg_tt[i].orig_addr == VG_TTE_EMPTY) {
328 /* Put it here, and set the back pointer. */
329 vg_tt[i] = *tte;
330 VG_WRITE_MISALIGNED_WORD(tte->trans_addr-4, i);
331 vg_tt_used++;
332 return;
333 }
334 i++;
335 if (i == VG_TT_SIZE) i = 0;
336 }
337}
338
339
340/* Copy a new translation's code into TC, leaving a 4-byte hole for
341 the back pointer, and returning a pointer to the code proper (not
342 the hole) in TC.
343*/
344Addr VG_(copy_to_transcache) ( Addr trans_addr, Int trans_size )
345{
346 Int i;
347 Addr ret_addr;
348 if (4+trans_size > VG_TC_SIZE-vg_tc_used)
349 VG_(panic)("copy_to_transcache: not enough free space?!");
350 /* Leave a hole for the back pointer to the TT entry. */
351 vg_tc_used += 4;
352 ret_addr = (Addr)&vg_tc[vg_tc_used];
353 for (i = 0; i < trans_size; i++)
354 vg_tc[vg_tc_used+i] = ((UChar*)trans_addr)[i];
355 vg_tc_used += trans_size;
356 return ret_addr;
357}
358
359
360/* Invalidate the tt_fast cache, for whatever reason. Tricky. We
361 have to find a TTE_EMPTY slot to point all entries at. */
362void VG_(invalidate_tt_fast)( void )
363{
364 Int i, j;
365 for (i = 0; i < VG_TT_SIZE && vg_tt[i].orig_addr != VG_TTE_EMPTY; i++)
366 ;
367 vg_assert(i < VG_TT_SIZE
368 && vg_tt[i].orig_addr == VG_TTE_EMPTY);
369 for (j = 0; j < VG_TT_FAST_SIZE; j++)
370 VG_(tt_fast)[j] = (Addr)&vg_tt[i];
371}
372
373
374/* Search TT to find the translated address of the supplied original,
375 or NULL if not found. This routine is used when we miss in
376 VG_(tt_fast).
377*/
378static __inline__ TTEntry* search_trans_table ( Addr orig_addr )
379{
380 //static Int queries = 0;
381 //static Int probes = 0;
382 Int i;
383 /* Hash to get initial probe point. */
384 // if (queries == 10000) {
385 // VG_(printf)("%d queries, %d probes\n", queries, probes);
386 // queries = probes = 0;
387 //}
388 //queries++;
389 i = ((UInt)orig_addr) % VG_TT_SIZE;
390 while (True) {
391 //probes++;
392 if (vg_tt[i].orig_addr == orig_addr)
393 return &vg_tt[i];
394 if (vg_tt[i].orig_addr == VG_TTE_EMPTY)
395 return NULL;
396 i++;
397 if (i == VG_TT_SIZE) i = 0;
398 }
399}
400
401
402/* Find the translation address for a given (original) code address.
403 If found, update VG_(tt_fast) so subsequent lookups are fast. If
404 no translation can be found, return zero. This routine is (the
405 only one) called from vg_run_innerloop. */
406Addr VG_(search_transtab) ( Addr original_addr )
407{
408 TTEntry* tte;
409 VGP_PUSHCC(VgpSlowFindT);
410 tte = search_trans_table ( original_addr );
411 if (tte == NULL) {
412 /* We didn't find it. vg_run_innerloop will have to request a
413 translation. */
414 VGP_POPCC;
415 return (Addr)0;
416 } else {
417 /* Found it. Put the search result into the fast cache now.
418 Also set the mru_epoch to mark this translation as used. */
419 UInt cno = (UInt)original_addr & VG_TT_FAST_MASK;
420 VG_(tt_fast)[cno] = (Addr)tte;
421 VG_(tt_fast_misses)++;
422 tte->mru_epoch = VG_(current_epoch);
423 VGP_POPCC;
424 return tte->trans_addr;
425 }
426}
427
428
429/*------------------------------------------------------------*/
430/*--- Detecting and handling self-modifying code. ---*/
431/*------------------------------------------------------------*/
432
433/* This mechanism uses two data structures:
434
435 vg_oldmap -- array[64k] of Bool, which approximately records
436 parts of the address space corresponding to code for which
437 a translation exists in the translation table. vg_oldmap is
438 consulted at each write, to determine whether that write might
439 be writing a code address; if so, the program is stopped at
440 the next jump, and the corresponding translations are invalidated.
441
442 Precise semantics: vg_oldmap[(a >> 8) & 0xFFFF] is true for all
443 addresses a containing a code byte which has been translated. So
444 it acts kind-of like a direct-mapped cache with 64k entries.
445
446 The second structure is vg_CAW, a small array of addresses at which
447 vg_oldmap indicates a code write may have happened. This is
448 (effectively) checked at each control transfer (jump), so that
449 translations can be discarded before going on. An array is
450 somewhat overkill, since it strikes me as very unlikely that a
451 single basic block will do more than one code write. Nevertheless
452 ...
453
454 ToDo: make this comment up-to-date.
455*/
456
457
458/* Definitions for the self-modifying-code detection cache, intended
459 as a fast check which clears the vast majority of writes. */
460
461#define VG_SMC_CACHE_HASH(aaa) \
462 ((((UInt)a) >> VG_SMC_CACHE_SHIFT) & VG_SMC_CACHE_MASK)
463
464Bool VG_(smc_cache)[VG_SMC_CACHE_SIZE];
465
466
467/* Definitions for the fallback mechanism, which, more slowly,
468 provides a precise record of which words in the address space
469 belong to original code. */
470
471typedef struct { UChar chars[2048]; } VgSmcSecondary;
472
473static VgSmcSecondary* vg_smc_primary[65536];
474
475static VgSmcSecondary* vg_smc_new_secondary ( void )
476{
477 Int i;
478 VgSmcSecondary* sec
479 = VG_(malloc) ( VG_AR_PRIVATE, sizeof(VgSmcSecondary) );
480 for (i = 0; i < 2048; i++)
481 sec->chars[i] = 0;
482 return sec;
483}
484
485#define GET_BIT_ARRAY(arr,indx) \
486 (1 & ( ((UChar*)arr)[((UInt)indx) / 8] \
487 >> ( ((UInt)indx) % 8) ) )
488
489#define SET_BIT_ARRAY(arr,indx) \
490 ((UChar*)arr)[((UInt)indx) / 8] |= (1 << ((UInt)indx) % 8)
491
492
493/* Finally, a place to record the original-code-write addresses
494 detected in a basic block. */
495
496#define VG_ORIGWRITES_SIZE 10
497
498static Addr vg_origwrites[VG_ORIGWRITES_SIZE];
499static Int vg_origwrites_used;
500
501
502/* Call here to check a written address. */
503
504void VG_(smc_check4) ( Addr a )
505{
506 UInt bit_index;
507 VgSmcSecondary* smc_secondary;
508
509# if VG_SMC_FASTCHECK_IN_C
510 VG_(smc_total_check4s)++;
511
512 /* Try the fast check first. */
513 if (VG_(smc_cache)[VG_SMC_CACHE_HASH(a)] == False) return;
514# endif
515
516 VG_(smc_cache_passed)++;
517
518 /* Need to do a slow check. */
519 smc_secondary = vg_smc_primary[a >> 16];
520 if (smc_secondary == NULL) return;
521
522 bit_index = (a & 0xFFFF) >> 2;
523 if (GET_BIT_ARRAY(smc_secondary->chars, bit_index) == 0) return;
524
525 VG_(smc_fancy_passed)++;
526
527 /* Detected a Real Live write to code which has been translated.
528 Note it. */
529 if (vg_origwrites_used == VG_ORIGWRITES_SIZE)
530 VG_(panic)("VG_ORIGWRITES_SIZE is too small; "
531 "increase and recompile.");
532 vg_origwrites[vg_origwrites_used] = a;
533 vg_origwrites_used++;
534
535 VG_(message)(Vg_DebugMsg, "self-modifying-code write at %p", a);
536
537 /* Force an exit before the next basic block, so the translation
538 cache can be flushed appropriately. */
sewardj2e93c502002-04-12 11:12:52 +0000539 // VG_(dispatch_ctr_SAVED) = VG_(dispatch_ctr);
540 //VG_(dispatch_ctr) = 1;
541 //VG_(interrupt_reason) = VG_Y_SMC;
sewardjde4a1d02002-03-22 01:27:54 +0000542}
543
544
545/* Mark an address range as containing an original translation,
546 updating both the fast-check cache and the slow-but-correct data
547 structure.
548*/
549void VG_(smc_mark_original) ( Addr orig_addr, Int orig_size )
550{
551 Addr a;
552 VgSmcSecondary* smc_secondary;
553 UInt bit_index;
554
555 for (a = orig_addr; a < orig_addr+orig_size; a++) {
556
557 VG_(smc_cache)[VG_SMC_CACHE_HASH(a)] = True;
558
559 smc_secondary = vg_smc_primary[a >> 16];
560 if (smc_secondary == NULL)
561 smc_secondary =
562 vg_smc_primary[a >> 16] = vg_smc_new_secondary();
563
564 bit_index = (a & 0xFFFF) >> 2;
565 SET_BIT_ARRAY(smc_secondary->chars, bit_index);
566 }
567}
568
569
570/* Discard any translations whose original code overlaps with the
571 range w_addr .. w_addr+3 inclusive.
572*/
573__attribute__ ((unused))
574static void discard_translations_bracketing ( Addr w_addr )
575{
576# if 0
577 Int i, rd, wr;
578 Addr o_start, o_end;
579 TTEntry* tt;
580
581 for (i = 0; i < VG_TRANSTAB_SLOW_SIZE; i++) {
582 tt = vg_transtab[i];
583 wr = 0;
584 for (rd = 0; rd < vg_transtab_used[i]; rd++) {
585 o_start = tt[rd].orig_addr;
586 o_end = o_start + tt[rd].orig_size;
587 if (w_addr > o_end || (w_addr+3) < o_start) {
588 /* No collision possible; keep this translation */
589 VG_(smc_mark_original) ( tt[rd].orig_addr, tt[rd].orig_size );
590 if (wr < rd) vg_transtab[wr] = vg_transtab[rd];
591 wr++;
592 } else {
593 /* Possible collision; discard. */
594 vg_smc_discards++;
595 VG_(message) (Vg_DebugMsg,
596 "discarding translation of %p .. %p",
597 tt[rd].orig_addr,
598 tt[rd].orig_addr + tt[rd].orig_size - 1);
599 VG_(free)((void*)tt[rd].trans_addr);
600 }
601 }
602 vg_transtab_used[i] = wr;
603 }
604# endif
605}
606
607
608/* Top-level function in charge of discarding out-of-date translations
609 following the discovery of a (potential) original-code-write.
610*/
611void VG_(flush_transtab) ( void )
612{
613# if 0
614 Addr w_addr;
615 Int i, j;
616
617 /* We shouldn't be here unless a code write was detected. */
618 vg_assert(vg_origwrites_used > 0);
619
620 /* Instead of incrementally fixing up the translation table cache,
621 just invalidate the whole darn thing. Pray this doesn't happen
622 very often :) */
623 for (i = 0; i < VG_TRANSTAB_CACHE_SIZE; i++)
624 VG_(transtab_cache_orig)[i] =
625 VG_(transtab_cache_trans)[i] = (Addr)0;
626
627 /* Clear out the fast cache; discard_translations_bracketing
628 reconstructs it. */
629 for (i = 0; i < VG_SMC_CACHE_SIZE; i++)
630 VG_(smc_cache)[i] = False;
631
632 /* And also clear the slow-but-correct table. */
633 for (i = 0; i < 65536; i++) {
634 VgSmcSecondary* sec = vg_smc_primary[i];
635 if (sec)
636 for (j = 0; j < 2048; j++)
637 sec->chars[j] = 0;
638 }
639
640 /* This doesn't need to be particularly fast, since we (presumably)
641 don't have to handle particularly frequent writes to code
642 addresses. */
643 while (vg_origwrites_used > 0) {
644 vg_origwrites_used--;
645 w_addr = vg_origwrites[vg_origwrites_used];
646 discard_translations_bracketing ( w_addr );
647 }
648
649 vg_assert(vg_origwrites_used == 0);
650# endif
651}
652
653
654/*------------------------------------------------------------*/
655/*--- Initialisation. ---*/
656/*------------------------------------------------------------*/
657
658void VG_(init_transtab_and_SMC) ( void )
659{
660 Int i;
661
662 /* Allocate the translation table and translation cache. */
663 vg_assert(vg_tc == NULL);
664 vg_tc = VG_(get_memory_from_mmap) ( VG_TC_SIZE * sizeof(UChar) );
665 vg_assert(vg_tc != NULL);
666
667 vg_assert(vg_tt == NULL);
668 vg_tt = VG_(get_memory_from_mmap) ( VG_TT_SIZE * sizeof(TTEntry) );
669 vg_assert(vg_tt != NULL);
670
671 /* The main translation table is empty. */
672 vg_tt_used = 0;
673 for (i = 0; i < VG_TT_SIZE; i++) {
674 vg_tt[i].orig_addr = VG_TTE_EMPTY;
675 }
676
677 /* The translation table's fast cache is empty. Point all entries
678 at the first TT entry, which is, of course, empty. */
679 for (i = 0; i < VG_TT_FAST_SIZE; i++)
680 VG_(tt_fast)[i] = (Addr)(&vg_tt[0]);
681
682 /* No part of the address space has any translations. */
683 for (i = 0; i < 65536; i++)
684 vg_smc_primary[i] = NULL;
685
686 /* ... and the associated fast-check cache reflects this. */
687 for (i = 0; i < VG_SMC_CACHE_SIZE; i++)
688 VG_(smc_cache)[i] = False;
689
690 /* Finally, no original-code-writes have been recorded. */
691 vg_origwrites_used = 0;
692}
693
694/*--------------------------------------------------------------------*/
695/*--- end vg_transtab.c ---*/
696/*--------------------------------------------------------------------*/