commit 6dbe2f6b0541c5eef5efcf1bdd753858d86f4f0e
parent 02a8784d1047cd5dcfd493ebe48e9a4afe6de469
Author: Kyle Milz <kyle@0x30.net>
Date: Fri, 13 Oct 2017 11:40:34 -0700
lib: add initial support for starting a viewer
- add new lib_os.h interface that is implemented in os specific src files
- add new test for 2 error cases and 1 happy path case
Diffstat:
4 files changed, 102 insertions(+), 1 deletion(-)
diff --git a/lib/lib.c b/lib/lib.c
@@ -113,6 +113,7 @@ citrun_node_add(unsigned int major, unsigned int minor, struct citrun_node *n)
if (header == NULL) {
fd = citrun_open_fd();
header = citrun_add_header(fd);
+ citrun_start_viewer();
}
/* Allocate enough room for node and live execution buffers. */
diff --git a/lib/lib_os.h b/lib/lib_os.h
@@ -4,3 +4,4 @@
void *citrun_extend(int, size_t);
int citrun_open_fd();
void citrun_os_info(struct citrun_header *);
+void citrun_start_viewer();
diff --git a/lib/lib_unix.c b/lib/lib_unix.c
@@ -23,11 +23,12 @@
#include <limits.h> /* PATH_MAX */
#include <stdlib.h> /* get{env,progname} */
#include <string.h> /* strl{cpy,cat} */
-#include <unistd.h> /* lseek get{cwd,pid,ppid,pgrp} */
+#include <unistd.h> /* access, execlp, fork, lseek, get* */
#include "lib.h" /* struct citrun_header */
#include "lib_os.h"
+#define UNIX_PROCDIR "/tmp/citrun"
/*
* Extend the length of the file descriptor passed as the first argument, by a
@@ -115,3 +116,47 @@ citrun_os_info(struct citrun_header *h)
if (getcwd(h->cwd, sizeof(h->cwd)) == NULL)
strncpy(h->cwd, "", sizeof(h->cwd));
}
+
+/*
+ * Checks for the global citrun_gl lock file in the directory either given by the
+ * CITRUN_PROCDIR environment variable value or UNIX_PROCDIR if CITRUN_PROCDIR
+ * doesn't exist.
+ *
+ * If no lock file exists a new process is forked that tries to exec 'citrun_gl'
+ * which must be on the path.
+ *
+ * The instrumented program exits on failure and returns nothing on success.
+ */
+void
+citrun_start_viewer()
+{
+ pid_t pid;
+ const char *procdir;
+ char viewer_file[PATH_MAX];
+
+ if ((procdir = getenv("CITRUN_PROCDIR")) == NULL)
+ procdir = UNIX_PROCDIR;
+
+ strlcpy(viewer_file, procdir, PATH_MAX);
+ strlcat(viewer_file, "/", PATH_MAX);
+ strlcat(viewer_file, "citrun_gl.lock", PATH_MAX);
+
+ if (access(viewer_file, F_OK)) {
+ /* If errno was ENOENT then fall through otherwise error. */
+ if (errno != ENOENT)
+ err(1, "access");
+ } else
+ /* File already exists, don't create a new viewer. */
+ return;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+ else if (pid > 0)
+ /* In parent process. */
+ return;
+
+ /* In child process, exec the viewer. */
+ if (execlp("citrun_gl", "citrun_gl", NULL))
+ err(1, "exec citrun_gl");
+}
diff --git a/t/lib_viewer.t b/t/lib_viewer.t
@@ -0,0 +1,54 @@
+#
+# Check that the runtime starts the viewer if no 'citrun_gl.lock' file exists.
+#
+# Cases:
+# 1) not having `citrun_gl` on the PATH and citrun_gl.lock missing is ok
+# 2) no lock file and `citrun_gl` on the PATH is ok
+# 3) no viewer is executed when the lock file is present
+#
+use Modern::Perl;
+use t::utils; # os_compiler()
+
+plan tests => 11;
+
+
+my $wrap = Test::Cmd->new( prog => 'bin/citrun_wrap', workdir => '' );
+
+# Write and compile bare minimum source file.
+$wrap->write( 'main.c', 'int main(void) { return 0; }' );
+$wrap->run( args => os_compiler() . 'main main.c', chdir => $wrap->curdir );
+
+# Don't check stdout.
+is( $wrap->stderr, '', 'is wrapped compile stderr silent' );
+is( $? >> 8, 0, 'is wrapped compile exit code 0' );
+
+# Case 1.
+my $inst_prog = Test::Cmd->new( prog => $wrap->workdir . "/main", workdir => '' );
+
+$ENV{CITRUN_PROCDIR} = $inst_prog->workdir;
+my $err_good = 'main: exec citrun_gl: No such file or directory';
+
+$inst_prog->run( chdir => $inst_prog->curdir );
+is( $inst_prog->stdout, '', 'is case 1 stdout silent' );
+like( $inst_prog->stderr, qr/$err_good/, 'is case 1 stderr an error' );
+is( $? >> 8, 0, 'is case 1 exit code 0' );
+
+# Case 2.
+$inst_prog->write( 'citrun_gl', <<EOF );
+#!/bin/sh
+echo ran citrun_gl
+EOF
+chmod(0775, $inst_prog->workdir . '/citrun_gl') or die $!;
+
+$inst_prog->run( chdir => $wrap->curdir );
+like( $inst_prog->stdout, qr/ran citrun_gl/, 'is case 2 viewer started' );
+is( $inst_prog->stderr, '', 'is case 2 stderr empty' );
+is( $? >> 8, 0, 'is case 2 exit code zero' );
+
+# Case 3.
+$inst_prog->write( $ENV{CITRUN_PROCDIR} . '/citrun_gl.lock', '' );
+$inst_prog->run( chdir => $wrap->curdir );
+
+is( $inst_prog->stdout, '', 'is case 3 stdout silent' );
+is( $inst_prog->stderr, '', 'is case 3 stderr silent' );
+is( $? >> 8, 0, 'is case 3 exit code zero' );