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