m_execontext.c revision a0b6b2cf9abc7b0d87be1215a245eaccc0452af9
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-2008 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_threadstate.h"   // VG_(is_valid_tid)
41#include "pub_core_execontext.h"    // self
42
43/*------------------------------------------------------------*/
44/*--- Low-level ExeContext storage.                        ---*/
45/*------------------------------------------------------------*/
46
47/* 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). */
75
76struct _ExeContext {
77   struct _ExeContext* chain;
78   /* 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;
84   /* Variable-length array.  The size is 'n_ips'; at
85      least 1, at most VG_DEEPEST_BACKTRACE.  [0] is the current IP,
86      [1] is its caller, [2] is the caller of [1], etc. */
87   UInt n_ips;
88   Addr ips[0];
89};
90
91
92/* 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 */
96
97/* ECU serial number */
98static UInt ec_next_ecu = 4; /* We must never issue zero */
99
100
101/* Stats only: the number of times the system was searched to locate a
102   context. */
103static ULong ec_searchreqs;
104
105/* Stats only: the number of full context comparisons done. */
106static ULong ec_searchcmps;
107
108/* Stats only: total number of stored contexts. */
109static ULong ec_totstored;
110
111/* Number of 2, 4 and (fast) full cmps done. */
112static ULong ec_cmp2s;
113static ULong ec_cmp4s;
114static ULong ec_cmpAlls;
115
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;
127   if (LIKELY(init_done))
128      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;
135
136   ec_htab_size_idx = 0;
137   ec_htab_size = ec_primes[ec_htab_size_idx];
138   ec_htab = VG_(arena_malloc)(VG_AR_EXECTXT,
139                               sizeof(ExeContext*) * ec_htab_size);
140   for (i = 0; i < ec_htab_size; i++)
141      ec_htab[i] = NULL;
142
143   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,
152      "   exectx: %'lu lists, %'llu contexts (avg %'llu per list)",
153      ec_htab_size, ec_totstored, ec_totstored / ec_htab_size
154   );
155   VG_(message)(Vg_DebugMsg,
156      "   exectx: %'llu searches, %'llu full compares (%'llu per 1000)",
157      ec_searchreqs, ec_searchcmps,
158      ec_searchreqs == 0
159         ? 0L
160         : ( (ec_searchcmps * 1000) / ec_searchreqs )
161   );
162   VG_(message)(Vg_DebugMsg,
163      "   exectx: %'llu cmp2, %'llu cmp4, %'llu cmpAll",
164      ec_cmp2s, ec_cmp4s, ec_cmpAlls
165   );
166}
167
168
169/* Print an ExeContext. */
170void VG_(pp_ExeContext) ( ExeContext* ec )
171{
172   VG_(pp_StackTrace)( ec->ips, ec->n_ips );
173}
174
175
176/* Compare two ExeContexts, comparing all callers. */
177Bool VG_(eq_ExeContext) ( VgRes res, ExeContext* e1, ExeContext* e2 )
178{
179   Int i;
180
181   if (e1 == NULL || e2 == NULL)
182      return False;
183
184   // Must be at least one address in each trace.
185   tl_assert(e1->n_ips >= 1 && e2->n_ips >= 1);
186
187   switch (res) {
188   case Vg_LowRes:
189      /* Just compare the top two callers. */
190      ec_cmp2s++;
191      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      }
197      return True;
198
199   case Vg_MedRes:
200      /* Just compare the top four callers. */
201      ec_cmp4s++;
202      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      }
208      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
221/* 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
225   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
228   Text type.
229
230   Also checks whether the hash table needs expanding, and expands it
231   if so. */
232
233static 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
240static 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];
263   new_ec_htab = VG_(arena_malloc)(VG_AR_EXECTXT,
264                                   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
292/* 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*/
296static ExeContext* record_ExeContext_wrk ( ThreadId tid, Word first_ip_delta,
297                                           Bool first_ip_only )
298{
299   Addr ips[VG_DEEPEST_BACKTRACE];
300   UInt n_ips;
301
302   init_ExeContext_storage();
303
304   vg_assert(sizeof(void*) == sizeof(UWord));
305   vg_assert(sizeof(void*) == sizeof(Addr));
306
307   vg_assert(VG_(is_valid_tid)(tid));
308
309   vg_assert(VG_(clo_backtrace_size) >= 1 &&
310             VG_(clo_backtrace_size) <= VG_DEEPEST_BACKTRACE);
311
312   if (first_ip_only) {
313      n_ips = 1;
314      ips[0] = VG_(get_IP)(tid);
315   } else {
316      n_ips = VG_(get_StackTrace)( tid, ips, VG_(clo_backtrace_size),
317                                   NULL/*array to dump SP values in*/,
318                                   NULL/*array to dump FP values in*/,
319                                   first_ip_delta );
320   }
321
322   return record_ExeContext_wrk2 ( &ips[0], n_ips );
323}
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
340   tl_assert(n_ips >= 1 && n_ips <= VG_(clo_backtrace_size));
341
342   /* Now figure out if we've seen this one before.  First hash it so
343      as to determine the list number. */
344   hash = calc_hash( ips, n_ips, ec_htab_size );
345
346   /* And (the expensive bit) look a for matching entry in the list. */
347
348   ec_searchreqs++;
349
350   prev2 = NULL;
351   prev  = NULL;
352   list  = ec_htab[hash];
353
354   while (True) {
355      if (list == NULL) break;
356      ec_searchcmps++;
357      same = True;
358      for (i = 0; i < n_ips; i++) {
359         if (list->ips[i] != ips[i]) {
360            same = False;
361            break;
362         }
363      }
364      if (same) break;
365      prev2 = prev;
366      prev  = list;
367      list  = list->chain;
368   }
369
370   if (list != NULL) {
371      /* 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. */
374      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         }
391      }
392      return list;
393   }
394
395   /* Bummer.  We have to allocate a new context record. */
396   ec_totstored++;
397
398   new_ec = VG_(arena_malloc)( VG_AR_EXECTXT,
399                               sizeof(struct _ExeContext)
400                               + n_ips * sizeof(Addr) );
401
402   for (i = 0; i < n_ips; i++)
403      new_ec->ips[i] = ips[i];
404
405   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
414   new_ec->n_ips = n_ips;
415   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   }
424
425   return new_ec;
426}
427
428ExeContext* 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
438ExeContext* VG_(make_depth_1_ExeContext_from_Addr)( Addr a ) {
439   init_ExeContext_storage();
440   return record_ExeContext_wrk2( &a, 1 );
441}
442
443StackTrace VG_(get_ExeContext_StackTrace) ( ExeContext* e ) {
444   return e->ips;
445}
446
447UInt 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
472/*--------------------------------------------------------------------*/
473/*--- end                                           m_execontext.c ---*/
474/*--------------------------------------------------------------------*/
475