--- /dev/null
+package grindhouse
+
+import "strings"
+import "testing"
+
+const CLEANXML1 = `
+<?xml version="1.0"?>
+
+<valgrindoutput>
+
+<protocolversion>4</protocolversion>
+<protocoltool>memcheck</protocoltool>
+
+<preamble>
+ <line>Memcheck, a memory error detector</line>
+ <line>Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.</line>
+ <line>Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info</line>
+ <line>Command: ./good</line>
+</preamble>
+
+<pid>13206</pid>
+<ppid>32361</ppid>
+<tool>memcheck</tool>
+
+<args>
+ <vargv>
+ <exe>/usr/bin/valgrind.bin</exe>
+ <arg>--suppressions=/usr/lib/valgrind/debian-libc6-dbg.supp</arg>
+ <arg>--xml-file=/tmp/t</arg>
+ <arg>--xml=yes</arg>
+ </vargv>
+ <argv>
+ <exe>./good</exe>
+ </argv>
+</args>
+
+<status>
+ <state>RUNNING</state>
+ <time>00:00:00:00.038 </time>
+</status>
+
+
+<status>
+ <state>FINISHED</state>
+ <time>00:00:00:00.363 </time>
+</status>
+
+<errorcounts>
+</errorcounts>
+
+<suppcounts>
+ <pair>
+ <count>2</count>
+ <name>dl-hack3-cond-1</name>
+ </pair>
+ <pair>
+ <count>2</count>
+ <name>glibc-2.5.x-on-SUSE-10.2-(PPC)-2a</name>
+ </pair>
+</suppcounts>
+
+</valgrindoutput>
+`
+
+const ERRORXML1 = `
+<?xml version="1.0"?>
+
+<valgrindoutput>
+
+<protocolversion>4</protocolversion>
+<protocoltool>memcheck</protocoltool>
+
+<preamble>
+ <line>Memcheck, a memory error detector</line>
+ <line>Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.</line>
+ <line>Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info</line>
+ <line>Command: ./leaky</line>
+</preamble>
+
+<pid>7673</pid>
+<ppid>32361</ppid>
+<tool>memcheck</tool>
+
+<args>
+ <vargv>
+ <exe>/usr/bin/valgrind.bin</exe>
+ <arg>--suppressions=/usr/lib/valgrind/debian-libc6-dbg.supp</arg>
+ <arg>--xml-file=/tmp/t</arg>
+ <arg>--xml=yes</arg>
+ </vargv>
+ <argv>
+ <exe>./leaky</exe>
+ </argv>
+</args>
+
+<status>
+ <state>RUNNING</state>
+ <time>00:00:00:00.039 </time>
+</status>
+
+<error>
+ <unique>0x4</unique>
+ <tid>1</tid>
+ <kind>UninitValue</kind>
+ <what>Use of uninitialised value of size 8</what>
+ <stack>
+ <frame>
+ <ip>0x4E6A76B</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>_itoa_word</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>_itoa.c</file>
+ <line>195</line>
+ </frame>
+ <frame>
+ <ip>0x4E6B9B8</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>vfprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>vfprintf.c</file>
+ <line>1613</line>
+ </frame>
+ <frame>
+ <ip>0x4E903F1</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>vsnprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/libio</dir>
+ <file>vsnprintf.c</file>
+ <line>120</line>
+ </frame>
+ <frame>
+ <ip>0x4E75902</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>snprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>snprintf.c</file>
+ <line>35</line>
+ </frame>
+ <frame>
+ <ip>0x40056D</ip>
+ <obj>/mnt/e4test/src/grindhouse/leaky</obj>
+ <fn>main</fn>
+ </frame>
+ </stack>
+</error>
+
+<error>
+ <unique>0x5</unique>
+ <tid>1</tid>
+ <kind>UninitCondition</kind>
+ <what>Conditional jump or move depends on uninitialised value(s)</what>
+ <stack>
+ <frame>
+ <ip>0x4E6A775</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>_itoa_word</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>_itoa.c</file>
+ <line>195</line>
+ </frame>
+ <frame>
+ <ip>0x4E6B9B8</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>vfprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>vfprintf.c</file>
+ <line>1613</line>
+ </frame>
+ <frame>
+ <ip>0x4E903F1</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>vsnprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/libio</dir>
+ <file>vsnprintf.c</file>
+ <line>120</line>
+ </frame>
+ <frame>
+ <ip>0x4E75902</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>snprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>snprintf.c</file>
+ <line>35</line>
+ </frame>
+ <frame>
+ <ip>0x40056D</ip>
+ <obj>/mnt/e4test/src/grindhouse/leaky</obj>
+ <fn>main</fn>
+ </frame>
+ </stack>
+</error>
+
+<error>
+ <unique>0x6</unique>
+ <tid>1</tid>
+ <kind>UninitCondition</kind>
+ <what>Conditional jump or move depends on uninitialised value(s)</what>
+ <stack>
+ <frame>
+ <ip>0x4E6D928</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>vfprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>vfprintf.c</file>
+ <line>1613</line>
+ </frame>
+ <frame>
+ <ip>0x4E903F1</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>vsnprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/libio</dir>
+ <file>vsnprintf.c</file>
+ <line>120</line>
+ </frame>
+ <frame>
+ <ip>0x4E75902</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>snprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>snprintf.c</file>
+ <line>35</line>
+ </frame>
+ <frame>
+ <ip>0x40056D</ip>
+ <obj>/mnt/e4test/src/grindhouse/leaky</obj>
+ <fn>main</fn>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>snprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>snprintf.c</file>
+ <line>35</line>
+ </frame>
+ <frame>
+ <ip>0x40056D</ip>
+ <obj>/mnt/e4test/src/grindhouse/leaky</obj>
+ <fn>main</fn>
+ <tid>1</tid>
+ <kind>UninitCondition</kind>
+ <what>Conditional jump or move depends on uninitialised value(s)</what>
+ <stack>
+ <frame>
+ <ip>0x4E6D928</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>vfprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>vfprintf.c</file>
+ <line>1613</line>
+ </frame>
+ <frame>
+ <ip>0x4E903F1</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>vsnprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/libio</dir>
+ <file>vsnprintf.c</file>
+ <line>120</line>
+ </frame>
+ <frame>
+ <ip>0x4E75902</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>snprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>snprintf.c</file>
+ <line>35</line>
+ </frame>
+ <frame>
+ <ip>0x40056D</ip>
+ <obj>/mnt/e4test/src/grindhouse/leaky</obj>
+ <fn>main</fn>
+ </frame>
+ </stack>
+</error>
+
+<error>
+ <unique>0x7</unique>
+ <tid>1</tid>
+ <kind>UninitCondition</kind>
+ <what>Conditional jump or move depends on uninitialised value(s)</what>
+ <stack>
+ <frame>
+ <ip>0x4E6BAA6</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>vfprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>vfprintf.c</file>
+ <line>1613</line>
+ </frame>
+ <frame>
+ <ip>0x4E903F1</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>vsnprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/libio</dir>
+ <file>vsnprintf.c</file>
+ <line>120</line>
+ </frame>
+ <frame>
+ <ip>0x4E75902</ip>
+ <obj>/lib/libc-2.11.2.so</obj>
+ <fn>snprintf</fn>
+ <dir>/home/aurel32/eglibc/eglibc-2.11.2/stdio-common</dir>
+ <file>snprintf.c</file>
+ <line>35</line>
+ </frame>
+ <frame>
+ <ip>0x40056D</ip>
+ <obj>/mnt/e4test/src/grindhouse/leaky</obj>
+ <fn>main</fn>
+ </frame>
+ </stack>
+</error>
+
+
+<status>
+ <state>FINISHED</state>
+ <time>00:00:00:00.364 </time>
+</status>
+
+<error>
+ <unique>0x8</unique>
+ <tid>1</tid>
+ <kind>Leak_DefinitelyLost</kind>
+ <xwhat>
+ <text>100 bytes in 1 blocks are definitely lost in loss record 1 of 1</text>
+ <leakedbytes>100</leakedbytes>
+ <leakedblocks>1</leakedblocks>
+ </xwhat>
+ <stack>
+ <frame>
+ <ip>0x4C244E8</ip>
+ <obj>/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so</obj>
+ <fn>malloc</fn>
+ <dir>/build/buildd-valgrind_3.6.0~svn11254+nmu1-amd64-08RYEm/valgrind-3.6.0~svn11254+nmu1/coregrind/m_replacemalloc</dir>
+ <file>vg_replace_malloc.c</file>
+ <line>236</line>
+ </frame>
+ <frame>
+ <ip>0x400548</ip>
+ <obj>/mnt/e4test/src/grindhouse/leaky</obj>
+ <fn>main</fn>
+ </frame>
+ </stack>
+</error>
+`
+
+func expectCleanToStr(e bool) string {
+ if (e) {
+ return "clean";
+ }
+ return "dirty";
+}
+
+func doParseValgrindXml(desc string, xml string,
+ t *testing.T, expectClean bool) {
+ vinfo, err := ParseValgrindXml(strings.NewReader(xml))
+ if (err != nil) {
+ t.Errorf("got parse error!")
+ return
+ }
+ if (vinfo.Clean != expectClean) {
+ t.Errorf("expected %s to be %s, but it was %s!",
+ desc, expectCleanToStr(expectClean), expectCleanToStr(vinfo.Clean))
+ return
+ }
+}
+
+func TestCleanXml1(t *testing.T) {
+ doParseValgrindXml("cleanXml1", CLEANXML1, t, true)
+}
+
+func TestErrorXml1(t *testing.T) {
+ doParseValgrindXml("errorXml1", ERRORXML1, t, false)
+}
--- /dev/null
+package grindhouse
+
+import "flag"
+import "fmt"
+import "os"
+
+/*
+ * Grindhouse
+ *
+ * Recommended valgrind options:
+ * valgrind --xml=yes --xml-file=<file>
+ */
+
+func usage() {
+ fmt.Fprintf(os.Stderr,
+`%s: a valgrind XML file analyzer.
+This program analyzes valgrind XML output files and summarizes them.
+
+Usage: %s [opts] [files]
+Options:
+-h: this help message
+`, os.Args[0], os.Args[0])
+}
+
+var getHelp = flag.Bool("h", false, "See usage")
+
+func handleFile(filename string) os.Error {
+ file, err := os.Open(filename)
+ if (err != nil) {
+ fmt.Fprintf(os.Stderr, "Failed to open file %s\n", filename)
+ return err
+ }
+ defer file.Close()
+ vinfo, err := ParseValgrindXml(file)
+ if (err != nil) {
+ fmt.Fprintf(os.Stderr, "Failed to parse file %s\n", filename)
+ return err
+ }
+ if !vinfo.Clean {
+ return os.EAFNOSUPPORT
+ }
+ return nil
+}
+
+func main() {
+ ret := 0
+ flag.Usage = usage
+ flag.Parse()
+ if (*getHelp) {
+ usage()
+ os.Exit(0)
+ }
+ filenames := make([]string, flag.NArg())
+ for i := 0; i < flag.NArg(); i++ {
+ filenames[i] = flag.Arg(i)
+ }
+ if (flag.NArg() == 0) {
+ fmt.Fprintf(os.Stderr, "You must give at least one file to open as an argument.")
+ usage()
+ os.Exit(1)
+ }
+ for i := 0; i < len(filenames); i++ {
+ if handleFile(filenames[i]) != nil {
+ ret = 1
+ }
+ }
+ if (ret != 0) {
+ fmt.Fprintf(os.Stderr, "Errors were encountered.")
+ }
+ os.Exit(ret)
+}