diff --git a/clang-tools-extra/clangd/JSONTransport.cpp b/clang-tools-extra/clangd/JSONTransport.cpp --- a/clang-tools-extra/clangd/JSONTransport.cpp +++ b/clang-tools-extra/clangd/JSONTransport.cpp @@ -10,9 +10,11 @@ #include "support/Cancellation.h" #include "support/Logger.h" #include "support/Shutdown.h" +#include "support/ThreadSignalHandler.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Errno.h" #include "llvm/Support/Error.h" +#include "llvm/Support/Threading.h" #include namespace clang { @@ -109,6 +111,11 @@ return llvm::errorCodeToError( std::error_code(errno, std::system_category())); if (readRawMessage(JSON)) { + ThreadSignalHandler ScopedHandler([JSON]() { + auto &OS = llvm::errs(); + OS << "Signalled while processing message:\n"; + OS << JSON << "\r\n"; + }); if (auto Doc = llvm::json::parse(JSON)) { vlog(Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc); if (!handleMessage(std::move(*Doc), Handler)) diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp --- a/clang-tools-extra/clangd/TUScheduler.cpp +++ b/clang-tools-extra/clangd/TUScheduler.cpp @@ -58,6 +58,7 @@ #include "support/Logger.h" #include "support/MemoryTree.h" #include "support/Path.h" +#include "support/ThreadSignalHandler.h" #include "support/Threading.h" #include "support/Trace.h" #include "clang/Frontend/CompilerInvocation.h" @@ -591,6 +592,8 @@ Deadline scheduleLocked(); /// Should the first task in the queue be skipped instead of run? bool shouldSkipHeadLocked() const; + /// Called by thread local signal handler. + void printRequestContextOnSignal() const; struct Request { llvm::unique_function Action; @@ -1281,6 +1284,8 @@ Status.ASTActivity.Name = CurrentRequest->Name; }); WithContext WithProvidedContext(ContextProvider(FileName)); + ThreadSignalHandler ScopedHandler( + [this]() { printRequestContextOnSignal(); }); CurrentRequest->Action(); } @@ -1371,6 +1376,22 @@ llvm_unreachable("Unknown WantDiagnostics"); } +void ASTWorker::printRequestContextOnSignal() const { + auto &OS = llvm::errs(); + OS << "Signalled during AST action:\n"; + auto &Command = FileInputs.CompileCommand; + OS << "Filename: " << Command.Filename << "\n"; + OS << "Directory: " << Command.Directory << "\n"; + OS << "Command Line:"; + for (auto &Arg : Command.CommandLine) { + OS << " " << Arg; + } + OS << "\n"; + OS << "Version: " << FileInputs.Version << "\n"; + OS << "Contents:\n"; + OS << FileInputs.Contents << "\n"; +} + bool ASTWorker::blockUntilIdle(Deadline Timeout) const { auto WaitUntilASTWorkerIsIdle = [&] { std::unique_lock Lock(Mutex); diff --git a/clang-tools-extra/clangd/support/CMakeLists.txt b/clang-tools-extra/clangd/support/CMakeLists.txt --- a/clang-tools-extra/clangd/support/CMakeLists.txt +++ b/clang-tools-extra/clangd/support/CMakeLists.txt @@ -24,6 +24,7 @@ MemoryTree.cpp Path.cpp Shutdown.cpp + ThreadSignalHandler.cpp Threading.cpp ThreadsafeFS.cpp Trace.cpp diff --git a/clang-tools-extra/clangd/support/ThreadSignalHandler.h b/clang-tools-extra/clangd/support/ThreadSignalHandler.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/support/ThreadSignalHandler.h @@ -0,0 +1,31 @@ +//===--- ThreadSignalHandler.h - Thread local signal handling ----*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include + +namespace clang { +namespace clangd { + +using ThreadSignalHandlerCallback = std::function; + +class ThreadSignalHandler { +public: + ThreadSignalHandler(const ThreadSignalHandlerCallback &ThreadLocalCallback); + ~ThreadSignalHandler(); + + ThreadSignalHandler(ThreadSignalHandler &&); + ThreadSignalHandler(const ThreadSignalHandler &) = delete; + ThreadSignalHandler &operator=(ThreadSignalHandler &&) = delete; + ThreadSignalHandler &operator=(const ThreadSignalHandler &) = delete; + +private: + ThreadSignalHandlerCallback Callback; +}; + +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/support/ThreadSignalHandler.cpp b/clang-tools-extra/clangd/support/ThreadSignalHandler.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/support/ThreadSignalHandler.cpp @@ -0,0 +1,50 @@ +//===--- ThreadSignalHandler.cpp - Thread local signal handling --*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "support/ThreadSignalHandler.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/ThreadLocal.h" + +using namespace clang::clangd; + +static llvm::sys::ThreadLocal CurrentCallback; + +static void registerSignalHandlerIfNeeded() { + static llvm::once_flag RegisterOnceFlag; + llvm::call_once(RegisterOnceFlag, []() { + auto PrintInputs = [](void *Cookie) { + ThreadSignalHandlerCallback *Callback = CurrentCallback.get(); + // TODO: ignore non thread local signals like SIGTERM + if (Callback) { + (*Callback)(); + } + }; + llvm::sys::AddSignalHandler(PrintInputs, nullptr); + }); +} + +ThreadSignalHandler::ThreadSignalHandler( + const ThreadSignalHandlerCallback &ThreadLocalCallback) + : Callback(ThreadLocalCallback) { + assert(CurrentCallback.get() == nullptr); + registerSignalHandlerIfNeeded(); + CurrentCallback.set(&Callback); +} + +ThreadSignalHandler::ThreadSignalHandler(ThreadSignalHandler &&RHS) + : Callback(RHS.Callback) { + assert(CurrentCallback.get() == &RHS.Callback); + RHS.Callback = nullptr; + CurrentCallback.set(&Callback); +} + +ThreadSignalHandler::~ThreadSignalHandler() { + if (Callback != nullptr) { // haven't been moved + CurrentCallback.set(nullptr); + } +}