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