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:
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;