Skip to content

Commit 62627a6

Browse files
author
git apple-llvm automerger
committed
Merge commit '045695f85cb8' from llvm.org/main into next
2 parents 275c5ff + 045695f commit 62627a6

File tree

9 files changed

+258
-0
lines changed

9 files changed

+258
-0
lines changed

clang-tools-extra/clangd/JSONTransport.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
#include "support/Cancellation.h"
1111
#include "support/Logger.h"
1212
#include "support/Shutdown.h"
13+
#include "support/ThreadCrashReporter.h"
1314
#include "llvm/ADT/SmallString.h"
1415
#include "llvm/Support/Errno.h"
1516
#include "llvm/Support/Error.h"
17+
#include "llvm/Support/Threading.h"
1618
#include <system_error>
1719

1820
namespace clang {
@@ -109,6 +111,11 @@ class JSONTransport : public Transport {
109111
return llvm::errorCodeToError(
110112
std::error_code(errno, std::system_category()));
111113
if (readRawMessage(JSON)) {
114+
ThreadCrashReporter ScopedReporter([&JSON]() {
115+
auto &OS = llvm::errs();
116+
OS << "Signalled while processing message:\n";
117+
OS << JSON << "\n";
118+
});
112119
if (auto Doc = llvm::json::parse(JSON)) {
113120
vlog(Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc);
114121
if (!handleMessage(std::move(*Doc), Handler))

clang-tools-extra/clangd/TUScheduler.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "support/Logger.h"
5959
#include "support/MemoryTree.h"
6060
#include "support/Path.h"
61+
#include "support/ThreadCrashReporter.h"
6162
#include "support/Threading.h"
6263
#include "support/Trace.h"
6364
#include "clang/Frontend/CompilerInvocation.h"
@@ -76,6 +77,7 @@
7677
#include "llvm/Support/FormatVariadic.h"
7778
#include "llvm/Support/Path.h"
7879
#include "llvm/Support/Threading.h"
80+
#include "llvm/Support/raw_ostream.h"
7981
#include <algorithm>
8082
#include <atomic>
8183
#include <chrono>
@@ -902,6 +904,40 @@ void ASTWorker::runWithAST(
902904
startTask(Name, std::move(Task), /*Update=*/None, Invalidation);
903905
}
904906

907+
/// To be called from ThreadCrashReporter's signal handler.
908+
static void crashDumpCompileCommand(llvm::raw_ostream &OS,
909+
const tooling::CompileCommand &Command) {
910+
OS << " Filename: " << Command.Filename << "\n";
911+
OS << " Directory: " << Command.Directory << "\n";
912+
OS << " Command Line:";
913+
for (auto &Arg : Command.CommandLine) {
914+
OS << " " << Arg;
915+
}
916+
OS << "\n";
917+
}
918+
919+
/// To be called from ThreadCrashReporter's signal handler.
920+
static void crashDumpFileContents(llvm::raw_ostream &OS,
921+
const std::string &Contents) {
922+
// Avoid flooding the terminal with source code by default, but allow clients
923+
// to opt in. Use an env var to preserve backwards compatibility of the
924+
// command line interface, while allowing it to be set outside the clangd
925+
// launch site for more flexibility.
926+
if (getenv("CLANGD_CRASH_DUMP_SOURCE")) {
927+
OS << " Contents:\n";
928+
OS << Contents << "\n";
929+
}
930+
}
931+
932+
/// To be called from ThreadCrashReporter's signal handler.
933+
static void crashDumpParseInputs(llvm::raw_ostream &OS,
934+
const ParseInputs &FileInputs) {
935+
auto &Command = FileInputs.CompileCommand;
936+
crashDumpCompileCommand(OS, Command);
937+
OS << " Version: " << FileInputs.Version << "\n";
938+
crashDumpFileContents(OS, FileInputs.Contents);
939+
}
940+
905941
void PreambleThread::build(Request Req) {
906942
assert(Req.CI && "Got preamble request with null compiler invocation");
907943
const ParseInputs &Inputs = Req.Inputs;
@@ -932,6 +968,11 @@ void PreambleThread::build(Request Req) {
932968
FileName, Inputs.Version, LatestBuild->Version);
933969
}
934970

971+
ThreadCrashReporter ScopedReporter([&Inputs]() {
972+
llvm::errs() << "Signalled while building preamble\n";
973+
crashDumpParseInputs(llvm::errs(), Inputs);
974+
});
975+
935976
LatestBuild = clang::clangd::buildPreamble(
936977
FileName, *Req.CI, Inputs, StoreInMemory,
937978
[this, Version(Inputs.Version)](ASTContext &Ctx,
@@ -1288,6 +1329,11 @@ void ASTWorker::run() {
12881329
Status.ASTActivity.Name = CurrentRequest->Name;
12891330
});
12901331
WithContext WithProvidedContext(ContextProvider(FileName));
1332+
ThreadCrashReporter ScopedReporter([this]() {
1333+
const char *Name = CurrentRequest ? CurrentRequest->Name.c_str() : "";
1334+
llvm::errs() << "Signalled during AST worker action: " << Name << "\n";
1335+
crashDumpParseInputs(llvm::errs(), FileInputs);
1336+
});
12911337
CurrentRequest->Action();
12921338
}
12931339

@@ -1613,6 +1659,11 @@ void TUScheduler::runWithPreamble(llvm::StringRef Name, PathRef File,
16131659
Ctx = Context::current().derive(kFileBeingProcessed,
16141660
std::string(File)),
16151661
Action = std::move(Action), this]() mutable {
1662+
ThreadCrashReporter ScopedReporter([&Name, &Contents, &Command]() {
1663+
llvm::errs() << "Signalled during preamble action: " << Name << "\n";
1664+
crashDumpCompileCommand(llvm::errs(), Command);
1665+
crashDumpFileContents(llvm::errs(), Contents);
1666+
});
16161667
std::shared_ptr<const PreambleData> Preamble;
16171668
if (Consistency == PreambleConsistency::Stale) {
16181669
// Wait until the preamble is built for the first time, if preamble

clang-tools-extra/clangd/support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ add_clang_library(clangdSupport
2424
MemoryTree.cpp
2525
Path.cpp
2626
Shutdown.cpp
27+
ThreadCrashReporter.cpp
2728
Threading.cpp
2829
ThreadsafeFS.cpp
2930
Trace.cpp
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//===--- ThreadCrashReporter.cpp - Thread local signal handling --*- C++-*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "support/ThreadCrashReporter.h"
10+
#include "llvm/Support/Signals.h"
11+
#include "llvm/Support/ThreadLocal.h"
12+
13+
namespace clang {
14+
namespace clangd {
15+
16+
static thread_local ThreadCrashReporter *CurrentReporter = nullptr;
17+
18+
void ThreadCrashReporter::runCrashHandlers() {
19+
// No signal handling is done here on top of what AddSignalHandler() does:
20+
// on Windows the signal handling is implmented via
21+
// SetUnhandledExceptionFilter() which is thread-directed, and on Unix
22+
// platforms the handlers are only called for KillSigs out of which only
23+
// SIGQUIT seems to be process-directed and would be delivered to any thread
24+
// that is not blocking it, but if the thread it gets delivered to has a
25+
// ThreadCrashReporter installed during the interrupt — it seems reasonable to
26+
// let it run and print the thread's context information.
27+
28+
// Call the reporters in LIFO order.
29+
ThreadCrashReporter *Reporter = CurrentReporter;
30+
while (Reporter) {
31+
Reporter->Callback();
32+
Reporter = Reporter->Next;
33+
}
34+
}
35+
36+
ThreadCrashReporter::ThreadCrashReporter(SignalCallback ThreadLocalCallback)
37+
: Callback(std::move(ThreadLocalCallback)), Next(nullptr) {
38+
this->Next = CurrentReporter;
39+
CurrentReporter = this;
40+
// Don't reorder subsequent operations: whatever comes after might crash and
41+
// we want the the crash handler to see the reporter values we just set.
42+
std::atomic_signal_fence(std::memory_order_seq_cst);
43+
}
44+
45+
ThreadCrashReporter::~ThreadCrashReporter() {
46+
assert(CurrentReporter == this);
47+
CurrentReporter = this->Next;
48+
// Don't reorder subsequent operations: whatever comes after might crash and
49+
// we want the the crash handler to see the reporter values we just set.
50+
std::atomic_signal_fence(std::memory_order_seq_cst);
51+
}
52+
53+
} // namespace clangd
54+
} // namespace clang
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//===--- ThreadCrashReporter.h - Thread local signal handling ----*- C++-*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_THREADCRASHREPORTER_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_THREADCRASHREPORTER_H
11+
12+
#include "llvm/ADT/FunctionExtras.h"
13+
14+
namespace clang {
15+
namespace clangd {
16+
17+
/// Allows setting per-thread abort/kill signal callbacks, to print additional
18+
/// information about the crash depending on which thread got signalled.
19+
class ThreadCrashReporter {
20+
public:
21+
using SignalCallback = llvm::unique_function<void(void)>;
22+
23+
/// Registers the callback as the first one in thread-local callback chain.
24+
///
25+
/// Asserts if the current thread's callback is already set. The callback is
26+
/// likely to be invoked in a signal handler. Most LLVM signal handling is not
27+
/// strictly async-signal-safe. However reporters should avoid accessing data
28+
/// structures likely to be in a bad state on crash.
29+
ThreadCrashReporter(SignalCallback ThreadLocalCallback);
30+
/// Resets the current thread's callback to nullptr.
31+
~ThreadCrashReporter();
32+
33+
/// Moves are disabled to ease nesting and escaping considerations.
34+
ThreadCrashReporter(ThreadCrashReporter &&RHS) = delete;
35+
ThreadCrashReporter(const ThreadCrashReporter &) = delete;
36+
ThreadCrashReporter &operator=(ThreadCrashReporter &&) = delete;
37+
ThreadCrashReporter &operator=(const ThreadCrashReporter &) = delete;
38+
39+
/// Calls all currently-active ThreadCrashReporters for the current thread.
40+
///
41+
/// To be called from sys::AddSignalHandler() callback. Any signal filtering
42+
/// is the responsibility of the caller. While this function is intended to be
43+
/// called from signal handlers, it is not strictly async-signal-safe - see
44+
/// constructor comment.
45+
///
46+
/// When several reporters are nested, the callbacks are called in LIFO order.
47+
static void runCrashHandlers();
48+
49+
private:
50+
SignalCallback Callback;
51+
/// Points to the next reporter up the stack.
52+
ThreadCrashReporter *Next;
53+
};
54+
55+
} // namespace clangd
56+
} // namespace clang
57+
58+
#endif
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Overflow the recursive json parser, prevent `yes` error due to broken pipe and `clangd` SIGSEGV from being treated as a failure.
2+
# RUN: (yes '[' || :) | head -n 50000 | (clangd --input-style=delimited 2>&1 || :) | FileCheck %s
3+
# CHECK: Signalled while processing message:
4+
# CHECK-NEXT: [
5+
# CHECK-NEXT: [

clang-tools-extra/clangd/tool/ClangdMain.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "refactor/Rename.h"
2525
#include "support/Path.h"
2626
#include "support/Shutdown.h"
27+
#include "support/ThreadCrashReporter.h"
2728
#include "support/ThreadsafeFS.h"
2829
#include "support/Trace.h"
2930
#include "clang/Format/Format.h"
@@ -679,6 +680,8 @@ int main(int argc, char *argv[]) {
679680

680681
llvm::InitializeAllTargetInfos();
681682
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
683+
llvm::sys::AddSignalHandler(
684+
[](void *) { ThreadCrashReporter::runCrashHandlers(); }, nullptr);
682685
llvm::sys::SetInterruptFunction(&requestShutdown);
683686
llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) {
684687
OS << versionString() << "\n"

clang-tools-extra/clangd/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ add_unittest(ClangdUnitTests ClangdTests
8888
TestIndex.cpp
8989
TestTU.cpp
9090
TestWorkspace.cpp
91+
ThreadCrashReporterTests.cpp
9192
TidyProviderTests.cpp
9293
TypeHierarchyTests.cpp
9394
URITests.cpp
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
///===- ThreadCrashReporterTests.cpp - Thread local signal handling tests -===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "support/ThreadCrashReporter.h"
10+
#include "support/Threading.h"
11+
#include "llvm/Support/Signals.h"
12+
#include "gtest/gtest.h"
13+
#include <csignal>
14+
#include <string>
15+
16+
namespace clang {
17+
namespace clangd {
18+
19+
namespace {
20+
21+
static void infoSignalHandler() { ThreadCrashReporter::runCrashHandlers(); }
22+
23+
TEST(ThreadCrashReporterTest, All) {
24+
#if defined(_WIN32)
25+
// Simulate signals on Windows for unit testing purposes.
26+
// The `crash.test` lit test checks the end-to-end integration.
27+
auto SignalCurrentThread = []() { infoSignalHandler(); };
28+
#else
29+
llvm::sys::SetInfoSignalFunction(&infoSignalHandler);
30+
auto SignalCurrentThread = []() { raise(SIGUSR1); };
31+
#endif
32+
33+
AsyncTaskRunner Runner;
34+
auto SignalAnotherThread = [&]() {
35+
Runner.runAsync("signal another thread", SignalCurrentThread);
36+
Runner.wait();
37+
};
38+
39+
bool Called;
40+
{
41+
ThreadCrashReporter ScopedReporter([&Called]() { Called = true; });
42+
// Check handler gets called when a signal gets delivered to the current
43+
// thread.
44+
Called = false;
45+
SignalCurrentThread();
46+
EXPECT_TRUE(Called);
47+
48+
// Check handler does not get called when another thread gets signalled.
49+
Called = false;
50+
SignalAnotherThread();
51+
EXPECT_FALSE(Called);
52+
}
53+
// Check handler does not get called when the reporter object goes out of
54+
// scope.
55+
Called = false;
56+
SignalCurrentThread();
57+
EXPECT_FALSE(Called);
58+
59+
std::string Order = "";
60+
{
61+
ThreadCrashReporter ScopedReporter([&Order] { Order.push_back('a'); });
62+
{
63+
ThreadCrashReporter ScopedReporter([&Order] { Order.push_back('b'); });
64+
SignalCurrentThread();
65+
}
66+
// Check that handlers are called in LIFO order.
67+
EXPECT_EQ(Order, "ba");
68+
69+
// Check that current handler is the only one after the nested scope is
70+
// over.
71+
SignalCurrentThread();
72+
EXPECT_EQ(Order, "baa");
73+
}
74+
}
75+
76+
} // namespace
77+
} // namespace clangd
78+
} // namespace clang

0 commit comments

Comments
 (0)