Initial revision
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/vg_errcontext.c b/coregrind/vg_errcontext.c
new file mode 100644
index 0000000..42e09b5
--- /dev/null
+++ b/coregrind/vg_errcontext.c
@@ -0,0 +1,1070 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Management of error messages. vg_errcontext.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, an x86 protected-mode emulator
+ designed for debugging and profiling binaries on x86-Unixes.
+
+ Copyright (C) 2000-2002 Julian Seward
+ jseward@acm.org
+ Julian_Seward@muraroa.demon.co.uk
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file LICENSE.
+*/
+
+#include "vg_include.h"
+#include "vg_constants.h"
+
+
+/*------------------------------------------------------------*/
+/*--- Defns ---*/
+/*------------------------------------------------------------*/
+
+/* Suppression is a type describing an error which we want to
+ suppress, ie, not show the user, usually because it is caused by a
+ problem in a library which we can't fix, replace or work around.
+ Suppressions are read from a file at startup time, specified by
+ vg_clo_suppressions, and placed in the vg_suppressions list. This
+ gives flexibility so that new suppressions can be added to the file
+ as and when needed.
+*/
+typedef
+ enum {
+ /* Bad syscall params */
+ Param,
+ /* Use of invalid values of given size */
+ Value0, Value1, Value2, Value4, Value8,
+ /* Invalid read/write attempt at given size */
+ Addr1, Addr2, Addr4, Addr8,
+ /* Invalid or mismatching free */
+ FreeS
+ }
+ SuppressionKind;
+
+
+/* For each caller specified for a suppression, record the nature of
+ the caller name. */
+typedef
+ enum {
+ /* Name is of an shared object file. */
+ ObjName,
+ /* Name is of a function. */
+ FunName
+ }
+ SuppressionLocTy;
+
+
+/* A complete suppression record. */
+typedef
+ struct _Suppression {
+ struct _Suppression* next;
+ /* The number of times this error has been suppressed. */
+ Int count;
+ /* The name by which the suppression is referred to. */
+ Char* sname;
+ /* What kind of suppression. */
+ SuppressionKind skind;
+ /* Name of syscall param if skind==Param */
+ Char* param;
+ /* Name of fn where err occurs, and immediate caller (mandatory). */
+ SuppressionLocTy caller0_ty;
+ Char* caller0;
+ SuppressionLocTy caller1_ty;
+ Char* caller1;
+ /* Optional extra callers. */
+ SuppressionLocTy caller2_ty;
+ Char* caller2;
+ SuppressionLocTy caller3_ty;
+ Char* caller3;
+ }
+ Suppression;
+
+
+/* ErrContext is a type for recording just enough info to generate an
+ error report for an illegal memory access. The idea is that
+ (typically) the same few points in the program generate thousands
+ of illegal accesses, and we don't want to spew out a fresh error
+ message for each one. Instead, we use these structures to common
+ up duplicates.
+*/
+
+/* What kind of error it is. */
+typedef
+ enum { ValueErr, AddrErr,
+ ParamErr, UserErr, /* behaves like an anonymous ParamErr */
+ FreeErr, FreeMismatchErr }
+ ErrKind;
+
+/* What kind of memory access is involved in the error? */
+typedef
+ enum { ReadAxs, WriteAxs, ExecAxs }
+ AxsKind;
+
+/* Top-level struct for recording errors. */
+typedef
+ struct _ErrContext {
+ /* ALL */
+ struct _ErrContext* next;
+ /* ALL */
+ /* NULL if unsuppressed; or ptr to suppression record. */
+ Suppression* supp;
+ /* ALL */
+ Int count;
+ /* ALL */
+ ErrKind ekind;
+ /* ALL */
+ ExeContext* where;
+ /* Addr */
+ AxsKind axskind;
+ /* Addr, Value */
+ Int size;
+ /* Addr, Free, Param, User */
+ Addr addr;
+ /* Addr, Free, Param, User */
+ AddrInfo addrinfo;
+ /* Param */
+ Char* syscall_param;
+ /* Param, User */
+ Bool isWriteableLack;
+ }
+ ErrContext;
+
+/* The list of error contexts found, both suppressed and unsuppressed.
+ Initially empty, and grows as errors are detected. */
+static ErrContext* vg_err_contexts = NULL;
+
+/* The list of suppression directives, as read from the specified
+ suppressions file. */
+static Suppression* vg_suppressions = NULL;
+
+/* Running count of unsuppressed errors detected. */
+static UInt vg_n_errs_found = 0;
+
+/* Running count of suppressed errors detected. */
+static UInt vg_n_errs_suppressed = 0;
+
+/* forwards ... */
+static Suppression* is_suppressible_error ( ErrContext* ec );
+
+
+/*------------------------------------------------------------*/
+/*--- Helper fns ---*/
+/*------------------------------------------------------------*/
+
+
+static void clear_AddrInfo ( AddrInfo* ai )
+{
+ ai->akind = Unknown;
+ ai->blksize = 0;
+ ai->rwoffset = 0;
+ ai->lastchange = NULL;
+}
+
+static void clear_ErrContext ( ErrContext* ec )
+{
+ ec->next = NULL;
+ ec->supp = NULL;
+ ec->count = 0;
+ ec->ekind = ValueErr;
+ ec->where = NULL;
+ ec->axskind = ReadAxs;
+ ec->size = 0;
+ ec->addr = 0;
+ clear_AddrInfo ( &ec->addrinfo );
+ ec->syscall_param = NULL;
+ ec->isWriteableLack = False;
+}
+
+
+static __inline__
+Bool vg_eq_ExeContext ( Bool top_2_only,
+ ExeContext* e1, ExeContext* e2 )
+{
+ /* Note that frames after the 4th are always ignored. */
+ if (top_2_only) {
+ return VG_(eq_ExeContext_top2(e1, e2));
+ } else {
+ return VG_(eq_ExeContext_top4(e1, e2));
+ }
+}
+
+
+static Bool eq_AddrInfo ( Bool cheap_addr_cmp,
+ AddrInfo* ai1, AddrInfo* ai2 )
+{
+ if (ai1->akind != ai2->akind)
+ return False;
+ if (ai1->akind == Freed || ai1->akind == Mallocd) {
+ if (ai1->blksize != ai2->blksize)
+ return False;
+ if (!vg_eq_ExeContext(cheap_addr_cmp,
+ ai1->lastchange, ai2->lastchange))
+ return False;
+ }
+ return True;
+}
+
+/* Compare error contexts, to detect duplicates. Note that if they
+ are otherwise the same, the faulting addrs and associated rwoffsets
+ are allowed to be different. */
+
+static Bool eq_ErrContext ( Bool cheap_addr_cmp,
+ ErrContext* e1, ErrContext* e2 )
+{
+ if (e1->ekind != e2->ekind)
+ return False;
+ if (!vg_eq_ExeContext(cheap_addr_cmp, e1->where, e2->where))
+ return False;
+
+ switch (e1->ekind) {
+ case UserErr:
+ case ParamErr:
+ if (e1->isWriteableLack != e2->isWriteableLack) return False;
+ if (e1->ekind == ParamErr
+ && 0 != VG_(strcmp)(e1->syscall_param, e2->syscall_param))
+ return False;
+ return True;
+ case FreeErr:
+ case FreeMismatchErr:
+ if (e1->addr != e2->addr) return False;
+ if (!eq_AddrInfo(cheap_addr_cmp, &e1->addrinfo, &e2->addrinfo))
+ return False;
+ return True;
+ case AddrErr:
+ if (e1->axskind != e2->axskind) return False;
+ if (e1->size != e2->size) return False;
+ if (!eq_AddrInfo(cheap_addr_cmp, &e1->addrinfo, &e2->addrinfo))
+ return False;
+ return True;
+ case ValueErr:
+ if (e1->size != e2->size) return False;
+ return True;
+ default:
+ VG_(panic)("eq_ErrContext");
+ }
+}
+
+static void pp_AddrInfo ( Addr a, AddrInfo* ai )
+{
+ switch (ai->akind) {
+ case Stack:
+ VG_(message)(Vg_UserMsg, " Address 0x%x is on the stack", a);
+ break;
+ case Unknown:
+ VG_(message)(Vg_UserMsg,
+ " Address 0x%x is not stack'd, malloc'd or free'd", a);
+ break;
+ case Freed: case Mallocd: case UserG: case UserS: {
+ UInt delta;
+ UChar* relative;
+ if (ai->rwoffset < 0) {
+ delta = (UInt)(- ai->rwoffset);
+ relative = "before";
+ } else if (ai->rwoffset >= ai->blksize) {
+ delta = ai->rwoffset - ai->blksize;
+ relative = "after";
+ } else {
+ delta = ai->rwoffset;
+ relative = "inside";
+ }
+ if (ai->akind == UserS) {
+ VG_(message)(Vg_UserMsg,
+ " Address 0x%x is %d bytes %s a %d-byte stack red-zone created",
+ a, delta, relative,
+ ai->blksize );
+ } else {
+ VG_(message)(Vg_UserMsg,
+ " Address 0x%x is %d bytes %s a block of size %d %s",
+ a, delta, relative,
+ ai->blksize,
+ ai->akind==Mallocd ? "alloc'd"
+ : ai->akind==Freed ? "free'd"
+ : "client-defined");
+ }
+ VG_(pp_ExeContext)(ai->lastchange);
+ break;
+ }
+ default:
+ VG_(panic)("pp_AddrInfo");
+ }
+}
+
+static void pp_ErrContext ( ErrContext* ec, Bool printCount )
+{
+ if (printCount)
+ VG_(message)(Vg_UserMsg, "Observed %d times:", ec->count );
+ switch (ec->ekind) {
+ case ValueErr:
+ if (ec->size == 0) {
+ VG_(message)(Vg_UserMsg,
+ "Use of uninitialised CPU condition code");
+ } else {
+ VG_(message)(Vg_UserMsg,
+ "Use of uninitialised value of size %d",
+ ec->size);
+ }
+ VG_(pp_ExeContext)(ec->where);
+ break;
+ case AddrErr:
+ switch (ec->axskind) {
+ case ReadAxs:
+ VG_(message)(Vg_UserMsg, "Invalid read of size %d",
+ ec->size );
+ break;
+ case WriteAxs:
+ VG_(message)(Vg_UserMsg, "Invalid write of size %d",
+ ec->size );
+ break;
+ case ExecAxs:
+ VG_(message)(Vg_UserMsg, "Jump to the invalid address "
+ "stated on the next line");
+ break;
+ default:
+ VG_(panic)("pp_ErrContext(axskind)");
+ }
+ VG_(pp_ExeContext)(ec->where);
+ pp_AddrInfo(ec->addr, &ec->addrinfo);
+ break;
+ case FreeErr:
+ VG_(message)(Vg_UserMsg,"Invalid free() / delete / delete[]");
+ /* fall through */
+ case FreeMismatchErr:
+ if (ec->ekind == FreeMismatchErr)
+ VG_(message)(Vg_UserMsg,
+ "Mismatched free() / delete / delete []");
+ VG_(pp_ExeContext)(ec->where);
+ pp_AddrInfo(ec->addr, &ec->addrinfo);
+ break;
+ case ParamErr:
+ if (ec->isWriteableLack) {
+ VG_(message)(Vg_UserMsg,
+ "Syscall param %s contains unaddressable byte(s)",
+ ec->syscall_param );
+ } else {
+ VG_(message)(Vg_UserMsg,
+ "Syscall param %s contains uninitialised or "
+ "unaddressable byte(s)",
+ ec->syscall_param);
+ }
+ VG_(pp_ExeContext)(ec->where);
+ pp_AddrInfo(ec->addr, &ec->addrinfo);
+ break;
+ case UserErr:
+ if (ec->isWriteableLack) {
+ VG_(message)(Vg_UserMsg,
+ "Unaddressable byte(s) found during client check request");
+ } else {
+ VG_(message)(Vg_UserMsg,
+ "Uninitialised or "
+ "unaddressable byte(s) found during client check request");
+ }
+ VG_(pp_ExeContext)(ec->where);
+ pp_AddrInfo(ec->addr, &ec->addrinfo);
+ break;
+ default:
+ VG_(panic)("pp_ErrContext");
+ }
+}
+
+
+/* Figure out if we want to attach for GDB for this error, possibly
+ by asking the user. */
+static
+Bool vg_is_GDB_attach_requested ( void )
+{
+ Char ch, ch2;
+ Int res;
+
+ if (VG_(clo_GDB_attach) == False)
+ return False;
+
+ VG_(message)(Vg_UserMsg, "");
+
+ again:
+ VG_(printf)(
+ "==%d== "
+ "---- Attach to GDB ? --- [Return/N/n/Y/y/C/c] ---- ",
+ VG_(getpid)()
+ );
+
+ res = VG_(read)(0 /*stdin*/, &ch, 1);
+ if (res != 1) goto ioerror;
+ /* res == 1 */
+ if (ch == '\n') return False;
+ if (ch != 'N' && ch != 'n' && ch != 'Y' && ch != 'y'
+ && ch != 'C' && ch != 'c') goto again;
+
+ res = VG_(read)(0 /*stdin*/, &ch2, 1);
+ if (res != 1) goto ioerror;
+ if (ch2 != '\n') goto again;
+
+ /* No, don't want to attach. */
+ if (ch == 'n' || ch == 'N') return False;
+ /* Yes, want to attach. */
+ if (ch == 'y' || ch == 'Y') return True;
+ /* No, don't want to attach, and don't ask again either. */
+ vg_assert(ch == 'c' || ch == 'C');
+
+ ioerror:
+ VG_(clo_GDB_attach) = False;
+ return False;
+}
+
+
+/* Top-level entry point to the error management subsystem. All
+ detected errors are notified here; this routine decides if/when the
+ user should see the error. */
+static void VG_(maybe_add_context) ( ErrContext* ec )
+{
+ ErrContext* p;
+ ErrContext* p_prev;
+ Bool cheap_addr_cmp = False;
+ static Bool is_first_shown_context = True;
+ static Bool stopping_message = False;
+ static Bool slowdown_message = False;
+ static Int vg_n_errs_shown = 0;
+
+ /* After M_VG_COLLECT_NO_ERRORS_AFTER different errors have been
+ found, just refuse to collect any more. */
+ if (vg_n_errs_shown >= M_VG_COLLECT_NO_ERRORS_AFTER) {
+ if (!stopping_message) {
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg,
+ "More than %d errors detected. I'm not reporting any more.",
+ M_VG_COLLECT_NO_ERRORS_AFTER);
+ VG_(message)(Vg_UserMsg,
+ "Final error counts may be inaccurate. Go fix your program!");
+ VG_(message)(Vg_UserMsg, "");
+ stopping_message = True;
+ }
+ return;
+ }
+
+ /* After M_VG_COLLECT_ERRORS_SLOWLY_AFTER different errors have
+ been found, be much more conservative about collecting new
+ ones. */
+ if (vg_n_errs_shown >= M_VG_COLLECT_ERRORS_SLOWLY_AFTER) {
+ cheap_addr_cmp = True;
+ if (!slowdown_message) {
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg,
+ "More than %d errors detected. Subsequent errors",
+ M_VG_COLLECT_ERRORS_SLOWLY_AFTER);
+ VG_(message)(Vg_UserMsg,
+ "will still be recorded, but in less detail than before.");
+ slowdown_message = True;
+ }
+ }
+
+
+ /* First, see if we've got an error record matching this one. */
+ p = vg_err_contexts;
+ p_prev = NULL;
+ while (p != NULL) {
+ if (eq_ErrContext(cheap_addr_cmp, p, ec)) {
+ /* Found it. */
+ p->count++;
+ if (p->supp != NULL) {
+ /* Deal correctly with suppressed errors. */
+ p->supp->count++;
+ vg_n_errs_suppressed++;
+ } else {
+ vg_n_errs_found++;
+ }
+
+ /* Move p to the front of the list so that future searches
+ for it are faster. */
+ if (p_prev != NULL) {
+ vg_assert(p_prev->next == p);
+ p_prev->next = p->next;
+ p->next = vg_err_contexts;
+ vg_err_contexts = p;
+ }
+ return;
+ }
+ p_prev = p;
+ p = p->next;
+ }
+
+ /* Didn't see it. Copy and add. */
+
+ /* OK, we're really going to collect it. */
+
+ p = VG_(malloc)(VG_AR_ERRCTXT, sizeof(ErrContext));
+ *p = *ec;
+ p->next = vg_err_contexts;
+ p->supp = is_suppressible_error(ec);
+ vg_err_contexts = p;
+ if (p->supp == NULL) {
+ vg_n_errs_found++;
+ if (!is_first_shown_context)
+ VG_(message)(Vg_UserMsg, "");
+ pp_ErrContext(p, False);
+ is_first_shown_context = False;
+ vg_n_errs_shown++;
+ /* Perhaps we want a GDB attach at this point? */
+ if (vg_is_GDB_attach_requested()) {
+ VG_(swizzle_esp_then_start_GDB)();
+ }
+ } else {
+ vg_n_errs_suppressed++;
+ p->supp->count++;
+ }
+}
+
+
+
+
+/*------------------------------------------------------------*/
+/*--- Exported fns ---*/
+/*------------------------------------------------------------*/
+
+void VG_(record_value_error) ( Int size )
+{
+ ErrContext ec;
+ clear_ErrContext( &ec );
+ ec.count = 1;
+ ec.next = NULL;
+ ec.where = VG_(get_ExeContext)( False );
+ ec.ekind = ValueErr;
+ ec.size = size;
+ VG_(maybe_add_context) ( &ec );
+}
+
+void VG_(record_address_error) ( Addr a, Int size, Bool isWrite )
+{
+ ErrContext ec;
+
+ /* If this is caused by an access immediately below %ESP, and the
+ user asks nicely, we just ignore it. */
+ if (VG_(clo_workaround_gcc296_bugs) && VG_(is_just_below_ESP)(a))
+ return;
+
+ clear_ErrContext( &ec );
+ ec.count = 1;
+ ec.next = NULL;
+ ec.where = VG_(get_ExeContext)( False );
+ ec.ekind = AddrErr;
+ ec.axskind = isWrite ? WriteAxs : ReadAxs;
+ ec.size = size;
+ ec.addr = a;
+ VG_(describe_addr) ( a, &ec.addrinfo );
+ VG_(maybe_add_context) ( &ec );
+}
+
+void VG_(record_jump_error) ( Addr a )
+{
+ ErrContext ec;
+ clear_ErrContext( &ec );
+ ec.count = 1;
+ ec.next = NULL;
+ ec.where = VG_(get_ExeContext)( False );
+ ec.ekind = AddrErr;
+ ec.axskind = ExecAxs;
+ ec.addr = a;
+ VG_(describe_addr) ( a, &ec.addrinfo );
+ VG_(maybe_add_context) ( &ec );
+}
+
+void VG_(record_free_error) ( Addr a )
+{
+ ErrContext ec;
+ clear_ErrContext( &ec );
+ ec.count = 1;
+ ec.next = NULL;
+ ec.where = VG_(get_ExeContext)( True );
+ ec.ekind = FreeErr;
+ ec.addr = a;
+ VG_(describe_addr) ( a, &ec.addrinfo );
+ VG_(maybe_add_context) ( &ec );
+}
+
+void VG_(record_freemismatch_error) ( Addr a )
+{
+ ErrContext ec;
+ clear_ErrContext( &ec );
+ ec.count = 1;
+ ec.next = NULL;
+ ec.where = VG_(get_ExeContext)( True );
+ ec.ekind = FreeMismatchErr;
+ ec.addr = a;
+ VG_(describe_addr) ( a, &ec.addrinfo );
+ VG_(maybe_add_context) ( &ec );
+}
+
+void VG_(record_param_err) ( Addr a, Bool isWriteLack, Char* msg )
+{
+ ErrContext ec;
+ clear_ErrContext( &ec );
+ ec.count = 1;
+ ec.next = NULL;
+ ec.where = VG_(get_ExeContext)( False );
+ ec.ekind = ParamErr;
+ ec.addr = a;
+ VG_(describe_addr) ( a, &ec.addrinfo );
+ ec.syscall_param = msg;
+ ec.isWriteableLack = isWriteLack;
+ VG_(maybe_add_context) ( &ec );
+}
+
+
+void VG_(record_user_err) ( Addr a, Bool isWriteLack )
+{
+ ErrContext ec;
+ clear_ErrContext( &ec );
+ ec.count = 1;
+ ec.next = NULL;
+ ec.where = VG_(get_ExeContext)( False );
+ ec.ekind = UserErr;
+ ec.addr = a;
+ VG_(describe_addr) ( a, &ec.addrinfo );
+ ec.isWriteableLack = isWriteLack;
+ VG_(maybe_add_context) ( &ec );
+}
+
+
+void VG_(show_all_errors) ( void )
+{
+ Int i, n_min;
+ Int n_err_contexts, n_supp_contexts;
+ ErrContext *p, *p_min;
+ Suppression *su;
+ Bool any_supp;
+
+ if (VG_(clo_verbosity) == 0)
+ return;
+
+ n_err_contexts = 0;
+ for (p = vg_err_contexts; p != NULL; p = p->next) {
+ if (p->supp == NULL)
+ n_err_contexts++;
+ }
+
+ n_supp_contexts = 0;
+ for (su = vg_suppressions; su != NULL; su = su->next) {
+ if (su->count > 0)
+ n_supp_contexts++;
+ }
+
+ VG_(message)(Vg_UserMsg,
+ "ERROR SUMMARY: "
+ "%d errors from %d contexts (suppressed: %d from %d)",
+ vg_n_errs_found, n_err_contexts,
+ vg_n_errs_suppressed, n_supp_contexts );
+
+ if (VG_(clo_verbosity) <= 1)
+ return;
+
+ /* Print the contexts in order of increasing error count. */
+ for (i = 0; i < n_err_contexts; i++) {
+ n_min = (1 << 30) - 1;
+ p_min = NULL;
+ for (p = vg_err_contexts; p != NULL; p = p->next) {
+ if (p->supp != NULL) continue;
+ if (p->count < n_min) {
+ n_min = p->count;
+ p_min = p;
+ }
+ }
+ if (p_min == NULL) VG_(panic)("pp_AllErrContexts");
+
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg, "%d errors in context %d of %d:",
+ p_min->count,
+ i+1, n_err_contexts);
+ pp_ErrContext( p_min, False );
+
+ if ((i+1 == VG_(clo_dump_error))) {
+ VG_(translate) ( p_min->where->eips[0], NULL, NULL, NULL );
+ }
+
+ p_min->count = 1 << 30;
+ }
+
+ if (n_supp_contexts > 0)
+ VG_(message)(Vg_DebugMsg, "");
+ any_supp = False;
+ for (su = vg_suppressions; su != NULL; su = su->next) {
+ if (su->count > 0) {
+ any_supp = True;
+ VG_(message)(Vg_DebugMsg, "supp: %4d %s", su->count,
+ su->sname);
+ }
+ }
+
+ if (n_err_contexts > 0) {
+ if (any_supp)
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg,
+ "IN SUMMARY: "
+ "%d errors from %d contexts (suppressed: %d from %d)",
+ vg_n_errs_found, n_err_contexts,
+ vg_n_errs_suppressed,
+ n_supp_contexts );
+ VG_(message)(Vg_UserMsg, "");
+ }
+}
+
+/*------------------------------------------------------------*/
+/*--- Standard suppressions ---*/
+/*------------------------------------------------------------*/
+
+/* Get a non-blank, non-comment line of at most nBuf chars from fd.
+ Skips leading spaces on the line. Return True if EOF was hit instead.
+*/
+
+#define VG_ISSPACE(ch) (((ch)==' ') || ((ch)=='\n') || ((ch)=='\t'))
+
+static Bool getLine ( Int fd, Char* buf, Int nBuf )
+{
+ Char ch;
+ Int n, i;
+ while (True) {
+ /* First, read until a non-blank char appears. */
+ while (True) {
+ n = VG_(read)(fd, &ch, 1);
+ if (n == 1 && !VG_ISSPACE(ch)) break;
+ if (n == 0) return True;
+ }
+
+ /* Now, read the line into buf. */
+ i = 0;
+ buf[i++] = ch; buf[i] = 0;
+ while (True) {
+ n = VG_(read)(fd, &ch, 1);
+ if (n == 0) return False; /* the next call will return True */
+ if (ch == '\n') break;
+ if (i > 0 && i == nBuf-1) i--;
+ buf[i++] = ch; buf[i] = 0;
+ }
+ while (i > 1 && VG_ISSPACE(buf[i-1])) {
+ i--; buf[i] = 0;
+ };
+
+ /* VG_(printf)("The line is `%s'\n", buf); */
+ /* Ok, we have a line. If a non-comment line, return.
+ If a comment line, start all over again. */
+ if (buf[0] != '#') return False;
+ }
+}
+
+
+/* *p_caller contains the raw name of a caller, supposedly either
+ fun:some_function_name or
+ obj:some_object_name.
+ Set *p_ty accordingly and advance *p_caller over the descriptor
+ (fun: or obj:) part.
+ Returns False if failed.
+*/
+static Bool setLocationTy ( Char** p_caller, SuppressionLocTy* p_ty )
+{
+ if (VG_(strncmp)(*p_caller, "fun:", 4) == 0) {
+ (*p_caller) += 4;
+ *p_ty = FunName;
+ return True;
+ }
+ if (VG_(strncmp)(*p_caller, "obj:", 4) == 0) {
+ (*p_caller) += 4;
+ *p_ty = ObjName;
+ return True;
+ }
+ VG_(printf)("location should start with fun: or obj:\n");
+ return False;
+}
+
+
+/* Read suppressions from the file specified in vg_clo_suppressions
+ and place them in the suppressions list. If there's any difficulty
+ doing this, just give up -- there's no point in trying to recover.
+*/
+#define STREQ(s1,s2) (s1 != NULL && s2 != NULL \
+ && VG_(strcmp)((s1),(s2))==0)
+
+static Char* copyStr ( Char* str )
+{
+ Int n, i;
+ Char* str2;
+ n = VG_(strlen)(str);
+ str2 = VG_(malloc)(VG_AR_PRIVATE, n+1);
+ vg_assert(n > 0);
+ for (i = 0; i < n+1; i++) str2[i] = str[i];
+ return str2;
+}
+
+static void load_one_suppressions_file ( Char* filename )
+{
+# define N_BUF 200
+ Int fd;
+ Bool eof;
+ Char buf[N_BUF+1];
+ fd = VG_(open_read)( filename );
+ if (fd == -1) {
+ VG_(message)(Vg_UserMsg,
+ "FATAL: can't open suppressions file `%s'",
+ filename );
+ VG_(exit)(1);
+ }
+
+ while (True) {
+ Suppression* supp;
+ supp = VG_(malloc)(VG_AR_PRIVATE, sizeof(Suppression));
+ supp->count = 0;
+ supp->param = supp->caller0 = supp->caller1
+ = supp->caller2 = supp->caller3 = NULL;
+
+ eof = getLine ( fd, buf, N_BUF );
+ if (eof) break;
+
+ if (!STREQ(buf, "{")) goto syntax_error;
+
+ eof = getLine ( fd, buf, N_BUF );
+ if (eof || STREQ(buf, "}")) goto syntax_error;
+ supp->sname = copyStr(buf);
+
+ eof = getLine ( fd, buf, N_BUF );
+ if (eof) goto syntax_error;
+ else if (STREQ(buf, "Param")) supp->skind = Param;
+ else if (STREQ(buf, "Value0")) supp->skind = Value0;
+ else if (STREQ(buf, "Value1")) supp->skind = Value1;
+ else if (STREQ(buf, "Value2")) supp->skind = Value2;
+ else if (STREQ(buf, "Value4")) supp->skind = Value4;
+ else if (STREQ(buf, "Value8")) supp->skind = Value8;
+ else if (STREQ(buf, "Addr1")) supp->skind = Addr1;
+ else if (STREQ(buf, "Addr2")) supp->skind = Addr2;
+ else if (STREQ(buf, "Addr4")) supp->skind = Addr4;
+ else if (STREQ(buf, "Addr8")) supp->skind = Addr8;
+ else if (STREQ(buf, "Free")) supp->skind = FreeS;
+ else goto syntax_error;
+
+ if (supp->skind == Param) {
+ eof = getLine ( fd, buf, N_BUF );
+ if (eof) goto syntax_error;
+ supp->param = copyStr(buf);
+ }
+
+ eof = getLine ( fd, buf, N_BUF );
+ if (eof) goto syntax_error;
+ supp->caller0 = copyStr(buf);
+ if (!setLocationTy(&(supp->caller0), &(supp->caller0_ty)))
+ goto syntax_error;
+
+ eof = getLine ( fd, buf, N_BUF );
+ if (eof) goto syntax_error;
+ supp->caller1 = copyStr(buf);
+ if (!setLocationTy(&(supp->caller1), &(supp->caller1_ty)))
+ goto syntax_error;
+
+ eof = getLine ( fd, buf, N_BUF );
+ if (eof) goto syntax_error;
+ if (!STREQ(buf, "}")) {
+ supp->caller2 = copyStr(buf);
+ if (!setLocationTy(&(supp->caller2), &(supp->caller2_ty)))
+ goto syntax_error;
+ eof = getLine ( fd, buf, N_BUF );
+ if (eof) goto syntax_error;
+ if (!STREQ(buf, "}")) {
+ supp->caller3 = copyStr(buf);
+ if (!setLocationTy(&(supp->caller3), &(supp->caller3_ty)))
+ goto syntax_error;
+ eof = getLine ( fd, buf, N_BUF );
+ if (eof || !STREQ(buf, "}")) goto syntax_error;
+ }
+ }
+
+ supp->next = vg_suppressions;
+ vg_suppressions = supp;
+ }
+
+ VG_(close)(fd);
+ return;
+
+ syntax_error:
+ if (eof) {
+ VG_(message)(Vg_UserMsg,
+ "FATAL: in suppressions file `%s': unexpected EOF",
+ filename );
+ } else {
+ VG_(message)(Vg_UserMsg,
+ "FATAL: in suppressions file `%s': syntax error on: %s",
+ filename, buf );
+ }
+ VG_(close)(fd);
+ VG_(message)(Vg_UserMsg, "exiting now.");
+ VG_(exit)(1);
+
+# undef N_BUF
+}
+
+
+void VG_(load_suppressions) ( void )
+{
+ Int i;
+ vg_suppressions = NULL;
+ for (i = 0; i < VG_(clo_n_suppressions); i++) {
+ if (VG_(clo_verbosity) > 1) {
+ VG_(message)(Vg_UserMsg, "Reading suppressions file: %s",
+ VG_(clo_suppressions)[i] );
+ }
+ load_one_suppressions_file( VG_(clo_suppressions)[i] );
+ }
+}
+
+
+/* Does an error context match a suppression? ie is this a
+ suppressible error? If so, return a pointer to the Suppression
+ record, otherwise NULL.
+ Tries to minimise the number of calls to what_fn_is_this since they
+ are expensive.
+*/
+static Suppression* is_suppressible_error ( ErrContext* ec )
+{
+# define STREQ(s1,s2) (s1 != NULL && s2 != NULL \
+ && VG_(strcmp)((s1),(s2))==0)
+
+ Char caller0_obj[M_VG_ERRTXT];
+ Char caller0_fun[M_VG_ERRTXT];
+ Char caller1_obj[M_VG_ERRTXT];
+ Char caller1_fun[M_VG_ERRTXT];
+ Char caller2_obj[M_VG_ERRTXT];
+ Char caller2_fun[M_VG_ERRTXT];
+ Char caller3_obj[M_VG_ERRTXT];
+ Char caller3_fun[M_VG_ERRTXT];
+
+ Suppression* su;
+ Int su_size;
+
+ /* vg_what_fn_or_object_is_this returns:
+ <function_name> or
+ <object_name> or
+ ???
+ so the strings in the suppression file should match these.
+ */
+
+ /* Initialise these strs so they are always safe to compare, even
+ if what_fn_or_object_is_this doesn't write anything to them. */
+ caller0_obj[0] = caller1_obj[0] = caller2_obj[0] = caller3_obj[0] = 0;
+ caller0_fun[0] = caller1_fun[0] = caller2_obj[0] = caller3_obj[0] = 0;
+
+ VG_(what_obj_and_fun_is_this)
+ ( ec->where->eips[0], caller0_obj, M_VG_ERRTXT,
+ caller0_fun, M_VG_ERRTXT );
+ VG_(what_obj_and_fun_is_this)
+ ( ec->where->eips[1], caller1_obj, M_VG_ERRTXT,
+ caller1_fun, M_VG_ERRTXT );
+
+ if (VG_(clo_backtrace_size) > 2) {
+ VG_(what_obj_and_fun_is_this)
+ ( ec->where->eips[2], caller2_obj, M_VG_ERRTXT,
+ caller2_fun, M_VG_ERRTXT );
+
+ if (VG_(clo_backtrace_size) > 3) {
+ VG_(what_obj_and_fun_is_this)
+ ( ec->where->eips[3], caller3_obj, M_VG_ERRTXT,
+ caller3_fun, M_VG_ERRTXT );
+ }
+ }
+
+ /* See if the error context matches any suppression. */
+ for (su = vg_suppressions; su != NULL; su = su->next) {
+ switch (su->skind) {
+ case FreeS:
+ case Param: case Value0: su_size = 0; break;
+ case Value1: case Addr1: su_size = 1; break;
+ case Value2: case Addr2: su_size = 2; break;
+ case Value4: case Addr4: su_size = 4; break;
+ case Value8: case Addr8: su_size = 8; break;
+ default: VG_(panic)("errcontext_matches_suppression");
+ }
+ switch (su->skind) {
+ case Param:
+ if (ec->ekind != ParamErr) continue;
+ if (!STREQ(su->param, ec->syscall_param)) continue;
+ break;
+ case Value0: case Value1: case Value2: case Value4: case Value8:
+ if (ec->ekind != ValueErr) continue;
+ if (ec->size != su_size) continue;
+ break;
+ case Addr1: case Addr2: case Addr4: case Addr8:
+ if (ec->ekind != AddrErr) continue;
+ if (ec->size != su_size) continue;
+ break;
+ case FreeS:
+ if (ec->ekind != FreeErr && ec->ekind != FreeMismatchErr) continue;
+ break;
+ }
+
+ switch (su->caller0_ty) {
+ case ObjName: if (!VG_(stringMatch)(su->caller0,
+ caller0_obj)) continue;
+ break;
+ case FunName: if (!VG_(stringMatch)(su->caller0,
+ caller0_fun)) continue;
+ break;
+ default: goto baaaad;
+ }
+
+ switch (su->caller1_ty) {
+ case ObjName: if (!VG_(stringMatch)(su->caller1,
+ caller1_obj)) continue;
+ break;
+ case FunName: if (!VG_(stringMatch)(su->caller1,
+ caller1_fun)) continue;
+ break;
+ default: goto baaaad;
+ }
+
+ if (VG_(clo_backtrace_size) > 2 && su->caller2 != NULL) {
+ switch (su->caller2_ty) {
+ case ObjName: if (!VG_(stringMatch)(su->caller2,
+ caller2_obj)) continue;
+ break;
+ case FunName: if (!VG_(stringMatch)(su->caller2,
+ caller2_fun)) continue;
+ break;
+ default: goto baaaad;
+ }
+ }
+
+ if (VG_(clo_backtrace_size) > 3 && su->caller3 != NULL) {
+ switch (su->caller3_ty) {
+ case ObjName: if (!VG_(stringMatch)(su->caller3,
+ caller3_obj)) continue;
+ break;
+ case FunName: if (!VG_(stringMatch)(su->caller3,
+ caller3_fun)) continue;
+ break;
+ default: goto baaaad;
+ }
+ }
+
+ return su;
+ }
+
+ return NULL;
+
+ baaaad:
+ VG_(panic)("is_suppressible_error");
+
+# undef STREQ
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end vg_errcontext.c ---*/
+/*--------------------------------------------------------------------*/