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