blob: 0f203793b1fd5cb433de5020cc0d7e530be04810 [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
sewardj9ebd6e02007-01-08 06:01:59 +000010 Copyright (C) 2000-2007 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"
33#include "pub_core_execontext.h" // self
njn132bfcc2005-06-04 19:16:06 +000034#include "pub_core_libcassert.h"
njn24a6efb2005-06-20 03:36:51 +000035#include "pub_core_libcprint.h" // For VG_(message)()
njnaf1d7df2005-06-11 01:31:52 +000036#include "pub_core_mallocfree.h"
njn20242342005-05-16 23:31:24 +000037#include "pub_core_options.h"
njnd0d7c1f2005-06-21 00:33:19 +000038#include "pub_core_stacktrace.h"
njnd01fef72005-03-25 23:35:48 +000039
40/*------------------------------------------------------------*/
41/*--- Low-level ExeContext storage. ---*/
42/*------------------------------------------------------------*/
43
sewardj9877e9b2007-08-23 10:24:30 +000044/* The first 4 IP values are used in comparisons to remove duplicate
45 errors, and for comparing against suppression specifications. The
46 rest are purely informational (but often important).
47
48 The contexts are stored in a traditional chained hash table, so as
49 to allow quick determination of whether a new context already
50 exists. The hash table starts small and expands dynamically, so as
51 to keep the load factor below 1.0.
52
53 The idea is only to ever store any one context once, so as to save
54 space and make exact comparisons faster. */
55
56
57/* Primes for the hash table */
58
59#define N_EC_PRIMES 18
60
61static SizeT ec_primes[N_EC_PRIMES] = {
62 769UL, 1543UL, 3079UL, 6151UL,
63 12289UL, 24593UL, 49157UL, 98317UL,
64 196613UL, 393241UL, 786433UL, 1572869UL,
65 3145739UL, 6291469UL, 12582917UL, 25165843UL,
66 50331653UL, 100663319UL
67};
68
69
70/* Each element is present in a hash chain, and also contains a
71 variable length array of guest code addresses (the useful part). */
njnd01fef72005-03-25 23:35:48 +000072
73struct _ExeContext {
sewardj9877e9b2007-08-23 10:24:30 +000074 struct _ExeContext* chain;
njnc0ec8e92005-12-25 06:34:04 +000075 UInt n_ips;
76 /* Variable-length array. The size is 'n_ips'; at
njnd01fef72005-03-25 23:35:48 +000077 least 1, at most VG_DEEPEST_BACKTRACE. [0] is the current IP,
78 [1] is its caller, [2] is the caller of [1], etc. */
79 Addr ips[0];
80};
81
njnd01fef72005-03-25 23:35:48 +000082
sewardj9877e9b2007-08-23 10:24:30 +000083/* This is the dynamically expanding hash table. */
84static ExeContext** ec_htab; /* array [ec_htab_size] of ExeContext* */
85static SizeT ec_htab_size; /* one of the values in ec_primes */
86static SizeT ec_htab_size_idx; /* 0 .. N_EC_PRIMES-1 */
njnd01fef72005-03-25 23:35:48 +000087
njnd01fef72005-03-25 23:35:48 +000088
89/* Stats only: the number of times the system was searched to locate a
90 context. */
njn0fd92f42005-10-06 03:32:42 +000091static ULong ec_searchreqs;
njnd01fef72005-03-25 23:35:48 +000092
93/* Stats only: the number of full context comparisons done. */
njn0fd92f42005-10-06 03:32:42 +000094static ULong ec_searchcmps;
njnd01fef72005-03-25 23:35:48 +000095
96/* Stats only: total number of stored contexts. */
njn0fd92f42005-10-06 03:32:42 +000097static ULong ec_totstored;
njnd01fef72005-03-25 23:35:48 +000098
99/* Number of 2, 4 and (fast) full cmps done. */
njn0fd92f42005-10-06 03:32:42 +0000100static ULong ec_cmp2s;
101static ULong ec_cmp4s;
102static ULong ec_cmpAlls;
njnd01fef72005-03-25 23:35:48 +0000103
104
105/*------------------------------------------------------------*/
106/*--- Exported functions. ---*/
107/*------------------------------------------------------------*/
108
109
110/* Initialise this subsystem. */
111static void init_ExeContext_storage ( void )
112{
113 Int i;
114 static Bool init_done = False;
115 if (init_done)
116 return;
117 ec_searchreqs = 0;
118 ec_searchcmps = 0;
119 ec_totstored = 0;
120 ec_cmp2s = 0;
121 ec_cmp4s = 0;
122 ec_cmpAlls = 0;
sewardj9877e9b2007-08-23 10:24:30 +0000123
124 ec_htab_size_idx = 0;
125 ec_htab_size = ec_primes[ec_htab_size_idx];
126 ec_htab = VG_(arena_malloc)(VG_AR_EXECTXT,
127 sizeof(ExeContext*) * ec_htab_size);
128 for (i = 0; i < ec_htab_size; i++)
129 ec_htab[i] = NULL;
130
njnd01fef72005-03-25 23:35:48 +0000131 init_done = True;
132}
133
134
135/* Print stats. */
136void VG_(print_ExeContext_stats) ( void )
137{
138 init_ExeContext_storage();
139 VG_(message)(Vg_DebugMsg,
njn0fd92f42005-10-06 03:32:42 +0000140 " exectx: %,lu lists, %,llu contexts (avg %,llu per list)",
sewardj9877e9b2007-08-23 10:24:30 +0000141 ec_htab_size, ec_totstored, ec_totstored / ec_htab_size
njnd01fef72005-03-25 23:35:48 +0000142 );
143 VG_(message)(Vg_DebugMsg,
njn0fd92f42005-10-06 03:32:42 +0000144 " exectx: %,llu searches, %,llu full compares (%,llu per 1000)",
njnd01fef72005-03-25 23:35:48 +0000145 ec_searchreqs, ec_searchcmps,
146 ec_searchreqs == 0
njn0fd92f42005-10-06 03:32:42 +0000147 ? 0L
148 : ( (ec_searchcmps * 1000) / ec_searchreqs )
njnd01fef72005-03-25 23:35:48 +0000149 );
150 VG_(message)(Vg_DebugMsg,
njn0fd92f42005-10-06 03:32:42 +0000151 " exectx: %,llu cmp2, %,llu cmp4, %,llu cmpAll",
njnd01fef72005-03-25 23:35:48 +0000152 ec_cmp2s, ec_cmp4s, ec_cmpAlls
153 );
154}
155
156
157/* Print an ExeContext. */
158void VG_(pp_ExeContext) ( ExeContext* ec )
159{
njnc0ec8e92005-12-25 06:34:04 +0000160 VG_(pp_StackTrace)( ec->ips, ec->n_ips );
njnd01fef72005-03-25 23:35:48 +0000161}
162
163
164/* Compare two ExeContexts, comparing all callers. */
165Bool VG_(eq_ExeContext) ( VgRes res, ExeContext* e1, ExeContext* e2 )
166{
njnc0ec8e92005-12-25 06:34:04 +0000167 Int i;
168
njnd01fef72005-03-25 23:35:48 +0000169 if (e1 == NULL || e2 == NULL)
170 return False;
njnc0ec8e92005-12-25 06:34:04 +0000171
172 // Must be at least one address in each trace.
173 tl_assert(e1->n_ips >= 1 && e2->n_ips >= 1);
174
njnd01fef72005-03-25 23:35:48 +0000175 switch (res) {
176 case Vg_LowRes:
177 /* Just compare the top two callers. */
178 ec_cmp2s++;
njnc0ec8e92005-12-25 06:34:04 +0000179 for (i = 0; i < 2; i++) {
180 if ( (e1->n_ips <= i) && (e2->n_ips <= i)) return True;
181 if ( (e1->n_ips <= i) && !(e2->n_ips <= i)) return False;
182 if (!(e1->n_ips <= i) && (e2->n_ips <= i)) return False;
183 if (e1->ips[i] != e2->ips[i]) return False;
184 }
njnd01fef72005-03-25 23:35:48 +0000185 return True;
186
187 case Vg_MedRes:
188 /* Just compare the top four callers. */
189 ec_cmp4s++;
njnc0ec8e92005-12-25 06:34:04 +0000190 for (i = 0; i < 4; i++) {
191 if ( (e1->n_ips <= i) && (e2->n_ips <= i)) return True;
192 if ( (e1->n_ips <= i) && !(e2->n_ips <= i)) return False;
193 if (!(e1->n_ips <= i) && (e2->n_ips <= i)) return False;
194 if (e1->ips[i] != e2->ips[i]) return False;
195 }
njnd01fef72005-03-25 23:35:48 +0000196 return True;
197
198 case Vg_HighRes:
199 ec_cmpAlls++;
200 /* Compare them all -- just do pointer comparison. */
201 if (e1 != e2) return False;
202 return True;
203
204 default:
205 VG_(core_panic)("VG_(eq_ExeContext): unrecognised VgRes");
206 }
207}
208
sewardj9877e9b2007-08-23 10:24:30 +0000209/* VG_(record_ExeContext) is the head honcho here. Take a snapshot of
210 the client's stack. Search our collection of ExeContexts to see if
211 we already have it, and if not, allocate a new one. Either way,
212 return a pointer to the context. If there is a matching context we
njnd01fef72005-03-25 23:35:48 +0000213 guarantee to not allocate a new one. Thus we never store
214 duplicates, and so exact equality can be quickly done as equality
215 on the returned ExeContext* values themselves. Inspired by Hugs's
sewardj9877e9b2007-08-23 10:24:30 +0000216 Text type.
217
218 Also checks whether the hash table needs expanding, and expands it
219 if so. */
220
sewardjbb2e09f2006-10-24 21:43:38 +0000221static inline UWord ROLW ( UWord w, Int n )
222{
223 Int bpw = 8 * sizeof(UWord);
224 w = (w << n) | (w >> (bpw-n));
225 return w;
226}
227
sewardj9877e9b2007-08-23 10:24:30 +0000228static UWord calc_hash ( Addr* ips, UInt n_ips, UWord htab_sz )
229{
230 UInt i;
231 UWord hash = 0;
232 vg_assert(htab_sz > 0);
233 for (i = 0; i < n_ips; i++) {
234 hash ^= ips[i];
235 hash = ROLW(hash, 19);
236 }
237 return hash % htab_sz;
238}
239
240static void resize_ec_htab ( void )
241{
242 SizeT i;
243 SizeT new_size;
244 ExeContext** new_ec_htab;
245
246 vg_assert(ec_htab_size_idx >= 0 && ec_htab_size_idx < N_EC_PRIMES);
247 if (ec_htab_size_idx == N_EC_PRIMES-1)
248 return; /* out of primes - can't resize further */
249
250 new_size = ec_primes[ec_htab_size_idx + 1];
251 new_ec_htab = VG_(arena_malloc)(VG_AR_EXECTXT,
252 sizeof(ExeContext*) * new_size);
253
254 VG_(debugLog)(
255 1, "execontext",
256 "resizing htab from size %lu to %lu (idx %lu) Total#ECs=%llu\n",
257 ec_htab_size, new_size, ec_htab_size_idx + 1, ec_totstored);
258
259 for (i = 0; i < new_size; i++)
260 new_ec_htab[i] = NULL;
261
262 for (i = 0; i < ec_htab_size; i++) {
263 ExeContext* cur = ec_htab[i];
264 while (cur) {
265 ExeContext* next = cur->chain;
266 UWord hash = calc_hash(cur->ips, cur->n_ips, new_size);
267 vg_assert(hash < new_size);
268 cur->chain = new_ec_htab[hash];
269 new_ec_htab[hash] = cur;
270 cur = next;
271 }
272 }
273
274 VG_(arena_free)(VG_AR_EXECTXT, ec_htab);
275 ec_htab = new_ec_htab;
276 ec_htab_size = new_size;
277 ec_htab_size_idx++;
278}
279
sewardj39f34232007-11-09 23:02:28 +0000280ExeContext* VG_(record_ExeContext) ( ThreadId tid, Word first_ip_delta )
njnd01fef72005-03-25 23:35:48 +0000281{
282 Int i;
283 Addr ips[VG_DEEPEST_BACKTRACE];
284 Bool same;
285 UWord hash;
286 ExeContext* new_ec;
287 ExeContext* list;
njnc0ec8e92005-12-25 06:34:04 +0000288 UInt n_ips;
sewardjbb2e09f2006-10-24 21:43:38 +0000289 ExeContext *prev2, *prev;
290
291 static UInt ctr = 0;
292
293 vg_assert(sizeof(void*) == sizeof(UWord));
294 vg_assert(sizeof(void*) == sizeof(Addr));
njnd01fef72005-03-25 23:35:48 +0000295
njnd01fef72005-03-25 23:35:48 +0000296 init_ExeContext_storage();
njnc0ec8e92005-12-25 06:34:04 +0000297 vg_assert(VG_(clo_backtrace_size) >= 1 &&
298 VG_(clo_backtrace_size) <= VG_DEEPEST_BACKTRACE);
njnd01fef72005-03-25 23:35:48 +0000299
sewardj39f34232007-11-09 23:02:28 +0000300 n_ips = VG_(get_StackTrace)( tid, ips, VG_(clo_backtrace_size),
301 first_ip_delta );
sewardj9877e9b2007-08-23 10:24:30 +0000302 tl_assert(n_ips >= 1 && n_ips <= VG_(clo_backtrace_size));
njnd01fef72005-03-25 23:35:48 +0000303
304 /* Now figure out if we've seen this one before. First hash it so
305 as to determine the list number. */
sewardj9877e9b2007-08-23 10:24:30 +0000306 hash = calc_hash( ips, n_ips, ec_htab_size );
njnd01fef72005-03-25 23:35:48 +0000307
sewardj9877e9b2007-08-23 10:24:30 +0000308 /* And (the expensive bit) look a for matching entry in the list. */
njnd01fef72005-03-25 23:35:48 +0000309
310 ec_searchreqs++;
311
sewardjbb2e09f2006-10-24 21:43:38 +0000312 prev2 = NULL;
313 prev = NULL;
sewardj9877e9b2007-08-23 10:24:30 +0000314 list = ec_htab[hash];
njnd01fef72005-03-25 23:35:48 +0000315
316 while (True) {
317 if (list == NULL) break;
318 ec_searchcmps++;
319 same = True;
njnc0ec8e92005-12-25 06:34:04 +0000320 for (i = 0; i < n_ips; i++) {
njnd01fef72005-03-25 23:35:48 +0000321 if (list->ips[i] != ips[i]) {
322 same = False;
323 break;
324 }
325 }
326 if (same) break;
sewardjbb2e09f2006-10-24 21:43:38 +0000327 prev2 = prev;
328 prev = list;
sewardj9877e9b2007-08-23 10:24:30 +0000329 list = list->chain;
njnd01fef72005-03-25 23:35:48 +0000330 }
331
332 if (list != NULL) {
sewardjbb2e09f2006-10-24 21:43:38 +0000333 /* Yay! We found it. Once every 8 searches, move it one step
334 closer to the start of the list to make future searches
335 cheaper. */
sewardj9877e9b2007-08-23 10:24:30 +0000336 if (0 == ((ctr++) & 7)) {
337 if (prev2 != NULL && prev != NULL) {
338 /* Found at 3rd or later position in the chain. */
339 vg_assert(prev2->chain == prev);
340 vg_assert(prev->chain == list);
341 prev2->chain = list;
342 prev->chain = list->chain;
343 list->chain = prev;
344 }
345 else if (prev2 == NULL && prev != NULL) {
346 /* Found at 2nd position in the chain. */
347 vg_assert(ec_htab[hash] == prev);
348 vg_assert(prev->chain == list);
349 prev->chain = list->chain;
350 list->chain = prev;
351 ec_htab[hash] = list;
352 }
sewardjbb2e09f2006-10-24 21:43:38 +0000353 }
njnd01fef72005-03-25 23:35:48 +0000354 return list;
355 }
356
357 /* Bummer. We have to allocate a new context record. */
358 ec_totstored++;
359
360 new_ec = VG_(arena_malloc)( VG_AR_EXECTXT,
njnc0ec8e92005-12-25 06:34:04 +0000361 sizeof(struct _ExeContext)
362 + n_ips * sizeof(Addr) );
njnd01fef72005-03-25 23:35:48 +0000363
njnc0ec8e92005-12-25 06:34:04 +0000364 for (i = 0; i < n_ips; i++)
njnd01fef72005-03-25 23:35:48 +0000365 new_ec->ips[i] = ips[i];
366
njnc0ec8e92005-12-25 06:34:04 +0000367 new_ec->n_ips = n_ips;
sewardj9877e9b2007-08-23 10:24:30 +0000368 new_ec->chain = ec_htab[hash];
369 ec_htab[hash] = new_ec;
370
371 /* Resize the hash table, maybe? */
372 if ( ((ULong)ec_totstored) > ((ULong)ec_htab_size) ) {
373 vg_assert(ec_htab_size_idx >= 0 && ec_htab_size_idx < N_EC_PRIMES);
374 if (ec_htab_size_idx < N_EC_PRIMES-1)
375 resize_ec_htab();
376 }
njnd01fef72005-03-25 23:35:48 +0000377
njnd01fef72005-03-25 23:35:48 +0000378 return new_ec;
379}
380
381StackTrace VG_(extract_StackTrace) ( ExeContext* e )
382{
383 return e->ips;
384}
385
386/*--------------------------------------------------------------------*/
sewardj267100d2005-04-24 12:33:12 +0000387/*--- end m_execontext.c ---*/
njnd01fef72005-03-25 23:35:48 +0000388/*--------------------------------------------------------------------*/