citrun

watch C/C++ source code execute
Log | Files | Refs | LICENSE

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:
Mlib/lib.c | 1+
Mlib/lib_os.h | 1+
Mlib/lib_unix.c | 47++++++++++++++++++++++++++++++++++++++++++++++-
At/lib_viewer.t | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
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' );