Index: cfe/trunk/include/clang-c/Index.h =================================================================== --- cfe/trunk/include/clang-c/Index.h +++ cfe/trunk/include/clang-c/Index.h @@ -334,6 +334,16 @@ CINDEX_LINKAGE unsigned clang_CXIndex_getGlobalOptions(CXIndex); /** + * \brief Sets the invocation emission path option in a CXIndex. + * + * The invocation emission path specifies a path which will contain log + * files for certain libclang invocations. A null value (default) implies that + * libclang invocations are not logged.. + */ +CINDEX_LINKAGE void +clang_CXIndex_setInvocationEmissionPathOption(CXIndex, const char *Path); + +/** * \defgroup CINDEX_FILES File manipulation routines * * @{ Index: cfe/trunk/test/Index/record-parsing-invocation.c =================================================================== --- cfe/trunk/test/Index/record-parsing-invocation.c +++ cfe/trunk/test/Index/record-parsing-invocation.c @@ -0,0 +1,21 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: not env CINDEXTEST_INVOCATION_EMISSION_PATH=%t c-index-test -test-load-source all %s +// RUN: cat %t/libclang-* | FileCheck %s + +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: env LIBCLANG_DISABLE_CRASH_RECOVERY=1 CINDEXTEST_INVOCATION_EMISSION_PATH=%t not --crash c-index-test -test-load-source all %s +// RUN: cat %t/libclang-* | FileCheck %s + +// Verify that the file is removed for successful operation: +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: env CINDEXTEST_INVOCATION_EMISSION_PATH=%t c-index-test -test-load-source all %s -DAVOID_CRASH +// RUN: ls %t | count 0 + +#ifndef AVOID_CRASH +# pragma clang __debug parser_crash +#endif + +// CHECK: {"toolchain":"{{.*}}","libclang.operation":"parse","libclang.opts":1,"args":["clang","-fno-spell-checking","{{.*}}/record-parsing-invocation.c","-Xclang","-detailed-preprocessing-record","-fallow-editor-placeholders"]} Index: cfe/trunk/tools/c-index-test/c-index-test.c =================================================================== --- cfe/trunk/tools/c-index-test/c-index-test.c +++ cfe/trunk/tools/c-index-test/c-index-test.c @@ -1747,11 +1747,15 @@ int result; unsigned Repeats = 0; unsigned I; + const char *InvocationPath; Idx = clang_createIndex(/* excludeDeclsFromPCH */ (!strcmp(filter, "local") || !strcmp(filter, "local-display"))? 1 : 0, /* displayDiagnostics=*/1); + InvocationPath = getenv("CINDEXTEST_INVOCATION_EMISSION_PATH"); + if (InvocationPath) + clang_CXIndex_setInvocationEmissionPathOption(Idx, InvocationPath); if ((CommentSchemaFile = parse_comments_schema(argc, argv))) { argc--; Index: cfe/trunk/tools/libclang/CIndex.cpp =================================================================== --- cfe/trunk/tools/libclang/CIndex.cpp +++ cfe/trunk/tools/libclang/CIndex.cpp @@ -3255,6 +3255,12 @@ return 0; } +void clang_CXIndex_setInvocationEmissionPathOption(CXIndex CIdx, + const char *Path) { + if (CIdx) + static_cast(CIdx)->setInvocationEmissionPath(Path ? Path : ""); +} + void clang_toggleCrashRecovery(unsigned isEnabled) { if (isEnabled) llvm::CrashRecoveryContext::Enable(); @@ -3431,6 +3437,11 @@ // faster, trading for a slower (first) reparse. unsigned PrecompilePreambleAfterNParses = !PrecompilePreamble ? 0 : 2 - CreatePreambleOnFirstParse; + + // FIXME: Record the hash of the unsaved files. + LibclangInvocationReporter InvocationReporter( + *CXXIdx, LibclangInvocationReporter::OperationKind::ParseOperation, + options, llvm::makeArrayRef(*Args)); std::unique_ptr Unit(ASTUnit::LoadFromCommandLine( Args->data(), Args->data() + Args->size(), CXXIdx->getPCHContainerOperations(), Diags, Index: cfe/trunk/tools/libclang/CIndexer.h =================================================================== --- cfe/trunk/tools/libclang/CIndexer.h +++ cfe/trunk/tools/libclang/CIndexer.h @@ -18,6 +18,7 @@ #include "clang-c/Index.h" #include "clang/Frontend/PCHContainerOperations.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Mutex.h" #include namespace llvm { @@ -40,6 +41,10 @@ std::string ResourcesPath; std::shared_ptr PCHContainerOps; + std::string ToolchainPath; + + std::string InvocationEmissionPath; + public: CIndexer(std::shared_ptr PCHContainerOps = std::make_shared()) @@ -71,6 +76,29 @@ /// \brief Get the path of the clang resource files. const std::string &getClangResourcesPath(); + + StringRef getClangToolchainPath(); + + void setInvocationEmissionPath(StringRef Str) { + InvocationEmissionPath = Str; + } + + StringRef getInvocationEmissionPath() const { return InvocationEmissionPath; } +}; + +/// Logs information about a particular libclang operation like parsing to +/// a new file in the invocation emission path. +class LibclangInvocationReporter { +public: + enum class OperationKind { ParseOperation, CompletionOperation }; + + LibclangInvocationReporter(CIndexer &Idx, OperationKind Op, + unsigned ParseOptions, + llvm::ArrayRef Args); + ~LibclangInvocationReporter(); + +private: + std::string File; }; /// \brief Return the current size to request for "safety". Index: cfe/trunk/tools/libclang/CIndexer.cpp =================================================================== --- cfe/trunk/tools/libclang/CIndexer.cpp +++ cfe/trunk/tools/libclang/CIndexer.cpp @@ -14,8 +14,10 @@ #include "CIndexer.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/Version.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/Config/llvm-config.h" +#include "llvm/Support/MutexGuard.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" #include @@ -76,3 +78,56 @@ ResourcesPath = LibClangPath.str(); return ResourcesPath; } + +StringRef CIndexer::getClangToolchainPath() { + if (!ToolchainPath.empty()) + return ToolchainPath; + StringRef ResourcePath = getClangResourcesPath(); + ToolchainPath = llvm::sys::path::parent_path( + llvm::sys::path::parent_path(llvm::sys::path::parent_path(ResourcePath))); + return ToolchainPath; +} + +LibclangInvocationReporter::LibclangInvocationReporter( + CIndexer &Idx, OperationKind Op, unsigned ParseOptions, + llvm::ArrayRef Args) { + StringRef Path = Idx.getInvocationEmissionPath(); + if (Path.empty()) + return; + + // Create a temporary file for the invocation log. + SmallString<256> TempPath; + TempPath = Path; + llvm::sys::path::append(TempPath, "libclang-%%%%%%%%%%%%"); + int FD; + if (llvm::sys::fs::createUniqueFile(TempPath, FD, TempPath)) + return; + File = std::string(TempPath.begin(), TempPath.end()); + llvm::raw_fd_ostream OS(FD, /*ShouldClose=*/true); + + // Write out the information about the invocation to it. + auto WriteStringKey = [&OS](StringRef Key, StringRef Value) { + OS << R"(")" << Key << R"(":")"; + OS << Value << '"'; + }; + OS << '{'; + WriteStringKey("toolchain", Idx.getClangToolchainPath()); + OS << ','; + WriteStringKey("libclang.operation", + Op == OperationKind::ParseOperation ? "parse" : "complete"); + OS << ','; + OS << R"("libclang.opts":)" << ParseOptions; + OS << ','; + OS << R"("args":[)"; + for (const auto &I : llvm::enumerate(Args)) { + if (I.index()) + OS << ','; + OS << '"' << I.value() << '"'; + } + OS << "]}"; +} + +LibclangInvocationReporter::~LibclangInvocationReporter() { + if (!File.empty()) + llvm::sys::fs::remove(File); +} Index: cfe/trunk/tools/libclang/libclang.exports =================================================================== --- cfe/trunk/tools/libclang/libclang.exports +++ cfe/trunk/tools/libclang/libclang.exports @@ -2,6 +2,7 @@ clang_CXCursorSet_insert clang_CXIndex_getGlobalOptions clang_CXIndex_setGlobalOptions +clang_CXIndex_setInvocationEmissionPathOption clang_CXXConstructor_isConvertingConstructor clang_CXXConstructor_isCopyConstructor clang_CXXConstructor_isDefaultConstructor