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