blob: 2b99e92e62074a72bfcea41492d1a5c0cecea8fb [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"
njnd0d7c1f2005-06-21 00:33:19 +000037#include "pub_core_stacktrace.h"
njnd01fef72005-03-25 23:35:48 +000038
39/*------------------------------------------------------------*/
40/*--- Low-level ExeContext storage. ---*/
41/*------------------------------------------------------------*/
42
sewardjef52b9d2005-06-28 00:12:31 +000043/* The first 4 IP values are used in comparisons to remove duplicate errors,
njnd01fef72005-03-25 23:35:48 +000044 and for comparing against suppression specifications. The rest are
45 purely informational (but often important). */
46
47struct _ExeContext {
48 struct _ExeContext * next;
njnc0ec8e92005-12-25 06:34:04 +000049 UInt n_ips;
50 /* Variable-length array. The size is 'n_ips'; at
njnd01fef72005-03-25 23:35:48 +000051 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{
njnc0ec8e92005-12-25 06:34:04 +0000130 VG_(pp_StackTrace)( ec->ips, ec->n_ips );
njnd01fef72005-03-25 23:35:48 +0000131}
132
133
134/* Compare two ExeContexts, comparing all callers. */
135Bool VG_(eq_ExeContext) ( VgRes res, ExeContext* e1, ExeContext* e2 )
136{
njnc0ec8e92005-12-25 06:34:04 +0000137 Int i;
138
njnd01fef72005-03-25 23:35:48 +0000139 if (e1 == NULL || e2 == NULL)
140 return False;
njnc0ec8e92005-12-25 06:34:04 +0000141
142 // Must be at least one address in each trace.
143 tl_assert(e1->n_ips >= 1 && e2->n_ips >= 1);
144
njnd01fef72005-03-25 23:35:48 +0000145 switch (res) {
146 case Vg_LowRes:
147 /* Just compare the top two callers. */
148 ec_cmp2s++;
njnc0ec8e92005-12-25 06:34:04 +0000149 for (i = 0; i < 2; i++) {
150 if ( (e1->n_ips <= i) && (e2->n_ips <= i)) return True;
151 if ( (e1->n_ips <= i) && !(e2->n_ips <= i)) return False;
152 if (!(e1->n_ips <= i) && (e2->n_ips <= i)) return False;
153 if (e1->ips[i] != e2->ips[i]) return False;
154 }
njnd01fef72005-03-25 23:35:48 +0000155 return True;
156
157 case Vg_MedRes:
158 /* Just compare the top four callers. */
159 ec_cmp4s++;
njnc0ec8e92005-12-25 06:34:04 +0000160 for (i = 0; i < 4; i++) {
161 if ( (e1->n_ips <= i) && (e2->n_ips <= i)) return True;
162 if ( (e1->n_ips <= i) && !(e2->n_ips <= i)) return False;
163 if (!(e1->n_ips <= i) && (e2->n_ips <= i)) return False;
164 if (e1->ips[i] != e2->ips[i]) return False;
165 }
njnd01fef72005-03-25 23:35:48 +0000166 return True;
167
168 case Vg_HighRes:
169 ec_cmpAlls++;
170 /* Compare them all -- just do pointer comparison. */
171 if (e1 != e2) return False;
172 return True;
173
174 default:
175 VG_(core_panic)("VG_(eq_ExeContext): unrecognised VgRes");
176 }
177}
178
179/* This guy is the head honcho here. Take a snapshot of the client's
180 stack. Search our collection of ExeContexts to see if we already
181 have it, and if not, allocate a new one. Either way, return a
182 pointer to the context. If there is a matching context we
183 guarantee to not allocate a new one. Thus we never store
184 duplicates, and so exact equality can be quickly done as equality
185 on the returned ExeContext* values themselves. Inspired by Hugs's
186 Text type.
187*/
188ExeContext* VG_(record_ExeContext) ( ThreadId tid )
189{
190 Int i;
191 Addr ips[VG_DEEPEST_BACKTRACE];
192 Bool same;
193 UWord hash;
194 ExeContext* new_ec;
195 ExeContext* list;
njnc0ec8e92005-12-25 06:34:04 +0000196 UInt n_ips;
njnd01fef72005-03-25 23:35:48 +0000197
njnd01fef72005-03-25 23:35:48 +0000198 init_ExeContext_storage();
njnc0ec8e92005-12-25 06:34:04 +0000199 vg_assert(VG_(clo_backtrace_size) >= 1 &&
200 VG_(clo_backtrace_size) <= VG_DEEPEST_BACKTRACE);
njnd01fef72005-03-25 23:35:48 +0000201
njnc0ec8e92005-12-25 06:34:04 +0000202 n_ips = VG_(get_StackTrace)( tid, ips, VG_(clo_backtrace_size) );
203 tl_assert(n_ips >= 1);
njnd01fef72005-03-25 23:35:48 +0000204
205 /* Now figure out if we've seen this one before. First hash it so
206 as to determine the list number. */
207
208 hash = 0;
njnc0ec8e92005-12-25 06:34:04 +0000209 for (i = 0; i < n_ips; i++) {
njnd01fef72005-03-25 23:35:48 +0000210 hash ^= ips[i];
211 hash = (hash << 29) | (hash >> 3);
212 }
213 hash = hash % N_EC_LISTS;
214
215 /* And (the expensive bit) look a matching entry in the list. */
216
217 ec_searchreqs++;
218
219 list = ec_list[hash];
220
221 while (True) {
222 if (list == NULL) break;
223 ec_searchcmps++;
224 same = True;
njnc0ec8e92005-12-25 06:34:04 +0000225 for (i = 0; i < n_ips; i++) {
njnd01fef72005-03-25 23:35:48 +0000226 if (list->ips[i] != ips[i]) {
227 same = False;
228 break;
229 }
230 }
231 if (same) break;
232 list = list->next;
233 }
234
235 if (list != NULL) {
236 /* Yay! We found it. */
njnd01fef72005-03-25 23:35:48 +0000237 return list;
238 }
239
240 /* Bummer. We have to allocate a new context record. */
241 ec_totstored++;
242
243 new_ec = VG_(arena_malloc)( VG_AR_EXECTXT,
njnc0ec8e92005-12-25 06:34:04 +0000244 sizeof(struct _ExeContext)
245 + n_ips * sizeof(Addr) );
njnd01fef72005-03-25 23:35:48 +0000246
njnc0ec8e92005-12-25 06:34:04 +0000247 for (i = 0; i < n_ips; i++)
njnd01fef72005-03-25 23:35:48 +0000248 new_ec->ips[i] = ips[i];
249
njnc0ec8e92005-12-25 06:34:04 +0000250 new_ec->n_ips = n_ips;
251 new_ec->next = ec_list[hash];
njnd01fef72005-03-25 23:35:48 +0000252 ec_list[hash] = new_ec;
253
njnd01fef72005-03-25 23:35:48 +0000254 return new_ec;
255}
256
257StackTrace VG_(extract_StackTrace) ( ExeContext* e )
258{
259 return e->ips;
260}
261
262/*--------------------------------------------------------------------*/
sewardj267100d2005-04-24 12:33:12 +0000263/*--- end m_execontext.c ---*/
njnd01fef72005-03-25 23:35:48 +0000264/*--------------------------------------------------------------------*/