citrun

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

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:
MSCV/Project.pm | 8++++++--
ASCV/Viewer.pm | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/scv_global.h | 8+++++---
Minstrument/instrumenter.cpp | 2+-
Mruntime/runtime.c | 112++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
At/runtime_sanity.t | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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" );