m_execontext.c revision 8617b5b8f16570c9003379464d60e8572a801a8c
1 2/*--------------------------------------------------------------------*/ 3/*--- Store and compare stack backtraces m_execontext.c ---*/ 4/*--------------------------------------------------------------------*/ 5 6/* 7 This file is part of Valgrind, a dynamic binary instrumentation 8 framework. 9 10 Copyright (C) 2000-2012 Julian Seward 11 jseward@acm.org 12 13 This program is free software; you can redistribute it and/or 14 modify it under the terms of the GNU General Public License as 15 published by the Free Software Foundation; either version 2 of the 16 License, or (at your option) any later version. 17 18 This program is distributed in the hope that it will be useful, but 19 WITHOUT ANY WARRANTY; without even the implied warranty of 20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 General Public License for more details. 22 23 You should have received a copy of the GNU General Public License 24 along with this program; if not, write to the Free Software 25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 26 02111-1307, USA. 27 28 The GNU General Public License is contained in the file COPYING. 29*/ 30 31#include "pub_core_basics.h" 32#include "pub_core_debuglog.h" 33#include "pub_core_libcassert.h" 34#include "pub_core_libcprint.h" // For VG_(message)() 35#include "pub_core_mallocfree.h" 36#include "pub_core_options.h" 37#include "pub_core_stacktrace.h" 38#include "pub_core_machine.h" // VG_(get_IP) 39#include "pub_core_vki.h" // To keep pub_core_threadstate.h happy 40#include "pub_core_libcsetjmp.h" // Ditto 41#include "pub_core_threadstate.h" // VG_(is_valid_tid) 42#include "pub_core_execontext.h" // self 43 44/*------------------------------------------------------------*/ 45/*--- Low-level ExeContext storage. ---*/ 46/*------------------------------------------------------------*/ 47 48/* The first 4 IP values are used in comparisons to remove duplicate 49 errors, and for comparing against suppression specifications. The 50 rest are purely informational (but often important). 51 52 The contexts are stored in a traditional chained hash table, so as 53 to allow quick determination of whether a new context already 54 exists. The hash table starts small and expands dynamically, so as 55 to keep the load factor below 1.0. 56 57 The idea is only to ever store any one context once, so as to save 58 space and make exact comparisons faster. */ 59 60 61/* Primes for the hash table */ 62 63#define N_EC_PRIMES 18 64 65static SizeT ec_primes[N_EC_PRIMES] = { 66 769UL, 1543UL, 3079UL, 6151UL, 67 12289UL, 24593UL, 49157UL, 98317UL, 68 196613UL, 393241UL, 786433UL, 1572869UL, 69 3145739UL, 6291469UL, 12582917UL, 25165843UL, 70 50331653UL, 100663319UL 71}; 72 73 74/* Each element is present in a hash chain, and also contains a 75 variable length array of guest code addresses (the useful part). */ 76 77struct _ExeContext { 78 struct _ExeContext* chain; 79 /* A 32-bit unsigned integer that uniquely identifies this 80 ExeContext. Memcheck uses these for origin tracking. Values 81 must be nonzero (else Memcheck's origin tracking is hosed), must 82 be a multiple of four, and must be unique. Hence they start at 83 4. */ 84 UInt ecu; 85 /* Variable-length array. The size is 'n_ips'; at 86 least 1, at most VG_DEEPEST_BACKTRACE. [0] is the current IP, 87 [1] is its caller, [2] is the caller of [1], etc. */ 88 UInt n_ips; 89 Addr ips[0]; 90}; 91 92 93/* This is the dynamically expanding hash table. */ 94static ExeContext** ec_htab; /* array [ec_htab_size] of ExeContext* */ 95static SizeT ec_htab_size; /* one of the values in ec_primes */ 96static SizeT ec_htab_size_idx; /* 0 .. N_EC_PRIMES-1 */ 97 98/* ECU serial number */ 99static UInt ec_next_ecu = 4; /* We must never issue zero */ 100 101static ExeContext* null_ExeContext; 102 103/* Stats only: the number of times the system was searched to locate a 104 context. */ 105static ULong ec_searchreqs; 106 107/* Stats only: the number of full context comparisons done. */ 108static ULong ec_searchcmps; 109 110/* Stats only: total number of stored contexts. */ 111static ULong ec_totstored; 112 113/* Number of 2, 4 and (fast) full cmps done. */ 114static ULong ec_cmp2s; 115static ULong ec_cmp4s; 116static ULong ec_cmpAlls; 117 118 119/*------------------------------------------------------------*/ 120/*--- Exported functions. ---*/ 121/*------------------------------------------------------------*/ 122 123static ExeContext* record_ExeContext_wrk2 ( Addr* ips, UInt n_ips ); /*fwds*/ 124 125/* Initialise this subsystem. */ 126static void init_ExeContext_storage ( void ) 127{ 128 Int i; 129 static Bool init_done = False; 130 if (LIKELY(init_done)) 131 return; 132 ec_searchreqs = 0; 133 ec_searchcmps = 0; 134 ec_totstored = 0; 135 ec_cmp2s = 0; 136 ec_cmp4s = 0; 137 ec_cmpAlls = 0; 138 139 ec_htab_size_idx = 0; 140 ec_htab_size = ec_primes[ec_htab_size_idx]; 141 ec_htab = VG_(arena_malloc)(VG_AR_EXECTXT, "execontext.iEs1", 142 sizeof(ExeContext*) * ec_htab_size); 143 for (i = 0; i < ec_htab_size; i++) 144 ec_htab[i] = NULL; 145 146 { 147 Addr ips[1]; 148 ips[0] = 0; 149 null_ExeContext = record_ExeContext_wrk2(ips, 1); 150 vg_assert(null_ExeContext->ecu == 4); // null execontext must be the first one. 151 } 152 153 init_done = True; 154} 155 156 157/* Print stats. */ 158void VG_(print_ExeContext_stats) ( Bool with_stacktraces ) 159{ 160 init_ExeContext_storage(); 161 162 if (with_stacktraces) { 163 Int i; 164 ExeContext* ec; 165 VG_(message)(Vg_DebugMsg, " exectx: Printing contexts stacktraces\n"); 166 for (i = 0; i < ec_htab_size; i++) { 167 for (ec = ec_htab[i]; ec; ec = ec->chain) { 168 VG_(message)(Vg_DebugMsg, " exectx: stacktrace ecu %u n_ips %u\n", 169 ec->ecu, ec->n_ips); 170 VG_(pp_StackTrace)( ec->ips, ec->n_ips ); 171 } 172 } 173 VG_(message)(Vg_DebugMsg, 174 " exectx: Printed %'llu contexts stacktraces\n", 175 ec_totstored); 176 } 177 178 VG_(message)(Vg_DebugMsg, 179 " exectx: %'lu lists, %'llu contexts (avg %'llu per list)\n", 180 ec_htab_size, ec_totstored, ec_totstored / (ULong)ec_htab_size 181 ); 182 VG_(message)(Vg_DebugMsg, 183 " exectx: %'llu searches, %'llu full compares (%'llu per 1000)\n", 184 ec_searchreqs, ec_searchcmps, 185 ec_searchreqs == 0 186 ? 0ULL 187 : ( (ec_searchcmps * 1000ULL) / ec_searchreqs ) 188 ); 189 VG_(message)(Vg_DebugMsg, 190 " exectx: %'llu cmp2, %'llu cmp4, %'llu cmpAll\n", 191 ec_cmp2s, ec_cmp4s, ec_cmpAlls 192 ); 193} 194 195 196/* Print an ExeContext. */ 197void VG_(pp_ExeContext) ( ExeContext* ec ) 198{ 199 VG_(pp_StackTrace)( ec->ips, ec->n_ips ); 200} 201 202 203/* Compare two ExeContexts. Number of callers considered depends on res. */ 204Bool VG_(eq_ExeContext) ( VgRes res, ExeContext* e1, ExeContext* e2 ) 205{ 206 Int i; 207 208 if (e1 == NULL || e2 == NULL) 209 return False; 210 211 // Must be at least one address in each trace. 212 tl_assert(e1->n_ips >= 1 && e2->n_ips >= 1); 213 214 switch (res) { 215 case Vg_LowRes: 216 /* Just compare the top two callers. */ 217 ec_cmp2s++; 218 for (i = 0; i < 2; i++) { 219 if ( (e1->n_ips <= i) && (e2->n_ips <= i)) return True; 220 if ( (e1->n_ips <= i) && !(e2->n_ips <= i)) return False; 221 if (!(e1->n_ips <= i) && (e2->n_ips <= i)) return False; 222 if (e1->ips[i] != e2->ips[i]) return False; 223 } 224 return True; 225 226 case Vg_MedRes: 227 /* Just compare the top four callers. */ 228 ec_cmp4s++; 229 for (i = 0; i < 4; i++) { 230 if ( (e1->n_ips <= i) && (e2->n_ips <= i)) return True; 231 if ( (e1->n_ips <= i) && !(e2->n_ips <= i)) return False; 232 if (!(e1->n_ips <= i) && (e2->n_ips <= i)) return False; 233 if (e1->ips[i] != e2->ips[i]) return False; 234 } 235 return True; 236 237 case Vg_HighRes: 238 ec_cmpAlls++; 239 /* Compare them all -- just do pointer comparison. */ 240 if (e1 != e2) return False; 241 return True; 242 243 default: 244 VG_(core_panic)("VG_(eq_ExeContext): unrecognised VgRes"); 245 } 246} 247 248/* VG_(record_ExeContext) is the head honcho here. Take a snapshot of 249 the client's stack. Search our collection of ExeContexts to see if 250 we already have it, and if not, allocate a new one. Either way, 251 return a pointer to the context. If there is a matching context we 252 guarantee to not allocate a new one. Thus we never store 253 duplicates, and so exact equality can be quickly done as equality 254 on the returned ExeContext* values themselves. Inspired by Hugs's 255 Text type. 256 257 Also checks whether the hash table needs expanding, and expands it 258 if so. */ 259 260static inline UWord ROLW ( UWord w, Int n ) 261{ 262 Int bpw = 8 * sizeof(UWord); 263 w = (w << n) | (w >> (bpw-n)); 264 return w; 265} 266 267static UWord calc_hash ( Addr* ips, UInt n_ips, UWord htab_sz ) 268{ 269 UInt i; 270 UWord hash = 0; 271 vg_assert(htab_sz > 0); 272 for (i = 0; i < n_ips; i++) { 273 hash ^= ips[i]; 274 hash = ROLW(hash, 19); 275 } 276 return hash % htab_sz; 277} 278 279static void resize_ec_htab ( void ) 280{ 281 SizeT i; 282 SizeT new_size; 283 ExeContext** new_ec_htab; 284 285 vg_assert(ec_htab_size_idx >= 0 && ec_htab_size_idx < N_EC_PRIMES); 286 if (ec_htab_size_idx == N_EC_PRIMES-1) 287 return; /* out of primes - can't resize further */ 288 289 new_size = ec_primes[ec_htab_size_idx + 1]; 290 new_ec_htab = VG_(arena_malloc)(VG_AR_EXECTXT, "execontext.reh1", 291 sizeof(ExeContext*) * new_size); 292 293 VG_(debugLog)( 294 1, "execontext", 295 "resizing htab from size %lu to %lu (idx %lu) Total#ECs=%llu\n", 296 ec_htab_size, new_size, ec_htab_size_idx + 1, ec_totstored); 297 298 for (i = 0; i < new_size; i++) 299 new_ec_htab[i] = NULL; 300 301 for (i = 0; i < ec_htab_size; i++) { 302 ExeContext* cur = ec_htab[i]; 303 while (cur) { 304 ExeContext* next = cur->chain; 305 UWord hash = calc_hash(cur->ips, cur->n_ips, new_size); 306 vg_assert(hash < new_size); 307 cur->chain = new_ec_htab[hash]; 308 new_ec_htab[hash] = cur; 309 cur = next; 310 } 311 } 312 313 VG_(arena_free)(VG_AR_EXECTXT, ec_htab); 314 ec_htab = new_ec_htab; 315 ec_htab_size = new_size; 316 ec_htab_size_idx++; 317} 318 319/* Do the first part of getting a stack trace: actually unwind the 320 stack, and hand the results off to the duplicate-trace-finder 321 (_wrk2). */ 322static ExeContext* record_ExeContext_wrk ( ThreadId tid, Word first_ip_delta, 323 Bool first_ip_only ) 324{ 325 Addr ips[VG_(clo_backtrace_size)]; 326 UInt n_ips; 327 328 init_ExeContext_storage(); 329 330 vg_assert(sizeof(void*) == sizeof(UWord)); 331 vg_assert(sizeof(void*) == sizeof(Addr)); 332 333 vg_assert(VG_(is_valid_tid)(tid)); 334 335 if (first_ip_only) { 336 n_ips = 1; 337 ips[0] = VG_(get_IP)(tid) + first_ip_delta; 338 } else { 339 n_ips = VG_(get_StackTrace)( tid, ips, VG_(clo_backtrace_size), 340 NULL/*array to dump SP values in*/, 341 NULL/*array to dump FP values in*/, 342 first_ip_delta ); 343 } 344 345 return record_ExeContext_wrk2 ( ips, n_ips ); 346} 347 348/* Do the second part of getting a stack trace: ips[0 .. n_ips-1] 349 holds a proposed trace. Find or allocate a suitable ExeContext. 350 Note that callers must have done init_ExeContext_storage() before 351 getting to this point. */ 352static ExeContext* record_ExeContext_wrk2 ( Addr* ips, UInt n_ips ) 353{ 354 Int i; 355 Bool same; 356 UWord hash; 357 ExeContext* new_ec; 358 ExeContext* list; 359 ExeContext *prev2, *prev; 360 361 static UInt ctr = 0; 362 363 tl_assert(n_ips >= 1 && n_ips <= VG_(clo_backtrace_size)); 364 365 /* Now figure out if we've seen this one before. First hash it so 366 as to determine the list number. */ 367 hash = calc_hash( ips, n_ips, ec_htab_size ); 368 369 /* And (the expensive bit) look a for matching entry in the list. */ 370 371 ec_searchreqs++; 372 373 prev2 = NULL; 374 prev = NULL; 375 list = ec_htab[hash]; 376 377 while (True) { 378 if (list == NULL) break; 379 ec_searchcmps++; 380 same = True; 381 for (i = 0; i < n_ips; i++) { 382 if (list->ips[i] != ips[i]) { 383 same = False; 384 break; 385 } 386 } 387 if (same) break; 388 prev2 = prev; 389 prev = list; 390 list = list->chain; 391 } 392 393 if (list != NULL) { 394 /* Yay! We found it. Once every 8 searches, move it one step 395 closer to the start of the list to make future searches 396 cheaper. */ 397 if (0 == ((ctr++) & 7)) { 398 if (prev2 != NULL && prev != NULL) { 399 /* Found at 3rd or later position in the chain. */ 400 vg_assert(prev2->chain == prev); 401 vg_assert(prev->chain == list); 402 prev2->chain = list; 403 prev->chain = list->chain; 404 list->chain = prev; 405 } 406 else if (prev2 == NULL && prev != NULL) { 407 /* Found at 2nd position in the chain. */ 408 vg_assert(ec_htab[hash] == prev); 409 vg_assert(prev->chain == list); 410 prev->chain = list->chain; 411 list->chain = prev; 412 ec_htab[hash] = list; 413 } 414 } 415 return list; 416 } 417 418 /* Bummer. We have to allocate a new context record. */ 419 ec_totstored++; 420 421 new_ec = VG_(arena_malloc)( VG_AR_EXECTXT, "execontext.rEw2.2", 422 sizeof(struct _ExeContext) 423 + n_ips * sizeof(Addr) ); 424 425 for (i = 0; i < n_ips; i++) 426 new_ec->ips[i] = ips[i]; 427 428 vg_assert(VG_(is_plausible_ECU)(ec_next_ecu)); 429 new_ec->ecu = ec_next_ecu; 430 ec_next_ecu += 4; 431 if (ec_next_ecu == 0) { 432 /* Urr. Now we're hosed; we emitted 2^30 ExeContexts already 433 and have run out of numbers. Not sure what to do. */ 434 VG_(core_panic)("m_execontext: more than 2^30 ExeContexts created"); 435 } 436 437 new_ec->n_ips = n_ips; 438 new_ec->chain = ec_htab[hash]; 439 ec_htab[hash] = new_ec; 440 441 /* Resize the hash table, maybe? */ 442 if ( ((ULong)ec_totstored) > ((ULong)ec_htab_size) ) { 443 vg_assert(ec_htab_size_idx >= 0 && ec_htab_size_idx < N_EC_PRIMES); 444 if (ec_htab_size_idx < N_EC_PRIMES-1) 445 resize_ec_htab(); 446 } 447 448 return new_ec; 449} 450 451ExeContext* VG_(record_ExeContext)( ThreadId tid, Word first_ip_delta ) { 452 return record_ExeContext_wrk( tid, first_ip_delta, 453 False/*!first_ip_only*/ ); 454} 455 456ExeContext* VG_(record_depth_1_ExeContext)( ThreadId tid, Word first_ip_delta ) 457{ 458 return record_ExeContext_wrk( tid, first_ip_delta, 459 True/*first_ip_only*/ ); 460} 461 462ExeContext* VG_(make_depth_1_ExeContext_from_Addr)( Addr a ) { 463 init_ExeContext_storage(); 464 return record_ExeContext_wrk2( &a, 1 ); 465} 466 467StackTrace VG_(get_ExeContext_StackTrace) ( ExeContext* e ) { 468 return e->ips; 469} 470 471UInt VG_(get_ECU_from_ExeContext)( ExeContext* e ) { 472 vg_assert(VG_(is_plausible_ECU)(e->ecu)); 473 return e->ecu; 474} 475 476Int VG_(get_ExeContext_n_ips)( ExeContext* e ) { 477 vg_assert(e->n_ips >= 1); 478 return e->n_ips; 479} 480 481ExeContext* VG_(get_ExeContext_from_ECU)( UInt ecu ) 482{ 483 UWord i; 484 ExeContext* ec; 485 vg_assert(VG_(is_plausible_ECU)(ecu)); 486 vg_assert(ec_htab_size > 0); 487 for (i = 0; i < ec_htab_size; i++) { 488 for (ec = ec_htab[i]; ec; ec = ec->chain) { 489 if (ec->ecu == ecu) 490 return ec; 491 } 492 } 493 return NULL; 494} 495 496ExeContext* VG_(make_ExeContext_from_StackTrace)( Addr* ips, UInt n_ips ) 497{ 498 init_ExeContext_storage(); 499 return record_ExeContext_wrk2(ips, n_ips); 500} 501 502ExeContext* VG_(null_ExeContext) (void) 503{ 504 init_ExeContext_storage(); 505 return null_ExeContext; 506} 507 508/*--------------------------------------------------------------------*/ 509/*--- end m_execontext.c ---*/ 510/*--------------------------------------------------------------------*/ 511