Added two client requests: VALGRIND_COUNT_ERRORS and VALGRIND_COUNT_LEAKS.
The first returns the number of errors found so far, and is a core request.
The second returns the number of bytes found
reachable/dubious/leaked/suppressed by all leak checks so far, for Memcheck and
Addrcheck.

Both are useful for using Valgrind in regression test suites where multiple
tests are present in a single file -- one can run Valgrind with no output
(using --logfile-fd=-1) and use the requests after each test to determine if
any errors happened.

Had to rename and make public vg_n_errs_found --> VG_(n_errs_found) to do so.
Nb: leak errors are not counted as errors for the purposes of
VALGRIND_COUNT_ERRORS.  This was decided as the best thing to do after
discussion with Olly Betts, who original suggested these changes.

Pulled out common client request code shared between Memcheck and Addrcheck.

Added a regression test for this.

Added some documentation too.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1533 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/addrcheck/ac_main.c b/addrcheck/ac_main.c
index dfa1e8f..2df76da 100644
--- a/addrcheck/ac_main.c
+++ b/addrcheck/ac_main.c
@@ -1082,9 +1082,10 @@
    return True;
 }
       
-/* 
-   Client requests
- */
+/*------------------------------------------------------------*/
+/*--- Client requests                                      ---*/
+/*------------------------------------------------------------*/
+
 Bool SK_(handle_client_request) ( ThreadState* tst, UInt* arg_block, UInt *ret )
 {
 #define IGNORE(what)                                                    \
@@ -1131,10 +1132,14 @@
          return False;
 
       default:
-         VG_(message)(Vg_UserMsg, 
-                      "Warning: unknown addrcheck client request code %d",
-                      arg[0]);
-         return False;
+         if (MAC_(handle_common_client_requests)(tst, arg_block, ret )) {
+            return True;
+         } else {
+            VG_(message)(Vg_UserMsg, 
+                         "Warning: unknown addrcheck client request code %d",
+                         arg[0]);
+            return False;
+         }
    }
    return True;
 
diff --git a/coregrind/docs/coregrind_core.html b/coregrind/docs/coregrind_core.html
index 14a235e..2ce6162 100644
--- a/coregrind/docs/coregrind_core.html
+++ b/coregrind/docs/coregrind_core.html
@@ -871,6 +871,17 @@
     chunks of old code all at once.
     <p>
     Warning: minimally tested, especially for the cache simulator.
+<li><code>VALGRIND_COUNT_ERRORS</code>: returns the number of errors
+    found so far by Valgrind.  Can be useful in test harness code when
+    combined with the <code>--logfile-fd=-1</code> option;  this runs
+    Valgrind silently, but the client program can detect when errors
+    occur.
+<p>
+<li><code>VALGRIND_COUNT_LEAKS</code>: fills in the four arguments with
+    the number of bytes of memory found by all previous leak checks that
+    were leaked, dubious, reachable and suppressed.  Again, useful for
+    test harness code.
+<p>
 </ul>
 <p>
 
diff --git a/coregrind/vg_errcontext.c b/coregrind/vg_errcontext.c
index b04be54..f36426e 100644
--- a/coregrind/vg_errcontext.c
+++ b/coregrind/vg_errcontext.c
@@ -46,7 +46,7 @@
 Supp* VG_(get_suppressions) ( void ) { return vg_suppressions; }
 
 /* Running count of unsuppressed errors detected. */
-static UInt vg_n_errs_found = 0;
+UInt VG_(n_errs_found) = 0;
 
 /* Running count of suppressed errors detected. */
 static UInt vg_n_errs_suppressed = 0;
@@ -292,7 +292,7 @@
       pointless to continue the Valgrind run after this point. */
    if (VG_(clo_error_limit) 
        && (vg_n_errs_shown >= M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN
-           || vg_n_errs_found >= M_VG_COLLECT_NO_ERRORS_AFTER_FOUND)) {
+           || VG_(n_errs_found) >= M_VG_COLLECT_NO_ERRORS_AFTER_FOUND)) {
       if (!stopping_message) {
          VG_(message)(Vg_UserMsg, "");
 
@@ -354,7 +354,7 @@
             p->supp->count++;
             vg_n_errs_suppressed++;	 
          } else {
-            vg_n_errs_found++;
+            VG_(n_errs_found)++;
          }
 
          /* Move p to the front of the list so that future searches
@@ -405,7 +405,7 @@
    p->supp = is_suppressible_error(&err);
    vg_errors = p;
    if (p->supp == NULL) {
-      vg_n_errs_found++;
+      VG_(n_errs_found)++;
       if (!is_first_shown_context)
          VG_(message)(Vg_UserMsg, "");
       pp_Error(p, False);
@@ -422,10 +422,12 @@
    errors that the skin want to report immediately, eg. because they're
    guaranteed to only happen once.  This avoids all the recording and
    comparing stuff.  But they can be suppressed;  returns True if it is
-   suppressed.  Bool `print_error' dictates whether to print the error. */
+   suppressed.  Bool `print_error' dictates whether to print the error. 
+   Bool `count_error' dictates whether to count the error in VG_(n_errs_found)
+*/
 Bool VG_(unique_error) ( ThreadState* tst, ErrorKind ekind, Addr a, Char* s,
                          void* extra, ExeContext* where, Bool print_error,
-                         Bool allow_GDB_attach )
+                         Bool allow_GDB_attach, Bool count_error )
 {
    Error  err;
    Addr   m_eip, m_esp, m_ebp;
@@ -443,7 +445,8 @@
    (void)SK_(update_extra)(&err);
 
    if (NULL == is_suppressible_error(&err)) {
-      vg_n_errs_found++;
+      if (count_error)
+         VG_(n_errs_found)++;
 
       if (print_error) {
          if (!is_first_shown_context)
@@ -502,7 +505,7 @@
    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_found), n_err_contexts, 
                 vg_n_errs_suppressed, n_supp_contexts );
 
    if (VG_(clo_verbosity) <= 1)
@@ -551,7 +554,7 @@
       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_found), n_err_contexts, 
                    vg_n_errs_suppressed,
                    n_supp_contexts );
       VG_(message)(Vg_UserMsg, "");
diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h
index feea316..4d3d33e 100644
--- a/coregrind/vg_include.h
+++ b/coregrind/vg_include.h
@@ -1170,6 +1170,8 @@
 
 extern void VG_(gen_suppression) ( Error* err );
 
+extern UInt VG_(n_errs_found);
+
 /* ---------------------------------------------------------------------
    Exports of vg_procselfmaps.c
    ------------------------------------------------------------------ */
diff --git a/coregrind/vg_scheduler.c b/coregrind/vg_scheduler.c
index 5d4c406..009b983 100644
--- a/coregrind/vg_scheduler.c
+++ b/coregrind/vg_scheduler.c
@@ -34,7 +34,7 @@
    directly. */
 #define __VALGRIND_SOMESKIN_H
 #include "valgrind.h" /* for VG_USERREQ__RUNNING_ON_VALGRIND and
-                             VG_USERREQ__DISCARD_TRANSLATIONS */
+                             VG_USERREQ__DISCARD_TRANSLATIONS, and others */
 
 /* BORKAGE/ISSUES as of 29 May 02
 
@@ -3558,6 +3558,10 @@
          SET_EDX( tid, 0 );     /* return value is meaningless */
 	 break;
 
+      case VG_USERREQ__COUNT_ERRORS:  
+         SET_EDX( tid, VG_(n_errs_found) );
+         break;
+
       default:
          if (VG_(needs).client_requests) {
 	    UInt ret;
diff --git a/include/valgrind.h b/include/valgrind.h
index e8359a2..b6768f7 100644
--- a/include/valgrind.h
+++ b/include/valgrind.h
@@ -166,6 +166,10 @@
           VG_USERREQ__CLIENT_tstCALL2,
           VG_USERREQ__CLIENT_tstCALL3,
 
+          /* Can be useful in regression testing suites -- eg. can send
+             Valgrind's output to /dev/null and still count errors. */
+          VG_USERREQ__COUNT_ERRORS = 0x1300,
+
           VG_USERREQ__FINAL_DUMMY_CLIENT_REQUEST
    } Vg_ClientRequest;
 
@@ -271,4 +275,15 @@
    })
 
 
+/* Counts the number of errors that have been recorded by a skin.  Nb:
+   the skin must record the errors with VG_(maybe_record_error)() or
+   VG_(unique_error)() for them to be counted. */
+#define VALGRIND_COUNT_ERRORS                                           \
+   ({unsigned int _qyy_res;                                             \
+    VALGRIND_MAGIC_SEQUENCE(_qyy_res, 0 /* default return */,           \
+                            VG_USERREQ__COUNT_ERRORS,                   \
+                            0, 0, 0, 0);                                \
+    _qyy_res;                                                           \
+   })
+
 #endif   /* __VALGRIND_H */
diff --git a/include/vg_skin.h b/include/vg_skin.h
index a372e40..3bb6e36 100644
--- a/include/vg_skin.h
+++ b/include/vg_skin.h
@@ -1202,11 +1202,12 @@
    suppressed, though.  Return value is True if it was suppressed.
    `print_error' dictates whether to print the error, which is a bit of a 
    hack that's useful sometimes if you just want to know if the error would
-   be suppressed without possibly printing it. */
+   be suppressed without possibly printing it.  `count_error' dictates 
+   whether to add the error in the error total count (another mild hack). */
 extern Bool VG_(unique_error) ( ThreadState* tst, ErrorKind ekind,
                                 Addr a, Char* s, void* extra,
                                 ExeContext* where, Bool print_error,
-                                Bool allow_GDB_attach );
+                                Bool allow_GDB_attach, Bool count_error );
 
 /* Gets a non-blank, non-comment line of at most nBuf chars from fd.
    Skips leading spaces on the line.  Returns True if EOF was hit instead. 
diff --git a/memcheck/mac_leakcheck.c b/memcheck/mac_leakcheck.c
index 3699de7..42e9205 100644
--- a/memcheck/mac_leakcheck.c
+++ b/memcheck/mac_leakcheck.c
@@ -356,6 +356,11 @@
    VG_(pp_ExeContext)(l->allocated_at);
 }
 
+Int MAC_(total_bytes_leaked)     = 0;
+Int MAC_(total_bytes_dubious)    = 0;
+Int MAC_(total_bytes_reachable)  = 0;
+Int MAC_(total_bytes_suppressed) = 0;
+
 /* Top level entry point to leak detector.  Call here, passing in
    suitable address-validating functions (see comment at top of
    vg_scan_all_valid_memory above).  All this is to avoid duplication
@@ -480,7 +485,7 @@
          VG_(unique_error) ( /*tst*/NULL, LeakErr, (UInt)i+1,
                              (Char*)n_lossrecords, (void*) p_min,
                              p_min->allocated_at, print_record,
-                             /*allow_GDB_attach*/False );
+                             /*allow_GDB_attach*/False, /*count_error*/False );
 
       if (is_suppressed) {
          blocks_suppressed += p_min->num_blocks;
@@ -522,6 +527,11 @@
    }
    VG_(message)(Vg_UserMsg, "");
 
+   MAC_(total_bytes_leaked)     += bytes_leaked;
+   MAC_(total_bytes_dubious)    += bytes_dubious;
+   MAC_(total_bytes_reachable)  += bytes_reachable;
+   MAC_(total_bytes_suppressed) += bytes_suppressed;
+
    VG_(free) ( lc_shadows );
    VG_(free) ( lc_reachedness );
 }
diff --git a/memcheck/mac_needs.c b/memcheck/mac_needs.c
index 1692a65..bd0f5c6 100644
--- a/memcheck/mac_needs.c
+++ b/memcheck/mac_needs.c
@@ -33,6 +33,8 @@
 
 #include "mac_shared.h"
 
+#include "memcheck.h"   /* for VG_USERREQ__* */
+
 /*------------------------------------------------------------*/
 /*--- Defns                                                ---*/
 /*------------------------------------------------------------*/
@@ -758,6 +760,29 @@
 }
 
 /*------------------------------------------------------------*/
+/*--- Common client request handling                       ---*/
+/*------------------------------------------------------------*/
+
+Bool MAC_(handle_common_client_requests)(ThreadState* tst, UInt* arg,
+                                         UInt* ret )
+{
+   UInt** argp = (UInt**)arg;
+   
+   switch (arg[0]) {
+   case VG_USERREQ__COUNT_LEAKS: /* count leaked bytes */
+      *argp[1] = MAC_(total_bytes_leaked);
+      *argp[2] = MAC_(total_bytes_dubious);
+      *argp[3] = MAC_(total_bytes_reachable);
+      *argp[4] = MAC_(total_bytes_suppressed);
+      *ret = 0;
+      return True;
+
+   default:
+      return False;
+   }
+}
+
+/*------------------------------------------------------------*/
 /*--- Syscall wrappers                                     ---*/
 /*------------------------------------------------------------*/
 
diff --git a/memcheck/mac_shared.h b/memcheck/mac_shared.h
index babe916..4683a31 100644
--- a/memcheck/mac_shared.h
+++ b/memcheck/mac_shared.h
@@ -266,6 +266,11 @@
 /* Used in describe_addr() */
 extern Bool (*MAC_(describe_addr_supp))    ( Addr a, AddrInfo* ai );
 
+/* For VALGRIND_COUNT_LEAKS client request */
+extern Int MAC_(total_bytes_leaked);
+extern Int MAC_(total_bytes_dubious);
+extern Int MAC_(total_bytes_reachable);
+extern Int MAC_(total_bytes_suppressed);
 
 /*------------------------------------------------------------*/
 /*--- Functions                                            ---*/
@@ -293,6 +298,9 @@
 extern void MAC_(common_pre_clo_init) ( void );
 extern void MAC_(common_fini)         ( void (*leak_check)(void) );
 
+extern Bool MAC_(handle_common_client_requests) 
+                  ( ThreadState* tst, UInt* arg_block, UInt* ret );
+
 extern void MAC_(print_malloc_stats) ( void );
 
 /* For leak checking */
diff --git a/memcheck/mc_clientreqs.c b/memcheck/mc_clientreqs.c
index f29261d..1ba3abf 100644
--- a/memcheck/mc_clientreqs.c
+++ b/memcheck/mc_clientreqs.c
@@ -144,12 +144,11 @@
    return False;
 }
 
-Bool SK_(handle_client_request) ( ThreadState* tst, UInt* arg_block, UInt *ret )
+Bool SK_(handle_client_request) ( ThreadState* tst, UInt* arg, UInt* ret )
 {
    Int   i;
    Bool  ok;
    Addr  bad_addr;
-   UInt* arg = arg_block;
 
    if (!VG_IS_SKIN_USERREQ('M','C',arg[0]))
       return False;
@@ -216,10 +215,14 @@
 	 break;
 
       default:
-         VG_(message)(Vg_UserMsg, 
-                      "Warning: unknown memcheck client request code %d",
-                      arg[0]);
-         return False;
+         if (MAC_(handle_common_client_requests)(tst, arg, ret )) {
+            return True;
+         } else {
+            VG_(message)(Vg_UserMsg, 
+                         "Warning: unknown memcheck client request code %d",
+                         arg[0]);
+            return False;
+         }
    }
    return True;
 }
diff --git a/memcheck/memcheck.h b/memcheck/memcheck.h
index f19a59e..f598beb 100644
--- a/memcheck/memcheck.h
+++ b/memcheck/memcheck.h
@@ -79,7 +79,8 @@
       VG_USERREQ__DISCARD,
       VG_USERREQ__CHECK_WRITABLE,
       VG_USERREQ__CHECK_READABLE,
-      VG_USERREQ__DO_LEAK_CHECK /* untested */
+      VG_USERREQ__DO_LEAK_CHECK,
+      VG_USERREQ__COUNT_LEAKS
    } Vg_MemCheckClientRequest;
 
 
@@ -169,9 +170,7 @@
       (volatile unsigned char *)&(__lvalue),                       \
                       (unsigned int)(sizeof (__lvalue)))
 
-/* Do a memory leak check mid-execution.
-   Currently implemented but untested.
-*/
+/* Do a memory leak check mid-execution.  */
 #define VALGRIND_DO_LEAK_CHECK                                     \
    {unsigned int _qzz_res;                                         \
     VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                           \
@@ -179,5 +178,13 @@
                             0, 0, 0, 0);                           \
    }
 
+/* Return number of leaked, dubious, reachable and suppressed bytes found by
+   all previous leak checks.  They must be lvalues. */
+#define VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed)    \
+   {unsigned int _qzz_res;                                              \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                                \
+                            VG_USERREQ__COUNT_LEAKS,                    \
+                            &leaked, &dubious, &reachable, &suppressed);\
+   }
 
 #endif
diff --git a/memcheck/tests/.cvsignore b/memcheck/tests/.cvsignore
index f95c80b..0a48efe 100644
--- a/memcheck/tests/.cvsignore
+++ b/memcheck/tests/.cvsignore
@@ -9,6 +9,7 @@
 clientstackperm
 dir
 doublefree
+error_counts
 errs1
 exitprog
 filter_leak_check_size
diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am
index d2d507f..81fe358 100644
--- a/memcheck/tests/Makefile.am
+++ b/memcheck/tests/Makefile.am
@@ -23,6 +23,7 @@
 	clientperm.stderr.exp \
 	clientperm.stdout.exp clientperm.vgtest \
 	doublefree.stderr.exp doublefree.vgtest \
+	error_counts.stderr.exp error_counts.stdout.exp error_count.vgtest \
 	errs1.stderr.exp errs1.vgtest \
 	exitprog.stderr.exp exitprog.vgtest \
 	fprw.stderr.exp fprw.vgtest \
@@ -59,7 +60,7 @@
 
 check_PROGRAMS = \
 	badaddrvalue badfree badjump badloop buflen_check clientperm \
-	doublefree errs1 exitprog fprw fwrite inits inline \
+	doublefree error_counts errs1 exitprog fprw fwrite inits inline \
 	malloc1 malloc2 malloc3 manuel1 manuel2 manuel3 \
 	memalign_test memcmptest mmaptest nanoleak overlap pushfpopf \
 	realloc1 realloc2 sigaltstack signal2 supp1 supp2 suppfree \
@@ -78,6 +79,7 @@
 buflen_check_SOURCES	= buflen_check.c
 clientperm_SOURCES 	= clientperm.c
 doublefree_SOURCES 	= doublefree.c
+error_counts_SOURCES 	= error_counts.c
 errs1_SOURCES 		= errs1.c
 exitprog_SOURCES 	= exitprog.c
 fprw_SOURCES 		= fprw.c
diff --git a/memcheck/tests/error_counts.c b/memcheck/tests/error_counts.c
new file mode 100644
index 0000000..1508121
--- /dev/null
+++ b/memcheck/tests/error_counts.c
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "../memcheck.h"
+
+int main(void)
+{
+   int  x;
+   int  y = 0;
+   int* reachable;
+   int* dubious;
+   int* leaked;
+   int  n_reachable  = 0;
+   int  n_dubious    = 0;
+   int  n_leaked     = 0;
+   int  n_suppressed = 0;
+   int  errs;
+
+   /* Error counting */
+   printf("errors: %d\n", VALGRIND_COUNT_ERRORS);
+
+   if (x == 0) {
+      y++;
+   } else {
+      y--;
+   }
+
+   printf("errors: %d\n", VALGRIND_COUNT_ERRORS);
+
+   /* Leak checking */
+   VALGRIND_DO_LEAK_CHECK;
+   VALGRIND_COUNT_LEAKS(n_leaked, n_dubious, n_reachable, n_suppressed);
+   printf("leaks: %dB, %dB, %dB, %dB\n",
+          n_leaked, n_dubious, n_reachable, n_suppressed);
+
+   leaked = malloc(77);
+   leaked = 0;
+
+   dubious = malloc(88);
+   dubious += 10;
+
+   reachable = malloc(99);
+
+   VALGRIND_DO_LEAK_CHECK;
+   VALGRIND_COUNT_LEAKS(n_leaked, n_dubious, n_reachable, n_suppressed);
+   printf("leaks: %dB, %dB, %dB, %dB\n",
+          n_leaked, n_dubious, n_reachable, n_suppressed);
+
+   printf("errors: %d\n", VALGRIND_COUNT_ERRORS);
+
+   return 0;
+}
diff --git a/memcheck/tests/error_counts.stderr.exp b/memcheck/tests/error_counts.stderr.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/memcheck/tests/error_counts.stderr.exp
diff --git a/memcheck/tests/error_counts.stdout.exp b/memcheck/tests/error_counts.stdout.exp
new file mode 100644
index 0000000..ea11aad
--- /dev/null
+++ b/memcheck/tests/error_counts.stdout.exp
@@ -0,0 +1,5 @@
+errors: 0
+errors: 1
+leaks: 0B, 0B, 0B, 0B
+leaks: 77B, 88B, 99B, 0B
+errors: 1
diff --git a/memcheck/tests/error_counts.vgtest b/memcheck/tests/error_counts.vgtest
new file mode 100644
index 0000000..ddd7241
--- /dev/null
+++ b/memcheck/tests/error_counts.vgtest
@@ -0,0 +1,2 @@
+vgopts: --logfile-fd=-1
+prog:   error_counts