1 /*--------------------------------------------------------------------*/ 2 /*--- A pool (memory) allocator that avoids duplicated copies. ---*/ 3 /*--- m_deduppoolalloc.c ---*/ 4 /*--------------------------------------------------------------------*/ 5 /* 6 This file is part of Valgrind, a dynamic binary instrumentation 7 framework. 8 9 Copyright (C) 2014-2015 Philippe Waroquiers philippe.waroquiers (at) skynet.be 10 11 This program is free software; you can redistribute it and/or 12 modify it under the terms of the GNU General Public License as 13 published by the Free Software Foundation; either version 2 of the 14 License, or (at your option) any later version. 15 16 This program is distributed in the hope that it will be useful, but 17 WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 General Public License for more details. 20 21 You should have received a copy of the GNU General Public License 22 along with this program; if not, write to the Free Software 23 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 24 02111-1307, USA. 25 26 The GNU General Public License is contained in the file COPYING. 27 */ 28 29 #include "pub_core_basics.h" 30 #include "pub_core_libcbase.h" 31 #include "pub_core_libcprint.h" 32 #include "pub_core_libcassert.h" 33 #include "pub_core_xarray.h" 34 #include "pub_core_deduppoolalloc.h" /* self */ 35 #include "pub_core_hashtable.h" 36 #include "pub_core_poolalloc.h" 37 #include "pub_core_options.h" 38 #include "pub_core_mallocfree.h" 39 #include "pub_core_debuglog.h" 40 41 struct _DedupPoolAlloc { 42 SizeT poolSzB; /* Minimum size of a pool. */ 43 SizeT fixedSzb; /* If using VG_(allocFixedEltDedupPA), size of elements */ 44 SizeT eltAlign; 45 void* (*alloc_fn)(const HChar*, SizeT); /* pool allocator */ 46 const HChar* cc; /* pool allocator's cost centre */ 47 void (*free_fn)(void*); /* pool allocator's deallocation function */ 48 /* XArray of void* (pointers to pools). The pools themselves. 49 Each element is a pointer to a block of size at least PoolSzB bytes. 50 The last block might be smaller due to a call to shrink_block. */ 51 XArray *pools; 52 53 /* hash table of pool elements, used to dedup. 54 If NULL, it means the DedupPoolAlloc is frozen. */ 55 VgHashTable *ht_elements; 56 57 /* Hash table nodes of pool_elements are allocated with a pool, to 58 decrease memory overhead during insertion in the DedupPoolAlloc. */ 59 PoolAlloc *ht_node_pa; 60 61 UChar *curpool; /* last allocated pool. */ 62 UChar *curpool_free; /* Pos in current pool to allocate next elt. 63 always aligned on eltAlign. */ 64 UChar *curpool_limit; /* Last pos in current pool. */ 65 /* Note that for a fixed size pool, we only have a single pool to allow 66 simple/fast indexing. This single pool is grown, which might change 67 the address of the already allocated elements. */ 68 69 /* Total nr of alloc calls, resulting in (we hope) a lot less 70 real (dedup) elements. */ 71 ULong nr_alloc_calls; 72 }; 73 74 typedef 75 struct _ht_node { 76 struct _ht_node *next; // Read/Write by hashtable (pub_tool_hashtable.h) 77 UWord key; // Read by hashtable (pub_tool_hashtable.h) 78 SizeT eltSzB; 79 const void *elt; 80 } 81 ht_node; 82 83 DedupPoolAlloc* VG_(newDedupPA) ( SizeT poolSzB, 84 SizeT eltAlign, 85 void* (*alloc_fn)(const HChar*, SizeT), 86 const HChar* cc, 87 void (*free_fn)(void*) ) 88 { 89 DedupPoolAlloc* ddpa; 90 vg_assert(poolSzB >= eltAlign); 91 vg_assert(poolSzB >= 100); /* let's say */ 92 vg_assert(poolSzB >= 10*eltAlign); /* let's say */ 93 vg_assert(alloc_fn); 94 vg_assert(cc); 95 vg_assert(free_fn); 96 ddpa = alloc_fn(cc, sizeof(*ddpa)); 97 VG_(memset)(ddpa, 0, sizeof(*ddpa)); 98 ddpa->poolSzB = poolSzB; 99 ddpa->fixedSzb = 0; 100 ddpa->eltAlign = eltAlign; 101 ddpa->alloc_fn = alloc_fn; 102 ddpa->cc = cc; 103 ddpa->free_fn = free_fn; 104 ddpa->pools = VG_(newXA)( alloc_fn, cc, free_fn, sizeof(void*) ); 105 106 ddpa->ht_elements = VG_(HT_construct) (cc); 107 ddpa->ht_node_pa = VG_(newPA) ( sizeof(ht_node), 108 1000, 109 alloc_fn, 110 cc, 111 free_fn); 112 ddpa->curpool = NULL; 113 ddpa->curpool_limit = NULL; 114 ddpa->curpool_free = NULL; 115 116 return ddpa; 117 } 118 119 void VG_(deleteDedupPA) ( DedupPoolAlloc* ddpa) 120 { 121 Word i; 122 if (ddpa->ht_elements) 123 // Free data structures used for insertion. 124 VG_(freezeDedupPA) (ddpa, NULL); 125 for (i = 0; i < VG_(sizeXA) (ddpa->pools); i++) 126 ddpa->free_fn (*(UWord **)VG_(indexXA) ( ddpa->pools, i )); 127 VG_(deleteXA) (ddpa->pools); 128 ddpa->free_fn (ddpa); 129 } 130 131 static __inline__ 132 UChar* ddpa_align ( DedupPoolAlloc* ddpa, UChar *c ) 133 { 134 return (UChar*)VG_ROUNDUP(c, ddpa->eltAlign); 135 } 136 137 /* Allocate a new pool or grow the (only) pool for a fixed size ddpa. */ 138 __attribute__((noinline)) 139 static void ddpa_add_new_pool_or_grow ( DedupPoolAlloc* ddpa ) 140 { 141 vg_assert(ddpa); 142 143 if (ddpa->fixedSzb > 0 && ddpa->curpool != NULL) { 144 // Grow (* 2) the current (fixed elt) pool 145 UChar *curpool_align = ddpa_align(ddpa, ddpa->curpool); 146 SizeT curpool_used = ddpa->curpool_free - curpool_align; 147 SizeT curpool_size = ddpa->curpool_limit - ddpa->curpool + 1; 148 UChar *newpool = ddpa->alloc_fn (ddpa->cc, 2 * curpool_size); 149 UChar *newpool_free = ddpa_align (ddpa, newpool); 150 UChar *newpool_limit = newpool + 2 * curpool_size - 1; 151 Word reloc_offset = (Addr)newpool_free - (Addr)curpool_align; 152 ht_node *n; 153 154 VG_(memcpy) (newpool_free, curpool_align, curpool_used); 155 /* We have reallocated the (only) pool. We need to relocate the pointers 156 in the hash table nodes. */ 157 VG_(HT_ResetIter) (ddpa->ht_elements); 158 while ((n = VG_(HT_Next) (ddpa->ht_elements))) { 159 n->elt = (void*)((Addr)n->elt + reloc_offset); 160 } 161 newpool_free += curpool_used; 162 163 VG_(dropHeadXA) (ddpa->pools, 1); 164 ddpa->free_fn (ddpa->curpool); 165 ddpa->curpool = newpool; 166 ddpa->curpool_free = newpool_free; 167 ddpa->curpool_limit = newpool_limit; 168 VG_(addToXA)( ddpa->pools, &ddpa->curpool); 169 } else { 170 /* Allocate a new pool, or allocate the first/only pool for a 171 fixed size ddpa. */ 172 ddpa->curpool = ddpa->alloc_fn( ddpa->cc, ddpa->poolSzB); 173 ddpa->curpool_limit = ddpa->curpool + ddpa->poolSzB - 1; 174 ddpa->curpool_free = ddpa_align (ddpa, ddpa->curpool); 175 /* add to our collection of pools */ 176 VG_(addToXA)( ddpa->pools, &ddpa->curpool ); 177 } 178 } 179 180 /* Compare function for 'gen' hash table. No need to compare the key 181 in this function, as the hash table already does it for us, 182 and that in any case, if the data is equal, the keys must also be 183 equal. */ 184 static Word cmp_pool_elt (const void* node1, const void* node2 ) 185 { 186 const ht_node* hnode1 = node1; 187 const ht_node* hnode2 = node2; 188 189 /* As this function is called by hashtable, that has already checked 190 for key equality, it is likely that it is the 'good' element. 191 So, we handle the equal case first. */ 192 if (hnode1->eltSzB == hnode2->eltSzB) 193 return VG_(memcmp) (hnode1->elt, hnode2->elt, hnode1->eltSzB); 194 else if (hnode1->eltSzB < hnode2->eltSzB) 195 return -1; 196 else 197 return 1; 198 } 199 200 /* Print some stats. */ 201 static void print_stats (DedupPoolAlloc *ddpa) 202 { 203 VG_(message)(Vg_DebugMsg, 204 "dedupPA:%s %ld allocs (%u uniq)" 205 " %ld pools (%ld bytes free in last pool)\n", 206 ddpa->cc, 207 (long int) ddpa->nr_alloc_calls, 208 VG_(HT_count_nodes)(ddpa->ht_elements), 209 VG_(sizeXA)(ddpa->pools), 210 ddpa->curpool ? 211 (long int) (ddpa->curpool_limit - ddpa->curpool_free + 1) : 0); 212 VG_(HT_print_stats) (ddpa->ht_elements, cmp_pool_elt); 213 } 214 215 /* Dummy free, as the ht elements are allocated in a pool, and 216 we will destroy the pool in one single operation. */ 217 static void htelem_dummyfree(void* ht_elem) 218 { 219 } 220 221 void VG_(freezeDedupPA) (DedupPoolAlloc *ddpa, 222 void (*shrink_block)(void*, SizeT)) 223 { 224 if (VG_(clo_stats) 225 && (VG_(clo_verbosity) > 2 || VG_(debugLog_getLevel) () >= 2)) { 226 print_stats(ddpa); 227 } 228 vg_assert (!ddpa->fixedSzb || VG_(sizeXA) (ddpa->pools) == 1); 229 if (shrink_block && ddpa->curpool_limit > ddpa->curpool_free) 230 (*shrink_block)(ddpa->curpool, ddpa->curpool_free - ddpa->curpool); 231 VG_(HT_destruct) ( ddpa->ht_elements, htelem_dummyfree); 232 ddpa->ht_elements = NULL; 233 VG_(deletePA) (ddpa->ht_node_pa); 234 ddpa->ht_node_pa = NULL; 235 } 236 237 238 // hash function used by gawk and SDBM. 239 static UInt sdbm_hash (const UChar* buf, UInt len ) 240 { 241 UInt h; 242 UInt i; 243 244 h = 0; 245 for (i = 0; i < len; i++) 246 h = *buf++ + (h<<6) + (h<<16) - h; 247 return h; 248 } 249 250 const void* VG_(allocEltDedupPA) (DedupPoolAlloc *ddpa, SizeT eltSzB, 251 const void *elt) 252 { 253 ht_node ht_elt; 254 void* elt_ins; 255 ht_node *ht_ins; 256 vg_assert(ddpa); 257 vg_assert(ddpa->ht_elements); 258 vg_assert (eltSzB <= ddpa->poolSzB); 259 260 ddpa->nr_alloc_calls++; 261 262 ht_elt.key = sdbm_hash (elt, eltSzB); 263 264 ht_elt.eltSzB = eltSzB; 265 ht_elt.elt = elt; 266 267 ht_ins = VG_(HT_gen_lookup) (ddpa->ht_elements, &ht_elt, cmp_pool_elt); 268 if (ht_ins) 269 return ht_ins->elt; 270 271 /* Not found -> we need to allocate a new element from the pool 272 and insert it in the hash table of inserted elements. */ 273 274 // Add a new pool or grow pool if not enough space in the current pool 275 if (UNLIKELY(ddpa->curpool_free == NULL 276 || ddpa->curpool_free + eltSzB - 1 > ddpa->curpool_limit)) { 277 ddpa_add_new_pool_or_grow (ddpa); 278 } 279 280 elt_ins = ddpa->curpool_free; 281 VG_(memcpy)(elt_ins, elt, eltSzB); 282 ddpa->curpool_free = ddpa_align(ddpa, ddpa->curpool_free + eltSzB); 283 284 ht_ins = VG_(allocEltPA) (ddpa->ht_node_pa); 285 ht_ins->key = ht_elt.key; 286 ht_ins->eltSzB = eltSzB; 287 ht_ins->elt = elt_ins; 288 VG_(HT_add_node)(ddpa->ht_elements, ht_ins); 289 return elt_ins; 290 } 291 292 static __inline__ 293 UInt elt2nr (DedupPoolAlloc *ddpa, const void *dedup_elt) 294 { 295 vg_assert (dedup_elt >= (const void *)ddpa->curpool 296 && dedup_elt < (const void *)ddpa->curpool_free); 297 return 1 + ((const UChar*)dedup_elt - (const UChar *)ddpa->curpool) 298 / VG_ROUNDUP(ddpa->fixedSzb, ddpa->eltAlign); 299 } 300 301 UInt VG_(allocFixedEltDedupPA) (DedupPoolAlloc *ddpa, 302 SizeT eltSzB, const void *elt) 303 { 304 if (ddpa->fixedSzb == 0) { 305 // First insertion in this ddpa 306 vg_assert (ddpa->nr_alloc_calls == 0); 307 vg_assert (eltSzB > 0); 308 ddpa->fixedSzb = eltSzB; 309 } 310 vg_assert (ddpa->fixedSzb == eltSzB); 311 const void *dedup_elt = VG_(allocEltDedupPA) (ddpa, eltSzB, elt); 312 return elt2nr (ddpa, dedup_elt); 313 } 314 315 void* VG_(indexEltNumber) (DedupPoolAlloc *ddpa, 316 UInt eltNr) 317 { 318 void *dedup_elt; 319 320 dedup_elt = ddpa->curpool 321 + (eltNr - 1) * VG_ROUNDUP(ddpa->fixedSzb, ddpa->eltAlign); 322 323 vg_assert ((UChar*)dedup_elt >= ddpa->curpool 324 && (UChar*)dedup_elt < ddpa->curpool_free); 325 326 return dedup_elt; 327 } 328 329 UInt VG_(sizeDedupPA) (DedupPoolAlloc *ddpa) 330 { 331 if (ddpa->curpool == NULL) 332 return 0; 333 334 vg_assert (ddpa->fixedSzb); 335 return (ddpa->curpool_free - ddpa_align(ddpa, ddpa->curpool)) 336 / VG_ROUNDUP(ddpa->fixedSzb, ddpa->eltAlign); 337 } 338