[PATCH xorg-gtest] Add support for starting a process through valgrind

Peter Hutterer peter.hutterer at who-t.net
Sun Nov 25 21:42:27 PST 2012


export XORG_GTEST_USE_VALGRIND="valgrind --leak-check=full"
./run-some-test

But really, can be used with any wrapper binary. Given that valgrind is the
main use-case here, keep the USE_VALGRIND naming instead of something more
generic like USE_PROCESS_WRAPPER or so.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 README                |   4 ++
 src/process.cpp       |  11 ++++-
 test/process-test.cpp | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 123 insertions(+), 1 deletion(-)

diff --git a/README b/README
index 53475db..2409bdb 100644
--- a/README
+++ b/README
@@ -85,3 +85,7 @@ XORG_GTEST_XSERVER_KEEPALIVE
 XORG_GTEST_CHILD_STDOUT
   If set to any value, Process::Start() will _not_ close stdout/stdin/stderr
   for the forked child.
+XORG_GTEST_USE_VALGRIND
+  Set to the valgrind command to use when starting a process. Options must
+  be space-separated, e.g. "valgrind --leak-check=full --someotherarg"
+  Not limited to valgrind, you can specify any executable here.
diff --git a/src/process.cpp b/src/process.cpp
index a743346..43ac0d6 100644
--- a/src/process.cpp
+++ b/src/process.cpp
@@ -100,13 +100,22 @@ void xorg::testing::Process::Start(const std::string &program, const std::vector
     std::vector<char*> args;
     std::vector<std::string>::const_iterator it;
 
+    char *valgrind = getenv("XORG_GTEST_USE_VALGRIND");
+    if (valgrind) {
+      char *tok = strtok(valgrind, " ");
+      while(tok) {
+        args.push_back(strdup(tok));
+        tok = strtok(NULL, " ");
+      }
+    }
+
     args.push_back(strdup(program.c_str()));
 
     for (it = argv.begin(); it != argv.end(); it++)
       args.push_back(strdup(it->c_str()));
     args.push_back(NULL);
 
-    execvp(program.c_str(), &args[0]);
+    execvp(args[0], &args[0]);
 
     d_->state = ERROR;
     throw std::runtime_error("Failed to start process");
diff --git a/test/process-test.cpp b/test/process-test.cpp
index 59f96c2..3446fe1 100644
--- a/test/process-test.cpp
+++ b/test/process-test.cpp
@@ -1,3 +1,6 @@
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
 #include <errno.h>
 #include <unistd.h>
 #include <sys/types.h>
@@ -264,6 +267,112 @@ TEST(Process, ForkedChildDoubleStart)
   }
 }
 
+class ProcessValgrindWrapper : public ::testing::Test,
+                               public ::testing::WithParamInterface<std::string> {
+public:
+  virtual void SetUp() {
+    CheckForValgrind();
+  }
+
+  virtual void CheckForValgrind() {
+    Process valgrind;
+
+    /* check if valgrind actually exists */
+    valgrind.Start("valgrind", "--version", NULL);
+    int status;
+    ASSERT_EQ(waitpid(valgrind.Pid(), &status, 0), valgrind.Pid());
+    ASSERT_TRUE(WIFEXITED(status));
+    ASSERT_EQ(WEXITSTATUS(status), 0) << "valgrind failed to start\n";
+  }
+};
+
+TEST_P(ProcessValgrindWrapper, ValgrindWrapper)
+{
+  XORG_TESTCASE("Use the valgrind wrapper to start valgrind");
+
+  std::string executable = GetParam();
+
+  /* now set the env and fire up valgrind */
+  setenv("XORG_GTEST_USE_VALGRIND", executable.c_str(), 1);
+  Process p;
+  p.Start("ls", NULL);
+  unsetenv("XORG_GTEST_USE_VALGRIND");
+
+  /* Check /proc/<pid>/comm to make sure valgrind
+     was started. But comm takes a while to update, it's first our binary
+     then valgrind, then memcheck-amd64 (or whatever applies)
+  */
+  char buff[1024] = {0};
+  char fname[128];
+  sprintf(fname, "/proc/%d/comm", p.Pid());
+
+  do {
+    FILE *fp = fopen(fname, "r");
+    ASSERT_TRUE(fp);
+    fgets(buff, sizeof(buff), fp);
+    fclose(fp);
+  } while(strstr(buff, program_invocation_short_name));
+
+  if (executable.compare("valgrind") == 0)
+    ASSERT_TRUE(strstr(buff, "memcheck") || strstr(buff, "valgrind"));
+  else
+    ASSERT_TRUE(strstr(buff, executable.c_str()));
+}
+
+class ProcessValgrindArgsWrapper : public ProcessValgrindWrapper {};
+
+TEST_P(ProcessValgrindArgsWrapper, ValgrindWrapperWithArgs)
+{
+  XORG_TESTCASE("Use the valgrind wrapper with additional args to start valgrind");
+
+  std::string vargs = GetParam();
+  std::vector<std::string> valgrind_args;
+  char *all_args = strdup(vargs.c_str());
+  char *tok = strtok(all_args, " ");
+  while(tok) {
+    valgrind_args.push_back(std::string(tok));
+    tok = strtok(NULL, " ");
+  }
+  free(all_args);
+
+  /* now set the env and fire up valgrind */
+  setenv("XORG_GTEST_USE_VALGRIND", vargs.c_str(), 1);
+  Process p;
+  p.Start(TEST_ROOT_DIR "process-test-helper", NULL);
+  unsetenv("XORG_GTEST_USE_VALGRIND");
+
+  ASSERT_EQ(p.GetState(), Process::RUNNING);
+
+  char buff[1024] = {0};
+  char fname[128];
+  sprintf(fname, "/proc/%d/cmdline", p.Pid());
+
+  do {
+    FILE *fp = fopen(fname, "r");
+    ASSERT_TRUE(fp);
+    fgets(buff, sizeof(buff), fp);
+    fclose(fp);
+  } while(strstr(buff, program_invocation_short_name));
+
+  const char * arg = buff + strlen(buff) + 1;
+  std::vector<std::string>::const_iterator it = valgrind_args.begin();
+
+  it++; /* first one is "valgrind" */
+
+  while(strlen(arg) && it != valgrind_args.end()) {
+    ASSERT_EQ(it->compare(arg), 0);
+    arg += strlen(arg) + 1;
+    it++;
+  }
+
+  ASSERT_EQ(it, valgrind_args.end());
+  p.Kill(100);
+}
+
+INSTANTIATE_TEST_CASE_P(, ProcessValgrindWrapper, ::testing::Values("valgrind", "ls"));
+INSTANTIATE_TEST_CASE_P(, ProcessValgrindArgsWrapper,
+                        ::testing::Values("valgrind --leak-check=full", "valgrind -q --trace-children=yes", "valgrind "));
+
 int main(int argc, char *argv[]) {
   testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
-- 
1.7.11.7



More information about the xorg-devel mailing list