citrun

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

commit 8187625a12e3f8d19484a0cd9de55b788c399596
parent 683443e278ae87ff04c5971685fc385aca52690e
Author: Kyle Milz <kyle@0x30.net>
Date:   Wed, 22 Jun 2016 18:25:20 -0600

change the way linking works, again

- now add a static constructor per primary source file
- this constructors job is to tell the runtime about the instrumentation struct
  also located in every primary source file

Diffstat:
MTest/Project.pm | 4++--
Mlib/runtime.c | 57++++++++++++++++++++++++++++++++++++++-------------------
Mlib/runtime.h | 2++
Msrc/inst_action.cc | 35++++++++---------------------------
Msrc/inst_ast_visitor.cc | 14--------------
Msrc/inst_main.cc | 46----------------------------------------------
Msrc/runtime_h.h | 2++
Mt/fibonacci.t | 4++--
Mt/for.t | 2+-
Mt/hello_world.t | 2+-
Mt/if.t | 2+-
Mt/inst_preamble.t | 9+++++++--
Mt/return.t | 4++--
Mt/runtime_sanity.t | 12++++++------
Mt/switch.t | 2+-
Mt/while.t | 2+-
16 files changed, 74 insertions(+), 125 deletions(-)

diff --git a/Test/Project.pm b/Test/Project.pm @@ -65,7 +65,7 @@ sub instrumented_src { open( my $inst_fh, "<", "$self->{tmp_dir}/source_0.c" ); # Knock off the instrumentation preamble - my $line = <$inst_fh> for (1..23); + my $line = <$inst_fh> for (1..28); my $inst_src; while (my $line = <$inst_fh>) { @@ -81,7 +81,7 @@ sub inst_src_preamble { open( my $inst_fh, "<", "$self->{tmp_dir}/source_0.c" ); my $preamble; - for (1..23) { + for (1..28) { my $line = <$inst_fh>; $preamble .= $line; } diff --git a/lib/runtime.c b/lib/runtime.c @@ -1,5 +1,5 @@ #include <assert.h> -#include <err.h> /* err, errx, warnx */ +#include <err.h> /* err, errx, warn */ #include <limits.h> /* PATH_MAX */ #include <pthread.h> /* pthread_create */ #include <stdlib.h> /* getenv */ @@ -15,12 +15,22 @@ #include "runtime.h" -/* Entrance into instrumented application. */ -extern struct citrun_node *citrun_nodes[]; -extern uint64_t citrun_nodes_total; +static struct citrun_node *citrun_nodes_head; +static struct citrun_node *citrun_nodes_tail; -/* Make sure instrumented programs rely on this library in some way. */ -int needs_to_link_against_libcitrun; +void +citrun_node_add(struct citrun_node *node) +{ + if (citrun_nodes_head == NULL) { + assert(citrun_nodes_tail == NULL); + citrun_nodes_head = node; + citrun_nodes_tail = node; + return; + } + + citrun_nodes_tail->next = node; + citrun_nodes_tail = node; +} static int xread(int d, const void *buf, size_t bytes_total) @@ -65,16 +75,23 @@ xwrite(int d, const void *buf, size_t bytes_total) return bytes_wrote; } + /* Walk the node array and send all of the static metadata information. */ static void send_metadata(int fd) { - struct citrun_node walk; + struct citrun_node *walk = citrun_nodes_head; + struct citrun_node node; + uint64_t citrun_nodes_total = 0; pid_t pids[3]; size_t file_name_sz; int i; /* Send the total number of instrumented nodes. */ + while (walk != NULL) { + walk = walk->next; + ++citrun_nodes_total; + } xwrite(fd, &citrun_nodes_total, sizeof(citrun_nodes_total)); /* Send process id, parent process id, group process id. */ @@ -87,21 +104,24 @@ send_metadata(int fd) xwrite(fd, &pids[i], sizeof(pid_t)); /* Send instrumented object file information. */ - for (i = 0; i < citrun_nodes_total; i++) { - walk = *citrun_nodes[i]; + walk = citrun_nodes_head; + while (walk != NULL) { + node = *walk; /* Length of the original source file name. */ - file_name_sz = strnlen(walk.file_name, PATH_MAX); + file_name_sz = strnlen(node.file_name, PATH_MAX); xwrite(fd, &file_name_sz, sizeof(file_name_sz)); /* The original source file name. */ - xwrite(fd, walk.file_name, file_name_sz); + xwrite(fd, node.file_name, file_name_sz); /* Size of the execution counters. */ - xwrite(fd, &walk.size, sizeof(walk.size)); + xwrite(fd, &node.size, sizeof(node.size)); /* Number of instrumentation sites. */ - xwrite(fd, &walk.inst_sites, sizeof(walk.size)); + xwrite(fd, &node.inst_sites, sizeof(node.size)); + + walk = walk->next; } } @@ -112,14 +132,13 @@ send_metadata(int fd) static void send_execution_data(int fd) { - struct citrun_node walk; + struct citrun_node *walk = citrun_nodes_head; int i; - for (i = 0; i < citrun_nodes_total; i++) { - walk = *citrun_nodes[i]; - + while (walk != NULL) { /* Write execution buffer, one 8 byte counter per source line */ - xwrite(fd, walk.lines_ptr, walk.size * sizeof(uint64_t)); + xwrite(fd, walk->lines_ptr, walk->size * sizeof(uint64_t)); + walk = walk->next; } } @@ -163,7 +182,7 @@ control_thread(void *arg) } } -/* Grab an execution context and start up the control thread. */ +/* Grab an execution context and start up the control thread. */ __attribute__((constructor)) static void runtime_init() { diff --git a/lib/runtime.h b/lib/runtime.h @@ -4,4 +4,6 @@ struct citrun_node { uint32_t size; uint32_t inst_sites; const char *file_name; + struct citrun_node *next; }; +void citrun_node_add(struct citrun_node *); diff --git a/src/inst_action.cc b/src/inst_action.cc @@ -34,26 +34,6 @@ InstrumentAction::CreateASTConsumer(clang::CompilerInstance &CI, clang::StringRe #endif } -std::string -get_current_node(std::string const &file_path) -{ - size_t last_slash = file_path.find_last_of('/'); - std::string fn(file_path.substr(last_slash + 1)); - - std::replace(fn.begin(), fn.end(), '-', '_'); - - size_t period = fn.find_first_of('.'); - return fn.substr(0, period); -} - -void -append_curr_node(std::string const &curr_node) -{ - // Append current primary source file to INSTRUMENTED list. - std::ofstream inst_ofstream("INSTRUMENTED", std::ofstream::app); - inst_ofstream << curr_node << std::endl; -} - void InstrumentAction::EndSourceFileAction() { @@ -68,10 +48,8 @@ InstrumentAction::EndSourceFileAction() unsigned int num_lines = sm.getPresumedLineNumber(end); std::string const file_name = getCurrentFile(); - std::string const curr_node = get_current_node(file_name); - append_curr_node(curr_node); - std::stringstream ss; + // Add preprocessor stuff so that the C runtime library links against // C++ object code. ss << "#ifdef __cplusplus" << std::endl; @@ -81,22 +59,25 @@ InstrumentAction::EndSourceFileAction() // Embed the header directly in the primary source file. ss << runtime_h << std::endl; - ss << "extern int needs_to_link_against_libcitrun;" << std::endl; - - // Define storage for coverage data + // Execution data needs to be big because it only increments. ss << "static uint64_t _citrun_lines[" << num_lines << "];" << std::endl; // Keep track of how many sites we instrumented. int rw_count = InstrumentASTConsumer->get_visitor().GetRewriteCount(); // Define this translation units main book keeping data structure - ss << "struct citrun_node citrun_node_" << curr_node << " = {" << std::endl + ss << "static struct citrun_node _citrun_node = {" << std::endl << " .lines_ptr = _citrun_lines," << std::endl << " .size = " << num_lines << "," << std::endl << " .inst_sites = " << rw_count << "," << std::endl << " .file_name = \"" << file_name << "\"," << std::endl; ss << "};" << std::endl; + ss << "__attribute__((constructor))" << std::endl + << "static void citrun_constructor() {" << std::endl + << " citrun_node_add(&_citrun_node);" << std::endl + << "}" << std::endl; + // Close extern "C" { ss << "#ifdef __cplusplus" << std::endl; ss << "}" << std::endl; diff --git a/src/inst_ast_visitor.cc b/src/inst_ast_visitor.cc @@ -62,20 +62,6 @@ RewriteASTVisitor::VisitStmt(clang::Stmt *s) bool RewriteASTVisitor::VisitFunctionDecl(clang::FunctionDecl *f) { - // Only function definitions (with bodies), not declarations. - if (f->hasBody() == 0) - return true; - - // We need to depend directly on a symbol provided by libcitrun, - // otherwise the library will get discarded at link time. - std::stringstream ss; - ss << "needs_to_link_against_libcitrun = 0;"; - - clang::Stmt *FuncBody = f->getBody(); - clang::SourceLocation curly_brace(FuncBody->getLocStart().getLocWithOffset(1)); - - TheRewriter.InsertTextBefore(curly_brace, ss.str()); - return true; } diff --git a/src/inst_main.cc b/src/inst_main.cc @@ -149,52 +149,6 @@ restore_original_src(std::map<std::string, std::string> const &temp_file_map) void patch_link_command(std::vector<char *> &args) { - std::string inst_files_list("INSTRUMENTED"); - - if (access(inst_files_list.c_str(), F_OK)) { - warnx("No instrumented object files found."); - if (execvp(args[0], &args[0])) - err(1, "execvp"); - } - - // std::cerr << "Link detected. Arguments are:" << std::endl; - // for (auto &arg : args) - // std::cerr << " '" << arg << "', " << std::endl; - - std::vector<std::string> instrumented_files; - std::ifstream inst_files_ifstream(inst_files_list); - - std::string temp_line; - while (std::getline(inst_files_ifstream, temp_line)) - instrumented_files.push_back(temp_line); - - inst_files_ifstream.close(); - - // std::cerr << "Instrumented object files are:" << std::endl; - // for (auto &line : instrumented_files) - // std::cerr << " '" << line << "', " << std::endl; - - std::ofstream patch_ofstream("citrun_patch.c"); - - // Inject the runtime header. - patch_ofstream << runtime_h << std::endl; - - for (auto &line : instrumented_files) - patch_ofstream << "extern struct citrun_node citrun_node_" << line << ";" << std::endl; - - int num_tus = instrumented_files.size(); - patch_ofstream << "struct citrun_node *citrun_nodes["; - patch_ofstream << num_tus << "] = {" << std::endl; - - for (auto &line : instrumented_files) - patch_ofstream << "\t&citrun_node_" << line << ", " << std::endl; - patch_ofstream << "};" << std::endl; - - patch_ofstream << "uint64_t citrun_nodes_total = " << num_tus << ";" << std::endl; - patch_ofstream.close(); - - args.push_back(const_cast<char *>("citrun_patch.c")); - // libcitrun.a needs pthread but is static and doesn't include it itself args.push_back(const_cast<char *>("-pthread")); diff --git a/src/runtime_h.h b/src/runtime_h.h @@ -6,5 +6,7 @@ static const char runtime_h[] = " uint32_t size;\n" " uint32_t inst_sites;\n" " const char *file_name;\n" +" struct citrun_node *next;\n" "};\n" +"void citrun_node_add(struct citrun_node *);\n" ; diff --git a/t/fibonacci.t b/t/fibonacci.t @@ -50,7 +50,7 @@ my $inst_src_good = <<EOF; long long fibonacci(long long n) -{needs_to_link_against_libcitrun = 0; +{ if ((++_citrun_lines[7], n == 0)) return (++_citrun_lines[8], 0); else if ((++_citrun_lines[9], n == 1)) @@ -61,7 +61,7 @@ fibonacci(long long n) int main(int argc, char *argv[]) -{needs_to_link_against_libcitrun = 0; +{ long long n; if ((++_citrun_lines[20], argc != 2)) { diff --git a/t/for.t b/t/for.t @@ -28,7 +28,7 @@ $project->compile(); my $inst_src_good = <<EOF; int main(void) -{needs_to_link_against_libcitrun = 0; +{ int i; for (i = 0; (++_citrun_lines[6], i < 19); i++) { diff --git a/t/hello_world.t b/t/hello_world.t @@ -27,7 +27,7 @@ my $inst_src_good = <<EOF; int main(void) -{needs_to_link_against_libcitrun = 0; +{ (++_citrun_lines[6], printf("hello, world!")); return (++_citrun_lines[7], 0); } diff --git a/t/if.t b/t/if.t @@ -38,7 +38,7 @@ my $inst_src_good = <<EOF; int main(int argc, char *argv[]) -{needs_to_link_against_libcitrun = 0; +{ if ((++_citrun_lines[6], argc == 1)) return (++_citrun_lines[7], 1); else diff --git a/t/inst_preamble.t b/t/inst_preamble.t @@ -32,16 +32,21 @@ struct citrun_node { uint32_t size; uint32_t inst_sites; const char *file_name; + struct citrun_node *next; }; +void citrun_node_add(struct citrun_node *); -extern int needs_to_link_against_libcitrun; static uint64_t _citrun_lines[6]; -struct citrun_node citrun_node_source_0 = { +static struct citrun_node _citrun_node = { .lines_ptr = _citrun_lines, .size = 6, .inst_sites = 1, .file_name = "$tmp_dir/source_0.c", }; +__attribute__((constructor)) +static void citrun_constructor() { + citrun_node_add(&_citrun_node); +} #ifdef __cplusplus } #endif diff --git a/t/return.t b/t/return.t @@ -27,11 +27,11 @@ EOF $project->compile(); my $inst_src_good = <<EOF; -int foo() {needs_to_link_against_libcitrun = 0; +int foo() { return (++_citrun_lines[2], 0); } -int main(void) {needs_to_link_against_libcitrun = 0; +int main(void) { return (++_citrun_lines[6], 10); return (++_citrun_lines[8], 10 + 10); diff --git a/t/runtime_sanity.t b/t/runtime_sanity.t @@ -67,22 +67,22 @@ my $runtime_metadata = $viewer->get_metadata(); my $tus = $runtime_metadata->{tus}; my ($source_0, $source_1, $source_2) = @$tus; -like( $source_0->{filename}, qr/.*source_0.c/, "runtime filename check 0" ); -is( $source_0->{lines}, 20, "runtime line count check 0" ); +like( $source_0->{filename}, qr/.*source_2.c/, "runtime filename check 0" ); +is( $source_0->{lines}, 9, "runtime line count check 0" ); #is( $source_0->{inst_sites}, 7, "instrumented site count 0" ); like( $source_1->{filename}, qr/.*source_1.c/, "runtime filename check 1" ); is( $source_1->{lines}, 11, "runtime line count check 1" ); #is( $source_1->{inst_sites}, 7, "instrumented site count 1" ); -like( $source_2->{filename}, qr/.*source_2.c/, "runtime filename check 2" ); -is( $source_2->{lines}, 9, "runtime line count check 2" ); +like( $source_2->{filename}, qr/.*source_0.c/, "runtime filename check 2" ); +is( $source_2->{lines}, 20, "runtime line count check 2" ); #is( $source_2->{inst_sites}, 6, "instrumented site count 2" ); # Request and check execution data my $data = $viewer->get_execution_data($tus); -my @lines = @{ $data->[0] }; +my @lines = @{ $data->[2] }; is ( $lines[$_], 0, "src 0 line $_ check" ) for (1..11); is ( $lines[12], 1, "src 0 line 14 check" ); is ( $lines[$_], 0, "src 0 line $_ check" ) for (13..14); @@ -96,7 +96,7 @@ is ( $lines[$_], 0, "src 1 line $_ check" ) for (0..3); cmp_ok ( $lines[$_], ">", 10, "src 1 line $_ check" ) for (4..7); is ( $lines[8], 0, "src 1 line 8 check" ); -my @lines = @{ $data->[2] }; +my @lines = @{ $data->[0] }; is ( $lines[$_], 0, "src 2 line $_ check" ) for (0..8); $project->kill(); diff --git a/t/switch.t b/t/switch.t @@ -32,7 +32,7 @@ $project->compile(); my $inst_src_good = <<EOF; int main(void) -{needs_to_link_against_libcitrun = 0; +{ int i; switch ((++_citrun_lines[6], i)) { diff --git a/t/while.t b/t/while.t @@ -30,7 +30,7 @@ $project->compile(); my $inst_src_good = <<EOF; int main(void) -{needs_to_link_against_libcitrun = 0; +{ int i; i = 0;