From 070a8e6aa3d2b50553d84f7d6e8e37b5a112804c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 13 Dec 2010 18:45:25 +1030 Subject: [PATCH] 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). --- tools/ccanlint/ccanlint.h | 3 + tools/ccanlint/tests/run_tests_valgrind.c | 114 +++++++++++++++++++++- 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/tools/ccanlint/ccanlint.h b/tools/ccanlint/ccanlint.h index 458bed54..41a4606c 100644 --- a/tools/ccanlint/ccanlint.h +++ b/tools/ccanlint/ccanlint.h @@ -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). */ diff --git a/tools/ccanlint/tests/run_tests_valgrind.c b/tools/ccanlint/tests/run_tests_valgrind.c index 310e8971..21b8660a 100644 --- a/tools/ccanlint/tests/run_tests_valgrind.c +++ b/tools/ccanlint/tests/run_tests_valgrind.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -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,16 +100,31 @@ 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)) { - score->score++; + 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); -- 2.39.2