blob: fcb45d7a5a1900675bc4b8288e172d39c1ab8dab [file] [log] [blame]
sewardj267100d2005-04-24 12:33:12 +00001
njnd01fef72005-03-25 23:35:48 +00002/*--------------------------------------------------------------------*/
sewardj267100d2005-04-24 12:33:12 +00003/*--- Store and compare stack backtraces m_execontext.c ---*/
njnd01fef72005-03-25 23:35:48 +00004/*--------------------------------------------------------------------*/
5
6/*
7 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
9
sewardj9eecbbb2010-05-03 21:37:12 +000010 Copyright (C) 2000-2010 Julian Seward
njnd01fef72005-03-25 23:35:48 +000011 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
njnc7561b92005-06-19 01:24:32 +000031#include "pub_core_basics.h"
sewardj9877e9b2007-08-23 10:24:30 +000032#include "pub_core_debuglog.h"
njn132bfcc2005-06-04 19:16:06 +000033#include "pub_core_libcassert.h"
njn24a6efb2005-06-20 03:36:51 +000034#include "pub_core_libcprint.h" // For VG_(message)()
njnaf1d7df2005-06-11 01:31:52 +000035#include "pub_core_mallocfree.h"
njn20242342005-05-16 23:31:24 +000036#include "pub_core_options.h"
njnd0d7c1f2005-06-21 00:33:19 +000037#include "pub_core_stacktrace.h"
sewardj3059d272007-12-21 01:24:59 +000038#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_threadstate.h" // VG_(is_valid_tid)
41#include "pub_core_execontext.h" // self
njnd01fef72005-03-25 23:35:48 +000042
43/*------------------------------------------------------------*/
44/*--- Low-level ExeContext storage. ---*/
45/*------------------------------------------------------------*/
46
sewardj9877e9b2007-08-23 10:24:30 +000047/* The first 4 IP values are used in comparisons to remove duplicate
48 errors, and for comparing against suppression specifications. The
49 rest are purely informational (but often important).
50
51 The contexts are stored in a traditional chained hash table, so as
52 to allow quick determination of whether a new context already
53 exists. The hash table starts small and expands dynamically, so as
54 to keep the load factor below 1.0.
55
56 The idea is only to ever store any one context once, so as to save
57 space and make exact comparisons faster. */
58
59
60/* Primes for the hash table */
61
62#define N_EC_PRIMES 18
63
64static SizeT ec_primes[N_EC_PRIMES] = {
65 769UL, 1543UL, 3079UL, 6151UL,
66 12289UL, 24593UL, 49157UL, 98317UL,
67 196613UL, 393241UL, 786433UL, 1572869UL,
68 3145739UL, 6291469UL, 12582917UL, 25165843UL,
69 50331653UL, 100663319UL
70};
71
72
73/* Each element is present in a hash chain, and also contains a
74 variable length array of guest code addresses (the useful part). */
njnd01fef72005-03-25 23:35:48 +000075
76struct _ExeContext {
sewardj9877e9b2007-08-23 10:24:30 +000077 struct _ExeContext* chain;
sewardj7cf4e6b2008-05-01 20:24:26 +000078 /* A 32-bit unsigned integer that uniquely identifies this
79 ExeContext. Memcheck uses these for origin tracking. Values
80 must be nonzero (else Memcheck's origin tracking is hosed), must
81 be a multiple of four, and must be unique. Hence they start at
82 4. */
83 UInt ecu;
njnc0ec8e92005-12-25 06:34:04 +000084 /* Variable-length array. The size is 'n_ips'; at
njnd01fef72005-03-25 23:35:48 +000085 least 1, at most VG_DEEPEST_BACKTRACE. [0] is the current IP,
86 [1] is its caller, [2] is the caller of [1], etc. */
sewardj7cf4e6b2008-05-01 20:24:26 +000087 UInt n_ips;
njnd01fef72005-03-25 23:35:48 +000088 Addr ips[0];
89};
90
njnd01fef72005-03-25 23:35:48 +000091
sewardj9877e9b2007-08-23 10:24:30 +000092/* This is the dynamically expanding hash table. */
93static ExeContext** ec_htab; /* array [ec_htab_size] of ExeContext* */
94static SizeT ec_htab_size; /* one of the values in ec_primes */
95static SizeT ec_htab_size_idx; /* 0 .. N_EC_PRIMES-1 */
njnd01fef72005-03-25 23:35:48 +000096
sewardj7cf4e6b2008-05-01 20:24:26 +000097/* ECU serial number */
98static UInt ec_next_ecu = 4; /* We must never issue zero */
99
njnd01fef72005-03-25 23:35:48 +0000100
101/* Stats only: the number of times the system was searched to locate a
102 context. */
njn0fd92f42005-10-06 03:32:42 +0000103static ULong ec_searchreqs;
njnd01fef72005-03-25 23:35:48 +0000104
105/* Stats only: the number of full context comparisons done. */
njn0fd92f42005-10-06 03:32:42 +0000106static ULong ec_searchcmps;
njnd01fef72005-03-25 23:35:48 +0000107
108/* Stats only: total number of stored contexts. */
njn0fd92f42005-10-06 03:32:42 +0000109static ULong ec_totstored;
njnd01fef72005-03-25 23:35:48 +0000110
111/* Number of 2, 4 and (fast) full cmps done. */
njn0fd92f42005-10-06 03:32:42 +0000112static ULong ec_cmp2s;
113static ULong ec_cmp4s;
114static ULong ec_cmpAlls;
njnd01fef72005-03-25 23:35:48 +0000115
116
117/*------------------------------------------------------------*/
118/*--- Exported functions. ---*/
119/*------------------------------------------------------------*/
120
121
122/* Initialise this subsystem. */
123static void init_ExeContext_storage ( void )
124{
125 Int i;
126 static Bool init_done = False;
sewardj7cf4e6b2008-05-01 20:24:26 +0000127 if (LIKELY(init_done))
njnd01fef72005-03-25 23:35:48 +0000128 return;
129 ec_searchreqs = 0;
130 ec_searchcmps = 0;
131 ec_totstored = 0;
132 ec_cmp2s = 0;
133 ec_cmp4s = 0;
134 ec_cmpAlls = 0;
sewardj9877e9b2007-08-23 10:24:30 +0000135
136 ec_htab_size_idx = 0;
137 ec_htab_size = ec_primes[ec_htab_size_idx];
sewardj9c606bd2008-09-18 18:12:50 +0000138 ec_htab = VG_(arena_malloc)(VG_AR_EXECTXT, "execontext.iEs1",
sewardj9877e9b2007-08-23 10:24:30 +0000139 sizeof(ExeContext*) * ec_htab_size);
140 for (i = 0; i < ec_htab_size; i++)
141 ec_htab[i] = NULL;
142
njnd01fef72005-03-25 23:35:48 +0000143 init_done = True;
144}
145
146
147/* Print stats. */
148void VG_(print_ExeContext_stats) ( void )
149{
150 init_ExeContext_storage();
151 VG_(message)(Vg_DebugMsg,
sewardj738856f2009-07-15 14:48:32 +0000152 " exectx: %'lu lists, %'llu contexts (avg %'llu per list)\n",
sewardja8ffda62008-07-18 18:23:24 +0000153 ec_htab_size, ec_totstored, ec_totstored / (ULong)ec_htab_size
njnd01fef72005-03-25 23:35:48 +0000154 );
155 VG_(message)(Vg_DebugMsg,
sewardj738856f2009-07-15 14:48:32 +0000156 " exectx: %'llu searches, %'llu full compares (%'llu per 1000)\n",
njnd01fef72005-03-25 23:35:48 +0000157 ec_searchreqs, ec_searchcmps,
158 ec_searchreqs == 0
sewardja8ffda62008-07-18 18:23:24 +0000159 ? 0ULL
160 : ( (ec_searchcmps * 1000ULL) / ec_searchreqs )
njnd01fef72005-03-25 23:35:48 +0000161 );
162 VG_(message)(Vg_DebugMsg,
sewardj738856f2009-07-15 14:48:32 +0000163 " exectx: %'llu cmp2, %'llu cmp4, %'llu cmpAll\n",
njnd01fef72005-03-25 23:35:48 +0000164 ec_cmp2s, ec_cmp4s, ec_cmpAlls
165 );
166}
167
168
169/* Print an ExeContext. */
170void VG_(pp_ExeContext) ( ExeContext* ec )
171{
njnc0ec8e92005-12-25 06:34:04 +0000172 VG_(pp_StackTrace)( ec->ips, ec->n_ips );
njnd01fef72005-03-25 23:35:48 +0000173}
174
175
njnb7a4e2e2009-05-01 00:30:43 +0000176/* Compare two ExeContexts. Number of callers considered depends on res. */
njnd01fef72005-03-25 23:35:48 +0000177Bool VG_(eq_ExeContext) ( VgRes res, ExeContext* e1, ExeContext* e2 )
178{
njnc0ec8e92005-12-25 06:34:04 +0000179 Int i;
180
njnd01fef72005-03-25 23:35:48 +0000181 if (e1 == NULL || e2 == NULL)
182 return False;
njnc0ec8e92005-12-25 06:34:04 +0000183
184 // Must be at least one address in each trace.
185 tl_assert(e1->n_ips >= 1 && e2->n_ips >= 1);
186
njnd01fef72005-03-25 23:35:48 +0000187 switch (res) {
188 case Vg_LowRes:
189 /* Just compare the top two callers. */
190 ec_cmp2s++;
njnc0ec8e92005-12-25 06:34:04 +0000191 for (i = 0; i < 2; i++) {
192 if ( (e1->n_ips <= i) && (e2->n_ips <= i)) return True;
193 if ( (e1->n_ips <= i) && !(e2->n_ips <= i)) return False;
194 if (!(e1->n_ips <= i) && (e2->n_ips <= i)) return False;
195 if (e1->ips[i] != e2->ips[i]) return False;
196 }
njnd01fef72005-03-25 23:35:48 +0000197 return True;
198
199 case Vg_MedRes:
200 /* Just compare the top four callers. */
201 ec_cmp4s++;
njnc0ec8e92005-12-25 06:34:04 +0000202 for (i = 0; i < 4; i++) {
203 if ( (e1->n_ips <= i) && (e2->n_ips <= i)) return True;
204 if ( (e1->n_ips <= i) && !(e2->n_ips <= i)) return False;
205 if (!(e1->n_ips <= i) && (e2->n_ips <= i)) return False;
206 if (e1->ips[i] != e2->ips[i]) return False;
207 }
njnd01fef72005-03-25 23:35:48 +0000208 return True;
209
210 case Vg_HighRes:
211 ec_cmpAlls++;
212 /* Compare them all -- just do pointer comparison. */
213 if (e1 != e2) return False;
214 return True;
215
216 default:
217 VG_(core_panic)("VG_(eq_ExeContext): unrecognised VgRes");
218 }
219}
220
sewardj9877e9b2007-08-23 10:24:30 +0000221/* VG_(record_ExeContext) is the head honcho here. Take a snapshot of
222 the client's stack. Search our collection of ExeContexts to see if
223 we already have it, and if not, allocate a new one. Either way,
224 return a pointer to the context. If there is a matching context we
njnd01fef72005-03-25 23:35:48 +0000225 guarantee to not allocate a new one. Thus we never store
226 duplicates, and so exact equality can be quickly done as equality
227 on the returned ExeContext* values themselves. Inspired by Hugs's
sewardj9877e9b2007-08-23 10:24:30 +0000228 Text type.
229
230 Also checks whether the hash table needs expanding, and expands it
231 if so. */
232
sewardjbb2e09f2006-10-24 21:43:38 +0000233static inline UWord ROLW ( UWord w, Int n )
234{
235 Int bpw = 8 * sizeof(UWord);
236 w = (w << n) | (w >> (bpw-n));
237 return w;
238}
239
sewardj9877e9b2007-08-23 10:24:30 +0000240static UWord calc_hash ( Addr* ips, UInt n_ips, UWord htab_sz )
241{
242 UInt i;
243 UWord hash = 0;
244 vg_assert(htab_sz > 0);
245 for (i = 0; i < n_ips; i++) {
246 hash ^= ips[i];
247 hash = ROLW(hash, 19);
248 }
249 return hash % htab_sz;
250}
251
252static void resize_ec_htab ( void )
253{
254 SizeT i;
255 SizeT new_size;
256 ExeContext** new_ec_htab;
257
258 vg_assert(ec_htab_size_idx >= 0 && ec_htab_size_idx < N_EC_PRIMES);
259 if (ec_htab_size_idx == N_EC_PRIMES-1)
260 return; /* out of primes - can't resize further */
261
262 new_size = ec_primes[ec_htab_size_idx + 1];
sewardj9c606bd2008-09-18 18:12:50 +0000263 new_ec_htab = VG_(arena_malloc)(VG_AR_EXECTXT, "execontext.reh1",
sewardj9877e9b2007-08-23 10:24:30 +0000264 sizeof(ExeContext*) * new_size);
265
266 VG_(debugLog)(
267 1, "execontext",
268 "resizing htab from size %lu to %lu (idx %lu) Total#ECs=%llu\n",
269 ec_htab_size, new_size, ec_htab_size_idx + 1, ec_totstored);
270
271 for (i = 0; i < new_size; i++)
272 new_ec_htab[i] = NULL;
273
274 for (i = 0; i < ec_htab_size; i++) {
275 ExeContext* cur = ec_htab[i];
276 while (cur) {
277 ExeContext* next = cur->chain;
278 UWord hash = calc_hash(cur->ips, cur->n_ips, new_size);
279 vg_assert(hash < new_size);
280 cur->chain = new_ec_htab[hash];
281 new_ec_htab[hash] = cur;
282 cur = next;
283 }
284 }
285
286 VG_(arena_free)(VG_AR_EXECTXT, ec_htab);
287 ec_htab = new_ec_htab;
288 ec_htab_size = new_size;
289 ec_htab_size_idx++;
290}
291
sewardj7cf4e6b2008-05-01 20:24:26 +0000292/* Do the first part of getting a stack trace: actually unwind the
293 stack, and hand the results off to the duplicate-trace-finder
294 (_wrk2). */
295static ExeContext* record_ExeContext_wrk2 ( Addr* ips, UInt n_ips ); /*fwds*/
sewardj3059d272007-12-21 01:24:59 +0000296static ExeContext* record_ExeContext_wrk ( ThreadId tid, Word first_ip_delta,
297 Bool first_ip_only )
njnd01fef72005-03-25 23:35:48 +0000298{
sewardj7cf4e6b2008-05-01 20:24:26 +0000299 Addr ips[VG_DEEPEST_BACKTRACE];
300 UInt n_ips;
sewardjbb2e09f2006-10-24 21:43:38 +0000301
sewardj7cf4e6b2008-05-01 20:24:26 +0000302 init_ExeContext_storage();
sewardjbb2e09f2006-10-24 21:43:38 +0000303
304 vg_assert(sizeof(void*) == sizeof(UWord));
305 vg_assert(sizeof(void*) == sizeof(Addr));
njnd01fef72005-03-25 23:35:48 +0000306
sewardj7cf4e6b2008-05-01 20:24:26 +0000307 vg_assert(VG_(is_valid_tid)(tid));
308
njnc0ec8e92005-12-25 06:34:04 +0000309 vg_assert(VG_(clo_backtrace_size) >= 1 &&
310 VG_(clo_backtrace_size) <= VG_DEEPEST_BACKTRACE);
njnd01fef72005-03-25 23:35:48 +0000311
sewardj3059d272007-12-21 01:24:59 +0000312 if (first_ip_only) {
sewardj3059d272007-12-21 01:24:59 +0000313 n_ips = 1;
314 ips[0] = VG_(get_IP)(tid);
315 } else {
316 n_ips = VG_(get_StackTrace)( tid, ips, VG_(clo_backtrace_size),
sewardjb8b79ad2008-03-03 01:35:41 +0000317 NULL/*array to dump SP values in*/,
318 NULL/*array to dump FP values in*/,
sewardj3059d272007-12-21 01:24:59 +0000319 first_ip_delta );
320 }
321
njn3a4b58f2009-05-07 23:08:10 +0000322 return record_ExeContext_wrk2 ( ips, n_ips );
sewardj7cf4e6b2008-05-01 20:24:26 +0000323}
324
325/* Do the second part of getting a stack trace: ips[0 .. n_ips-1]
326 holds a proposed trace. Find or allocate a suitable ExeContext.
327 Note that callers must have done init_ExeContext_storage() before
328 getting to this point. */
329static ExeContext* record_ExeContext_wrk2 ( Addr* ips, UInt n_ips )
330{
331 Int i;
332 Bool same;
333 UWord hash;
334 ExeContext* new_ec;
335 ExeContext* list;
336 ExeContext *prev2, *prev;
337
338 static UInt ctr = 0;
339
sewardj9877e9b2007-08-23 10:24:30 +0000340 tl_assert(n_ips >= 1 && n_ips <= VG_(clo_backtrace_size));
njnd01fef72005-03-25 23:35:48 +0000341
342 /* Now figure out if we've seen this one before. First hash it so
343 as to determine the list number. */
sewardj9877e9b2007-08-23 10:24:30 +0000344 hash = calc_hash( ips, n_ips, ec_htab_size );
njnd01fef72005-03-25 23:35:48 +0000345
sewardj9877e9b2007-08-23 10:24:30 +0000346 /* And (the expensive bit) look a for matching entry in the list. */
njnd01fef72005-03-25 23:35:48 +0000347
348 ec_searchreqs++;
349
sewardjbb2e09f2006-10-24 21:43:38 +0000350 prev2 = NULL;
351 prev = NULL;
sewardj9877e9b2007-08-23 10:24:30 +0000352 list = ec_htab[hash];
njnd01fef72005-03-25 23:35:48 +0000353
354 while (True) {
355 if (list == NULL) break;
356 ec_searchcmps++;
357 same = True;
njnc0ec8e92005-12-25 06:34:04 +0000358 for (i = 0; i < n_ips; i++) {
njnd01fef72005-03-25 23:35:48 +0000359 if (list->ips[i] != ips[i]) {
360 same = False;
361 break;
362 }
363 }
364 if (same) break;
sewardjbb2e09f2006-10-24 21:43:38 +0000365 prev2 = prev;
366 prev = list;
sewardj9877e9b2007-08-23 10:24:30 +0000367 list = list->chain;
njnd01fef72005-03-25 23:35:48 +0000368 }
369
370 if (list != NULL) {
sewardjbb2e09f2006-10-24 21:43:38 +0000371 /* Yay! We found it. Once every 8 searches, move it one step
372 closer to the start of the list to make future searches
373 cheaper. */
sewardj9877e9b2007-08-23 10:24:30 +0000374 if (0 == ((ctr++) & 7)) {
375 if (prev2 != NULL && prev != NULL) {
376 /* Found at 3rd or later position in the chain. */
377 vg_assert(prev2->chain == prev);
378 vg_assert(prev->chain == list);
379 prev2->chain = list;
380 prev->chain = list->chain;
381 list->chain = prev;
382 }
383 else if (prev2 == NULL && prev != NULL) {
384 /* Found at 2nd position in the chain. */
385 vg_assert(ec_htab[hash] == prev);
386 vg_assert(prev->chain == list);
387 prev->chain = list->chain;
388 list->chain = prev;
389 ec_htab[hash] = list;
390 }
sewardjbb2e09f2006-10-24 21:43:38 +0000391 }
njnd01fef72005-03-25 23:35:48 +0000392 return list;
393 }
394
395 /* Bummer. We have to allocate a new context record. */
396 ec_totstored++;
397
sewardj9c606bd2008-09-18 18:12:50 +0000398 new_ec = VG_(arena_malloc)( VG_AR_EXECTXT, "execontext.rEw2.2",
njnc0ec8e92005-12-25 06:34:04 +0000399 sizeof(struct _ExeContext)
400 + n_ips * sizeof(Addr) );
njnd01fef72005-03-25 23:35:48 +0000401
njnc0ec8e92005-12-25 06:34:04 +0000402 for (i = 0; i < n_ips; i++)
njnd01fef72005-03-25 23:35:48 +0000403 new_ec->ips[i] = ips[i];
404
sewardj7cf4e6b2008-05-01 20:24:26 +0000405 vg_assert(VG_(is_plausible_ECU)(ec_next_ecu));
406 new_ec->ecu = ec_next_ecu;
407 ec_next_ecu += 4;
408 if (ec_next_ecu == 0) {
409 /* Urr. Now we're hosed; we emitted 2^30 ExeContexts already
410 and have run out of numbers. Not sure what to do. */
411 VG_(core_panic)("m_execontext: more than 2^30 ExeContexts created");
412 }
413
njnc0ec8e92005-12-25 06:34:04 +0000414 new_ec->n_ips = n_ips;
sewardj9877e9b2007-08-23 10:24:30 +0000415 new_ec->chain = ec_htab[hash];
416 ec_htab[hash] = new_ec;
417
418 /* Resize the hash table, maybe? */
419 if ( ((ULong)ec_totstored) > ((ULong)ec_htab_size) ) {
420 vg_assert(ec_htab_size_idx >= 0 && ec_htab_size_idx < N_EC_PRIMES);
421 if (ec_htab_size_idx < N_EC_PRIMES-1)
422 resize_ec_htab();
423 }
njnd01fef72005-03-25 23:35:48 +0000424
njnd01fef72005-03-25 23:35:48 +0000425 return new_ec;
426}
427
sewardj3059d272007-12-21 01:24:59 +0000428ExeContext* VG_(record_ExeContext)( ThreadId tid, Word first_ip_delta ) {
429 return record_ExeContext_wrk( tid, first_ip_delta,
430 False/*!first_ip_only*/ );
431}
432
433ExeContext* VG_(record_depth_1_ExeContext)( ThreadId tid ) {
434 return record_ExeContext_wrk( tid, 0/*first_ip_delta*/,
435 True/*first_ip_only*/ );
436}
437
sewardj7cf4e6b2008-05-01 20:24:26 +0000438ExeContext* VG_(make_depth_1_ExeContext_from_Addr)( Addr a ) {
439 init_ExeContext_storage();
440 return record_ExeContext_wrk2( &a, 1 );
441}
sewardj3059d272007-12-21 01:24:59 +0000442
sewardj7cf4e6b2008-05-01 20:24:26 +0000443StackTrace VG_(get_ExeContext_StackTrace) ( ExeContext* e ) {
njnd01fef72005-03-25 23:35:48 +0000444 return e->ips;
445}
446
sewardj7cf4e6b2008-05-01 20:24:26 +0000447UInt VG_(get_ECU_from_ExeContext)( ExeContext* e ) {
448 vg_assert(VG_(is_plausible_ECU)(e->ecu));
449 return e->ecu;
450}
451
452Int VG_(get_ExeContext_n_ips)( ExeContext* e ) {
453 vg_assert(e->n_ips >= 1);
454 return e->n_ips;
455}
456
457ExeContext* VG_(get_ExeContext_from_ECU)( UInt ecu )
458{
459 UWord i;
460 ExeContext* ec;
461 vg_assert(VG_(is_plausible_ECU)(ecu));
462 vg_assert(ec_htab_size > 0);
463 for (i = 0; i < ec_htab_size; i++) {
464 for (ec = ec_htab[i]; ec; ec = ec->chain) {
465 if (ec->ecu == ecu)
466 return ec;
467 }
468 }
469 return NULL;
470}
471
sewardjf98e1c02008-10-25 16:22:41 +0000472ExeContext* VG_(make_ExeContext_from_StackTrace)( Addr* ips, UInt n_ips )
473{
474 return record_ExeContext_wrk2(ips, n_ips);
475}
476
njnd01fef72005-03-25 23:35:48 +0000477/*--------------------------------------------------------------------*/
sewardj267100d2005-04-24 12:33:12 +0000478/*--- end m_execontext.c ---*/
njnd01fef72005-03-25 23:35:48 +0000479/*--------------------------------------------------------------------*/