1
2 /*--------------------------------------------------------------------*/
3 /*--- Management of error messages. m_errormgr.c ---*/
4 /*--------------------------------------------------------------------*/
5
6 /*
7 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
9
10 Copyright (C) 2000-2010 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 "pub_core_basics.h"
32 #include "pub_core_vki.h"
33 #include "pub_core_threadstate.h" // For VG_N_THREADS
34 #include "pub_core_debugger.h"
35 #include "pub_core_debuginfo.h"
36 #include "pub_core_errormgr.h"
37 #include "pub_core_execontext.h"
38 #include "pub_core_libcbase.h"
39 #include "pub_core_libcassert.h"
40 #include "pub_core_libcfile.h"
41 #include "pub_core_libcprint.h"
42 #include "pub_core_libcproc.h" // For VG_(getpid)()
43 #include "pub_core_seqmatch.h"
44 #include "pub_core_mallocfree.h"
45 #include "pub_core_options.h"
46 #include "pub_core_stacktrace.h"
47 #include "pub_core_tooliface.h"
48 #include "pub_core_translate.h" // for VG_(translate)()
49 #include "pub_core_xarray.h" // VG_(xaprintf) et al
50
51 /*------------------------------------------------------------*/
52 /*--- Globals ---*/
53 /*------------------------------------------------------------*/
54
55 /* After this many different unsuppressed errors have been observed,
56 be more conservative about collecting new ones. */
57 #define M_COLLECT_ERRORS_SLOWLY_AFTER 100
58
59 /* After this many different unsuppressed errors have been observed,
60 stop collecting errors at all, and tell the user their program is
61 evidently a steaming pile of camel dung. */
62 #define M_COLLECT_NO_ERRORS_AFTER_SHOWN 1000
63
64 /* After this many total errors have been observed, stop collecting
65 errors at all. Counterpart to M_COLLECT_NO_ERRORS_AFTER_SHOWN. */
66 #define M_COLLECT_NO_ERRORS_AFTER_FOUND 10000000
67
68 /* The list of error contexts found, both suppressed and unsuppressed.
69 Initially empty, and grows as errors are detected. */
70 static Error* errors = NULL;
71
72 /* The list of suppression directives, as read from the specified
73 suppressions file. Note that the list gets rearranged as a result
74 of the searches done by is_suppressible_error(). */
75 static Supp* suppressions = NULL;
76
77 /* Running count of unsuppressed errors detected. */
78 static UInt n_errs_found = 0;
79
80 /* Running count of suppressed errors detected. */
81 static UInt n_errs_suppressed = 0;
82
83 /* Running count of unsuppressed error contexts. */
84 static UInt n_err_contexts = 0;
85
86 /* Running count of suppressed error contexts. */
87 static UInt n_supp_contexts = 0;
88
89
90 /* forwards ... */
91 static Supp* is_suppressible_error ( Error* err );
92
93 static ThreadId last_tid_printed = 1;
94
95 /* Stats: number of searches of the error list initiated. */
96 static UWord em_errlist_searches = 0;
97
98 /* Stats: number of comparisons done during error list
99 searching. */
100 static UWord em_errlist_cmps = 0;
101
102 /* Stats: number of searches of the suppression list initiated. */
103 static UWord em_supplist_searches = 0;
104
105 /* Stats: number of comparisons done during suppression list
106 searching. */
107 static UWord em_supplist_cmps = 0;
108
109 /*------------------------------------------------------------*/
110 /*--- Error type ---*/
111 /*------------------------------------------------------------*/
112
113 /* Errors. Extensible (via the 'extra' field). Tools can use a normal
114 enum (with element values in the normal range (0..)) for 'ekind'.
115 Functions for getting/setting the tool-relevant fields are in
116 include/pub_tool_errormgr.h.
117
118 When errors are found and recorded with VG_(maybe_record_error)(), all
119 the tool must do is pass in the four parameters; core will
120 allocate/initialise the error record.
121 */
122 struct _Error {
123 struct _Error* next;
124 // Unique tag. This gives the error a unique identity (handle) by
125 // which it can be referred to afterwords. Currently only used for
126 // XML printing.
127 UInt unique;
128 // NULL if unsuppressed; or ptr to suppression record.
129 Supp* supp;
130 Int count;
131
132 // The tool-specific part
133 ThreadId tid; // Initialised by core
134 ExeContext* where; // Initialised by core
135 ErrorKind ekind; // Used by ALL. Must be in the range (0..)
136 Addr addr; // Used frequently
137 Char* string; // Used frequently
138 void* extra; // For any tool-specific extras
139 };
140
141
VG_(get_error_where)142 ExeContext* VG_(get_error_where) ( Error* err )
143 {
144 return err->where;
145 }
146
VG_(get_error_kind)147 ErrorKind VG_(get_error_kind) ( Error* err )
148 {
149 return err->ekind;
150 }
151
VG_(get_error_address)152 Addr VG_(get_error_address) ( Error* err )
153 {
154 return err->addr;
155 }
156
VG_(get_error_string)157 Char* VG_(get_error_string) ( Error* err )
158 {
159 return err->string;
160 }
161
VG_(get_error_extra)162 void* VG_(get_error_extra) ( Error* err )
163 {
164 return err->extra;
165 }
166
VG_(get_n_errs_found)167 UInt VG_(get_n_errs_found)( void )
168 {
169 return n_errs_found;
170 }
171
172 /*------------------------------------------------------------*/
173 /*--- Suppression type ---*/
174 /*------------------------------------------------------------*/
175
176 /* Note: it is imperative this doesn't overlap with (0..) at all, as tools
177 * effectively extend it by defining their own enums in the (0..) range. */
178 typedef
179 enum {
180 // Nb: thread errors are a relic of the time when Valgrind's core
181 // could detect them. This example is left commented-out as an
182 // example should new core errors ever be added.
183 ThreadSupp = -1, /* Matches ThreadErr */
184 }
185 CoreSuppKind;
186
187 /* Max number of callers for context in a suppression. */
188 #define VG_MAX_SUPP_CALLERS 24
189
190 /* For each caller specified for a suppression, record the nature of
191 the caller name. Not of interest to tools. */
192 typedef
193 enum {
194 NoName, /* Error case */
195 ObjName, /* Name is of an shared object file. */
196 FunName, /* Name is of a function. */
197 DotDotDot /* Frame-level wildcard */
198 }
199 SuppLocTy;
200
201 typedef
202 struct {
203 SuppLocTy ty;
204 Char* name; /* NULL for NoName and DotDotDot */
205 }
206 SuppLoc;
207
208 /* Suppressions. Tools can get/set tool-relevant parts with functions
209 declared in include/pub_tool_errormgr.h. Extensible via the 'extra' field.
210 Tools can use a normal enum (with element values in the normal range
211 (0..)) for 'skind'. */
212 struct _Supp {
213 struct _Supp* next;
214 Int count; // The number of times this error has been suppressed.
215 Char* sname; // The name by which the suppression is referred to.
216
217 // Length of 'callers'
218 Int n_callers;
219 // Array of callers, for matching stack traces. First one (name of fn
220 // where err occurs) is mandatory; rest are optional.
221 SuppLoc* callers;
222
223 /* The tool-specific part */
224 SuppKind skind; // What kind of suppression. Must use the range (0..).
225 Char* string; // String -- use is optional. NULL by default.
226 void* extra; // Anything else -- use is optional. NULL by default.
227 };
228
VG_(get_supp_kind)229 SuppKind VG_(get_supp_kind) ( Supp* su )
230 {
231 return su->skind;
232 }
233
VG_(get_supp_string)234 Char* VG_(get_supp_string) ( Supp* su )
235 {
236 return su->string;
237 }
238
VG_(get_supp_extra)239 void* VG_(get_supp_extra) ( Supp* su )
240 {
241 return su->extra;
242 }
243
244
VG_(set_supp_kind)245 void VG_(set_supp_kind) ( Supp* su, SuppKind skind )
246 {
247 su->skind = skind;
248 }
249
VG_(set_supp_string)250 void VG_(set_supp_string) ( Supp* su, Char* string )
251 {
252 su->string = string;
253 }
254
VG_(set_supp_extra)255 void VG_(set_supp_extra) ( Supp* su, void* extra )
256 {
257 su->extra = extra;
258 }
259
260
261 /*------------------------------------------------------------*/
262 /*--- Helper fns ---*/
263 /*------------------------------------------------------------*/
264
265 // Only show core errors if the tool wants to, we're not running with -q,
266 // and were not outputting XML.
VG_(showing_core_errors)267 Bool VG_(showing_core_errors)(void)
268 {
269 return VG_(needs).core_errors && VG_(clo_verbosity) >= 1 && !VG_(clo_xml);
270 }
271
272 /* Compare errors, to detect duplicates.
273 */
eq_Error(VgRes res,Error * e1,Error * e2)274 static Bool eq_Error ( VgRes res, Error* e1, Error* e2 )
275 {
276 if (e1->ekind != e2->ekind)
277 return False;
278 if (!VG_(eq_ExeContext)(res, e1->where, e2->where))
279 return False;
280
281 switch (e1->ekind) {
282 //(example code, see comment on CoreSuppKind above)
283 //case ThreadErr:
284 // vg_assert(VG_(needs).core_errors);
285 // return <something>
286 default:
287 if (VG_(needs).tool_errors) {
288 return VG_TDICT_CALL(tool_eq_Error, res, e1, e2);
289 } else {
290 VG_(printf)("\nUnhandled error type: %u. VG_(needs).tool_errors\n"
291 "probably needs to be set.\n",
292 e1->ekind);
293 VG_(tool_panic)("unhandled error type");
294 }
295 }
296 }
297
298
299 /* Helper functions for suppression generation: print a single line of
300 a suppression pseudo-stack-trace, either in XML or text mode. It's
301 important that the behaviour of these two functions exactly
302 corresponds.
303 */
304 #define ERRTXT_LEN 4096
305
printSuppForIp_XML(UInt n,Addr ip,void * uu_opaque)306 static void printSuppForIp_XML(UInt n, Addr ip, void* uu_opaque)
307 {
308 static UChar buf[ERRTXT_LEN];
309 if ( VG_(get_fnname_no_cxx_demangle) (ip, buf, ERRTXT_LEN) ) {
310 VG_(printf_xml_no_f_c)(" <sframe> <fun>%t</fun> </sframe>\n", buf);
311 } else
312 if ( VG_(get_objname)(ip, buf, ERRTXT_LEN) ) {
313 VG_(printf_xml_no_f_c)(" <sframe> <obj>%t</obj> </sframe>\n", buf);
314 } else {
315 VG_(printf_xml_no_f_c)(" <sframe> <obj>*</obj> </sframe>\n");
316 }
317 }
318
printSuppForIp_nonXML(UInt n,Addr ip,void * textV)319 static void printSuppForIp_nonXML(UInt n, Addr ip, void* textV)
320 {
321 static UChar buf[ERRTXT_LEN];
322 XArray* /* of HChar */ text = (XArray*)textV;
323 if ( VG_(get_fnname_no_cxx_demangle) (ip, buf, ERRTXT_LEN) ) {
324 VG_(xaprintf)(text, " fun:%s\n", buf);
325 } else
326 if ( VG_(get_objname)(ip, buf, ERRTXT_LEN) ) {
327 VG_(xaprintf)(text, " obj:%s\n", buf);
328 } else {
329 VG_(xaprintf)(text, " obj:*\n");
330 }
331 }
332
333 /* Generate a suppression for an error, either in text or XML mode.
334 */
gen_suppression(Error * err)335 static void gen_suppression(Error* err)
336 {
337 Char xtra[256]; /* assumed big enough (is overrun-safe) */
338 Bool anyXtra;
339 Char* name;
340 ExeContext* ec;
341 XArray* /* HChar */ text;
342
343 const HChar* dummy_name = "insert_a_suppression_name_here";
344
345 vg_assert(err);
346
347 /* In XML mode, we also need to print the plain text version of the
348 suppresion in a CDATA section. What that really means is, we
349 need to generate the plaintext version both in XML and text
350 mode. So generate it into TEXT. */
351 text = VG_(newXA)( VG_(malloc), "errormgr.gen_suppression.1",
352 VG_(free), sizeof(HChar) );
353 vg_assert(text);
354
355 ec = VG_(get_error_where)(err);
356 vg_assert(ec);
357
358 name = VG_TDICT_CALL(tool_get_error_name, err);
359 if (NULL == name) {
360 VG_(umsg)("(%s does not allow error to be suppressed)\n",
361 VG_(details).name);
362 return;
363 }
364
365 /* Ok. Generate the plain text version into TEXT. */
366 VG_(xaprintf)(text, "{\n");
367 VG_(xaprintf)(text, " <%s>\n", dummy_name);
368 VG_(xaprintf)(text, " %s:%s\n", VG_(details).name, name);
369
370 VG_(memset)(xtra, 0, sizeof(xtra));
371 anyXtra = VG_TDICT_CALL(tool_get_extra_suppression_info,
372 err, xtra, sizeof(xtra));
373 vg_assert(xtra[sizeof(xtra)-1] == 0);
374
375 if (anyXtra)
376 VG_(xaprintf)(text, " %s\n", xtra);
377
378 // Print stack trace elements
379 UInt n_ips = VG_(get_ExeContext_n_ips)(ec);
380 tl_assert(n_ips > 0);
381 if (n_ips > VG_MAX_SUPP_CALLERS)
382 n_ips = VG_MAX_SUPP_CALLERS;
383 VG_(apply_StackTrace)(printSuppForIp_nonXML,
384 text,
385 VG_(get_ExeContext_StackTrace)(ec),
386 n_ips);
387
388 VG_(xaprintf)(text, "}\n");
389 // zero terminate
390 VG_(xaprintf)(text, "%c", (HChar)0 );
391 // VG_(printf) of text
392
393 /* And now display it. */
394 if (! VG_(clo_xml) ) {
395
396 // the simple case
397 VG_(printf)("%s", (HChar*) VG_(indexXA)(text, 0) );
398
399 } else {
400
401 /* Now we have to print the XML directly. No need to go to the
402 effort of stuffing it in an XArray, since we won't need it
403 again. */
404 VG_(printf_xml)(" <suppression>\n");
405 VG_(printf_xml)(" <sname>%s</sname>\n", dummy_name);
406 VG_(printf_xml_no_f_c)(
407 " <skind>%t:%t</skind>\n", VG_(details).name, name);
408 if (anyXtra)
409 VG_(printf_xml_no_f_c)(" <skaux>%t</skaux>\n", xtra);
410
411 // Print stack trace elements
412 VG_(apply_StackTrace)(printSuppForIp_XML,
413 NULL,
414 VG_(get_ExeContext_StackTrace)(ec),
415 VG_(get_ExeContext_n_ips)(ec));
416
417 // And now the cdata bit
418 // XXX FIXME! properly handle the case where the raw text
419 // itself contains "]]>", as specified in Protocol 4.
420 VG_(printf_xml)(" <rawtext>\n");
421 VG_(printf_xml)("<![CDATA[\n");
422 VG_(printf_xml)("%s", (HChar*) VG_(indexXA)(text, 0) );
423 VG_(printf_xml)("]]>\n");
424 VG_(printf_xml)(" </rawtext>\n");
425 VG_(printf_xml)(" </suppression>\n");
426
427 }
428
429 VG_(deleteXA)(text);
430 }
431
432
433 /* Figure out if we want to perform a given action for this error,
434 possibly by asking the user.
435 */
VG_(is_action_requested)436 Bool VG_(is_action_requested) ( Char* action, Bool* clo )
437 {
438 Char ch, ch2;
439 Int res;
440
441 /* First off, we shouldn't be asking the user anything if
442 we're in XML mode. */
443 if (VG_(clo_xml))
444 return False; /* That's a Nein, oder Nay as they say down here in B-W */
445
446 if (*clo == False)
447 return False;
448
449 VG_(umsg)("\n");
450
451 again:
452 VG_(printf)(
453 "==%d== "
454 "---- %s ? --- [Return/N/n/Y/y/C/c] ---- ",
455 VG_(getpid)(), action
456 );
457
458 res = VG_(read)(VG_(clo_input_fd), &ch, 1);
459 if (res != 1) goto ioerror;
460 /* res == 1 */
461 if (ch == '\n') return False;
462 if (ch != 'N' && ch != 'n' && ch != 'Y' && ch != 'y'
463 && ch != 'C' && ch != 'c') goto again;
464
465 res = VG_(read)(VG_(clo_input_fd), &ch2, 1);
466 if (res != 1) goto ioerror;
467 if (ch2 != '\n') goto again;
468
469 /* No, don't want to do action. */
470 if (ch == 'n' || ch == 'N') return False;
471 /* Yes, want to do action. */
472 if (ch == 'y' || ch == 'Y') return True;
473 /* No, don't want to do action, and don't ask again either. */
474 vg_assert(ch == 'c' || ch == 'C');
475
476 ioerror:
477 *clo = False;
478 return False;
479 }
480
481
482 /* Do text-mode actions on error, that is, immediately after an error
483 is printed. These are:
484 * possibly, attach to a debugger
485 * possibly, generate a suppression.
486 Note this should not be called in XML mode!
487 */
488 static
do_actions_on_error(Error * err,Bool allow_db_attach)489 void do_actions_on_error(Error* err, Bool allow_db_attach)
490 {
491 Bool still_noisy = True;
492
493 /* Should be assured by caller */
494 vg_assert( ! VG_(clo_xml) );
495
496 /* Perhaps we want a debugger attach at this point? */
497 if (allow_db_attach &&
498 VG_(is_action_requested)( "Attach to debugger", & VG_(clo_db_attach) ))
499 {
500 if (0) VG_(printf)("starting debugger\n");
501 VG_(start_debugger)( err->tid );
502 }
503 /* Or maybe we want to generate the error's suppression? */
504 if (VG_(clo_gen_suppressions) == 2
505 || (VG_(clo_gen_suppressions) == 1
506 && VG_(is_action_requested)( "Print suppression", &still_noisy ))
507 ) {
508 gen_suppression(err);
509 }
510 if (VG_(clo_gen_suppressions) == 1 && !still_noisy)
511 VG_(clo_gen_suppressions) = 0;
512 }
513
514 // See https://bugs.kde.org/show_bug.cgi?id=265803 and b/3423996
515 static Bool seen_pc_with_no_function_name_nor_object_file_name = False;
516
ErrHasNoFunctionNamesNorObjectFileNames(Error * err)517 static Bool ErrHasNoFunctionNamesNorObjectFileNames(Error *err) {
518 // boil out if the stack trace has no function/object names.
519 StackTrace ips = VG_(get_ExeContext_StackTrace)(err->where);
520 UWord n_ips = VG_(get_ExeContext_n_ips)(err->where);
521 UWord i;
522 for (i = 0; i < n_ips; i++) {
523 Addr ip = ips[i];
524 Char buffer[1024];
525 if (VG_(get_fnname)(ip, buffer, sizeof(buffer))) {
526 return False;
527 }
528 if (VG_(get_objname)(ip, buffer, sizeof(buffer))) {
529 return False;
530 }
531 }
532 if (!seen_pc_with_no_function_name_nor_object_file_name)
533 VG_(umsg)("\n\n\nWARNING: Valgrind encountered a stack trace which has\n"
534 "no function names nor object file names.\n"
535 "Unless your program has a dynamically generated code (e.g. it is a JIT)\n"
536 "something is very much wrong with your binary's debug info.\n"
537 "See https://bugs.kde.org/show_bug.cgi?id=265803 and b/3423996\n\n\n"
538 );
539 seen_pc_with_no_function_name_nor_object_file_name = True;
540 return True;
541 }
542
543 /* Prints an error. Not entirely simple because of the differences
544 between XML and text mode output.
545
546 In XML mode:
547
548 * calls the tool's pre-show method, so the tool can create any
549 preamble ahead of the message, if it wants.
550
551 * prints the opening tag, and the <unique> and <tid> fields
552
553 * prints the tool-specific parts of the message
554
555 * if suppression generation is required, a suppression
556
557 * the closing tag
558
559 In text mode:
560
561 * calls the tool's pre-show method, so the tool can create any
562 preamble ahead of the message, if it wants.
563
564 * prints the tool-specific parts of the message
565
566 * calls do_actions_on_error. This optionally does a debugger
567 attach (and detach), and optionally prints a suppression; both
568 of these may require user input.
569 */
pp_Error(Error * err,Bool allow_db_attach)570 static void pp_Error ( Error* err, Bool allow_db_attach )
571 {
572 /* If this fails, you probably specified your tool's method
573 dictionary incorrectly. */
574 vg_assert(VG_(needs).tool_errors);
575
576 if (VG_(clo_xml)) {
577
578 /* Note, allow_db_attach is ignored in here. */
579
580 /* Ensure that suppression generation is either completely
581 enabled or completely disabled; either way, we won't require
582 any user input. m_main.process_cmd_line_options should
583 ensure the asserted condition holds. */
584 vg_assert( VG_(clo_gen_suppressions) == 0 /* disabled */
585 || VG_(clo_gen_suppressions) == 2 /* for all errors */ );
586
587 /* Pre-show it to the tool */
588 VG_TDICT_CALL( tool_before_pp_Error, err );
589
590 /* standard preamble */
591 VG_(printf_xml)("<error>\n");
592 VG_(printf_xml)(" <unique>0x%x</unique>\n", err->unique);
593 VG_(printf_xml)(" <tid>%d</tid>\n", err->tid);
594
595 /* actually print it */
596 VG_TDICT_CALL( tool_pp_Error, err );
597
598 if (VG_(clo_gen_suppressions) > 0)
599 gen_suppression(err);
600
601 /* postamble */
602 VG_(printf_xml)("</error>\n");
603 VG_(printf_xml)("\n");
604
605 } else {
606
607 VG_TDICT_CALL( tool_before_pp_Error, err );
608
609 if (VG_(tdict).tool_show_ThreadIDs_for_errors
610 && err->tid > 0 && err->tid != last_tid_printed) {
611 VG_(umsg)("Thread %d:\n", err->tid );
612 last_tid_printed = err->tid;
613 }
614
615 VG_TDICT_CALL( tool_pp_Error, err );
616 VG_(umsg)("\n");
617
618 do_actions_on_error(err, allow_db_attach);
619 }
620 }
621
622
623 /* Construct an error */
624 static
construct_error(Error * err,ThreadId tid,ErrorKind ekind,Addr a,Char * s,void * extra,ExeContext * where)625 void construct_error ( Error* err, ThreadId tid, ErrorKind ekind, Addr a,
626 Char* s, void* extra, ExeContext* where )
627 {
628 /* DO NOT MAKE unique_counter NON-STATIC */
629 static UInt unique_counter = 0;
630
631 tl_assert(tid < VG_N_THREADS);
632
633 /* Core-only parts */
634 err->unique = unique_counter++;
635 err->next = NULL;
636 err->supp = NULL;
637 err->count = 1;
638 err->tid = tid;
639 if (NULL == where)
640 err->where = VG_(record_ExeContext)( tid, 0 );
641 else
642 err->where = where;
643
644 /* Tool-relevant parts */
645 err->ekind = ekind;
646 err->addr = a;
647 err->extra = extra;
648 err->string = s;
649
650 /* sanity... */
651 vg_assert( tid < VG_N_THREADS );
652 }
653
654
655
656 static Int n_errs_shown = 0;
657
658 /* Top-level entry point to the error management subsystem.
659 All detected errors are notified here; this routine decides if/when the
660 user should see the error. */
VG_(maybe_record_error)661 void VG_(maybe_record_error) ( ThreadId tid,
662 ErrorKind ekind, Addr a, Char* s, void* extra )
663 {
664 Error err;
665 Error* p;
666 Error* p_prev;
667 UInt extra_size;
668 VgRes exe_res = Vg_MedRes;
669 static Bool stopping_message = False;
670 static Bool slowdown_message = False;
671
672 /* After M_COLLECT_NO_ERRORS_AFTER_SHOWN different errors have
673 been found, or M_COLLECT_NO_ERRORS_AFTER_FOUND total errors
674 have been found, just refuse to collect any more. This stops
675 the burden of the error-management system becoming excessive in
676 extremely buggy programs, although it does make it pretty
677 pointless to continue the Valgrind run after this point. */
678 if (VG_(clo_error_limit)
679 && (n_errs_shown >= M_COLLECT_NO_ERRORS_AFTER_SHOWN
680 || n_errs_found >= M_COLLECT_NO_ERRORS_AFTER_FOUND)
681 && !VG_(clo_xml)) {
682 if (!stopping_message) {
683 VG_(umsg)("\n");
684
685 if (n_errs_shown >= M_COLLECT_NO_ERRORS_AFTER_SHOWN) {
686 VG_(umsg)(
687 "More than %d different errors detected. "
688 "I'm not reporting any more.\n",
689 M_COLLECT_NO_ERRORS_AFTER_SHOWN );
690 } else {
691 VG_(umsg)(
692 "More than %d total errors detected. "
693 "I'm not reporting any more.\n",
694 M_COLLECT_NO_ERRORS_AFTER_FOUND );
695 }
696
697 VG_(umsg)("Final error counts will be inaccurate. "
698 "Go fix your program!\n");
699 VG_(umsg)("Rerun with --error-limit=no to disable "
700 "this cutoff. Note\n");
701 VG_(umsg)("that errors may occur in your program without "
702 "prior warning from\n");
703 VG_(umsg)("Valgrind, because errors are no longer "
704 "being displayed.\n");
705 VG_(umsg)("\n");
706 stopping_message = True;
707 }
708 return;
709 }
710
711 /* After M_COLLECT_ERRORS_SLOWLY_AFTER different errors have
712 been found, be much more conservative about collecting new
713 ones. */
714 if (n_errs_shown >= M_COLLECT_ERRORS_SLOWLY_AFTER
715 && !VG_(clo_xml)) {
716 exe_res = Vg_LowRes;
717 if (!slowdown_message) {
718 VG_(umsg)("\n");
719 VG_(umsg)("More than %d errors detected. Subsequent errors\n",
720 M_COLLECT_ERRORS_SLOWLY_AFTER);
721 VG_(umsg)("will still be recorded, but in less "
722 "detail than before.\n");
723 slowdown_message = True;
724 }
725 } else if (seen_pc_with_no_function_name_nor_object_file_name) {
726 // we are probably inside some unknown code -- don't spend too much time
727 // matching the error reports.
728 exe_res = Vg_LowRes;
729 }
730
731 /* Build ourselves the error */
732 construct_error ( &err, tid, ekind, a, s, extra, NULL );
733
734 /* First, see if we've got an error record matching this one. */
735 em_errlist_searches++;
736 p = errors;
737 p_prev = NULL;
738 while (p != NULL) {
739 em_errlist_cmps++;
740 if (eq_Error(exe_res, p, &err)) {
741 /* Found it. */
742 p->count++;
743 if (p->supp != NULL) {
744 /* Deal correctly with suppressed errors. */
745 p->supp->count++;
746 n_errs_suppressed++;
747 } else {
748 if (!seen_pc_with_no_function_name_nor_object_file_name)
749 n_errs_found++;
750 }
751
752 /* Move p to the front of the list so that future searches
753 for it are faster. */
754 if (p_prev != NULL) {
755 vg_assert(p_prev->next == p);
756 p_prev->next = p->next;
757 p->next = errors;
758 errors = p;
759 }
760
761 return;
762 }
763 p_prev = p;
764 p = p->next;
765 }
766
767 /* Didn't see it. Copy and add. */
768
769 /* OK, we're really going to collect it. The context is on the stack and
770 will disappear shortly, so we must copy it. First do the main
771 (non-'extra') part.
772
773 Then VG_(tdict).tool_update_extra can update the 'extra' part. This
774 is for when there are more details to fill in which take time to work
775 out but don't affect our earlier decision to include the error -- by
776 postponing those details until now, we avoid the extra work in the
777 case where we ignore the error. Ugly.
778
779 Then, if there is an 'extra' part, copy it too, using the size that
780 VG_(tdict).tool_update_extra returned. Also allow for people using
781 the void* extra field for a scalar value like an integer.
782 */
783
784 /* copy main part */
785 p = VG_(arena_malloc)(VG_AR_ERRORS, "errormgr.mre.1", sizeof(Error));
786 *p = err;
787
788 /* update 'extra' */
789 switch (ekind) {
790 //(example code, see comment on CoreSuppKind above)
791 //case ThreadErr:
792 // vg_assert(VG_(needs).core_errors);
793 // extra_size = <something>
794 // break;
795 default:
796 vg_assert(VG_(needs).tool_errors);
797 extra_size = VG_TDICT_CALL(tool_update_extra, p);
798 break;
799 }
800
801 /* copy block pointed to by 'extra', if there is one */
802 if (NULL != p->extra && 0 != extra_size) {
803 void* new_extra = VG_(malloc)("errormgr.mre.2", extra_size);
804 VG_(memcpy)(new_extra, p->extra, extra_size);
805 p->extra = new_extra;
806 }
807
808 p->next = errors;
809 p->supp = is_suppressible_error(&err);
810 errors = p;
811
812 if (ErrHasNoFunctionNamesNorObjectFileNames(p))
813 return;
814
815 if (p->supp == NULL) {
816 n_err_contexts++;
817 n_errs_found++;
818 /* Actually show the error; more complex than you might think. */
819 pp_Error( p, /*allow_db_attach*/True );
820 /* update stats */
821 n_errs_shown++;
822 } else {
823 n_supp_contexts++;
824 n_errs_suppressed++;
825 p->supp->count++;
826 }
827 }
828
829 /* Second top-level entry point to the error management subsystem, for
830 errors that the tool wants to report immediately, eg. because they're
831 guaranteed to only happen once. This avoids all the recording and
832 comparing stuff. But they can be suppressed; returns True if it is
833 suppressed. Bool 'print_error' dictates whether to print the error.
834 Bool 'count_error' dictates whether to count the error in n_errs_found.
835 */
VG_(unique_error)836 Bool VG_(unique_error) ( ThreadId tid, ErrorKind ekind, Addr a, Char* s,
837 void* extra, ExeContext* where, Bool print_error,
838 Bool allow_db_attach, Bool count_error )
839 {
840 Error err;
841 Supp *su;
842
843 /* Build ourselves the error */
844 construct_error ( &err, tid, ekind, a, s, extra, where );
845
846 /* Unless it's suppressed, we're going to show it. Don't need to make
847 a copy, because it's only temporary anyway.
848
849 Then update the 'extra' part with VG_(tdict).tool_update_extra),
850 because that can have an affect on whether it's suppressed. Ignore
851 the size return value of VG_(tdict).tool_update_extra, because we're
852 not copying 'extra'. */
853 (void)VG_TDICT_CALL(tool_update_extra, &err);
854
855 su = is_suppressible_error(&err);
856 if (NULL == su) {
857 if (count_error) {
858 n_errs_found++;
859 n_err_contexts++;
860 }
861
862 if (print_error) {
863 /* Actually show the error; more complex than you might think. */
864 pp_Error(&err, allow_db_attach);
865 /* update stats */
866 n_errs_shown++;
867 }
868 return False;
869
870 } else {
871 if (count_error) {
872 n_errs_suppressed++;
873 n_supp_contexts++;
874 }
875 su->count++;
876 return True;
877 }
878 }
879
880
881 /*------------------------------------------------------------*/
882 /*--- Exported fns ---*/
883 /*------------------------------------------------------------*/
884
885 /* Show the used suppressions. Returns False if no suppression
886 got used. */
show_used_suppressions(void)887 static Bool show_used_suppressions ( void )
888 {
889 Supp *su;
890 Bool any_supp;
891
892 if (VG_(clo_xml))
893 VG_(printf_xml)("<suppcounts>\n");
894
895 any_supp = False;
896 for (su = suppressions; su != NULL; su = su->next) {
897 if (su->count <= 0)
898 continue;
899 if (VG_(clo_xml)) {
900 VG_(printf_xml_no_f_c)( " <pair>\n"
901 " <count>%d</count>\n"
902 " <name>%t</name>\n"
903 " </pair>\n",
904 su->count, su->sname );
905 } else {
906 // blank line before the first shown suppression, if any
907 if (!any_supp)
908 VG_(dmsg)("\n");
909 VG_(dmsg)("used_suppression: %6d %s\n", su->count, su->sname);
910 }
911 any_supp = True;
912 }
913
914 if (VG_(clo_xml))
915 VG_(printf_xml)("</suppcounts>\n");
916
917 return any_supp;
918 }
919
920
921 /* Show all the errors that occurred, and possibly also the
922 suppressions used. */
VG_(show_all_errors)923 void VG_(show_all_errors) ( void )
924 {
925 Int i, n_min;
926 Error *p, *p_min;
927 Bool any_supp;
928
929 if (VG_(clo_verbosity) == 0)
930 return;
931
932 /* If we're printing XML, just show the suppressions and stop. */
933 if (VG_(clo_xml)) {
934 (void)show_used_suppressions();
935 return;
936 }
937
938 /* We only get here if not printing XML. */
939 VG_(umsg)("ERROR SUMMARY: "
940 "%d errors from %d contexts (suppressed: %d from %d)\n",
941 n_errs_found, n_err_contexts,
942 n_errs_suppressed, n_supp_contexts );
943
944 if (VG_(clo_verbosity) <= 1)
945 return;
946
947 // We do the following only at -v or above, and only in non-XML
948 // mode
949
950 /* Print the contexts in order of increasing error count. */
951 for (i = 0; i < n_err_contexts; i++) {
952 n_min = (1 << 30) - 1;
953 p_min = NULL;
954 for (p = errors; p != NULL; p = p->next) {
955 if (p->supp != NULL) continue;
956 if (p->count < n_min) {
957 n_min = p->count;
958 p_min = p;
959 }
960 }
961 // XXX: this isn't right. See bug 203651.
962 if (p_min == NULL) continue; //VG_(tool_panic)("show_all_errors()");
963
964 VG_(umsg)("\n");
965 VG_(umsg)("%d errors in context %d of %d:\n",
966 p_min->count, i+1, n_err_contexts);
967 pp_Error( p_min, False/*allow_db_attach*/ );
968
969 // We're not printing XML -- we'd have exited above if so.
970 vg_assert(! VG_(clo_xml));
971
972 if ((i+1 == VG_(clo_dump_error))) {
973 StackTrace ips = VG_(get_ExeContext_StackTrace)(p_min->where);
974 VG_(translate) ( 0 /* dummy ThreadId; irrelevant due to debugging*/,
975 ips[0], /*debugging*/True, 0xFE/*verbosity*/,
976 /*bbs_done*/0,
977 /*allow redir?*/True);
978 }
979
980 p_min->count = 1 << 30;
981 }
982
983 any_supp = show_used_suppressions();
984
985 if (any_supp)
986 VG_(umsg)("\n");
987 // reprint this, so users don't have to scroll way up to find
988 // the first printing
989 VG_(umsg)("ERROR SUMMARY: "
990 "%d errors from %d contexts (suppressed: %d from %d)\n",
991 n_errs_found, n_err_contexts, n_errs_suppressed,
992 n_supp_contexts );
993 }
994
995
996 /* Show occurrence counts of all errors, in XML form. */
VG_(show_error_counts_as_XML)997 void VG_(show_error_counts_as_XML) ( void )
998 {
999 Error* err;
1000 VG_(printf_xml)("<errorcounts>\n");
1001 for (err = errors; err != NULL; err = err->next) {
1002 if (err->supp != NULL)
1003 continue;
1004 if (err->count <= 0)
1005 continue;
1006 VG_(printf_xml)(" <pair>\n");
1007 VG_(printf_xml)(" <count>%d</count>\n", err->count);
1008 VG_(printf_xml)(" <unique>0x%x</unique>\n", err->unique);
1009 VG_(printf_xml)(" </pair>\n");
1010 }
1011 VG_(printf_xml)("</errorcounts>\n");
1012 VG_(printf_xml)("\n");
1013 }
1014
1015
1016 /*------------------------------------------------------------*/
1017 /*--- Suppression parsing ---*/
1018 /*------------------------------------------------------------*/
1019
1020 /* Get the next char from fd into *out_buf. Returns 1 if success,
1021 0 if eof or < 0 if error. */
1022
get_char(Int fd,Char * out_buf)1023 static Int get_char ( Int fd, Char* out_buf )
1024 {
1025 Int r;
1026 static Char buf[256];
1027 static Int buf_size = 0;
1028 static Int buf_used = 0;
1029 vg_assert(buf_size >= 0 && buf_size <= 256);
1030 vg_assert(buf_used >= 0 && buf_used <= buf_size);
1031 if (buf_used == buf_size) {
1032 r = VG_(read)(fd, buf, 256);
1033 if (r < 0) return r; /* read failed */
1034 vg_assert(r >= 0 && r <= 256);
1035 buf_size = r;
1036 buf_used = 0;
1037 }
1038 if (buf_size == 0)
1039 return 0; /* eof */
1040 vg_assert(buf_size >= 0 && buf_size <= 256);
1041 vg_assert(buf_used >= 0 && buf_used < buf_size);
1042 *out_buf = buf[buf_used];
1043 buf_used++;
1044 return 1;
1045 }
1046
VG_(get_line)1047 Bool VG_(get_line) ( Int fd, Char** bufpp, SizeT* nBufp, Int* lineno )
1048 {
1049 Char* buf = *bufpp;
1050 SizeT nBuf = *nBufp;
1051 Char ch;
1052 Int n, i;
1053 while (True) {
1054 /* First, read until a non-blank char appears. */
1055 while (True) {
1056 n = get_char(fd, &ch);
1057 if (n == 1 && !VG_(isspace)(ch)) break;
1058 if (n == 1 && ch == '\n' && lineno)
1059 (*lineno)++;
1060 if (n <= 0) return True;
1061 }
1062
1063 /* Now, read the line into buf. */
1064 i = 0;
1065 buf[i++] = ch; buf[i] = 0;
1066 while (True) {
1067 n = get_char(fd, &ch);
1068 if (n <= 0) return False; /* the next call will return True */
1069 if (ch == '\n' && lineno)
1070 (*lineno)++;
1071 if (ch == '\n') break;
1072 if (i > 0 && i == nBuf-1) {
1073 *nBufp = nBuf = nBuf * 2;
1074 #define RIDICULOUS 100000
1075 vg_assert2(nBuf < RIDICULOUS, // Just a sanity check, really.
1076 "VG_(get_line): line longer than %d chars, aborting\n",
1077 RIDICULOUS);
1078 *bufpp = buf = VG_(realloc)("errormgr.get_line.1", buf, nBuf);
1079 }
1080 buf[i++] = ch; buf[i] = 0;
1081 }
1082 while (i > 1 && VG_(isspace)(buf[i-1])) {
1083 i--; buf[i] = 0;
1084 };
1085
1086 /* VG_(printf)("The line is '%s'\n", buf); */
1087 /* Ok, we have a line. If a non-comment line, return.
1088 If a comment line, start all over again. */
1089 if (buf[0] != '#') return False;
1090 }
1091 }
1092
1093
1094 /* *p_caller contains the raw name of a caller, supposedly either
1095 fun:some_function_name or
1096 obj:some_object_name.
1097 Set *p_ty accordingly and advance *p_caller over the descriptor
1098 (fun: or obj:) part.
1099 Returns False if failed.
1100 */
setLocationTy(SuppLoc * p)1101 static Bool setLocationTy ( SuppLoc* p )
1102 {
1103 if (VG_(strncmp)(p->name, "fun:", 4) == 0) {
1104 p->name += 4;
1105 p->ty = FunName;
1106 return True;
1107 }
1108 if (VG_(strncmp)(p->name, "obj:", 4) == 0) {
1109 p->name += 4;
1110 p->ty = ObjName;
1111 return True;
1112 }
1113 if (VG_(strcmp)(p->name, "...") == 0) {
1114 p->name = NULL;
1115 p->ty = DotDotDot;
1116 return True;
1117 }
1118 VG_(printf)("location should be \"...\", or should start "
1119 "with \"fun:\" or \"obj:\"\n");
1120 return False;
1121 }
1122
1123
1124 /* Look for "tool" in a string like "tool1,tool2,tool3" */
tool_name_present(Char * name,Char * names)1125 static Bool tool_name_present(Char *name, Char *names)
1126 {
1127 Bool found;
1128 Char *s = NULL; /* Shut gcc up */
1129 Int len = VG_(strlen)(name);
1130
1131 found = (NULL != (s = VG_(strstr)(names, name)) &&
1132 (s == names || *(s-1) == ',') &&
1133 (*(s+len) == ',' || *(s+len) == '\0')
1134 );
1135
1136 return found;
1137 }
1138
1139 /* Read suppressions from the file specified in VG_(clo_suppressions)
1140 and place them in the suppressions list. If there's any difficulty
1141 doing this, just give up -- there's no point in trying to recover.
1142 */
load_one_suppressions_file(Char * filename)1143 static void load_one_suppressions_file ( Char* filename )
1144 {
1145 SysRes sres;
1146 Int fd, i, j, lineno = 0;
1147 Bool eof;
1148 SizeT nBuf = 200;
1149 Char* buf = VG_(malloc)("errormgr.losf.1", nBuf);
1150 Char* tool_names;
1151 Char* supp_name;
1152 Char* err_str = NULL;
1153 SuppLoc tmp_callers[VG_MAX_SUPP_CALLERS];
1154
1155 // Check it's not a directory.
1156 if (VG_(is_dir)( filename )) {
1157 if (VG_(clo_xml))
1158 VG_(printf_xml)("</valgrindoutput>\n");
1159 VG_(umsg)("FATAL: suppressions file \"%s\" is a directory\n", filename );
1160 VG_(exit)(1);
1161 }
1162
1163 // Open the suppression file.
1164 sres = VG_(open)( filename, VKI_O_RDONLY, 0 );
1165 if (sr_isError(sres)) {
1166 if (VG_(clo_xml))
1167 VG_(printf_xml)("</valgrindoutput>\n");
1168 VG_(umsg)("FATAL: can't open suppressions file \"%s\"\n", filename );
1169 VG_(exit)(1);
1170 }
1171 fd = sr_Res(sres);
1172
1173 # define BOMB(S) { err_str = S; goto syntax_error; }
1174
1175 while (True) {
1176 /* Assign and initialise the two suppression halves (core and tool) */
1177 Supp* supp;
1178 supp = VG_(arena_malloc)(VG_AR_CORE, "errormgr.losf.1",
1179 sizeof(Supp));
1180 supp->count = 0;
1181
1182 // Initialise temporary reading-in buffer.
1183 for (i = 0; i < VG_MAX_SUPP_CALLERS; i++) {
1184 tmp_callers[i].ty = NoName;
1185 tmp_callers[i].name = NULL;
1186 }
1187
1188 supp->string = supp->extra = NULL;
1189
1190 eof = VG_(get_line) ( fd, &buf, &nBuf, &lineno );
1191 if (eof) break;
1192
1193 if (!VG_STREQ(buf, "{")) BOMB("expected '{' or end-of-file");
1194
1195 eof = VG_(get_line) ( fd, &buf, &nBuf, &lineno );
1196
1197 if (eof || VG_STREQ(buf, "}")) BOMB("unexpected '}'");
1198
1199 supp->sname = VG_(arena_strdup)(VG_AR_CORE, "errormgr.losf.2", buf);
1200
1201 eof = VG_(get_line) ( fd, &buf, &nBuf, &lineno );
1202
1203 if (eof) BOMB("unexpected end-of-file");
1204
1205 /* Check it has the "tool1,tool2,...:supp" form (look for ':') */
1206 i = 0;
1207 while (True) {
1208 if (buf[i] == ':') break;
1209 if (buf[i] == '\0') BOMB("malformed 'tool1,tool2,...:supp' line");
1210 i++;
1211 }
1212 buf[i] = '\0'; /* Replace ':', splitting into two strings */
1213
1214 tool_names = & buf[0];
1215 supp_name = & buf[i+1];
1216
1217 if (VG_(needs).core_errors && tool_name_present("core", tool_names))
1218 {
1219 // A core suppression
1220 //(example code, see comment on CoreSuppKind above)
1221 //if (VG_STREQ(supp_name, "Thread"))
1222 // supp->skind = ThreadSupp;
1223 //else
1224 BOMB("unknown core suppression type");
1225 }
1226 else if (VG_(needs).tool_errors &&
1227 tool_name_present(VG_(details).name, tool_names))
1228 {
1229 // A tool suppression
1230 if (VG_TDICT_CALL(tool_recognised_suppression, supp_name, supp)) {
1231 /* Do nothing, function fills in supp->skind */
1232 } else {
1233 BOMB("unknown tool suppression type");
1234 }
1235 }
1236 else {
1237 // Ignore rest of suppression
1238 while (True) {
1239 eof = VG_(get_line) ( fd, &buf, &nBuf, &lineno );
1240 if (eof) BOMB("unexpected end-of-file");
1241 if (VG_STREQ(buf, "}"))
1242 break;
1243 }
1244 continue;
1245 }
1246
1247 if (VG_(needs).tool_errors &&
1248 !VG_TDICT_CALL(tool_read_extra_suppression_info,
1249 fd, &buf, &nBuf, supp))
1250 {
1251 BOMB("bad or missing extra suppression info");
1252 }
1253
1254 /* the main frame-descriptor reading loop */
1255 i = 0;
1256 while (True) {
1257 eof = VG_(get_line) ( fd, &buf, &nBuf, &lineno );
1258 if (eof)
1259 BOMB("unexpected end-of-file");
1260 if (VG_STREQ(buf, "}")) {
1261 if (i > 0) {
1262 break;
1263 } else {
1264 BOMB("missing stack trace");
1265 }
1266 }
1267 if (i == VG_MAX_SUPP_CALLERS)
1268 BOMB("too many callers in stack trace");
1269 if (i > 0 && i >= VG_(clo_backtrace_size))
1270 break;
1271 tmp_callers[i].name = VG_(arena_strdup)(VG_AR_CORE,
1272 "errormgr.losf.3", buf);
1273 if (!setLocationTy(&(tmp_callers[i])))
1274 BOMB("location should be \"...\", or should start "
1275 "with \"fun:\" or \"obj:\"");
1276 i++;
1277 }
1278
1279 // If the num callers is >= VG_(clo_backtrace_size), ignore any extra
1280 // lines and grab the '}'.
1281 if (!VG_STREQ(buf, "}")) {
1282 do {
1283 eof = VG_(get_line) ( fd, &buf, &nBuf, &lineno );
1284 } while (!eof && !VG_STREQ(buf, "}"));
1285 }
1286
1287 // Reject entries which are entirely composed of frame
1288 // level wildcards.
1289 vg_assert(i > 0); // guaranteed by frame-descriptor reading loop
1290 for (j = 0; j < i; j++) {
1291 if (tmp_callers[j].ty == FunName || tmp_callers[j].ty == ObjName)
1292 break;
1293 vg_assert(tmp_callers[j].ty == DotDotDot);
1294 }
1295 vg_assert(j >= 0 && j <= i);
1296 if (j == i) {
1297 // we didn't find any non-"..." entries
1298 BOMB("suppression must contain at least one location "
1299 "line which is not \"...\"");
1300 }
1301
1302 // Copy tmp_callers[] into supp->callers[]
1303 supp->n_callers = i;
1304 supp->callers = VG_(arena_malloc)(VG_AR_CORE, "errormgr.losf.4",
1305 i*sizeof(SuppLoc));
1306 for (i = 0; i < supp->n_callers; i++) {
1307 supp->callers[i] = tmp_callers[i];
1308 }
1309
1310 supp->next = suppressions;
1311 suppressions = supp;
1312 }
1313 VG_(free)(buf);
1314 VG_(close)(fd);
1315 return;
1316
1317 syntax_error:
1318 if (VG_(clo_xml))
1319 VG_(printf_xml)("</valgrindoutput>\n");
1320 VG_(umsg)("FATAL: in suppressions file \"%s\" near line %d:\n",
1321 filename, lineno );
1322 VG_(umsg)(" %s\n", err_str );
1323
1324 VG_(close)(fd);
1325 VG_(umsg)("exiting now.\n");
1326 VG_(exit)(1);
1327
1328 # undef BOMB
1329 }
1330
1331
VG_(load_suppressions)1332 void VG_(load_suppressions) ( void )
1333 {
1334 Int i;
1335 suppressions = NULL;
1336 for (i = 0; i < VG_(clo_n_suppressions); i++) {
1337 if (VG_(clo_verbosity) > 1) {
1338 VG_(dmsg)("Reading suppressions file: %s\n",
1339 VG_(clo_suppressions)[i] );
1340 }
1341 load_one_suppressions_file( VG_(clo_suppressions)[i] );
1342 }
1343 }
1344
1345
1346 /*------------------------------------------------------------*/
1347 /*--- Matching errors to suppressions ---*/
1348 /*------------------------------------------------------------*/
1349
1350 /* Parameterising functions for the use of VG_(generic_match) in
1351 suppression-vs-error matching. The suppression frames (SuppLoc)
1352 play the role of 'pattern'-element, and the error frames (IPs,
1353 hence simply Addrs) play the role of 'input'. In short then, we're
1354 matching a sequence of Addrs against a pattern composed of a
1355 sequence of SuppLocs.
1356 */
supploc_IsStar(void * supplocV)1357 static Bool supploc_IsStar ( void* supplocV )
1358 {
1359 SuppLoc* supploc = (SuppLoc*)supplocV;
1360 return supploc->ty == DotDotDot;
1361 }
1362
supploc_IsQuery(void * supplocV)1363 static Bool supploc_IsQuery ( void* supplocV )
1364 {
1365 return False; /* there's no '?' equivalent in the supp syntax */
1366 }
1367
supp_pattEQinp(void * supplocV,void * addrV)1368 static Bool supp_pattEQinp ( void* supplocV, void* addrV )
1369 {
1370 SuppLoc* supploc = (SuppLoc*)supplocV; /* PATTERN */
1371 Addr ip = *(Addr*)addrV; /* INPUT */
1372
1373 Char caller_name[ERRTXT_LEN];
1374 caller_name[0] = 0;
1375
1376 /* So, does this IP address match this suppression-line? */
1377 switch (supploc->ty) {
1378 case DotDotDot:
1379 /* supp_pattEQinp is a callback from VG_(generic_match). As
1380 per the spec thereof (see include/pub_tool_seqmatch.h), we
1381 should never get called with a pattern value for which the
1382 _IsStar or _IsQuery function would return True. Hence
1383 this can't happen. */
1384 vg_assert(0);
1385 case ObjName:
1386 /* Get the object name into 'caller_name', or "???"
1387 if unknown. */
1388 if (!VG_(get_objname)(ip, caller_name, ERRTXT_LEN))
1389 VG_(strcpy)(caller_name, "???");
1390 break;
1391 case FunName:
1392 /* Get the function name into 'caller_name', or "???"
1393 if unknown. */
1394 // Nb: C++-mangled names are used in suppressions. Do, though,
1395 // Z-demangle them, since otherwise it's possible to wind
1396 // up comparing "malloc" in the suppression against
1397 // "_vgrZU_libcZdsoZa_malloc" in the backtrace, and the
1398 // two of them need to be made to match.
1399 if (!VG_(get_fnname_no_cxx_demangle)(ip, caller_name, ERRTXT_LEN))
1400 VG_(strcpy)(caller_name, "???");
1401 break;
1402 default:
1403 vg_assert(0);
1404 }
1405
1406 /* So now we have the function or object name in caller_name, and
1407 the pattern (at the character level) to match against is in
1408 supploc->name. Hence (and leading to a re-entrant call of
1409 VG_(generic_match)): */
1410 return VG_(string_match)(supploc->name, caller_name);
1411 }
1412
1413 /////////////////////////////////////////////////////
1414
supp_matches_callers(Error * err,Supp * su)1415 static Bool supp_matches_callers(Error* err, Supp* su)
1416 {
1417 /* Unwrap the args and set up the correct parameterisation of
1418 VG_(generic_match), using supploc_IsStar, supploc_IsQuery and
1419 supp_pattEQinp. */
1420 /* note, StackTrace === Addr* */
1421 StackTrace ips = VG_(get_ExeContext_StackTrace)(err->where);
1422 UWord n_ips = VG_(get_ExeContext_n_ips)(err->where);
1423 SuppLoc* supps = su->callers;
1424 UWord n_supps = su->n_callers;
1425 UWord szbPatt = sizeof(SuppLoc);
1426 UWord szbInput = sizeof(Addr);
1427 Bool matchAll = False; /* we just want to match a prefix */
1428 return
1429 VG_(generic_match)(
1430 matchAll,
1431 /*PATT*/supps, szbPatt, n_supps, 0/*initial Ix*/,
1432 /*INPUT*/ips, szbInput, n_ips, 0/*initial Ix*/,
1433 supploc_IsStar, supploc_IsQuery, supp_pattEQinp
1434 );
1435 }
1436
1437 /////////////////////////////////////////////////////
1438
1439 static
supp_matches_error(Supp * su,Error * err)1440 Bool supp_matches_error(Supp* su, Error* err)
1441 {
1442 switch (su->skind) {
1443 //(example code, see comment on CoreSuppKind above)
1444 //case ThreadSupp:
1445 // return (err->ekind == ThreadErr);
1446 default:
1447 if (VG_(needs).tool_errors) {
1448 return VG_TDICT_CALL(tool_error_matches_suppression, err, su);
1449 } else {
1450 VG_(printf)(
1451 "\nUnhandled suppression type: %u. VG_(needs).tool_errors\n"
1452 "probably needs to be set.\n",
1453 err->ekind);
1454 VG_(tool_panic)("unhandled suppression type");
1455 }
1456 }
1457 }
1458
1459 /////////////////////////////////////////////////////
1460
1461 /* Does an error context match a suppression? ie is this a suppressible
1462 error? If so, return a pointer to the Supp record, otherwise NULL.
1463 Tries to minimise the number of symbol searches since they are expensive.
1464 */
is_suppressible_error(Error * err)1465 static Supp* is_suppressible_error ( Error* err )
1466 {
1467 Supp* su;
1468 Supp* su_prev;
1469
1470 /* stats gathering */
1471 em_supplist_searches++;
1472
1473 /* See if the error context matches any suppression. */
1474 su_prev = NULL;
1475 for (su = suppressions; su != NULL; su = su->next) {
1476 em_supplist_cmps++;
1477 if (supp_matches_error(su, err) && supp_matches_callers(err, su)) {
1478 /* got a match. Move this entry to the head of the list
1479 in the hope of making future searches cheaper. */
1480 if (su_prev) {
1481 vg_assert(su_prev->next == su);
1482 su_prev->next = su->next;
1483 su->next = suppressions;
1484 suppressions = su;
1485 }
1486 return su;
1487 }
1488 su_prev = su;
1489 }
1490 return NULL; /* no matches */
1491 }
1492
1493 /* Show accumulated error-list and suppression-list search stats.
1494 */
VG_(print_errormgr_stats)1495 void VG_(print_errormgr_stats) ( void )
1496 {
1497 VG_(dmsg)(
1498 " errormgr: %'lu supplist searches, %'lu comparisons during search\n",
1499 em_supplist_searches, em_supplist_cmps
1500 );
1501 VG_(dmsg)(
1502 " errormgr: %'lu errlist searches, %'lu comparisons during search\n",
1503 em_errlist_searches, em_errlist_cmps
1504 );
1505 }
1506
1507 /*--------------------------------------------------------------------*/
1508 /*--- end ---*/
1509 /*--------------------------------------------------------------------*/
1510