First pass at adding ability for Memcheck to print all output in XML
form. The relevant flag is --xml=yes. Currently this only works with
Memcheck.
Specifying this flag fixes various other options relating to verbosity
and behaviour of the leak checker, so that the resulting output is in
a relatively fixed form suitable for parsing by GUIs.
Still to do:
* Add mechanism to show error counts
* Add regression test
* Document the resulting format
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3773 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/m_errormgr.c b/coregrind/m_errormgr.c
index df05c20..79b2d15 100644
--- a/coregrind/m_errormgr.c
+++ b/coregrind/m_errormgr.c
@@ -251,11 +251,18 @@
static void pp_Error ( Error* err, Bool printCount )
{
- if (printCount)
- VG_(message)(Vg_UserMsg, "Observed %d times:", err->count );
- if (err->tid > 0 && err->tid != last_tid_printed) {
- VG_(message)(Vg_UserMsg, "Thread %d:", err->tid );
- last_tid_printed = err->tid;
+ if (VG_(clo_xml)) {
+ VG_(message)(Vg_UserMsg, "<error>");
+ VG_(message)(Vg_UserMsg, " <tid>%d</tid>", err->tid);
+ }
+
+ if (!VG_(clo_xml)) {
+ if (printCount)
+ VG_(message)(Vg_UserMsg, "Observed %d times:", err->count );
+ if (err->tid > 0 && err->tid != last_tid_printed) {
+ VG_(message)(Vg_UserMsg, "Thread %d:", err->tid );
+ last_tid_printed = err->tid;
+ }
}
switch (err->ekind) {
@@ -274,6 +281,9 @@
VG_(tool_panic)("unhandled error type");
}
}
+
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, "</error>");
}
/* Figure out if we want to perform a given action for this error, possibly
@@ -629,6 +639,31 @@
/*--- Exported fns ---*/
/*------------------------------------------------------------*/
+/* Show the used suppressions. Returns False if no suppression
+ got used. */
+static Bool show_used_suppressions ( void )
+{
+ Supp *su;
+ Bool any_supp;
+
+ any_supp = False;
+ for (su = suppressions; su != NULL; su = su->next) {
+ if (su->count <= 0)
+ continue;
+ any_supp = True;
+ if (VG_(clo_xml)) {
+ VG_(message)(Vg_DebugMsg,
+ "<supp><count>%d</count><name>%s</name></supp>",
+ su->count, su->sname);
+ } else {
+ VG_(message)(Vg_DebugMsg, "supp: %4d %s", su->count, su->sname);
+ }
+ }
+
+ return any_supp;
+}
+
+
/* This is called not from generated code but from the scheduler */
void VG_(show_all_errors) ( void )
{
@@ -652,6 +687,15 @@
if (su->count > 0)
n_supp_contexts++;
}
+
+ /* If we're printing XML, just show the suppressions and stop.
+ */
+ if (VG_(clo_xml)) {
+ (void)show_used_suppressions();
+ return;
+ }
+
+ /* We only get here if not printing XML. */
VG_(message)(Vg_UserMsg,
"ERROR SUMMARY: "
"%d errors from %d contexts (suppressed: %d from %d)",
@@ -691,13 +735,7 @@
if (n_supp_contexts > 0)
VG_(message)(Vg_DebugMsg, "");
- any_supp = False;
- for (su = suppressions; su != NULL; su = su->next) {
- if (su->count > 0) {
- any_supp = True;
- VG_(message)(Vg_DebugMsg, "supp: %4d %s", su->count, su->sname);
- }
- }
+ any_supp = show_used_suppressions();
if (n_err_contexts > 0) {
if (any_supp)
diff --git a/coregrind/m_options.c b/coregrind/m_options.c
index 154cfbf..40f2251 100644
--- a/coregrind/m_options.c
+++ b/coregrind/m_options.c
@@ -43,6 +43,7 @@
Int VG_(clo_gen_suppressions) = 0;
Int VG_(clo_sanity_level) = 1;
Int VG_(clo_verbosity) = 1;
+Bool VG_(clo_xml) = False;
Bool VG_(clo_demangle) = True;
Bool VG_(clo_trace_children) = False;
Int VG_(clo_log_fd) = 2;
diff --git a/coregrind/m_stacktrace.c b/coregrind/m_stacktrace.c
index 2c56d66..7dab5be 100644
--- a/coregrind/m_stacktrace.c
+++ b/coregrind/m_stacktrace.c
@@ -191,14 +191,26 @@
static UChar buf[VG_ERRTXT_LEN];
VG_(describe_IP)(ip, buf, VG_ERRTXT_LEN);
- VG_(message)(Vg_UserMsg, " %s %s", ( n == 0 ? "at" : "by" ), buf);
+
+ if (VG_(clo_xml)) {
+ VG_(message)(Vg_UserMsg, " %s", buf);
+ } else {
+ VG_(message)(Vg_UserMsg, " %s %s", ( n == 0 ? "at" : "by" ), buf);
+ }
}
/* Print a StackTrace. */
void VG_(pp_StackTrace) ( StackTrace ips, UInt n_ips )
{
vg_assert( n_ips > 0 );
+
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <stack>");
+
VG_(apply_StackTrace)( printIpDesc, ips, n_ips );
+
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " </stack>");
}
/* Get and immediately print a StackTrace. */
diff --git a/coregrind/m_translate.c b/coregrind/m_translate.c
index fd5b286..ee20139 100644
--- a/coregrind/m_translate.c
+++ b/coregrind/m_translate.c
@@ -407,7 +407,7 @@
ok = VG_(get_fnname_w_offset)(redir, name2, 64);
if (!ok) VG_(strcpy)(name2, "???");
VG_(message)(Vg_DebugMsg,
- "TRANSLATE: 0x%llx (%s) redirected to 0x%llx (%s)",
+ "REDIR: 0x%llx (%s) redirected to 0x%llx (%s)",
orig_addr, name1,
redir, name2 );
}
diff --git a/coregrind/vg_main.c b/coregrind/vg_main.c
index 23450d8..4120180 100644
--- a/coregrind/vg_main.c
+++ b/coregrind/vg_main.c
@@ -1392,6 +1392,12 @@
vg_assert(0 == res);
}
+/*====================================================================*/
+/*=== Command-line: variables, processing, etc ===*/
+/*====================================================================*/
+
+// See pub_{core,tool}_options.h for explanations of all these.
+
static void usage ( Bool debug_help )
{
Char* usage1 =
@@ -1404,6 +1410,7 @@
" --version show version\n"
" -q --quiet run silently; only print error msgs\n"
" -v --verbose be more verbose, incl counts of errors\n"
+" --xml=yes show errors as XML\n"
" --trace-children=no|yes Valgrind-ise child processes? [no]\n"
" --track-fds=no|yes track open file descriptors? [no]\n"
" --time-stamp=no|yes add timestamps to log messages? [no]\n"
@@ -1627,6 +1634,7 @@
/* do nothing */
}
+ else VG_BOOL_CLO(arg, "--xml", VG_(clo_xml))
else VG_BOOL_CLO(arg, "--branchpred", VG_(clo_branchpred))
else VG_BOOL_CLO(arg, "--db-attach", VG_(clo_db_attach))
else VG_BOOL_CLO(arg, "--demangle", VG_(clo_demangle))
@@ -1798,6 +1806,29 @@
VG_(bad_option)("--gen-suppressions=");
}
+ /* If we've been asked to emit XML, mash around various other
+ options so as to constrain the output somewhat, and to remove
+ any need for user input during the run. */
+ if (VG_(clo_xml)) {
+ /* Disable suppression generation (requires user input) */
+ VG_(clo_gen_suppressions) = 0;
+ /* Disable attaching to GDB (requires user input) */
+ VG_(clo_db_attach) = False;
+ /* Set a known verbosity level */
+ VG_(clo_verbosity) = 1;
+ /* Disable error limits (this might be a bad idea!) */
+ VG_(clo_error_limit) = False;
+ /* Disable emulation warnings */
+ VG_(clo_show_emwarns) = False;
+ /* Disable waiting for GDB to debug Valgrind */
+ VG_(clo_wait_for_gdb) = False;
+ /* No file-descriptor leak checking yet */
+ VG_(clo_track_fds) = False;
+ /* Also, we want to set options for the leak checker, but that
+ will have to be done in Memcheck's flag-handling code, not
+ here. */
+ }
+
/* All non-logging-related options have been checked. If the logging
option specified is ok, we can switch to it, as we know we won't
have to generate any other command-line-related error messages.
@@ -1914,6 +1945,16 @@
}
}
+
+ /* Check that the requested tool actually supports XML output. */
+ if (VG_(clo_xml) && 0 != VG_(strcmp)(toolname, "memcheck")) {
+ VG_(clo_xml) = False;
+ VG_(message)(Vg_UserMsg,
+ "Currently only Memcheck supports XML output.");
+ VG_(bad_option)("--xml=yes");
+ /*NOTREACHED*/
+ }
+
// Move log_fd into the safe range, so it doesn't conflict with any app fds.
// XXX: this is more or less duplicating the behaviour of the calls to
// VG_(safe_fd)() above, although this does not close the original fd.
@@ -1932,27 +1973,43 @@
command line args, to help people trying to interpret the
results of a run which encompasses multiple processes. */
+ if (VG_(clo_xml)) {
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg, "<valgrindoutput>");
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg, "<protocolversion>1</protocolversion>");
+ VG_(message)(Vg_UserMsg, "");
+ }
+
+ HChar* xpre = VG_(clo_xml) ? "<preamble>" : "";
+ HChar* xpost = VG_(clo_xml) ? "</preamble>" : "";
+
if (VG_(clo_verbosity > 0)) {
/* Tool details */
- VG_(message)(Vg_UserMsg, "%s%s%s, %s.",
+ VG_(message)(Vg_UserMsg, "%s%s%s%s, %s.%s",
+ xpre,
VG_(details).name,
NULL == VG_(details).version ? "" : "-",
NULL == VG_(details).version
? (Char*)"" : VG_(details).version,
- VG_(details).description);
- VG_(message)(Vg_UserMsg, "%s", VG_(details).copyright_author);
+ VG_(details).description,
+ xpost);
+ VG_(message)(Vg_UserMsg, "%s%s%s",
+ xpre, VG_(details).copyright_author, xpost);
/* Core details */
VG_(message)(Vg_UserMsg,
- "Using LibVEX rev %s, a library for dynamic binary translation.",
- LibVEX_Version() );
+ "%sUsing LibVEX rev %s, a library for dynamic binary translation.%s",
+ xpre, LibVEX_Version(), xpost );
VG_(message)(Vg_UserMsg,
- "Copyright (C) 2004-2005, and GNU GPL'd, by OpenWorks LLP.");
+ "%sCopyright (C) 2004-2005, and GNU GPL'd, by OpenWorks LLP.%s",
+ xpre, xpost );
VG_(message)(Vg_UserMsg,
- "Using valgrind-%s, a dynamic binary instrumentation framework.",
- VERSION);
+ "%sUsing valgrind-%s, a dynamic binary instrumentation framework.%s",
+ xpre, VERSION, xpost);
VG_(message)(Vg_UserMsg,
- "Copyright (C) 2000-2005, and GNU GPL'd, by Julian Seward et al.");
+ "%sCopyright (C) 2000-2005, and GNU GPL'd, by Julian Seward et al.%s",
+ xpre, xpost );
}
if (VG_(clo_verbosity) > 0 && log_to != VgLogTo_Fd) {
@@ -1963,6 +2020,17 @@
for (i = 0; i < VG_(client_argc); i++)
VG_(message)(Vg_UserMsg, " %s", VG_(client_argv)[i]);
}
+ else
+ if (VG_(clo_xml)) {
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg, "<pid>%d</pid>", VG_(getpid)());
+ VG_(message)(Vg_UserMsg, "<ppid>%d</ppid>", VG_(getppid)());
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg, "<argv>");
+ for (i = 0; i < VG_(client_argc); i++)
+ VG_(message)(Vg_UserMsg, " <arg>%s</arg>", VG_(client_argv)[i]);
+ VG_(message)(Vg_UserMsg, "</argv>");
+ }
if (VG_(clo_verbosity) > 1) {
Int fd;
@@ -1983,7 +2051,7 @@
if (fd < 0) {
VG_(message)(Vg_DebugMsg, " can't open /proc/version");
} else {
- #define BUF_LEN 256
+# define BUF_LEN 256
Char version_buf[BUF_LEN];
Int n = VG_(read) ( fd, version_buf, BUF_LEN );
vg_assert(n <= BUF_LEN);
@@ -1994,7 +2062,7 @@
VG_(message)(Vg_DebugMsg, " (empty?)");
}
VG_(close)(fd);
- #undef BUF_LEN
+# undef BUF_LEN
}
}
@@ -2291,8 +2359,10 @@
remains = VGA_(stack_unused)(tid);
if (remains < VKI_PAGE_SIZE)
- VG_(message)(Vg_DebugMsg, "WARNING: Thread %d is within %d bytes of running out of stack!",
- tid, remains);
+ VG_(message)(Vg_DebugMsg,
+ "WARNING: Thread %d is within %d bytes "
+ "of running out of stack!",
+ tid, remains);
}
/*
@@ -2726,7 +2796,7 @@
// Verbosity message
// p: end_rdtsc_calibration [so startup message is printed first]
//--------------------------------------------------------------
- if (VG_(clo_verbosity) == 1)
+ if (VG_(clo_verbosity) == 1 && !VG_(clo_xml))
VG_(message)(Vg_UserMsg, "For more details, rerun with: -v");
if (VG_(clo_verbosity) > 0)
VG_(message)(Vg_UserMsg, "");
@@ -2745,6 +2815,11 @@
vg_assert(VG_(master_tid) == 1);
+ if (VG_(clo_xml)) {
+ VG_(message)(Vg_UserMsg, "<status>RUNNING</status>");
+ VG_(message)(Vg_UserMsg, "");
+ }
+
VG_(debugLog)(1, "main", "Running thread 1\n");
VGA_(main_thread_wrapper)(1);
@@ -2780,6 +2855,11 @@
if (VG_(clo_verbosity) > 0)
VG_(message)(Vg_UserMsg, "");
+ if (VG_(clo_xml)) {
+ VG_(message)(Vg_UserMsg, "<status>FINISHED</status>");
+ VG_(message)(Vg_UserMsg, "");
+ }
+
/* Print out file descriptor summary and stats. */
if (VG_(clo_track_fds))
VG_(show_open_fds)();
@@ -2789,6 +2869,12 @@
VG_TDICT_CALL(tool_fini, 0/*exitcode*/);
+ if (VG_(clo_xml)) {
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg, "</valgrindoutput>");
+ VG_(message)(Vg_UserMsg, "");
+ }
+
VG_(sanity_check_general)( True /*include expensive checks*/ );
if (VG_(clo_verbosity) > 1)
diff --git a/coregrind/vg_messages.c b/coregrind/vg_messages.c
index ad5928f..fe4004c 100644
--- a/coregrind/vg_messages.c
+++ b/coregrind/vg_messages.c
@@ -59,7 +59,9 @@
// Print the message
count = 0;
- count += VG_(printf) ("%s%c%c", pfx_s, c,c);
+
+ if (!VG_(clo_xml))
+ count += VG_(printf) ("%s%c%c", pfx_s, c,c);
if (VG_(clo_time_stamp)) {
struct timeval tv;
@@ -75,7 +77,9 @@
}
}
- count += VG_(printf) ("%d%c%c ", VG_(getpid)(), c,c);
+ if (!VG_(clo_xml))
+ count += VG_(printf) ("%d%c%c ", VG_(getpid)(), c,c);
+
count += VG_(vprintf)(format, vargs);
count += VG_(printf) ("\n");
return count;
diff --git a/coregrind/vg_symtab2.c b/coregrind/vg_symtab2.c
index a05f130..eb6a3f3 100644
--- a/coregrind/vg_symtab2.c
+++ b/coregrind/vg_symtab2.c
@@ -2436,7 +2436,7 @@
buf[n] = '\0'; \
}
UInt lineno;
- UChar ibuf[20];
+ UChar ibuf[50];
UInt n = 0;
static UChar buf_fn[VG_ERRTXT_LEN];
static UChar buf_obj[VG_ERRTXT_LEN];
@@ -2445,29 +2445,62 @@
Bool know_objname = VG_(get_objname)(eip, buf_obj, VG_ERRTXT_LEN);
Bool know_srcloc = VG_(get_filename_linenum)(eip, buf_srcloc,
VG_ERRTXT_LEN, &lineno);
- VG_(sprintf)(ibuf,"0x%llx: ", (ULong)eip);
- APPEND(ibuf);
- if (know_fnname) {
- APPEND(buf_fn);
- if (!know_srcloc && know_objname) {
- APPEND(" (in ");
+
+ if (VG_(clo_xml)) {
+
+ /* Print in XML format, dumping in as much info as we know. */
+ APPEND("<frame>");
+ VG_(sprintf)(ibuf,"<ip>0x%llx</ip>", (ULong)eip);
+ APPEND(ibuf);
+ if (know_objname) {
+ APPEND("<obj>");
+ APPEND(buf_obj);
+ APPEND("</obj>");
+ }
+ if (know_fnname) {
+ APPEND("<fn>");
+ APPEND(buf_fn);
+ APPEND("</fn>");
+ }
+ if (know_srcloc) {
+ APPEND("<file>");
+ APPEND(buf_srcloc);
+ APPEND("</file>");
+ APPEND("<line>");
+ VG_(sprintf)(ibuf,"%d",lineno);
+ APPEND(ibuf);
+ APPEND("</line>");
+ }
+ APPEND("</frame>");
+
+ } else {
+
+ /* Print for humans to read */
+ VG_(sprintf)(ibuf,"0x%llx: ", (ULong)eip);
+ APPEND(ibuf);
+ if (know_fnname) {
+ APPEND(buf_fn);
+ if (!know_srcloc && know_objname) {
+ APPEND(" (in ");
+ APPEND(buf_obj);
+ APPEND(")");
+ }
+ } else if (know_objname && !know_srcloc) {
+ APPEND("(within ");
APPEND(buf_obj);
APPEND(")");
+ } else {
+ APPEND("???");
}
- } else if (know_objname && !know_srcloc) {
- APPEND("(within ");
- APPEND(buf_obj);
- APPEND(")");
- } else {
- APPEND("???");
- }
- if (know_srcloc) {
- APPEND(" (");
- APPEND(buf_srcloc);
- APPEND(":");
- VG_(sprintf)(ibuf,"%d",lineno);
- APPEND(ibuf);
- APPEND(")");
+ if (know_srcloc) {
+ APPEND(" (");
+ APPEND(buf_srcloc);
+ APPEND(":");
+ VG_(sprintf)(ibuf,"%d",lineno);
+ APPEND(ibuf);
+ APPEND(")");
+ }
+
}
return buf;
diff --git a/include/pub_tool_options.h b/include/pub_tool_options.h
index e321635..e27e158 100644
--- a/include/pub_tool_options.h
+++ b/include/pub_tool_options.h
@@ -61,10 +61,15 @@
}
/* Verbosity level: 0 = silent, 1 (default), > 1 = more verbose. */
-extern Int VG_(clo_verbosity);
+extern Int VG_(clo_verbosity);
-/* Profile? */
-extern Bool VG_(clo_profile);
+/* Profile? default: NO */
+extern Bool VG_(clo_profile);
+
+/* Emit all messages as XML? default: NO */
+/* If clo_xml is set, various other options are set in a non-default
+ way. See vg_main.c and mc_main.c. */
+extern Bool VG_(clo_xml);
/* Call this if a recognised option was bad for some reason.
Note: don't use it just because an option was unrecognised -- return 'False'
diff --git a/memcheck/mac_leakcheck.c b/memcheck/mac_leakcheck.c
index 38e6d49..d97730d 100644
--- a/memcheck/mac_leakcheck.c
+++ b/memcheck/mac_leakcheck.c
@@ -184,40 +184,79 @@
static Bool (*lc_is_within_valid_secondary) (Addr addr);
static Bool (*lc_is_valid_aligned_word) (Addr addr);
-static const Char *str_lossmode(Reachedness lossmode)
+static const HChar* str_lossmode ( Reachedness lossmode )
{
- const Char *loss = "?";
-
- switch(lossmode) {
- case Unreached: loss = "definitely lost"; break;
- case IndirectLeak: loss = "indirectly lost"; break;
- case Interior: loss = "possibly lost"; break;
- case Proper: loss = "still reachable"; break;
+ const HChar *loss = "?";
+ switch (lossmode) {
+ case Unreached: loss = "definitely lost"; break;
+ case IndirectLeak: loss = "indirectly lost"; break;
+ case Interior: loss = "possibly lost"; break;
+ case Proper: loss = "still reachable"; break;
}
-
return loss;
}
+static const HChar* xml_kind ( Reachedness lossmode )
+{
+ const HChar *loss = "?";
+ switch (lossmode) {
+ case Unreached: loss = "Leak_DefinitelyLost"; break;
+ case IndirectLeak: loss = "Leak_IndirectlyLost"; break;
+ case Interior: loss = "Leak_PossiblyLost"; break;
+ case Proper: loss = "Leak_StillReachable"; break;
+ }
+ return loss;
+}
+
+
/* Used for printing leak errors, avoids exposing the LossRecord type (which
comes in as void*, requiring a cast. */
void MAC_(pp_LeakError)(void* vextra)
{
+ HChar* xpre = VG_(clo_xml) ? " <what>" : "";
+ HChar* xpost = VG_(clo_xml) ? "</what>" : "";
+
LeakExtra* extra = (LeakExtra*)vextra;
LossRecord* l = extra->lossRecord;
const Char *loss = str_lossmode(l->loss_mode);
- VG_(message)(Vg_UserMsg, "");
+ if (VG_(clo_xml)) {
+ VG_(message)(Vg_UserMsg, " <kind>%s</kind>", xml_kind(l->loss_mode));
+ } else {
+ VG_(message)(Vg_UserMsg, "");
+ }
+
if (l->indirect_bytes) {
VG_(message)(Vg_UserMsg,
- "%d (%d direct, %d indirect) bytes in %d blocks are %s in loss record %d of %d",
- l->total_bytes + l->indirect_bytes,
- l->total_bytes, l->indirect_bytes, l->num_blocks,
- loss, extra->n_this_record, extra->n_total_records);
+ "%s%d (%d direct, %d indirect) bytes in %d blocks"
+ " are %s in loss record %d of %d%s",
+ xpre,
+ l->total_bytes + l->indirect_bytes,
+ l->total_bytes, l->indirect_bytes, l->num_blocks,
+ loss, extra->n_this_record, extra->n_total_records,
+ xpost
+ );
+ if (VG_(clo_xml)) {
+ VG_(message)(Vg_UserMsg, " <leakedbytes>%d</leakedbytes>",
+ l->total_bytes + l->indirect_bytes);
+ VG_(message)(Vg_UserMsg, " <leakedblocks>%d</leakedblocks>",
+ l->num_blocks);
+ }
} else {
- VG_(message)(Vg_UserMsg,
- "%d bytes in %d blocks are %s in loss record %d of %d",
- l->total_bytes, l->num_blocks,
- loss, extra->n_this_record, extra->n_total_records);
+ VG_(message)(
+ Vg_UserMsg,
+ "%s%d bytes in %d blocks are %s in loss record %d of %d%s",
+ xpre,
+ l->total_bytes, l->num_blocks,
+ loss, extra->n_this_record, extra->n_total_records,
+ xpost
+ );
+ if (VG_(clo_xml)) {
+ VG_(message)(Vg_UserMsg, " <leakedbytes>%d</leakedbytes>",
+ l->total_bytes);
+ VG_(message)(Vg_UserMsg, " <leakedblocks>%d</leakedblocks>",
+ l->num_blocks);
+ }
}
VG_(pp_ExeContext)(l->allocated_at);
}
@@ -612,14 +651,14 @@
if (lc_n_shadows == 0) {
tl_assert(lc_shadows == NULL);
- if (VG_(clo_verbosity) >= 1) {
+ if (VG_(clo_verbosity) >= 1 && !VG_(clo_xml)) {
VG_(message)(Vg_UserMsg,
"No malloc'd blocks -- no leaks are possible.");
}
return;
}
- if (VG_(clo_verbosity) > 0)
+ if (VG_(clo_verbosity) > 0 && !VG_(clo_xml))
VG_(message)(Vg_UserMsg,
"searching for pointers to %d not-freed blocks.",
lc_n_shadows );
@@ -650,7 +689,7 @@
/* Keep walking the heap until everything is found */
lc_do_leakcheck(-1);
- if (VG_(clo_verbosity) > 0)
+ if (VG_(clo_verbosity) > 0 && !VG_(clo_xml))
VG_(message)(Vg_UserMsg, "checked %d bytes.", lc_scanned);
blocks_leaked = MAC_(bytes_leaked) = 0;
@@ -664,7 +703,7 @@
else
make_summary();
- if (VG_(clo_verbosity) > 0) {
+ if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) {
VG_(message)(Vg_UserMsg, "");
VG_(message)(Vg_UserMsg, "LEAK SUMMARY:");
VG_(message)(Vg_UserMsg, " definitely lost: %d bytes in %d blocks.",
diff --git a/memcheck/mac_malloc_wrappers.c b/memcheck/mac_malloc_wrappers.c
index 8119a02..2167293 100644
--- a/memcheck/mac_malloc_wrappers.c
+++ b/memcheck/mac_malloc_wrappers.c
@@ -549,6 +549,8 @@
if (VG_(clo_verbosity) == 0)
return;
+ if (VG_(clo_xml))
+ return;
/* Count memory still in use. */
VG_(HT_apply_to_all_nodes)(MAC_(malloc_list), malloc_stats_count_chunk, &ms);
diff --git a/memcheck/mac_shared.c b/memcheck/mac_shared.c
index 2fc4b31..bcd440a 100644
--- a/memcheck/mac_shared.c
+++ b/memcheck/mac_shared.c
@@ -222,23 +222,27 @@
void MAC_(pp_AddrInfo) ( Addr a, AddrInfo* ai )
{
+ HChar* xpre = VG_(clo_xml) ? " <auxwhat>" : " ";
+ HChar* xpost = VG_(clo_xml) ? "</auxwhat>" : "";
+
switch (ai->akind) {
case Stack:
VG_(message)(Vg_UserMsg,
- " Address 0x%lx is on thread %d's stack",
- (ULong)a, ai->stack_tid);
+ "%sAddress 0x%llx is on thread %d's stack%s",
+ xpre, (ULong)a, ai->stack_tid, xpost);
break;
case Unknown:
if (ai->maybe_gcc) {
VG_(message)(Vg_UserMsg,
- " Address 0x%lx is just below %%esp. Possibly a bug in GCC/G++",
- (ULong)a);
- VG_(message)(Vg_UserMsg,
- " v 2.96 or 3.0.X. To suppress, use: --workaround-gcc296-bugs=yes");
+ "%sAddress 0x%llx is just below the stack ptr. "
+ "To suppress, use: --workaround-gcc296-bugs=yes%s",
+ xpre, (ULong)a, xpost
+ );
} else {
VG_(message)(Vg_UserMsg,
- " Address 0x%lx is not stack'd, malloc'd or (recently) free'd",
- (ULong)a);
+ "%sAddress 0x%llx "
+ "is not stack'd, malloc'd or (recently) free'd%s",
+ xpre, (ULong)a, xpost);
}
break;
case Freed: case Mallocd: case UserG: case Mempool: {
@@ -264,12 +268,14 @@
relative = "inside";
}
VG_(message)(Vg_UserMsg,
- " Address 0x%lx is %llu bytes %s a %s of size %d %s",
+ "%sAddress 0x%llx is %llu bytes %s a %s of size %d %s%s",
+ xpre,
(ULong)a, (ULong)delta, relative, kind,
ai->blksize,
ai->akind==Mallocd ? "alloc'd"
: ai->akind==Freed ? "free'd"
- : "client-defined");
+ : "client-defined",
+ xpost);
VG_(pp_ExeContext)(ai->lastchange);
break;
}
@@ -288,14 +294,26 @@
{
MAC_Error* err_extra = VG_(get_error_extra)(err);
+ HChar* xpre = VG_(clo_xml) ? " <what>" : "";
+ HChar* xpost = VG_(clo_xml) ? "</what>" : "";
+
switch (VG_(get_error_kind)(err)) {
case FreeErr:
- VG_(message)(Vg_UserMsg, "Invalid free() / delete / delete[]");
- /* fall through */
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <kind>InvalidFree</kind>");
+ VG_(message)(Vg_UserMsg,
+ "%sInvalid free() / delete / delete[]%s",
+ xpre, xpost);
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+ MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
+ break;
+
case FreeMismatchErr:
- if (VG_(get_error_kind)(err) == FreeMismatchErr)
- VG_(message)(Vg_UserMsg,
- "Mismatched free() / delete / delete []");
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <kind>MismatchedFree</kind>");
+ VG_(message)(Vg_UserMsg,
+ "%sMismatched free() / delete / delete []%s",
+ xpre, xpost);
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
break;
@@ -303,16 +321,26 @@
case AddrErr:
switch (err_extra->axskind) {
case ReadAxs:
- VG_(message)(Vg_UserMsg, "Invalid read of size %d",
- err_extra->size );
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <kind>InvalidRead</kind>");
+ VG_(message)(Vg_UserMsg,
+ "%sInvalid read of size %d%s",
+ xpre, err_extra->size, xpost );
break;
case WriteAxs:
- VG_(message)(Vg_UserMsg, "Invalid write of size %d",
- err_extra->size );
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <kind>InvalidWrite</kind>");
+ VG_(message)(Vg_UserMsg,
+ "%sInvalid write of size %d%s",
+ xpre, err_extra->size, xpost );
break;
case ExecAxs:
- VG_(message)(Vg_UserMsg, "Jump to the invalid address "
- "stated on the next line");
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <kind>InvalidJump</kind>");
+ VG_(message)(Vg_UserMsg,
+ "%sJump to the invalid address "
+ "stated on the next line%s",
+ xpre, xpost);
break;
default:
VG_(tool_panic)("MAC_(pp_shared_Error)(axskind)");
@@ -323,16 +351,22 @@
case OverlapErr: {
OverlapExtra* ov_extra = (OverlapExtra*)VG_(get_error_extra)(err);
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <kind>Overlap</kind>");
if (ov_extra->len == -1)
VG_(message)(Vg_UserMsg,
- "Source and destination overlap in %s(%p, %p)",
+ "%sSource and destination overlap in %s(%p, %p)%s",
+ xpre,
VG_(get_error_string)(err),
- ov_extra->dst, ov_extra->src);
+ ov_extra->dst, ov_extra->src,
+ xpost);
else
VG_(message)(Vg_UserMsg,
- "Source and destination overlap in %s(%p, %p, %d)",
+ "%sSource and destination overlap in %s(%p, %p, %d)%s",
+ xpre,
VG_(get_error_string)(err),
- ov_extra->dst, ov_extra->src, ov_extra->len);
+ ov_extra->dst, ov_extra->src, ov_extra->len,
+ xpost);
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
break;
}
@@ -342,7 +376,10 @@
}
case IllegalMempoolErr:
- VG_(message)(Vg_UserMsg, "Illegal memory pool address");
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <kind>InvalidMemPool</kind>");
+ VG_(message)(Vg_UserMsg, "%sIllegal memory pool address%s",
+ xpre, xpost);
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
break;
@@ -860,7 +897,7 @@
{
MAC_(print_malloc_stats)();
- if (VG_(clo_verbosity) == 1) {
+ if (VG_(clo_verbosity) == 1 && !VG_(clo_xml)) {
if (MAC_(clo_leak_check) == LC_Off)
VG_(message)(Vg_UserMsg,
"For a detailed leak analysis, rerun with: --leak-check=yes");
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
index d28c91f..3244214 100644
--- a/memcheck/mc_main.c
+++ b/memcheck/mc_main.c
@@ -1313,11 +1313,18 @@
{
MAC_Error* err_extra = VG_(get_error_extra)(err);
+ HChar* xpre = VG_(clo_xml) ? " <what>" : "";
+ HChar* xpost = VG_(clo_xml) ? "</what>" : "";
+
switch (VG_(get_error_kind)(err)) {
case CoreMemErr: {
Char* s = ( err_extra->isUnaddr ? "unaddressable" : "uninitialised" );
- VG_(message)(Vg_UserMsg, "%s contains %s byte(s)",
- VG_(get_error_string)(err), s);
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <kind>CoreMemError</kind>");
+ /* What the hell *is* a CoreMemError? jrs 2005-May-18 */
+ VG_(message)(Vg_UserMsg, "%s%s contains %s byte(s)%s",
+ xpre, VG_(get_error_string)(err), s, xpost);
+
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
break;
@@ -1325,12 +1332,17 @@
case ValueErr:
if (err_extra->size == 0) {
- VG_(message)(Vg_UserMsg,
- "Conditional jump or move depends on uninitialised value(s)");
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <kind>UninitCondition</kind>");
+ VG_(message)(Vg_UserMsg, "%sConditional jump or move depends"
+ " on uninitialised value(s)%s",
+ xpre, xpost);
} else {
- VG_(message)(Vg_UserMsg,
- "Use of uninitialised value of size %d",
- err_extra->size);
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <kind>UninitValue</kind>");
+ VG_(message)(Vg_UserMsg,
+ "%sUse of uninitialised value of size %d%s",
+ xpre, err_extra->size, xpost);
}
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
break;
@@ -1341,8 +1353,10 @@
Char* s2 = ( err_extra->isUnaddr ? "unaddressable" : "uninitialised" );
if (isReg) tl_assert(!err_extra->isUnaddr);
- VG_(message)(Vg_UserMsg, "Syscall param %s %s %s byte(s)",
- VG_(get_error_string)(err), s1, s2);
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <kind>SyscallParam</kind>");
+ VG_(message)(Vg_UserMsg, "%sSyscall param %s %s %s byte(s)%s",
+ xpre, VG_(get_error_string)(err), s1, s2, xpost);
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
@@ -1351,8 +1365,11 @@
case UserErr: {
Char* s = ( err_extra->isUnaddr ? "Unaddressable" : "Uninitialised" );
+ if (VG_(clo_xml))
+ VG_(message)(Vg_UserMsg, " <kind>ClientCheck</kind>");
VG_(message)(Vg_UserMsg,
- "%s byte(s) found during client check request", s);
+ "%s%s byte(s) found during client check request%s",
+ xpre, s, xpost);
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
@@ -2426,6 +2443,13 @@
static void mc_post_clo_init ( void )
{
+ /* If we've been asked to emit XML, mash around various other
+ options so as to constrain the output somewhat. */
+ if (VG_(clo_xml)) {
+ /* Extract as much info as possible from the leak checker. */
+ MAC_(clo_show_reachable) = True;
+ MAC_(clo_leak_check) = LC_Full;
+ }
}
static void mc_fini ( Int exitcode )