m_execontext.c revision 36a20fa5f779a0a6fb7b4a90dcaa6376481f1faa
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-2005 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 "core.h"
32#include "pub_core_execontext.h"
33#include "pub_core_libcprint.h"
34#include "pub_core_options.h"
35#include "pub_core_profile.h"
36
37/*------------------------------------------------------------*/
38/*--- Low-level ExeContext storage.                        ---*/
39/*------------------------------------------------------------*/
40
41/* The first 4 IP values are used in comparisons do remove duplicate errors,
42   and for comparing against suppression specifications.  The rest are
43   purely informational (but often important). */
44
45struct _ExeContext {
46   struct _ExeContext * next;
47   /* Variable-length array.  The size is VG_(clo_backtrace_size); at
48      least 1, at most VG_DEEPEST_BACKTRACE.  [0] is the current IP,
49      [1] is its caller, [2] is the caller of [1], etc. */
50   Addr ips[0];
51};
52
53/* Number of lists in which we keep track of ExeContexts.  Should be
54   prime. */
55#define N_EC_LISTS 4999 /* a prime number */
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
60static ExeContext* ec_list[N_EC_LISTS];
61
62/* Stats only: the number of times the system was searched to locate a
63   context. */
64static UInt ec_searchreqs;
65
66/* Stats only: the number of full context comparisons done. */
67static UInt ec_searchcmps;
68
69/* Stats only: total number of stored contexts. */
70static UInt ec_totstored;
71
72/* Number of 2, 4 and (fast) full cmps done. */
73static UInt ec_cmp2s;
74static UInt ec_cmp4s;
75static UInt ec_cmpAlls;
76
77
78/*------------------------------------------------------------*/
79/*--- Exported functions.                                  ---*/
80/*------------------------------------------------------------*/
81
82
83/* Initialise this subsystem. */
84static void init_ExeContext_storage ( void )
85{
86   Int i;
87   static Bool init_done = False;
88   if (init_done)
89      return;
90   ec_searchreqs = 0;
91   ec_searchcmps = 0;
92   ec_totstored = 0;
93   ec_cmp2s = 0;
94   ec_cmp4s = 0;
95   ec_cmpAlls = 0;
96   for (i = 0; i < N_EC_LISTS; i++)
97      ec_list[i] = NULL;
98   init_done = True;
99}
100
101
102/* Print stats. */
103void VG_(print_ExeContext_stats) ( void )
104{
105   init_ExeContext_storage();
106   VG_(message)(Vg_DebugMsg,
107      "   exectx: %d lists, %d contexts (avg %d per list)",
108      N_EC_LISTS, ec_totstored,
109      ec_totstored / N_EC_LISTS
110   );
111   VG_(message)(Vg_DebugMsg,
112      "   exectx: %d searches, %d full compares (%d per 1000)",
113      ec_searchreqs, ec_searchcmps,
114      ec_searchreqs == 0
115         ? 0
116         : (UInt)( (((ULong)ec_searchcmps) * 1000)
117           / ((ULong)ec_searchreqs ))
118   );
119   VG_(message)(Vg_DebugMsg,
120      "   exectx: %d cmp2, %d cmp4, %d cmpAll",
121      ec_cmp2s, ec_cmp4s, ec_cmpAlls
122   );
123}
124
125
126/* Print an ExeContext. */
127void VG_(pp_ExeContext) ( ExeContext* ec )
128{
129   VG_(pp_StackTrace)( ec->ips, VG_(clo_backtrace_size) );
130}
131
132
133/* Compare two ExeContexts, comparing all callers. */
134Bool VG_(eq_ExeContext) ( VgRes res, ExeContext* e1, ExeContext* e2 )
135{
136   if (e1 == NULL || e2 == NULL)
137      return False;
138   switch (res) {
139   case Vg_LowRes:
140      /* Just compare the top two callers. */
141      ec_cmp2s++;
142      if (e1->ips[0] != e2->ips[0]) return False;
143
144      if (VG_(clo_backtrace_size) < 2) return True;
145      if (e1->ips[1] != e2->ips[1]) return False;
146      return True;
147
148   case Vg_MedRes:
149      /* Just compare the top four callers. */
150      ec_cmp4s++;
151      if (e1->ips[0] != e2->ips[0]) return False;
152
153      if (VG_(clo_backtrace_size) < 2) return True;
154      if (e1->ips[1] != e2->ips[1]) return False;
155
156      if (VG_(clo_backtrace_size) < 3) return True;
157      if (e1->ips[2] != e2->ips[2]) return False;
158
159      if (VG_(clo_backtrace_size) < 4) return True;
160      if (e1->ips[3] != e2->ips[3]) return False;
161      return True;
162
163   case Vg_HighRes:
164      ec_cmpAlls++;
165      /* Compare them all -- just do pointer comparison. */
166      if (e1 != e2) return False;
167      return True;
168
169   default:
170      VG_(core_panic)("VG_(eq_ExeContext): unrecognised VgRes");
171   }
172}
173
174/* This guy is the head honcho here.  Take a snapshot of the client's
175   stack.  Search our collection of ExeContexts to see if we already
176   have it, and if not, allocate a new one.  Either way, return a
177   pointer to the context.  If there is a matching context we
178   guarantee to not allocate a new one.  Thus we never store
179   duplicates, and so exact equality can be quickly done as equality
180   on the returned ExeContext* values themselves.  Inspired by Hugs's
181   Text type.
182*/
183ExeContext* VG_(record_ExeContext) ( ThreadId tid )
184{
185   Int         i;
186   Addr        ips[VG_DEEPEST_BACKTRACE];
187   Bool        same;
188   UWord       hash;
189   ExeContext* new_ec;
190   ExeContext* list;
191
192   VGP_PUSHCC(VgpExeContext);
193
194   init_ExeContext_storage();
195   vg_assert(VG_(clo_backtrace_size) >= 1
196             && VG_(clo_backtrace_size) <= VG_DEEPEST_BACKTRACE);
197
198   VG_(get_StackTrace)( tid, ips, VG_(clo_backtrace_size) );
199
200   /* Now figure out if we've seen this one before.  First hash it so
201      as to determine the list number. */
202
203   hash = 0;
204   for (i = 0; i < VG_(clo_backtrace_size); i++) {
205      hash ^= ips[i];
206      hash = (hash << 29) | (hash >> 3);
207   }
208   hash = hash % N_EC_LISTS;
209
210   /* And (the expensive bit) look a matching entry in the list. */
211
212   ec_searchreqs++;
213
214   list = ec_list[hash];
215
216   while (True) {
217      if (list == NULL) break;
218      ec_searchcmps++;
219      same = True;
220      for (i = 0; i < VG_(clo_backtrace_size); i++) {
221         if (list->ips[i] != ips[i]) {
222            same = False;
223            break;
224         }
225      }
226      if (same) break;
227      list = list->next;
228   }
229
230   if (list != NULL) {
231      /* Yay!  We found it.  */
232      VGP_POPCC(VgpExeContext);
233      return list;
234   }
235
236   /* Bummer.  We have to allocate a new context record. */
237   ec_totstored++;
238
239   new_ec = VG_(arena_malloc)( VG_AR_EXECTXT,
240                               sizeof(struct _ExeContext *)
241                               + VG_(clo_backtrace_size) * sizeof(Addr) );
242
243   for (i = 0; i < VG_(clo_backtrace_size); i++)
244      new_ec->ips[i] = ips[i];
245
246   new_ec->next = ec_list[hash];
247   ec_list[hash] = new_ec;
248
249   VGP_POPCC(VgpExeContext);
250   return new_ec;
251}
252
253StackTrace VG_(extract_StackTrace) ( ExeContext* e )
254{
255   return e->ips;
256}
257
258/*--------------------------------------------------------------------*/
259/*--- end                                           m_execontext.c ---*/
260/*--------------------------------------------------------------------*/
261