blob: 700e4c67a1a4aa8a535aab66e843a353f887fb7c [file] [log] [blame]
sewardjde4a1d02002-03-22 01:27:54 +00001
2/*--------------------------------------------------------------------*/
3/*--- Management of error messages. vg_errcontext.c ---*/
4/*--------------------------------------------------------------------*/
5
6/*
njnc9539842002-10-02 13:26:35 +00007 This file is part of Valgrind, an extensible x86 protected-mode
8 emulator for monitoring program execution on x86-Unixes.
sewardjde4a1d02002-03-22 01:27:54 +00009
njn0e1b5142003-04-15 14:58:06 +000010 Copyright (C) 2000-2003 Julian Seward
sewardjde4a1d02002-03-22 01:27:54 +000011 jseward@acm.org
sewardjde4a1d02002-03-22 01:27:54 +000012
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
njn25e49d8e72002-09-23 09:36:25 +000028 The GNU General Public License is contained in the file COPYING.
sewardjde4a1d02002-03-22 01:27:54 +000029*/
30
31#include "vg_include.h"
sewardjde4a1d02002-03-22 01:27:54 +000032
33/*------------------------------------------------------------*/
njn25e49d8e72002-09-23 09:36:25 +000034/*--- Globals ---*/
sewardjde4a1d02002-03-22 01:27:54 +000035/*------------------------------------------------------------*/
36
sewardjde4a1d02002-03-22 01:27:54 +000037/* The list of error contexts found, both suppressed and unsuppressed.
38 Initially empty, and grows as errors are detected. */
njn810086f2002-11-14 12:42:47 +000039static Error* vg_errors = NULL;
sewardjde4a1d02002-03-22 01:27:54 +000040
41/* The list of suppression directives, as read from the specified
42 suppressions file. */
njn810086f2002-11-14 12:42:47 +000043static Supp* vg_suppressions = NULL;
sewardjde4a1d02002-03-22 01:27:54 +000044
45/* Running count of unsuppressed errors detected. */
njn47363ab2003-04-21 13:24:40 +000046UInt VG_(n_errs_found) = 0;
sewardjde4a1d02002-03-22 01:27:54 +000047
48/* Running count of suppressed errors detected. */
49static UInt vg_n_errs_suppressed = 0;
50
51/* forwards ... */
njn810086f2002-11-14 12:42:47 +000052static Supp* is_suppressible_error ( Error* err );
sewardjde4a1d02002-03-22 01:27:54 +000053
54
55/*------------------------------------------------------------*/
56/*--- Helper fns ---*/
57/*------------------------------------------------------------*/
58
sewardjde4a1d02002-03-22 01:27:54 +000059/* Compare error contexts, to detect duplicates. Note that if they
60 are otherwise the same, the faulting addrs and associated rwoffsets
61 are allowed to be different. */
njn810086f2002-11-14 12:42:47 +000062static Bool eq_Error ( VgRes res, Error* e1, Error* e2 )
sewardjde4a1d02002-03-22 01:27:54 +000063{
njn810086f2002-11-14 12:42:47 +000064 if (e1->ekind != e2->ekind)
sewardjde4a1d02002-03-22 01:27:54 +000065 return False;
njn25e49d8e72002-09-23 09:36:25 +000066 if (!VG_(eq_ExeContext)(res, e1->where, e2->where))
sewardjde4a1d02002-03-22 01:27:54 +000067 return False;
68
njn810086f2002-11-14 12:42:47 +000069 switch (e1->ekind) {
sewardj4dced352002-06-04 22:54:20 +000070 case PThreadErr:
njn25e49d8e72002-09-23 09:36:25 +000071 vg_assert(VG_(needs).core_errors);
njn810086f2002-11-14 12:42:47 +000072 if (e1->string == e2->string)
sewardj4dced352002-06-04 22:54:20 +000073 return True;
njn810086f2002-11-14 12:42:47 +000074 if (0 == VG_(strcmp)(e1->string, e2->string))
sewardj4dced352002-06-04 22:54:20 +000075 return True;
76 return False;
sewardjde4a1d02002-03-22 01:27:54 +000077 default:
njn25e49d8e72002-09-23 09:36:25 +000078 if (VG_(needs).skin_errors)
njn810086f2002-11-14 12:42:47 +000079 return SK_(eq_SkinError)(res, e1, e2);
njn25e49d8e72002-09-23 09:36:25 +000080 else {
81 VG_(printf)("\nUnhandled error type: %u. VG_(needs).skin_errors\n"
82 "probably needs to be set.\n",
njn810086f2002-11-14 12:42:47 +000083 e1->ekind);
njne427a662002-10-02 11:08:25 +000084 VG_(skin_panic)("unhandled error type");
njn25e49d8e72002-09-23 09:36:25 +000085 }
sewardjde4a1d02002-03-22 01:27:54 +000086 }
87}
88
njn810086f2002-11-14 12:42:47 +000089static void pp_Error ( Error* err, Bool printCount )
sewardjde4a1d02002-03-22 01:27:54 +000090{
sewardjde4a1d02002-03-22 01:27:54 +000091 if (printCount)
njn25e49d8e72002-09-23 09:36:25 +000092 VG_(message)(Vg_UserMsg, "Observed %d times:", err->count );
93 if (err->tid > 1)
94 VG_(message)(Vg_UserMsg, "Thread %d:", err->tid );
95
njn810086f2002-11-14 12:42:47 +000096 switch (err->ekind) {
sewardj4dced352002-06-04 22:54:20 +000097 case PThreadErr:
njn25e49d8e72002-09-23 09:36:25 +000098 vg_assert(VG_(needs).core_errors);
njn810086f2002-11-14 12:42:47 +000099 VG_(message)(Vg_UserMsg, "%s", err->string );
njn25e49d8e72002-09-23 09:36:25 +0000100 VG_(pp_ExeContext)(err->where);
sewardj4dced352002-06-04 22:54:20 +0000101 break;
sewardjde4a1d02002-03-22 01:27:54 +0000102 default:
njn25e49d8e72002-09-23 09:36:25 +0000103 if (VG_(needs).skin_errors)
njn43c799e2003-04-08 00:08:52 +0000104 SK_(pp_SkinError)( err );
njn25e49d8e72002-09-23 09:36:25 +0000105 else {
106 VG_(printf)("\nUnhandled error type: %u. VG_(needs).skin_errors\n"
107 "probably needs to be set?\n",
njn810086f2002-11-14 12:42:47 +0000108 err->ekind);
njne427a662002-10-02 11:08:25 +0000109 VG_(skin_panic)("unhandled error type");
njn25e49d8e72002-09-23 09:36:25 +0000110 }
sewardjde4a1d02002-03-22 01:27:54 +0000111 }
112}
113
sewardjde4a1d02002-03-22 01:27:54 +0000114/* Figure out if we want to attach for GDB for this error, possibly
115 by asking the user. */
njn43c799e2003-04-08 00:08:52 +0000116Bool VG_(is_action_requested) ( Char* action, Bool* clo )
sewardjde4a1d02002-03-22 01:27:54 +0000117{
118 Char ch, ch2;
119 Int res;
120
njn43c799e2003-04-08 00:08:52 +0000121 if (*clo == False)
sewardjde4a1d02002-03-22 01:27:54 +0000122 return False;
123
124 VG_(message)(Vg_UserMsg, "");
125
126 again:
127 VG_(printf)(
128 "==%d== "
njn43c799e2003-04-08 00:08:52 +0000129 "---- %s ? --- [Return/N/n/Y/y/C/c] ---- ",
130 VG_(getpid)(), action
sewardjde4a1d02002-03-22 01:27:54 +0000131 );
132
sewardj6024b212003-07-13 10:54:33 +0000133 res = VG_(read)(VG_(clo_input_fd), &ch, 1);
sewardjde4a1d02002-03-22 01:27:54 +0000134 if (res != 1) goto ioerror;
135 /* res == 1 */
136 if (ch == '\n') return False;
137 if (ch != 'N' && ch != 'n' && ch != 'Y' && ch != 'y'
138 && ch != 'C' && ch != 'c') goto again;
139
sewardj6024b212003-07-13 10:54:33 +0000140 res = VG_(read)(VG_(clo_input_fd), &ch2, 1);
sewardjde4a1d02002-03-22 01:27:54 +0000141 if (res != 1) goto ioerror;
142 if (ch2 != '\n') goto again;
143
njn43c799e2003-04-08 00:08:52 +0000144 /* No, don't want to do action. */
sewardjde4a1d02002-03-22 01:27:54 +0000145 if (ch == 'n' || ch == 'N') return False;
njn43c799e2003-04-08 00:08:52 +0000146 /* Yes, want to do action. */
sewardjde4a1d02002-03-22 01:27:54 +0000147 if (ch == 'y' || ch == 'Y') return True;
njn43c799e2003-04-08 00:08:52 +0000148 /* No, don't want to do action, and don't ask again either. */
sewardjde4a1d02002-03-22 01:27:54 +0000149 vg_assert(ch == 'c' || ch == 'C');
150
151 ioerror:
njn43c799e2003-04-08 00:08:52 +0000152 *clo = False;
sewardjde4a1d02002-03-22 01:27:54 +0000153 return False;
154}
155
156
njn25e49d8e72002-09-23 09:36:25 +0000157/* I've gone all object-oriented... initialisation depends on where the
158 error comes from:
159
160 - If from generated code (tst == NULL), the %EIP/%EBP values that we
njn3e884182003-04-15 13:03:23 +0000161 need in order to attach GDB are picked up out of VG_(baseBlock) rather
162 than from the thread table (vg_threads in vg_scheduler.c).
njn25e49d8e72002-09-23 09:36:25 +0000163
164 - If not from generated code but in response to requests passed back to
165 the scheduler (tst != NULL), we pick up %EIP/%EBP values from the
166 stored thread state, not from VG_(baseBlock).
167*/
168static __inline__
njn72718642003-07-24 08:45:32 +0000169void construct_error ( Error* err, ThreadId tid, ErrorKind ekind, Addr a,
170 Char* s, void* extra, ExeContext* where )
sewardjde4a1d02002-03-22 01:27:54 +0000171{
njn72718642003-07-24 08:45:32 +0000172 sk_assert(tid < VG_N_THREADS);
173
njn810086f2002-11-14 12:42:47 +0000174 /* Core-only parts */
njn25e49d8e72002-09-23 09:36:25 +0000175 err->next = NULL;
176 err->supp = NULL;
177 err->count = 1;
njn72718642003-07-24 08:45:32 +0000178 err->tid = tid;
njn43c799e2003-04-08 00:08:52 +0000179 if (NULL == where)
njn72718642003-07-24 08:45:32 +0000180 err->where = VG_(get_ExeContext)( tid );
njn43c799e2003-04-08 00:08:52 +0000181 else
182 err->where = where;
njn1d6c4bc2002-11-21 13:38:08 +0000183
njn810086f2002-11-14 12:42:47 +0000184 /* Skin-relevant parts */
185 err->ekind = ekind;
186 err->addr = a;
187 err->string = s;
188 err->extra = extra;
njn25e49d8e72002-09-23 09:36:25 +0000189
190 /* sanity... */
njn72718642003-07-24 08:45:32 +0000191 vg_assert( tid < VG_N_THREADS );
njn25e49d8e72002-09-23 09:36:25 +0000192}
193
njn43c799e2003-04-08 00:08:52 +0000194void VG_(gen_suppression)(Error* err)
195{
sewardj05bcdcb2003-05-18 10:05:38 +0000196 Int i;
njn43c799e2003-04-08 00:08:52 +0000197 UChar buf[M_VG_ERRTXT];
198 ExeContext* ec = VG_(get_error_where)(err);
199 Int stop_at = VG_(clo_backtrace_size);
njn43c799e2003-04-08 00:08:52 +0000200
njn633de322003-05-12 20:40:13 +0000201 if (stop_at > 4) stop_at = 4; /* At most four names */
njn43c799e2003-04-08 00:08:52 +0000202 vg_assert(stop_at > 0);
203
204 VG_(printf)("{\n");
205 VG_(printf)(" <insert a suppression name here>\n");
njn6a230532003-07-21 10:38:23 +0000206
207 if (PThreadErr == err->ekind) {
208 VG_(printf)(" core:PThread\n");
209
210 } else {
211 Char* name = SK_(get_error_name)(err);
212 if (NULL == name) {
213 VG_(message)(Vg_UserMsg,
214 "(skin does not allow error to be suppressed)");
215 return;
216 }
217 VG_(printf)(" %s:%s\n", VG_(details).name, name);
218 SK_(print_extra_suppression_info)(err);
219 }
njn43c799e2003-04-08 00:08:52 +0000220
221 /* This loop condensed from VG_(mini_stack_dump)() */
222 i = 0;
223 do {
224 Addr eip = ec->eips[i];
225 if (i > 0)
226 eip--; /* point to calling line */
227
228 if ( VG_(get_fnname_nodemangle) (eip, buf, M_VG_ERRTXT) ) {
229 VG_(printf)(" fun:%s\n", buf);
230 } else if ( VG_(get_objname)(eip, buf, M_VG_ERRTXT) ) {
231 VG_(printf)(" obj:%s\n", buf);
232 } else {
233 VG_(printf)(" ???:??? "
234 "# unknown, suppression will not work, sorry)\n");
235 }
236 i++;
237 } while (i < stop_at && ec->eips[i] != 0);
238
239 VG_(printf)("}\n");
240}
241
njnb4aee052003-04-15 14:09:58 +0000242static
njn72718642003-07-24 08:45:32 +0000243void do_actions_on_error(Error* err, Bool allow_GDB_attach)
njn43c799e2003-04-08 00:08:52 +0000244{
245 /* Perhaps we want a GDB attach at this point? */
njn3e884182003-04-15 13:03:23 +0000246 if (allow_GDB_attach &&
247 VG_(is_action_requested)( "Attach to GDB", & VG_(clo_GDB_attach) ))
248 {
njn72718642003-07-24 08:45:32 +0000249 Addr m_eip, m_esp, m_ebp;
250
251 if (VG_(is_running_thread)( err->tid )) {
252 m_eip = VG_(baseBlock)[VGOFF_(m_eip)];
253 m_esp = VG_(baseBlock)[VGOFF_(m_esp)];
254 m_ebp = VG_(baseBlock)[VGOFF_(m_ebp)];
255 } else {
256 ThreadState* tst = & VG_(threads)[ err->tid ];
257 m_eip = tst->m_eip;
258 m_esp = tst->m_esp;
259 m_ebp = tst->m_ebp;
260 }
njn3e884182003-04-15 13:03:23 +0000261 VG_(swizzle_esp_then_start_GDB)( m_eip, m_esp, m_ebp );
njn43c799e2003-04-08 00:08:52 +0000262 }
263 /* Or maybe we want to generate the error's suppression? */
264 if (VG_(is_action_requested)( "Print suppression",
265 & VG_(clo_gen_suppressions) )) {
266 VG_(gen_suppression)(err);
267 }
268}
269
270/* Shared between VG_(maybe_record_error)() and VG_(unique_error)(),
271 just for pretty printing purposes. */
272static Bool is_first_shown_context = True;
273
njn25e49d8e72002-09-23 09:36:25 +0000274/* Top-level entry point to the error management subsystem.
275 All detected errors are notified here; this routine decides if/when the
276 user should see the error. */
njn72718642003-07-24 08:45:32 +0000277void VG_(maybe_record_error) ( ThreadId tid,
njn25e49d8e72002-09-23 09:36:25 +0000278 ErrorKind ekind, Addr a, Char* s, void* extra )
279{
njn810086f2002-11-14 12:42:47 +0000280 Error err;
281 Error* p;
282 Error* p_prev;
njn43c799e2003-04-08 00:08:52 +0000283 UInt extra_size;
njn810086f2002-11-14 12:42:47 +0000284 VgRes exe_res = Vg_MedRes;
njn810086f2002-11-14 12:42:47 +0000285 static Bool stopping_message = False;
286 static Bool slowdown_message = False;
287 static Int vg_n_errs_shown = 0;
sewardjde4a1d02002-03-22 01:27:54 +0000288
sewardjf2537be2002-04-24 21:03:47 +0000289 /* After M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN different errors have
290 been found, or M_VG_COLLECT_NO_ERRORS_AFTER_FOUND total errors
291 have been found, just refuse to collect any more. This stops
292 the burden of the error-management system becoming excessive in
293 extremely buggy programs, although it does make it pretty
294 pointless to continue the Valgrind run after this point. */
sewardj2e432902002-06-13 20:44:00 +0000295 if (VG_(clo_error_limit)
sewardj72f98ff2002-06-13 17:23:38 +0000296 && (vg_n_errs_shown >= M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN
njn47363ab2003-04-21 13:24:40 +0000297 || VG_(n_errs_found) >= M_VG_COLLECT_NO_ERRORS_AFTER_FOUND)) {
sewardjde4a1d02002-03-22 01:27:54 +0000298 if (!stopping_message) {
299 VG_(message)(Vg_UserMsg, "");
sewardjf2537be2002-04-24 21:03:47 +0000300
301 if (vg_n_errs_shown >= M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN) {
302 VG_(message)(Vg_UserMsg,
303 "More than %d different errors detected. "
304 "I'm not reporting any more.",
305 M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN );
306 } else {
307 VG_(message)(Vg_UserMsg,
308 "More than %d total errors detected. "
309 "I'm not reporting any more.",
310 M_VG_COLLECT_NO_ERRORS_AFTER_FOUND );
311 }
312
sewardjde4a1d02002-03-22 01:27:54 +0000313 VG_(message)(Vg_UserMsg,
sewardjf2537be2002-04-24 21:03:47 +0000314 "Final error counts will be inaccurate. Go fix your program!");
sewardj72f98ff2002-06-13 17:23:38 +0000315 VG_(message)(Vg_UserMsg,
sewardj2e432902002-06-13 20:44:00 +0000316 "Rerun with --error-limit=no to disable this cutoff. Note");
sewardj72f98ff2002-06-13 17:23:38 +0000317 VG_(message)(Vg_UserMsg,
njn25e49d8e72002-09-23 09:36:25 +0000318 "that errors may occur in your program without prior warning from");
sewardj72f98ff2002-06-13 17:23:38 +0000319 VG_(message)(Vg_UserMsg,
320 "Valgrind, because errors are no longer being displayed.");
sewardjde4a1d02002-03-22 01:27:54 +0000321 VG_(message)(Vg_UserMsg, "");
322 stopping_message = True;
323 }
324 return;
325 }
326
327 /* After M_VG_COLLECT_ERRORS_SLOWLY_AFTER different errors have
328 been found, be much more conservative about collecting new
329 ones. */
330 if (vg_n_errs_shown >= M_VG_COLLECT_ERRORS_SLOWLY_AFTER) {
njn25e49d8e72002-09-23 09:36:25 +0000331 exe_res = Vg_LowRes;
sewardjde4a1d02002-03-22 01:27:54 +0000332 if (!slowdown_message) {
333 VG_(message)(Vg_UserMsg, "");
334 VG_(message)(Vg_UserMsg,
335 "More than %d errors detected. Subsequent errors",
336 M_VG_COLLECT_ERRORS_SLOWLY_AFTER);
337 VG_(message)(Vg_UserMsg,
338 "will still be recorded, but in less detail than before.");
339 slowdown_message = True;
340 }
341 }
342
njn25e49d8e72002-09-23 09:36:25 +0000343 /* Build ourselves the error */
njn72718642003-07-24 08:45:32 +0000344 construct_error ( &err, tid, ekind, a, s, extra, NULL );
sewardjde4a1d02002-03-22 01:27:54 +0000345
346 /* First, see if we've got an error record matching this one. */
njn25e49d8e72002-09-23 09:36:25 +0000347 p = vg_errors;
sewardjde4a1d02002-03-22 01:27:54 +0000348 p_prev = NULL;
349 while (p != NULL) {
njn810086f2002-11-14 12:42:47 +0000350 if (eq_Error(exe_res, p, &err)) {
sewardjde4a1d02002-03-22 01:27:54 +0000351 /* Found it. */
352 p->count++;
353 if (p->supp != NULL) {
354 /* Deal correctly with suppressed errors. */
355 p->supp->count++;
356 vg_n_errs_suppressed++;
357 } else {
njn47363ab2003-04-21 13:24:40 +0000358 VG_(n_errs_found)++;
sewardjde4a1d02002-03-22 01:27:54 +0000359 }
360
361 /* Move p to the front of the list so that future searches
362 for it are faster. */
363 if (p_prev != NULL) {
364 vg_assert(p_prev->next == p);
365 p_prev->next = p->next;
njn25e49d8e72002-09-23 09:36:25 +0000366 p->next = vg_errors;
367 vg_errors = p;
sewardjde4a1d02002-03-22 01:27:54 +0000368 }
369 return;
370 }
371 p_prev = p;
372 p = p->next;
373 }
374
375 /* Didn't see it. Copy and add. */
376
njn43c799e2003-04-08 00:08:52 +0000377 /* OK, we're really going to collect it. The context is on the stack and
378 will disappear shortly, so we must copy it. First do the main
379 (non-`extra') part.
njn25e49d8e72002-09-23 09:36:25 +0000380
njn43c799e2003-04-08 00:08:52 +0000381 Then SK_(update_extra) can update the `extra' part. This is for when
382 there are more details to fill in which take time to work out but
383 don't affect our earlier decision to include the error -- by
njn25e49d8e72002-09-23 09:36:25 +0000384 postponing those details until now, we avoid the extra work in the
njn810086f2002-11-14 12:42:47 +0000385 case where we ignore the error. Ugly.
njn43c799e2003-04-08 00:08:52 +0000386
387 Then, if there is an `extra' part, copy it too, using the size that
388 SK_(update_extra) returned.
389 */
390
391 /* copy main part */
njn810086f2002-11-14 12:42:47 +0000392 p = VG_(arena_malloc)(VG_AR_ERRORS, sizeof(Error));
njn25e49d8e72002-09-23 09:36:25 +0000393 *p = err;
njn43c799e2003-04-08 00:08:52 +0000394
njn6a230532003-07-21 10:38:23 +0000395 /* update `extra', for non-core errors (core ones don't use 'extra') */
396 if (VG_(needs).skin_errors) {
397 extra_size = SK_(update_extra)(p);
njn43c799e2003-04-08 00:08:52 +0000398
njn6a230532003-07-21 10:38:23 +0000399 /* copy `extra' if there is one */
400 if (NULL != p->extra) {
401 void* new_extra = VG_(malloc)(extra_size);
402 VG_(memcpy)(new_extra, p->extra, extra_size);
403 p->extra = new_extra;
404 }
njn43c799e2003-04-08 00:08:52 +0000405 }
406
njn25e49d8e72002-09-23 09:36:25 +0000407 p->next = vg_errors;
408 p->supp = is_suppressible_error(&err);
409 vg_errors = p;
sewardjde4a1d02002-03-22 01:27:54 +0000410 if (p->supp == NULL) {
njn47363ab2003-04-21 13:24:40 +0000411 VG_(n_errs_found)++;
sewardjde4a1d02002-03-22 01:27:54 +0000412 if (!is_first_shown_context)
413 VG_(message)(Vg_UserMsg, "");
njn43c799e2003-04-08 00:08:52 +0000414 pp_Error(p, False);
sewardjde4a1d02002-03-22 01:27:54 +0000415 is_first_shown_context = False;
416 vg_n_errs_shown++;
njn72718642003-07-24 08:45:32 +0000417 do_actions_on_error(p, /*allow_GDB_attach*/True);
sewardjde4a1d02002-03-22 01:27:54 +0000418 } else {
419 vg_n_errs_suppressed++;
420 p->supp->count++;
421 }
422}
423
njn43c799e2003-04-08 00:08:52 +0000424/* Second top-level entry point to the error management subsystem, for
425 errors that the skin want to report immediately, eg. because they're
426 guaranteed to only happen once. This avoids all the recording and
427 comparing stuff. But they can be suppressed; returns True if it is
njn47363ab2003-04-21 13:24:40 +0000428 suppressed. Bool `print_error' dictates whether to print the error.
429 Bool `count_error' dictates whether to count the error in VG_(n_errs_found)
430*/
njn72718642003-07-24 08:45:32 +0000431Bool VG_(unique_error) ( ThreadId tid, ErrorKind ekind, Addr a, Char* s,
njn3e884182003-04-15 13:03:23 +0000432 void* extra, ExeContext* where, Bool print_error,
njn47363ab2003-04-21 13:24:40 +0000433 Bool allow_GDB_attach, Bool count_error )
njn43c799e2003-04-08 00:08:52 +0000434{
435 Error err;
436
437 /* Build ourselves the error */
njn72718642003-07-24 08:45:32 +0000438 construct_error ( &err, tid, ekind, a, s, extra, where );
njn43c799e2003-04-08 00:08:52 +0000439
440 /* Unless it's suppressed, we're going to show it. Don't need to make
441 a copy, because it's only temporary anyway.
442
443 Then update the `extra' part with SK_(update_extra), because that can
444 have an affect on whether it's suppressed. Ignore the size return
445 value of SK_(update_extra), because we're not copying `extra'. */
446 (void)SK_(update_extra)(&err);
447
448 if (NULL == is_suppressible_error(&err)) {
njn47363ab2003-04-21 13:24:40 +0000449 if (count_error)
450 VG_(n_errs_found)++;
njn43c799e2003-04-08 00:08:52 +0000451
452 if (print_error) {
453 if (!is_first_shown_context)
454 VG_(message)(Vg_UserMsg, "");
455 pp_Error(&err, False);
456 is_first_shown_context = False;
457 }
njn72718642003-07-24 08:45:32 +0000458 do_actions_on_error(&err, allow_GDB_attach);
njn43c799e2003-04-08 00:08:52 +0000459
460 return False;
461
462 } else {
463 vg_n_errs_suppressed++;
464 return True;
465 }
466}
467
sewardjde4a1d02002-03-22 01:27:54 +0000468
sewardjde4a1d02002-03-22 01:27:54 +0000469/*------------------------------------------------------------*/
470/*--- Exported fns ---*/
471/*------------------------------------------------------------*/
472
njn25e49d8e72002-09-23 09:36:25 +0000473/* These are called not from generated code but from the scheduler */
sewardj8c824512002-04-14 04:16:48 +0000474
njn25e49d8e72002-09-23 09:36:25 +0000475void VG_(record_pthread_error) ( ThreadId tid, Char* msg )
sewardjde4a1d02002-03-22 01:27:54 +0000476{
njn25e49d8e72002-09-23 09:36:25 +0000477 if (! VG_(needs).core_errors) return;
njn72718642003-07-24 08:45:32 +0000478 VG_(maybe_record_error)( tid, PThreadErr, /*addr*/0, msg, /*extra*/NULL );
sewardjde4a1d02002-03-22 01:27:54 +0000479}
480
sewardj8c824512002-04-14 04:16:48 +0000481/*------------------------------*/
482
sewardjde4a1d02002-03-22 01:27:54 +0000483void VG_(show_all_errors) ( void )
484{
njn810086f2002-11-14 12:42:47 +0000485 Int i, n_min;
486 Int n_err_contexts, n_supp_contexts;
487 Error *p, *p_min;
488 Supp *su;
489 Bool any_supp;
sewardjde4a1d02002-03-22 01:27:54 +0000490
491 if (VG_(clo_verbosity) == 0)
492 return;
493
494 n_err_contexts = 0;
njn25e49d8e72002-09-23 09:36:25 +0000495 for (p = vg_errors; p != NULL; p = p->next) {
sewardjde4a1d02002-03-22 01:27:54 +0000496 if (p->supp == NULL)
497 n_err_contexts++;
498 }
499
500 n_supp_contexts = 0;
501 for (su = vg_suppressions; su != NULL; su = su->next) {
502 if (su->count > 0)
503 n_supp_contexts++;
504 }
sewardjde4a1d02002-03-22 01:27:54 +0000505 VG_(message)(Vg_UserMsg,
506 "ERROR SUMMARY: "
507 "%d errors from %d contexts (suppressed: %d from %d)",
njn47363ab2003-04-21 13:24:40 +0000508 VG_(n_errs_found), n_err_contexts,
sewardjde4a1d02002-03-22 01:27:54 +0000509 vg_n_errs_suppressed, n_supp_contexts );
510
511 if (VG_(clo_verbosity) <= 1)
512 return;
513
514 /* Print the contexts in order of increasing error count. */
515 for (i = 0; i < n_err_contexts; i++) {
516 n_min = (1 << 30) - 1;
517 p_min = NULL;
njn25e49d8e72002-09-23 09:36:25 +0000518 for (p = vg_errors; p != NULL; p = p->next) {
sewardjde4a1d02002-03-22 01:27:54 +0000519 if (p->supp != NULL) continue;
520 if (p->count < n_min) {
521 n_min = p->count;
522 p_min = p;
523 }
524 }
njne427a662002-10-02 11:08:25 +0000525 if (p_min == NULL) VG_(skin_panic)("show_all_errors()");
sewardjde4a1d02002-03-22 01:27:54 +0000526
527 VG_(message)(Vg_UserMsg, "");
528 VG_(message)(Vg_UserMsg, "%d errors in context %d of %d:",
529 p_min->count,
530 i+1, n_err_contexts);
njn810086f2002-11-14 12:42:47 +0000531 pp_Error( p_min, False );
sewardjde4a1d02002-03-22 01:27:54 +0000532
533 if ((i+1 == VG_(clo_dump_error))) {
sewardj1e8cdc92002-04-18 11:37:52 +0000534 VG_(translate) ( 0 /* dummy ThreadId; irrelevant due to below NULLs */,
sewardj22854b92002-11-30 14:00:47 +0000535 p_min->where->eips[0], NULL, NULL, NULL, NULL );
sewardjde4a1d02002-03-22 01:27:54 +0000536 }
537
538 p_min->count = 1 << 30;
539 }
540
541 if (n_supp_contexts > 0)
542 VG_(message)(Vg_DebugMsg, "");
543 any_supp = False;
544 for (su = vg_suppressions; su != NULL; su = su->next) {
545 if (su->count > 0) {
546 any_supp = True;
njn25e49d8e72002-09-23 09:36:25 +0000547 VG_(message)(Vg_DebugMsg, "supp: %4d %s", su->count, su->sname);
sewardjde4a1d02002-03-22 01:27:54 +0000548 }
549 }
550
551 if (n_err_contexts > 0) {
552 if (any_supp)
553 VG_(message)(Vg_UserMsg, "");
554 VG_(message)(Vg_UserMsg,
555 "IN SUMMARY: "
556 "%d errors from %d contexts (suppressed: %d from %d)",
njn47363ab2003-04-21 13:24:40 +0000557 VG_(n_errs_found), n_err_contexts,
sewardjde4a1d02002-03-22 01:27:54 +0000558 vg_n_errs_suppressed,
559 n_supp_contexts );
560 VG_(message)(Vg_UserMsg, "");
561 }
562}
563
564/*------------------------------------------------------------*/
565/*--- Standard suppressions ---*/
566/*------------------------------------------------------------*/
567
568/* Get a non-blank, non-comment line of at most nBuf chars from fd.
569 Skips leading spaces on the line. Return True if EOF was hit instead.
570*/
571
572#define VG_ISSPACE(ch) (((ch)==' ') || ((ch)=='\n') || ((ch)=='\t'))
573
njn4ba5a792002-09-30 10:23:54 +0000574Bool VG_(get_line) ( Int fd, Char* buf, Int nBuf )
sewardjde4a1d02002-03-22 01:27:54 +0000575{
576 Char ch;
577 Int n, i;
578 while (True) {
579 /* First, read until a non-blank char appears. */
580 while (True) {
581 n = VG_(read)(fd, &ch, 1);
582 if (n == 1 && !VG_ISSPACE(ch)) break;
583 if (n == 0) return True;
584 }
585
586 /* Now, read the line into buf. */
587 i = 0;
588 buf[i++] = ch; buf[i] = 0;
589 while (True) {
590 n = VG_(read)(fd, &ch, 1);
591 if (n == 0) return False; /* the next call will return True */
592 if (ch == '\n') break;
593 if (i > 0 && i == nBuf-1) i--;
594 buf[i++] = ch; buf[i] = 0;
595 }
596 while (i > 1 && VG_ISSPACE(buf[i-1])) {
597 i--; buf[i] = 0;
598 };
599
600 /* VG_(printf)("The line is `%s'\n", buf); */
601 /* Ok, we have a line. If a non-comment line, return.
602 If a comment line, start all over again. */
603 if (buf[0] != '#') return False;
604 }
605}
606
607
608/* *p_caller contains the raw name of a caller, supposedly either
609 fun:some_function_name or
610 obj:some_object_name.
611 Set *p_ty accordingly and advance *p_caller over the descriptor
612 (fun: or obj:) part.
613 Returns False if failed.
614*/
njn25e49d8e72002-09-23 09:36:25 +0000615static Bool setLocationTy ( Char** p_caller, SuppLocTy* p_ty )
sewardjde4a1d02002-03-22 01:27:54 +0000616{
617 if (VG_(strncmp)(*p_caller, "fun:", 4) == 0) {
618 (*p_caller) += 4;
619 *p_ty = FunName;
620 return True;
621 }
622 if (VG_(strncmp)(*p_caller, "obj:", 4) == 0) {
623 (*p_caller) += 4;
624 *p_ty = ObjName;
625 return True;
626 }
627 VG_(printf)("location should start with fun: or obj:\n");
628 return False;
629}
630
631
njn11cc9252002-10-07 14:42:59 +0000632/* Look for "skin" in a string like "skin1,skin2,skin3" */
633static __inline__
634Bool skin_name_present(Char *name, Char *names)
635{
636 Bool found;
637 Char *s = NULL; /* Shut gcc up */
638 Int len = VG_(strlen)(name);
639
640 found = (NULL != (s = VG_(strstr)(names, name)) &&
641 (s == names || *(s-1) == ',') &&
642 (*(s+len) == ',' || *(s+len) == '\0')
643 );
644
645 return found;
646}
647
sewardjde4a1d02002-03-22 01:27:54 +0000648/* Read suppressions from the file specified in vg_clo_suppressions
649 and place them in the suppressions list. If there's any difficulty
650 doing this, just give up -- there's no point in trying to recover.
651*/
sewardjde4a1d02002-03-22 01:27:54 +0000652static void load_one_suppressions_file ( Char* filename )
653{
654# define N_BUF 200
njnc40c3a82002-10-02 11:02:27 +0000655 Int fd, i;
656 Bool eof;
657 Char buf[N_BUF+1];
njn11cc9252002-10-07 14:42:59 +0000658 Char* skin_names;
njnc40c3a82002-10-02 11:02:27 +0000659 Char* supp_name;
660
njn25e49d8e72002-09-23 09:36:25 +0000661 fd = VG_(open)( filename, VKI_O_RDONLY, 0 );
sewardjde4a1d02002-03-22 01:27:54 +0000662 if (fd == -1) {
njn25e49d8e72002-09-23 09:36:25 +0000663 VG_(message)(Vg_UserMsg, "FATAL: can't open suppressions file `%s'",
sewardjde4a1d02002-03-22 01:27:54 +0000664 filename );
665 VG_(exit)(1);
666 }
667
668 while (True) {
njn25e49d8e72002-09-23 09:36:25 +0000669 /* Assign and initialise the two suppression halves (core and skin) */
njn810086f2002-11-14 12:42:47 +0000670 Supp* supp;
671 supp = VG_(arena_malloc)(VG_AR_CORE, sizeof(Supp));
sewardjde4a1d02002-03-22 01:27:54 +0000672 supp->count = 0;
njn25e49d8e72002-09-23 09:36:25 +0000673 for (i = 0; i < VG_N_SUPP_CALLERS; i++) supp->caller[i] = NULL;
njn810086f2002-11-14 12:42:47 +0000674 supp->string = supp->extra = NULL;
sewardjde4a1d02002-03-22 01:27:54 +0000675
njn4ba5a792002-09-30 10:23:54 +0000676 eof = VG_(get_line) ( fd, buf, N_BUF );
sewardjde4a1d02002-03-22 01:27:54 +0000677 if (eof) break;
678
njn43c799e2003-04-08 00:08:52 +0000679 if (!VG_STREQ(buf, "{")) goto syntax_error;
sewardjde4a1d02002-03-22 01:27:54 +0000680
njn4ba5a792002-09-30 10:23:54 +0000681 eof = VG_(get_line) ( fd, buf, N_BUF );
njn43c799e2003-04-08 00:08:52 +0000682 if (eof || VG_STREQ(buf, "}")) goto syntax_error;
njn25e49d8e72002-09-23 09:36:25 +0000683 supp->sname = VG_(arena_strdup)(VG_AR_CORE, buf);
sewardjde4a1d02002-03-22 01:27:54 +0000684
njn4ba5a792002-09-30 10:23:54 +0000685 eof = VG_(get_line) ( fd, buf, N_BUF );
njn25e49d8e72002-09-23 09:36:25 +0000686
sewardjde4a1d02002-03-22 01:27:54 +0000687 if (eof) goto syntax_error;
sewardjde4a1d02002-03-22 01:27:54 +0000688
njn11cc9252002-10-07 14:42:59 +0000689 /* Check it has the "skin1,skin2,...:supp" form (look for ':') */
njnc40c3a82002-10-02 11:02:27 +0000690 i = 0;
691 while (True) {
692 if (buf[i] == ':') break;
693 if (buf[i] == '\0') goto syntax_error;
694 i++;
njn25e49d8e72002-09-23 09:36:25 +0000695 }
njnc40c3a82002-10-02 11:02:27 +0000696 buf[i] = '\0'; /* Replace ':', splitting into two strings */
697
njn11cc9252002-10-07 14:42:59 +0000698 skin_names = & buf[0];
699 supp_name = & buf[i+1];
njnc40c3a82002-10-02 11:02:27 +0000700
njn11cc9252002-10-07 14:42:59 +0000701 /* Is it a core suppression? */
702 if (VG_(needs).core_errors && skin_name_present("core", skin_names))
njnc40c3a82002-10-02 11:02:27 +0000703 {
njn43c799e2003-04-08 00:08:52 +0000704 if (VG_STREQ(supp_name, "PThread"))
njn810086f2002-11-14 12:42:47 +0000705 supp->skind = PThreadSupp;
njnc40c3a82002-10-02 11:02:27 +0000706 else
707 goto syntax_error;
708 }
709
njn11cc9252002-10-07 14:42:59 +0000710 /* Is it a skin suppression? */
711 else if (VG_(needs).skin_errors &&
712 skin_name_present(VG_(details).name, skin_names))
njnc40c3a82002-10-02 11:02:27 +0000713 {
njn810086f2002-11-14 12:42:47 +0000714 if (SK_(recognised_suppression)(supp_name, supp))
njnc40c3a82002-10-02 11:02:27 +0000715 {
njn810086f2002-11-14 12:42:47 +0000716 /* Do nothing, function fills in supp->skind */
njnc40c3a82002-10-02 11:02:27 +0000717 } else
718 goto syntax_error;
719 }
720
njn25e49d8e72002-09-23 09:36:25 +0000721 else {
njnc40c3a82002-10-02 11:02:27 +0000722 /* Ignore rest of suppression */
njn25e49d8e72002-09-23 09:36:25 +0000723 while (True) {
njn4ba5a792002-09-30 10:23:54 +0000724 eof = VG_(get_line) ( fd, buf, N_BUF );
njn25e49d8e72002-09-23 09:36:25 +0000725 if (eof) goto syntax_error;
njn43c799e2003-04-08 00:08:52 +0000726 if (VG_STREQ(buf, "}"))
njn25e49d8e72002-09-23 09:36:25 +0000727 break;
728 }
729 continue;
sewardjde4a1d02002-03-22 01:27:54 +0000730 }
731
njn25e49d8e72002-09-23 09:36:25 +0000732 if (VG_(needs).skin_errors &&
njn810086f2002-11-14 12:42:47 +0000733 !SK_(read_extra_suppression_info)(fd, buf, N_BUF, supp))
sewardjde4a1d02002-03-22 01:27:54 +0000734 goto syntax_error;
735
njn25e49d8e72002-09-23 09:36:25 +0000736 /* "i > 0" ensures at least one caller read. */
njn633de322003-05-12 20:40:13 +0000737 for (i = 0; i <= VG_N_SUPP_CALLERS; i++) {
njn4ba5a792002-09-30 10:23:54 +0000738 eof = VG_(get_line) ( fd, buf, N_BUF );
sewardjde4a1d02002-03-22 01:27:54 +0000739 if (eof) goto syntax_error;
njn43c799e2003-04-08 00:08:52 +0000740 if (i > 0 && VG_STREQ(buf, "}"))
njn25e49d8e72002-09-23 09:36:25 +0000741 break;
njn633de322003-05-12 20:40:13 +0000742 if (i == VG_N_SUPP_CALLERS)
743 break;
njn25e49d8e72002-09-23 09:36:25 +0000744 supp->caller[i] = VG_(arena_strdup)(VG_AR_CORE, buf);
745 if (!setLocationTy(&(supp->caller[i]), &(supp->caller_ty[i])))
746 goto syntax_error;
sewardjde4a1d02002-03-22 01:27:54 +0000747 }
748
sewardj57a8f5f2003-07-06 01:40:11 +0000749 /* make sure to grab the '}' if the num callers is >=
750 VG_N_SUPP_CALLERS */
751 if (!VG_STREQ(buf, "}")) {
752 do {
753 eof = VG_(get_line) ( fd, buf, N_BUF );
754 } while (!eof && !VG_STREQ(buf, "}"));
755 }
756
sewardjde4a1d02002-03-22 01:27:54 +0000757 supp->next = vg_suppressions;
758 vg_suppressions = supp;
759 }
sewardjde4a1d02002-03-22 01:27:54 +0000760 VG_(close)(fd);
761 return;
762
763 syntax_error:
764 if (eof) {
765 VG_(message)(Vg_UserMsg,
766 "FATAL: in suppressions file `%s': unexpected EOF",
767 filename );
768 } else {
769 VG_(message)(Vg_UserMsg,
njn11cc9252002-10-07 14:42:59 +0000770 "FATAL: in suppressions file: `%s': syntax error on: %s",
sewardjde4a1d02002-03-22 01:27:54 +0000771 filename, buf );
772 }
773 VG_(close)(fd);
774 VG_(message)(Vg_UserMsg, "exiting now.");
775 VG_(exit)(1);
776
777# undef N_BUF
778}
779
780
781void VG_(load_suppressions) ( void )
782{
783 Int i;
784 vg_suppressions = NULL;
785 for (i = 0; i < VG_(clo_n_suppressions); i++) {
786 if (VG_(clo_verbosity) > 1) {
787 VG_(message)(Vg_UserMsg, "Reading suppressions file: %s",
788 VG_(clo_suppressions)[i] );
789 }
790 load_one_suppressions_file( VG_(clo_suppressions)[i] );
791 }
792}
793
njn25e49d8e72002-09-23 09:36:25 +0000794/* Return the name of an erring fn in a way which is useful
795 for comparing against the contents of a suppressions file.
796 Doesn't demangle the fn name, because we want to refer to
797 mangled names in the suppressions file.
sewardj99aac972002-12-26 01:53:45 +0000798*/
njn43c799e2003-04-08 00:08:52 +0000799static void get_objname_fnname ( Addr a, Char* obj_buf, Int n_obj_buf,
800 Char* fun_buf, Int n_fun_buf )
njn25e49d8e72002-09-23 09:36:25 +0000801{
802 (void)VG_(get_objname) ( a, obj_buf, n_obj_buf );
803 (void)VG_(get_fnname_nodemangle)( a, fun_buf, n_fun_buf );
804}
805
806static __inline__
njn810086f2002-11-14 12:42:47 +0000807Bool supp_matches_error(Supp* su, Error* err)
njn25e49d8e72002-09-23 09:36:25 +0000808{
njn810086f2002-11-14 12:42:47 +0000809 switch (su->skind) {
njn25e49d8e72002-09-23 09:36:25 +0000810 case PThreadSupp:
njn810086f2002-11-14 12:42:47 +0000811 return (err->ekind == PThreadErr);
njn25e49d8e72002-09-23 09:36:25 +0000812 default:
813 if (VG_(needs).skin_errors) {
njn810086f2002-11-14 12:42:47 +0000814 return SK_(error_matches_suppression)(err, su);
njn25e49d8e72002-09-23 09:36:25 +0000815 } else {
816 VG_(printf)(
817 "\nUnhandled suppression type: %u. VG_(needs).skin_errors\n"
818 "probably needs to be set.\n",
njn810086f2002-11-14 12:42:47 +0000819 err->ekind);
njne427a662002-10-02 11:08:25 +0000820 VG_(skin_panic)("unhandled suppression type");
njn25e49d8e72002-09-23 09:36:25 +0000821 }
822 }
823}
824
825static __inline__
njn810086f2002-11-14 12:42:47 +0000826Bool supp_matches_callers(Supp* su, Char caller_obj[][M_VG_ERRTXT],
827 Char caller_fun[][M_VG_ERRTXT])
njn25e49d8e72002-09-23 09:36:25 +0000828{
829 Int i;
830
njn633de322003-05-12 20:40:13 +0000831 for (i = 0; i < VG_N_SUPP_CALLERS && su->caller[i] != NULL; i++) {
njn25e49d8e72002-09-23 09:36:25 +0000832 switch (su->caller_ty[i]) {
njn4ba5a792002-09-30 10:23:54 +0000833 case ObjName: if (VG_(string_match)(su->caller[i],
834 caller_obj[i])) break;
njn25e49d8e72002-09-23 09:36:25 +0000835 return False;
njn4ba5a792002-09-30 10:23:54 +0000836 case FunName: if (VG_(string_match)(su->caller[i],
837 caller_fun[i])) break;
njn25e49d8e72002-09-23 09:36:25 +0000838 return False;
njn43c799e2003-04-08 00:08:52 +0000839 default: VG_(skin_panic)("supp_matches_callers");
njn25e49d8e72002-09-23 09:36:25 +0000840 }
841 }
842
843 /* If we reach here, it's a match */
844 return True;
845}
sewardjde4a1d02002-03-22 01:27:54 +0000846
njn810086f2002-11-14 12:42:47 +0000847/* Does an error context match a suppression? ie is this a suppressible
848 error? If so, return a pointer to the Supp record, otherwise NULL.
njn25e49d8e72002-09-23 09:36:25 +0000849 Tries to minimise the number of symbol searches since they are expensive.
sewardjde4a1d02002-03-22 01:27:54 +0000850*/
njn810086f2002-11-14 12:42:47 +0000851static Supp* is_suppressible_error ( Error* err )
sewardjde4a1d02002-03-22 01:27:54 +0000852{
njn25e49d8e72002-09-23 09:36:25 +0000853 Int i;
sewardjde4a1d02002-03-22 01:27:54 +0000854
njn25e49d8e72002-09-23 09:36:25 +0000855 Char caller_obj[VG_N_SUPP_CALLERS][M_VG_ERRTXT];
856 Char caller_fun[VG_N_SUPP_CALLERS][M_VG_ERRTXT];
sewardjde4a1d02002-03-22 01:27:54 +0000857
njn810086f2002-11-14 12:42:47 +0000858 Supp* su;
sewardjde4a1d02002-03-22 01:27:54 +0000859
njn25e49d8e72002-09-23 09:36:25 +0000860 /* get_objname_fnname() writes the function name and object name if
njn43c799e2003-04-08 00:08:52 +0000861 it finds them in the debug info. So the strings in the suppression
njn25e49d8e72002-09-23 09:36:25 +0000862 file should match these.
sewardjde4a1d02002-03-22 01:27:54 +0000863 */
864
865 /* Initialise these strs so they are always safe to compare, even
njn25e49d8e72002-09-23 09:36:25 +0000866 if get_objname_fnname doesn't write anything to them. */
867 for (i = 0; i < VG_N_SUPP_CALLERS; i++)
868 caller_obj[i][0] = caller_fun[i][0] = 0;
sewardjde4a1d02002-03-22 01:27:54 +0000869
njn25e49d8e72002-09-23 09:36:25 +0000870 for (i = 0; i < VG_N_SUPP_CALLERS && i < VG_(clo_backtrace_size); i++) {
njn43c799e2003-04-08 00:08:52 +0000871 get_objname_fnname ( err->where->eips[i], caller_obj[i], M_VG_ERRTXT,
872 caller_fun[i], M_VG_ERRTXT );
sewardjde4a1d02002-03-22 01:27:54 +0000873 }
874
875 /* See if the error context matches any suppression. */
876 for (su = vg_suppressions; su != NULL; su = su->next) {
njn25e49d8e72002-09-23 09:36:25 +0000877 if (supp_matches_error(su, err) &&
878 supp_matches_callers(su, caller_obj, caller_fun)) {
879 return su;
sewardjde4a1d02002-03-22 01:27:54 +0000880 }
sewardjde4a1d02002-03-22 01:27:54 +0000881 }
njn25e49d8e72002-09-23 09:36:25 +0000882 return NULL; /* no matches */
sewardjde4a1d02002-03-22 01:27:54 +0000883}
884
sewardjde4a1d02002-03-22 01:27:54 +0000885/*--------------------------------------------------------------------*/
886/*--- end vg_errcontext.c ---*/
887/*--------------------------------------------------------------------*/