citrun

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

commit a90c7f9894aec5bf8d0910726a139589339fdb96
parent 9f7ac79b0925b9476da3853423a38596ed14efe8
Author: Kyle Milz <kyle@0x30.net>
Date:   Fri, 12 Aug 2016 19:17:37 -0600

src: add a logging class for instrument

Diffstat:
Asrc/inst_log.h | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/inst_main.cc | 187++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/inst_main.h | 20++++++++------------
3 files changed, 167 insertions(+), 105 deletions(-)

diff --git a/src/inst_log.h b/src/inst_log.h @@ -0,0 +1,65 @@ +#include <llvm/Support/raw_ostream.h> + +class InstrumentLogger { +public: + InstrumentLogger() : + m_pid(getpid()), + m_needs_prefix(true) + {}; + ~InstrumentLogger() + { std::cerr << "~InstrumentLogger()" << std::endl; }; + + void set_output(const bool &is_citruninst) { + + if (is_citruninst) { + m_output = &llvm::outs(); + return; + } else { + std::error_code ec; + llvm::raw_fd_ostream *log_file = + new llvm::raw_fd_ostream("citrun.log", ec, llvm::sys::fs::F_Append); + + if (ec.value()) { + warnx("citrun.log: %s", ec.message().c_str()); + m_output = &llvm::nulls(); + return; + } + m_output = log_file; + } + + //m_output = &output; + }; + + template <typename T> + friend InstrumentLogger& operator<<(InstrumentLogger& out, const T &rhs) + { + out.print_prefix(); + *out.m_output << rhs; + return out; + } + friend InstrumentLogger& operator<<(InstrumentLogger& out, const char *rhs) + { + out.print_prefix(); + *out.m_output << rhs; + out.check_newline(rhs); + return out; + } + + pid_t m_pid; + llvm::raw_ostream *m_output; + +private: + void print_prefix() { + if (m_needs_prefix) { + *m_output << m_pid << ": "; + m_needs_prefix = false; + } + }; + + void check_newline(const std::string &rhs) { + if (std::find(rhs.begin(), rhs.end(), '\n') != rhs.end()) + m_needs_prefix = true; + }; + + bool m_needs_prefix; +}; diff --git a/src/inst_main.cc b/src/inst_main.cc @@ -39,73 +39,18 @@ static llvm::cl::OptionCategory ToolingCategory("citrun-inst options"); -int -main(int argc, char *argv[]) -{ - CitrunInst main(argc, argv); - - main.clean_PATH(); - - main.process_cmdline(); - - if (main.instrument()) - return 1; - - return main.compile_modified(); -} - -CitrunInst::CitrunInst(int argc, char *argv[]) : - m_args(argv, argv + argc), - m_ec(), - m_log("citrun.log", m_ec, llvm::sys::fs::F_Append), - m_pid(getpid()), - m_pfx(std::to_string(m_pid) + ": "), - m_is_citruninst(false) -{ - if (m_ec.value()) - warnx("citrun.log: %s", m_ec.message().c_str()); - - struct utsname utsname; - if (uname(&utsname) == -1) - err(1, "uname"); - - m_log << "\n" << m_pfx << "citrun-inst v" - << unsigned(citrun_major) << "." << unsigned(citrun_minor) - << " (" << utsname.sysname << "-" << utsname.release - << " " << utsname.machine - << ") called as '" << m_args[0] << "'.\n"; - m_log << m_pfx << "Resource directory is '" << STR(CITRUN_SHARE) << "'\n"; - - char *base_name; - if ((base_name = basename(m_args[0])) == NULL) - err(1, "basename"); - - if (std::strcmp(base_name, m_args[0]) != 0) { - m_log << m_pfx << "Changing '" << m_args[0] << "' to '" - << base_name << "'.\n"; - m_args[0] = base_name; - } - - if (std::strcmp(m_args[0], "citrun-inst") == 0) - m_is_citruninst = true; - - setprogname("citrun-inst"); -} +InstrumentLogger llog; void -CitrunInst::clean_PATH() +clean_PATH() { - if (m_is_citruninst) - // Running citrun-inst directly is guarded against exec() loops - return; - char *path; if ((path = std::getenv("PATH")) == NULL) { - m_log << m_pfx << "PATH is not set.\n"; + llog << "PATH is not set.\n"; errx(1, "PATH must be set"); } - m_log << m_pfx << "PATH='" << path << "'\n"; + llog << "PATH='" << path << "'\n"; // Filter CITRUN_SHARE out of PATH std::stringstream path_ss(path); @@ -129,7 +74,7 @@ CitrunInst::clean_PATH() } if (!found_citrun_path) { - m_log << m_pfx << "'" << STR(CITRUN_SHARE) << "' not in PATH.\n"; + llog << "'" << STR(CITRUN_SHARE) << "' not in PATH.\n"; errx(1, "'%s' not in PATH", STR(CITRUN_SHARE)); } @@ -138,6 +83,65 @@ CitrunInst::clean_PATH() err(1, "setenv"); } +void +print_toolinfo(const char *argv0) +{ + llog << "citrun-inst " + << unsigned(citrun_major) << "." + << unsigned(citrun_minor) << " "; + + struct utsname utsname; + if (uname(&utsname) == -1) { + warn("uname"); + llog << "(?) "; + } else { + llog << "(" + << utsname.sysname << "-" + << utsname.release << " " + << utsname.machine << ") "; + } + llog << "called as '" << argv0 << "'.\n"; + llog << "Resource directory is '" << STR(CITRUN_SHARE) << "'\n"; +} + +int +main(int argc, char *argv[]) +{ + 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; + + llog.set_output(is_citruninst); + + print_toolinfo(argv[0]); + + if (std::strcmp(base_name, argv[0]) != 0) { + llog << "Changing '" << argv[0] << "' to '" << base_name << "'.\n"; + argv[0] = base_name; + } + + setprogname("citrun-inst"); + + if (is_citruninst == false) + clean_PATH(); + + CitrunInst main(argc, argv, &llog, is_citruninst); + main.process_cmdline(); + if (main.instrument()) + return 1; + return main.compile_modified(); +} + +CitrunInst::CitrunInst(int argc, char *argv[], InstrumentLogger *l, bool is_citruninst) : + m_args(argv, argv + argc), + m_log(l), + m_is_citruninst(is_citruninst) +{ +} // Returns true if value ends with suffix, false otherwise. static bool @@ -185,7 +189,7 @@ CitrunInst::save_if_srcfile(char *arg) ends_with(arg, ".cpp") || ends_with(arg, ".cxx")) { m_source_files.push_back(arg); - m_log << m_pfx << "Found source file '" << arg << "'.\n"; + *m_log << "Found source file '" << arg << "'.\n"; if (m_is_citruninst) // In this mode the modified source file is written to a @@ -207,15 +211,15 @@ CitrunInst::process_cmdline() bool object_arg = false; bool compile_arg = false; - m_log << m_pfx << "Command line is '"; + *m_log << "Command line is '"; for (auto &arg : m_args) - m_log << arg << " "; - m_log << "'.\n"; + *m_log << arg << " "; + *m_log << "'.\n"; for (auto &arg : m_args) { if (std::strcmp(arg, "-E") == 0) { - m_log << m_pfx << "Preprocessor argument found\n"; + *m_log << "Preprocessor argument found\n"; exec_compiler(); } else if (std::strcmp(arg, "-o") == 0) @@ -226,7 +230,7 @@ CitrunInst::process_cmdline() save_if_srcfile(arg); } - m_log << m_pfx << "Object arg = " << object_arg << ", " + *m_log << "Object arg = " << object_arg << ", " << "compile arg = " << compile_arg << "\n"; bool linking = false; @@ -240,24 +244,24 @@ CitrunInst::process_cmdline() linking = true; if (linking) { - m_log << m_pfx << "Link detected, adding '"; + *m_log << "Link detected, adding '"; #ifndef __APPLE__ // OSX always links this. m_args.push_back(const_cast<char *>("-pthread")); - m_log << m_args.back() << " "; + *m_log << m_args.back() << " "; #endif #ifdef CITRUN_COVERAGE // Needed because libcitrun.a will be instrumented with gcov. m_args.push_back(const_cast<char *>("-coverage")); #endif m_args.push_back(const_cast<char *>(STR(CITRUN_SHARE) "/libcitrun.a")); - m_log << m_args.back() << "' to command line.\n"; + *m_log << m_args.back() << "' to command line.\n"; } if (m_source_files.size() != 0) return; - m_log << m_pfx << "No source files found. Executing command line.\n"; + *m_log << "No source files found. Executing command line.\n"; exec_compiler(); } @@ -276,10 +280,10 @@ CitrunInst::instrument() 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.1/include"); - m_log << m_pfx << "Added clangtool argument '" << clang_argv.back() << "'.\n"; + *m_log << "Added clangtool argument '" << clang_argv.back() << "'.\n"; #elif defined(__APPLE__) clang_argv.push_back("-I/opt/local/libexec/llvm-3.8/lib/clang/3.8.1/include"); - m_log << m_pfx << "Added clangtool argument '" << clang_argv.back() << "'.\n"; + *m_log << "Added clangtool argument '" << clang_argv.back() << "'.\n"; #endif int clang_argc = clang_argv.size(); @@ -288,26 +292,22 @@ CitrunInst::instrument() clang::tooling::ClangTool Tool(op.getCompilations(), op.getSourcePathList()); - if (!m_is_citruninst) { - clang::DiagnosticOptions diags; - clang::TextDiagnosticPrinter *log; + clang::DiagnosticOptions diags; + clang::TextDiagnosticPrinter *log; - log = new clang::TextDiagnosticPrinter(m_log, &diags, false); - log->setPrefix(std::to_string(m_pid)); - Tool.setDiagnosticConsumer(log); - } + log = new clang::TextDiagnosticPrinter(*m_log->m_output, &diags, false); + log->setPrefix(std::to_string(m_log->m_pid)); + Tool.setDiagnosticConsumer(log); std::unique_ptr<InstrumentActionFactory> f = - llvm::make_unique<InstrumentActionFactory>(&m_log, m_pfx, m_is_citruninst, m_source_files); + llvm::make_unique<InstrumentActionFactory>(m_log, m_is_citruninst, m_source_files); int ret = Tool.run(f.get()); - m_log << m_pfx << "Instrumentation " << (ret ? "failed.\n" : "successful.\n"); + *m_log << "Instrumentation " << (ret ? "failed.\n" : "successful.\n"); - if (m_is_citruninst) { + if (m_is_citruninst) // Nothing left to do if we're in this mode. - m_log.close(); exit(ret); - } if (ret) return try_unmodified_compile(); @@ -321,11 +321,11 @@ CitrunInst::try_unmodified_compile() int ret = fork_compiler(); if (ret == 0) { - m_log << m_pfx << "But the native compile succeeded!\n"; + *m_log << "But the native compile succeeded!\n"; return 1; } - m_log << m_pfx << "And the native compile failed.\n"; + *m_log << "And the native compile failed.\n"; return ret; } @@ -333,7 +333,7 @@ void CitrunInst::restore_original_src() { for (auto &tmp_file : m_temp_file_map) { - m_log << m_pfx << "Restored '" << tmp_file.first << "'.\n"; + *m_log << "Restored '" << tmp_file.first << "'.\n"; copy_file(tmp_file.first, tmp_file.second); unlink(tmp_file.second.c_str()); @@ -343,10 +343,11 @@ CitrunInst::restore_original_src() void CitrunInst::exec_compiler() { - m_log.close(); + // XXX: Need to destroy log here. + m_log->m_output->flush(); if (m_is_citruninst) { - m_log << m_pfx << "Running as citrun-inst, not re-exec()'ing\n"; + *m_log << "Running as citrun-inst, not re-exec()'ing\n"; exit(0); } @@ -359,7 +360,7 @@ int CitrunInst::fork_compiler() { // Otherwise we'll get two copies of buffers after fork(). - m_log.flush(); + m_log->m_output->flush(); pid_t child_pid; if ((child_pid = fork()) < 0) @@ -369,7 +370,7 @@ CitrunInst::fork_compiler() // In child. exec_compiler(); - m_log << m_pfx << "Forked '" << m_args[0] << "' " + *m_log << "Forked '" << m_args[0] << "' " << "pid is '" << child_pid << "'.\n"; int status; @@ -381,14 +382,14 @@ CitrunInst::fork_compiler() if (WIFEXITED(status)) exit = WEXITSTATUS(status); - m_log << m_pfx << "'" << child_pid << "' exited " << exit << ".\n"; + *m_log << "'" << child_pid << "' exited " << exit << ".\n"; return exit; } int CitrunInst::compile_modified() { - m_log << m_pfx << "Running native compiler on modified source code.\n"; + *m_log << "Running native compiler on modified source code.\n"; int ret = fork_compiler(); restore_original_src(); diff --git a/src/inst_main.h b/src/inst_main.h @@ -1,10 +1,11 @@ #include <string> #include "inst_action.h" // InstrumentAction +#include "inst_log.h" class CitrunInst { public: - CitrunInst(int, char *argv[]); + CitrunInst(int, char *argv[], InstrumentLogger *, bool); void clean_PATH(); void process_cmdline(); @@ -19,13 +20,10 @@ private: int try_unmodified_compile(); std::vector<char *> m_args; + InstrumentLogger *m_log; + bool m_is_citruninst; std::vector<std::string> m_source_files; std::map<std::string, std::string> m_temp_file_map; - std::error_code m_ec; - llvm::raw_fd_ostream m_log; - pid_t m_pid; - std::string m_pfx; - bool m_is_citruninst; }; // @@ -33,22 +31,20 @@ private: // class InstrumentActionFactory : public clang::tooling::FrontendActionFactory { public: - InstrumentActionFactory(llvm::raw_fd_ostream *log, std::string const &pfx, - 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_pfx(pfx), m_is_citruninst(citruninst), m_source_files(src_files), m_i(0) {}; clang::ASTFrontendAction *create() { - return new InstrumentAction(m_log, m_pfx, m_is_citruninst, m_source_files[m_i++]); + return new InstrumentAction((llvm::raw_fd_ostream*)m_log->m_output, "", m_is_citruninst, m_source_files[m_i++]); } private: - llvm::raw_fd_ostream *m_log; - std::string m_pfx; + InstrumentLogger *m_log; bool m_is_citruninst; std::vector<std::string> m_source_files; int m_i;