├── .clang-format ├── test ├── templight_as_compiler.cpp ├── templight-driver-flag.cpp ├── Unit │ ├── lit.cfg.py │ └── lit.site.cfg.py.in ├── lit.cfg ├── CMakeLists.txt └── lit.site.cfg.in ├── utils ├── format.sh ├── create_patch.sh ├── ProtobufReader │ ├── TemplightProtobufReader.h │ └── TemplightProtobufReader.cpp └── ExtraWriters │ ├── TemplightExtraWriters.h │ └── TemplightExtraWriters.cpp ├── lib ├── CMakeLists.txt ├── TemplightEntryPrinter.cpp ├── TemplightAction.cpp ├── TemplightTracer.cpp └── TemplightProtobufWriter.cpp ├── templight_clang_version.txt ├── unittests ├── CMakeLists.txt └── TemplightActionTest.cpp ├── Info.plist.in ├── templight_symlink.cmake ├── include ├── PrintableTemplightEntries.h ├── TemplightProtobufWriter.h ├── TemplightDebugger.h ├── TemplightEntryPrinter.h ├── TemplightTracer.h ├── TemplightAction.h └── ThinProtobuf.h ├── templight_messages.proto ├── Makefile ├── LICENSE.TXT ├── CMakeLists.txt ├── templight_clang_patch.diff ├── README.md └── templight_driver.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | -------------------------------------------------------------------------------- /test/templight_as_compiler.cpp: -------------------------------------------------------------------------------- 1 | // RUN: templight++ %s 2 | 3 | int main() { 4 | int i = 10; 5 | (void)i; 6 | } 7 | -------------------------------------------------------------------------------- /utils/format.sh: -------------------------------------------------------------------------------- 1 | for i in c cc cpp h hpp; do 2 | for file in $(find -type f -name "*.$i"); do 3 | clang-format -i $file 4 | done 5 | done 6 | -------------------------------------------------------------------------------- /test/templight-driver-flag.cpp: -------------------------------------------------------------------------------- 1 | // RUN: rm -f %t.trace.pbf 2 | 3 | // RUN: %templight_cc1 %s -Xtemplight -profiler \ 4 | // RUN: -Xtemplight -output=%t.trace.pbf 5 | 6 | // grep the filename in the output trace file. That shouldn't be mangled. 7 | // RUN: grep "%s" %t.trace.pbf 8 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_clang_library(clangTemplight 2 | TemplightAction.cpp 3 | TemplightDebugger.cpp 4 | TemplightEntryPrinter.cpp 5 | TemplightProtobufWriter.cpp 6 | TemplightTracer.cpp 7 | 8 | LINK_LIBS 9 | clangAST 10 | clangBasic 11 | clangFrontend 12 | clangLex 13 | clangSema 14 | clangSerialization 15 | ) 16 | -------------------------------------------------------------------------------- /templight_clang_version.txt: -------------------------------------------------------------------------------- 1 | 'templight_clang_patch*.diff' was created using the following LLVM/Clang versions: 2 | 3 | LLVM git: 7256bf169ac3709b3c3ad5c6d541cf809ec341fb 4 | LLVM svn: https://llvm.org/svn/llvm-project/llvm/trunk@330373 5 | Clang git: c33e1469f018cf71327e06df054a0ffc87f4d9f8 6 | Clang svn: https://llvm.org/svn/llvm-project/cfe/trunk@330382 7 | -------------------------------------------------------------------------------- /unittests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_custom_target(TemplightUnitTests) 2 | set_target_properties(TemplightUnitTests PROPERTIES FOLDER "Templight Unit Tests") 3 | 4 | function(add_templight_unittest test_dirname) 5 | add_unittest(TemplightUnitTests ${test_dirname} ${ARGN}) 6 | endfunction() 7 | 8 | set(LLVM_LINK_COMPONENTS 9 | Support 10 | ) 11 | 12 | add_templight_unittest(TemplightTests 13 | TemplightActionTest.cpp 14 | ) 15 | 16 | target_link_libraries(TemplightTests 17 | PRIVATE 18 | clangBasic 19 | clangFrontend 20 | clangSerialization 21 | clangTemplight 22 | clangTooling 23 | ) 24 | -------------------------------------------------------------------------------- /Info.plist.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | @TOOL_INFO_UTI@ 7 | CFBundleInfoDictionaryVersion 8 | 6.0 9 | CFBundleName 10 | @TOOL_INFO_NAME 11 | CFBundleShortVersionString 12 | @TOOL_INFO_VERSION@ 13 | CFBundleVersion 14 | @TOOL_INFO_BUILD_VERSION@ 15 | CFBundleSignature 16 | ???? 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/Unit/lit.cfg.py: -------------------------------------------------------------------------------- 1 | # -*- Python -*- 2 | 3 | # Configuration file for the 'lit' test runner. 4 | 5 | import os 6 | import platform 7 | import subprocess 8 | 9 | import lit.formats 10 | import lit.util 11 | 12 | # name: The name of this test suite. 13 | config.name = 'Templight-Unit' 14 | 15 | # suffixes: A list of file extensions to treat as test files. 16 | config.suffixes = [] 17 | 18 | # test_source_root: The root path where tests are located. 19 | # test_exec_root: The root path where tests should be run. 20 | config.test_exec_root = os.path.join(config.templight_obj_root, 'unittests') 21 | config.test_source_root = config.test_exec_root 22 | 23 | # testFormat: The test format to use to interpret tests. 24 | config.test_format = lit.formats.GoogleTest('.', 'Tests') 25 | -------------------------------------------------------------------------------- /unittests/TemplightActionTest.cpp: -------------------------------------------------------------------------------- 1 | //===- TemplightActionTest.cpp ---------------------*- C++ -*--------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "TemplightAction.h" 11 | #include "clang/Frontend/CompilerInstance.h" 12 | #include "clang/Frontend/FrontendActions.h" 13 | #include "clang/Tooling/Tooling.h" 14 | #include "llvm/ADT/StringRef.h" 15 | #include "gtest/gtest.h" 16 | 17 | using namespace clang; 18 | 19 | TEST(TemplightActionTest, SimpleInvocation) { 20 | EXPECT_TRUE( 21 | tooling::runToolOnCode(std::make_unique( 22 | std::make_unique()), 23 | "void f() {;}")); 24 | } 25 | -------------------------------------------------------------------------------- /templight_symlink.cmake: -------------------------------------------------------------------------------- 1 | # We need to execute this script at installation time because the 2 | # DESTDIR environment variable may be unset at configuration time. 3 | # See PR8397. 4 | 5 | if(UNIX) 6 | set(CLANGXX_LINK_OR_COPY create_symlink) 7 | set(CLANGXX_DESTDIR $ENV{DESTDIR}) 8 | else() 9 | set(CLANGXX_LINK_OR_COPY copy) 10 | endif() 11 | 12 | # CMAKE_EXECUTABLE_SUFFIX is undefined on cmake scripts. See PR9286. 13 | if( WIN32 ) 14 | set(EXECUTABLE_SUFFIX ".exe") 15 | else() 16 | set(EXECUTABLE_SUFFIX "") 17 | endif() 18 | 19 | set(bindir "${CLANGXX_DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/") 20 | set(templight "templight${EXECUTABLE_SUFFIX}") 21 | set(templightxx "templight++${EXECUTABLE_SUFFIX}") 22 | set(templight_cl "templight-cl${EXECUTABLE_SUFFIX}") 23 | 24 | message("Creating templight++ executable based on ${templight}") 25 | 26 | execute_process( 27 | COMMAND "${CMAKE_COMMAND}" -E ${CLANGXX_LINK_OR_COPY} "${templight}" "${templightxx}" 28 | WORKING_DIRECTORY "${bindir}") 29 | 30 | message("Creating templight-cl executable based on ${templight}") 31 | 32 | execute_process( 33 | COMMAND "${CMAKE_COMMAND}" -E ${CLANGXX_LINK_OR_COPY} "${templight}" "${templight_cl}" 34 | WORKING_DIRECTORY "${bindir}") 35 | -------------------------------------------------------------------------------- /utils/create_patch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script can be used for re-creating the patch. It assumes, that the source 4 | # code has been checked out as part of the llvm/clang source tree according to 5 | # the tutorial found in README.md of this repository. 6 | 7 | export LANG=C 8 | export LC_MESSAGES=C 9 | export LC_COLLATE=C 10 | 11 | SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" 12 | cd "${SCRIPTPATH}/../../../../../" 13 | 14 | VERSIONFILE="${SCRIPTPATH}/../templight_clang_version.txt" 15 | 16 | LLVMGIT="$(git log -1 | grep "commit .*" | awk -v N=2 '{print $N}')" 17 | LLVMSVN="$(git log -1 | grep "git-svn-id: .*" | awk -v N=2 '{print $N}')" 18 | cd tools/clang/ 19 | CLANGGIT="$(git log -1 | grep "commit .*" | awk -v N=2 '{print $N}')" 20 | CLANGSVN="$(git log -1 | grep "git-svn-id: .*" | awk -v N=2 '{print $N}')" 21 | 22 | echo "'templight_clang_patch*.diff' was created using the following LLVM/Clang versions:\n" > $VERSIONFILE 23 | 24 | echo "LLVM git: $LLVMGIT" >> $VERSIONFILE 25 | echo "LLVM svn: $LLVMSVN" >> $VERSIONFILE 26 | 27 | echo "Clang git: $CLANGGIT" >> $VERSIONFILE 28 | echo "Clang svn: $CLANGSVN" >> $VERSIONFILE 29 | 30 | git diff > $SCRIPTPATH/../templight_clang_patch.diff 31 | git diff -U999999 > $SCRIPTPATH/../templight_clang_patch_with_context.diff 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/Unit/lit.site.cfg.py.in: -------------------------------------------------------------------------------- 1 | @LIT_SITE_CFG_IN_HEADER@ 2 | 3 | import sys 4 | 5 | config.llvm_src_root = "@LLVM_SOURCE_DIR@" 6 | config.llvm_obj_root = "@LLVM_BINARY_DIR@" 7 | config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" 8 | config.llvm_libs_dir = "@LLVM_LIBS_DIR@" 9 | config.llvm_build_mode = "@LLVM_BUILD_MODE@" 10 | config.clang_obj_root = "@CLANG_BINARY_DIR@" 11 | config.enable_shared = @ENABLE_SHARED@ 12 | config.shlibdir = "@SHLIBDIR@" 13 | config.target_triple = "@TARGET_TRIPLE@" 14 | 15 | config.templight_obj_root = "@TEMPLIGHT_BINARY_DIR@" 16 | config.templight_src_root = "@TEMPLIGHT_SOURCE_DIR@" 17 | 18 | # Support substitution of the tools_dir, libs_dirs, and build_mode with user 19 | # parameters. This is used when we can't determine the tool dir at 20 | # configuration time. 21 | try: 22 | config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params 23 | config.llvm_libs_dir = config.llvm_libs_dir % lit_config.params 24 | config.llvm_build_mode = config.llvm_build_mode % lit_config.params 25 | except KeyError: 26 | e = sys.exc_info()[1] 27 | key, = e.args 28 | lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) 29 | 30 | # Let the main config do the real work. 31 | lit_config.load_config(config, "@TEMPLIGHT_SOURCE_DIR@/test/Unit/lit.cfg.py") 32 | -------------------------------------------------------------------------------- /test/lit.cfg: -------------------------------------------------------------------------------- 1 | # -*- Python -*- 2 | 3 | import os 4 | import platform 5 | import re 6 | import subprocess 7 | import tempfile 8 | 9 | import lit.formats 10 | import lit.util 11 | 12 | from lit.llvm import llvm_config# Configuration file for the 'lit' test runner. 13 | 14 | # name: The name of this test suite. 15 | config.name = 'Templight' 16 | 17 | # suffixes: A list of file extensions to treat as test files. 18 | config.suffixes = ['.c', '.cpp', '.cppm', '.m', '.mm', '.cu', 19 | '.ll', '.cl', '.s', '.S', '.modulemap', '.test', '.rs'] 20 | 21 | # excludes: A list of directories to exclude from the testsuite. The 'Inputs' 22 | # subdirectories contain auxiliary inputs for various tests in their parent 23 | # directories. 24 | config.excludes = ['Inputs', 'CMakeLists.txt'] 25 | 26 | # test_source_root: The root path where tests are located. 27 | config.test_source_root = os.path.dirname(__file__) 28 | 29 | config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) 30 | 31 | # test_exec_root: The root path where tests should be run. 32 | config.test_exec_root = os.path.join(config.templight_obj_root, 'test') 33 | 34 | llvm_config.use_default_substitutions() 35 | 36 | llvm_config.use_clang() 37 | 38 | config.substitutions.append(('%PATH%', config.environment['PATH'])) 39 | 40 | config.substitutions.append(('%templight_cc1', 'templight -cc1')) 41 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Test runner infrastructure for Templight. This configures the Clang 2 | # test trees for use by Lit, and delegates to LLVM's lit test handlers. 3 | 4 | set(TEMPLIGHT_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") 5 | set(TEMPLIGHT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/..") 6 | 7 | if (CMAKE_CFG_INTDIR STREQUAL ".") 8 | set(LLVM_BUILD_MODE ".") 9 | else () 10 | set(LLVM_BUILD_MODE "%(build_mode)s") 11 | endif () 12 | 13 | string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} TEMPLIGHT_DIR ${LLVM_RUNTIME_OUTPUT_INTDIR}) 14 | 15 | set(TEMPLIGHT_TEST_DEPS 16 | ${CLANG_TEST_DEPS} 17 | TemplightUnitTests 18 | templight 19 | llvm-config 20 | FileCheck 21 | count 22 | not 23 | ) 24 | 25 | set(TEMPLIGHT_TEST_EXTRA_ARGS "") 26 | 27 | configure_lit_site_cfg( 28 | ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in 29 | ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg 30 | ) 31 | 32 | configure_lit_site_cfg( 33 | ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.py.in 34 | ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg.py 35 | MAIN_CONFIG 36 | ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.cfg.py 37 | ) 38 | 39 | add_lit_testsuite(check-templight "Running Templight regression tests" 40 | ${CMAKE_CURRENT_BINARY_DIR} 41 | DEPENDS ${TEMPLIGHT_TEST_DEPS} 42 | ARGS ${TEMPLIGHT_TEST_EXTRA_ARGS} 43 | ) 44 | 45 | set_target_properties(check-templight PROPERTIES FOLDER "Templight tests") 46 | -------------------------------------------------------------------------------- /include/PrintableTemplightEntries.h: -------------------------------------------------------------------------------- 1 | //===- PrintableTemplightEntries.h -------------------*- C++ -*------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LLVM_CLANG_PRINTABLE_TEMPLIGHT_ENTRIES_H 11 | #define LLVM_CLANG_PRINTABLE_TEMPLIGHT_ENTRIES_H 12 | 13 | #include 14 | #include 15 | 16 | namespace llvm { 17 | class raw_ostream; 18 | } 19 | 20 | namespace clang { 21 | 22 | struct PrintableTemplightEntryBegin { 23 | int SynthesisKind; 24 | std::string Name; 25 | std::string FileName; 26 | int Line; 27 | int Column; 28 | double TimeStamp; 29 | std::uint64_t MemoryUsage; 30 | std::string TempOri_FileName; 31 | int TempOri_Line; 32 | int TempOri_Column; 33 | }; 34 | 35 | struct PrintableTemplightEntryEnd { 36 | double TimeStamp; 37 | std::uint64_t MemoryUsage; 38 | }; 39 | 40 | class TemplightWriter { 41 | public: 42 | TemplightWriter(llvm::raw_ostream &aOS) : OutputOS(aOS){}; 43 | virtual ~TemplightWriter(){}; 44 | 45 | virtual void initialize(const std::string &aSourceName = "") = 0; 46 | virtual void finalize() = 0; 47 | 48 | virtual void printEntry(const PrintableTemplightEntryBegin &aEntry) = 0; 49 | virtual void printEntry(const PrintableTemplightEntryEnd &aEntry) = 0; 50 | 51 | protected: 52 | llvm::raw_ostream &OutputOS; 53 | }; 54 | 55 | } // namespace clang 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /include/TemplightProtobufWriter.h: -------------------------------------------------------------------------------- 1 | //===- TemplightProtobufWriter.h --------------------*- C++ -*-------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LLVM_CLANG_TEMPLIGHT_PROTOBUF_WRITER_H 11 | #define LLVM_CLANG_TEMPLIGHT_PROTOBUF_WRITER_H 12 | 13 | #include "PrintableTemplightEntries.h" 14 | 15 | #include 16 | #include 17 | 18 | namespace llvm { 19 | class raw_ostream; 20 | } 21 | 22 | namespace clang { 23 | 24 | class TemplightProtobufWriter : public TemplightWriter { 25 | private: 26 | std::string buffer; 27 | std::unordered_map fileNameMap; 28 | std::unordered_map templateNameMap; 29 | int compressionMode; 30 | 31 | std::size_t createDictionaryEntry(const std::string &Name); 32 | std::string printEntryLocation(const std::string &FileName, int Line, 33 | int Column); 34 | std::string printTemplateName(const std::string &Name); 35 | 36 | public: 37 | TemplightProtobufWriter(llvm::raw_ostream &aOS, int aCompressLevel = 2); 38 | 39 | void initialize(const std::string &aSourceName = "") override; 40 | void finalize() override; 41 | 42 | void printEntry(const PrintableTemplightEntryBegin &aEntry) override; 43 | void printEntry(const PrintableTemplightEntryEnd &aEntry) override; 44 | }; 45 | 46 | } // namespace clang 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /include/TemplightDebugger.h: -------------------------------------------------------------------------------- 1 | //===- TemplightDebugger.h ------ Clang Templight Debugger -*- C++ -*------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LLVM_CLANG_TEMPLIGHT_DEBUGGER_H 11 | #define LLVM_CLANG_TEMPLIGHT_DEBUGGER_H 12 | 13 | #include "clang/Sema/TemplateInstCallback.h" 14 | 15 | #include 16 | #include 17 | 18 | namespace clang { 19 | 20 | class TemplightDebugger : public TemplateInstantiationCallback { 21 | public: 22 | class InteractiveAgent; // forward-decl. 23 | 24 | void initialize(const Sema &TheSema) override; 25 | void finalize(const Sema &TheSema) override; 26 | void atTemplateBegin(const Sema &TheSema, 27 | const Sema::CodeSynthesisContext &Inst) override; 28 | void atTemplateEnd(const Sema &TheSema, 29 | const Sema::CodeSynthesisContext &Inst) override; 30 | 31 | private: 32 | unsigned MemoryFlag : 1; 33 | unsigned IgnoreSystemFlag : 1; 34 | 35 | std::unique_ptr Interactor; 36 | 37 | public: 38 | /// \brief Construct the templight debugger. 39 | TemplightDebugger(const Sema &TheSema, bool Memory = false, 40 | bool IgnoreSystem = false); 41 | 42 | ~TemplightDebugger() override; 43 | 44 | bool getMemoryFlag() const { return MemoryFlag; }; 45 | 46 | void readBlacklists(const std::string &BLFilename); 47 | }; 48 | 49 | } // namespace clang 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /utils/ProtobufReader/TemplightProtobufReader.h: -------------------------------------------------------------------------------- 1 | //===- TemplightProtobufReader.h --------------------*- C++ -*-------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LLVM_CLANG_TEMPLIGHT_PROTOBUF_READER_H 11 | #define LLVM_CLANG_TEMPLIGHT_PROTOBUF_READER_H 12 | 13 | #include "PrintableTemplightEntries.h" 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | namespace clang { 21 | 22 | class TemplightProtobufReader { 23 | private: 24 | llvm::StringRef buffer; 25 | llvm::StringRef remainder_buffer; 26 | 27 | std::vector fileNameMap; 28 | std::vector templateNameMap; 29 | 30 | void loadHeader(llvm::StringRef aSubBuffer); 31 | void loadDictionaryEntry(llvm::StringRef aSubBuffer); 32 | void loadTemplateName(llvm::StringRef aSubBuffer); 33 | void loadBeginEntry(llvm::StringRef aSubBuffer); 34 | void loadEndEntry(llvm::StringRef aSubBuffer); 35 | 36 | public: 37 | enum LastChunkType { 38 | EndOfFile = 0, 39 | Header, 40 | BeginEntry, 41 | EndEntry, 42 | Other 43 | } LastChunk; 44 | 45 | unsigned int Version; 46 | std::string SourceName; 47 | 48 | PrintableTemplightEntryBegin LastBeginEntry; 49 | PrintableTemplightEntryEnd LastEndEntry; 50 | 51 | TemplightProtobufReader(); 52 | 53 | LastChunkType startOnBuffer(llvm::StringRef aBuffer); 54 | LastChunkType next(); 55 | }; 56 | 57 | } // namespace clang 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /include/TemplightEntryPrinter.h: -------------------------------------------------------------------------------- 1 | //===- TemplightProtobufReader.h --------------------------*- C++ -*-------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LLVM_CLANG_TEMPLIGHT_ENTRY_PRINTER_H 11 | #define LLVM_CLANG_TEMPLIGHT_ENTRY_PRINTER_H 12 | 13 | #include "PrintableTemplightEntries.h" 14 | 15 | #include 16 | #include 17 | 18 | namespace llvm { 19 | class Regex; 20 | } 21 | 22 | namespace clang { 23 | 24 | class TemplightWriter; 25 | 26 | class TemplightEntryPrinter { 27 | public: 28 | void skipEntry(); 29 | bool shouldIgnoreEntry(const PrintableTemplightEntryBegin &Entry); 30 | bool shouldIgnoreEntry(const PrintableTemplightEntryEnd &Entry); 31 | 32 | void printEntry(const PrintableTemplightEntryBegin &Entry); 33 | void printEntry(const PrintableTemplightEntryEnd &Entry); 34 | 35 | void initialize(const std::string &SourceName = ""); 36 | void finalize(); 37 | 38 | TemplightEntryPrinter(const std::string &Output); 39 | ~TemplightEntryPrinter(); 40 | 41 | bool isValid() const; 42 | 43 | llvm::raw_ostream *getTraceStream() const; 44 | void takeWriter(TemplightWriter *aPWriter); 45 | 46 | void readBlacklists(const std::string &BLFilename); 47 | 48 | private: 49 | std::size_t SkippedEndingsCount; 50 | std::unique_ptr CoRegex; 51 | std::unique_ptr IdRegex; 52 | 53 | llvm::raw_ostream *TraceOS; 54 | 55 | std::unique_ptr p_writer; 56 | }; 57 | 58 | } // namespace clang 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /include/TemplightTracer.h: -------------------------------------------------------------------------------- 1 | //===- TemplightTracer.h ---------------------------*- C++ -*--------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LLVM_CLANG_TEMPLIGHT_TRACER_H 11 | #define LLVM_CLANG_TEMPLIGHT_TRACER_H 12 | 13 | #include "clang/Sema/TemplateInstCallback.h" 14 | 15 | #include 16 | #include 17 | 18 | namespace clang { 19 | 20 | class TemplightTracer : public TemplateInstantiationCallback { 21 | public: 22 | class TracePrinter; // forward-decl. 23 | 24 | void initialize(const Sema &TheSema) override; 25 | void finalize(const Sema &TheSema) override; 26 | void atTemplateBegin(const Sema &TheSema, 27 | const Sema::CodeSynthesisContext &Inst) override; 28 | void atTemplateEnd(const Sema &TheSema, 29 | const Sema::CodeSynthesisContext &Inst) override; 30 | 31 | private: 32 | unsigned MemoryFlag : 1; 33 | unsigned SafeModeFlag : 1; 34 | 35 | std::unique_ptr Printer; 36 | 37 | public: 38 | /// \brief Sets the format type of the template trace file. 39 | /// The argument can be xml/yaml/text 40 | TemplightTracer(const Sema &TheSema, std::string Output = "", 41 | bool Memory = false, bool Safemode = false, 42 | bool IgnoreSystem = false); 43 | 44 | ~TemplightTracer() override; 45 | 46 | bool getMemoryFlag() const { return MemoryFlag; }; 47 | bool getSafeModeFlag() const { return SafeModeFlag; }; 48 | 49 | void readBlacklists(const std::string &BLFilename); 50 | }; 51 | 52 | } // namespace clang 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /test/lit.site.cfg.in: -------------------------------------------------------------------------------- 1 | @LIT_SITE_CFG_IN_HEADER@ 2 | 3 | import sys 4 | 5 | config.llvm_src_root = "@LLVM_SOURCE_DIR@" 6 | config.llvm_obj_root = "@LLVM_BINARY_DIR@" 7 | config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" 8 | config.llvm_libs_dir = "@LLVM_LIBS_DIR@" 9 | config.llvm_shlib_dir = "@SHLIBDIR@" 10 | config.llvm_plugin_ext = "@LLVM_PLUGIN_EXT@" 11 | 12 | config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" 13 | 14 | config.clang_obj_root = "@CLANG_BINARY_DIR@" 15 | config.clang_src_dir = "@CLANG_SOURCE_DIR@" 16 | config.clang_tools_dir = "@CLANG_TOOLS_DIR@" 17 | 18 | config.templight_obj_root = "@TEMPLIGHT_BINARY_DIR@" 19 | config.templight_src_root = "@TEMPLIGHT_SOURCE_DIR@" 20 | 21 | config.host_triple = "@LLVM_HOST_TRIPLE@" 22 | config.target_triple = "@LLVM_TARGET_TRIPLE@" 23 | config.host_cxx = "@CMAKE_CXX_COMPILER@" 24 | config.llvm_use_sanitizer = "@LLVM_USE_SANITIZER@" 25 | config.clang_default_cxx_stdlib = "@CLANG_DEFAULT_CXX_STDLIB@" 26 | config.host_arch = "@HOST_ARCH@" 27 | config.python_executable = "@PYTHON_EXECUTABLE@" 28 | config.use_z3_solver = lit_config.params.get('USE_Z3_SOLVER', "@USE_Z3_SOLVER@") 29 | 30 | # Support substitution of the tools and libs dirs with user parameters. This is 31 | # used when we can't determine the tool dir at configuration time. 32 | try: 33 | config.clang_tools_dir = config.clang_tools_dir % lit_config.params 34 | config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params 35 | config.llvm_shlib_dir = config.llvm_shlib_dir % lit_config.params 36 | config.llvm_libs_dir = config.llvm_libs_dir % lit_config.params 37 | except KeyError: 38 | e = sys.exc_info()[1] 39 | key, = e.args 40 | lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) 41 | 42 | import lit.llvm 43 | lit.llvm.initialize(lit_config, config) 44 | 45 | # Let the main config do the real work. 46 | lit_config.load_config(config, "@TEMPLIGHT_SOURCE_DIR@/test/lit.cfg") 47 | -------------------------------------------------------------------------------- /templight_messages.proto: -------------------------------------------------------------------------------- 1 | message TemplightHeader { 2 | required uint32 version = 1; 3 | optional string source_file = 2; 4 | } 5 | 6 | message TemplightEntry { 7 | enum SynthesisKind { 8 | TemplateInstantiation = 0; 9 | DefaultTemplateArgumentInstantiation = 1; 10 | DefaultFunctionArgumentInstantiation = 2; 11 | ExplicitTemplateArgumentSubstitution = 3; 12 | DeducedTemplateArgumentSubstitution = 4; 13 | PriorTemplateArgumentSubstitution = 5; 14 | DefaultTemplateArgumentChecking = 6; 15 | ExceptionSpecEvaluation = 7; 16 | ExceptionSpecInstantiation = 8; 17 | DeclaringSpecialMember = 9; 18 | DefiningSynthesizedFunction = 10; 19 | Memoization = 11; 20 | } 21 | 22 | message TemplateName { 23 | // oneof rep { 24 | // string name = 1; 25 | // bytes compressed_name = 2; 26 | // } 27 | optional string name = 1; 28 | optional bytes compressed_name = 2; 29 | optional uint32 dict_id = 3; 30 | } 31 | 32 | message SourceLocation { 33 | optional string file_name = 1; 34 | required uint32 file_id = 2; 35 | required uint32 line = 3; 36 | optional uint32 column = 4; 37 | } 38 | 39 | message Begin { 40 | required SynthesisKind kind = 1; 41 | required TemplateName name = 2; 42 | required SourceLocation location = 3; 43 | optional double time_stamp = 4; 44 | optional uint64 memory_usage = 5; 45 | optional SourceLocation template_origin = 6; 46 | } 47 | 48 | message End { 49 | optional double time_stamp = 1; 50 | optional uint64 memory_usage = 2; 51 | } 52 | 53 | // oneof begin_or_end { 54 | // Begin begin = 1; 55 | // End end = 2; 56 | // } 57 | optional Begin begin = 1; 58 | optional End end = 2; 59 | } 60 | 61 | message DictionaryEntry { 62 | required string marked_name = 1; 63 | repeated uint32 marker_ids = 2; 64 | } 65 | 66 | message TemplightTrace { 67 | required TemplightHeader header = 1; 68 | repeated TemplightEntry entries = 2; 69 | repeated DictionaryEntry names = 3; 70 | } 71 | 72 | message TemplightTraceCollection { 73 | repeated TemplightTrace traces = 1; 74 | } 75 | -------------------------------------------------------------------------------- /include/TemplightAction.h: -------------------------------------------------------------------------------- 1 | //===- TemplightAction.h ------ Clang Templight Frontend Action -*- C++ -*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LLVM_CLANG_TEMPLIGHT_TEMPLIGHT_ACTION_H 11 | #define LLVM_CLANG_TEMPLIGHT_TEMPLIGHT_ACTION_H 12 | 13 | #include 14 | 15 | #include "clang/Frontend/CompilerInstance.h" 16 | #include "clang/Frontend/FrontendAction.h" 17 | #include "llvm/ADT/StringRef.h" 18 | 19 | namespace clang { 20 | 21 | class TemplightAction : public WrapperFrontendAction { 22 | protected: 23 | std::unique_ptr 24 | CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override; 25 | bool BeginInvocation(CompilerInstance &CI) override; 26 | bool BeginSourceFileAction(CompilerInstance &CI) override; 27 | void ExecuteAction() override; 28 | void EndSourceFileAction() override; 29 | 30 | public: 31 | /// Construct a TemplightAction from an existing action, taking 32 | /// ownership of it. 33 | TemplightAction(std::unique_ptr WrappedAction); 34 | 35 | bool usesPreprocessorOnly() const override; 36 | TranslationUnitKind getTranslationUnitKind() override; 37 | bool hasPCHSupport() const override; 38 | bool hasASTFileSupport() const override; 39 | bool hasIRSupport() const override; 40 | bool hasCodeCompletionSupport() const override; 41 | 42 | static std::string CreateOutputFilename(CompilerInstance *CI, 43 | const std::string &OptOutputName, 44 | bool OptInstProfiler, 45 | bool OptOutputToStdOut, 46 | bool OptMemoryProfile); 47 | 48 | unsigned InstProfiler : 1; 49 | unsigned OutputToStdOut : 1; 50 | unsigned MemoryProfile : 1; 51 | unsigned OutputInSafeMode : 1; 52 | unsigned IgnoreSystemInst : 1; 53 | unsigned InteractiveDebug : 1; 54 | std::string OutputFilename; 55 | std::string BlackListFilename; 56 | 57 | private: 58 | void EnsureHasSema(CompilerInstance &CI); 59 | }; 60 | 61 | } // namespace clang 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ##===- tools/templight/Makefile ----------------------------*- Makefile -*-===## 2 | # 3 | # The LLVM Compiler Infrastructure 4 | # 5 | # This file is distributed under the University of Illinois Open Source 6 | # License. See LICENSE.TXT for details. 7 | # 8 | ##===----------------------------------------------------------------------===## 9 | CLANG_LEVEL := ../.. 10 | 11 | TOOLNAME = templight 12 | TOOLALIAS = templight++ 13 | 14 | ifdef CLANG_ORDER_FILE 15 | TOOL_ORDER_FILE := $(CLANG_ORDER_FILE) 16 | endif 17 | 18 | # Include tool version information on OS X. 19 | TOOL_INFO_PLIST := Info.plist 20 | 21 | # Include this here so we can get the configuration of the targets that have 22 | # been configured for construction. We have to do this early so we can set up 23 | # LINK_COMPONENTS before including Makefile.rules 24 | include $(CLANG_LEVEL)/../../Makefile.config 25 | 26 | # Have the option of not supporting plugins. This is important for startup 27 | # performance. 28 | ifeq ($(CLANG_PLUGIN_SUPPORT), 1) 29 | NO_DEAD_STRIP := 1 30 | else 31 | TOOL_NO_EXPORTS := 1 32 | endif 33 | 34 | LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader bitwriter codegen \ 35 | instrumentation ipo irreader linker objcarcopts option \ 36 | profiledata selectiondag 37 | USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \ 38 | clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \ 39 | clangRewriteFrontend.a clangRewrite.a 40 | 41 | ifeq ($(ENABLE_CLANG_STATIC_ANALYZER),1) 42 | USEDLIBS += clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \ 43 | clangStaticAnalyzerCore.a 44 | endif 45 | 46 | ifeq ($(ENABLE_CLANG_ARCMT),1) 47 | USEDLIBS += clangARCMigrate.a 48 | endif 49 | 50 | USEDLIBS += clangAnalysis.a clangEdit.a clangAST.a clangLex.a clangBasic.a 51 | 52 | include $(CLANG_LEVEL)/Makefile 53 | 54 | # Set the tool version information values. 55 | ifeq ($(HOST_OS),Darwin) 56 | ifdef CLANG_VENDOR 57 | TOOL_INFO_NAME := $(CLANG_VENDOR) templight 58 | else 59 | TOOL_INFO_NAME := templight 60 | endif 61 | 62 | ifdef CLANG_VENDOR_UTI 63 | TOOL_INFO_UTI := $(CLANG_VENDOR_UTI) 64 | else 65 | TOOL_INFO_UTI := org.llvm.clang 66 | endif 67 | 68 | TOOL_INFO_VERSION := $(word 3,$(shell grep "CLANG_VERSION " \ 69 | $(PROJ_OBJ_DIR)/$(CLANG_LEVEL)/include/clang/Basic/Version.inc)) 70 | ifdef LLVM_SUBMIT_VERSION 71 | TOOL_INFO_BUILD_VERSION := $(LLVM_SUBMIT_VERSION).$(LLVM_SUBMIT_SUBVERSION) 72 | else 73 | TOOL_INFO_BUILD_VERSION := 74 | endif 75 | endif 76 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | LLVM Release License 3 | ============================================================================== 4 | University of Illinois/NCSA 5 | Open Source License 6 | 7 | Copyright (c) 2007-2014 University of Illinois at Urbana-Champaign. 8 | All rights reserved. 9 | 10 | Developed by: 11 | 12 | LLVM Team 13 | 14 | University of Illinois at Urbana-Champaign 15 | 16 | http://llvm.org 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy of 19 | this software and associated documentation files (the "Software"), to deal with 20 | the Software without restriction, including without limitation the rights to 21 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 22 | of the Software, and to permit persons to whom the Software is furnished to do 23 | so, subject to the following conditions: 24 | 25 | * Redistributions of source code must retain the above copyright notice, 26 | this list of conditions and the following disclaimers. 27 | 28 | * Redistributions in binary form must reproduce the above copyright notice, 29 | this list of conditions and the following disclaimers in the 30 | documentation and/or other materials provided with the distribution. 31 | 32 | * Neither the names of the LLVM Team, University of Illinois at 33 | Urbana-Champaign, nor the names of its contributors may be used to 34 | endorse or promote products derived from this Software without specific 35 | prior written permission. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 39 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE 43 | SOFTWARE. 44 | 45 | ============================================================================== 46 | The LLVM software contains code written by third parties. Such software will 47 | have its own individual LICENSE.TXT file in the directory in which it appears. 48 | This file will describe the copyrights, license, and restrictions which apply 49 | to that code. 50 | 51 | The disclaimer of warranty in the University of Illinois Open Source License 52 | applies to all code in the LLVM Distribution, and nothing in any of the 53 | other licenses gives permission to use the names of the LLVM Team or the 54 | University of Illinois to endorse or promote products derived from this 55 | Software. 56 | 57 | The following pieces of software have additional or alternate copyrights, 58 | licenses, and/or restrictions: 59 | 60 | Program Directory 61 | ------- --------- 62 | 63 | 64 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | set( LLVM_LINK_COMPONENTS 3 | ${LLVM_TARGETS_TO_BUILD} 4 | Analysis 5 | CodeGen 6 | Core 7 | IPO 8 | InstCombine 9 | Instrumentation 10 | MC 11 | MCParser 12 | ObjCARCOpts 13 | Option 14 | ScalarOpts 15 | Support 16 | TargetParser 17 | TransformUtils 18 | Vectorize 19 | ) 20 | 21 | include_directories(BEFORE 22 | ${CMAKE_CURRENT_SOURCE_DIR}/include 23 | ) 24 | add_subdirectory(lib) 25 | 26 | add_clang_executable(templight 27 | templight_driver.cpp 28 | ) 29 | 30 | target_link_libraries(templight 31 | PRIVATE 32 | clangBasic 33 | clangDriver 34 | clangFrontend 35 | clangFrontendTool 36 | clangSerialization 37 | clangTemplight 38 | ) 39 | 40 | set_target_properties(templight PROPERTIES VERSION ${CLANG_EXECUTABLE_VERSION}) 41 | 42 | add_dependencies(templight clang-headers) 43 | 44 | if(UNIX) 45 | set(CLANGXX_LINK_OR_COPY create_symlink) 46 | # Create a relative symlink 47 | set(templight_binary "templight${CMAKE_EXECUTABLE_SUFFIX}") 48 | else() 49 | set(CLANGXX_LINK_OR_COPY copy) 50 | set(templight_binary "${LLVM_RUNTIME_OUTPUT_INTDIR}/templight${CMAKE_EXECUTABLE_SUFFIX}") 51 | endif() 52 | 53 | # Create the templight++ symlink in the build directory. 54 | set(templight_pp "${LLVM_RUNTIME_OUTPUT_INTDIR}/templight++${CMAKE_EXECUTABLE_SUFFIX}") 55 | add_custom_command(TARGET templight POST_BUILD 56 | COMMAND ${CMAKE_COMMAND} -E ${CLANGXX_LINK_OR_COPY} "${templight_binary}" "${templight_pp}" 57 | WORKING_DIRECTORY "${LLVM_RUNTIME_OUTPUT_INTDIR}") 58 | 59 | set_property(DIRECTORY APPEND 60 | PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${templight_pp}) 61 | 62 | # Create the templight-cl symlink in the build directory. 63 | set(templight_cl "${LLVM_RUNTIME_OUTPUT_INTDIR}/templight-cl${CMAKE_EXECUTABLE_SUFFIX}") 64 | add_custom_command(TARGET templight POST_BUILD 65 | COMMAND ${CMAKE_COMMAND} -E ${CLANGXX_LINK_OR_COPY} "${templight_binary}" "${templight_cl}" 66 | WORKING_DIRECTORY "${LLVM_RUNTIME_OUTPUT_INTDIR}") 67 | 68 | set_property(DIRECTORY APPEND 69 | PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${templight_cl}) 70 | 71 | install(TARGETS templight 72 | RUNTIME DESTINATION bin) 73 | 74 | # Create the templight-cl symlinks at installation time. 75 | install(SCRIPT templight_symlink.cmake -DCMAKE_INSTALL_PREFIX=\"${CMAKE_INSTALL_PREFIX}\") 76 | 77 | # Configure plist creation for OS X. 78 | set (TOOL_INFO_PLIST "Info.plist" CACHE STRING "Plist name") 79 | if (APPLE) 80 | if (CLANG_VENDOR) 81 | set(TOOL_INFO_NAME "${CLANG_VENDOR} templight") 82 | else() 83 | set(TOOL_INFO_NAME "templight") 84 | endif() 85 | 86 | set(TOOL_INFO_UTI "${CLANG_VENDOR_UTI}") 87 | set(TOOL_INFO_VERSION "${CLANG_VERSION}") 88 | if (LLVM_SUBMIT_VERSION) 89 | set(TOOL_INFO_BUILD_VERSION 90 | "${LLVM_SUBMIT_VERSION}.${LLVM_SUBMIT_SUBVERSION}") 91 | endif() 92 | 93 | set(TOOL_INFO_PLIST_OUT "${CMAKE_CURRENT_BINARY_DIR}/${TOOL_INFO_PLIST}") 94 | target_link_libraries(templight 95 | PRIVATE 96 | "-Wl,-sectcreate,__TEXT,__info_plist,${TOOL_INFO_PLIST_OUT}") 97 | configure_file("${TOOL_INFO_PLIST}.in" "${TOOL_INFO_PLIST_OUT}" @ONLY) 98 | 99 | set(TOOL_INFO_UTI) 100 | set(TOOL_INFO_NAME) 101 | set(TOOL_INFO_VERSION) 102 | set(TOOL_INFO_BUILD_VERSION) 103 | endif() 104 | 105 | if(CLANG_ORDER_FILE) 106 | target_link_libraries(templight PRIVATE "-Wl,-order_file,${CLANG_ORDER_FILE}") 107 | endif() 108 | 109 | if(WITH_POLLY AND LINK_POLLY_INTO_TOOLS) 110 | target_link_libraries(templight PRIVATE Polly) 111 | if(POLLY_LINK_LIBS) 112 | foreach(lib ${POLLY_LINK_LIBS}) 113 | target_link_libraries(templight PRIVATE ${lib}) 114 | endforeach(lib) 115 | endif(POLLY_LINK_LIBS) 116 | endif(WITH_POLLY AND LINK_POLLY_INTO_TOOLS) 117 | 118 | set(TEMPLIGHT_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 119 | set(TEMPLIGHT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") 120 | 121 | if(CLANG_INCLUDE_TESTS) 122 | add_subdirectory(test) 123 | add_subdirectory(unittests) 124 | endif() 125 | -------------------------------------------------------------------------------- /utils/ExtraWriters/TemplightExtraWriters.h: -------------------------------------------------------------------------------- 1 | //===- TemplightProtobufWriter.h --------------------*- C++ -*-------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LLVM_CLANG_TEMPLIGHT_EXTRA_WRITERS_H 11 | #define LLVM_CLANG_TEMPLIGHT_EXTRA_WRITERS_H 12 | 13 | #include "PrintableTemplightEntries.h" 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | namespace llvm { 21 | namespace yaml { 22 | class Output; 23 | } 24 | } // namespace llvm 25 | 26 | namespace clang { 27 | 28 | class TemplightYamlWriter : public TemplightWriter { 29 | public: 30 | TemplightYamlWriter(llvm::raw_ostream &aOS); 31 | ~TemplightYamlWriter(); 32 | 33 | void initialize(const std::string &aSourceName = "") override; 34 | void finalize() override; 35 | 36 | void printEntry(const PrintableTemplightEntryBegin &aEntry) override; 37 | void printEntry(const PrintableTemplightEntryEnd &aEntry) override; 38 | 39 | private: 40 | std::unique_ptr Output; 41 | }; 42 | 43 | class TemplightXmlWriter : public TemplightWriter { 44 | public: 45 | TemplightXmlWriter(llvm::raw_ostream &aOS); 46 | ~TemplightXmlWriter(); 47 | 48 | void initialize(const std::string &aSourceName = "") override; 49 | void finalize() override; 50 | 51 | void printEntry(const PrintableTemplightEntryBegin &aEntry) override; 52 | void printEntry(const PrintableTemplightEntryEnd &aEntry) override; 53 | }; 54 | 55 | class TemplightTextWriter : public TemplightWriter { 56 | public: 57 | TemplightTextWriter(llvm::raw_ostream &aOS); 58 | ~TemplightTextWriter(); 59 | 60 | void initialize(const std::string &aSourceName = "") override; 61 | void finalize() override; 62 | 63 | void printEntry(const PrintableTemplightEntryBegin &aEntry) override; 64 | void printEntry(const PrintableTemplightEntryEnd &aEntry) override; 65 | }; 66 | 67 | struct RecordedDFSEntryTree; 68 | struct EntryTraversalTask; 69 | 70 | class TemplightTreeWriter : public TemplightWriter { 71 | public: 72 | TemplightTreeWriter(llvm::raw_ostream &aOS); 73 | ~TemplightTreeWriter(); 74 | 75 | void initialize(const std::string &aSourceName = "") override; 76 | void finalize() override; 77 | 78 | void printEntry(const PrintableTemplightEntryBegin &aEntry) override; 79 | void printEntry(const PrintableTemplightEntryEnd &aEntry) override; 80 | 81 | protected: 82 | virtual void openPrintedTreeNode(const EntryTraversalTask &aNode) = 0; 83 | virtual void closePrintedTreeNode(const EntryTraversalTask &aNode) = 0; 84 | 85 | virtual void initializeTree(const std::string &aSourceName) = 0; 86 | virtual void finalizeTree() = 0; 87 | 88 | std::unique_ptr p_tree; 89 | }; 90 | 91 | class TemplightNestedXMLWriter : public TemplightTreeWriter { 92 | public: 93 | TemplightNestedXMLWriter(llvm::raw_ostream &aOS); 94 | ~TemplightNestedXMLWriter(); 95 | 96 | protected: 97 | void openPrintedTreeNode(const EntryTraversalTask &aNode) override; 98 | void closePrintedTreeNode(const EntryTraversalTask &aNode) override; 99 | 100 | void initializeTree(const std::string &aSourceName = "") override; 101 | void finalizeTree() override; 102 | }; 103 | 104 | class TemplightGraphMLWriter : public TemplightTreeWriter { 105 | public: 106 | TemplightGraphMLWriter(llvm::raw_ostream &aOS); 107 | ~TemplightGraphMLWriter(); 108 | 109 | protected: 110 | void openPrintedTreeNode(const EntryTraversalTask &aNode) override; 111 | void closePrintedTreeNode(const EntryTraversalTask &aNode) override; 112 | 113 | void initializeTree(const std::string &aSourceName = "") override; 114 | void finalizeTree() override; 115 | 116 | private: 117 | int last_edge_id; 118 | }; 119 | 120 | class TemplightGraphVizWriter : public TemplightTreeWriter { 121 | public: 122 | TemplightGraphVizWriter(llvm::raw_ostream &aOS); 123 | ~TemplightGraphVizWriter(); 124 | 125 | protected: 126 | void openPrintedTreeNode(const EntryTraversalTask &aNode) override; 127 | void closePrintedTreeNode(const EntryTraversalTask &aNode) override; 128 | 129 | void initializeTree(const std::string &aSourceName = "") override; 130 | void finalizeTree() override; 131 | }; 132 | 133 | /* 134 | class ProtobufPrinter : public TemplightTracer::TracePrinter { 135 | protected: 136 | void startTraceImpl() override { 137 | // get the source name from the source manager: 138 | FileID fileID = TheSema.getSourceManager().getMainFileID(); 139 | std::string src_name = 140 | TheSema.getSourceManager().getFileEntryForID(fileID)->getName(); 141 | Writer.initialize(src_name); 142 | }; 143 | void endTraceImpl() override { 144 | Writer.finalize(); 145 | Writer.dumpOnStream(*this->TraceOS); 146 | }; 147 | 148 | void printEntryImpl(const PrintableTemplightEntryBegin& Entry) override { 149 | Writer.printEntry(Entry); 150 | }; 151 | 152 | void printEntryImpl(const PrintableTemplightEntryEnd& Entry) override { 153 | Writer.printEntry(Entry); 154 | }; 155 | 156 | public: 157 | 158 | ProtobufPrinter(const Sema &aSema, const std::string &Output) : 159 | TemplightTracer::TracePrinter(aSema, Output) { }; 160 | 161 | private: 162 | TemplightProtobufWriter Writer; 163 | }; 164 | */ 165 | 166 | } // namespace clang 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /lib/TemplightEntryPrinter.cpp: -------------------------------------------------------------------------------- 1 | //===- TemplightEntryPrinter.cpp --------------------*- C++ -*-------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "TemplightEntryPrinter.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace clang { 18 | 19 | void TemplightEntryPrinter::skipEntry() { 20 | if (SkippedEndingsCount) { 21 | ++SkippedEndingsCount; // note: this is needed because skipped entries are 22 | // begin entries for which "shouldIgnoreEntry" is 23 | // never called. 24 | return; // Already skipping entries. 25 | } 26 | SkippedEndingsCount = 1; 27 | } 28 | 29 | bool TemplightEntryPrinter::shouldIgnoreEntry( 30 | const PrintableTemplightEntryBegin &Entry) { 31 | // Check the black-lists: 32 | // (1) Is currently ignoring entries? 33 | if (SkippedEndingsCount) { 34 | ++SkippedEndingsCount; 35 | return true; 36 | } 37 | // (2) Regexes: 38 | if ((CoRegex && (CoRegex->match(Entry.Name))) || 39 | (IdRegex && (IdRegex->match(Entry.Name)))) { 40 | skipEntry(); 41 | return true; 42 | } 43 | 44 | return false; 45 | } 46 | 47 | bool TemplightEntryPrinter::shouldIgnoreEntry( 48 | const PrintableTemplightEntryEnd &Entry) { 49 | // Check the black-lists: 50 | // (1) Is currently ignoring entries? 51 | if (SkippedEndingsCount) { 52 | --SkippedEndingsCount; 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | void TemplightEntryPrinter::printEntry( 59 | const PrintableTemplightEntryBegin &Entry) { 60 | if (shouldIgnoreEntry(Entry)) 61 | return; 62 | 63 | if (p_writer) 64 | p_writer->printEntry(Entry); 65 | } 66 | 67 | void TemplightEntryPrinter::printEntry( 68 | const PrintableTemplightEntryEnd &Entry) { 69 | if (shouldIgnoreEntry(Entry)) 70 | return; 71 | 72 | if (p_writer) 73 | p_writer->printEntry(Entry); 74 | } 75 | 76 | void TemplightEntryPrinter::initialize(const std::string &SourceName) { 77 | if (p_writer) 78 | p_writer->initialize(SourceName); 79 | } 80 | 81 | void TemplightEntryPrinter::finalize() { 82 | if (p_writer) 83 | p_writer->finalize(); 84 | } 85 | 86 | TemplightEntryPrinter::TemplightEntryPrinter(const std::string &Output) 87 | : SkippedEndingsCount(0), TraceOS(0) { 88 | if (Output == "-") { 89 | TraceOS = &llvm::outs(); 90 | } else { 91 | std::error_code error; 92 | TraceOS = new llvm::raw_fd_ostream(Output, error, llvm::sys::fs::OF_None); 93 | if (error) { 94 | llvm::errs() << "Error: [Templight] Can not open file to write trace of " 95 | "template instantiations: " 96 | << Output << " Error: " << error.message(); 97 | TraceOS = 0; 98 | return; 99 | } 100 | } 101 | } 102 | 103 | TemplightEntryPrinter::~TemplightEntryPrinter() { 104 | p_writer.reset(); // Delete writer before the trace-OS. 105 | if (TraceOS) { 106 | TraceOS->flush(); 107 | if (TraceOS != &llvm::outs()) 108 | delete TraceOS; 109 | } 110 | } 111 | 112 | bool TemplightEntryPrinter::isValid() const { return p_writer.get(); } 113 | 114 | llvm::raw_ostream *TemplightEntryPrinter::getTraceStream() const { 115 | return TraceOS; 116 | } 117 | 118 | void TemplightEntryPrinter::takeWriter(TemplightWriter *aPWriter) { 119 | p_writer.reset(aPWriter); 120 | } 121 | 122 | void TemplightEntryPrinter::readBlacklists(const std::string &BLFilename) { 123 | if (BLFilename.empty()) { 124 | CoRegex.reset(); 125 | IdRegex.reset(); 126 | return; 127 | } 128 | 129 | std::string CoPattern, IdPattern; 130 | 131 | llvm::ErrorOr> file_epbuf = 132 | llvm::MemoryBuffer::getFile(llvm::Twine(BLFilename)); 133 | if (!file_epbuf || (!file_epbuf.get())) { 134 | llvm::errs() 135 | << "Error: [Templight-Action] Could not open the blacklist file!\n"; 136 | CoRegex.reset(); 137 | IdRegex.reset(); 138 | return; 139 | } 140 | 141 | llvm::Regex findCo("^context "); 142 | llvm::Regex findId("^identifier "); 143 | 144 | const char *it = file_epbuf.get()->getBufferStart(); 145 | const char *it_mark = file_epbuf.get()->getBufferStart(); 146 | const char *it_end = file_epbuf.get()->getBufferEnd(); 147 | 148 | while (it_mark != it_end) { 149 | it_mark = std::find(it, it_end, '\n'); 150 | if (*(it_mark - 1) == '\r') 151 | --it_mark; 152 | llvm::StringRef curLine(&(*it), it_mark - it); 153 | if (findCo.match(curLine)) { 154 | if (!CoPattern.empty()) 155 | CoPattern += '|'; 156 | CoPattern += '('; 157 | CoPattern.append(&(*(it + 8)), it_mark - it - 8); 158 | CoPattern += ')'; 159 | } else if (findId.match(curLine)) { 160 | if (!IdPattern.empty()) 161 | IdPattern += '|'; 162 | IdPattern += '('; 163 | IdPattern.append(&(*(it + 11)), it_mark - it - 11); 164 | IdPattern += ')'; 165 | } 166 | while ((it_mark != it_end) && ((*it_mark == '\n') || (*it_mark == '\r'))) 167 | ++it_mark; 168 | it = it_mark; 169 | } 170 | 171 | CoRegex.reset(new llvm::Regex(CoPattern)); 172 | IdRegex.reset(new llvm::Regex(IdPattern)); 173 | return; 174 | } 175 | 176 | } // namespace clang 177 | -------------------------------------------------------------------------------- /lib/TemplightAction.cpp: -------------------------------------------------------------------------------- 1 | //===- TemplightAction.cpp --------------------------------*- C++-*--------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "TemplightAction.h" 11 | #include "TemplightDebugger.h" 12 | #include "TemplightTracer.h" 13 | 14 | #include "clang/Basic/FileManager.h" 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | namespace clang { 26 | 27 | std::unique_ptr 28 | TemplightAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 29 | return WrapperFrontendAction::CreateASTConsumer(CI, InFile); 30 | } 31 | bool TemplightAction::BeginInvocation(CompilerInstance &CI) { 32 | return WrapperFrontendAction::BeginInvocation(CI); 33 | } 34 | bool TemplightAction::BeginSourceFileAction(CompilerInstance &CI) { 35 | return WrapperFrontendAction::BeginSourceFileAction(CI); 36 | } 37 | 38 | std::string TemplightAction::CreateOutputFilename( 39 | CompilerInstance *CI, const std::string &OptOutputName, 40 | bool OptInstProfiler, bool OptOutputToStdOut, bool OptMemoryProfile) { 41 | std::string result; 42 | 43 | if (!OptInstProfiler) { 44 | return result; // no need for an output-filename. 45 | } 46 | 47 | if (OptOutputToStdOut) { 48 | return "-"; 49 | } 50 | if (CI && OptOutputName.empty()) { 51 | result = CI->getFrontendOpts().OutputFile; 52 | } else { 53 | result = OptOutputName; 54 | } 55 | 56 | // Should never get executed. 57 | if (CI && result.empty()) { 58 | // then, derive output name from the input name: 59 | if (CI->hasSourceManager()) { 60 | FileID fileID = CI->getSourceManager().getMainFileID(); 61 | OptionalFileEntryRef file_ref = 62 | CI->getSourceManager().getFileEntryRefForID(fileID); 63 | if (file_ref.has_value()) { 64 | result = file_ref->getName().str(); 65 | } else {// or, last resort: 66 | result = "a"; 67 | } 68 | } else { // or, last resort: 69 | result = "a"; 70 | } 71 | } 72 | 73 | if (result.rfind(".trace.") == std::string::npos) { 74 | result += (OptMemoryProfile ? ".memory.trace." : ".trace."); 75 | result += "pbf"; 76 | } 77 | 78 | return result; 79 | } 80 | 81 | void TemplightAction::EnsureHasSema(CompilerInstance &CI) { 82 | if (!CI.hasSema()) { 83 | // This part is normally done by ASTFrontEndAction, but needs to happen 84 | // before Templight observers can be created ----------------------->> 85 | // FIXME: Move the truncation aspect of this into Sema, we delayed this till 86 | // here so the source manager would be initialized. 87 | if (hasCodeCompletionSupport() && 88 | !CI.getFrontendOpts().CodeCompletionAt.FileName.empty()) 89 | CI.createCodeCompletionConsumer(); 90 | 91 | // Use a code completion consumer? 92 | CodeCompleteConsumer *CompletionConsumer = nullptr; 93 | if (CI.hasCodeCompletionConsumer()) 94 | CompletionConsumer = &CI.getCodeCompletionConsumer(); 95 | 96 | CI.createSema(getTranslationUnitKind(), CompletionConsumer); 97 | //<<-------------------------------------------------------------- 98 | } 99 | } 100 | 101 | void TemplightAction::ExecuteAction() { 102 | 103 | CompilerInstance &CI = WrapperFrontendAction::getCompilerInstance(); 104 | if (!CI.hasPreprocessor()) 105 | return; 106 | 107 | if (InstProfiler) { 108 | EnsureHasSema(CI); 109 | 110 | std::unique_ptr p_t( 111 | new TemplightTracer(CI.getSema(), OutputFilename, MemoryProfile, 112 | OutputInSafeMode, IgnoreSystemInst)); 113 | p_t->readBlacklists(BlackListFilename); 114 | CI.getSema().TemplateInstCallbacks.push_back(std::move(p_t)); 115 | } 116 | if (InteractiveDebug) { 117 | EnsureHasSema(CI); 118 | 119 | std::unique_ptr p_t( 120 | new TemplightDebugger(CI.getSema(), MemoryProfile, IgnoreSystemInst)); 121 | p_t->readBlacklists(BlackListFilename); 122 | CI.getSema().TemplateInstCallbacks.push_back(std::move(p_t)); 123 | } 124 | 125 | WrapperFrontendAction::ExecuteAction(); 126 | } 127 | void TemplightAction::EndSourceFileAction() { 128 | WrapperFrontendAction::EndSourceFileAction(); 129 | } 130 | 131 | bool TemplightAction::usesPreprocessorOnly() const { 132 | return WrapperFrontendAction::usesPreprocessorOnly(); 133 | } 134 | TranslationUnitKind TemplightAction::getTranslationUnitKind() { 135 | return WrapperFrontendAction::getTranslationUnitKind(); 136 | } 137 | bool TemplightAction::hasPCHSupport() const { 138 | return WrapperFrontendAction::hasPCHSupport(); 139 | } 140 | bool TemplightAction::hasASTFileSupport() const { 141 | return WrapperFrontendAction::hasASTFileSupport(); 142 | } 143 | bool TemplightAction::hasIRSupport() const { 144 | return WrapperFrontendAction::hasIRSupport(); 145 | } 146 | bool TemplightAction::hasCodeCompletionSupport() const { 147 | return WrapperFrontendAction::hasCodeCompletionSupport(); 148 | } 149 | 150 | TemplightAction::TemplightAction(std::unique_ptr WrappedAction) 151 | : WrapperFrontendAction(std::move(WrappedAction)), InstProfiler(false), 152 | OutputToStdOut(false), MemoryProfile(false), OutputInSafeMode(false), 153 | IgnoreSystemInst(false), InteractiveDebug(false) {} 154 | 155 | } // namespace clang 156 | -------------------------------------------------------------------------------- /include/ThinProtobuf.h: -------------------------------------------------------------------------------- 1 | //===- TemplightProtobufWriter.cpp -----------------------*- C++ -*--------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LLVM_SUPPORT_THIN_PROTOBUF_H 11 | #define LLVM_SUPPORT_THIN_PROTOBUF_H 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | namespace llvm { 21 | 22 | namespace protobuf { 23 | 24 | namespace { 25 | 26 | union float_to_ulong { 27 | float f; 28 | std::uint32_t ui32; 29 | }; 30 | 31 | union double_to_ulong { 32 | double d; 33 | std::uint64_t ui64; 34 | std::uint32_t ui32[2]; 35 | }; 36 | 37 | union int64_to_uint64 { 38 | std::int64_t i64; 39 | std::uint64_t ui64; 40 | }; 41 | 42 | } // namespace 43 | 44 | inline std::uint64_t loadVarInt(StringRef &p_buf) { 45 | std::uint64_t u = 0; 46 | if (p_buf.empty()) 47 | return u; 48 | std::uint8_t shifts = 0; 49 | while (p_buf.front() & 0x80) { 50 | u |= (p_buf.front() & 0x7F) << shifts; 51 | p_buf = p_buf.drop_front(1); 52 | if (p_buf.empty()) 53 | return u; 54 | shifts += 7; 55 | }; 56 | u |= (p_buf.front() & 0x7F) << shifts; 57 | p_buf = p_buf.drop_front(1); 58 | return u; 59 | } 60 | 61 | template struct getVarIntWire { 62 | static const unsigned int value = (tag << 3); 63 | }; 64 | 65 | template struct getIntWire { 66 | static const unsigned int value = (tag << 3); 67 | }; 68 | 69 | inline std::int64_t loadSInt(StringRef &p_buf) { 70 | std::uint64_t u = loadVarInt(p_buf); 71 | return (u >> 1) ^ (-static_cast(u & 1)); 72 | } 73 | 74 | template struct getSIntWire { 75 | static const unsigned int value = (tag << 3); 76 | }; 77 | 78 | inline double loadDouble(StringRef &p_buf) { 79 | if (p_buf.size() < sizeof(double_to_ulong)) { 80 | p_buf = p_buf.drop_front(p_buf.size()); 81 | return double(0.0); 82 | }; 83 | double_to_ulong tmp; 84 | std::memcpy(reinterpret_cast(&tmp), p_buf.data(), 85 | sizeof(double_to_ulong)); 86 | p_buf = p_buf.drop_front(sizeof(double_to_ulong)); 87 | tmp.ui64 = llvm::support::endian::byte_swap< 88 | std::uint64_t, llvm::endianness::little>(tmp.ui64); 89 | return tmp.d; 90 | } 91 | 92 | template struct getDoubleWire { 93 | static const unsigned int value = (tag << 3) | 1; 94 | }; 95 | 96 | inline float loadFloat(StringRef &p_buf) { 97 | if (p_buf.size() < sizeof(float_to_ulong)) { 98 | p_buf = p_buf.drop_front(p_buf.size()); 99 | return float(0.0); 100 | }; 101 | float_to_ulong tmp; 102 | std::memcpy(reinterpret_cast(&tmp), p_buf.data(), 103 | sizeof(float_to_ulong)); 104 | p_buf = p_buf.drop_front(sizeof(float_to_ulong)); 105 | tmp.ui32 = llvm::support::endian::byte_swap< 106 | std::uint32_t, llvm::endianness::little>(tmp.ui32); 107 | return tmp.f; 108 | } 109 | 110 | template struct getFloatWire { 111 | static const unsigned int value = (tag << 3) | 5; 112 | }; 113 | 114 | inline bool loadBool(StringRef &p_buf) { 115 | if (p_buf.empty()) 116 | return false; 117 | char tmp = p_buf.front(); 118 | p_buf = p_buf.drop_front(1); 119 | return tmp; 120 | } 121 | 122 | template struct getBoolWire { 123 | static const unsigned int value = (tag << 3); 124 | }; 125 | 126 | inline std::string loadString(StringRef &p_buf) { 127 | unsigned int u = loadVarInt(p_buf); 128 | if (p_buf.size() < u) { 129 | p_buf = p_buf.drop_front(p_buf.size()); 130 | return std::string(); 131 | }; 132 | std::string s(p_buf.data(), u); 133 | p_buf = p_buf.drop_front(u); 134 | return s; // NRVO 135 | } 136 | 137 | template struct getStringWire { 138 | static const unsigned int value = (tag << 3) | 2; 139 | }; 140 | 141 | inline void skipData(StringRef &p_buf, unsigned int wire) { 142 | switch (wire & 0x7) { 143 | case 0: 144 | loadVarInt(p_buf); 145 | break; 146 | case 1: 147 | if (p_buf.size() < sizeof(double_to_ulong)) 148 | p_buf = p_buf.drop_front(p_buf.size()); 149 | else 150 | p_buf = p_buf.drop_front(sizeof(double_to_ulong)); 151 | break; 152 | case 2: { 153 | unsigned int u = loadVarInt(p_buf); 154 | if (p_buf.size() < u) 155 | p_buf = p_buf.drop_front(p_buf.size()); 156 | else 157 | p_buf = p_buf.drop_front(u); 158 | break; 159 | }; 160 | case 5: 161 | if (p_buf.size() < sizeof(float_to_ulong)) 162 | p_buf = p_buf.drop_front(p_buf.size()); 163 | else 164 | p_buf = p_buf.drop_front(sizeof(float_to_ulong)); 165 | break; 166 | default: 167 | break; 168 | } 169 | } 170 | 171 | inline void saveVarInt(llvm::raw_ostream &OS, std::uint64_t u) { 172 | std::uint8_t buf[] = { 173 | 0, 0, 0, 0, 0, 174 | 0, 0, 0, 0, 0}; // 80-bits, supports at most a 64-bit varint. 175 | std::uint8_t *pbuf = buf; 176 | *pbuf = (u & 0x7F); 177 | u >>= 7; 178 | while (u) { 179 | *pbuf |= 0x80; // set first msb because there is more to come. 180 | pbuf++; 181 | *pbuf = (u & 0x7F); 182 | u >>= 7; 183 | }; 184 | OS.write(reinterpret_cast(buf), pbuf - buf + 1); 185 | } 186 | 187 | inline void saveVarInt(llvm::raw_ostream &OS, unsigned int tag, 188 | std::uint64_t u) { 189 | saveVarInt(OS, (tag << 3)); // wire-type 0: Varint. 190 | saveVarInt(OS, u); 191 | } 192 | 193 | inline void saveInt(llvm::raw_ostream &OS, unsigned int tag, std::int64_t i) { 194 | saveVarInt(OS, (tag << 3)); // wire-type 0: Varint. 195 | int64_to_uint64 tmp; 196 | tmp.i64 = i; 197 | saveVarInt(OS, tmp.ui64); 198 | } 199 | 200 | inline void saveSInt(llvm::raw_ostream &OS, std::int64_t i) { 201 | // Apply the ZigZag encoding for the sign: 202 | saveVarInt(OS, (i << 1) ^ (i >> (sizeof(std::int64_t) * 8 - 1))); 203 | } 204 | 205 | inline void saveSInt(llvm::raw_ostream &OS, unsigned int tag, std::int64_t i) { 206 | saveVarInt(OS, (tag << 3)); // wire-type 0: Varint. 207 | saveSInt(OS, i); 208 | } 209 | 210 | inline void saveDouble(llvm::raw_ostream &OS, double d) { 211 | double_to_ulong tmp = {d}; 212 | tmp.ui64 = llvm::support::endian::byte_swap< 213 | std::uint64_t, llvm::endianness::little>(tmp.ui64); 214 | OS.write(reinterpret_cast(&tmp), sizeof(double_to_ulong)); 215 | } 216 | 217 | inline void saveDouble(llvm::raw_ostream &OS, unsigned int tag, double d) { 218 | saveVarInt(OS, (tag << 3) | 1); // wire-type 1: 64-bit. 219 | saveDouble(OS, d); 220 | } 221 | 222 | inline void saveFloat(llvm::raw_ostream &OS, float d) { 223 | float_to_ulong tmp = {d}; 224 | tmp.ui32 = llvm::support::endian::byte_swap< 225 | std::uint32_t, llvm::endianness::little>(tmp.ui32); 226 | OS.write(reinterpret_cast(&tmp), sizeof(float_to_ulong)); 227 | } 228 | 229 | inline void saveFloat(llvm::raw_ostream &OS, unsigned int tag, float d) { 230 | saveVarInt(OS, (tag << 3) | 5); // wire-type 5: 32-bit. 231 | saveFloat(OS, d); 232 | } 233 | 234 | inline void saveBool(llvm::raw_ostream &OS, bool b) { 235 | char tmp = 0; 236 | if (b) 237 | tmp = 1; 238 | OS.write(&tmp, 1); 239 | } 240 | 241 | inline void saveBool(llvm::raw_ostream &OS, unsigned int tag, bool b) { 242 | saveVarInt(OS, (tag << 3)); // wire-type 0: varint. 243 | saveBool(OS, b); 244 | } 245 | 246 | inline void saveString(llvm::raw_ostream &OS, StringRef s) { 247 | unsigned int u = s.size(); 248 | saveVarInt(OS, u); 249 | OS.write(s.data(), u); 250 | } 251 | 252 | inline void saveString(llvm::raw_ostream &OS, unsigned int tag, StringRef s) { 253 | saveVarInt(OS, (tag << 3) | 2); // wire-type 2: length-delimited. 254 | saveString(OS, s); 255 | } 256 | 257 | } // namespace protobuf 258 | 259 | } // namespace llvm 260 | 261 | #endif 262 | -------------------------------------------------------------------------------- /templight_clang_patch.diff: -------------------------------------------------------------------------------- 1 | diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td 2 | index 03b43ddd..f22af830 100644 3 | --- a/include/clang/Driver/CC1Options.td 4 | +++ b/include/clang/Driver/CC1Options.td 5 | @@ -501,6 +501,8 @@ def ftest_module_file_extension_EQ : 6 | "The argument is parsed as blockname:major:minor:hashed:user info">; 7 | def fconcepts_ts : Flag<["-"], "fconcepts-ts">, 8 | HelpText<"Enable C++ Extensions for Concepts.">; 9 | +def templight_profile : Flag<["-"], "templight-profile">, 10 | + HelpText<"Add timestamps to the templight-dump output">; 11 | 12 | let Group = Action_Group in { 13 | 14 | diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h 15 | index 768957ac..e1d4908c 100644 16 | --- a/include/clang/Frontend/FrontendOptions.h 17 | +++ b/include/clang/Frontend/FrontendOptions.h 18 | @@ -301,6 +301,10 @@ public: 19 | 20 | /// Whether timestamps should be written to the produced PCH file. 21 | unsigned IncludeTimestamps : 1; 22 | + 23 | + /// Whether to add additional 24 | + /// information to Templight dumps. 25 | + unsigned TemplightProfile : 1; 26 | 27 | CodeCompleteOptions CodeCompleteOpts; 28 | 29 | @@ -447,7 +451,8 @@ public: 30 | SkipFunctionBodies(false), UseGlobalModuleIndex(true), 31 | GenerateGlobalModuleIndex(true), ASTDumpDecls(false), 32 | ASTDumpLookups(false), BuildingImplicitModule(false), 33 | - ModulesEmbedAllFiles(false), IncludeTimestamps(true) {} 34 | + ModulesEmbedAllFiles(false), IncludeTimestamps(true), 35 | + TemplightProfile(false) {} 36 | 37 | /// getInputKindForExtension - Return the appropriate input kind for a file 38 | /// extension. For example, "c" would return InputKind::C. 39 | diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp 40 | index 67e15b41..19ef1194 100644 41 | --- a/lib/Frontend/CompilerInvocation.cpp 42 | +++ b/lib/Frontend/CompilerInvocation.cpp 43 | @@ -1509,6 +1509,7 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, 44 | Opts.ModulesEmbedFiles = Args.getAllArgValues(OPT_fmodules_embed_file_EQ); 45 | Opts.ModulesEmbedAllFiles = Args.hasArg(OPT_fmodules_embed_all_files); 46 | Opts.IncludeTimestamps = !Args.hasArg(OPT_fno_pch_timestamp); 47 | + Opts.TemplightProfile = Args.hasArg(OPT_templight_profile); 48 | 49 | Opts.CodeCompleteOpts.IncludeMacros 50 | = Args.hasArg(OPT_code_completion_macros); 51 | diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp 52 | index 8cb6a042..5cb70482 100644 53 | --- a/lib/Frontend/FrontendActions.cpp 54 | +++ b/lib/Frontend/FrontendActions.cpp 55 | @@ -26,6 +26,7 @@ 56 | #include "llvm/Support/Path.h" 57 | #include "llvm/Support/raw_ostream.h" 58 | #include "llvm/Support/YAMLTraits.h" 59 | +#include 60 | #include 61 | #include 62 | 63 | @@ -288,19 +289,14 @@ struct TemplightEntry { 64 | std::string Event; 65 | std::string DefinitionLocation; 66 | std::string PointOfInstantiation; 67 | + Optional TimeStamp; 68 | }; 69 | } // namespace 70 | 71 | namespace llvm { 72 | namespace yaml { 73 | template <> struct MappingTraits { 74 | - static void mapping(IO &io, TemplightEntry &fields) { 75 | - io.mapRequired("name", fields.Name); 76 | - io.mapRequired("kind", fields.Kind); 77 | - io.mapRequired("event", fields.Event); 78 | - io.mapRequired("orig", fields.DefinitionLocation); 79 | - io.mapRequired("poi", fields.PointOfInstantiation); 80 | - } 81 | + static void mapping(IO &io, TemplightEntry &fields); 82 | }; 83 | } // namespace yaml 84 | } // namespace llvm 85 | @@ -312,19 +308,46 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { 86 | public: 87 | void initialize(const Sema &) override {} 88 | 89 | - void finalize(const Sema &) override {} 90 | + void finalize(const Sema &) override { 91 | + if(isProfilingEnabled()) 92 | + for(auto &Entry : *TemplightEntries) 93 | + displayTemplightEntry(llvm::outs(), Entry); 94 | + } 95 | 96 | void atTemplateBegin(const Sema &TheSema, 97 | const CodeSynthesisContext &Inst) override { 98 | - displayTemplightEntry(llvm::outs(), TheSema, Inst); 99 | + TemplightEntry Entry = getTemplightEntry(TheSema, Inst); 100 | + 101 | + if(isProfilingEnabled()) 102 | + TemplightEntries->push_back(std::move(Entry)); 103 | + else 104 | + displayTemplightEntry(llvm::outs(), Entry); 105 | } 106 | 107 | void atTemplateEnd(const Sema &TheSema, 108 | const CodeSynthesisContext &Inst) override { 109 | - displayTemplightEntry(llvm::outs(), TheSema, Inst); 110 | + TemplightEntry Entry = getTemplightEntry(TheSema, Inst); 111 | + 112 | + if(isProfilingEnabled()) 113 | + TemplightEntries->push_back(std::move(Entry)); 114 | + else 115 | + displayTemplightEntry(llvm::outs(), Entry); 116 | + } 117 | + 118 | + void enableProfiling() { 119 | + TemplightEntries = std::vector(); 120 | } 121 | + 122 | + bool isProfilingEnabled() { 123 | + return TemplightEntries.hasValue(); 124 | + } 125 | + 126 | + static const std::chrono::time_point start; 127 | 128 | private: 129 | + 130 | + Optional> TemplightEntries = None; 131 | + 132 | static std::string toString(CodeSynthesisContext::SynthesisKind Kind) { 133 | switch (Kind) { 134 | case CodeSynthesisContext::TemplateInstantiation: 135 | @@ -353,15 +376,12 @@ private: 136 | return ""; 137 | } 138 | 139 | - template 140 | - static void displayTemplightEntry(llvm::raw_ostream &Out, const Sema &TheSema, 141 | - const CodeSynthesisContext &Inst) { 142 | + void displayTemplightEntry(llvm::raw_ostream &Out, 143 | + TemplightEntry& Entry) { 144 | std::string YAML; 145 | { 146 | llvm::raw_string_ostream OS(YAML); 147 | llvm::yaml::Output YO(OS); 148 | - TemplightEntry Entry = 149 | - getTemplightEntry(TheSema, Inst); 150 | llvm::yaml::EmptyContext Context; 151 | llvm::yaml::yamlize(YO, Entry, true, Context); 152 | } 153 | @@ -369,9 +389,13 @@ private: 154 | } 155 | 156 | template 157 | - static TemplightEntry getTemplightEntry(const Sema &TheSema, 158 | + TemplightEntry getTemplightEntry(const Sema &TheSema, 159 | const CodeSynthesisContext &Inst) { 160 | TemplightEntry Entry; 161 | + if (isProfilingEnabled()){ 162 | + auto end = std::chrono::high_resolution_clock::now(); 163 | + Entry.TimeStamp = std::chrono::nanoseconds(end-start).count(); 164 | + } 165 | Entry.Kind = toString(Inst.Kind); 166 | Entry.Event = BeginInstantiation ? "Begin" : "End"; 167 | if (auto *NamedTemplate = dyn_cast_or_null(Inst.Entity)) { 168 | @@ -394,8 +418,27 @@ private: 169 | return Entry; 170 | } 171 | }; 172 | + 173 | +const std::chrono::time_point 174 | + DefaultTemplateInstCallback::start = std::chrono::high_resolution_clock::now(); 175 | + 176 | } // namespace 177 | 178 | +namespace llvm { 179 | +namespace yaml { 180 | +void MappingTraits::mapping(IO &io, TemplightEntry &fields) 181 | +{ 182 | + io.mapRequired("name", fields.Name); 183 | + io.mapRequired("kind", fields.Kind); 184 | + io.mapRequired("event", fields.Event); 185 | + io.mapRequired("orig", fields.DefinitionLocation); 186 | + io.mapRequired("poi", fields.PointOfInstantiation); 187 | + if(fields.TimeStamp) 188 | + io.mapRequired("stamp", fields.TimeStamp.getValue()); 189 | +} 190 | +} // namespace yaml 191 | +} // namespace llvm 192 | + 193 | std::unique_ptr 194 | TemplightDumpAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 195 | return llvm::make_unique(); 196 | @@ -410,8 +453,11 @@ void TemplightDumpAction::ExecuteAction() { 197 | // here so the source manager would be initialized. 198 | EnsureSemaIsCreated(CI, *this); 199 | 200 | - CI.getSema().TemplateInstCallbacks.push_back( 201 | - llvm::make_unique()); 202 | + auto D = llvm::make_unique(); 203 | + if(CI.getFrontendOpts().TemplightProfile) 204 | + D->enableProfiling(); 205 | + 206 | + CI.getSema().TemplateInstCallbacks.push_back(std::move(D)); 207 | ASTFrontendAction::ExecuteAction(); 208 | } 209 | 210 | -------------------------------------------------------------------------------- /utils/ProtobufReader/TemplightProtobufReader.cpp: -------------------------------------------------------------------------------- 1 | //===- TemplightProtobufWriter.cpp -----------------*- C++ -*--------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "TemplightProtobufReader.h" 11 | 12 | #include "ThinProtobuf.h" 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | namespace clang { 20 | 21 | TemplightProtobufReader::TemplightProtobufReader() {} 22 | 23 | void TemplightProtobufReader::loadHeader(llvm::StringRef aSubBuffer) { 24 | // Set default values: 25 | Version = 0; 26 | SourceName = ""; 27 | 28 | while (aSubBuffer.size()) { 29 | unsigned int cur_wire = llvm::protobuf::loadVarInt(aSubBuffer); 30 | switch (cur_wire) { 31 | case llvm::protobuf::getVarIntWire<1>::value: 32 | Version = llvm::protobuf::loadVarInt(aSubBuffer); 33 | break; 34 | case llvm::protobuf::getStringWire<2>::value: 35 | SourceName = llvm::protobuf::loadString(aSubBuffer); 36 | break; 37 | default: 38 | llvm::protobuf::skipData(aSubBuffer, cur_wire); 39 | break; 40 | } 41 | } 42 | 43 | LastChunk = TemplightProtobufReader::Header; 44 | } 45 | 46 | void TemplightProtobufReader::loadDictionaryEntry(llvm::StringRef aSubBuffer) { 47 | // Set default values: 48 | std::string name = ""; 49 | llvm::SmallVector markers; 50 | 51 | while (aSubBuffer.size()) { 52 | unsigned int cur_wire = llvm::protobuf::loadVarInt(aSubBuffer); 53 | switch (cur_wire) { 54 | case llvm::protobuf::getStringWire<1>::value: 55 | name = llvm::protobuf::loadString(aSubBuffer); 56 | break; 57 | case llvm::protobuf::getVarIntWire<2>::value: 58 | markers.push_back(llvm::protobuf::loadVarInt(aSubBuffer)); 59 | break; 60 | default: 61 | llvm::protobuf::skipData(aSubBuffer, cur_wire); 62 | break; 63 | } 64 | } 65 | 66 | std::string::iterator it_name = std::find(name.begin(), name.end(), '\0'); 67 | llvm::SmallVector::iterator it_mark = markers.begin(); 68 | while ((it_name != name.end()) && (it_mark != markers.end())) { 69 | std::size_t offset = it_name - name.begin(); 70 | name.replace(it_name, it_name + 1, templateNameMap[*it_mark]); 71 | it_name = std::find(name.begin() + offset, name.end(), '\0'); 72 | ++it_mark; 73 | } 74 | 75 | templateNameMap.push_back(name); 76 | } 77 | 78 | static void loadLocation(llvm::StringRef aSubBuffer, 79 | std::vector &fileNameMap, 80 | std::string &FileName, int &Line, int &Column) { 81 | // Set default values: 82 | FileName = ""; 83 | std::size_t FileID = std::numeric_limits::max(); 84 | Line = 0; 85 | Column = 0; 86 | 87 | while (aSubBuffer.size()) { 88 | unsigned int cur_wire = llvm::protobuf::loadVarInt(aSubBuffer); 89 | switch (cur_wire) { 90 | case llvm::protobuf::getStringWire<1>::value: 91 | FileName = llvm::protobuf::loadString(aSubBuffer); 92 | break; 93 | case llvm::protobuf::getVarIntWire<2>::value: 94 | FileID = llvm::protobuf::loadVarInt(aSubBuffer); 95 | break; 96 | case llvm::protobuf::getVarIntWire<3>::value: 97 | Line = llvm::protobuf::loadVarInt(aSubBuffer); 98 | break; 99 | case llvm::protobuf::getVarIntWire<4>::value: 100 | Column = llvm::protobuf::loadVarInt(aSubBuffer); 101 | break; 102 | default: 103 | llvm::protobuf::skipData(aSubBuffer, cur_wire); 104 | break; 105 | } 106 | } 107 | 108 | if (FileID != std::numeric_limits::max()) { 109 | if (fileNameMap.size() <= FileID) 110 | fileNameMap.resize(FileID + 1); 111 | if (!FileName.empty()) { 112 | fileNameMap[FileID] = 113 | FileName; // overwrite existing names, if any, but there shouldn't be. 114 | } else { 115 | FileName = fileNameMap[FileID]; 116 | } 117 | } // else we don't care? 118 | } 119 | 120 | void TemplightProtobufReader::loadTemplateName(llvm::StringRef aSubBuffer) { 121 | // Set default values: 122 | LastBeginEntry.Name = ""; 123 | 124 | while (aSubBuffer.size()) { 125 | unsigned int cur_wire = llvm::protobuf::loadVarInt(aSubBuffer); 126 | switch (cur_wire) { 127 | case llvm::protobuf::getStringWire<1>::value: 128 | LastBeginEntry.Name = llvm::protobuf::loadString(aSubBuffer); 129 | break; 130 | case llvm::protobuf::getStringWire<2>::value: { 131 | LastBeginEntry.Name = llvm::protobuf::loadString(aSubBuffer); 132 | llvm::SmallVector UBuf; 133 | if (llvm::zlib::uncompress(LastBeginEntry.Name, UBuf, 134 | LastBeginEntry.Name.size() * 2) == 135 | llvm::zlib::StatusOK) 136 | LastBeginEntry.Name.assign(UBuf.begin(), UBuf.end()); 137 | else 138 | LastBeginEntry.Name = ""; 139 | break; 140 | } 141 | case llvm::protobuf::getVarIntWire<3>::value: { 142 | LastBeginEntry.Name = 143 | templateNameMap[llvm::protobuf::loadVarInt(aSubBuffer)]; 144 | break; 145 | } 146 | default: 147 | llvm::protobuf::skipData(aSubBuffer, cur_wire); 148 | break; 149 | } 150 | } 151 | } 152 | 153 | void TemplightProtobufReader::loadBeginEntry(llvm::StringRef aSubBuffer) { 154 | // Set default values: 155 | LastBeginEntry.SynthesisKind = 0; 156 | LastBeginEntry.Name = ""; 157 | LastBeginEntry.TimeStamp = 0.0; 158 | LastBeginEntry.MemoryUsage = 0; 159 | 160 | while (aSubBuffer.size()) { 161 | unsigned int cur_wire = llvm::protobuf::loadVarInt(aSubBuffer); 162 | switch (cur_wire) { 163 | case llvm::protobuf::getVarIntWire<1>::value: 164 | LastBeginEntry.SynthesisKind = llvm::protobuf::loadVarInt(aSubBuffer); 165 | break; 166 | case llvm::protobuf::getStringWire<2>::value: { 167 | std::uint64_t cur_size = llvm::protobuf::loadVarInt(aSubBuffer); 168 | loadTemplateName(aSubBuffer.slice(0, cur_size)); 169 | aSubBuffer = aSubBuffer.drop_front(cur_size); 170 | break; 171 | } 172 | case llvm::protobuf::getStringWire<3>::value: { 173 | std::uint64_t cur_size = llvm::protobuf::loadVarInt(aSubBuffer); 174 | loadLocation(aSubBuffer.slice(0, cur_size), fileNameMap, 175 | LastBeginEntry.FileName, LastBeginEntry.Line, 176 | LastBeginEntry.Column); 177 | aSubBuffer = aSubBuffer.drop_front(cur_size); 178 | break; 179 | } 180 | case llvm::protobuf::getDoubleWire<4>::value: 181 | LastBeginEntry.TimeStamp = llvm::protobuf::loadDouble(aSubBuffer); 182 | break; 183 | case llvm::protobuf::getVarIntWire<5>::value: 184 | LastBeginEntry.MemoryUsage = llvm::protobuf::loadVarInt(aSubBuffer); 185 | break; 186 | case llvm::protobuf::getStringWire<6>::value: { 187 | std::uint64_t cur_size = llvm::protobuf::loadVarInt(aSubBuffer); 188 | loadLocation(aSubBuffer.slice(0, cur_size), fileNameMap, 189 | LastBeginEntry.TempOri_FileName, LastBeginEntry.TempOri_Line, 190 | LastBeginEntry.TempOri_Column); 191 | aSubBuffer = aSubBuffer.drop_front(cur_size); 192 | break; 193 | } 194 | default: 195 | llvm::protobuf::skipData(aSubBuffer, cur_wire); 196 | break; 197 | } 198 | } 199 | 200 | LastChunk = TemplightProtobufReader::BeginEntry; 201 | } 202 | 203 | void TemplightProtobufReader::loadEndEntry(llvm::StringRef aSubBuffer) { 204 | // Set default values: 205 | LastEndEntry.TimeStamp = 0.0; 206 | LastEndEntry.MemoryUsage = 0; 207 | 208 | while (aSubBuffer.size()) { 209 | unsigned int cur_wire = llvm::protobuf::loadVarInt(aSubBuffer); 210 | switch (cur_wire) { 211 | case llvm::protobuf::getDoubleWire<1>::value: 212 | LastEndEntry.TimeStamp = llvm::protobuf::loadDouble(aSubBuffer); 213 | break; 214 | case llvm::protobuf::getVarIntWire<2>::value: 215 | LastEndEntry.MemoryUsage = llvm::protobuf::loadVarInt(aSubBuffer); 216 | break; 217 | default: 218 | llvm::protobuf::skipData(aSubBuffer, cur_wire); 219 | break; 220 | } 221 | } 222 | 223 | LastChunk = TemplightProtobufReader::EndEntry; 224 | } 225 | 226 | TemplightProtobufReader::LastChunkType 227 | TemplightProtobufReader::startOnBuffer(llvm::StringRef aBuffer) { 228 | buffer = aBuffer; 229 | fileNameMap.clear(); 230 | unsigned int cur_wire = llvm::protobuf::loadVarInt(buffer); 231 | if (cur_wire != llvm::protobuf::getStringWire<1>::value) { 232 | buffer = llvm::StringRef(); 233 | remainder_buffer = llvm::StringRef(); 234 | LastChunk = TemplightProtobufReader::EndOfFile; 235 | return LastChunk; 236 | } 237 | std::uint64_t cur_size = llvm::protobuf::loadVarInt(buffer); 238 | remainder_buffer = buffer.slice(cur_size, buffer.size()); 239 | buffer = buffer.slice(0, cur_size); 240 | return next(); 241 | } 242 | 243 | TemplightProtobufReader::LastChunkType TemplightProtobufReader::next() { 244 | if (buffer.empty()) { 245 | if (remainder_buffer.empty()) { 246 | LastChunk = TemplightProtobufReader::EndOfFile; 247 | return LastChunk; 248 | } else { 249 | return startOnBuffer(remainder_buffer); 250 | } 251 | } 252 | unsigned int cur_wire = llvm::protobuf::loadVarInt(buffer); 253 | switch (cur_wire) { 254 | case llvm::protobuf::getStringWire<1>::value: { 255 | std::uint64_t cur_size = llvm::protobuf::loadVarInt(buffer); 256 | loadHeader(buffer.slice(0, cur_size)); 257 | buffer = buffer.drop_front(cur_size); 258 | return LastChunk; 259 | }; 260 | case llvm::protobuf::getStringWire<2>::value: { 261 | std::uint64_t cur_size = llvm::protobuf::loadVarInt(buffer); 262 | llvm::StringRef sub_buffer = buffer.slice(0, cur_size); 263 | buffer = buffer.drop_front(cur_size); 264 | cur_wire = llvm::protobuf::loadVarInt(sub_buffer); 265 | cur_size = llvm::protobuf::loadVarInt(sub_buffer); 266 | switch (cur_wire) { 267 | case llvm::protobuf::getStringWire<1>::value: 268 | loadBeginEntry(sub_buffer); 269 | break; 270 | case llvm::protobuf::getStringWire<2>::value: 271 | loadEndEntry(sub_buffer); 272 | break; 273 | default: // ignore for fwd-compat. 274 | break; 275 | }; 276 | return LastChunk; 277 | }; 278 | case llvm::protobuf::getStringWire<3>::value: { 279 | std::uint64_t cur_size = llvm::protobuf::loadVarInt(buffer); 280 | loadDictionaryEntry(buffer.slice(0, cur_size)); 281 | buffer = buffer.drop_front(cur_size); 282 | LastChunk = TemplightProtobufReader::Other; 283 | return LastChunk; 284 | }; 285 | default: { // ignore for fwd-compat. 286 | llvm::protobuf::skipData(buffer, cur_wire); 287 | return next(); // tail-call 288 | }; 289 | } 290 | } 291 | 292 | } // namespace clang 293 | -------------------------------------------------------------------------------- /lib/TemplightTracer.cpp: -------------------------------------------------------------------------------- 1 | //===- TemplightTracer.cpp -----------------------*- C++ -*----------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "TemplightTracer.h" 11 | 12 | #include "PrintableTemplightEntries.h" 13 | #include "TemplightEntryPrinter.h" 14 | #include "TemplightProtobufWriter.h" 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | namespace clang { 32 | 33 | namespace { 34 | 35 | struct RawTemplightTraceEntry { 36 | bool IsTemplateBegin; 37 | std::size_t ParentBeginIdx; 38 | Sema::CodeSynthesisContext::SynthesisKind SynthesisKind; 39 | Decl *Entity; 40 | SourceLocation PointOfInstantiation; 41 | double TimeStamp; 42 | std::uint64_t MemoryUsage; 43 | 44 | static const std::size_t invalid_parent = ~std::size_t(0); 45 | 46 | RawTemplightTraceEntry() 47 | : IsTemplateBegin(true), ParentBeginIdx(invalid_parent), 48 | SynthesisKind(Sema::CodeSynthesisContext::TemplateInstantiation), 49 | Entity(0), TimeStamp(0.0), MemoryUsage(0){}; 50 | }; 51 | 52 | PrintableTemplightEntryBegin 53 | rawToPrintableBegin(const Sema &TheSema, const RawTemplightTraceEntry &Entry) { 54 | PrintableTemplightEntryBegin Ret; 55 | 56 | Ret.SynthesisKind = Entry.SynthesisKind; 57 | 58 | NamedDecl *NamedTemplate = dyn_cast_or_null(Entry.Entity); 59 | if (NamedTemplate) { 60 | llvm::raw_string_ostream OS(Ret.Name); 61 | NamedTemplate->getNameForDiagnostic(OS, TheSema.getLangOpts(), true); 62 | } 63 | 64 | PresumedLoc Loc = 65 | TheSema.getSourceManager().getPresumedLoc(Entry.PointOfInstantiation); 66 | if (!Loc.isInvalid()) { 67 | Ret.FileName = Loc.getFilename(); 68 | Ret.Line = Loc.getLine(); 69 | Ret.Column = Loc.getColumn(); 70 | } else { 71 | Ret.FileName = ""; 72 | Ret.Line = 0; 73 | Ret.Column = 0; 74 | } 75 | 76 | Ret.TimeStamp = Entry.TimeStamp; 77 | Ret.MemoryUsage = Entry.MemoryUsage; 78 | 79 | if (Entry.Entity) { 80 | PresumedLoc Loc = 81 | TheSema.getSourceManager().getPresumedLoc(Entry.Entity->getLocation()); 82 | if (!Loc.isInvalid()) { 83 | Ret.TempOri_FileName = Loc.getFilename(); 84 | Ret.TempOri_Line = Loc.getLine(); 85 | Ret.TempOri_Column = Loc.getColumn(); 86 | } else { 87 | Ret.TempOri_FileName = ""; 88 | Ret.TempOri_Line = 0; 89 | Ret.TempOri_Column = 0; 90 | } 91 | } 92 | 93 | return Ret; 94 | } 95 | 96 | PrintableTemplightEntryEnd 97 | rawToPrintableEnd(const Sema &TheSema, const RawTemplightTraceEntry &Entry) { 98 | return {Entry.TimeStamp, Entry.MemoryUsage}; 99 | } 100 | 101 | } // unnamed namespace 102 | 103 | class TemplightTracer::TracePrinter : public TemplightEntryPrinter { 104 | public: 105 | void skipRawEntry(const RawTemplightTraceEntry &Entry) { skipEntry(); } 106 | 107 | bool shouldIgnoreRawEntry(const RawTemplightTraceEntry &Entry) { 108 | 109 | // Avoid some duplication of memoization entries: 110 | if ((Entry.SynthesisKind == Sema::CodeSynthesisContext::Memoization) && 111 | LastClosedMemoization && (LastClosedMemoization == Entry.Entity)) { 112 | return true; 113 | } 114 | 115 | // if we have an end entry, we must ensure it corresponds to the current 116 | // begin entry: 117 | // these checks are a bit redundant and overly cautious, but better safe 118 | // than sorry when sanitizing. 119 | if ((!Entry.IsTemplateBegin) && 120 | ((TraceEntries.empty()) || 121 | (CurrentParentBegin == RawTemplightTraceEntry::invalid_parent) || 122 | (CurrentParentBegin >= TraceEntries.size()) || 123 | !((TraceEntries[CurrentParentBegin].SynthesisKind == 124 | Entry.SynthesisKind) && 125 | (TraceEntries[CurrentParentBegin].Entity == Entry.Entity)))) { 126 | return true; // ignore end entries that don't match the current begin 127 | // entry. 128 | } 129 | 130 | return false; 131 | }; 132 | 133 | void printOrSkipEntry(RawTemplightTraceEntry &Entry) { 134 | if (IgnoreSystemFlag && !Entry.PointOfInstantiation.isInvalid() && 135 | TheSema.getSourceManager().isInSystemHeader( 136 | Entry.PointOfInstantiation)) { 137 | skipRawEntry( 138 | Entry); // recursively skip all entries until end of this one. 139 | } else { 140 | if (Entry.IsTemplateBegin) { 141 | printEntry(rawToPrintableBegin(TheSema, Entry)); 142 | } else { 143 | printEntry(rawToPrintableEnd(TheSema, Entry)); 144 | } 145 | } 146 | }; 147 | 148 | void printCachedRawEntries() { 149 | for (std::vector::iterator it = 150 | TraceEntries.begin(); 151 | it != TraceEntries.end(); ++it) 152 | printOrSkipEntry(*it); 153 | TraceEntries.clear(); 154 | CurrentParentBegin = RawTemplightTraceEntry::invalid_parent; 155 | }; 156 | 157 | void printRawEntry(RawTemplightTraceEntry Entry, bool inSafeMode = false) { 158 | if (shouldIgnoreRawEntry(Entry)) 159 | return; 160 | 161 | if (inSafeMode) 162 | printOrSkipEntry(Entry); 163 | 164 | // Always maintain a stack of cached trace entries such that the sanity of 165 | // the traces can be enforced. 166 | if (Entry.IsTemplateBegin) { 167 | Entry.ParentBeginIdx = CurrentParentBegin; 168 | CurrentParentBegin = TraceEntries.size(); 169 | } else { // note: this point should not be reached if CurrentParentBegin is 170 | // not valid. 171 | Entry.ParentBeginIdx = TraceEntries[CurrentParentBegin].ParentBeginIdx; 172 | CurrentParentBegin = Entry.ParentBeginIdx; 173 | }; 174 | TraceEntries.push_back(Entry); 175 | 176 | if (Entry.IsTemplateBegin) 177 | LastClosedMemoization = nullptr; 178 | if (!Entry.IsTemplateBegin && 179 | (Entry.SynthesisKind == Sema::CodeSynthesisContext::Memoization)) 180 | LastClosedMemoization = Entry.Entity; 181 | 182 | if (!Entry.IsTemplateBegin && 183 | (Entry.SynthesisKind == TraceEntries.front().SynthesisKind) && 184 | (Entry.Entity == 185 | TraceEntries.front() 186 | .Entity)) { // did we reach the end of the top-level begin entry? 187 | if (!inSafeMode) { // if not in safe-mode, print out the cached entries. 188 | printCachedRawEntries(); 189 | } else { // if in safe-mode, simply clear the cached entries. 190 | TraceEntries.clear(); 191 | CurrentParentBegin = RawTemplightTraceEntry::invalid_parent; 192 | } 193 | } 194 | }; 195 | 196 | void startTrace() { 197 | // get the source name from the source manager: 198 | std::string src_name = "a"; 199 | FileID fileID = TheSema.getSourceManager().getMainFileID(); 200 | OptionalFileEntryRef file_ref = 201 | TheSema.getSourceManager().getFileEntryRefForID(fileID); 202 | if (file_ref.has_value()) { 203 | src_name = file_ref->getName().str(); 204 | } 205 | initialize(src_name); 206 | }; 207 | 208 | void endTrace() { 209 | printCachedRawEntries(); 210 | finalize(); 211 | }; 212 | 213 | TracePrinter(const Sema &aSema, const std::string &Output, 214 | bool IgnoreSystem = false) 215 | : TemplightEntryPrinter(Output), TheSema(aSema), 216 | LastClosedMemoization(nullptr), 217 | CurrentParentBegin(RawTemplightTraceEntry::invalid_parent), 218 | IgnoreSystemFlag(IgnoreSystem){}; 219 | 220 | ~TracePrinter(){}; 221 | 222 | const Sema &TheSema; 223 | 224 | std::vector TraceEntries; 225 | Decl *LastClosedMemoization; 226 | std::size_t CurrentParentBegin; 227 | 228 | unsigned IgnoreSystemFlag : 1; 229 | }; 230 | 231 | void TemplightTracer::atTemplateBegin(const Sema &TheSema, 232 | const Sema::CodeSynthesisContext &Inst) { 233 | if (!Printer) 234 | return; 235 | 236 | RawTemplightTraceEntry Entry; 237 | 238 | Entry.IsTemplateBegin = true; 239 | Entry.SynthesisKind = Inst.Kind; 240 | Entry.Entity = Inst.Entity; 241 | Entry.PointOfInstantiation = Inst.PointOfInstantiation; 242 | 243 | // NOTE: Use this function because it produces time since start of process. 244 | llvm::sys::TimePoint<> now; 245 | std::chrono::nanoseconds user, sys; 246 | llvm::sys::Process::GetTimeUsage(now, user, sys); 247 | if (user != std::chrono::nanoseconds::zero()) 248 | now = llvm::sys::TimePoint<>(user); 249 | 250 | using Seconds = std::chrono::duration>; 251 | Entry.TimeStamp = Seconds(now.time_since_epoch()).count(); 252 | Entry.MemoryUsage = (MemoryFlag ? llvm::sys::Process::GetMallocUsage() : 0); 253 | 254 | Printer->printRawEntry(Entry, SafeModeFlag); 255 | } 256 | 257 | void TemplightTracer::atTemplateEnd(const Sema &TheSema, 258 | const Sema::CodeSynthesisContext &Inst) { 259 | if (!Printer) 260 | return; 261 | 262 | RawTemplightTraceEntry Entry; 263 | 264 | Entry.IsTemplateBegin = false; 265 | Entry.SynthesisKind = Inst.Kind; 266 | Entry.Entity = Inst.Entity; 267 | 268 | // NOTE: Use this function because it produces time since start of process. 269 | llvm::sys::TimePoint<> now; 270 | std::chrono::nanoseconds user, sys; 271 | llvm::sys::Process::GetTimeUsage(now, user, sys); 272 | if (user != std::chrono::nanoseconds::zero()) 273 | now = llvm::sys::TimePoint<>(user); 274 | 275 | using Seconds = std::chrono::duration>; 276 | Entry.TimeStamp = Seconds(now.time_since_epoch()).count(); 277 | Entry.MemoryUsage = (MemoryFlag ? llvm::sys::Process::GetMallocUsage() : 0); 278 | 279 | Printer->printRawEntry(Entry, SafeModeFlag); 280 | } 281 | 282 | TemplightTracer::TemplightTracer(const Sema &TheSema, std::string Output, 283 | bool Memory, bool Safemode, bool IgnoreSystem) 284 | : MemoryFlag(Memory), SafeModeFlag(Safemode) { 285 | 286 | Printer.reset( 287 | new TemplightTracer::TracePrinter(TheSema, Output, IgnoreSystem)); 288 | 289 | if (!Printer->getTraceStream()) { 290 | llvm::errs() 291 | << "Error: [Templight-Tracer] Failed to create template trace file!"; 292 | Printer.reset(); 293 | llvm::errs() << "Note: [Templight] Template trace has been disabled."; 294 | return; 295 | } 296 | 297 | Printer->takeWriter( 298 | new clang::TemplightProtobufWriter(*Printer->getTraceStream())); 299 | } 300 | 301 | TemplightTracer::~TemplightTracer() { 302 | // must be defined here due to TracePrinter being incomplete in header. 303 | } 304 | 305 | void TemplightTracer::initialize(const Sema &) { 306 | if (Printer) 307 | Printer->startTrace(); 308 | } 309 | 310 | void TemplightTracer::finalize(const Sema &) { 311 | if (Printer) 312 | Printer->endTrace(); 313 | } 314 | 315 | void TemplightTracer::readBlacklists(const std::string &BLFilename) { 316 | if (Printer) 317 | Printer->readBlacklists(BLFilename); 318 | } 319 | 320 | } // namespace clang 321 | -------------------------------------------------------------------------------- /lib/TemplightProtobufWriter.cpp: -------------------------------------------------------------------------------- 1 | //===- TemplightProtobufWriter.cpp ------------*- C++ -*-------------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "TemplightProtobufWriter.h" 11 | #include "PrintableTemplightEntries.h" 12 | 13 | #include "ThinProtobuf.h" 14 | #include "llvm/ADT/StringExtras.h" 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | namespace clang { 24 | 25 | TemplightProtobufWriter::TemplightProtobufWriter(llvm::raw_ostream &aOS, 26 | int aCompressLevel) 27 | : TemplightWriter(aOS), compressionMode(aCompressLevel) {} 28 | 29 | void TemplightProtobufWriter::initialize(const std::string &aSourceName) { 30 | 31 | std::string hdr_contents; 32 | { 33 | llvm::raw_string_ostream OS_inner(hdr_contents); 34 | 35 | /* 36 | message TemplightHeader { 37 | required uint32 version = 1; 38 | optional string source_file = 2; 39 | } 40 | */ 41 | 42 | llvm::protobuf::saveVarInt(OS_inner, 1, 1); // version 43 | if (!aSourceName.empty()) 44 | llvm::protobuf::saveString(OS_inner, 2, aSourceName); // source_file 45 | } 46 | 47 | llvm::raw_string_ostream OS(buffer); 48 | 49 | // required TemplightHeader header = 1; 50 | llvm::protobuf::saveString(OS, 1, hdr_contents); 51 | } 52 | 53 | void TemplightProtobufWriter::finalize() { 54 | // repeated TemplightTrace traces = 1; 55 | llvm::protobuf::saveString(OutputOS, 1, buffer); 56 | } 57 | 58 | std::string 59 | TemplightProtobufWriter::printEntryLocation(const std::string &FileName, 60 | int Line, int Column) { 61 | 62 | /* 63 | message SourceLocation { 64 | optional string file_name = 1; 65 | required uint32 file_id = 2; 66 | required uint32 line = 3; 67 | optional uint32 column = 4; 68 | } 69 | */ 70 | 71 | std::string location_contents; 72 | llvm::raw_string_ostream OS_inner(location_contents); 73 | 74 | std::unordered_map::iterator it = 75 | fileNameMap.find(FileName); 76 | 77 | if (it == fileNameMap.end()) { 78 | llvm::protobuf::saveString(OS_inner, 1, FileName); // file_name 79 | std::size_t file_id = fileNameMap.size(); 80 | llvm::protobuf::saveVarInt(OS_inner, 2, file_id); // file_id 81 | fileNameMap[FileName] = file_id; 82 | } else { 83 | llvm::protobuf::saveVarInt(OS_inner, 2, it->second); // file_id 84 | } 85 | 86 | llvm::protobuf::saveVarInt(OS_inner, 3, Line); // line 87 | llvm::protobuf::saveVarInt(OS_inner, 4, Column); // column 88 | 89 | OS_inner.str(); 90 | 91 | return location_contents; // NRVO 92 | } 93 | 94 | static void trimSpaces(std::string::iterator &it, 95 | std::string::iterator &it_end) { 96 | while (it < it_end) { 97 | if (*it == ' ') 98 | ++it; 99 | else if (*it_end == ' ') 100 | --it_end; 101 | else 102 | break; 103 | } 104 | ++it_end; 105 | } 106 | 107 | std::size_t 108 | TemplightProtobufWriter::createDictionaryEntry(const std::string &NameOrig) { 109 | std::unordered_map::iterator it_found = 110 | templateNameMap.find(NameOrig); 111 | if (it_found != templateNameMap.end()) 112 | return it_found->second; 113 | 114 | // FIXME: Convert this code to being constructive of "Name", instead of 115 | // destructive (replacing sub-strings with '\0' characters). 116 | std::string Name = NameOrig; 117 | std::string::iterator it_open = Name.end(); 118 | std::string::iterator it_colon_lo = Name.begin(); 119 | int srch_state = 0; 120 | llvm::SmallVector markers; 121 | for (std::string::iterator it = Name.begin(); it != Name.end(); ++it) { 122 | switch (srch_state) { 123 | case 0: 124 | if (*it == '<') { 125 | // check for "operator<<", "operator<" and "operator<=" 126 | llvm::StringRef test_str(Name.data(), it - Name.begin() + 1); 127 | if (test_str.ends_with("operator<")) { 128 | it_open = Name.end(); 129 | srch_state = 0; 130 | } else { 131 | it_open = it; 132 | ++srch_state; 133 | } 134 | } else if ((*it == ':') && (it + 1 < Name.end()) && (*(it + 1) == ':')) { 135 | if (it_colon_lo < it) { 136 | markers.push_back( 137 | createDictionaryEntry(std::string(it_colon_lo, it))); 138 | std::size_t offset_lo = it_colon_lo - Name.begin(); 139 | Name.replace(it_colon_lo, it, 1, '\0'); 140 | it = Name.begin() + offset_lo + 2; 141 | } else { 142 | it += 1; 143 | } 144 | it_colon_lo = it + 1; 145 | it_open = Name.end(); 146 | } 147 | break; 148 | case 1: 149 | if (*it == '<') { 150 | // check for "operator<<" and "operator<" 151 | llvm::StringRef test_str(Name.data(), it - Name.begin() + 1); 152 | if (test_str.ends_with("operator<<<")) { 153 | it_open = it; 154 | srch_state = 1; 155 | } else { 156 | ++srch_state; 157 | } 158 | } else if ((*it == ',') || (*it == '>')) { 159 | if (it_colon_lo < it_open) { 160 | std::size_t offset_end = it - it_open; 161 | std::size_t offset_lo = it_colon_lo - Name.begin(); 162 | markers.push_back( 163 | createDictionaryEntry(std::string(it_colon_lo, it_open))); 164 | Name.replace(it_colon_lo, it_open, 1, '\0'); 165 | it_open = Name.begin() + offset_lo + 1; 166 | it = it_open + offset_end; 167 | it_colon_lo = Name.end(); 168 | } 169 | std::string::iterator it_lo = it_open + 1; 170 | std::string::iterator it_hi = it - 1; 171 | trimSpaces(it_lo, it_hi); 172 | // Create or find the marker entry: 173 | markers.push_back(createDictionaryEntry(std::string(it_lo, it_hi))); 174 | std::size_t offset_end = it - it_hi; 175 | std::size_t offset_lo = it_lo - Name.begin(); 176 | Name.replace(it_lo, it_hi, 1, '\0'); 177 | it = Name.begin() + offset_lo + 1 + offset_end; 178 | it_open = it; 179 | it_colon_lo = Name.end(); 180 | if (*it == '>') { 181 | it_open = Name.end(); 182 | srch_state = 0; 183 | it_colon_lo = it + 1; 184 | } 185 | } 186 | break; 187 | default: 188 | if (*it == '<') { 189 | ++srch_state; 190 | } else if (*it == '>') { 191 | --srch_state; 192 | } 193 | break; 194 | } 195 | } 196 | if (!markers.empty() && it_colon_lo != Name.end()) { 197 | markers.push_back( 198 | createDictionaryEntry(std::string(it_colon_lo, Name.end()))); 199 | Name.replace(it_colon_lo, Name.end(), 1, '\0'); 200 | } 201 | 202 | /* 203 | message DictionaryEntry { 204 | required string marked_name = 1; 205 | repeated uint32 marker_ids = 2; 206 | } 207 | */ 208 | std::string dict_entry; 209 | llvm::raw_string_ostream OS_dict(dict_entry); 210 | llvm::protobuf::saveString(OS_dict, 1, Name); // marked_name 211 | for (llvm::SmallVector::iterator mk = markers.begin(), 212 | mk_end = markers.end(); 213 | mk != mk_end; ++mk) { 214 | llvm::protobuf::saveVarInt(OS_dict, 2, *mk); // marker_ids 215 | } 216 | OS_dict.str(); 217 | 218 | std::size_t id = templateNameMap.size(); 219 | templateNameMap[NameOrig] = id; 220 | 221 | llvm::raw_string_ostream OS_outer(buffer); 222 | // repeated DictionaryEntry names = 3; 223 | llvm::protobuf::saveString(OS_outer, 3, dict_entry); 224 | 225 | return id; 226 | } 227 | 228 | std::string 229 | TemplightProtobufWriter::printTemplateName(const std::string &Name) { 230 | 231 | /* 232 | message TemplateName { 233 | optional string name = 1; 234 | optional bytes compressed_name = 2; 235 | } 236 | */ 237 | 238 | std::string tname_contents; 239 | llvm::raw_string_ostream OS_inner(tname_contents); 240 | 241 | switch (compressionMode) { 242 | case 1: { // zlib-compressed name: 243 | llvm::SmallVector CompressedBuffer; 244 | llvm::compression::zlib::compress(llvm::arrayRefFromStringRef(Name), 245 | CompressedBuffer); 246 | // optional bytes compressed_name = 2; 247 | llvm::protobuf::saveString(OS_inner, 2, 248 | llvm::toStringRef(CompressedBuffer)); 249 | break; 250 | } 251 | LLVM_FALLTHROUGH; 252 | case 0: 253 | // optional string name = 1; 254 | llvm::protobuf::saveString(OS_inner, 1, Name); // name 255 | break; 256 | case 2: 257 | default: 258 | // optional uint32 dict_id = 3; 259 | llvm::protobuf::saveVarInt(OS_inner, 3, createDictionaryEntry(Name)); 260 | break; 261 | } 262 | 263 | OS_inner.str(); 264 | 265 | return tname_contents; // NRVO 266 | } 267 | 268 | void TemplightProtobufWriter::printEntry( 269 | const PrintableTemplightEntryBegin &aEntry) { 270 | 271 | std::string entry_contents; 272 | { 273 | llvm::raw_string_ostream OS_inner(entry_contents); 274 | 275 | /* 276 | message Begin { 277 | required SynthesisKind kind = 1; 278 | required string name = 2; 279 | required SourceLocation location = 3; 280 | optional double time_stamp = 4; 281 | optional uint64 memory_usage = 5; 282 | optional SourceLocation template_origin = 6; 283 | } 284 | */ 285 | 286 | llvm::protobuf::saveVarInt(OS_inner, 1, aEntry.SynthesisKind); // kind 287 | llvm::protobuf::saveString(OS_inner, 2, 288 | printTemplateName(aEntry.Name)); // name 289 | llvm::protobuf::saveString(OS_inner, 3, 290 | printEntryLocation(aEntry.FileName, aEntry.Line, 291 | aEntry.Column)); // location 292 | llvm::protobuf::saveDouble(OS_inner, 4, aEntry.TimeStamp); // time_stamp 293 | if (aEntry.MemoryUsage > 0) 294 | llvm::protobuf::saveVarInt(OS_inner, 5, 295 | aEntry.MemoryUsage); // memory_usage 296 | if (!aEntry.TempOri_FileName.empty()) 297 | llvm::protobuf::saveString( 298 | OS_inner, 6, 299 | printEntryLocation(aEntry.TempOri_FileName, aEntry.TempOri_Line, 300 | aEntry.TempOri_Column)); // template_origin 301 | } 302 | 303 | std::string oneof_contents; 304 | { 305 | llvm::raw_string_ostream OS_inner(oneof_contents); 306 | 307 | /* 308 | oneof begin_or_end { 309 | Begin begin = 1; 310 | End end = 2; 311 | } 312 | */ 313 | 314 | llvm::protobuf::saveString(OS_inner, 1, entry_contents); // begin 315 | } 316 | 317 | llvm::raw_string_ostream OS(buffer); 318 | 319 | // repeated TemplightEntry entries = 2; 320 | llvm::protobuf::saveString(OS, 2, oneof_contents); 321 | } 322 | 323 | void TemplightProtobufWriter::printEntry( 324 | const PrintableTemplightEntryEnd &aEntry) { 325 | 326 | std::string entry_contents; 327 | { 328 | llvm::raw_string_ostream OS_inner(entry_contents); 329 | 330 | /* 331 | message End { 332 | optional double time_stamp = 1; 333 | optional uint64 memory_usage = 2; 334 | } 335 | */ 336 | 337 | llvm::protobuf::saveDouble(OS_inner, 1, aEntry.TimeStamp); // time_stamp 338 | if (aEntry.MemoryUsage > 0) 339 | llvm::protobuf::saveVarInt(OS_inner, 2, 340 | aEntry.MemoryUsage); // memory_usage 341 | } 342 | 343 | std::string oneof_contents; 344 | { 345 | llvm::raw_string_ostream OS_inner(oneof_contents); 346 | 347 | /* 348 | oneof begin_or_end { 349 | Begin begin = 1; 350 | End end = 2; 351 | } 352 | */ 353 | 354 | llvm::protobuf::saveString(OS_inner, 2, entry_contents); // end 355 | } 356 | 357 | llvm::raw_string_ostream OS(buffer); 358 | 359 | // repeated TemplightEntry entries = 2; 360 | llvm::protobuf::saveString(OS, 2, oneof_contents); 361 | } 362 | 363 | } // namespace clang 364 | -------------------------------------------------------------------------------- /utils/ExtraWriters/TemplightExtraWriters.cpp: -------------------------------------------------------------------------------- 1 | //===- TemplightExtraWriters.cpp ----------------*- C++ -*-----------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "TemplightExtraWriters.h" 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace clang { 19 | 20 | static const char *const SynthesisKindStrings[] = { 21 | "TemplateInstantiation", 22 | "DefaultTemplateArgumentInstantiation", 23 | "DefaultFunctionArgumentInstantiation", 24 | "ExplicitTemplateArgumentSubstitution", 25 | "DeducedTemplateArgumentSubstitution", 26 | "PriorTemplateArgumentSubstitution", 27 | "DefaultTemplateArgumentChecking", 28 | "ExceptionSpecInstantiation", 29 | "DeclaringSpecialMember", 30 | "DefiningSynthesizedFunction", 31 | "Memoization"}; 32 | 33 | static std::string escapeXml(const std::string &Input) { 34 | std::string Result; 35 | Result.reserve(64); 36 | 37 | unsigned i, pos = 0; 38 | for (i = 0; i < Input.length(); ++i) { 39 | if (Input[i] == '<' || Input[i] == '>' || Input[i] == '"' || 40 | Input[i] == '\'' || Input[i] == '&') { 41 | Result.insert(Result.length(), Input, pos, i - pos); 42 | pos = i + 1; 43 | switch (Input[i]) { 44 | case '<': 45 | Result += "<"; 46 | break; 47 | case '>': 48 | Result += ">"; 49 | break; 50 | case '\'': 51 | Result += "'"; 52 | break; 53 | case '"': 54 | Result += """; 55 | break; 56 | case '&': 57 | Result += "&"; 58 | break; 59 | default: 60 | break; 61 | } 62 | } 63 | } 64 | Result.insert(Result.length(), Input, pos, i - pos); 65 | return Result; 66 | } 67 | 68 | } // namespace clang 69 | namespace llvm { 70 | namespace yaml { 71 | 72 | template <> 73 | struct ScalarEnumerationTraits< 74 | clang::Sema::CodeSynthesisContext::SynthesisKind> { 75 | static void 76 | enumeration(IO &io, clang::Sema::CodeSynthesisContext::SynthesisKind &value) { 77 | 78 | #define def_enum_case(e) io.enumCase(value, SynthesisKindStrings[e], e) 79 | 80 | using namespace clang; 81 | def_enum_case(CodeSynthesisContext::TemplateInstantiation); 82 | def_enum_case(CodeSynthesisContext::DefaultTemplateArgumentInstantiation); 83 | def_enum_case(CodeSynthesisContext::DefaultFunctionArgumentInstantiation); 84 | def_enum_case(CodeSynthesisContext::ExplicitTemplateArgumentSubstitution); 85 | def_enum_case(CodeSynthesisContext::DeducedTemplateArgumentSubstitution); 86 | def_enum_case(CodeSynthesisContext::PriorTemplateArgumentSubstitution); 87 | def_enum_case(CodeSynthesisContext::DefaultTemplateArgumentChecking); 88 | def_enum_case(CodeSynthesisContext::ExceptionSpecInstantiation); 89 | def_enum_case(CodeSynthesisContext::DeclaringSpecialMember); 90 | def_enum_case(CodeSynthesisContext::DefiningSynthesizedFunction); 91 | def_enum_case(CodeSynthesisContext::Memoization); 92 | 93 | #undef def_enum_case 94 | } 95 | }; 96 | 97 | template <> struct MappingTraits { 98 | static void mapping(IO &io, clang::PrintableTemplightEntryBegin &Entry) { 99 | bool b = true; 100 | io.mapRequired("IsBegin", b); 101 | // must be converted to string before, due to some BS with yaml traits. 102 | std::string kind = clang::SynthesisKindStrings[Entry.SynthesisKind]; 103 | io.mapRequired("Kind", kind); 104 | io.mapOptional("Name", Entry.Name); 105 | std::string loc = Entry.FileName + "|" + std::to_string(Entry.Line) + "|" + 106 | std::to_string(Entry.Column); 107 | io.mapOptional("Location", loc); 108 | io.mapRequired("TimeStamp", Entry.TimeStamp); 109 | io.mapOptional("MemoryUsage", Entry.MemoryUsage); 110 | std::string ori = Entry.TempOri_FileName + "|" + 111 | std::to_string(Entry.TempOri_Line) + "|" + 112 | std::to_string(Entry.TempOri_Column); 113 | io.mapOptional("TemplateOrigin", ori); 114 | } 115 | }; 116 | 117 | template <> struct MappingTraits { 118 | static void mapping(IO &io, clang::PrintableTemplightEntryEnd &Entry) { 119 | bool b = false; 120 | io.mapRequired("IsBegin", b); 121 | io.mapRequired("TimeStamp", Entry.TimeStamp); 122 | io.mapOptional("MemoryUsage", Entry.MemoryUsage); 123 | } 124 | }; 125 | 126 | } // namespace yaml 127 | } // namespace llvm 128 | namespace clang { 129 | 130 | void TemplightYamlWriter::initialize(const std::string &aSourceName) { 131 | Output->beginSequence(); 132 | } 133 | 134 | void TemplightYamlWriter::finalize() { Output->endSequence(); } 135 | 136 | void TemplightYamlWriter::printEntry( 137 | const PrintableTemplightEntryBegin &Entry) { 138 | void *SaveInfo; 139 | if (Output->preflightElement(1, SaveInfo)) { 140 | llvm::yaml::EmptyContext Context; 141 | llvm::yaml::yamlize(*Output, 142 | const_cast(Entry), true, 143 | Context); 144 | Output->postflightElement(SaveInfo); 145 | } 146 | } 147 | 148 | void TemplightYamlWriter::printEntry(const PrintableTemplightEntryEnd &Entry) { 149 | void *SaveInfo; 150 | if (Output->preflightElement(1, SaveInfo)) { 151 | llvm::yaml::EmptyContext Context; 152 | llvm::yaml::yamlize(*Output, 153 | const_cast(Entry), true, 154 | Context); 155 | Output->postflightElement(SaveInfo); 156 | } 157 | } 158 | 159 | TemplightYamlWriter::TemplightYamlWriter(llvm::raw_ostream &aOS) 160 | : TemplightWriter(aOS) { 161 | Output.reset(new llvm::yaml::Output(OutputOS)); 162 | Output->beginDocuments(); 163 | } 164 | 165 | TemplightYamlWriter::~TemplightYamlWriter() { Output->endDocuments(); } 166 | 167 | TemplightXmlWriter::TemplightXmlWriter(llvm::raw_ostream &aOS) 168 | : TemplightWriter(aOS) { 169 | OutputOS << "\n"; 170 | } 171 | 172 | TemplightXmlWriter::~TemplightXmlWriter() {} 173 | 174 | void TemplightXmlWriter::initialize(const std::string &aSourceName) { 175 | OutputOS << "\n"; 176 | } 177 | 178 | void TemplightXmlWriter::finalize() { OutputOS << "\n"; } 179 | 180 | void TemplightXmlWriter::printEntry( 181 | const PrintableTemplightEntryBegin &aEntry) { 182 | std::string EscapedName = escapeXml(aEntry.Name); 183 | OutputOS << llvm::format("\n" 184 | " %s\n" 185 | " \n" 186 | " %s|%d|%d\n", 187 | SynthesisKindStrings[aEntry.SynthesisKind], 188 | EscapedName.c_str(), aEntry.FileName.c_str(), 189 | aEntry.Line, aEntry.Column); 190 | OutputOS << llvm::format(" \n" 191 | " \n", 192 | aEntry.TimeStamp, aEntry.MemoryUsage); 193 | if (!aEntry.TempOri_FileName.empty()) { 194 | OutputOS << llvm::format(" %s|%d|%d\n", 195 | aEntry.TempOri_FileName.c_str(), 196 | aEntry.TempOri_Line, aEntry.TempOri_Column); 197 | } 198 | OutputOS << "\n"; 199 | } 200 | 201 | void TemplightXmlWriter::printEntry(const PrintableTemplightEntryEnd &aEntry) { 202 | OutputOS << llvm::format("\n" 203 | " \n" 204 | " \n" 205 | "\n", 206 | aEntry.TimeStamp, aEntry.MemoryUsage); 207 | } 208 | 209 | TemplightTextWriter::TemplightTextWriter(llvm::raw_ostream &aOS) 210 | : TemplightWriter(aOS) {} 211 | 212 | TemplightTextWriter::~TemplightTextWriter() {} 213 | 214 | void TemplightTextWriter::initialize(const std::string &aSourceName) { 215 | OutputOS << " SourceFile = " << aSourceName << "\n"; 216 | } 217 | 218 | void TemplightTextWriter::finalize() {} 219 | 220 | void TemplightTextWriter::printEntry( 221 | const PrintableTemplightEntryBegin &aEntry) { 222 | OutputOS << llvm::format("TemplateBegin\n" 223 | " Kind = %s\n" 224 | " Name = %s\n" 225 | " Location = %s|%d|%d\n", 226 | SynthesisKindStrings[aEntry.SynthesisKind], 227 | aEntry.Name.c_str(), aEntry.FileName.c_str(), 228 | aEntry.Line, aEntry.Column); 229 | OutputOS << llvm::format(" TimeStamp = %.9f\n" 230 | " MemoryUsage = %d\n", 231 | aEntry.TimeStamp, aEntry.MemoryUsage); 232 | if (!aEntry.TempOri_FileName.empty()) { 233 | OutputOS << llvm::format(" TemplateOrigin = %s|%d|%d\n", 234 | aEntry.TempOri_FileName.c_str(), 235 | aEntry.TempOri_Line, aEntry.TempOri_Column); 236 | } 237 | } 238 | 239 | void TemplightTextWriter::printEntry(const PrintableTemplightEntryEnd &aEntry) { 240 | OutputOS << llvm::format("TemplateEnd\n" 241 | " TimeStamp = %.9f\n" 242 | " MemoryUsage = %d\n", 243 | aEntry.TimeStamp, aEntry.MemoryUsage); 244 | } 245 | 246 | struct EntryTraversalTask { 247 | static const std::size_t invalid_id = ~std::size_t(0); 248 | 249 | PrintableTemplightEntryBegin start; 250 | PrintableTemplightEntryEnd finish; 251 | std::size_t nd_id, id_end, parent_id; 252 | EntryTraversalTask(const PrintableTemplightEntryBegin &aStart, 253 | std::size_t aNdId, std::size_t aParentId) 254 | : start(aStart), finish(), nd_id(aNdId), id_end(invalid_id), 255 | parent_id(aParentId){}; 256 | }; 257 | 258 | struct RecordedDFSEntryTree { 259 | static const std::size_t invalid_id = ~std::size_t(0); 260 | 261 | std::vector parent_stack; 262 | std::size_t cur_top; 263 | 264 | RecordedDFSEntryTree() : cur_top(invalid_id){}; 265 | 266 | void beginEntry(const PrintableTemplightEntryBegin &aEntry) { 267 | parent_stack.push_back( 268 | EntryTraversalTask(aEntry, parent_stack.size(), 269 | (cur_top == invalid_id ? invalid_id : cur_top))); 270 | cur_top = parent_stack.size() - 1; 271 | }; 272 | 273 | void endEntry(const PrintableTemplightEntryEnd &aEntry) { 274 | parent_stack[cur_top].finish = aEntry; 275 | parent_stack[cur_top].id_end = parent_stack.size(); 276 | if (parent_stack[cur_top].parent_id == invalid_id) 277 | cur_top = invalid_id; 278 | else 279 | cur_top = parent_stack[cur_top].parent_id; 280 | }; 281 | }; 282 | 283 | TemplightTreeWriter::TemplightTreeWriter(llvm::raw_ostream &aOS) 284 | : TemplightWriter(aOS), p_tree(new RecordedDFSEntryTree()) {} 285 | 286 | TemplightTreeWriter::~TemplightTreeWriter() {} 287 | 288 | void TemplightTreeWriter::printEntry( 289 | const PrintableTemplightEntryBegin &aEntry) { 290 | p_tree->beginEntry(aEntry); 291 | } 292 | 293 | void TemplightTreeWriter::printEntry(const PrintableTemplightEntryEnd &aEntry) { 294 | p_tree->endEntry(aEntry); 295 | } 296 | 297 | void TemplightTreeWriter::initialize(const std::string &aSourceName) { 298 | this->initializeTree(aSourceName); 299 | } 300 | 301 | void TemplightTreeWriter::finalize() { 302 | std::vector open_set; 303 | std::vector &tree = p_tree->parent_stack; 304 | 305 | for (std::size_t i = 0, i_end = tree.size(); i != i_end; ++i) { 306 | while (!open_set.empty() && (i >= tree[open_set.back()].id_end)) { 307 | closePrintedTreeNode(tree[open_set.back()]); 308 | open_set.pop_back(); 309 | } 310 | openPrintedTreeNode(tree[i]); 311 | open_set.push_back(i); 312 | } 313 | while (!open_set.empty()) { 314 | closePrintedTreeNode(tree[open_set.back()]); 315 | open_set.pop_back(); 316 | } 317 | 318 | this->finalizeTree(); 319 | } 320 | 321 | TemplightNestedXMLWriter::TemplightNestedXMLWriter(llvm::raw_ostream &aOS) 322 | : TemplightTreeWriter(aOS) { 323 | OutputOS << "\n"; 324 | } 325 | 326 | TemplightNestedXMLWriter::~TemplightNestedXMLWriter() {} 327 | 328 | void TemplightNestedXMLWriter::initializeTree(const std::string &aSourceName) { 329 | OutputOS << "\n"; 330 | } 331 | 332 | void TemplightNestedXMLWriter::finalizeTree() { OutputOS << "\n"; } 333 | 334 | void TemplightNestedXMLWriter::openPrintedTreeNode( 335 | const EntryTraversalTask &aNode) { 336 | const PrintableTemplightEntryBegin &BegEntry = aNode.start; 337 | const PrintableTemplightEntryEnd &EndEntry = aNode.finish; 338 | std::string EscapedName = escapeXml(BegEntry.Name); 339 | 340 | OutputOS << llvm::format("\n", 351 | EndEntry.TimeStamp - BegEntry.TimeStamp, 352 | EndEntry.MemoryUsage - BegEntry.MemoryUsage); 353 | 354 | // Print only first part (heading). 355 | } 356 | 357 | void TemplightNestedXMLWriter::closePrintedTreeNode( 358 | const EntryTraversalTask &aNode) { 359 | OutputOS << "\n"; 360 | } 361 | 362 | TemplightGraphMLWriter::TemplightGraphMLWriter(llvm::raw_ostream &aOS) 363 | : TemplightTreeWriter(aOS), last_edge_id(0) { 364 | OutputOS << "\n" 365 | "\n"; 369 | OutputOS << "\n" 371 | "\n" 373 | "\n" 375 | "\n" 377 | "0.0\n" 378 | "\n" 379 | "\n" 381 | "0\n" 382 | "\n" 383 | "\n"; 385 | } 386 | 387 | TemplightGraphMLWriter::~TemplightGraphMLWriter() { 388 | OutputOS << "\n"; 389 | } 390 | 391 | void TemplightGraphMLWriter::initializeTree(const std::string &aSourceName) { 392 | OutputOS << "\n"; 393 | } 394 | 395 | void TemplightGraphMLWriter::finalizeTree() { OutputOS << "\n"; } 396 | 397 | void TemplightGraphMLWriter::openPrintedTreeNode( 398 | const EntryTraversalTask &aNode) { 399 | const PrintableTemplightEntryBegin &BegEntry = aNode.start; 400 | const PrintableTemplightEntryEnd &EndEntry = aNode.finish; 401 | 402 | OutputOS << llvm::format("\n", aNode.nd_id); 403 | 404 | std::string EscapedName = escapeXml(BegEntry.Name); 405 | OutputOS << llvm::format(" %s\n" 406 | " \"%s\"\n" 407 | " \"%s|%d|%d\"\n", 408 | SynthesisKindStrings[BegEntry.SynthesisKind], 409 | EscapedName.c_str(), BegEntry.FileName.c_str(), 410 | BegEntry.Line, BegEntry.Column); 411 | OutputOS << llvm::format(" %.9f\n" 412 | " %d\n", 413 | EndEntry.TimeStamp - BegEntry.TimeStamp, 414 | EndEntry.MemoryUsage - BegEntry.MemoryUsage); 415 | if (!BegEntry.TempOri_FileName.empty()) { 416 | OutputOS << llvm::format(" \"%s|%d|%d\"\n", 417 | BegEntry.TempOri_FileName.c_str(), 418 | BegEntry.TempOri_Line, BegEntry.TempOri_Column); 419 | } 420 | 421 | OutputOS << "\n"; 422 | if (aNode.parent_id == RecordedDFSEntryTree::invalid_id) 423 | return; 424 | 425 | OutputOS << llvm::format("\n", 426 | last_edge_id++, aNode.parent_id, aNode.nd_id); 427 | } 428 | 429 | void TemplightGraphMLWriter::closePrintedTreeNode( 430 | const EntryTraversalTask &aNode) {} 431 | 432 | TemplightGraphVizWriter::TemplightGraphVizWriter(llvm::raw_ostream &aOS) 433 | : TemplightTreeWriter(aOS) {} 434 | 435 | TemplightGraphVizWriter::~TemplightGraphVizWriter() {} 436 | 437 | void TemplightGraphVizWriter::initializeTree(const std::string &aSourceName) { 438 | OutputOS << "digraph Trace {\n"; 439 | } 440 | 441 | void TemplightGraphVizWriter::finalizeTree() { OutputOS << "}\n"; } 442 | 443 | void TemplightGraphVizWriter::openPrintedTreeNode( 444 | const EntryTraversalTask &aNode) { 445 | const PrintableTemplightEntryBegin &BegEntry = aNode.start; 446 | const PrintableTemplightEntryEnd &EndEntry = aNode.finish; 447 | 448 | std::string EscapedName = escapeXml(BegEntry.Name); 449 | OutputOS << llvm::format("n%d [label = ", aNode.nd_id) 450 | << llvm::format("\"%s\\n" 451 | "%s\\n" 452 | "At %s Line %d Column %d\\n", 453 | SynthesisKindStrings[BegEntry.SynthesisKind], 454 | EscapedName.c_str(), BegEntry.FileName.c_str(), 455 | BegEntry.Line, BegEntry.Column); 456 | if (!BegEntry.TempOri_FileName.empty()) { 457 | OutputOS << llvm::format("From %s Line %d Column %d\\n", 458 | BegEntry.TempOri_FileName.c_str(), 459 | BegEntry.TempOri_Line, BegEntry.TempOri_Column); 460 | } 461 | OutputOS << llvm::format("Time: %.9f seconds Memory: %d bytes\" ];\n", 462 | EndEntry.TimeStamp - BegEntry.TimeStamp, 463 | EndEntry.MemoryUsage - BegEntry.MemoryUsage); 464 | 465 | if (aNode.parent_id == RecordedDFSEntryTree::invalid_id) 466 | return; 467 | 468 | OutputOS << llvm::format("n%d -> n%d;\n", aNode.parent_id, aNode.nd_id); 469 | } 470 | 471 | void TemplightGraphVizWriter::closePrintedTreeNode( 472 | const EntryTraversalTask &aNode) {} 473 | 474 | } // namespace clang 475 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Templight 2.0 - Template Instantiation Profiler and Debugger 2 | 3 | Templight is a Clang-based tool to profile the time and memory consumption of template instantiations and to perform interactive debugging sessions to gain introspection into the template instantiation process. 4 | 5 | **Disclaimer**: Templight is still at a very preliminary state. We hope to get as much early adoption and testing as we can get, but be aware that we are still experimenting with the output formats and some of the behaviors. 6 | 7 | ## Table of Contents 8 | 9 | - [Templight Profiler](#templight-profiler) 10 | - [Templight Debugger](#templight-debugger) 11 | - [Getting Started](#getting-started) 12 | - [Getting and Compiling Templight](#getting-and-compiling-templight) 13 | - [Invoking Templight](#invoking-templight) 14 | - [Using the Templight Profiler](#using-the-templight-profiler) 15 | - [Default Output Location](#default-output-location) 16 | - [Using the Templight Debugger](#using-the-templight-debugger) 17 | - [Using Blacklists](#using-blacklists) 18 | - [Inspecting the profiles](#inspecting-the-profiles) 19 | - [Credits](#credits) 20 | 21 | ## Templight Profiler 22 | 23 | The templight profiler is intended to be used as a drop-in substitute for the clang compiler (or one of its variants, like clang-cl) and it performs a full compilation, *honoring all the clang compiler options*, but records a trace (or history) of the template instantiations performed by the compiler for each translation unit. 24 | 25 | The profiler will record a time-stamp when a template instantiation is entered and when it is exited, which is what forms the compilation-time profile of the template instantiations of a translation unit. The total memory consumption can also be recorded at these points, which will skew the time profiles, but provide a profile of the memory consumed by the compiler during the template instantiations. 26 | 27 | The profiler is enabled by the templight option `-profiler`, and it supports the following additional templight options: 28 | 29 | - `-stdout` - Output template instantiation traces to standard output (mainly for piping / redirecting purposes). Warning: you need to make sure the source files compile cleanly, otherwise, the output will be corrupted by warning or error messages. 30 | - `-memory` - Profile the memory usage during template instantiations. 31 | - `-safe-mode` - Output Templight traces without buffering, not to lose them at failure (note: this will distort the timing profiles due to file I/O latency). 32 | - `-ignore-system` - Ignore any template instantiation located in system-includes (-isystem), such as from the STL. 33 | - `-output=` - Write Templight profiling traces to . By default, it outputs to "current_source.cpp.trace.pbf" or "current_source.cpp.memory.trace.pbf" (if `-memory` is used). 34 | - `-blacklist=` - Specify a blacklist file that lists declaration contexts (e.g., namespaces) and identifiers (e.g., `std::basic_string`) as regular expressions to be filtered out of the trace (not appear in the profiler trace files). Every line of the blacklist file should contain either "context" or "identifier", followed by a single space character and then, a valid regular expression. 35 | 36 | ## Templight Debugger 37 | 38 | The templight interactive debugger is also a drop-in substitute for the clang compiler, honoring all its options, but it will interrupt the compilation with a console prompt (stdin). The operations of the templight debugger are modeled after the GDB debugger and essentially works the same, *with most commands being the same*, but instead of stepping through the execution of the code (in GDB), it steps through the instantiation of the templates. 39 | 40 | In short, the templight debugger is to template meta-programming what GDB is to regular code. 41 | 42 | The debugger is enabled by the templight option `-debugger`, and it supports the following additional templight options: 43 | 44 | - `-ignore-system` - Ignore any template instantiation located in system-includes (-isystem), such as from the STL. 45 | - `-blacklist=` - Specify a blacklist file that lists declaration contexts (e.g., namespaces) and identifiers (e.g., `std::basic_string`) as regular expressions to be ignored by the debugger. Every line of the blacklist file should contain either "context" or "identifier", followed by a single space character and then, a valid regular expression. 46 | 47 | **NOTE**: Both the debugger and the profiler **can be used together**. Obviously, if compilation is constantly interrupted by the interactive debugger, the time traces recorded by the profiler will be meaningless. Nevertheless, running the profiler during debugging will have the benefit of recording the history of the template instantiations, for later review, and can also record, with reasonably accuracy, the memory consumption profiles. 48 | 49 | ## Getting Started 50 | 51 | ### Getting and Compiling Templight 52 | 53 | Templight must be compiled from source, alongside the Clang source code. 54 | 55 | 1. [Follow the instructions from LLVM/Clang](http://clang.llvm.org/get_started.html) to get a local copy of the Clang source code. 56 | 57 | 2. Clone the templight repository into the clang directories, as follows: 58 | ```bash 59 | (from top-level folder) 60 | $ cd clang/tools 61 | $ mkdir templight 62 | $ git clone templight 63 | ``` 64 | 65 | 3. Add the `templight` subdirectory to CMake: 66 | ```bash 67 | (from top-level folder) 68 | $ cd clang/tools 69 | $ echo "add_clang_subdirectory(templight)" >> CMakeLists.txt 70 | ``` 71 | 72 | 4. (Re-)Compile LLVM / Clang: (same as the corresponding step in LLVM/Clang instructions) 73 | ```bash 74 | (from top-level folder) 75 | $ mkdir build 76 | $ cd build 77 | $ cmake -DLLVM_ENABLE_PROJECTS=clang ../llvm/ 78 | $ make 79 | ``` 80 | 81 | 5. If successful, there should be a `templight` and a `templight++` executable in the build/bin folder. 82 | 83 | **NOTE**: You do not need to apply the patch `templight_clang_patch.diff` and you can ignore the corresponding 84 | version numbers in `templight_clang_version.txt`. Those are meant for other uses case (testing some hooks within clang). 85 | 86 | 87 | ### Invoking Templight 88 | 89 | Templight is designed to be a drop-in replacement for the clang compiler. This is because in order to correctly profile the compilation, it must run as the compiler would, honoring all compilation options specified by the user. 90 | 91 | Because of this particular situation, the options for templight must be specially marked with `-Xtemplight` to distinguish them from other Clang options. The general usage goes like this: 92 | 93 | ```bash 94 | $ templight++ [[-Xtemplight [templight-option]]|[clang-option]] 95 | ``` 96 | 97 | Or, for the MSVC-compatible version of templight (analoguous to clang-cl) use as follows: 98 | 99 | ```bash 100 | $ templight-cl [[-Xtemplight [templight-option]]|[clang-option]] 101 | (OR) 102 | > templight-cl.exe [[-Xtemplight [templight-option]]|[clang-option]] 103 | ``` 104 | 105 | For example, if we have this simple command-line invocation of clang: 106 | 107 | ```bash 108 | $ clang++ -Wall -c some_source.cpp -o some_source.o 109 | ``` 110 | 111 | The corresponding templight profiler invocation, with options `-memory` and `-ignore-system` would look like this: 112 | 113 | ```bash 114 | $ templight++ -Xtemplight -profiler -Xtemplight -memory -Xtemplight -ignore-system -Wall -c some_source.cpp -o some_source.o 115 | ``` 116 | 117 | Note that the order in which the templight-options appear is not important, and that clang options can be interleaved with templight-options. However, **every single templight-option must be immediately preceeded with `-Xtemplight`**. 118 | 119 | As can be seen from that simple example, one can easily substitude the clang command (e.g., `clang++`) with a compound templight invocation (e.g., `templight++ -Xtemplight -profiler -Xtemplight -memory`) and thus, leave the remainder of the command-line intact. That is how templight can be used as a drop-in replacement for clang in any given project, build script or an IDE's build configuration. 120 | 121 | If you use CMake and Clang with the following common trick: 122 | ```bash 123 | $ export CC=/path/to/llvm/build/bin/clang 124 | $ export CXX=/path/to/llvm/build/bin/clang++ 125 | $ cmake 126 | ``` 127 | Then, templight could be swapped in by the same trick: 128 | ```bash 129 | $ export CC="/path/to/llvm/build/bin/templight -Xtemplight -profiler -Xtemplight -memory" 130 | $ export CXX="/path/to/llvm/build/bin/templight++ -Xtemplight -profiler -Xtemplight -memory" 131 | $ cmake 132 | ``` 133 | But be warned that the **cmake scripts will not fully recognize this compiler**, and therefore, you might have to make small changes in the CMake files to be able to handle it. Up to now, experience with building complete cmake projects with templight has been very successful as cmake essentially recognizes templight as a "Clang" compiler and applies the appropriate configuration. There were, however, a few minor issues found that were easily circumvented on a case-by-case basis, and so, be warned that this might not work perfectly on the first try. If anyone is interested in creating some CMake modules to deal with templight specifically, please contact the maintainer, such modules would be more than welcomed. Also, any feedback on your experience working with templight on a large project would be much appreciated. 134 | 135 | ## Using the Templight Profiler 136 | 137 | The templight profiler is invoked by specifying the templight-option `-profiler`. By default, if no other templight-option is specified, the templight profiler will output only time profiling data and will output to one file per input source file and will append the extension `.trace.pbf` to the output filename (object file output). 138 | 139 | For example, running the following: 140 | ```bash 141 | $ templight++ -Xtemplight -profiler -c some_source.cpp 142 | ``` 143 | will produce a file called `some_source.o.trace.pbf` in the same directory as `some_source.cpp`. 144 | 145 | It is, in general, highly recommended to use the `-ignore-system` option for the profiler (and debugger too) because instantiations from standard or third-party libraries can cause significant noise and dramatically increase the size of the trace files produced. If libraries like the STL or most of Boost libraries are used, it is likely that most of the traces produced relate to those libraries and not to your own. Therefore, it is recommended to use `-isystem` when specifying the include directories for those libraries and then use the `-ignore-system` templight-option when producing the traces. 146 | 147 | *Warning*: Trace files produced by the profiler can be very large, especially in template-heavy code. So, use this tool with caution. It is not recommended to blindly generate templight trace files for all the source files of a particular project (e.g., using templight profiler in place of clang in a CMake build tree). You should first identify specific parts of your project that you suspect (or know) lead to template instantiation problems (e.g., compile-time performance issues, or actual bugs or failures), and then create a small source file that exercises those specific parts and produce a templight trace for that source file. 148 | 149 | ### Default Output Location 150 | 151 | The location and names of the output files for the templight traces needs some explanation, because it might not always be straight-forward. The main problem here is that templight is meant to be used as a drop-in replacement for the compiler (and it actually compiles the code, exactly as the vanilla Clang compiler does it). The problem is that you can invoke a compiler in many different ways. On typical small tests, you would simply invoke the compiler on a single source file and produce either an executable or an object file. In other case, you might invoke it with a list of source files to produce either an executable or a library. And for "real" projects, a build system will generally invoke the compiler for each source file, producing an object file each time, and later invoking the linker separately. 152 | 153 | Templight must find a reasonable place to output its traces in all those cases, and often, specifying an output location with `-output` does not really solve the problem (like this problem: multiple source files, one output location?). The only really straight-forward case is with the `-stdout` option, where the traces are simply printed out to the standard output (presumably to be piped elsewhere). 154 | 155 | **One Source File, One Object File** (without `-output` option): Whenever doing a simple compilation where each source file is turned into a single object file (i.e., when using the `-c` option), then the templight tracer will, by default, *output the traces into files located where the output object files are put* and with the same name, but with the addition of the templight extensions. In other words, for compilation instructions like `-c some/path/first_source.cpp some/other/path/second_source.cpp`, you would get traces files: `some/path/first_source.o.trace.pbf` and `some/other/path/second_source.o.trace.pbf`. Whichever options you specify to change the location or name of the generated object file, templight traces will follow. In other words, the default trace filenames are derived from the final output object-file names. 156 | 157 | **One Source File, One Object File** (with `-output` option): Because the `-output` option cannot deal with multiple files specified, templight will ignore this option in this case. 158 | 159 | **One Source File, (Some Output) File**: If you do any other operation that is like a simple compilation (no linking), such as using the `-S` option to general assembly listings or using the `-ast-dump` option, then the situation is analoguous to when you use the `-c` option (produce object-files). The templight trace file names are simply derived from whatever is the final output file name (e.g., `some_source.s.trace.pbf`). If you invoke templight in a way that does not involve any kind of an output file (such as syntax-only or ast-dump to stdout), then templight falls back to deriving the trace file names from the source file names (e.g., `some_source.cpp.trace.pbf`). 160 | 161 | The overall behavior in the above three cases is designed such that when templight is invoked as part of a compilation driven by a build-system (e.g., cmake, make, etc.), which is often done out-of-source, it will output all its traces wherever the build-system stashes the object-files, which is a safe and clean place to put them (e.g., avoid pollution in source tree, avoid possible file-permission problems (read-only source tree), etc..). It is also easy, through most build-systems to create scripts that will move or copy those traces out of those locations (that might be temporary under some setups) and put them wherever is more convenient. 162 | 163 | **Many Source Files, (Some Output) File**: If you invoke the compilation of several source files to be linked into a single executable or library, then templight will merge all the traces into a single output file, whose name is derived from the executable or library name. If no output name is specified, the compiler puts the executable into `a.out`, and templight will put its traces into `a.trace.pbf`. If you specify an output file via the `-output` option, then the trace will be put into that file. 164 | 165 | ## Using the Templight Debugger 166 | 167 | The templight debugger is invoked by specifying the templight-option `-debugger`. When the debugger is launched, it will interrupt the compilation with a console command prompt, just like GDB. The templight debugger is designed to work in a similar fashion as GDB, with many of the same familiar commands and behavior. 168 | 169 | Here is a quick reference for the available commands: 170 | 171 | `break