m_execontext.c revision 9877e9b983797707c37bbf01bba491e894073617
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-2007 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_execontext.h"    // self
34#include "pub_core_libcassert.h"
35#include "pub_core_libcprint.h"     // For VG_(message)()
36#include "pub_core_mallocfree.h"
37#include "pub_core_options.h"
38#include "pub_core_stacktrace.h"
39
40/*------------------------------------------------------------*/
41/*--- Low-level ExeContext storage.                        ---*/
42/*------------------------------------------------------------*/
43
44/* 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). */
72
73struct _ExeContext {
74   struct _ExeContext* chain;
75   UInt   n_ips;
76   /* Variable-length array.  The size is 'n_ips'; at
77      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
82
83/* 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 */
87
88
89/* Stats only: the number of times the system was searched to locate a
90   context. */
91static ULong ec_searchreqs;
92
93/* Stats only: the number of full context comparisons done. */
94static ULong ec_searchcmps;
95
96/* Stats only: total number of stored contexts. */
97static ULong ec_totstored;
98
99/* Number of 2, 4 and (fast) full cmps done. */
100static ULong ec_cmp2s;
101static ULong ec_cmp4s;
102static ULong ec_cmpAlls;
103
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;
123
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
131   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,
140      "   exectx: %,lu lists, %,llu contexts (avg %,llu per list)",
141      ec_htab_size, ec_totstored, ec_totstored / ec_htab_size
142   );
143   VG_(message)(Vg_DebugMsg,
144      "   exectx: %,llu searches, %,llu full compares (%,llu per 1000)",
145      ec_searchreqs, ec_searchcmps,
146      ec_searchreqs == 0
147         ? 0L
148         : ( (ec_searchcmps * 1000) / ec_searchreqs )
149   );
150   VG_(message)(Vg_DebugMsg,
151      "   exectx: %,llu cmp2, %,llu cmp4, %,llu cmpAll",
152      ec_cmp2s, ec_cmp4s, ec_cmpAlls
153   );
154}
155
156
157/* Print an ExeContext. */
158void VG_(pp_ExeContext) ( ExeContext* ec )
159{
160   VG_(pp_StackTrace)( ec->ips, ec->n_ips );
161}
162
163
164/* Compare two ExeContexts, comparing all callers. */
165Bool VG_(eq_ExeContext) ( VgRes res, ExeContext* e1, ExeContext* e2 )
166{
167   Int i;
168
169   if (e1 == NULL || e2 == NULL)
170      return False;
171
172   // Must be at least one address in each trace.
173   tl_assert(e1->n_ips >= 1 && e2->n_ips >= 1);
174
175   switch (res) {
176   case Vg_LowRes:
177      /* Just compare the top two callers. */
178      ec_cmp2s++;
179      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      }
185      return True;
186
187   case Vg_MedRes:
188      /* Just compare the top four callers. */
189      ec_cmp4s++;
190      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      }
196      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
209/* 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
213   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
216   Text type.
217
218   Also checks whether the hash table needs expanding, and expands it
219   if so. */
220
221static 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
228static 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
280ExeContext* VG_(record_ExeContext) ( ThreadId tid )
281{
282   Int         i;
283   Addr        ips[VG_DEEPEST_BACKTRACE];
284   Bool        same;
285   UWord       hash;
286   ExeContext* new_ec;
287   ExeContext* list;
288   UInt        n_ips;
289   ExeContext  *prev2, *prev;
290
291   static UInt ctr = 0;
292
293   vg_assert(sizeof(void*) == sizeof(UWord));
294   vg_assert(sizeof(void*) == sizeof(Addr));
295
296   init_ExeContext_storage();
297   vg_assert(VG_(clo_backtrace_size) >= 1 &&
298             VG_(clo_backtrace_size) <= VG_DEEPEST_BACKTRACE);
299
300   n_ips = VG_(get_StackTrace)( tid, ips, VG_(clo_backtrace_size) );
301   tl_assert(n_ips >= 1 && n_ips <= VG_(clo_backtrace_size));
302
303   /* Now figure out if we've seen this one before.  First hash it so
304      as to determine the list number. */
305   hash = calc_hash( ips, n_ips, ec_htab_size );
306
307   /* And (the expensive bit) look a for matching entry in the list. */
308
309   ec_searchreqs++;
310
311   prev2 = NULL;
312   prev  = NULL;
313   list  = ec_htab[hash];
314
315   while (True) {
316      if (list == NULL) break;
317      ec_searchcmps++;
318      same = True;
319      for (i = 0; i < n_ips; i++) {
320         if (list->ips[i] != ips[i]) {
321            same = False;
322            break;
323         }
324      }
325      if (same) break;
326      prev2 = prev;
327      prev  = list;
328      list  = list->chain;
329   }
330
331   if (list != NULL) {
332      /* Yay!  We found it.  Once every 8 searches, move it one step
333         closer to the start of the list to make future searches
334         cheaper. */
335      if (0 == ((ctr++) & 7)) {
336         if (prev2 != NULL && prev != NULL) {
337            /* Found at 3rd or later position in the chain. */
338            vg_assert(prev2->chain == prev);
339            vg_assert(prev->chain  == list);
340            prev2->chain = list;
341            prev->chain  = list->chain;
342            list->chain  = prev;
343         }
344         else if (prev2 == NULL && prev != NULL) {
345            /* Found at 2nd position in the chain. */
346            vg_assert(ec_htab[hash] == prev);
347            vg_assert(prev->chain == list);
348            prev->chain = list->chain;
349            list->chain = prev;
350            ec_htab[hash] = list;
351         }
352      }
353      return list;
354   }
355
356   /* Bummer.  We have to allocate a new context record. */
357   ec_totstored++;
358
359   new_ec = VG_(arena_malloc)( VG_AR_EXECTXT,
360                               sizeof(struct _ExeContext)
361                               + n_ips * sizeof(Addr) );
362
363   for (i = 0; i < n_ips; i++)
364      new_ec->ips[i] = ips[i];
365
366   new_ec->n_ips = n_ips;
367   new_ec->chain = ec_htab[hash];
368   ec_htab[hash] = new_ec;
369
370   /* Resize the hash table, maybe? */
371   if ( ((ULong)ec_totstored) > ((ULong)ec_htab_size) ) {
372      vg_assert(ec_htab_size_idx >= 0 && ec_htab_size_idx < N_EC_PRIMES);
373      if (ec_htab_size_idx < N_EC_PRIMES-1)
374         resize_ec_htab();
375   }
376
377   return new_ec;
378}
379
380StackTrace VG_(extract_StackTrace) ( ExeContext* e )
381{
382   return e->ips;
383}
384
385/*--------------------------------------------------------------------*/
386/*--- end                                           m_execontext.c ---*/
387/*--------------------------------------------------------------------*/
388