citrun

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

commit c31eeb2c4387cd35a0a75dabf87bffd225ade36f
parent 79b129ef21726edee03e003bdbf5eba0592ef494
Author: Kyle Milz <kyle@0x30.net>
Date:   Sun,  4 Dec 2016 19:43:48 -0700

src: move most of inst_main.cc into inst_frontend.cc

Diffstat:
Mman/citrun-check.1 | 2--
Msrc/check.sh | 22+++++++++-------------
Msrc/inst_action.cc | 14+++++++-------
Msrc/inst_action.h | 4++--
Msrc/inst_frontend.cc | 146++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/inst_frontend.h | 14++++++++------
Msrc/inst_log.h | 18++++++++++++------
Msrc/inst_main.cc | 116+++----------------------------------------------------------------------------
Mt/inst_fail.sh | 1-
Mt/inst_log.sh | 2+-
10 files changed, 158 insertions(+), 181 deletions(-)

diff --git a/man/citrun-check.1 b/man/citrun-check.1 @@ -74,8 +74,6 @@ The following table lists all possible events and describes them: The total number of C/C++ source files that had rewriting attempted on them. .It Qq Application link commands Number of link commands detected and modified to include the runtime. -.It Qq Rewrite parse {warnings,errors} -The total number of parsing problems the rewriter had. .It Qq Rewrite successes The number of times the rewriter successfully transformed the entire source file. diff --git a/src/check.sh b/src/check.sh @@ -45,12 +45,10 @@ fi find $@ -name citrun.log -print0 | xargs -0 awk ' $0~/Found source file/ { summary[0] += 1 } $0~/Link detected/ { summary[1] += 1 } -$0~/warning:/ { summary[2] += 1 } -$0~/error:/ { summary[3] += 1 } -$0~/Rewriting successful/ { summary[4] += 1 } -$0~/Rewriting failed/ { summary[5] += 1 } -$0~/Rewritten source compile successful/ { summary[6] += 1 } -$0~/Rewritten source compile failed/ { summary[7] += 1 } +$0~/Rewriting successful/ { summary[2] += 1 } +$0~/Rewriting failed/ { summary[3] += 1 } +$0~/Rewritten source compile successful/ { summary[4] += 1 } +$0~/Rewritten source compile failed/ { summary[5] += 1 } $0~/Lines of source code/ { totals[0] += $2 } $0~/Milliseconds spent rewriting source/ { totals[1] += $2 } @@ -69,15 +67,13 @@ $0~/Errors rewriting source/ { totals[12] += $2 } END { summary_desc[0] = "Source files used as input" summary_desc[1] = "Application link commands" - summary_desc[2] = "Rewrite parse warnings" - summary_desc[3] = "Rewrite parse errors" - summary_desc[4] = "Rewrite successes" - summary_desc[5] = "Rewrite failures" - summary_desc[6] = "Rewritten source compile successes" - summary_desc[7] = "Rewritten source compile failures" + summary_desc[2] = "Rewrite successes" + summary_desc[3] = "Rewrite failures" + summary_desc[4] = "Rewritten source compile successes" + summary_desc[5] = "Rewritten source compile failures" print "Summary:" - for (i = 0; i < 8; i++) { + for (i = 0; i < 6; i++) { if (i != 0 && summary[i] == 0) continue printf "%10i %s\n", summary[i], summary_desc[i] } diff --git a/src/inst_action.cc b/src/inst_action.cc @@ -43,20 +43,20 @@ InstrumentAction::write_modified_src(clang::FileID const &fid) if (m_is_citruninst) { out_file += ".citrun"; - *m_log << "Writing modified source to '" << out_file << "'" << std::endl; + m_log << "Writing modified source to '" << out_file << "'" << std::endl; } std::error_code ec; llvm::raw_fd_ostream output(out_file, ec, llvm::sys::fs::F_None); if (ec.value()) { - *m_log << "Error writing modified source '" << out_file + m_log << "Error writing modified source '" << out_file << "': " << ec.message() << std::endl; return; } // Write the instrumented source file m_TheRewriter.getEditBuffer(fid).write(output); - *m_log << "Modified source written successfully." << std::endl; + m_log << "Modified source written successfully." << std::endl; } void @@ -95,12 +95,12 @@ InstrumentAction::EndSourceFileAction() clang::SourceLocation start = sm.getLocForStartOfFile(main_fid); if (m_TheRewriter.InsertTextAfter(start, preamble.str())) { - *m_log << "Failed to insert the instrumentation preabmle."; + m_log << "Failed to insert the instrumentation preabmle."; return; } - *m_log << "Instrumentation of '" << m_compiler_file_name << "' finished:" << std::endl; - *m_log << " " << num_lines << " Lines of source code" << std::endl; + m_log << "Instrumentation of '" << m_compiler_file_name << "' finished:" << std::endl; + m_log << " " << num_lines << " Lines of source code" << std::endl; // // Write out statistics from the AST visitor. @@ -109,7 +109,7 @@ InstrumentAction::EndSourceFileAction() for (int i = 0; i < NCOUNTERS; ++i) { if (v.m_counters[i] == 0) continue; - *m_log << " " << v.m_counters[i] << " " + m_log << " " << v.m_counters[i] << " " << v.m_counter_descr[i] << std::endl; } diff --git a/src/inst_action.h b/src/inst_action.h @@ -30,7 +30,7 @@ private: // For each source file provided to the tool, a new FrontendAction is created. class InstrumentAction : public clang::ASTFrontendAction { public: - InstrumentAction(InstrumentLogger *log, bool citruninst, + InstrumentAction(InstrumentLogger &log, bool citruninst, std::string const &filename) : m_log(log), m_is_citruninst(citruninst), @@ -45,7 +45,7 @@ private: clang::Rewriter m_TheRewriter; RewriteASTConsumer *m_InstrumentASTConsumer; - InstrumentLogger *m_log; + InstrumentLogger& m_log; bool m_is_citruninst; std::string m_compiler_file_name; }; diff --git a/src/inst_frontend.cc b/src/inst_frontend.cc @@ -14,12 +14,14 @@ // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // #include "inst_frontend.h" +#include "version.h" // citrun_major, citrun_minor #include <sys/stat.h> // stat #include <sys/time.h> // utimes +#include <sys/utsname.h> // uname #include <sys/wait.h> // waitpid -#include <clang/Frontend/TextDiagnosticPrinter.h> +#include <clang/Frontend/TextDiagnosticBuffer.h> #include <clang/Tooling/CommonOptionsParser.h> #include <clang/Tooling/Tooling.h> #include <cstdio> // tmpnam @@ -32,14 +34,87 @@ static llvm::cl::OptionCategory ToolingCategory("citrun-inst options"); -InstrumentFrontend::InstrumentFrontend(int argc, char *argv[], - InstrumentLogger *l, bool is_citruninst) : +InstrumentFrontend::InstrumentFrontend(int argc, char *argv[]) : m_args(argv, argv + argc), - m_log(l), - m_is_citruninst(is_citruninst) + m_is_citruninst(false), + m_start_time(std::chrono::high_resolution_clock::now()) { + + char *base_name; + if ((base_name = basename(m_args[0])) == NULL) + err(1, "basename"); + + if (std::strcmp(base_name, "citrun-inst") == 0) + m_is_citruninst = true; + + if (m_is_citruninst) + m_log.set_citruninst(); + + m_log << "citrun-inst " << citrun_major << "." << citrun_minor << " "; + + struct utsname utsname; + if (uname(&utsname) == -1) + m_log << "(Unknown OS)"; + else { + m_log << "(" << utsname.sysname << "-" << utsname.release << " " + << utsname.machine << ")"; + } + m_log << " '" << CITRUN_SHARE << "'" << std::endl; + + if (m_is_citruninst) { + m_log << ">> Welcome to C It Run! Have a nice day." << std::endl; + } else { + // There's extra work to do if we're not running as citrun-inst. + m_log << "Tool called as '" << m_args[0] << "'"; + if (std::strcmp(base_name, m_args[0]) != 0) { + m_log << ", changing to '" << base_name << "'"; + m_args[0] = base_name; + } + m_log << std::endl; + + setprogname("citrun-inst"); + clean_PATH(); + } } +void +InstrumentFrontend::clean_PATH() +{ + char *path; + if ((path = std::getenv("PATH")) == NULL) + errx(1, "Error: PATH is not set."); + + m_log << "PATH='" << path << "'" << std::endl; + + // Filter CITRUN_SHARE out of PATH + std::stringstream path_ss(path); + std::ostringstream new_path; + std::string component; + bool first_component = 1; + bool found_citrun_path = 0; + + while (std::getline(path_ss, component, ':')) { + if (component.compare(CITRUN_SHARE) == 0) { + found_citrun_path = 1; + continue; + } + + if (first_component == 0) + new_path << ":"; + + // It wasn't CITRUN_SHARE, keep it + new_path << component; + first_component = 0; + } + + if (!found_citrun_path) + errx(1, "Error: CITRUN_SHARE not in PATH."); + + if (setenv("PATH", new_path.str().c_str(), 1)) + err(1, "setenv"); +} + + // Returns true if value ends with suffix, false otherwise. static bool ends_with(std::string const &value, std::string const &suffix) @@ -87,7 +162,7 @@ InstrumentFrontend::save_if_srcfile(char *arg) return; m_source_files.push_back(arg); - *m_log << "Found source file '" << arg << "'" << std::endl; + m_log << "Found source file '" << arg << "'" << std::endl; if (m_is_citruninst) // In this mode the modified source file is written to a @@ -119,7 +194,7 @@ InstrumentFrontend::if_link_add_runtime(bool object_arg, bool compile_arg) if (!linking) return; - *m_log << "Link detected, adding '"<< CITRUN_SHARE "/libcitrun.a" + m_log << "Link detected, adding '"<< CITRUN_SHARE "/libcitrun.a" << "' to command line." << std::endl; m_args.push_back(const_cast<char *>(CITRUN_SHARE "/libcitrun.a")); } @@ -136,7 +211,7 @@ InstrumentFrontend::process_cmdline() if (std::strcmp(arg, "-E") == 0 || std::strcmp(arg, "-MM") == 0) { - *m_log << "Preprocessor argument found" << std::endl; + m_log << "Preprocessor argument found" << std::endl; exec_compiler(); } else if (std::strcmp(arg, "-o") == 0) @@ -146,20 +221,20 @@ InstrumentFrontend::process_cmdline() save_if_srcfile(arg); } - *m_log << "Command line is '" << cmd_line.str() << "'" << std::endl; + m_log << "Command line is '" << cmd_line.str() << "'" << std::endl; if_link_add_runtime(object_arg, compile_arg); if (m_source_files.size() != 0) return; - *m_log << "No source files found on command line." << std::endl; + m_log << "No source files found on command line." << std::endl; if (m_is_citruninst) exit(0); exec_compiler(); } -int +void InstrumentFrontend::instrument() { // @@ -173,11 +248,11 @@ InstrumentFrontend::instrument() clang_argv.push_back("--"); clang_argv.insert(clang_argv.end(), m_args.begin(), m_args.end()); #if defined(__OpenBSD__) - clang_argv.push_back("-I/usr/local/lib/clang/3.8.0/include"); - *m_log << "Added clangtool argument '" << clang_argv.back() << "'.\n"; + clang_argv.push_back("-I/usr/local/lib/clang/3.9.0/include"); + m_log << "Added clangtool argument '" << clang_argv.back() << "'" << std::endl; #elif defined(__APPLE__) clang_argv.push_back("-I/opt/local/libexec/llvm-3.8/lib/clang/3.8.1/include"); - *m_log << "Added clangtool argument '" << clang_argv.back() << "'" << std::endl; + m_log << "Added clangtool argument '" << clang_argv.back() << "'" << std::endl; #endif int clang_argc = clang_argv.size(); @@ -186,24 +261,36 @@ InstrumentFrontend::instrument() clang::tooling::ClangTool Tool(op.getCompilations(), op.getSourcePathList()); - clang::DiagnosticOptions diags; - clang::TextDiagnosticPrinter *log; - - //log = new clang::TextDiagnosticPrinter(m_log, &diags, false); - // log->setPrefix(std::to_string(m_log->m_pid)); - //Tool.setDiagnosticConsumer(log); + clang::TextDiagnosticBuffer diag_buffer; + Tool.setDiagnosticConsumer(&diag_buffer); std::unique_ptr<InstrumentActionFactory> f = llvm::make_unique<InstrumentActionFactory>(m_log, m_is_citruninst, m_source_files); - return Tool.run(f.get()); + int ret = Tool.run(f.get()); + + m_log << "Rewriting " << (ret ? "failed." : "successful.") << std::endl; + + std::chrono::high_resolution_clock::time_point now = + std::chrono::high_resolution_clock::now(); + m_log << std::chrono::duration_cast<std::chrono::milliseconds>(now - m_start_time).count() + << " Milliseconds spent rewriting source." << std::endl; + + if (m_is_citruninst) + exit(ret); + if (ret) { + // Rewriting failed. Original source files may be in an + // inconsistent state. + restore_original_src(); + exec_compiler(); + } } void InstrumentFrontend::restore_original_src() { for (auto &tmp_file : m_temp_file_map) { - *m_log << "Restored '" << tmp_file.first << "'" << std::endl; + m_log << "Restored '" << tmp_file.first << "'" << std::endl; copy_file(tmp_file.first, tmp_file.second); unlink(tmp_file.second.c_str()); @@ -213,11 +300,8 @@ InstrumentFrontend::restore_original_src() void InstrumentFrontend::exec_compiler() { - // XXX: Need to destroy log here. - // m_log->m_outfile.flush(); - if (m_is_citruninst) { - *m_log << "Running as citrun-inst, not re-exec()'ing" << std::endl; + m_log << "Running as citrun-inst, not re-exec()'ing" << std::endl; exit(0); } @@ -229,9 +313,6 @@ InstrumentFrontend::exec_compiler() int InstrumentFrontend::fork_compiler() { - // Otherwise we'll get two copies of buffers after fork(). - // m_log->m_outfile.flush(); - pid_t child_pid; if ((child_pid = fork()) < 0) err(1, "fork"); @@ -240,7 +321,7 @@ InstrumentFrontend::fork_compiler() // In child. exec_compiler(); - *m_log << "Forked '" << m_args[0] << "' " + m_log << "Forked '" << m_args[0] << "' " << "pid is '" << child_pid << "'" << std::endl; int status; @@ -252,6 +333,9 @@ InstrumentFrontend::fork_compiler() if (WIFEXITED(status)) exit = WEXITSTATUS(status); - *m_log << "'" << child_pid << "' exited " << exit << "." << std::endl; + m_log << "'" << child_pid << "' exited " << exit << std::endl; + + m_log << "Rewritten source compile " + << (exit ? "failed." : "successful.") << std::endl; return exit; } diff --git a/src/inst_frontend.h b/src/inst_frontend.h @@ -1,25 +1,28 @@ #include "inst_action.h" // InstrumentAction #include "inst_log.h" +#include <chrono> // std::chrono::high_resolution_clock #include <string> class InstrumentFrontend { public: - InstrumentFrontend(int, char *argv[], InstrumentLogger *, bool); + InstrumentFrontend(int, char *argv[]); void process_cmdline(); - int instrument(); + void instrument(); int fork_compiler(); void exec_compiler(); void restore_original_src(); private: + void clean_PATH(); void save_if_srcfile(char *); void if_link_add_runtime(bool, bool); std::vector<char *> m_args; - InstrumentLogger *m_log; + InstrumentLogger m_log; bool m_is_citruninst; + std::chrono::high_resolution_clock::time_point m_start_time; std::vector<std::string> m_source_files; std::map<std::string, std::string> m_temp_file_map; }; @@ -29,8 +32,7 @@ private: // class InstrumentActionFactory : public clang::tooling::FrontendActionFactory { public: - InstrumentActionFactory(InstrumentLogger *log, bool citruninst, - std::vector<std::string> const &src_files) : + InstrumentActionFactory(InstrumentLogger &log, bool citruninst, std::vector<std::string> const &src_files) : m_log(log), m_is_citruninst(citruninst), m_source_files(src_files), @@ -42,7 +44,7 @@ public: } private: - InstrumentLogger *m_log; + InstrumentLogger& m_log; bool m_is_citruninst; std::vector<std::string> m_source_files; int m_i; diff --git a/src/inst_log.h b/src/inst_log.h @@ -11,16 +11,17 @@ class InstrumentLogger : public std::ostream { class LogBuffer : public std::stringbuf { - bool m_iscitruninst; pid_t m_pid; std::error_code m_ec; llvm::raw_fd_ostream m_outfile; public: - LogBuffer(const bool &is_citruninst) : - m_iscitruninst(is_citruninst), + bool m_iscitruninst; + + LogBuffer() : m_pid(getpid()), m_ec(), - m_outfile("citrun.log", m_ec, llvm::sys::fs::F_Append) + m_outfile("citrun.log", m_ec, llvm::sys::fs::F_Append), + m_iscitruninst(false) { if (m_ec.value()) std::cerr << "Can't open citrun.log: " << m_ec.message(); @@ -41,10 +42,15 @@ class InstrumentLogger : public std::ostream LogBuffer buffer; public: - InstrumentLogger(const bool &is_citruninst) : + InstrumentLogger() : std::ostream(&buffer), - buffer(is_citruninst) + buffer() + { + } + + void set_citruninst() { + buffer.m_iscitruninst = true; } }; diff --git a/src/inst_main.cc b/src/inst_main.cc @@ -13,126 +13,18 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // -#include "inst_frontend.h" // InstrumentFrontend -#include "version.h" // citrun_major, citrun_minor - -#include <sys/utsname.h> // uname - -#include <chrono> // std::chrono::high_resolution_clock -#include <cstring> // strcmp -#include <err.h> -#include <libgen.h> // basename -#include <sstream> // stringstream - - -void -clean_PATH(InstrumentLogger &llog) -{ - char *path; - if ((path = std::getenv("PATH")) == NULL) - errx(1, "Error: PATH is not set."); - - llog << "PATH='" << path << "'" << std::endl; - - // Filter CITRUN_SHARE out of PATH - std::stringstream path_ss(path); - std::ostringstream new_path; - std::string component; - bool first_component = 1; - bool found_citrun_path = 0; - - while (std::getline(path_ss, component, ':')) { - if (component.compare(CITRUN_SHARE) == 0) { - found_citrun_path = 1; - continue; - } - - if (first_component == 0) - new_path << ":"; - - // It wasn't CITRUN_SHARE, keep it - new_path << component; - first_component = 0; - } - - if (!found_citrun_path) - errx(1, "Error: CITRUN_SHARE not in PATH."); - - if (setenv("PATH", new_path.str().c_str(), 1)) - err(1, "setenv"); -} -void -print_toolinfo(InstrumentLogger &llog) -{ - struct utsname utsname; - - llog << "citrun-inst " << citrun_major << "." << citrun_minor << " "; - if (uname(&utsname) == -1) - llog << "(Unknown OS) "; - else { - llog << "(" << utsname.sysname << "-" - << utsname.release << " " - << utsname.machine << ") "; - } - llog << "'" << CITRUN_SHARE << "'" << std::endl; -} +#include "inst_frontend.h" // InstrumentFrontend int main(int argc, char *argv[]) { - std::chrono::high_resolution_clock::time_point m_start_time = - std::chrono::high_resolution_clock::now(); - - char *base_name; - if ((base_name = basename(argv[0])) == NULL) - err(1, "basename"); - - bool is_citruninst = false; - if (std::strcmp(base_name, "citrun-inst") == 0) - is_citruninst = true; - - InstrumentLogger llog(is_citruninst); - print_toolinfo(llog); - - if (is_citruninst) { - llog << ">> Welcome to C It Run! Have a nice day." << std::endl; - } else { - // There's extra work to do if we're not running as citrun-inst. - llog << "Tool called as '" << argv[0] << "'"; - if (std::strcmp(base_name, argv[0]) != 0) { - llog << ", changing to '" << base_name << "'"; - argv[0] = base_name; - } - llog << std::endl; - - setprogname("citrun-inst"); - clean_PATH(llog); - } - - InstrumentFrontend main(argc, argv, &llog, is_citruninst); + InstrumentFrontend main(argc, argv); main.process_cmdline(); - int ret = main.instrument(); - llog << "Rewriting " << (ret ? "failed." : "successful.") << std::endl; - - std::chrono::high_resolution_clock::time_point now = - std::chrono::high_resolution_clock::now(); - llog << std::chrono::duration_cast<std::chrono::milliseconds>(now - m_start_time).count() - << " Milliseconds spent rewriting source." << std::endl; - - if (is_citruninst) - return ret; - if (ret) { - // Rewriting failed. Original source files may be in an - // inconsistent state. - main.restore_original_src(); - main.exec_compiler(); - } + main.instrument(); - ret = main.fork_compiler(); - llog << "Rewritten source compile " - << (ret ? "failed." : "successful.") << std::endl; + int ret = main.fork_compiler(); main.restore_original_src(); if (ret) diff --git a/t/inst_fail.sh b/t/inst_fail.sh @@ -17,7 +17,6 @@ ok "is citrun-wrap exit code 1" test $? -eq 1 cat <<EOF > check.good Summary: 1 Source files used as input - 1 Rewrite parse errors 1 Rewrite failures Totals: diff --git a/t/inst_log.sh b/t/inst_log.sh @@ -57,7 +57,7 @@ Instrumentation of '' finished: Modified source written successfully. Rewriting successful. Forked '' -'' exited 0. +'' exited 0 Rewritten source compile successful. Restored '' citrun-inst 0.0 () ''