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-2017 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 Bool strPA; /* True if this is a string dedup pool */ 45 SizeT eltAlign; 46 Alloc_Fn_t alloc_fn; /* pool allocator */ 47 const HChar* cc; /* pool allocator's cost centre */ 48 Free_Fn_t free_fn; /* pool allocator's deallocation function */ 49 /* XArray of void* (pointers to pools). The pools themselves. 50 Each element is a pointer to a block of size at least PoolSzB bytes. 51 The last block might be smaller due to a call to shrink_block. */ 52 XArray *pools; 53 54 /* hash table of pool elements, used to dedup. 55 If NULL, it means the DedupPoolAlloc is frozen. */ 56 VgHashTable *ht_elements; 57 58 /* Hash table nodes of pool_elements are allocated with a pool, to 59 decrease memory overhead during insertion in the DedupPoolAlloc. */ 60 PoolAlloc *ht_node_pa; 61 62 UChar *curpool; /* last allocated pool. */ 63 UChar *curpool_free; /* Pos in current pool to allocate next elt. 64 always aligned on eltAlign. */ 65 UChar *curpool_limit; /* Last pos in current pool. */ 66 /* Note that for a fixed size pool, we only have a single pool to allow 67 simple/fast indexing. This single pool is grown, which might change 68 the address of the already allocated elements. */ 69 70 /* Total nr of alloc calls, resulting in (we hope) a lot less 71 real (dedup) elements. */ 72 ULong nr_alloc_calls; 73 }; 74 75 typedef 76 struct _ht_node { 77 struct _ht_node *next; // Read/Write by hashtable (pub_tool_hashtable.h) 78 UWord key; // Read by hashtable (pub_tool_hashtable.h) 79 SizeT eltSzBorStrNr; // for a normal pool, elt size 80 // for a string pool, the unique str number 81 const void *elt; 82 } 83 ht_node; 84 85 DedupPoolAlloc* VG_(newDedupPA) ( SizeT poolSzB, 86 SizeT eltAlign, 87 Alloc_Fn_t alloc_fn, 88 const HChar* cc, 89 Free_Fn_t free_fn ) 90 { 91 DedupPoolAlloc* ddpa; 92 vg_assert(poolSzB >= eltAlign); 93 vg_assert(poolSzB >= 100); /* let's say */ 94 vg_assert(poolSzB >= 10*eltAlign); /* let's say */ 95 vg_assert(alloc_fn); 96 vg_assert(cc); 97 vg_assert(free_fn); 98 ddpa = alloc_fn(cc, sizeof(*ddpa)); 99 VG_(memset)(ddpa, 0, sizeof(*ddpa)); 100 ddpa->poolSzB = poolSzB; 101 ddpa->fixedSzb = 0; 102 ddpa->strPA = False; 103 ddpa->eltAlign = eltAlign; 104 ddpa->alloc_fn = alloc_fn; 105 ddpa->cc = cc; 106 ddpa->free_fn = free_fn; 107 ddpa->pools = VG_(newXA)( alloc_fn, cc, free_fn, sizeof(void*) ); 108 109 ddpa->ht_elements = VG_(HT_construct) (cc); 110 ddpa->ht_node_pa = VG_(newPA) ( sizeof(ht_node), 111 1000, 112 alloc_fn, 113 cc, 114 free_fn); 115 ddpa->curpool = NULL; 116 ddpa->curpool_limit = NULL; 117 ddpa->curpool_free = NULL; 118 119 return ddpa; 120 } 121 122 void VG_(deleteDedupPA) ( DedupPoolAlloc* ddpa) 123 { 124 Word i; 125 if (ddpa->ht_elements) 126 // Free data structures used for insertion. 127 VG_(freezeDedupPA) (ddpa, NULL); 128 for (i = 0; i < VG_(sizeXA) (ddpa->pools); i++) 129 ddpa->free_fn (*(UWord **)VG_(indexXA) ( ddpa->pools, i )); 130 VG_(deleteXA) (ddpa->pools); 131 ddpa->free_fn (ddpa); 132 } 133 134 static __inline__ 135 UChar* ddpa_align ( DedupPoolAlloc* ddpa, UChar *c ) 136 { 137 return (UChar*)VG_ROUNDUP(c, ddpa->eltAlign); 138 } 139 140 /* Allocate a new pool or grow the (only) pool for a fixed size ddpa. */ 141 __attribute__((noinline)) 142 static void ddpa_add_new_pool_or_grow ( DedupPoolAlloc* ddpa ) 143 { 144 vg_assert(ddpa); 145 146 if (ddpa->fixedSzb > 0 && ddpa->curpool != NULL) { 147 // Grow (* 2) the current (fixed elt) pool 148 UChar *curpool_align = ddpa_align(ddpa, ddpa->curpool); 149 SizeT curpool_used = ddpa->curpool_free - curpool_align; 150 SizeT curpool_size = ddpa->curpool_limit - ddpa->curpool + 1; 151 UChar *newpool = ddpa->alloc_fn (ddpa->cc, 2 * curpool_size); 152 UChar *newpool_free = ddpa_align (ddpa, newpool); 153 UChar *newpool_limit = newpool + 2 * curpool_size - 1; 154 Word reloc_offset = (Addr)newpool_free - (Addr)curpool_align; 155 ht_node *n; 156 157 VG_(memcpy) (newpool_free, curpool_align, curpool_used); 158 /* We have reallocated the (only) pool. We need to relocate the pointers 159 in the hash table nodes. */ 160 VG_(HT_ResetIter) (ddpa->ht_elements); 161 while ((n = VG_(HT_Next) (ddpa->ht_elements))) { 162 n->elt = (void*)((Addr)n->elt + reloc_offset); 163 } 164 newpool_free += curpool_used; 165 166 VG_(dropHeadXA) (ddpa->pools, 1); 167 ddpa->free_fn (ddpa->curpool); 168 ddpa->curpool = newpool; 169 ddpa->curpool_free = newpool_free; 170 ddpa->curpool_limit = newpool_limit; 171 VG_(addToXA)( ddpa->pools, &ddpa->curpool); 172 } else { 173 /* Allocate a new pool, or allocate the first/only pool for a 174 fixed size ddpa. */ 175 ddpa->curpool = ddpa->alloc_fn( ddpa->cc, ddpa->poolSzB); 176 ddpa->curpool_limit = ddpa->curpool + ddpa->poolSzB - 1; 177 ddpa->curpool_free = ddpa_align (ddpa, ddpa->curpool); 178 /* add to our collection of pools */ 179 VG_(addToXA)( ddpa->pools, &ddpa->curpool ); 180 } 181 } 182 183 /* Compare function for 'gen' hash table. No need to compare the key 184 in this function, as the hash table already does it for us, 185 and that in any case, if the data is equal, the keys must also be 186 equal. */ 187 static Word cmp_pool_elt (const void* node1, const void* node2 ) 188 { 189 const ht_node* hnode1 = node1; 190 const ht_node* hnode2 = node2; 191 192 /* As this function is called by hashtable, that has already checked 193 for key equality, it is likely that it is the 'good' element. 194 So, we handle the equal case first. */ 195 if (hnode1->eltSzBorStrNr == hnode2->eltSzBorStrNr) 196 return VG_(memcmp) (hnode1->elt, hnode2->elt, hnode1->eltSzBorStrNr); 197 else if (hnode1->eltSzBorStrNr < hnode2->eltSzBorStrNr) 198 return -1; 199 else 200 return 1; 201 } 202 203 /* String compare function for 'gen' hash table. 204 Similarly to cmp_pool_elt, no need to compare the key. */ 205 static Word cmp_pool_str (const void* node1, const void* node2 ) 206 { 207 const ht_node* hnode1 = node1; 208 const ht_node* hnode2 = node2; 209 210 return VG_(strcmp)(hnode1->elt, hnode2->elt); 211 } 212 213 /* Print some stats. */ 214 static void print_stats (DedupPoolAlloc *ddpa) 215 { 216 VG_(message)(Vg_DebugMsg, 217 "dedupPA:%s %ld allocs (%u uniq)" 218 " %ld pools (%ld bytes free in last pool)\n", 219 ddpa->cc, 220 (long int) ddpa->nr_alloc_calls, 221 VG_(HT_count_nodes)(ddpa->ht_elements), 222 VG_(sizeXA)(ddpa->pools), 223 ddpa->curpool ? 224 (long int) (ddpa->curpool_limit - ddpa->curpool_free + 1) : 0); 225 if (ddpa->strPA) 226 VG_(HT_print_stats) (ddpa->ht_elements, cmp_pool_str); 227 else 228 VG_(HT_print_stats) (ddpa->ht_elements, cmp_pool_elt); 229 } 230 231 /* Dummy free, as the ht elements are allocated in a pool, and 232 we will destroy the pool in one single operation. */ 233 static void htelem_dummyfree(void* ht_elem) 234 { 235 } 236 237 void VG_(freezeDedupPA) (DedupPoolAlloc *ddpa, 238 void (*shrink_block)(void*, SizeT)) 239 { 240 if (VG_(clo_stats) 241 && (VG_(clo_verbosity) > 2 || VG_(debugLog_getLevel) () >= 2)) { 242 print_stats(ddpa); 243 } 244 vg_assert (!ddpa->fixedSzb || VG_(sizeXA) (ddpa->pools) == 1); 245 if (shrink_block && ddpa->curpool_limit > ddpa->curpool_free) 246 (*shrink_block)(ddpa->curpool, ddpa->curpool_free - ddpa->curpool); 247 VG_(HT_destruct) ( ddpa->ht_elements, htelem_dummyfree); 248 ddpa->ht_elements = NULL; 249 VG_(deletePA) (ddpa->ht_node_pa); 250 ddpa->ht_node_pa = NULL; 251 } 252 253 254 // hash function used by gawk and SDBM. 255 static UInt sdbm_hash (const UChar* buf, UInt len ) 256 { 257 UInt h; 258 UInt i; 259 260 h = 0; 261 for (i = 0; i < len; i++) 262 h = *buf++ + (h<<6) + (h<<16) - h; 263 return h; 264 } 265 266 static ht_node* allocEltDedupPA (DedupPoolAlloc *ddpa, SizeT eltSzB, 267 const void *elt) 268 { 269 ht_node ht_elt; 270 void* elt_ins; 271 ht_node *ht_ins; 272 vg_assert(ddpa); 273 vg_assert(ddpa->ht_elements); 274 275 ddpa->nr_alloc_calls++; 276 277 ht_elt.key = sdbm_hash (elt, eltSzB); 278 279 ht_elt.elt = elt; 280 281 if (ddpa->strPA) 282 ht_ins = VG_(HT_gen_lookup) (ddpa->ht_elements, &ht_elt, cmp_pool_str); 283 else { 284 ht_elt.eltSzBorStrNr = eltSzB; 285 ht_ins = VG_(HT_gen_lookup) (ddpa->ht_elements, &ht_elt, cmp_pool_elt); 286 } 287 if (ht_ins) 288 return ht_ins; 289 290 /* Not found -> we need to allocate a new element from the pool 291 and insert it in the hash table of inserted elements. */ 292 293 // Add a new pool or grow pool if not enough space in the current pool 294 if (eltSzB + ddpa->eltAlign > ddpa->poolSzB) { 295 // Element (+eltAlign for worst case) bigger than the pool size 296 // => allocate a specific pool just for this element 297 UChar *newpool = ddpa->alloc_fn (ddpa->cc, eltSzB + ddpa->eltAlign); 298 /* add to our collection of pools */ 299 VG_(addToXA)( ddpa->pools, &newpool ); 300 elt_ins = ddpa_align (ddpa, newpool); 301 } else { 302 if (UNLIKELY(ddpa->curpool_free == NULL 303 || ddpa->curpool_free + eltSzB - 1 > ddpa->curpool_limit)) { 304 ddpa_add_new_pool_or_grow (ddpa); 305 } 306 elt_ins = ddpa->curpool_free; 307 ddpa->curpool_free = ddpa_align(ddpa, ddpa->curpool_free + eltSzB); 308 } 309 310 311 VG_(memcpy)(elt_ins, elt, eltSzB); 312 ht_ins = VG_(allocEltPA) (ddpa->ht_node_pa); 313 ht_ins->key = ht_elt.key; 314 if (ddpa->strPA) 315 ht_ins->eltSzBorStrNr = VG_(HT_count_nodes)(ddpa->ht_elements) + 1; 316 else 317 ht_ins->eltSzBorStrNr = eltSzB; 318 ht_ins->elt = elt_ins; 319 VG_(HT_add_node)(ddpa->ht_elements, ht_ins); 320 return ht_ins; 321 } 322 323 const void* VG_(allocEltDedupPA) (DedupPoolAlloc *ddpa, SizeT eltSzB, 324 const void *elt) 325 { 326 return allocEltDedupPA(ddpa, eltSzB, elt)->elt; 327 } 328 329 UInt VG_(allocStrDedupPA) (DedupPoolAlloc *ddpa, 330 const HChar* str, 331 Bool* newStr) 332 { 333 if (!ddpa->strPA) { 334 // First insertion in this ddpa 335 vg_assert (ddpa->nr_alloc_calls == 0); 336 vg_assert (ddpa->fixedSzb == 0); 337 ddpa->strPA = True; 338 } 339 340 const UInt nr_str = VG_(HT_count_nodes)(ddpa->ht_elements); 341 const ht_node* ht_ins = allocEltDedupPA(ddpa, VG_(strlen)(str)+1, str); 342 343 *newStr = nr_str < VG_(HT_count_nodes)(ddpa->ht_elements); 344 return ht_ins->eltSzBorStrNr; 345 } 346 347 static __inline__ 348 UInt elt2nr (DedupPoolAlloc *ddpa, const void *dedup_elt) 349 { 350 vg_assert (dedup_elt >= (const void *)ddpa->curpool 351 && dedup_elt < (const void *)ddpa->curpool_free); 352 return 1 + ((const UChar*)dedup_elt - (const UChar *)ddpa->curpool) 353 / VG_ROUNDUP(ddpa->fixedSzb, ddpa->eltAlign); 354 } 355 356 UInt VG_(allocFixedEltDedupPA) (DedupPoolAlloc *ddpa, 357 SizeT eltSzB, const void *elt) 358 { 359 if (ddpa->fixedSzb == 0) { 360 // First insertion in this ddpa 361 vg_assert (!ddpa->strPA); 362 vg_assert (ddpa->nr_alloc_calls == 0); 363 vg_assert (eltSzB > 0); 364 ddpa->fixedSzb = eltSzB; 365 } 366 vg_assert (ddpa->fixedSzb == eltSzB); 367 const void *dedup_elt = VG_(allocEltDedupPA) (ddpa, eltSzB, elt); 368 return elt2nr (ddpa, dedup_elt); 369 } 370 371 void* VG_(indexEltNumber) (DedupPoolAlloc *ddpa, 372 UInt eltNr) 373 { 374 void *dedup_elt; 375 376 dedup_elt = ddpa->curpool 377 + (eltNr - 1) * VG_ROUNDUP(ddpa->fixedSzb, ddpa->eltAlign); 378 379 vg_assert ((UChar*)dedup_elt >= ddpa->curpool 380 && (UChar*)dedup_elt < ddpa->curpool_free); 381 382 return dedup_elt; 383 } 384 385 UInt VG_(sizeDedupPA) (DedupPoolAlloc *ddpa) 386 { 387 if (ddpa->curpool == NULL) 388 return 0; 389 390 vg_assert (ddpa->fixedSzb); 391 return (ddpa->curpool_free - ddpa_align(ddpa, ddpa->curpool)) 392 / VG_ROUNDUP(ddpa->fixedSzb, ddpa->eltAlign); 393 } 394