Commit 070a8e6a authored by Rusty Russell's avatar Rusty Russell

ccanlint: report valgrind "definite" leaks.

This is complicated by valgrind's limited options, and our desire not to run
valgrind twice (it's already the slowest part of the tests).

Ideally I'd like a different error code for each kind of error.  I
could parse and pretty-print the XML output, but instead I just parse
the human-readable (which is fragile).
parent 89d06e0d
......@@ -168,6 +168,9 @@ struct ccan_file {
/* Compiled with coverage information. */
char *cov_compiled;
/* Leak output from valgrind. */
char *leak_info;
};
/* A new ccan_file, with the given name (talloc_steal onto returned value). */
......
......@@ -3,6 +3,8 @@
#include <ccan/talloc/talloc.h>
#include <ccan/str/str.h>
#include <ccan/foreach/foreach.h>
#include <ccan/grab_file/grab_file.h>
#include <ccan/str_talloc/str_talloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
......@@ -27,6 +29,67 @@ static const char *can_run_vg(struct manifest *m)
return NULL;
}
/* Example output:
==2749== Conditional jump or move depends on uninitialised value(s)
==2749== at 0x4026C60: strnlen (mc_replace_strmem.c:263)
==2749== by 0x40850E3: vfprintf (vfprintf.c:1614)
==2749== by 0x408EACF: printf (printf.c:35)
==2749== by 0x8048465: main (in /tmp/foo)
==2749==
==2749== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2749== at 0x4025BD3: malloc (vg_replace_malloc.c:236)
==2749== by 0x8048444: main (in /tmp/foo)
==2749==
*/
static bool blank_line(const char *line)
{
return line[strspn(line, "=0123456789 ")] == '\0';
}
static char *get_leaks(const char *output, char **errs)
{
char *leaks = talloc_strdup(output, "");
unsigned int i, num;
char **lines = strsplit(output, output, "\n", &num);
*errs = talloc_strdup(output, "");
for (i = 0; i < num; i++) {
if (strstr(lines[i], " lost ")) {
/* A leak... */
if (strstr(lines[i], " definitely lost ")) {
/* Definite leak, report. */
while (lines[i] && !blank_line(lines[i])) {
leaks = talloc_append_string(leaks,
lines[i]);
leaks = talloc_append_string(leaks,
"\n");
i++;
}
} else
/* Not definite, ignore. */
while (lines[i] && !blank_line(lines[i]))
i++;
} else {
/* A real error. */
while (lines[i] && !blank_line(lines[i])) {
*errs = talloc_append_string(*errs, lines[i]);
*errs = talloc_append_string(*errs, "\n");
i++;
}
}
}
if (!leaks[0]) {
talloc_free(leaks);
leaks = NULL;
}
if (!(*errs)[0]) {
talloc_free(*errs);
*errs = NULL;
}
return leaks;
}
/* FIXME: Run examples, too! */
static void do_run_tests_vg(struct manifest *m,
bool keep,
......@@ -37,15 +100,30 @@ static void do_run_tests_vg(struct manifest *m,
struct list_head *list;
char *cmdout;
/* This is slow, so we run once but grab leak info. */
score->total = 0;
foreach_ptr(list, &m->run_tests, &m->api_tests) {
list_for_each(list, i, list) {
char *output, *err;
score->total++;
/* FIXME: Valgrind's output sucks. XML is unreadable by
* humans, and you can't have both. */
if (run_command(score, timeleft, &cmdout,
"valgrind -q --error-exitcode=100%s %s",
"valgrind -q --leak-check=full"
" --log-fd=3 %s %s"
" 3> valgrind.log",
run_tests_vg.options ?
run_tests_vg.options : "",
i->compiled)) {
output = grab_file(i, "valgrind.log", NULL);
if (!output || output[0] == '\0') {
err = NULL;
} else {
i->leak_info = get_leaks(output, &err);
}
if (err) {
score_file_error(score, i, 0, err);
} else
score->score++;
} else {
score_file_error(score, i, 0, cmdout);
......@@ -57,6 +135,30 @@ static void do_run_tests_vg(struct manifest *m,
score->pass = true;
}
static void do_leakcheck_vg(struct manifest *m,
bool keep,
unsigned int *timeleft,
struct score *score)
{
struct ccan_file *i;
struct list_head *list;
bool leaks = false;
foreach_ptr(list, &m->run_tests, &m->api_tests) {
list_for_each(list, i, list) {
if (i->leak_info) {
score_file_error(score, i, 0, i->leak_info);
leaks = true;
}
}
}
if (!leaks) {
score->score = 1;
score->pass = true;
}
}
/* Gcc's warn_unused_result is fascist bullshit. */
#define doesnt_matter()
......@@ -85,3 +187,11 @@ struct ccanlint run_tests_vg = {
};
REGISTER_TEST(run_tests_vg, &run_tests, NULL);
struct ccanlint run_tests_vg_leak = {
.key = "valgrind-leaks",
.name = "Module's run and api tests leak memory",
.check = do_leakcheck_vg,
};
REGISTER_TEST(run_tests_vg_leak, &run_tests_vg, NULL);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment