citrun

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

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 }