blob: 4da1b31e180283dd728c9635e1cc964ba2b5c88a [file] [log] [blame]
sewardjde4a1d02002-03-22 01:27:54 +00001
2/*--------------------------------------------------------------------*/
3/*--- Storage, and equality on, execution contexts (backtraces). ---*/
4/*--- vg_execontext.c ---*/
5/*--------------------------------------------------------------------*/
6
7/*
8 This file is part of Valgrind, an x86 protected-mode emulator
9 designed for debugging and profiling binaries on x86-Unixes.
10
11 Copyright (C) 2000-2002 Julian Seward
12 jseward@acm.org
sewardjde4a1d02002-03-22 01:27:54 +000013
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of the
17 License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful, but
20 WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27 02111-1307, USA.
28
29 The GNU General Public License is contained in the file LICENSE.
30*/
31
32#include "vg_include.h"
33#include "vg_constants.h"
34
35
36/*------------------------------------------------------------*/
37/*--- Low-level ExeContext storage. ---*/
38/*------------------------------------------------------------*/
39
40/* The idea is only to ever store any one context once, so as to save
41 space and make exact comparisons faster. */
42
43static ExeContext* vg_ec_list[VG_N_EC_LISTS];
44
45/* Stats only: the number of times the system was searched to locate a
46 context. */
47static UInt vg_ec_searchreqs;
48
49/* Stats only: the number of full context comparisons done. */
50static UInt vg_ec_searchcmps;
51
52/* Stats only: total number of stored contexts. */
53static UInt vg_ec_totstored;
54
55/* Number of 2, 4 and (fast) full cmps done. */
56static UInt vg_ec_cmp2s;
57static UInt vg_ec_cmp4s;
58static UInt vg_ec_cmpAlls;
59
60
61/*------------------------------------------------------------*/
62/*--- Exported functions. ---*/
63/*------------------------------------------------------------*/
64
65
66/* Initialise this subsystem. */
67void VG_(init_ExeContext_storage) ( void )
68{
69 Int i;
70 vg_ec_searchreqs = 0;
71 vg_ec_searchcmps = 0;
72 vg_ec_totstored = 0;
73 vg_ec_cmp2s = 0;
74 vg_ec_cmp4s = 0;
75 vg_ec_cmpAlls = 0;
76 for (i = 0; i < VG_N_EC_LISTS; i++)
77 vg_ec_list[i] = NULL;
78}
79
80
81/* Show stats. */
82void VG_(show_ExeContext_stats) ( void )
83{
84 VG_(message)(Vg_DebugMsg,
85 "exectx: %d lists, %d contexts (avg %d per list)",
86 VG_N_EC_LISTS, vg_ec_totstored,
87 vg_ec_totstored / VG_N_EC_LISTS
88 );
89 VG_(message)(Vg_DebugMsg,
90 "exectx: %d searches, %d full compares (%d per 1000)",
91 vg_ec_searchreqs, vg_ec_searchcmps,
92 vg_ec_searchreqs == 0
93 ? 0
94 : (UInt)( (((ULong)vg_ec_searchcmps) * 1000)
95 / ((ULong)vg_ec_searchreqs ))
96 );
97 VG_(message)(Vg_DebugMsg,
98 "exectx: %d cmp2, %d cmp4, %d cmpAll",
99 vg_ec_cmp2s, vg_ec_cmp4s, vg_ec_cmpAlls
100 );
101}
102
103
104/* Print an ExeContext. */
105void VG_(pp_ExeContext) ( ExeContext* e )
106{
107 VG_(mini_stack_dump) ( e );
108}
109
110
111/* Compare two ExeContexts, comparing all callers. */
112Bool VG_(eq_ExeContext_all) ( ExeContext* e1, ExeContext* e2 )
113{
114 vg_ec_cmpAlls++;
115 /* Just do pointer comparison. */
116 if (e1 != e2) return False;
117 return True;
118}
119
120
121/* Compare two ExeContexts, just comparing the top two callers. */
122Bool VG_(eq_ExeContext_top2) ( ExeContext* e1, ExeContext* e2 )
123{
124 vg_ec_cmp2s++;
125 if (e1->eips[0] != e2->eips[0]
126 || e1->eips[1] != e2->eips[1]) return False;
127 return True;
128}
129
130
131/* Compare two ExeContexts, just comparing the top four callers. */
132Bool VG_(eq_ExeContext_top4) ( ExeContext* e1, ExeContext* e2 )
133{
134 vg_ec_cmp4s++;
135 if (e1->eips[0] != e2->eips[0]
136 || e1->eips[1] != e2->eips[1]) return False;
137
138 if (VG_(clo_backtrace_size) < 3) return True;
139 if (e1->eips[2] != e2->eips[2]) return False;
140
141 if (VG_(clo_backtrace_size) < 4) return True;
142 if (e1->eips[3] != e2->eips[3]) return False;
143
144 return True;
145}
146
147
148/* This guy is the head honcho here. Take a snapshot of the client's
149 stack. Search our collection of ExeContexts to see if we already
150 have it, and if not, allocate a new one. Either way, return a
151 pointer to the context. If there is a matching context we
152 guarantee to not allocate a new one. Thus we never store
153 duplicates, and so exact equality can be quickly done as equality
154 on the returned ExeContext* values themselves. Inspired by Hugs's
155 Text type.
sewardj8c824512002-04-14 04:16:48 +0000156
157 In order to be thread-safe, we pass in the thread's %EIP and %EBP.
sewardjde4a1d02002-03-22 01:27:54 +0000158*/
sewardj8c824512002-04-14 04:16:48 +0000159ExeContext* VG_(get_ExeContext) ( Bool skip_top_frame,
160 Addr eip, Addr ebp )
sewardjde4a1d02002-03-22 01:27:54 +0000161{
162 Int i;
sewardjde4a1d02002-03-22 01:27:54 +0000163 Addr eips[VG_DEEPEST_BACKTRACE];
164 Bool same;
165 UInt hash;
166 ExeContext* new_ec;
167 ExeContext* list;
168
169 VGP_PUSHCC(VgpExeContext);
170
171 vg_assert(VG_(clo_backtrace_size) >= 2
172 && VG_(clo_backtrace_size) <= VG_DEEPEST_BACKTRACE);
173
174 /* First snaffle %EIPs from the client's stack into eips[0
175 .. VG_(clo_backtrace_size)-1], putting zeroes in when the trail
176 goes cold. */
177
178 for (i = 0; i < VG_(clo_backtrace_size); i++)
179 eips[i] = 0;
180
181# define GET_CALLER(lval) \
182 if (ebp != 0 && VGM_(check_readable)(ebp, 8, NULL)) { \
183 lval = ((UInt*)ebp)[1]; /* ret addr */ \
184 ebp = ((UInt*)ebp)[0]; /* old ebp */ \
185 } else { \
186 lval = ebp = 0; \
187 }
188
sewardjde4a1d02002-03-22 01:27:54 +0000189 if (skip_top_frame) {
190 for (i = 0; i < VG_(clo_backtrace_size); i++)
191 GET_CALLER(eips[i]);
192 } else {
sewardj8c824512002-04-14 04:16:48 +0000193 eips[0] = eip;
sewardjde4a1d02002-03-22 01:27:54 +0000194 for (i = 1; i < VG_(clo_backtrace_size); i++)
195 GET_CALLER(eips[i]);
196 }
197# undef GET_CALLER
198
199 /* Now figure out if we've seen this one before. First hash it so
200 as to determine the list number. */
201
202 hash = 0;
203 for (i = 0; i < VG_(clo_backtrace_size); i++) {
204 hash ^= (UInt)eips[i];
205 hash = (hash << 29) | (hash >> 3);
206 }
207 hash = hash % VG_N_EC_LISTS;
208
209 /* And (the expensive bit) look a matching entry in the list. */
210
211 vg_ec_searchreqs++;
212
213 list = vg_ec_list[hash];
214
215 while (True) {
216 if (list == NULL) break;
217 vg_ec_searchcmps++;
218 same = True;
219 for (i = 0; i < VG_(clo_backtrace_size); i++) {
220 if (list->eips[i] != eips[i]) {
221 same = False;
222 break;
223 }
224 }
225 if (same) break;
226 list = list->next;
227 }
228
229 if (list != NULL) {
230 /* Yay! We found it. */
231 VGP_POPCC;
232 return list;
233 }
234
235 /* Bummer. We have to allocate a new context record. */
236 vg_ec_totstored++;
237
238 new_ec
239 = VG_(malloc)(
240 VG_AR_EXECTXT,
241 sizeof(struct _ExeContextRec *)
242 + VG_(clo_backtrace_size) * sizeof(Addr)
243 );
244
245 for (i = 0; i < VG_(clo_backtrace_size); i++)
246 new_ec->eips[i] = eips[i];
247
248 new_ec->next = vg_ec_list[hash];
249 vg_ec_list[hash] = new_ec;
250
251 VGP_POPCC;
252 return new_ec;
253}
254
255
256/*--------------------------------------------------------------------*/
257/*--- end vg_execontext.c ---*/
258/*--------------------------------------------------------------------*/