commit 42d625e6fd7be18f6d2bda9477a18728d384f8fe
parent 214bd113298b0a1c674e76951cffb1c20aa7ec79
Author: kyle <kyle@getaddrinfo.net>
Date: Thu, 10 Mar 2016 20:41:07 -0700
add socket read/write to runtime
- runtime now listens for messages from server
- when it gets message type 0, it walks the tu's and writes their current line
execution counts to the socket
- also add support in the testsuite for this
- runtime will exit if it can't connect to a viewer
- also add new test that gets execution counts and makes sure the lines make
sense
Diffstat:
6 files changed, 249 insertions(+), 38 deletions(-)
diff --git a/SCV/Project.pm b/SCV/Project.pm
@@ -76,11 +76,15 @@ sub instrumented_src {
sub run {
my ($self, @args) = @_;
+
my $tmp_dir = $self->{tmp_dir};
+ $self->{pid} = open3(undef, undef, \*CHLD_ERR, "$tmp_dir/program", @args);
+}
- my $pid = open3(undef, undef, \*CHLD_ERR, "$tmp_dir/program", @args);
+sub wait {
+ my ($self) = @_;
- waitpid( $pid, 0 );
+ waitpid( $self->{pid}, 0 );
my $real_ret = $? >> 8;
my $stderr;
diff --git a/SCV/Viewer.pm b/SCV/Viewer.pm
@@ -0,0 +1,77 @@
+package SCV::Viewer;
+use strict;
+
+use IO::Socket::UNIX;
+use Test;
+
+sub new {
+ my ($class, $tmp_dir) = @_;
+ my $self = {};
+ bless ($self, $class);
+
+ my $viewer_socket = IO::Socket::UNIX->new(
+ Type => SOCK_STREAM(),
+ Local => "/tmp/viewer_test.socket",
+ Listen => 1,
+ );
+ die "socket error: $!\n" unless ($viewer_socket);
+
+ $self->{viewer_socket} = $viewer_socket;
+ return $self;
+}
+
+sub accept {
+ my ($self) = @_;
+
+ my $socket = $self->{viewer_socket};
+ $self->{client_socket} = $socket->accept();
+
+ print STDERR "accept(): accepted client\n";
+}
+
+sub request_data {
+ my ($self) = @_;
+ my $client = $self->{client_socket};
+
+ $client->syswrite("\x00", 1);
+
+ my $buf = read_all($client, 8);
+ my $file_name_sz = unpack("Q", $buf);
+
+ my $file_name = read_all($client, $file_name_sz);
+
+ $buf = read_all($client, 8);
+ my $num_lines = unpack("Q", $buf);
+
+ $buf = read_all($client, 8 * $num_lines);
+ my @data = unpack("Q$num_lines", $buf);
+
+ return ({ file_name => $file_name, data => \@data });
+}
+
+sub read_all {
+ my ($sock, $bytes_total) = @_;
+
+ my $data;
+ my $bytes_read = 0;
+ while ($bytes_total > 0) {
+ my $read = $sock->sysread($data, $bytes_total, $bytes_read);
+
+ die "error: read failed: $!" if (!defined $read);
+ die "disconnected!\n" if ($read == 0);
+
+ $bytes_total -= $read;
+ $bytes_read += $read;
+ }
+
+ return $data;
+}
+
+sub DESTROY {
+ my ($self) = @_;
+
+ close($self->{viewer_socket});
+ unlink "/tmp/viewer_test.socket";
+}
+
+1;
diff --git a/include/scv_global.h b/include/scv_global.h
@@ -1,7 +1,9 @@
+#include <stdint.h>
+
struct scv_node {
- unsigned int *lines_ptr;
- unsigned int size;
+ /* long long in C99 is also guaranteed to be 64 bits */
+ uint64_t *lines_ptr;
+ uint64_t size;
const char *file_name;
struct scv_node *next;
- /* unsigned int not_end; */
};
diff --git a/instrument/instrumenter.cpp b/instrument/instrumenter.cpp
@@ -180,7 +180,7 @@ MyFrontendAction::EndSourceFileAction()
ss << "#include <scv_global.h>" << std::endl;
// Define storage for coverage data
- ss << "static unsigned int lines[" << num_lines << "];" << std::endl;
+ ss << "static uint64_t lines[" << num_lines << "];" << std::endl;
// Always declare this. The next TU will overwrite this or there won't
// be a next TU.
diff --git a/runtime/runtime.c b/runtime/runtime.c
@@ -7,29 +7,11 @@
#include <scv_global.h>
-/* Entry point into an instrumented application */
+/* Entry point into instrumented application */
extern struct scv_node node0;
-void
-walk_nodes()
-{
- int i;
-
- fprintf(stderr, "%s: alive", __func__);
-
- /* Copy node0, don't use it directly */
- struct scv_node temp = node0;
- while (temp.size != 0) {
- printf("filename = %s\n", temp.file_name);
- printf("size = %u\n", temp.size);
- for (i = 0; i < temp.size; i++) {
- fprintf(stderr, " ln %i = %u\n", i, temp.lines_ptr[i]);
- }
- temp = *temp.next;
- }
+void walk_nodes(int);
- fprintf(stderr, "%s: done", __func__);
-}
void *
control_thread(void *arg)
@@ -46,23 +28,20 @@ control_thread(void *arg)
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, "/tmp/viewer_test.socket", sizeof(addr.sun_path) - 1);
- // fprintf(stderr, "%s: initialized\n", __func__);
-
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr))) {
- // warn("connect");
+ err(1, "connect");
return 0;
}
- uint8_t version;
- uint8_t msg_type;
- read(fd, version, 1);
- read(fd, msg_type, 1);
-
- if (version != 0)
- err(1, "version != 0");
+ while (1) {
+ uint8_t msg_type;
+ xread(fd, &msg_type, 1);
- if (msg_type == 0)
- walk_nodes();
+ if (msg_type == 0)
+ walk_nodes(fd);
+ else
+ errx(1, "unknown message type %i", msg_type);
+ }
}
__attribute__((constructor))
@@ -71,3 +50,72 @@ static void runtime_init()
pthread_t tid;
pthread_create(&tid, NULL, control_thread, NULL);
}
+
+void
+walk_nodes(int fd)
+{
+ size_t file_name_sz;
+
+ /* Copy node0, don't use it directly */
+ struct scv_node walk = node0;
+ while (walk.size != 0) {
+ file_name_sz = strnlen(walk.file_name, PATH_MAX);
+
+ /* Send file name size and then the file name itself. */
+ xwrite(fd, &file_name_sz, sizeof(file_name_sz));
+ xwrite(fd, walk.file_name, file_name_sz);
+
+ /* Send the contents of the coverage buffer */
+ xwrite(fd, &walk.size, sizeof(uint64_t));
+ xwrite(fd, walk.lines_ptr, walk.size * sizeof(uint64_t));
+
+ walk = *walk.next;
+ }
+}
+
+int
+xwrite(int d, const void *buf, size_t bytes_total)
+{
+ int bytes_left;
+ int bytes_wrote;
+ ssize_t n;
+
+ bytes_left = bytes_total;
+ bytes_wrote = 0;
+ while (bytes_left > 0) {
+ n = write(d, buf + bytes_wrote, bytes_left);
+
+ if (n < 0)
+ err(1, "write()");
+
+ bytes_wrote += n;
+ bytes_left -= n;
+ }
+
+ return bytes_wrote;
+}
+
+int
+xread(int d, void *buf, size_t bytes_total)
+{
+ ssize_t bytes_left;
+ size_t bytes_read;
+ ssize_t n;
+
+ bytes_left = bytes_total;
+ bytes_read = 0;
+ while (bytes_left > 0) {
+ n = read(d, buf + bytes_read, bytes_left);
+
+ /* Disconnect */
+ if (n == 0)
+ err(1, "read 0 bytes on socket");
+ if (n < 0)
+ err(1, "read()");
+
+ bytes_read += n;
+ bytes_left -= n;
+ }
+
+ return bytes_read;
+}
diff --git a/t/runtime_sanity.t b/t/runtime_sanity.t
@@ -0,0 +1,80 @@
+use strict;
+use SCV::Project;
+use SCV::Viewer;
+use Test::More tests => 37;
+use Test::Differences;
+use Time::HiRes qw( usleep );
+
+my $project = SCV::Project->new();
+my $viewer = SCV::Viewer->new();
+unified_diff;
+
+$project->add_src(
+<<EOF
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+long long
+fib(long long n)
+{
+ if (n == 0)
+ return 0;
+ else if (n == 1)
+ return 1;
+
+ return fib(n - 1) + fib(n - 2);
+}
+
+int
+main(int argc, char *argv[])
+{
+ long long n;
+ const char *errstr = NULL;
+
+ if (argc != 2)
+ errx(1, "argc != 2");
+
+ n = strtonum(argv[1], LONG_MIN, LONG_MAX, &errstr);
+ if (errstr)
+ err(1, "%s", errstr);
+
+ fprintf(stderr, "%lli", fib(n));
+ return 0;
+}
+EOF
+);
+
+# Compile the above inefficient program and have it compute the input 40, which
+# takes a few seconds
+$project->compile();
+$project->run(40);
+
+# Accept the runtime's connection
+$viewer->accept();
+
+usleep(100 * 1000);
+my $data = $viewer->request_data();
+
+like ($data->{file_name}, qr/tmp\/.*source_0\.c/, "runtime filename check");
+my @lines = @{ $data->{data} };
+
+# Check the line counts are something reasonable
+is (scalar(@lines), 33, "runtime lines count");
+is ( $lines[$_], 0, "line $_ check" ) for (0..8);
+cmp_ok ( $lines[$_], ">", 0, "line $_ check" ) for (9..12);
+is ( $lines[13], 0, "line 13 check" );
+cmp_ok ( $lines[14], ">", 0, "line 14 check" );
+is ( $lines[$_], 0, "line $_ check" ) for (15..22);
+is ( $lines[23], 1, "line 23 check" );
+is ( $lines[$_], 0, "line $_ check" ) for (24..25);
+is ( $lines[$_], 1, "line $_ check" ) for (26..27);
+is ( $lines[$_], 0, "line $_ check" ) for (28..29);
+is ( $lines[30], 2, "line 30 check" );
+# Make sure return code hasn't fired yet
+is ( $lines[$_], 0, "line $_ check" ) for (31..32);
+
+my ($ret, $err) = $project->wait();
+is( $ret, 0, "runtime sanity return code check" );
+is( $err, "102334155", "runtime sanity program output" );