action.cc (4342B)
1 // 2 // Copyright (c) 2016 Kyle Milz <kyle@0x30.net> 3 // 4 // Permission to use, copy, modify, and distribute this software for any 5 // purpose with or without fee is hereby granted, provided that the above 6 // copyright notice and this permission notice appear in all copies. 7 // 8 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 // 16 #include "action.h" 17 #include "citrun_h.h" // citrun_h 18 19 #include <clang/Frontend/CompilerInstance.h> 20 #include <fstream> 21 #include <sstream> 22 #include <string> 23 24 25 std::unique_ptr<clang::ASTConsumer> 26 InstrumentAction::CreateASTConsumer(clang::CompilerInstance &CI, clang::StringRef file) 27 { 28 // llvm::errs() << "** Creating AST consumer for: " << file << "\n"; 29 clang::SourceManager &sm = CI.getSourceManager(); 30 m_TheRewriter.setSourceMgr(sm, CI.getLangOpts()); 31 32 // Hang onto a reference to this so we can read from it later 33 m_InstrumentASTConsumer = new RewriteASTConsumer(m_TheRewriter); 34 return std::unique_ptr<clang::ASTConsumer>(m_InstrumentASTConsumer); 35 } 36 37 void 38 InstrumentAction::write_modified_src(clang::FileID const &fid) 39 { 40 std::string out_file(getCurrentFile()); 41 42 std::error_code ec; 43 llvm::raw_fd_ostream output(out_file, ec, llvm::sys::fs::OF_None); 44 if (ec.value()) { 45 m_log << "Error writing modified source '" << out_file 46 << "': " << ec.message() << std::endl; 47 return; 48 } 49 50 // Write the instrumented source file 51 m_TheRewriter.getEditBuffer(fid).write(output); 52 m_log << "Modified source written successfully." << std::endl; 53 } 54 55 void 56 InstrumentAction::EndSourceFileAction() 57 { 58 clang::SourceManager &sm = m_TheRewriter.getSourceMgr(); 59 const clang::FileID main_fid = sm.getMainFileID(); 60 61 clang::SourceLocation end = sm.getLocForEndOfFile(main_fid); 62 unsigned int num_lines = sm.getPresumedLineNumber(end); 63 64 // 65 // Write instrumentation preamble. Includes: 66 // - runtime header 67 // - per tu citrun_node 68 // - static constructor for runtime initialization 69 // 70 std::ostringstream preamble; 71 preamble << 72 R"(#ifdef __cplusplus 73 extern "C" { 74 #endif 75 )"; 76 preamble << citrun_h; 77 preamble << "static struct citrun_node _citrun = {\n" 78 << " " << num_lines << ",\n" 79 << " \"" << m_compiler_file_name << "\",\n" 80 << " \"" << getCurrentFile().str() << "\",\n"; 81 preamble << "};\n"; 82 83 #ifdef _WIN32 84 // 85 // Cribbed from an answer by Joe: 86 // http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc 87 // 88 preamble << R"( 89 #pragma section(".CRT$XCU",read) 90 #define INITIALIZER2_(f,p) \ 91 static void f(void); \ 92 __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ 93 __pragma(comment(linker,"/include:" p #f "_")) \ 94 static void f(void) 95 #define INITIALIZER(f) INITIALIZER2_(f,"_") 96 )"; 97 preamble << "INITIALIZER( init_" 98 << m_compiler_file_name.substr(0, m_compiler_file_name.find(".")) 99 << ")" 100 << R"( 101 { 102 citrun_node_add(citrun_major, citrun_minor, &_citrun); 103 } 104 )"; 105 #else 106 preamble << R"( 107 __attribute__((constructor)) static void 108 citrun_constructor() 109 { 110 citrun_node_add(citrun_major, citrun_minor, &_citrun); 111 } 112 )"; 113 #endif 114 115 preamble << R"( 116 #ifdef __cplusplus 117 } 118 #endif 119 #line 1 120 )"; 121 122 clang::SourceLocation start = sm.getLocForStartOfFile(main_fid); 123 if (m_is_citruninst) { 124 std::ofstream preamble_file(getCurrentFile().str() + ".preamble"); 125 preamble_file << preamble.str(); 126 preamble_file.close(); 127 } else if (m_TheRewriter.InsertTextAfter(start, preamble.str())) { 128 m_log << "Failed to insert the instrumentation preabmle."; 129 return; 130 } 131 132 m_log << "Instrumentation of '" << m_compiler_file_name << "' finished:" << std::endl; 133 m_log << " " << num_lines << " Lines of source code" << std::endl; 134 135 // 136 // Write out statistics from the AST visitor. 137 // 138 RewriteASTVisitor v = m_InstrumentASTConsumer->get_visitor(); 139 for (int i = 0; i < NCOUNTERS; ++i) { 140 if (v.m_counters[i] == 0) 141 continue; 142 m_log << " " << v.m_counters[i] << " " 143 << v.m_counter_descr[i] << std::endl; 144 } 145 146 write_modified_src(main_fid); 147 }