[clang] [llvm] [clang][MBD] set up module build daemon infrastructure (PR #67562)
Connor Sughrue via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 6 13:02:38 PST 2024
https://github.com/cpsughrue updated https://github.com/llvm/llvm-project/pull/67562
>From 7902b01d845766bac53f85e870048bee3e299ef7 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Sun, 9 Jul 2023 23:19:58 -0400
Subject: [PATCH 01/27] [clang][MBD] set up module build daemon infrastructure
The module build daemon (mbd) will serve as a cc1 tool that helps convert
implicit module command lines to explicit module command lines. A clang
invocation will check to see if a mbd exists, if not the invocation will spawn
one. After the mbd is up and running and a handshake has successfully been
carried out between the mbd and clang invocation the clang invocation will
share it's command line with the mbd. The mbd will then scan the translation
unit and build all modular dependencies before returning a modified cc1 command
line such that all arguments related to modules are converted from the
implicit option to their explicit option.
This commit sets up the basic mbd infrastructure. Including the ability to
spawn a mbd and carry out a handshake between the clang invocation and mbd.
RFC: https://discourse.llvm.org/t/rfc-modules-build-daemon-build-system-agnostic-support-for-explicitly-built-modules/71524
---
clang/include/clang/Driver/Options.td | 12 +
.../include/clang/Frontend/FrontendOptions.h | 7 +
.../clang/Tooling/ModuleBuildDaemon/Client.h | 44 +++
.../ModuleBuildDaemon/SocketMsgSupport.h | 134 +++++++++
.../Tooling/ModuleBuildDaemon/SocketSupport.h | 31 ++
.../clang/Tooling/ModuleBuildDaemon/Utils.h | 28 ++
clang/lib/Driver/ToolChains/Clang.cpp | 14 +-
clang/lib/Tooling/CMakeLists.txt | 1 +
.../Tooling/ModuleBuildDaemon/CMakeLists.txt | 9 +
.../lib/Tooling/ModuleBuildDaemon/Client.cpp | 167 +++++++++++
.../ModuleBuildDaemon/SocketSupport.cpp | 128 +++++++++
clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp | 32 +++
clang/test/Driver/unknown-arg.c | 2 +-
clang/test/ModuleBuildDaemon/handshake.c | 18 ++
clang/test/ModuleBuildDaemon/launch.c | 14 +
clang/tools/driver/CMakeLists.txt | 3 +
clang/tools/driver/cc1_main.cpp | 28 +-
clang/tools/driver/cc1modbuildd_main.cpp | 267 ++++++++++++++++++
clang/tools/driver/driver.cpp | 17 +-
19 files changed, 947 insertions(+), 9 deletions(-)
create mode 100644 clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
create mode 100644 clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
create mode 100644 clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
create mode 100644 clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
create mode 100644 clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
create mode 100644 clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
create mode 100644 clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
create mode 100644 clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
create mode 100644 clang/test/ModuleBuildDaemon/handshake.c
create mode 100644 clang/test/ModuleBuildDaemon/launch.c
create mode 100644 clang/tools/driver/cc1modbuildd_main.cpp
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 4b232b8aab722a..425cf05b260504 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2960,6 +2960,18 @@ defm declspec : BoolOption<"f", "declspec",
NegFlag<SetFalse, [], [ClangOption], "Disallow">,
BothFlags<[], [ClangOption, CC1Option],
" __declspec as a keyword">>, Group<f_clang_Group>;
+
+def fmodule_build_daemon : Flag<["-"], "fmodule-build-daemon">, Group<f_Group>,
+ Flags<[NoXarchOption]>,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<"Enables module build daemon functionality">,
+ MarshallingInfoFlag<FrontendOpts<"ModuleBuildDaemon">>;
+def fmodule_build_daemon_EQ : Joined<["-"], "fmodule-build-daemon=">, Group<f_Group>,
+ Flags<[NoXarchOption]>,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<"Enables module build daemon functionality and defines location of output files">,
+ MarshallingInfoString<FrontendOpts<"ModuleBuildDaemonPath">>;
+
def fmodules_cache_path : Joined<["-"], "fmodules-cache-path=">, Group<i_Group>,
Flags<[]>, Visibility<[ClangOption, CC1Option]>,
MetaVarName<"<directory>">,
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index 53a8681cfdbba0..8bdbb69e720e60 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -383,6 +383,9 @@ class FrontendOptions {
LLVM_PREFERRED_TYPE(bool)
unsigned ModulesShareFileManager : 1;
+ /// Connect to module build daemon
+ unsigned ModuleBuildDaemon : 1;
+
CodeCompleteOptions CodeCompleteOpts;
/// Specifies the output format of the AST.
@@ -468,6 +471,10 @@ class FrontendOptions {
/// The output file, if any.
std::string OutputFile;
+ /// If given, the path to the module build daemon's output files and socket
+ /// address
+ std::string ModuleBuildDaemonPath;
+
/// If given, the new suffix for fix-it rewritten files.
std::string FixItSuffix;
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
new file mode 100644
index 00000000000000..d7506fa3011cff
--- /dev/null
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
@@ -0,0 +1,44 @@
+//===----------------------------- Protocol.h -----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_CLIENT_H
+#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_CLIENT_H
+
+#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
+
+#define MAX_BUFFER 4096
+#define SOCKET_FILE_NAME "mbd.sock"
+#define STDOUT_FILE_NAME "mbd.out"
+#define STDERR_FILE_NAME "mbd.err"
+
+using namespace clang;
+using namespace llvm;
+
+namespace cc1modbuildd {
+
+// Returns where to store log files and socket address. Of the format
+// /tmp/clang-<BLAKE3HashOfClagnFullVersion>/
+std::string getBasePath();
+
+llvm::Error attemptHandshake(int SocketFD);
+
+llvm::Error spawnModuleBuildDaemon(StringRef BasePath, const char *Argv0);
+
+Expected<int> getModuleBuildDaemon(const char *Argv0, StringRef BasePath);
+
+llvm::Error handshakeModuleBuildDaemon(const CompilerInvocation &Clang,
+ const char *Argv0);
+
+} // namespace cc1modbuildd
+
+#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_PROTOCAL_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
new file mode 100644
index 00000000000000..16666c177eaa80
--- /dev/null
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
@@ -0,0 +1,134 @@
+//===------------------------- SocketMsgSupport.h -------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
+#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
+
+#include "clang/Tooling/ModuleBuildDaemon/Client.h"
+#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
+
+using namespace clang;
+using namespace llvm;
+
+namespace cc1modbuildd {
+
+enum class ActionType { HANDSHAKE };
+enum class StatusType { REQUEST, SUCCESS, FAILURE };
+
+struct BaseMsg {
+ ActionType MsgAction;
+ StatusType MsgStatus;
+
+ BaseMsg() = default;
+ BaseMsg(ActionType Action, StatusType Status)
+ : MsgAction(Action), MsgStatus(Status) {}
+};
+
+struct HandshakeMsg : public BaseMsg {
+ HandshakeMsg() = default;
+ HandshakeMsg(ActionType Action, StatusType Status)
+ : BaseMsg(Action, Status) {}
+};
+
+template <typename T> std::string getBufferFromSocketMsg(T Msg) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
+ "T must inherit from cc1modbuildd::BaseMsg");
+
+ std::string Buffer;
+ llvm::raw_string_ostream OS(Buffer);
+ llvm::yaml::Output YamlOut(OS);
+
+ YamlOut << Msg;
+ return Buffer;
+}
+
+template <typename T> Expected<T> getSocketMsgFromBuffer(const char *Buffer) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
+ "T must inherit from cc1modbuildd::BaseMsg");
+
+ T ClientRequest;
+ llvm::yaml::Input YamlIn(Buffer);
+ YamlIn >> ClientRequest;
+
+ if (YamlIn.error()) {
+ std::string Msg = "Syntax or semantic error during YAML parsing";
+ return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
+ }
+
+ return ClientRequest;
+}
+
+template <typename T> Expected<T> readSocketMsgFromSocket(int FD) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
+ "T must inherit from cc1modbuildd::BaseMsg");
+
+ Expected<std::string> MaybeResponseBuffer = readFromSocket(FD);
+ if (!MaybeResponseBuffer)
+ return std::move(MaybeResponseBuffer.takeError());
+
+ // Wait for response from module build daemon
+ Expected<T> MaybeResponse =
+ getSocketMsgFromBuffer<T>(std::move(*MaybeResponseBuffer).c_str());
+ if (!MaybeResponse)
+ return std::move(MaybeResponse.takeError());
+ return std::move(*MaybeResponse);
+}
+
+template <typename T> llvm::Error writeSocketMsgToSocket(T Msg, int FD) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
+ "T must inherit from cc1modbuildd::BaseMsg");
+
+ std::string Buffer = getBufferFromSocketMsg(Msg);
+ if (llvm::Error Err = writeToSocket(Buffer, FD))
+ return std::move(Err);
+
+ return llvm::Error::success();
+}
+
+template <typename T>
+Expected<int> connectAndWriteSocketMsgToSocket(T Msg, StringRef SocketPath) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
+ "T must inherit from cc1modbuildd::BaseMsg");
+
+ Expected<int> MaybeFD = connectToSocket(SocketPath);
+ if (!MaybeFD)
+ return std::move(MaybeFD.takeError());
+ int FD = std::move(*MaybeFD);
+
+ if (llvm::Error Err = writeSocketMsgToSocket(Msg, FD))
+ return std::move(Err);
+
+ return FD;
+}
+
+} // namespace cc1modbuildd
+
+template <>
+struct llvm::yaml::ScalarEnumerationTraits<cc1modbuildd::StatusType> {
+ static void enumeration(IO &Io, cc1modbuildd::StatusType &Value) {
+ Io.enumCase(Value, "REQUEST", cc1modbuildd::StatusType::REQUEST);
+ Io.enumCase(Value, "SUCCESS", cc1modbuildd::StatusType::SUCCESS);
+ Io.enumCase(Value, "FAILURE", cc1modbuildd::StatusType::FAILURE);
+ }
+};
+
+template <>
+struct llvm::yaml::ScalarEnumerationTraits<cc1modbuildd::ActionType> {
+ static void enumeration(IO &Io, cc1modbuildd::ActionType &Value) {
+ Io.enumCase(Value, "HANDSHAKE", cc1modbuildd::ActionType::HANDSHAKE);
+ }
+};
+
+template <> struct llvm::yaml::MappingTraits<cc1modbuildd::HandshakeMsg> {
+ static void mapping(IO &Io, cc1modbuildd::HandshakeMsg &Info) {
+ Io.mapRequired("Action", Info.MsgAction);
+ Io.mapRequired("Status", Info.MsgStatus);
+ }
+};
+
+#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
new file mode 100644
index 00000000000000..bc21084faab396
--- /dev/null
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
@@ -0,0 +1,31 @@
+//===-------------------------- SocketSupport.h ---------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
+#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
+
+#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace clang;
+using namespace llvm;
+
+namespace cc1modbuildd {
+
+Expected<int> createSocket();
+Expected<int> connectToSocket(StringRef SocketPath);
+Expected<int> connectAndWriteToSocket(std::string Buffer, StringRef SocketPath);
+Expected<std::string> readFromSocket(int FD);
+llvm::Error writeToSocket(std::string Buffer, int WriteFD);
+
+} // namespace cc1modbuildd
+
+#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
new file mode 100644
index 00000000000000..000081be92793c
--- /dev/null
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
@@ -0,0 +1,28 @@
+//===------------------------------ Utils.h -------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Functions required by both the module build daemon (server) and clang
+// invocation (client)
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
+#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
+
+#include "llvm/Support/Error.h"
+#include <string>
+
+namespace cc1modbuildd {
+
+void writeError(llvm::Error Err, std::string Msg);
+std::string getFullErrorMsg(llvm::Error Err, std::string Msg);
+llvm::Error makeStringError(llvm::Error Err, std::string Msg);
+
+} // namespace cc1modbuildd
+
+#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 13bf2421154372..762f760af84228 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5,7 +5,6 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-
#include "Clang.h"
#include "AMDGPU.h"
#include "Arch/AArch64.h"
@@ -3764,6 +3763,19 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D,
bool HaveStdCXXModules = IsCXX && HaveStd20;
bool HaveModules = HaveStdCXXModules;
+ // -fmodule-build-daemon enables module build daemon functionality
+ if (Args.hasArg(options::OPT_fmodule_build_daemon))
+ Args.AddLastArg(CmdArgs, options::OPT_fmodule_build_daemon);
+
+ // by default module build daemon socket address and output files are saved
+ // under /tmp/ but that can be overridden by providing the
+ // -fmodule-build-daemon=<path> flag
+ if (Arg *A = Args.getLastArg(options::OPT_fmodule_build_daemon_EQ)) {
+ CmdArgs.push_back(
+ Args.MakeArgString(Twine("-fmodule-build-daemon=") + A->getValue()));
+ CmdArgs.push_back("-fmodule-build-daemon");
+ }
+
// -fmodules enables the use of precompiled modules (off by default).
// Users can pass -fno-cxx-modules to turn off modules support for
// C++/Objective-C++ programs.
diff --git a/clang/lib/Tooling/CMakeLists.txt b/clang/lib/Tooling/CMakeLists.txt
index aff39e4de13c0b..85752e57733265 100644
--- a/clang/lib/Tooling/CMakeLists.txt
+++ b/clang/lib/Tooling/CMakeLists.txt
@@ -13,6 +13,7 @@ add_subdirectory(DumpTool)
add_subdirectory(Syntax)
add_subdirectory(DependencyScanning)
add_subdirectory(Transformer)
+add_subdirectory(ModuleBuildDaemon)
# Replace the last lib component of the current binary directory with include
string(FIND ${CMAKE_CURRENT_BINARY_DIR} "/lib/" PATH_LIB_START REVERSE)
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt b/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
new file mode 100644
index 00000000000000..9c1f5dc1aa2c0c
--- /dev/null
+++ b/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(clangModuleBuildDaemon
+ Client.cpp
+ SocketSupport.cpp
+ Utils.cpp
+ )
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
new file mode 100644
index 00000000000000..f247c4c64da957
--- /dev/null
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
@@ -0,0 +1,167 @@
+//===----------------------------- Client.cpp -----------------------------===//
+//
+// 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 "clang/Tooling/ModuleBuildDaemon/Client.h"
+#include "clang/Basic/Version.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
+#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
+#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/BLAKE3.h"
+
+// TODO: Make portable
+#if LLVM_ON_UNIX
+
+#include <cerrno>
+#include <filesystem>
+#include <fstream>
+#include <signal.h>
+#include <spawn.h>
+#include <string>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+using namespace clang;
+using namespace llvm;
+using namespace cc1modbuildd;
+
+std::string cc1modbuildd::getBasePath() {
+ llvm::BLAKE3 Hash;
+ Hash.update(getClangFullVersion());
+ auto HashResult = Hash.final<sizeof(uint64_t)>();
+ uint64_t HashValue =
+ llvm::support::endian::read<uint64_t, llvm::support::native>(
+ HashResult.data());
+ std::string Key = toString(llvm::APInt(64, HashValue), 36, /*Signed*/ false);
+
+ // set paths
+ SmallString<128> BasePath;
+ llvm::sys::path::system_temp_directory(/*erasedOnReboot*/ true, BasePath);
+ llvm::sys::path::append(BasePath, "clang-" + Key);
+ return BasePath.c_str();
+}
+
+llvm::Error cc1modbuildd::attemptHandshake(int SocketFD) {
+
+ HandshakeMsg Request{ActionType::HANDSHAKE, StatusType::REQUEST};
+ std::string Buffer = getBufferFromSocketMsg(Request);
+
+ if (llvm::Error Err = writeToSocket(Buffer, SocketFD))
+ return std::move(Err);
+
+ Expected<HandshakeMsg> MaybeServerResponse =
+ readSocketMsgFromSocket<HandshakeMsg>(SocketFD);
+ if (!MaybeServerResponse)
+ return std::move(MaybeServerResponse.takeError());
+
+ HandshakeMsg ServerResponse = std::move(*MaybeServerResponse);
+
+ assert(ServerResponse.MsgAction == ActionType::HANDSHAKE &&
+ "At this point response ActionType should only ever be HANDSHAKE");
+
+ if (ServerResponse.MsgStatus == StatusType::SUCCESS)
+ return llvm::Error::success();
+
+ return llvm::make_error<StringError>(
+ "Received failed handshake response from module build daemon",
+ inconvertibleErrorCode());
+}
+
+llvm::Error cc1modbuildd::spawnModuleBuildDaemon(StringRef BasePath,
+ const char *Argv0) {
+ std::string BasePathStr = BasePath.str();
+ const char *Args[] = {Argv0, "-cc1modbuildd", BasePathStr.c_str(), nullptr};
+ pid_t pid;
+ int EC = posix_spawn(&pid, Args[0],
+ /*file_actions*/ nullptr,
+ /*spawnattr*/ nullptr, const_cast<char **>(Args),
+ /*envp*/ nullptr);
+ if (EC)
+ return createStringError(std::error_code(EC, std::generic_category()),
+ "failed to spawn module build daemon");
+
+ return llvm::Error::success();
+}
+
+Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
+ StringRef BasePath) {
+
+ SmallString<128> SocketPath = BasePath;
+ llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME);
+
+ if (llvm::sys::fs::exists(SocketPath)) {
+ Expected<int> MaybeFD = connectToSocket(SocketPath);
+ if (MaybeFD)
+ return std::move(*MaybeFD);
+ consumeError(MaybeFD.takeError());
+ }
+
+ if (llvm::Error Err = cc1modbuildd::spawnModuleBuildDaemon(BasePath, Argv0))
+ return std::move(Err);
+
+ const unsigned int MICROSEC_IN_SEC = 1000000;
+ constexpr unsigned int MAX_TIME = 30 * MICROSEC_IN_SEC;
+ const unsigned short INTERVAL = 100;
+
+ unsigned int CumulativeTime = 0;
+ unsigned int WaitTime = 0;
+
+ while (CumulativeTime <= MAX_TIME) {
+ // Wait a bit then check to see if the module build daemon has initialized
+ usleep(WaitTime);
+
+ if (llvm::sys::fs::exists(SocketPath)) {
+ Expected<int> MaybeFD = connectToSocket(SocketPath);
+ if (MaybeFD)
+ return std::move(*MaybeFD);
+ consumeError(MaybeFD.takeError());
+ }
+
+ CumulativeTime += INTERVAL;
+ }
+
+ // After waiting 30 seconds give up
+ return llvm::make_error<StringError>(
+ "Module build daemon could not be spawned", inconvertibleErrorCode());
+}
+
+llvm::Error
+cc1modbuildd::handshakeModuleBuildDaemon(const CompilerInvocation &Clang,
+ const char *Argv0) {
+
+ // The module build daemon stores all output files and its socket address
+ // under BasePath. Either set BasePath to a user provided option or create an
+ // appropriate BasePath based on the hash of the clang version
+ std::string BasePath;
+ if (!Clang.getFrontendOpts().ModuleBuildDaemonPath.empty())
+ BasePath = Clang.getFrontendOpts().ModuleBuildDaemonPath;
+ else
+ BasePath = cc1modbuildd::getBasePath();
+
+ // If module build daemon does not exist spawn module build daemon
+ Expected<int> MaybeDaemonFD =
+ cc1modbuildd::getModuleBuildDaemon(Argv0, BasePath);
+ if (!MaybeDaemonFD)
+ return makeStringError(MaybeDaemonFD.takeError(),
+ "Attempt to connect to daemon has failed: ");
+ int DaemonFD = std::move(*MaybeDaemonFD);
+
+ if (llvm::Error HandshakeErr = attemptHandshake(DaemonFD))
+ return makeStringError(std::move(HandshakeErr),
+ "Attempted hadshake with daemon has failed: ");
+
+ return llvm::Error::success();
+}
+
+#endif // LLVM_ON_UNIX
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
new file mode 100644
index 00000000000000..58526e4422f457
--- /dev/null
+++ b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
@@ -0,0 +1,128 @@
+//===------------------------- SocketSupport.cpp --------------------------===//
+//
+// 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 "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
+#include "clang/Basic/Version.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Tooling/ModuleBuildDaemon/Client.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/BLAKE3.h"
+
+// TODO: Make portable
+#if LLVM_ON_UNIX
+
+#include <cerrno>
+#include <filesystem>
+#include <fstream>
+#include <signal.h>
+#include <spawn.h>
+#include <string>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+Expected<int> cc1modbuildd::createSocket() {
+ int FD;
+ if ((FD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ std::string Msg = "socket create error: " + std::string(strerror(errno));
+ return createStringError(inconvertibleErrorCode(), Msg);
+ }
+ return FD;
+}
+
+Expected<int> cc1modbuildd::connectToSocket(StringRef SocketPath) {
+
+ Expected<int> MaybeFD = cc1modbuildd::createSocket();
+ if (!MaybeFD)
+ return std::move(MaybeFD.takeError());
+
+ int FD = std::move(*MaybeFD);
+
+ struct sockaddr_un Addr;
+ memset(&Addr, 0, sizeof(Addr));
+ Addr.sun_family = AF_UNIX;
+ strncpy(Addr.sun_path, SocketPath.str().c_str(), sizeof(Addr.sun_path) - 1);
+
+ if (connect(FD, (struct sockaddr *)&Addr, sizeof(Addr)) == -1) {
+ close(FD);
+ std::string msg = "socket connect error: " + std::string(strerror(errno));
+ return createStringError(inconvertibleErrorCode(), msg);
+ }
+ return FD;
+}
+
+Expected<int> cc1modbuildd::connectAndWriteToSocket(std::string Buffer,
+ StringRef SocketPath) {
+
+ Expected<int> MaybeConnectedFD = connectToSocket(SocketPath);
+ if (!MaybeConnectedFD)
+ return std::move(MaybeConnectedFD.takeError());
+
+ int ConnectedFD = std::move(*MaybeConnectedFD);
+ llvm::Error Err = writeToSocket(Buffer, ConnectedFD);
+ if (Err)
+ return std::move(Err);
+
+ return ConnectedFD;
+}
+
+Expected<std::string> cc1modbuildd::readFromSocket(int FD) {
+
+ const size_t BUFFER_SIZE = 4096;
+ std::vector<char> Buffer(BUFFER_SIZE);
+ size_t TotalBytesRead = 0;
+
+ ssize_t n;
+ while ((n = read(FD, Buffer.data() + TotalBytesRead,
+ Buffer.size() - TotalBytesRead)) > 0) {
+
+ TotalBytesRead += n;
+ // Read until ...\n encountered (last line of YAML document)
+ if (std::string(&Buffer[TotalBytesRead - 4], 4) == "...\n")
+ break;
+ if (Buffer.size() - TotalBytesRead < BUFFER_SIZE)
+ Buffer.resize(Buffer.size() + BUFFER_SIZE);
+ }
+
+ if (n < 0) {
+ std::string Msg = "socket read error: " + std::string(strerror(errno));
+ return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
+ }
+ if (n == 0)
+ return llvm::make_error<StringError>("EOF", inconvertibleErrorCode());
+ return std::string(Buffer.begin(), Buffer.end());
+}
+
+llvm::Error cc1modbuildd::writeToSocket(std::string Buffer, int WriteFD) {
+
+ ssize_t BytesToWrite = static_cast<ssize_t>(Buffer.size());
+ const char *Bytes = Buffer.c_str();
+
+ while (BytesToWrite) {
+ ssize_t BytesWritten = write(WriteFD, Bytes, BytesToWrite);
+ if (BytesWritten == -1) {
+ std::string Msg = "socket write error: " + std::string(strerror(errno));
+ return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
+ }
+
+ if (!BytesWritten || BytesWritten > BytesToWrite)
+ return llvm::errorCodeToError(
+ std::error_code(EIO, std::generic_category()));
+
+ BytesToWrite -= BytesWritten;
+ Bytes += BytesWritten;
+ }
+ return llvm::Error::success();
+}
+
+#endif // LLVM_ON_UNIX
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
new file mode 100644
index 00000000000000..0e291987faf93f
--- /dev/null
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
@@ -0,0 +1,32 @@
+//===------------------------------ Utils.cpp -----------------------------===//
+//
+// 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 <clang/Tooling/ModuleBuildDaemon/Utils.h>
+#include <llvm/Support/Error.h>
+#include <string>
+
+using namespace llvm;
+
+void cc1modbuildd::writeError(llvm::Error Err, std::string Msg) {
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
+ errs() << Msg << EIB.message() << '\n';
+ });
+}
+
+std::string cc1modbuildd::getFullErrorMsg(llvm::Error Err, std::string Msg) {
+ std::string ErrMessage;
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
+ ErrMessage = Msg + EIB.message();
+ });
+ return ErrMessage;
+}
+
+llvm::Error cc1modbuildd::makeStringError(llvm::Error Err, std::string Msg) {
+ std::string ErrMsg = getFullErrorMsg(std::move(Err), Msg);
+ return llvm::make_error<StringError>(ErrMsg, inconvertibleErrorCode());
+}
diff --git a/clang/test/Driver/unknown-arg.c b/clang/test/Driver/unknown-arg.c
index 52ea0f5ff3220f..3a22b824adc5c7 100644
--- a/clang/test/Driver/unknown-arg.c
+++ b/clang/test/Driver/unknown-arg.c
@@ -59,7 +59,7 @@
// SILENT-NOT: warning:
// CC1AS-DID-YOU-MEAN: error: unknown argument '-hell'; did you mean '-help'?
// CC1AS-DID-YOU-MEAN: error: unknown argument '--version'; did you mean '-version'?
-// UNKNOWN-INTEGRATED: error: unknown integrated tool '-cc1asphalt'. Valid tools include '-cc1' and '-cc1as'.
+// UNKNOWN-INTEGRATED: error: unknown integrated tool '-cc1asphalt'. Valid tools include '-cc1', '-cc1as', '-cc1gen-reproducer', and '-cc1modbuildd'.
// RUN: %clang -S %s -o %t.s -Wunknown-to-clang-option 2>&1 | FileCheck --check-prefix=IGNORED %s
diff --git a/clang/test/ModuleBuildDaemon/handshake.c b/clang/test/ModuleBuildDaemon/handshake.c
new file mode 100644
index 00000000000000..ac9a5ebab87e4d
--- /dev/null
+++ b/clang/test/ModuleBuildDaemon/handshake.c
@@ -0,0 +1,18 @@
+// Check that clang invocation can spawn and handshake with module build daemon
+
+// REQUIRES: !system-windows
+
+// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
+// RUN: rm -rf mbd-handshake %t
+// RUN: split-file %s %t
+
+//--- main.c
+int main() {return 0;}
+
+// RUN: %clang -fmodule-build-daemon=mbd-handshake %t/main.c > output
+// RUN: cat output | FileCheck %s
+
+// CHECK: Completed successfull handshake with module build daemon
+
+// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
+// RUN: rm -rf mbd-handshake %t
diff --git a/clang/test/ModuleBuildDaemon/launch.c b/clang/test/ModuleBuildDaemon/launch.c
new file mode 100644
index 00000000000000..878bdfa4052f54
--- /dev/null
+++ b/clang/test/ModuleBuildDaemon/launch.c
@@ -0,0 +1,14 @@
+// Check that module build daemon can create unix socket
+
+// REQUIRES: !system-windows
+
+// RUN: if pgrep -f "cc1modbuildd mbd-launch"; then pkill -f "cc1modbuildd mbd-launch"; fi
+// RUN: rm -rf mbd-launch %t
+
+// RUN: %clang -cc1modbuildd mbd-launch -v
+// RUN: cat mbd-launch/mbd.out | FileCheck %s
+
+// CHECK: mbd created and binded to socket address at: mbd-launch/mbd.sock
+
+// RUN: if pgrep -f "cc1modbuildd mbd-launch"; then pkill -f "cc1modbuildd mbd-launch"; fi
+// RUN: rm -rf mbd-launch %t
diff --git a/clang/tools/driver/CMakeLists.txt b/clang/tools/driver/CMakeLists.txt
index d70b92b0984e52..e44475b58b8ac4 100644
--- a/clang/tools/driver/CMakeLists.txt
+++ b/clang/tools/driver/CMakeLists.txt
@@ -28,6 +28,7 @@ add_clang_tool(clang
cc1_main.cpp
cc1as_main.cpp
cc1gen_reproducer_main.cpp
+ cc1modbuildd_main.cpp
DEPENDS
intrinsics_gen
@@ -39,9 +40,11 @@ clang_target_link_libraries(clang
PRIVATE
clangBasic
clangCodeGen
+ clangDependencyScanning
clangDriver
clangFrontend
clangFrontendTool
+ clangModuleBuildDaemon
clangSerialization
)
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index e9d2c6aad371db..25db6bf09082e0 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -25,6 +25,7 @@
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
#include "clang/FrontendTool/Utils.h"
+#include "clang/Tooling/ModuleBuildDaemon/Client.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/LinkAllPasses.h"
@@ -63,7 +64,7 @@ using namespace llvm::opt;
static void LLVMErrorHandler(void *UserData, const char *Message,
bool GenCrashDiag) {
- DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
+ DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine *>(UserData);
Diags.Report(diag::err_fe_error_backend) << Message;
@@ -201,7 +202,7 @@ static int PrintSupportedExtensions(std::string TargetStr) {
const llvm::Triple &MachineTriple = TheTargetMachine->getTargetTriple();
const llvm::MCSubtargetInfo *MCInfo = TheTargetMachine->getMCSubtargetInfo();
const llvm::ArrayRef<llvm::SubtargetFeatureKV> Features =
- MCInfo->getAllProcessorFeatures();
+ MCInfo->getAllProcessorFeatures();
llvm::StringMap<llvm::StringRef> DescMap;
for (const llvm::SubtargetFeatureKV &feature : Features)
@@ -270,7 +271,7 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
if (Clang->getHeaderSearchOpts().UseBuiltinIncludes &&
Clang->getHeaderSearchOpts().ResourceDir.empty())
Clang->getHeaderSearchOpts().ResourceDir =
- CompilerInvocation::GetResourcesPath(Argv0, MainAddr);
+ CompilerInvocation::GetResourcesPath(Argv0, MainAddr);
// Create the actual diagnostics engine.
Clang->createDiagnostics();
@@ -279,8 +280,8 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
// Set an error handler, so that any LLVM backend diagnostics go through our
// error handler.
- llvm::install_fatal_error_handler(LLVMErrorHandler,
- static_cast<void*>(&Clang->getDiagnostics()));
+ llvm::install_fatal_error_handler(
+ LLVMErrorHandler, static_cast<void *>(&Clang->getDiagnostics()));
DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics());
if (!Success) {
@@ -288,6 +289,23 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
return 1;
}
+#if LLVM_ON_UNIX
+ // Handle module build daemon functionality if enabled
+ if (Clang->getFrontendOpts().ModuleBuildDaemon) {
+
+ llvm::Error HandshakeErr =
+ cc1modbuildd::handshakeModuleBuildDaemon(Clang->getInvocation(), Argv0);
+ if (HandshakeErr) {
+ handleAllErrors(std::move(HandshakeErr), [&](ErrorInfoBase &EIB) {
+ errs() << EIB.message() << '\n';
+ });
+ return 1;
+ }
+ outs() << "Completed successfull handshake with module build daemon"
+ << '\n';
+ }
+#endif
+
// Execute the frontend actions.
{
llvm::TimeTraceScope TimeScope("ExecuteCompiler");
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
new file mode 100644
index 00000000000000..bb8ace9dfde651
--- /dev/null
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -0,0 +1,267 @@
+//===------- cc1modbuildd_main.cpp - Clang CC1 Module Build Daemon --------===//
+//
+// 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 "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
+#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
+#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
+
+// TODO: Make portable
+#if LLVM_ON_UNIX
+
+#include <errno.h>
+#include <fstream>
+#include <mutex>
+#include <optional>
+#include <signal.h>
+#include <sstream>
+#include <stdbool.h>
+#include <string>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <type_traits>
+#include <unistd.h>
+#include <unordered_map>
+
+using namespace llvm;
+using namespace clang;
+using namespace cc1modbuildd;
+
+// Create unbuffered STDOUT stream so that any logging done by module build
+// daemon can be viewed without having to terminate the process
+static raw_fd_ostream &unbuff_outs() {
+ static raw_fd_ostream S(STDOUT_FILENO, false, true);
+ return S;
+}
+
+namespace {
+
+class ModuleBuildDaemonServer {
+public:
+ SmallString<128> BasePath;
+ SmallString<128> SocketPath;
+ SmallString<128> PidPath;
+
+ ModuleBuildDaemonServer(SmallString<128> Path, ArrayRef<const char *> Argv)
+ : BasePath(Path), SocketPath(Path) {
+ llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME);
+ }
+
+ ~ModuleBuildDaemonServer() { shutdownDaemon(SIGTERM); }
+
+ int forkDaemon();
+ int launchDaemon();
+ int listenForClients();
+
+ static void handleClient(int Client);
+
+ void shutdownDaemon(int signal) {
+ unlink(SocketPath.c_str());
+ shutdown(ListenSocketFD, SHUT_RD);
+ close(ListenSocketFD);
+ exit(EXIT_SUCCESS);
+ }
+
+private:
+ // Initializes and returns DiagnosticsEngine
+ pid_t Pid = -1;
+ int ListenSocketFD = -1;
+};
+
+// Required to handle SIGTERM by calling Shutdown
+ModuleBuildDaemonServer *DaemonPtr = nullptr;
+void handleSignal(int Signal) {
+ if (DaemonPtr != nullptr) {
+ DaemonPtr->shutdownDaemon(Signal);
+ }
+}
+} // namespace
+
+static bool verbose = false;
+static void verbose_print(const llvm::Twine &message) {
+ if (verbose) {
+ unbuff_outs() << message << '\n';
+ }
+}
+
+// Forks and detaches process, creating module build daemon
+int ModuleBuildDaemonServer::forkDaemon() {
+
+ pid_t pid = fork();
+
+ if (pid < 0) {
+ exit(EXIT_FAILURE);
+ }
+ if (pid > 0) {
+ exit(EXIT_SUCCESS);
+ }
+
+ Pid = getpid();
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ SmallString<128> STDOUT = BasePath;
+ llvm::sys::path::append(STDOUT, STDOUT_FILE_NAME);
+ freopen(STDOUT.c_str(), "a", stdout);
+
+ SmallString<128> STDERR = BasePath;
+ llvm::sys::path::append(STDERR, STDERR_FILE_NAME);
+ freopen(STDERR.c_str(), "a", stderr);
+
+ if (signal(SIGTERM, handleSignal) == SIG_ERR) {
+ errs() << "failed to handle SIGTERM" << '\n';
+ exit(EXIT_FAILURE);
+ }
+ if (signal(SIGHUP, SIG_IGN) == SIG_ERR) {
+ errs() << "failed to ignore SIGHUP" << '\n';
+ exit(EXIT_FAILURE);
+ }
+ if (setsid() == -1) {
+ errs() << "setsid failed" << '\n';
+ exit(EXIT_FAILURE);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+// Creates unix socket for IPC with module build daemon
+int ModuleBuildDaemonServer::launchDaemon() {
+
+ // new socket
+ if ((ListenSocketFD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ std::perror("Socket create error: ");
+ exit(EXIT_FAILURE);
+ }
+
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(struct sockaddr_un));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, SocketPath.c_str(), sizeof(addr.sun_path) - 1);
+
+ // bind to local address
+ if (bind(ListenSocketFD, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+
+ // If the socket address is already in use, exit because another module
+ // build daemon has successfully launched. When translation units are
+ // compiled in parallel, until the socket file is created, all clang
+ // invocations will spawn a module build daemon.
+ if (errno == EADDRINUSE) {
+ close(ListenSocketFD);
+ exit(EXIT_SUCCESS);
+ }
+ std::perror("Socket bind error: ");
+ exit(EXIT_FAILURE);
+ }
+ verbose_print("mbd created and binded to socket address at: " + SocketPath);
+
+ // set socket to accept incoming connection request
+ unsigned MaxBacklog = llvm::hardware_concurrency().compute_thread_count();
+ if (listen(ListenSocketFD, MaxBacklog) == -1) {
+ std::perror("Socket listen error: ");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
+
+// Function submitted to thread pool with each client connection. Not
+// responsible for closing client connections
+void ModuleBuildDaemonServer::handleClient(int Client) {
+
+ // Read handshake from client
+ Expected<HandshakeMsg> MaybeHandshakeMsg =
+ readSocketMsgFromSocket<HandshakeMsg>(Client);
+
+ if (!MaybeHandshakeMsg) {
+ writeError(MaybeHandshakeMsg.takeError(),
+ "Failed to read handshake message from socket: ");
+ return;
+ }
+
+ // Handle HANDSHAKE
+ HandshakeMsg Msg(ActionType::HANDSHAKE, StatusType::SUCCESS);
+ llvm::Error WriteErr = writeSocketMsgToSocket(Msg, Client);
+
+ if (WriteErr) {
+ writeError(std::move(WriteErr),
+ "Failed to notify client that handshake was received");
+ return;
+ }
+ return;
+}
+
+int ModuleBuildDaemonServer::listenForClients() {
+
+ llvm::ThreadPool Pool;
+ int Client;
+
+ while (true) {
+
+ if ((Client = accept(ListenSocketFD, NULL, NULL)) == -1) {
+ std::perror("Socket accept error: ");
+ continue;
+ }
+
+ Pool.async(handleClient, Client);
+ }
+ return 0;
+}
+
+// Module build daemon is spawned with the following command line:
+//
+// clang -cc1modbuildd <path> -v
+//
+// <path> defines the location of all files created by the module build daemon
+// and should follow the format /path/to/dir. For example, `clang
+// -cc1modbuildd /tmp/` creates a socket file at `/tmp/mbd.sock`
+//
+// When a module build daemon is spawned by a cc1 invocations, <path> follows
+// the format /tmp/clang-<BLAKE3HashOfClangFullVersion> and looks something like
+// /tmp/clang-3NXKISKJ0WJTN
+//
+// -v is optional and provides berbose debug information
+//
+int cc1modbuildd_main(ArrayRef<const char *> Argv) {
+
+ if (Argv.size() < 1) {
+ outs() << "spawning a module build daemon requies a command line format of "
+ "`clang -cc1modbuildd <path>`. <path> defines where the module "
+ "build daemon will create mbd.out, mbd.err, mbd.sock"
+ << '\n';
+ return 1;
+ }
+
+ // Where to store log files and socket address
+ // TODO: Add check to confirm BasePath is directory
+ SmallString<128> BasePath(Argv[0]);
+ llvm::sys::fs::create_directories(BasePath);
+ ModuleBuildDaemonServer Daemon(BasePath, Argv);
+
+ // Used to handle signals
+ DaemonPtr = &Daemon;
+
+ if (find(Argv, StringRef("-v")) != Argv.end())
+ verbose = true;
+
+ Daemon.forkDaemon();
+ Daemon.launchDaemon();
+ Daemon.listenForClients();
+
+ return 0;
+}
+
+#endif // LLVM_ON_UNIX
diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp
index 1407c7fcdab769..3098084ea6005f 100644
--- a/clang/tools/driver/driver.cpp
+++ b/clang/tools/driver/driver.cpp
@@ -212,6 +212,9 @@ extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv,
const char *Argv0, void *MainAddr,
const llvm::ToolContext &);
+#if LLVM_ON_UNIX
+extern int cc1modbuildd_main(ArrayRef<const char *> Argv);
+#endif
static void insertTargetAndModeArgs(const ParsedClangName &NameParts,
SmallVectorImpl<const char *> &ArgVector,
@@ -368,9 +371,19 @@ static int ExecuteCC1Tool(SmallVectorImpl<const char *> &ArgV,
if (Tool == "-cc1gen-reproducer")
return cc1gen_reproducer_main(ArrayRef(ArgV).slice(2), ArgV[0],
GetExecutablePathVP, ToolContext);
- // Reject unknown tools.
+ if (Tool == "-cc1modbuildd") {
+#if LLVM_ON_UNIX
+ return cc1modbuildd_main(ArrayRef(ArgV).slice(2));
+#else
+ llvm::errs() << "-cc1modbuildd not supported by current platform" << '\n';
+ return 1;
+#endif
+ }
+
+ // Reject unknown tools
llvm::errs() << "error: unknown integrated tool '" << Tool << "'. "
- << "Valid tools include '-cc1' and '-cc1as'.\n";
+ << "Valid tools include '-cc1', '-cc1as', '-cc1gen-reproducer', "
+ "and '-cc1modbuildd'.\n";
return 1;
}
>From dfdce6d4668608cf2dcb85fdaa2a2a3d47e8c89a Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Fri, 29 Sep 2023 00:30:25 -0400
Subject: [PATCH 02/27] Address feedback
- Fix accidental formats
- Clarify HelpText for -fmodule-build-daemon=
- Correct file name in header license comment and header guard comment
- Moved #define(s) used by both the client and server from Client.h to Util.h
- Change socket write functions to take buffer as StringRef instead of std::string
- Fix exponential backoff behavior when waiting for module build daemon to spawn
- Remove unused function from SocketSupport.cpp
- Modify where path to stdout and stderr file descriptors get set
- Add error message for when -fmodule-build-daemon is used on unsupported platform
- Remove parameter from shutdownDaemon because it was never used
- Do not delete temp directory at end of llvm-lit test
- Rename launchDaemon to createDaemonSocket
- Rename handshakeModuleBuildDaemon to spawnModuleBuildDaemonAndHandshake
- Close client before daemon handleClient returns
- Rework read from socket to buffer to better handle stream
---
clang/include/clang/Driver/Options.td | 3 +-
.../clang/Tooling/ModuleBuildDaemon/Client.h | 13 +--
.../ModuleBuildDaemon/SocketMsgSupport.h | 10 +--
.../Tooling/ModuleBuildDaemon/SocketSupport.h | 5 +-
.../clang/Tooling/ModuleBuildDaemon/Utils.h | 5 ++
.../lib/Tooling/ModuleBuildDaemon/Client.cpp | 18 ++--
.../ModuleBuildDaemon/SocketSupport.cpp | 39 +++------
clang/test/ModuleBuildDaemon/handshake.c | 1 -
clang/test/ModuleBuildDaemon/launch.c | 1 -
clang/tools/driver/cc1_main.cpp | 12 +--
clang/tools/driver/cc1modbuildd_main.cpp | 85 ++++++++++++-------
11 files changed, 97 insertions(+), 95 deletions(-)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 425cf05b260504..376b7a313bebee 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2969,7 +2969,8 @@ def fmodule_build_daemon : Flag<["-"], "fmodule-build-daemon">, Group<f_Group>,
def fmodule_build_daemon_EQ : Joined<["-"], "fmodule-build-daemon=">, Group<f_Group>,
Flags<[NoXarchOption]>,
Visibility<[ClangOption, CC1Option]>,
- HelpText<"Enables module build daemon functionality and defines location of output files">,
+ HelpText<"Enables module build daemon functionality and defines the path to "
+ "where the module build daemon will write log and socket files">,
MarshallingInfoString<FrontendOpts<"ModuleBuildDaemonPath">>;
def fmodules_cache_path : Joined<["-"], "fmodules-cache-path=">, Group<i_Group>,
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
index d7506fa3011cff..eb4cac1aa8b168 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
@@ -1,4 +1,4 @@
-//===----------------------------- Protocol.h -----------------------------===//
+//===------------------------------ Client.h ------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -16,11 +16,6 @@
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
-#define MAX_BUFFER 4096
-#define SOCKET_FILE_NAME "mbd.sock"
-#define STDOUT_FILE_NAME "mbd.out"
-#define STDERR_FILE_NAME "mbd.err"
-
using namespace clang;
using namespace llvm;
@@ -36,9 +31,9 @@ llvm::Error spawnModuleBuildDaemon(StringRef BasePath, const char *Argv0);
Expected<int> getModuleBuildDaemon(const char *Argv0, StringRef BasePath);
-llvm::Error handshakeModuleBuildDaemon(const CompilerInvocation &Clang,
- const char *Argv0);
+llvm::Error spawnModuleBuildDaemonAndHandshake(const CompilerInvocation &Clang,
+ const char *Argv0);
} // namespace cc1modbuildd
-#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_PROTOCAL_H
+#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_CLIENT_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
index 16666c177eaa80..0c2994e0489251 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
@@ -47,7 +47,7 @@ template <typename T> std::string getBufferFromSocketMsg(T Msg) {
return Buffer;
}
-template <typename T> Expected<T> getSocketMsgFromBuffer(const char *Buffer) {
+template <typename T> Expected<T> getSocketMsgFromBuffer(StringRef Buffer) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
@@ -67,13 +67,13 @@ template <typename T> Expected<T> readSocketMsgFromSocket(int FD) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
- Expected<std::string> MaybeResponseBuffer = readFromSocket(FD);
- if (!MaybeResponseBuffer)
- return std::move(MaybeResponseBuffer.takeError());
+ std::string BufferConsumer;
+ if (llvm::Error ReadErr = readFromSocket(FD, BufferConsumer))
+ return std::move(ReadErr);
// Wait for response from module build daemon
Expected<T> MaybeResponse =
- getSocketMsgFromBuffer<T>(std::move(*MaybeResponseBuffer).c_str());
+ getSocketMsgFromBuffer<T>(std::move(BufferConsumer).c_str());
if (!MaybeResponse)
return std::move(MaybeResponse.takeError());
return std::move(*MaybeResponse);
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
index bc21084faab396..712f65e695086b 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
@@ -22,9 +22,8 @@ namespace cc1modbuildd {
Expected<int> createSocket();
Expected<int> connectToSocket(StringRef SocketPath);
-Expected<int> connectAndWriteToSocket(std::string Buffer, StringRef SocketPath);
-Expected<std::string> readFromSocket(int FD);
-llvm::Error writeToSocket(std::string Buffer, int WriteFD);
+llvm::Error readFromSocket(int FD, std::string &BufferConsumer);
+llvm::Error writeToSocket(StringRef Buffer, int WriteFD);
} // namespace cc1modbuildd
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
index 000081be92793c..63ac2bf54c6ba8 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
@@ -17,6 +17,11 @@
#include "llvm/Support/Error.h"
#include <string>
+#define MAX_BUFFER 4096
+#define SOCKET_FILE_NAME "mbd.sock"
+#define STDOUT_FILE_NAME "mbd.out"
+#define STDERR_FILE_NAME "mbd.err"
+
namespace cc1modbuildd {
void writeError(llvm::Error Err, std::string Msg);
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
index f247c4c64da957..766b78ee940d41 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
@@ -45,7 +45,7 @@ std::string cc1modbuildd::getBasePath() {
HashResult.data());
std::string Key = toString(llvm::APInt(64, HashValue), 36, /*Signed*/ false);
- // set paths
+ // Set paths
SmallString<128> BasePath;
llvm::sys::path::system_temp_directory(/*erasedOnReboot*/ true, BasePath);
llvm::sys::path::append(BasePath, "clang-" + Key);
@@ -111,13 +111,12 @@ Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
return std::move(Err);
const unsigned int MICROSEC_IN_SEC = 1000000;
- constexpr unsigned int MAX_TIME = 30 * MICROSEC_IN_SEC;
- const unsigned short INTERVAL = 100;
+ constexpr unsigned int MAX_WAIT_TIME = 30 * MICROSEC_IN_SEC;
unsigned int CumulativeTime = 0;
- unsigned int WaitTime = 0;
+ unsigned int WaitTime = 10;
- while (CumulativeTime <= MAX_TIME) {
+ while (CumulativeTime <= MAX_WAIT_TIME) {
// Wait a bit then check to see if the module build daemon has initialized
usleep(WaitTime);
@@ -128,7 +127,9 @@ Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
consumeError(MaybeFD.takeError());
}
- CumulativeTime += INTERVAL;
+ CumulativeTime += WaitTime;
+ // Exponential backoff
+ WaitTime = WaitTime * 2;
}
// After waiting 30 seconds give up
@@ -136,9 +137,8 @@ Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
"Module build daemon could not be spawned", inconvertibleErrorCode());
}
-llvm::Error
-cc1modbuildd::handshakeModuleBuildDaemon(const CompilerInvocation &Clang,
- const char *Argv0) {
+llvm::Error cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
+ const CompilerInvocation &Clang, const char *Argv0) {
// The module build daemon stores all output files and its socket address
// under BasePath. Either set BasePath to a user provided option or create an
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
index 58526e4422f457..ae1e40a9a44376 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
@@ -10,6 +10,7 @@
#include "clang/Basic/Version.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Tooling/ModuleBuildDaemon/Client.h"
+#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
@@ -61,37 +62,17 @@ Expected<int> cc1modbuildd::connectToSocket(StringRef SocketPath) {
return FD;
}
-Expected<int> cc1modbuildd::connectAndWriteToSocket(std::string Buffer,
- StringRef SocketPath) {
-
- Expected<int> MaybeConnectedFD = connectToSocket(SocketPath);
- if (!MaybeConnectedFD)
- return std::move(MaybeConnectedFD.takeError());
-
- int ConnectedFD = std::move(*MaybeConnectedFD);
- llvm::Error Err = writeToSocket(Buffer, ConnectedFD);
- if (Err)
- return std::move(Err);
-
- return ConnectedFD;
-}
-
-Expected<std::string> cc1modbuildd::readFromSocket(int FD) {
-
- const size_t BUFFER_SIZE = 4096;
- std::vector<char> Buffer(BUFFER_SIZE);
- size_t TotalBytesRead = 0;
+llvm::Error cc1modbuildd::readFromSocket(int FD, std::string &BufferConsumer) {
+ char Buffer[MAX_BUFFER];
ssize_t n;
- while ((n = read(FD, Buffer.data() + TotalBytesRead,
- Buffer.size() - TotalBytesRead)) > 0) {
- TotalBytesRead += n;
+ while ((n = read(FD, Buffer, MAX_BUFFER)) > 0) {
+
+ BufferConsumer.assign(Buffer, n);
// Read until ...\n encountered (last line of YAML document)
- if (std::string(&Buffer[TotalBytesRead - 4], 4) == "...\n")
+ if (BufferConsumer.find("...\n") != std::string::npos)
break;
- if (Buffer.size() - TotalBytesRead < BUFFER_SIZE)
- Buffer.resize(Buffer.size() + BUFFER_SIZE);
}
if (n < 0) {
@@ -100,13 +81,13 @@ Expected<std::string> cc1modbuildd::readFromSocket(int FD) {
}
if (n == 0)
return llvm::make_error<StringError>("EOF", inconvertibleErrorCode());
- return std::string(Buffer.begin(), Buffer.end());
+ return llvm::Error::success();
}
-llvm::Error cc1modbuildd::writeToSocket(std::string Buffer, int WriteFD) {
+llvm::Error cc1modbuildd::writeToSocket(StringRef Buffer, int WriteFD) {
ssize_t BytesToWrite = static_cast<ssize_t>(Buffer.size());
- const char *Bytes = Buffer.c_str();
+ const char *Bytes = Buffer.data();
while (BytesToWrite) {
ssize_t BytesWritten = write(WriteFD, Bytes, BytesToWrite);
diff --git a/clang/test/ModuleBuildDaemon/handshake.c b/clang/test/ModuleBuildDaemon/handshake.c
index ac9a5ebab87e4d..5dd8f1b5a03a84 100644
--- a/clang/test/ModuleBuildDaemon/handshake.c
+++ b/clang/test/ModuleBuildDaemon/handshake.c
@@ -15,4 +15,3 @@ int main() {return 0;}
// CHECK: Completed successfull handshake with module build daemon
// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
-// RUN: rm -rf mbd-handshake %t
diff --git a/clang/test/ModuleBuildDaemon/launch.c b/clang/test/ModuleBuildDaemon/launch.c
index 878bdfa4052f54..d447cf9e26774b 100644
--- a/clang/test/ModuleBuildDaemon/launch.c
+++ b/clang/test/ModuleBuildDaemon/launch.c
@@ -11,4 +11,3 @@
// CHECK: mbd created and binded to socket address at: mbd-launch/mbd.sock
// RUN: if pgrep -f "cc1modbuildd mbd-launch"; then pkill -f "cc1modbuildd mbd-launch"; fi
-// RUN: rm -rf mbd-launch %t
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index 25db6bf09082e0..46bc310bcf335c 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -289,12 +289,11 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
return 1;
}
-#if LLVM_ON_UNIX
// Handle module build daemon functionality if enabled
if (Clang->getFrontendOpts().ModuleBuildDaemon) {
-
- llvm::Error HandshakeErr =
- cc1modbuildd::handshakeModuleBuildDaemon(Clang->getInvocation(), Argv0);
+#if LLVM_ON_UNIX
+ llvm::Error HandshakeErr = cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
+ Clang->getInvocation(), Argv0);
if (HandshakeErr) {
handleAllErrors(std::move(HandshakeErr), [&](ErrorInfoBase &EIB) {
errs() << EIB.message() << '\n';
@@ -303,8 +302,11 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
}
outs() << "Completed successfull handshake with module build daemon"
<< '\n';
- }
+#else
+ errs() << "-fmodule-build-daemon not supported by current platform" << '\n';
+ return 1;
#endif
+ }
// Execute the frontend actions.
{
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index bb8ace9dfde651..1f934de3d6e9ac 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -48,26 +48,33 @@ static raw_fd_ostream &unbuff_outs() {
namespace {
+struct ClientConnection {
+ int ClientFD;
+ std::string Buffer;
+};
+
class ModuleBuildDaemonServer {
public:
- SmallString<128> BasePath;
- SmallString<128> SocketPath;
- SmallString<128> PidPath;
+ SmallString<256> SocketPath;
+ SmallString<256> STDERR;
+ SmallString<256> STDOUT;
- ModuleBuildDaemonServer(SmallString<128> Path, ArrayRef<const char *> Argv)
- : BasePath(Path), SocketPath(Path) {
+ ModuleBuildDaemonServer(StringRef Path, ArrayRef<const char *> Argv)
+ : SocketPath(Path), STDERR(Path), STDOUT(Path) {
llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME);
+ llvm::sys::path::append(STDOUT, STDOUT_FILE_NAME);
+ llvm::sys::path::append(STDERR, STDERR_FILE_NAME);
}
- ~ModuleBuildDaemonServer() { shutdownDaemon(SIGTERM); }
+ ~ModuleBuildDaemonServer() { shutdownDaemon(); }
int forkDaemon();
- int launchDaemon();
+ int createDaemonSocket();
int listenForClients();
- static void handleClient(int Client);
+ static void handleClient(ClientConnection Connection);
- void shutdownDaemon(int signal) {
+ void shutdownDaemon() {
unlink(SocketPath.c_str());
shutdown(ListenSocketFD, SHUT_RD);
close(ListenSocketFD);
@@ -84,14 +91,14 @@ class ModuleBuildDaemonServer {
ModuleBuildDaemonServer *DaemonPtr = nullptr;
void handleSignal(int Signal) {
if (DaemonPtr != nullptr) {
- DaemonPtr->shutdownDaemon(Signal);
+ DaemonPtr->shutdownDaemon();
}
}
} // namespace
-static bool verbose = false;
-static void verbose_print(const llvm::Twine &message) {
- if (verbose) {
+static bool VerboseLog = false;
+static void printVerboseLog(const llvm::Twine &message) {
+ if (VerboseLog) {
unbuff_outs() << message << '\n';
}
}
@@ -114,12 +121,7 @@ int ModuleBuildDaemonServer::forkDaemon() {
close(STDOUT_FILENO);
close(STDERR_FILENO);
- SmallString<128> STDOUT = BasePath;
- llvm::sys::path::append(STDOUT, STDOUT_FILE_NAME);
freopen(STDOUT.c_str(), "a", stdout);
-
- SmallString<128> STDERR = BasePath;
- llvm::sys::path::append(STDERR, STDERR_FILE_NAME);
freopen(STDERR.c_str(), "a", stderr);
if (signal(SIGTERM, handleSignal) == SIG_ERR) {
@@ -139,7 +141,7 @@ int ModuleBuildDaemonServer::forkDaemon() {
}
// Creates unix socket for IPC with module build daemon
-int ModuleBuildDaemonServer::launchDaemon() {
+int ModuleBuildDaemonServer::createDaemonSocket() {
// new socket
if ((ListenSocketFD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
@@ -166,7 +168,7 @@ int ModuleBuildDaemonServer::launchDaemon() {
std::perror("Socket bind error: ");
exit(EXIT_FAILURE);
}
- verbose_print("mbd created and binded to socket address at: " + SocketPath);
+ printVerboseLog("mbd created and binded to socket address at: " + SocketPath);
// set socket to accept incoming connection request
unsigned MaxBacklog = llvm::hardware_concurrency().compute_thread_count();
@@ -180,27 +182,43 @@ int ModuleBuildDaemonServer::launchDaemon() {
// Function submitted to thread pool with each client connection. Not
// responsible for closing client connections
-void ModuleBuildDaemonServer::handleClient(int Client) {
+// TODO: Setup something like ScopedHandle to auto close client on return
+void ModuleBuildDaemonServer::handleClient(ClientConnection Connection) {
// Read handshake from client
- Expected<HandshakeMsg> MaybeHandshakeMsg =
- readSocketMsgFromSocket<HandshakeMsg>(Client);
+ if (llvm::Error ReadErr =
+ readFromSocket(Connection.ClientFD, Connection.Buffer)) {
+ writeError(std::move(ReadErr), "Daemon failed to read buffer from socket");
+ close(Connection.ClientFD);
+ return;
+ }
+ // Wait for response from module build daemon
+ Expected<HandshakeMsg> MaybeHandshakeMsg =
+ getSocketMsgFromBuffer<HandshakeMsg>(Connection.Buffer);
if (!MaybeHandshakeMsg) {
writeError(MaybeHandshakeMsg.takeError(),
- "Failed to read handshake message from socket: ");
+ "Failed to convert buffer to HandshakeMsg: ");
+ close(Connection.ClientFD);
return;
}
- // Handle HANDSHAKE
+ // Have received HandshakeMsg - send HandshakeMsg response to clang invocation
HandshakeMsg Msg(ActionType::HANDSHAKE, StatusType::SUCCESS);
- llvm::Error WriteErr = writeSocketMsgToSocket(Msg, Client);
-
- if (WriteErr) {
+ if (llvm::Error WriteErr = writeSocketMsgToSocket(Msg, Connection.ClientFD)) {
writeError(std::move(WriteErr),
"Failed to notify client that handshake was received");
+ close(Connection.ClientFD);
return;
}
+
+ // Remove HandshakeMsg from Buffer in preperation for next read. Not currently
+ // necessary but will be once Daemon increases communication
+ size_t Position = Connection.Buffer.find("...\n");
+ if (Position != std::string::npos)
+ Connection.Buffer = Connection.Buffer.substr(Position + 4);
+
+ close(Connection.ClientFD);
return;
}
@@ -216,7 +234,10 @@ int ModuleBuildDaemonServer::listenForClients() {
continue;
}
- Pool.async(handleClient, Client);
+ ClientConnection Connection;
+ Connection.ClientFD = Client;
+
+ Pool.async(handleClient, Connection);
}
return 0;
}
@@ -247,7 +268,7 @@ int cc1modbuildd_main(ArrayRef<const char *> Argv) {
// Where to store log files and socket address
// TODO: Add check to confirm BasePath is directory
- SmallString<128> BasePath(Argv[0]);
+ std::string BasePath(Argv[0]);
llvm::sys::fs::create_directories(BasePath);
ModuleBuildDaemonServer Daemon(BasePath, Argv);
@@ -255,10 +276,10 @@ int cc1modbuildd_main(ArrayRef<const char *> Argv) {
DaemonPtr = &Daemon;
if (find(Argv, StringRef("-v")) != Argv.end())
- verbose = true;
+ VerboseLog = true;
Daemon.forkDaemon();
- Daemon.launchDaemon();
+ Daemon.createDaemonSocket();
Daemon.listenForClients();
return 0;
>From e5ef1d69bba983244e53e0d3271b7df1aea49b2b Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Fri, 29 Sep 2023 14:43:34 -0400
Subject: [PATCH 03/27] Address feedback
- Add remark for module build daemon and update tests
- Change return value of spawnModuleBuildDaemonAndHandshake to void
- Add check to make sure provided socket address is an appropriate length
- Make ListenSocketFD atomic
---
.../clang/Basic/DiagnosticFrontendKinds.td | 5 ++
clang/include/clang/Basic/DiagnosticGroups.td | 1 +
.../clang/Tooling/ModuleBuildDaemon/Client.h | 14 +--
.../lib/Tooling/ModuleBuildDaemon/Client.cpp | 86 ++++++++++++++-----
clang/test/ModuleBuildDaemon/handshake.c | 26 +++++-
clang/test/ModuleBuildDaemon/launch.c | 2 +-
clang/tools/driver/cc1_main.cpp | 12 +--
clang/tools/driver/cc1modbuildd_main.cpp | 36 +++++---
8 files changed, 129 insertions(+), 53 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 85ecfdf9de62d4..d8fe479c789fb9 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -262,6 +262,11 @@ def err_test_module_file_extension_version : Error<
"test module file extension '%0' has different version (%1.%2) than expected "
"(%3.%4)">;
+def warn_module_build_daemon : Warning<"%0">,
+ InGroup<ModuleBuildDaemon>;
+def remark_module_build_daemon : Remark<"%0">,
+ InGroup<ModuleBuildDaemon>;
+
def warn_eagerly_load_for_standard_cplusplus_modules : Warning<
"the form '-fmodule-file=<BMI-path>' is deprecated for standard C++ named modules;"
"consider to use '-fmodule-file=<module-name>=<BMI-path>' instead">,
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 6765721ae7002c..59fdae48f8f6e9 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -517,6 +517,7 @@ def MismatchedTags : DiagGroup<"mismatched-tags">;
def MissingFieldInitializers : DiagGroup<"missing-field-initializers">;
def ModuleLock : DiagGroup<"module-lock">;
def ModuleBuild : DiagGroup<"module-build">;
+def ModuleBuildDaemon : DiagGroup<"module-build-daemon">;
def ModuleImport : DiagGroup<"module-import">;
def ModuleConflict : DiagGroup<"module-conflict">;
def ModuleFileExtension : DiagGroup<"module-file-extension">;
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
index eb4cac1aa8b168..de9db38a6612b5 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
@@ -12,7 +12,6 @@
#include "clang/Frontend/CompilerInstance.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
-#include "llvm/Config/llvm-config.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
@@ -25,14 +24,17 @@ namespace cc1modbuildd {
// /tmp/clang-<BLAKE3HashOfClagnFullVersion>/
std::string getBasePath();
-llvm::Error attemptHandshake(int SocketFD);
+llvm::Error attemptHandshake(int SocketFD, DiagnosticsEngine &Diag);
-llvm::Error spawnModuleBuildDaemon(StringRef BasePath, const char *Argv0);
+llvm::Error spawnModuleBuildDaemon(StringRef BasePath, const char *Argv0,
+ DiagnosticsEngine &Diag);
-Expected<int> getModuleBuildDaemon(const char *Argv0, StringRef BasePath);
+Expected<int> getModuleBuildDaemon(const char *Argv0, StringRef BasePath,
+ DiagnosticsEngine &Diag);
-llvm::Error spawnModuleBuildDaemonAndHandshake(const CompilerInvocation &Clang,
- const char *Argv0);
+void spawnModuleBuildDaemonAndHandshake(const CompilerInvocation &Clang,
+ DiagnosticsEngine &Diag,
+ const char *Argv0);
} // namespace cc1modbuildd
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
index 766b78ee940d41..2516a8bcc2d8a8 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
@@ -9,6 +9,7 @@
#include "clang/Tooling/ModuleBuildDaemon/Client.h"
#include "clang/Basic/Version.h"
#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
@@ -52,26 +53,37 @@ std::string cc1modbuildd::getBasePath() {
return BasePath.c_str();
}
-llvm::Error cc1modbuildd::attemptHandshake(int SocketFD) {
+llvm::Error cc1modbuildd::attemptHandshake(int SocketFD,
+ DiagnosticsEngine &Diag) {
HandshakeMsg Request{ActionType::HANDSHAKE, StatusType::REQUEST};
std::string Buffer = getBufferFromSocketMsg(Request);
+ // Send HandshakeMsg to module build daemon
+ Diag.Report(diag::remark_module_build_daemon)
+ << "Trying to send HandshakeMsg to module build daemon";
if (llvm::Error Err = writeToSocket(Buffer, SocketFD))
return std::move(Err);
+ Diag.Report(diag::remark_module_build_daemon)
+ << "Successfully sent HandshakeMsg to module build daemon";
+ // Receive response from module build daemon
+ Diag.Report(diag::remark_module_build_daemon)
+ << "Waiting to receive module build daemon response";
Expected<HandshakeMsg> MaybeServerResponse =
readSocketMsgFromSocket<HandshakeMsg>(SocketFD);
if (!MaybeServerResponse)
return std::move(MaybeServerResponse.takeError());
-
HandshakeMsg ServerResponse = std::move(*MaybeServerResponse);
assert(ServerResponse.MsgAction == ActionType::HANDSHAKE &&
- "At this point response ActionType should only ever be HANDSHAKE");
+ "Response ActionType should only ever be HANDSHAKE");
- if (ServerResponse.MsgStatus == StatusType::SUCCESS)
+ if (ServerResponse.MsgStatus == StatusType::SUCCESS) {
+ Diag.Report(diag::remark_module_build_daemon)
+ << "Successfully received HandshakeMsg from module build daemon";
return llvm::Error::success();
+ }
return llvm::make_error<StringError>(
"Received failed handshake response from module build daemon",
@@ -79,35 +91,45 @@ llvm::Error cc1modbuildd::attemptHandshake(int SocketFD) {
}
llvm::Error cc1modbuildd::spawnModuleBuildDaemon(StringRef BasePath,
- const char *Argv0) {
+ const char *Argv0,
+ DiagnosticsEngine &Diag) {
std::string BasePathStr = BasePath.str();
const char *Args[] = {Argv0, "-cc1modbuildd", BasePathStr.c_str(), nullptr};
pid_t pid;
+ Diag.Report(diag::remark_module_build_daemon)
+ << "Trying to spawn module build daemon";
int EC = posix_spawn(&pid, Args[0],
/*file_actions*/ nullptr,
/*spawnattr*/ nullptr, const_cast<char **>(Args),
/*envp*/ nullptr);
if (EC)
return createStringError(std::error_code(EC, std::generic_category()),
- "failed to spawn module build daemon");
+ "Failed to spawn module build daemon");
+ Diag.Report(diag::remark_module_build_daemon)
+ << "Successfully spawned module build daemon";
return llvm::Error::success();
}
Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
- StringRef BasePath) {
+ StringRef BasePath,
+ DiagnosticsEngine &Diag) {
SmallString<128> SocketPath = BasePath;
llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME);
if (llvm::sys::fs::exists(SocketPath)) {
Expected<int> MaybeFD = connectToSocket(SocketPath);
- if (MaybeFD)
+ if (MaybeFD) {
+ Diag.Report(diag::remark_module_build_daemon)
+ << "Module build daemon already exists";
return std::move(*MaybeFD);
+ }
consumeError(MaybeFD.takeError());
}
- if (llvm::Error Err = cc1modbuildd::spawnModuleBuildDaemon(BasePath, Argv0))
+ if (llvm::Error Err =
+ cc1modbuildd::spawnModuleBuildDaemon(BasePath, Argv0, Diag))
return std::move(Err);
const unsigned int MICROSEC_IN_SEC = 1000000;
@@ -120,10 +142,15 @@ Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
// Wait a bit then check to see if the module build daemon has initialized
usleep(WaitTime);
+ Diag.Report(diag::remark_module_build_daemon)
+ << "Trying to connect to recently spawned module build daemon";
if (llvm::sys::fs::exists(SocketPath)) {
Expected<int> MaybeFD = connectToSocket(SocketPath);
- if (MaybeFD)
+ if (MaybeFD) {
+ Diag.Report(diag::remark_module_build_daemon)
+ << "Succesfully connected to recently spawned module build daemon";
return std::move(*MaybeFD);
+ }
consumeError(MaybeFD.takeError());
}
@@ -137,31 +164,48 @@ Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
"Module build daemon could not be spawned", inconvertibleErrorCode());
}
-llvm::Error cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
- const CompilerInvocation &Clang, const char *Argv0) {
+void cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
+ const CompilerInvocation &Clang, DiagnosticsEngine &Diag,
+ const char *Argv0) {
// The module build daemon stores all output files and its socket address
// under BasePath. Either set BasePath to a user provided option or create an
- // appropriate BasePath based on the hash of the clang version
+ // appropriate BasePath based on the hash of the full clang version
std::string BasePath;
if (!Clang.getFrontendOpts().ModuleBuildDaemonPath.empty())
BasePath = Clang.getFrontendOpts().ModuleBuildDaemonPath;
else
BasePath = cc1modbuildd::getBasePath();
+ // On most unix platforms a socket address cannot be over 108 characters
+ int MAX_ADDR = 108;
+ if (BasePath.length() >= MAX_ADDR - std::string(SOCKET_FILE_NAME).length()) {
+ Diag.Report(diag::warn_module_build_daemon)
+ << "Provided socket path" + BasePath +
+ " is too long. Socket path much be equal to or less then 100 "
+ "characters. Invocation will not spawn module build daemon.";
+ return;
+ }
+
// If module build daemon does not exist spawn module build daemon
Expected<int> MaybeDaemonFD =
- cc1modbuildd::getModuleBuildDaemon(Argv0, BasePath);
- if (!MaybeDaemonFD)
- return makeStringError(MaybeDaemonFD.takeError(),
- "Attempt to connect to daemon has failed: ");
+ cc1modbuildd::getModuleBuildDaemon(Argv0, BasePath, Diag);
+ if (!MaybeDaemonFD) {
+ Diag.Report(diag::warn_module_build_daemon) << getFullErrorMsg(
+ MaybeDaemonFD.takeError(), "Attempt to connect to daemon has failed: ");
+ return;
+ }
int DaemonFD = std::move(*MaybeDaemonFD);
- if (llvm::Error HandshakeErr = attemptHandshake(DaemonFD))
- return makeStringError(std::move(HandshakeErr),
- "Attempted hadshake with daemon has failed: ");
+ if (llvm::Error HandshakeErr = attemptHandshake(DaemonFD, Diag)) {
+ Diag.Report(diag::warn_module_build_daemon) << getFullErrorMsg(
+ std::move(HandshakeErr), "Attempted hadshake with daemon has failed: ");
+ return;
+ }
- return llvm::Error::success();
+ Diag.Report(diag::remark_module_build_daemon)
+ << "Completed successfull handshake with module build daemon";
+ return;
}
#endif // LLVM_ON_UNIX
diff --git a/clang/test/ModuleBuildDaemon/handshake.c b/clang/test/ModuleBuildDaemon/handshake.c
index 5dd8f1b5a03a84..497b47af039f40 100644
--- a/clang/test/ModuleBuildDaemon/handshake.c
+++ b/clang/test/ModuleBuildDaemon/handshake.c
@@ -1,4 +1,5 @@
-// Check that clang invocation can spawn and handshake with module build daemon
+// COM: Check that clang invocation can spawn and handshake with module build daemon
+// COM: Also check that clang invocation can handshake with existing module build daemon
// REQUIRES: !system-windows
@@ -9,9 +10,26 @@
//--- main.c
int main() {return 0;}
-// RUN: %clang -fmodule-build-daemon=mbd-handshake %t/main.c > output
-// RUN: cat output | FileCheck %s
+// RUN: %clang -fmodule-build-daemon=mbd-handshake -Rmodule-build-daemon %t/main.c &> %t/output-new
+// RUN: cat %t/output-new | FileCheck %s
+// RUN: %clang -fmodule-build-daemon=mbd-handshake -Rmodule-build-daemon %t/main.c &> %t/output-existing
+// RUN: cat %t/output-existing | FileCheck %s --check-prefix=CHECK-EXIST
-// CHECK: Completed successfull handshake with module build daemon
+// CHECK: remark: Trying to spawn module build daemon [-Rmodule-build-daemon]
+// CHECK: remark: Successfully spawned module build daemon [-Rmodule-build-daemon]
+// CHECK: remark: Trying to connect to recently spawned module build daemon [-Rmodule-build-daemon]
+// CHECK: remark: Succesfully connected to recently spawned module build daemon [-Rmodule-build-daemon]
+// CHECK: remark: Trying to send HandshakeMsg to module build daemon [-Rmodule-build-daemon]
+// CHECK: remark: Successfully sent HandshakeMsg to module build daemon [-Rmodule-build-daemon]
+// CHECK: remark: Waiting to receive module build daemon response [-Rmodule-build-daemon]
+// CHECK: remark: Successfully received HandshakeMsg from module build daemon [-Rmodule-build-daemon]
+// CHECK: remark: Completed successfull handshake with module build daemon [-Rmodule-build-daemon]
+
+// CHECK-EXIST: remark: Module build daemon already exists [-Rmodule-build-daemon]
+// CHECK-EXIST: remark: Trying to send HandshakeMsg to module build daemon [-Rmodule-build-daemon]
+// CHECK-EXIST: remark: Successfully sent HandshakeMsg to module build daemon [-Rmodule-build-daemon]
+// CHECK-EXIST: remark: Waiting to receive module build daemon response [-Rmodule-build-daemon]
+// CHECK-EXIST: remark: Successfully received HandshakeMsg from module build daemon [-Rmodule-build-daemon]
+// CHECK-EXIST: remark: Completed successfull handshake with module build daemon [-Rmodule-build-daemon]
// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
diff --git a/clang/test/ModuleBuildDaemon/launch.c b/clang/test/ModuleBuildDaemon/launch.c
index d447cf9e26774b..cb4cdbc2b32af9 100644
--- a/clang/test/ModuleBuildDaemon/launch.c
+++ b/clang/test/ModuleBuildDaemon/launch.c
@@ -1,4 +1,4 @@
-// Check that module build daemon can create unix socket
+// COM: Check that module build daemon can create unix socket
// REQUIRES: !system-windows
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index 46bc310bcf335c..2bf4f95c34b46d 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -292,16 +292,8 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
// Handle module build daemon functionality if enabled
if (Clang->getFrontendOpts().ModuleBuildDaemon) {
#if LLVM_ON_UNIX
- llvm::Error HandshakeErr = cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
- Clang->getInvocation(), Argv0);
- if (HandshakeErr) {
- handleAllErrors(std::move(HandshakeErr), [&](ErrorInfoBase &EIB) {
- errs() << EIB.message() << '\n';
- });
- return 1;
- }
- outs() << "Completed successfull handshake with module build daemon"
- << '\n';
+ cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
+ Clang->getInvocation(), Clang->getDiagnostics(), Argv0);
#else
errs() << "-fmodule-build-daemon not supported by current platform" << '\n';
return 1;
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index 1f934de3d6e9ac..75d2771c747746 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -75,16 +75,17 @@ class ModuleBuildDaemonServer {
static void handleClient(ClientConnection Connection);
void shutdownDaemon() {
+ int SocketFD = ListenSocketFD.load();
+
unlink(SocketPath.c_str());
- shutdown(ListenSocketFD, SHUT_RD);
- close(ListenSocketFD);
+ shutdown(SocketFD, SHUT_RD);
+ close(SocketFD);
exit(EXIT_SUCCESS);
}
private:
- // Initializes and returns DiagnosticsEngine
pid_t Pid = -1;
- int ListenSocketFD = -1;
+ std::atomic<int> ListenSocketFD = -1;
};
// Required to handle SIGTERM by calling Shutdown
@@ -143,8 +144,10 @@ int ModuleBuildDaemonServer::forkDaemon() {
// Creates unix socket for IPC with module build daemon
int ModuleBuildDaemonServer::createDaemonSocket() {
- // new socket
- if ((ListenSocketFD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ // New socket
+ int SocketFD = socket(AF_UNIX, SOCK_STREAM, 0);
+
+ if (SocketFD == -1) {
std::perror("Socket create error: ");
exit(EXIT_FAILURE);
}
@@ -155,14 +158,14 @@ int ModuleBuildDaemonServer::createDaemonSocket() {
strncpy(addr.sun_path, SocketPath.c_str(), sizeof(addr.sun_path) - 1);
// bind to local address
- if (bind(ListenSocketFD, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ if (bind(SocketFD, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
// If the socket address is already in use, exit because another module
// build daemon has successfully launched. When translation units are
// compiled in parallel, until the socket file is created, all clang
// invocations will spawn a module build daemon.
if (errno == EADDRINUSE) {
- close(ListenSocketFD);
+ close(SocketFD);
exit(EXIT_SUCCESS);
}
std::perror("Socket bind error: ");
@@ -172,11 +175,12 @@ int ModuleBuildDaemonServer::createDaemonSocket() {
// set socket to accept incoming connection request
unsigned MaxBacklog = llvm::hardware_concurrency().compute_thread_count();
- if (listen(ListenSocketFD, MaxBacklog) == -1) {
+ if (listen(SocketFD, MaxBacklog) == -1) {
std::perror("Socket listen error: ");
exit(EXIT_FAILURE);
}
+ ListenSocketFD.store(SocketFD);
return 0;
}
@@ -229,7 +233,7 @@ int ModuleBuildDaemonServer::listenForClients() {
while (true) {
- if ((Client = accept(ListenSocketFD, NULL, NULL)) == -1) {
+ if ((Client = accept(ListenSocketFD.load(), NULL, NULL)) == -1) {
std::perror("Socket accept error: ");
continue;
}
@@ -259,7 +263,7 @@ int ModuleBuildDaemonServer::listenForClients() {
int cc1modbuildd_main(ArrayRef<const char *> Argv) {
if (Argv.size() < 1) {
- outs() << "spawning a module build daemon requies a command line format of "
+ errs() << "spawning a module build daemon requies a command line format of "
"`clang -cc1modbuildd <path>`. <path> defines where the module "
"build daemon will create mbd.out, mbd.err, mbd.sock"
<< '\n';
@@ -269,6 +273,16 @@ int cc1modbuildd_main(ArrayRef<const char *> Argv) {
// Where to store log files and socket address
// TODO: Add check to confirm BasePath is directory
std::string BasePath(Argv[0]);
+
+ // On most unix platforms a socket address cannot be over 108 characters
+ int MAX_ADDR = 108;
+ if (BasePath.length() >= MAX_ADDR - std::string(SOCKET_FILE_NAME).length()) {
+ errs() << "Provided socket path" + BasePath +
+ " is too long. Socket path much be equal to or less then 100 "
+ "characters. Module build daemon will not be spawned.";
+ return 1;
+ }
+
llvm::sys::fs::create_directories(BasePath);
ModuleBuildDaemonServer Daemon(BasePath, Argv);
>From c28c706394e616310523f15579cbac97f84a1b07 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Sat, 30 Sep 2023 14:24:47 -0400
Subject: [PATCH 04/27] Updated test Frontend/warning-options.cpp
Adding -Wmodule-build-daemon prevents the compiler from suggesting
-Wmodule-conflict as an alternative to -Wmodule-build
---
clang/test/Frontend/warning-options.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/Frontend/warning-options.cpp b/clang/test/Frontend/warning-options.cpp
index 444733c8b7f365..42a7109b8d48c5 100644
--- a/clang/test/Frontend/warning-options.cpp
+++ b/clang/test/Frontend/warning-options.cpp
@@ -3,6 +3,6 @@
// CHECK: unknown warning option '-Wmonkey'
// CHECK: unknown warning option '-Wno-monkey'
// CHECK: unknown warning option '-Wno-unused-command-line-arguments'; did you mean '-Wno-unused-command-line-argument'?
-// CHECK: unknown warning option '-Wmodule-build'; did you mean '-Wmodule-conflict'?
+// CHECK: unknown warning option '-Wmodule-build'
// CHECK-NEXT: unknown -Werror warning specifier: '-Werror-vla'
// CHECK: unknown remark option '-Rmodule-built'; did you mean '-Rmodule-build'?
>From 00bf28b7a6063d7b3888aaaf22af65e158dc502d Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Wed, 25 Oct 2023 13:56:27 -0400
Subject: [PATCH 05/27] Address feedback
- In readFromSocket change assign to append
- Change token used to find end of YAML document
- Remove condition from writeToSocket that will never be met
- Change exit to _Exit in signal handle
- Remove unessary use of remark and update test
- Convert warning to error for when socket address is too long to fit in unix_path.sun_addr
- Improve diagnositc options for module build daemon
---
.../clang/Basic/DiagnosticFrontendKinds.td | 20 ++++++++-
.../clang/Tooling/ModuleBuildDaemon/Client.h | 2 +-
.../ModuleBuildDaemon/SocketMsgSupport.h | 1 +
.../lib/Tooling/ModuleBuildDaemon/Client.cpp | 42 ++++++-------------
.../ModuleBuildDaemon/SocketSupport.cpp | 10 ++---
clang/test/ModuleBuildDaemon/handshake.c | 22 +++-------
clang/tools/driver/cc1modbuildd_main.cpp | 5 ++-
7 files changed, 45 insertions(+), 57 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index d8fe479c789fb9..8a69a599369063 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -262,9 +262,25 @@ def err_test_module_file_extension_version : Error<
"test module file extension '%0' has different version (%1.%2) than expected "
"(%3.%4)">;
-def warn_module_build_daemon : Warning<"%0">,
+def err_unix_socket_addr_length :
+ Error<"Socket address %0 is %1 characters long which is larger then the max length of %2">,
+ DefaultFatal;
+
+// Module Build Daemon
+def err_mbd_handshake :
+ Error<"Attempt to hadshake with daemon has failed: %0">,
+ DefaultFatal;
+def err_mbd_connect :
+ Error<"Attempt to connect to daemon has failed: %0">,
+ DefaultFatal;
+def remark_mbd_spawn :
+ Remark<"Successfully spawned module build daemon">,
+ InGroup<ModuleBuildDaemon>;
+def remark_mbd_connection :
+ Remark<"Successfully connected to module build daemon at %0">,
InGroup<ModuleBuildDaemon>;
-def remark_module_build_daemon : Remark<"%0">,
+def remark_mbd_handshake :
+ Remark<"Successfully completed handshake with module build daemon">,
InGroup<ModuleBuildDaemon>;
def warn_eagerly_load_for_standard_cplusplus_modules : Warning<
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
index de9db38a6612b5..648e9bf440b95f 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
@@ -21,7 +21,7 @@ using namespace llvm;
namespace cc1modbuildd {
// Returns where to store log files and socket address. Of the format
-// /tmp/clang-<BLAKE3HashOfClagnFullVersion>/
+// /tmp/clang-<BLAKE3HashOfClangFullVersion>/
std::string getBasePath();
llvm::Error attemptHandshake(int SocketFD, DiagnosticsEngine &Diag);
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
index 0c2994e0489251..280a528131412b 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
@@ -55,6 +55,7 @@ template <typename T> Expected<T> getSocketMsgFromBuffer(StringRef Buffer) {
llvm::yaml::Input YamlIn(Buffer);
YamlIn >> ClientRequest;
+ // YamlIn.error() dumps an error message if there is one
if (YamlIn.error()) {
std::string Msg = "Syntax or semantic error during YAML parsing";
return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
index 2516a8bcc2d8a8..b2a444dd362f59 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
@@ -60,16 +60,10 @@ llvm::Error cc1modbuildd::attemptHandshake(int SocketFD,
std::string Buffer = getBufferFromSocketMsg(Request);
// Send HandshakeMsg to module build daemon
- Diag.Report(diag::remark_module_build_daemon)
- << "Trying to send HandshakeMsg to module build daemon";
if (llvm::Error Err = writeToSocket(Buffer, SocketFD))
return std::move(Err);
- Diag.Report(diag::remark_module_build_daemon)
- << "Successfully sent HandshakeMsg to module build daemon";
// Receive response from module build daemon
- Diag.Report(diag::remark_module_build_daemon)
- << "Waiting to receive module build daemon response";
Expected<HandshakeMsg> MaybeServerResponse =
readSocketMsgFromSocket<HandshakeMsg>(SocketFD);
if (!MaybeServerResponse)
@@ -80,8 +74,6 @@ llvm::Error cc1modbuildd::attemptHandshake(int SocketFD,
"Response ActionType should only ever be HANDSHAKE");
if (ServerResponse.MsgStatus == StatusType::SUCCESS) {
- Diag.Report(diag::remark_module_build_daemon)
- << "Successfully received HandshakeMsg from module build daemon";
return llvm::Error::success();
}
@@ -96,8 +88,8 @@ llvm::Error cc1modbuildd::spawnModuleBuildDaemon(StringRef BasePath,
std::string BasePathStr = BasePath.str();
const char *Args[] = {Argv0, "-cc1modbuildd", BasePathStr.c_str(), nullptr};
pid_t pid;
- Diag.Report(diag::remark_module_build_daemon)
- << "Trying to spawn module build daemon";
+
+ // TODO: Swich to llvm::sys::ExecuteNoWait
int EC = posix_spawn(&pid, Args[0],
/*file_actions*/ nullptr,
/*spawnattr*/ nullptr, const_cast<char **>(Args),
@@ -106,8 +98,7 @@ llvm::Error cc1modbuildd::spawnModuleBuildDaemon(StringRef BasePath,
return createStringError(std::error_code(EC, std::generic_category()),
"Failed to spawn module build daemon");
- Diag.Report(diag::remark_module_build_daemon)
- << "Successfully spawned module build daemon";
+ Diag.Report(diag::remark_mbd_spawn);
return llvm::Error::success();
}
@@ -121,8 +112,6 @@ Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
if (llvm::sys::fs::exists(SocketPath)) {
Expected<int> MaybeFD = connectToSocket(SocketPath);
if (MaybeFD) {
- Diag.Report(diag::remark_module_build_daemon)
- << "Module build daemon already exists";
return std::move(*MaybeFD);
}
consumeError(MaybeFD.takeError());
@@ -142,13 +131,10 @@ Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
// Wait a bit then check to see if the module build daemon has initialized
usleep(WaitTime);
- Diag.Report(diag::remark_module_build_daemon)
- << "Trying to connect to recently spawned module build daemon";
if (llvm::sys::fs::exists(SocketPath)) {
Expected<int> MaybeFD = connectToSocket(SocketPath);
if (MaybeFD) {
- Diag.Report(diag::remark_module_build_daemon)
- << "Succesfully connected to recently spawned module build daemon";
+ Diag.Report(diag::remark_mbd_connection) << SocketPath;
return std::move(*MaybeFD);
}
consumeError(MaybeFD.takeError());
@@ -177,13 +163,14 @@ void cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
else
BasePath = cc1modbuildd::getBasePath();
- // On most unix platforms a socket address cannot be over 108 characters
+ // TODO: Max length may vary across different platforms. Incoming llvm/Support
+ // for sockets will help make this portable. On most unix platforms a socket
+ // address cannot be over 108 characters. The socket file, mbd.sock, takes up
+ // 8 characters leaving 100 characters left for the user/system
int MAX_ADDR = 108;
if (BasePath.length() >= MAX_ADDR - std::string(SOCKET_FILE_NAME).length()) {
- Diag.Report(diag::warn_module_build_daemon)
- << "Provided socket path" + BasePath +
- " is too long. Socket path much be equal to or less then 100 "
- "characters. Invocation will not spawn module build daemon.";
+ Diag.Report(diag::err_unix_socket_addr_length)
+ << BasePath << BasePath.length() << 100;
return;
}
@@ -191,20 +178,17 @@ void cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
Expected<int> MaybeDaemonFD =
cc1modbuildd::getModuleBuildDaemon(Argv0, BasePath, Diag);
if (!MaybeDaemonFD) {
- Diag.Report(diag::warn_module_build_daemon) << getFullErrorMsg(
- MaybeDaemonFD.takeError(), "Attempt to connect to daemon has failed: ");
+ Diag.Report(diag::err_mbd_connect) << MaybeDaemonFD.takeError();
return;
}
int DaemonFD = std::move(*MaybeDaemonFD);
if (llvm::Error HandshakeErr = attemptHandshake(DaemonFD, Diag)) {
- Diag.Report(diag::warn_module_build_daemon) << getFullErrorMsg(
- std::move(HandshakeErr), "Attempted hadshake with daemon has failed: ");
+ Diag.Report(diag::err_mbd_handshake) << std::move(HandshakeErr);
return;
}
- Diag.Report(diag::remark_module_build_daemon)
- << "Completed successfull handshake with module build daemon";
+ Diag.Report(diag::remark_mbd_handshake);
return;
}
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
index ae1e40a9a44376..a2997aa11756b1 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
@@ -69,9 +69,9 @@ llvm::Error cc1modbuildd::readFromSocket(int FD, std::string &BufferConsumer) {
while ((n = read(FD, Buffer, MAX_BUFFER)) > 0) {
- BufferConsumer.assign(Buffer, n);
- // Read until ...\n encountered (last line of YAML document)
- if (BufferConsumer.find("...\n") != std::string::npos)
+ BufferConsumer.append(Buffer, n);
+ // Read until \n... encountered (last line of YAML document)
+ if (BufferConsumer.find("\n...") != std::string::npos)
break;
}
@@ -96,10 +96,6 @@ llvm::Error cc1modbuildd::writeToSocket(StringRef Buffer, int WriteFD) {
return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
}
- if (!BytesWritten || BytesWritten > BytesToWrite)
- return llvm::errorCodeToError(
- std::error_code(EIO, std::generic_category()));
-
BytesToWrite -= BytesWritten;
Bytes += BytesWritten;
}
diff --git a/clang/test/ModuleBuildDaemon/handshake.c b/clang/test/ModuleBuildDaemon/handshake.c
index 497b47af039f40..288e99f51bea4e 100644
--- a/clang/test/ModuleBuildDaemon/handshake.c
+++ b/clang/test/ModuleBuildDaemon/handshake.c
@@ -1,6 +1,3 @@
-// COM: Check that clang invocation can spawn and handshake with module build daemon
-// COM: Also check that clang invocation can handshake with existing module build daemon
-
// REQUIRES: !system-windows
// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
@@ -15,21 +12,12 @@ int main() {return 0;}
// RUN: %clang -fmodule-build-daemon=mbd-handshake -Rmodule-build-daemon %t/main.c &> %t/output-existing
// RUN: cat %t/output-existing | FileCheck %s --check-prefix=CHECK-EXIST
-// CHECK: remark: Trying to spawn module build daemon [-Rmodule-build-daemon]
+// COM: Check that clang invocation can spawn and handshake with module build daemon
// CHECK: remark: Successfully spawned module build daemon [-Rmodule-build-daemon]
-// CHECK: remark: Trying to connect to recently spawned module build daemon [-Rmodule-build-daemon]
-// CHECK: remark: Succesfully connected to recently spawned module build daemon [-Rmodule-build-daemon]
-// CHECK: remark: Trying to send HandshakeMsg to module build daemon [-Rmodule-build-daemon]
-// CHECK: remark: Successfully sent HandshakeMsg to module build daemon [-Rmodule-build-daemon]
-// CHECK: remark: Waiting to receive module build daemon response [-Rmodule-build-daemon]
-// CHECK: remark: Successfully received HandshakeMsg from module build daemon [-Rmodule-build-daemon]
-// CHECK: remark: Completed successfull handshake with module build daemon [-Rmodule-build-daemon]
+// CHECK: remark: Successfully connected to module build daemon at mbd-handshake/mbd.sock [-Rmodule-build-daemon]
+// CHECK: remark: Successfully completed handshake with module build daemon [-Rmodule-build-daemon]
-// CHECK-EXIST: remark: Module build daemon already exists [-Rmodule-build-daemon]
-// CHECK-EXIST: remark: Trying to send HandshakeMsg to module build daemon [-Rmodule-build-daemon]
-// CHECK-EXIST: remark: Successfully sent HandshakeMsg to module build daemon [-Rmodule-build-daemon]
-// CHECK-EXIST: remark: Waiting to receive module build daemon response [-Rmodule-build-daemon]
-// CHECK-EXIST: remark: Successfully received HandshakeMsg from module build daemon [-Rmodule-build-daemon]
-// CHECK-EXIST: remark: Completed successfull handshake with module build daemon [-Rmodule-build-daemon]
+// COM: Check that clang invocation can handshake with existing module build daemon
+// CHECK-EXIST: remark: Successfully completed handshake with module build daemon [-Rmodule-build-daemon]
// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index 75d2771c747746..a20016b89961d0 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -74,13 +74,16 @@ class ModuleBuildDaemonServer {
static void handleClient(ClientConnection Connection);
+ // TODO: modify so when shutdownDaemon is called the daemon stops accepting
+ // new client connections and waits for all existing client connections to
+ // terminate before exiting
void shutdownDaemon() {
int SocketFD = ListenSocketFD.load();
unlink(SocketPath.c_str());
shutdown(SocketFD, SHUT_RD);
close(SocketFD);
- exit(EXIT_SUCCESS);
+ _Exit(EXIT_SUCCESS);
}
private:
>From 69c8abc124810f3b4bd9b331aef8035a4719f48b Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Tue, 31 Oct 2023 21:47:00 -0400
Subject: [PATCH 06/27] Address Feedback
- Improve regression tests so that daemon is shutdown even when clang fails
- Allow <path> in [-cc1modbuildd <path>] to be optional
- Fix namespace issues
- Clean up error handling
- Replace client side posix_spawn with ExecuteNoWait
- Rebase fixes
---
.../clang/Basic/DiagnosticFrontendKinds.td | 10 +--
.../clang/Tooling/ModuleBuildDaemon/Client.h | 26 +++----
.../ModuleBuildDaemon/SocketMsgSupport.h | 46 ++++++------
.../Tooling/ModuleBuildDaemon/SocketSupport.h | 13 ++--
.../clang/Tooling/ModuleBuildDaemon/Utils.h | 8 +-
.../lib/Tooling/ModuleBuildDaemon/Client.cpp | 75 +++++++------------
.../ModuleBuildDaemon/SocketSupport.cpp | 22 ++++--
clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp | 37 +++++++--
clang/test/ModuleBuildDaemon/handshake.c | 13 ++--
clang/test/ModuleBuildDaemon/launch.c | 4 +-
clang/tools/driver/cc1_main.cpp | 5 +-
clang/tools/driver/cc1modbuildd_main.cpp | 53 +++++++------
12 files changed, 161 insertions(+), 151 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 8a69a599369063..4e6c477d7bf93d 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -262,24 +262,24 @@ def err_test_module_file_extension_version : Error<
"test module file extension '%0' has different version (%1.%2) than expected "
"(%3.%4)">;
-def err_unix_socket_addr_length :
+def err_unix_socket_addr_length :
Error<"Socket address %0 is %1 characters long which is larger then the max length of %2">,
DefaultFatal;
// Module Build Daemon
-def err_mbd_handshake :
+def err_mbd_handshake :
Error<"Attempt to hadshake with daemon has failed: %0">,
DefaultFatal;
-def err_mbd_connect :
+def err_mbd_connect :
Error<"Attempt to connect to daemon has failed: %0">,
DefaultFatal;
-def remark_mbd_spawn :
+def remark_mbd_spawn :
Remark<"Successfully spawned module build daemon">,
InGroup<ModuleBuildDaemon>;
def remark_mbd_connection :
Remark<"Successfully connected to module build daemon at %0">,
InGroup<ModuleBuildDaemon>;
-def remark_mbd_handshake :
+def remark_mbd_handshake :
Remark<"Successfully completed handshake with module build daemon">,
InGroup<ModuleBuildDaemon>;
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
index 648e9bf440b95f..045fc7ffb4f258 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
@@ -15,27 +15,21 @@
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
-using namespace clang;
-using namespace llvm;
+namespace clang::tooling::cc1modbuildd {
-namespace cc1modbuildd {
+llvm::Error attemptHandshake(int SocketFD, clang::DiagnosticsEngine &Diag);
-// Returns where to store log files and socket address. Of the format
-// /tmp/clang-<BLAKE3HashOfClangFullVersion>/
-std::string getBasePath();
+llvm::Error spawnModuleBuildDaemon(llvm::StringRef BasePath, const char *Argv0,
+ clang::DiagnosticsEngine &Diag);
-llvm::Error attemptHandshake(int SocketFD, DiagnosticsEngine &Diag);
+llvm::Expected<int> getModuleBuildDaemon(const char *Argv0,
+ llvm::StringRef BasePath,
+ clang::DiagnosticsEngine &Diag);
-llvm::Error spawnModuleBuildDaemon(StringRef BasePath, const char *Argv0,
- DiagnosticsEngine &Diag);
-
-Expected<int> getModuleBuildDaemon(const char *Argv0, StringRef BasePath,
- DiagnosticsEngine &Diag);
-
-void spawnModuleBuildDaemonAndHandshake(const CompilerInvocation &Clang,
- DiagnosticsEngine &Diag,
+void spawnModuleBuildDaemonAndHandshake(const clang::CompilerInvocation &Clang,
+ clang::DiagnosticsEngine &Diag,
const char *Argv0);
-} // namespace cc1modbuildd
+} // namespace clang::tooling::cc1modbuildd
#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_CLIENT_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
index 280a528131412b..b8d6717b02cb5a 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
@@ -12,10 +12,7 @@
#include "clang/Tooling/ModuleBuildDaemon/Client.h"
#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
-using namespace clang;
-using namespace llvm;
-
-namespace cc1modbuildd {
+namespace clang::tooling::cc1modbuildd {
enum class ActionType { HANDSHAKE };
enum class StatusType { REQUEST, SUCCESS, FAILURE };
@@ -47,7 +44,8 @@ template <typename T> std::string getBufferFromSocketMsg(T Msg) {
return Buffer;
}
-template <typename T> Expected<T> getSocketMsgFromBuffer(StringRef Buffer) {
+template <typename T>
+llvm::Expected<T> getSocketMsgFromBuffer(llvm::StringRef Buffer) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
@@ -58,13 +56,14 @@ template <typename T> Expected<T> getSocketMsgFromBuffer(StringRef Buffer) {
// YamlIn.error() dumps an error message if there is one
if (YamlIn.error()) {
std::string Msg = "Syntax or semantic error during YAML parsing";
- return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
+ return llvm::make_error<llvm::StringError>(Msg,
+ llvm::inconvertibleErrorCode());
}
return ClientRequest;
}
-template <typename T> Expected<T> readSocketMsgFromSocket(int FD) {
+template <typename T> llvm::Expected<T> readSocketMsgFromSocket(int FD) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
@@ -73,7 +72,7 @@ template <typename T> Expected<T> readSocketMsgFromSocket(int FD) {
return std::move(ReadErr);
// Wait for response from module build daemon
- Expected<T> MaybeResponse =
+ llvm::Expected<T> MaybeResponse =
getSocketMsgFromBuffer<T>(std::move(BufferConsumer).c_str());
if (!MaybeResponse)
return std::move(MaybeResponse.takeError());
@@ -92,11 +91,12 @@ template <typename T> llvm::Error writeSocketMsgToSocket(T Msg, int FD) {
}
template <typename T>
-Expected<int> connectAndWriteSocketMsgToSocket(T Msg, StringRef SocketPath) {
+llvm::Expected<int>
+connectAndWriteSocketMsgToSocket(T Msg, llvm::StringRef SocketPath) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
- Expected<int> MaybeFD = connectToSocket(SocketPath);
+ llvm::Expected<int> MaybeFD = connectToSocket(SocketPath);
if (!MaybeFD)
return std::move(MaybeFD.takeError());
int FD = std::move(*MaybeFD);
@@ -107,26 +107,26 @@ Expected<int> connectAndWriteSocketMsgToSocket(T Msg, StringRef SocketPath) {
return FD;
}
-} // namespace cc1modbuildd
+} // namespace clang::tooling::cc1modbuildd
+
+namespace cc1mod = clang::tooling::cc1modbuildd;
-template <>
-struct llvm::yaml::ScalarEnumerationTraits<cc1modbuildd::StatusType> {
- static void enumeration(IO &Io, cc1modbuildd::StatusType &Value) {
- Io.enumCase(Value, "REQUEST", cc1modbuildd::StatusType::REQUEST);
- Io.enumCase(Value, "SUCCESS", cc1modbuildd::StatusType::SUCCESS);
- Io.enumCase(Value, "FAILURE", cc1modbuildd::StatusType::FAILURE);
+template <> struct llvm::yaml::ScalarEnumerationTraits<cc1mod::StatusType> {
+ static void enumeration(IO &Io, cc1mod::StatusType &Value) {
+ Io.enumCase(Value, "REQUEST", cc1mod::StatusType::REQUEST);
+ Io.enumCase(Value, "SUCCESS", cc1mod::StatusType::SUCCESS);
+ Io.enumCase(Value, "FAILURE", cc1mod::StatusType::FAILURE);
}
};
-template <>
-struct llvm::yaml::ScalarEnumerationTraits<cc1modbuildd::ActionType> {
- static void enumeration(IO &Io, cc1modbuildd::ActionType &Value) {
- Io.enumCase(Value, "HANDSHAKE", cc1modbuildd::ActionType::HANDSHAKE);
+template <> struct llvm::yaml::ScalarEnumerationTraits<cc1mod::ActionType> {
+ static void enumeration(IO &Io, cc1mod::ActionType &Value) {
+ Io.enumCase(Value, "HANDSHAKE", cc1mod::ActionType::HANDSHAKE);
}
};
-template <> struct llvm::yaml::MappingTraits<cc1modbuildd::HandshakeMsg> {
- static void mapping(IO &Io, cc1modbuildd::HandshakeMsg &Info) {
+template <> struct llvm::yaml::MappingTraits<cc1mod::HandshakeMsg> {
+ static void mapping(IO &Io, cc1mod::HandshakeMsg &Info) {
Io.mapRequired("Action", Info.MsgAction);
Io.mapRequired("Status", Info.MsgStatus);
}
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
index 712f65e695086b..55a09c142a6543 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
@@ -15,16 +15,13 @@
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
-using namespace clang;
-using namespace llvm;
+namespace clang::tooling::cc1modbuildd {
-namespace cc1modbuildd {
-
-Expected<int> createSocket();
-Expected<int> connectToSocket(StringRef SocketPath);
+llvm::Expected<int> createSocket();
+llvm::Expected<int> connectToSocket(llvm::StringRef SocketPath);
llvm::Error readFromSocket(int FD, std::string &BufferConsumer);
-llvm::Error writeToSocket(StringRef Buffer, int WriteFD);
+llvm::Error writeToSocket(llvm::StringRef Buffer, int WriteFD);
-} // namespace cc1modbuildd
+} // namespace clang::tooling::cc1modbuildd
#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
index 63ac2bf54c6ba8..4dfde73a1143fe 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
@@ -22,12 +22,16 @@
#define STDOUT_FILE_NAME "mbd.out"
#define STDERR_FILE_NAME "mbd.err"
-namespace cc1modbuildd {
+namespace clang::tooling::cc1modbuildd {
void writeError(llvm::Error Err, std::string Msg);
std::string getFullErrorMsg(llvm::Error Err, std::string Msg);
llvm::Error makeStringError(llvm::Error Err, std::string Msg);
-} // namespace cc1modbuildd
+// Get a temprary location where the daemon can store log files and a socket
+// address. Of the format /tmp/clang-<BLAKE3HashOfClangFullVersion>/
+std::string getBasePath();
+
+} // namespace clang::tooling::cc1modbuildd
#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
index b2a444dd362f59..f57dd6b2e367c5 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
@@ -18,6 +18,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/BLAKE3.h"
+#include "llvm/Support/Program.h"
// TODO: Make portable
#if LLVM_ON_UNIX
@@ -25,6 +26,7 @@
#include <cerrno>
#include <filesystem>
#include <fstream>
+#include <optional>
#include <signal.h>
#include <spawn.h>
#include <string>
@@ -33,28 +35,11 @@
#include <sys/un.h>
#include <unistd.h>
-using namespace clang;
using namespace llvm;
-using namespace cc1modbuildd;
-
-std::string cc1modbuildd::getBasePath() {
- llvm::BLAKE3 Hash;
- Hash.update(getClangFullVersion());
- auto HashResult = Hash.final<sizeof(uint64_t)>();
- uint64_t HashValue =
- llvm::support::endian::read<uint64_t, llvm::support::native>(
- HashResult.data());
- std::string Key = toString(llvm::APInt(64, HashValue), 36, /*Signed*/ false);
-
- // Set paths
- SmallString<128> BasePath;
- llvm::sys::path::system_temp_directory(/*erasedOnReboot*/ true, BasePath);
- llvm::sys::path::append(BasePath, "clang-" + Key);
- return BasePath.c_str();
-}
-llvm::Error cc1modbuildd::attemptHandshake(int SocketFD,
- DiagnosticsEngine &Diag) {
+namespace clang::tooling::cc1modbuildd {
+
+llvm::Error attemptHandshake(int SocketFD, DiagnosticsEngine &Diag) {
HandshakeMsg Request{ActionType::HANDSHAKE, StatusType::REQUEST};
std::string Buffer = getBufferFromSocketMsg(Request);
@@ -82,29 +67,25 @@ llvm::Error cc1modbuildd::attemptHandshake(int SocketFD,
inconvertibleErrorCode());
}
-llvm::Error cc1modbuildd::spawnModuleBuildDaemon(StringRef BasePath,
- const char *Argv0,
- DiagnosticsEngine &Diag) {
- std::string BasePathStr = BasePath.str();
- const char *Args[] = {Argv0, "-cc1modbuildd", BasePathStr.c_str(), nullptr};
- pid_t pid;
-
- // TODO: Swich to llvm::sys::ExecuteNoWait
- int EC = posix_spawn(&pid, Args[0],
- /*file_actions*/ nullptr,
- /*spawnattr*/ nullptr, const_cast<char **>(Args),
- /*envp*/ nullptr);
- if (EC)
- return createStringError(std::error_code(EC, std::generic_category()),
- "Failed to spawn module build daemon");
+llvm::Error spawnModuleBuildDaemon(StringRef BasePath, const char *Argv0,
+ DiagnosticsEngine &Diag) {
+
+ std::vector<StringRef> Args = {Argv0, "-cc1modbuildd", BasePath.str()};
+
+ std::string ErrorBuffer;
+ // Will wait until module build daemon has forked and parent process. There
+ // is extra work that needs to be done for Windows when using ExecuteNoWait
+ llvm::sys::ExecuteAndWait(Argv0, Args, std::nullopt, {}, 0, 0, &ErrorBuffer);
+
+ if (!ErrorBuffer.empty())
+ return llvm::make_error<StringError>(ErrorBuffer, inconvertibleErrorCode());
Diag.Report(diag::remark_mbd_spawn);
return llvm::Error::success();
}
-Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
- StringRef BasePath,
- DiagnosticsEngine &Diag) {
+Expected<int> getModuleBuildDaemon(const char *Argv0, StringRef BasePath,
+ DiagnosticsEngine &Diag) {
SmallString<128> SocketPath = BasePath;
llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME);
@@ -117,8 +98,7 @@ Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
consumeError(MaybeFD.takeError());
}
- if (llvm::Error Err =
- cc1modbuildd::spawnModuleBuildDaemon(BasePath, Argv0, Diag))
+ if (llvm::Error Err = spawnModuleBuildDaemon(BasePath, Argv0, Diag))
return std::move(Err);
const unsigned int MICROSEC_IN_SEC = 1000000;
@@ -150,18 +130,18 @@ Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
"Module build daemon could not be spawned", inconvertibleErrorCode());
}
-void cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
- const CompilerInvocation &Clang, DiagnosticsEngine &Diag,
- const char *Argv0) {
+void spawnModuleBuildDaemonAndHandshake(const CompilerInvocation &Clang,
+ DiagnosticsEngine &Diag,
+ const char *Argv0) {
// The module build daemon stores all output files and its socket address
// under BasePath. Either set BasePath to a user provided option or create an
- // appropriate BasePath based on the hash of the full clang version
+ // appropriate BasePath based on the BLAKE3 hash of the full clang version
std::string BasePath;
if (!Clang.getFrontendOpts().ModuleBuildDaemonPath.empty())
BasePath = Clang.getFrontendOpts().ModuleBuildDaemonPath;
else
- BasePath = cc1modbuildd::getBasePath();
+ BasePath = getBasePath();
// TODO: Max length may vary across different platforms. Incoming llvm/Support
// for sockets will help make this portable. On most unix platforms a socket
@@ -175,8 +155,7 @@ void cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
}
// If module build daemon does not exist spawn module build daemon
- Expected<int> MaybeDaemonFD =
- cc1modbuildd::getModuleBuildDaemon(Argv0, BasePath, Diag);
+ Expected<int> MaybeDaemonFD = getModuleBuildDaemon(Argv0, BasePath, Diag);
if (!MaybeDaemonFD) {
Diag.Report(diag::err_mbd_connect) << MaybeDaemonFD.takeError();
return;
@@ -192,4 +171,6 @@ void cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
return;
}
+} // namespace clang::tooling::cc1modbuildd
+
#endif // LLVM_ON_UNIX
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
index a2997aa11756b1..1ac7acefe1c247 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
@@ -32,18 +32,22 @@
#include <sys/un.h>
#include <unistd.h>
-Expected<int> cc1modbuildd::createSocket() {
+using namespace llvm;
+
+namespace clang::tooling::cc1modbuildd {
+
+Expected<int> createSocket() {
int FD;
if ((FD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
std::string Msg = "socket create error: " + std::string(strerror(errno));
- return createStringError(inconvertibleErrorCode(), Msg);
+ return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
}
return FD;
}
-Expected<int> cc1modbuildd::connectToSocket(StringRef SocketPath) {
+Expected<int> connectToSocket(StringRef SocketPath) {
- Expected<int> MaybeFD = cc1modbuildd::createSocket();
+ Expected<int> MaybeFD = createSocket();
if (!MaybeFD)
return std::move(MaybeFD.takeError());
@@ -56,13 +60,13 @@ Expected<int> cc1modbuildd::connectToSocket(StringRef SocketPath) {
if (connect(FD, (struct sockaddr *)&Addr, sizeof(Addr)) == -1) {
close(FD);
- std::string msg = "socket connect error: " + std::string(strerror(errno));
- return createStringError(inconvertibleErrorCode(), msg);
+ std::string Msg = "socket connect error: " + std::string(strerror(errno));
+ return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
}
return FD;
}
-llvm::Error cc1modbuildd::readFromSocket(int FD, std::string &BufferConsumer) {
+llvm::Error readFromSocket(int FD, std::string &BufferConsumer) {
char Buffer[MAX_BUFFER];
ssize_t n;
@@ -84,7 +88,7 @@ llvm::Error cc1modbuildd::readFromSocket(int FD, std::string &BufferConsumer) {
return llvm::Error::success();
}
-llvm::Error cc1modbuildd::writeToSocket(StringRef Buffer, int WriteFD) {
+llvm::Error writeToSocket(StringRef Buffer, int WriteFD) {
ssize_t BytesToWrite = static_cast<ssize_t>(Buffer.size());
const char *Bytes = Buffer.data();
@@ -102,4 +106,6 @@ llvm::Error cc1modbuildd::writeToSocket(StringRef Buffer, int WriteFD) {
return llvm::Error::success();
}
+} // namespace clang::tooling::cc1modbuildd
+
#endif // LLVM_ON_UNIX
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
index 0e291987faf93f..dcef183a06c078 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
@@ -6,19 +6,28 @@
//
//===----------------------------------------------------------------------===//
-#include <clang/Tooling/ModuleBuildDaemon/Utils.h>
-#include <llvm/Support/Error.h>
+#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
+#include "clang/Basic/Version.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/BLAKE3.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Path.h"
+
#include <string>
using namespace llvm;
-void cc1modbuildd::writeError(llvm::Error Err, std::string Msg) {
+namespace clang::tooling::cc1modbuildd {
+
+void writeError(llvm::Error Err, std::string Msg) {
handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
errs() << Msg << EIB.message() << '\n';
});
}
-std::string cc1modbuildd::getFullErrorMsg(llvm::Error Err, std::string Msg) {
+std::string getFullErrorMsg(llvm::Error Err, std::string Msg) {
std::string ErrMessage;
handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
ErrMessage = Msg + EIB.message();
@@ -26,7 +35,25 @@ std::string cc1modbuildd::getFullErrorMsg(llvm::Error Err, std::string Msg) {
return ErrMessage;
}
-llvm::Error cc1modbuildd::makeStringError(llvm::Error Err, std::string Msg) {
+llvm::Error makeStringError(llvm::Error Err, std::string Msg) {
std::string ErrMsg = getFullErrorMsg(std::move(Err), Msg);
return llvm::make_error<StringError>(ErrMsg, inconvertibleErrorCode());
}
+
+std::string getBasePath() {
+ llvm::BLAKE3 Hash;
+ Hash.update(clang::getClangFullVersion());
+ auto HashResult = Hash.final<sizeof(uint64_t)>();
+ uint64_t HashValue =
+ llvm::support::endian::read<uint64_t, llvm::endianness::native>(
+ HashResult.data());
+ std::string Key = toString(llvm::APInt(64, HashValue), 36, /*Signed*/ false);
+
+ // Set paths
+ SmallString<128> BasePath;
+ llvm::sys::path::system_temp_directory(/*erasedOnReboot*/ true, BasePath);
+ llvm::sys::path::append(BasePath, "clang-" + Key);
+ return BasePath.c_str();
+}
+
+} // namespace clang::tooling::cc1modbuildd
\ No newline at end of file
diff --git a/clang/test/ModuleBuildDaemon/handshake.c b/clang/test/ModuleBuildDaemon/handshake.c
index 288e99f51bea4e..976cac47c1bcef 100644
--- a/clang/test/ModuleBuildDaemon/handshake.c
+++ b/clang/test/ModuleBuildDaemon/handshake.c
@@ -7,17 +7,18 @@
//--- main.c
int main() {return 0;}
-// RUN: %clang -fmodule-build-daemon=mbd-handshake -Rmodule-build-daemon %t/main.c &> %t/output-new
+// Add '|| true' to ensure RUN command never fails so that daemon shutdown command is always run
+// RUN: %clang -fmodule-build-daemon=mbd-handshake -Rmodule-build-daemon %t/main.c &> %t/output-new || true
+// RUN: %clang -fmodule-build-daemon=mbd-handshake -Rmodule-build-daemon %t/main.c &> %t/output-existing || true
+// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
+
// RUN: cat %t/output-new | FileCheck %s
-// RUN: %clang -fmodule-build-daemon=mbd-handshake -Rmodule-build-daemon %t/main.c &> %t/output-existing
// RUN: cat %t/output-existing | FileCheck %s --check-prefix=CHECK-EXIST
-// COM: Check that clang invocation can spawn and handshake with module build daemon
+// Check that a clang invocation can spawn and handshake with a module build daemon
// CHECK: remark: Successfully spawned module build daemon [-Rmodule-build-daemon]
// CHECK: remark: Successfully connected to module build daemon at mbd-handshake/mbd.sock [-Rmodule-build-daemon]
// CHECK: remark: Successfully completed handshake with module build daemon [-Rmodule-build-daemon]
-// COM: Check that clang invocation can handshake with existing module build daemon
+// Check that a clang invocation can handshake with an existing module build daemon
// CHECK-EXIST: remark: Successfully completed handshake with module build daemon [-Rmodule-build-daemon]
-
-// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
diff --git a/clang/test/ModuleBuildDaemon/launch.c b/clang/test/ModuleBuildDaemon/launch.c
index cb4cdbc2b32af9..d1bd8af7f244fa 100644
--- a/clang/test/ModuleBuildDaemon/launch.c
+++ b/clang/test/ModuleBuildDaemon/launch.c
@@ -1,4 +1,4 @@
-// COM: Check that module build daemon can create unix socket
+// Check that the module build daemon can create a unix socket
// REQUIRES: !system-windows
@@ -8,6 +8,6 @@
// RUN: %clang -cc1modbuildd mbd-launch -v
// RUN: cat mbd-launch/mbd.out | FileCheck %s
-// CHECK: mbd created and binded to socket address at: mbd-launch/mbd.sock
+// CHECK: mbd created and binded to socket at: mbd-launch/mbd.sock
// RUN: if pgrep -f "cc1modbuildd mbd-launch"; then pkill -f "cc1modbuildd mbd-launch"; fi
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index 2bf4f95c34b46d..b0c044feaa938f 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -292,10 +292,11 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
// Handle module build daemon functionality if enabled
if (Clang->getFrontendOpts().ModuleBuildDaemon) {
#if LLVM_ON_UNIX
- cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
+ clang::tooling::cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
Clang->getInvocation(), Clang->getDiagnostics(), Argv0);
#else
- errs() << "-fmodule-build-daemon not supported by current platform" << '\n';
+ llvm::errs() << "-fmodule-build-daemon not supported by current platform"
+ << '\n';
return 1;
#endif
}
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index a20016b89961d0..bfe636b2587311 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -36,8 +36,7 @@
#include <unordered_map>
using namespace llvm;
-using namespace clang;
-using namespace cc1modbuildd;
+using namespace clang::tooling::cc1modbuildd;
// Create unbuffered STDOUT stream so that any logging done by module build
// daemon can be viewed without having to terminate the process
@@ -76,7 +75,7 @@ class ModuleBuildDaemonServer {
// TODO: modify so when shutdownDaemon is called the daemon stops accepting
// new client connections and waits for all existing client connections to
- // terminate before exiting
+ // terminate before closing the file descriptor and exiting
void shutdownDaemon() {
int SocketFD = ListenSocketFD.load();
@@ -174,7 +173,7 @@ int ModuleBuildDaemonServer::createDaemonSocket() {
std::perror("Socket bind error: ");
exit(EXIT_FAILURE);
}
- printVerboseLog("mbd created and binded to socket address at: " + SocketPath);
+ printVerboseLog("mbd created and binded to socket at: " + SocketPath);
// set socket to accept incoming connection request
unsigned MaxBacklog = llvm::hardware_concurrency().compute_thread_count();
@@ -251,34 +250,37 @@ int ModuleBuildDaemonServer::listenForClients() {
// Module build daemon is spawned with the following command line:
//
-// clang -cc1modbuildd <path> -v
+// clang [-cc1modbuildd <path>] [-v]
//
-// <path> defines the location of all files created by the module build daemon
-// and should follow the format /path/to/dir. For example, `clang
-// -cc1modbuildd /tmp/` creates a socket file at `/tmp/mbd.sock`
+// OPTIONS
+// -cc1modbuildd <path>
+// Specifies the path to all the files created by the module build daemon.
+// The <path> should immediately follow the -cc1modbuildd option.
//
-// When a module build daemon is spawned by a cc1 invocations, <path> follows
-// the format /tmp/clang-<BLAKE3HashOfClangFullVersion> and looks something like
-// /tmp/clang-3NXKISKJ0WJTN
+// -v
+// Provides verbose debug information.
//
-// -v is optional and provides berbose debug information
+// NOTES
+// The arguments <path> and -v are optional. By default <path> follows the
+// format: /tmp/clang-<BLAKE3HashOfClangFullVersion>.
//
int cc1modbuildd_main(ArrayRef<const char *> Argv) {
- if (Argv.size() < 1) {
- errs() << "spawning a module build daemon requies a command line format of "
- "`clang -cc1modbuildd <path>`. <path> defines where the module "
- "build daemon will create mbd.out, mbd.err, mbd.sock"
- << '\n';
- return 1;
- }
+ std::string BasePath;
+ if (!Argv.empty()) {
- // Where to store log files and socket address
- // TODO: Add check to confirm BasePath is directory
- std::string BasePath(Argv[0]);
+ if (find(Argv, StringRef("-v")) != Argv.end())
+ VerboseLog = true;
- // On most unix platforms a socket address cannot be over 108 characters
- int MAX_ADDR = 108;
+ if (strcmp(Argv[0], "-v") != 0)
+ BasePath = Argv[0];
+ } else
+ BasePath = getBasePath();
+
+ // TODO: Make portable. On most unix platforms a socket address cannot be over
+ // 108 characters but that may not always be the case. Functionality will be
+ // provided by llvm/Support/Sockets once that is implemented
+ const int MAX_ADDR = 108;
if (BasePath.length() >= MAX_ADDR - std::string(SOCKET_FILE_NAME).length()) {
errs() << "Provided socket path" + BasePath +
" is too long. Socket path much be equal to or less then 100 "
@@ -292,9 +294,6 @@ int cc1modbuildd_main(ArrayRef<const char *> Argv) {
// Used to handle signals
DaemonPtr = &Daemon;
- if (find(Argv, StringRef("-v")) != Argv.end())
- VerboseLog = true;
-
Daemon.forkDaemon();
Daemon.createDaemonSocket();
Daemon.listenForClients();
>From 470e8509b018cd7437fa653db7837881675abcf1 Mon Sep 17 00:00:00 2001
From: Connor Sughrue <cpsughrue at gmail.com>
Date: Sun, 26 Nov 2023 21:13:15 -0500
Subject: [PATCH 07/27] Improve portability by removing MBD related guards in
driver.cpp and cc1_main.cpp
---
clang/tools/driver/cc1_main.cpp | 6 ---
clang/tools/driver/cc1modbuildd_main.cpp | 57 ++++++++++++++----------
clang/tools/driver/driver.cpp | 5 ---
3 files changed, 34 insertions(+), 34 deletions(-)
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index b0c044feaa938f..1161ffa6e5a602 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -291,14 +291,8 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
// Handle module build daemon functionality if enabled
if (Clang->getFrontendOpts().ModuleBuildDaemon) {
-#if LLVM_ON_UNIX
clang::tooling::cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
Clang->getInvocation(), Clang->getDiagnostics(), Argv0);
-#else
- llvm::errs() << "-fmodule-build-daemon not supported by current platform"
- << '\n';
- return 1;
-#endif
}
// Execute the frontend actions.
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index bfe636b2587311..ea441bdfbfb042 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -17,41 +17,50 @@
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
-// TODO: Make portable
-#if LLVM_ON_UNIX
+using namespace llvm;
+using namespace clang::tooling::cc1modbuildd;
#include <errno.h>
#include <fstream>
#include <mutex>
#include <optional>
-#include <signal.h>
#include <sstream>
#include <stdbool.h>
#include <string>
+#include <type_traits>
+#include <unordered_map>
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
-#include <type_traits>
#include <unistd.h>
-#include <unordered_map>
-
-using namespace llvm;
-using namespace clang::tooling::cc1modbuildd;
+#endif
// Create unbuffered STDOUT stream so that any logging done by module build
// daemon can be viewed without having to terminate the process
static raw_fd_ostream &unbuff_outs() {
- static raw_fd_ostream S(STDOUT_FILENO, false, true);
+ static raw_fd_ostream S(fileno(stdout), false, true);
return S;
}
-namespace {
+static bool VerboseLog = false;
+static void printVerboseLog(const llvm::Twine &message) {
+ if (VerboseLog) {
+ unbuff_outs() << message << '\n';
+ }
+}
struct ClientConnection {
int ClientFD;
std::string Buffer;
};
+namespace {
+
class ModuleBuildDaemonServer {
public:
SmallString<256> SocketPath;
@@ -77,16 +86,21 @@ class ModuleBuildDaemonServer {
// new client connections and waits for all existing client connections to
// terminate before closing the file descriptor and exiting
void shutdownDaemon() {
+#ifdef _WIN32
+ // TODO: implement windows version
+#else
int SocketFD = ListenSocketFD.load();
-
unlink(SocketPath.c_str());
shutdown(SocketFD, SHUT_RD);
close(SocketFD);
_Exit(EXIT_SUCCESS);
+#endif
}
private:
+#ifndef _WIN32
pid_t Pid = -1;
+#endif
std::atomic<int> ListenSocketFD = -1;
};
@@ -99,13 +113,7 @@ void handleSignal(int Signal) {
}
} // namespace
-static bool VerboseLog = false;
-static void printVerboseLog(const llvm::Twine &message) {
- if (VerboseLog) {
- unbuff_outs() << message << '\n';
- }
-}
-
+#ifndef _WIN32
// Forks and detaches process, creating module build daemon
int ModuleBuildDaemonServer::forkDaemon() {
@@ -143,6 +151,7 @@ int ModuleBuildDaemonServer::forkDaemon() {
return EXIT_SUCCESS;
}
+// TODO: Make portable
// Creates unix socket for IPC with module build daemon
int ModuleBuildDaemonServer::createDaemonSocket() {
@@ -247,15 +256,16 @@ int ModuleBuildDaemonServer::listenForClients() {
}
return 0;
}
+#endif // LLVM_ON_UNIX
// Module build daemon is spawned with the following command line:
//
-// clang [-cc1modbuildd <path>] [-v]
+// clang -cc1modbuildd [<path>] [-v]
//
// OPTIONS
-// -cc1modbuildd <path>
+// <path>
// Specifies the path to all the files created by the module build daemon.
-// The <path> should immediately follow the -cc1modbuildd option.
+// If provided, <path> should immediately follow -cc1modbuildd.
//
// -v
// Provides verbose debug information.
@@ -264,6 +274,7 @@ int ModuleBuildDaemonServer::listenForClients() {
// The arguments <path> and -v are optional. By default <path> follows the
// format: /tmp/clang-<BLAKE3HashOfClangFullVersion>.
//
+
int cc1modbuildd_main(ArrayRef<const char *> Argv) {
std::string BasePath;
@@ -294,11 +305,11 @@ int cc1modbuildd_main(ArrayRef<const char *> Argv) {
// Used to handle signals
DaemonPtr = &Daemon;
+#ifndef _WIN32
Daemon.forkDaemon();
Daemon.createDaemonSocket();
Daemon.listenForClients();
+#endif
return 0;
}
-
-#endif // LLVM_ON_UNIX
diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp
index 3098084ea6005f..7413eb5aa9c18b 100644
--- a/clang/tools/driver/driver.cpp
+++ b/clang/tools/driver/driver.cpp
@@ -372,12 +372,7 @@ static int ExecuteCC1Tool(SmallVectorImpl<const char *> &ArgV,
return cc1gen_reproducer_main(ArrayRef(ArgV).slice(2), ArgV[0],
GetExecutablePathVP, ToolContext);
if (Tool == "-cc1modbuildd") {
-#if LLVM_ON_UNIX
return cc1modbuildd_main(ArrayRef(ArgV).slice(2));
-#else
- llvm::errs() << "-cc1modbuildd not supported by current platform" << '\n';
- return 1;
-#endif
}
// Reject unknown tools
>From 24b405917f5ad8e8d96e6449caea63f44a3a0626 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Wed, 6 Dec 2023 12:21:01 -0500
Subject: [PATCH 08/27] Add ability to detach process when using
llvm::sys::Execute
---
llvm/include/llvm/Support/Program.h | 3 ++-
llvm/lib/Support/Program.cpp | 9 +++++----
llvm/lib/Support/Unix/Program.inc | 18 ++++++++++++++----
llvm/lib/Support/Windows/Program.inc | 7 +++++--
4 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/llvm/include/llvm/Support/Program.h b/llvm/include/llvm/Support/Program.h
index 4c1133e44a21c9..a6a897704f2081 100644
--- a/llvm/include/llvm/Support/Program.h
+++ b/llvm/include/llvm/Support/Program.h
@@ -152,7 +152,8 @@ namespace sys {
unsigned MemoryLimit = 0,
std::string *ErrMsg = nullptr,
bool *ExecutionFailed = nullptr,
- BitVector *AffinityMask = nullptr);
+ BitVector *AffinityMask = nullptr,
+ bool DetachProcess = false);
/// Return true if the given arguments fit within system-specific
/// argument length limits.
diff --git a/llvm/lib/Support/Program.cpp b/llvm/lib/Support/Program.cpp
index 1dcd45e2d69e89..642f6e73f32a77 100644
--- a/llvm/lib/Support/Program.cpp
+++ b/llvm/lib/Support/Program.cpp
@@ -27,7 +27,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program,
std::optional<ArrayRef<StringRef>> Env,
ArrayRef<std::optional<StringRef>> Redirects,
unsigned MemoryLimit, std::string *ErrMsg,
- BitVector *AffinityMask);
+ BitVector *AffinityMask, bool DetachProcess);
int sys::ExecuteAndWait(StringRef Program, ArrayRef<StringRef> Args,
std::optional<ArrayRef<StringRef>> Env,
@@ -39,7 +39,7 @@ int sys::ExecuteAndWait(StringRef Program, ArrayRef<StringRef> Args,
assert(Redirects.empty() || Redirects.size() == 3);
ProcessInfo PI;
if (Execute(PI, Program, Args, Env, Redirects, MemoryLimit, ErrMsg,
- AffinityMask)) {
+ AffinityMask, /*DetachProcess*/ false)) {
if (ExecutionFailed)
*ExecutionFailed = false;
ProcessInfo Result = Wait(
@@ -58,13 +58,14 @@ ProcessInfo sys::ExecuteNoWait(StringRef Program, ArrayRef<StringRef> Args,
std::optional<ArrayRef<StringRef>> Env,
ArrayRef<std::optional<StringRef>> Redirects,
unsigned MemoryLimit, std::string *ErrMsg,
- bool *ExecutionFailed, BitVector *AffinityMask) {
+ bool *ExecutionFailed, BitVector *AffinityMask,
+ bool DetachProcess) {
assert(Redirects.empty() || Redirects.size() == 3);
ProcessInfo PI;
if (ExecutionFailed)
*ExecutionFailed = false;
if (!Execute(PI, Program, Args, Env, Redirects, MemoryLimit, ErrMsg,
- AffinityMask))
+ AffinityMask, DetachProcess))
if (ExecutionFailed)
*ExecutionFailed = true;
diff --git a/llvm/lib/Support/Unix/Program.inc b/llvm/lib/Support/Unix/Program.inc
index 5d9757bcc51b3e..56f653a165bfb5 100644
--- a/llvm/lib/Support/Unix/Program.inc
+++ b/llvm/lib/Support/Unix/Program.inc
@@ -173,10 +173,11 @@ toNullTerminatedCStringArray(ArrayRef<StringRef> Strings, StringSaver &Saver) {
}
static bool Execute(ProcessInfo &PI, StringRef Program,
- ArrayRef<StringRef> Args, std::optional<ArrayRef<StringRef>> Env,
+ ArrayRef<StringRef> Args,
+ std::optional<ArrayRef<StringRef>> Env,
ArrayRef<std::optional<StringRef>> Redirects,
unsigned MemoryLimit, std::string *ErrMsg,
- BitVector *AffinityMask) {
+ BitVector *AffinityMask, bool DetachProcess) {
if (!llvm::sys::fs::exists(Program)) {
if (ErrMsg)
*ErrMsg = std::string("Executable \"") + Program.str() +
@@ -202,7 +203,8 @@ static bool Execute(ProcessInfo &PI, StringRef Program,
// If this OS has posix_spawn and there is no memory limit being implied, use
// posix_spawn. It is more efficient than fork/exec.
#ifdef HAVE_POSIX_SPAWN
- if (MemoryLimit == 0) {
+ // Cannot use posix_spawn if you would like to detach the process
+ if (MemoryLimit == 0 && !DetachProcess) {
posix_spawn_file_actions_t FileActionsStore;
posix_spawn_file_actions_t *FileActions = nullptr;
@@ -270,7 +272,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program,
return true;
}
-#endif
+#endif // HAVE_POSIX_SPAWN
// Create a child process.
int child = fork();
@@ -307,6 +309,14 @@ static bool Execute(ProcessInfo &PI, StringRef Program,
}
}
+ if (DetachProcess) {
+ // Detach from controlling terminal
+ if (setsid() == -1) {
+ MakeErrMsg(ErrMsg, "Couldn't detach process, setsid() failed");
+ return false;
+ }
+ }
+
// Set memory limits
if (MemoryLimit != 0) {
SetMemoryLimits(MemoryLimit);
diff --git a/llvm/lib/Support/Windows/Program.inc b/llvm/lib/Support/Windows/Program.inc
index 0de9d3f7564481..d98d55f317a35a 100644
--- a/llvm/lib/Support/Windows/Program.inc
+++ b/llvm/lib/Support/Windows/Program.inc
@@ -172,10 +172,11 @@ static HANDLE RedirectIO(std::optional<StringRef> Path, int fd,
} // namespace llvm
static bool Execute(ProcessInfo &PI, StringRef Program,
- ArrayRef<StringRef> Args, std::optional<ArrayRef<StringRef>> Env,
+ ArrayRef<StringRef> Args,
+ std::optional<ArrayRef<StringRef>> Env,
ArrayRef<std::optional<StringRef>> Redirects,
unsigned MemoryLimit, std::string *ErrMsg,
- BitVector *AffinityMask) {
+ BitVector *AffinityMask, bool DetachProcess) {
if (!sys::fs::can_execute(Program)) {
if (ErrMsg)
*ErrMsg = "program not executable";
@@ -284,6 +285,8 @@ static bool Execute(ProcessInfo &PI, StringRef Program,
unsigned CreateFlags = CREATE_UNICODE_ENVIRONMENT;
if (AffinityMask)
CreateFlags |= CREATE_SUSPENDED;
+ if (DetachProcess)
+ CreateFlags |= DETACHED_PROCESS;
std::vector<wchar_t> CommandUtf16(Command.size() + 1, 0);
std::copy(Command.begin(), Command.end(), CommandUtf16.begin());
>From d1e6e88432c75ca0b636b02684b9fd7a4363d65c Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Thu, 7 Dec 2023 00:38:43 -0500
Subject: [PATCH 09/27] Now that ExecuteNoWait can detach the spawned process
update forkDaemon and spawnModuleBuildDaemon
---
.../lib/Tooling/ModuleBuildDaemon/Client.cpp | 6 ++---
clang/test/ModuleBuildDaemon/launch.c | 8 +++---
clang/tools/driver/cc1modbuildd_main.cpp | 25 +++----------------
3 files changed, 8 insertions(+), 31 deletions(-)
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
index f57dd6b2e367c5..be26049cce84a0 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
@@ -71,11 +71,9 @@ llvm::Error spawnModuleBuildDaemon(StringRef BasePath, const char *Argv0,
DiagnosticsEngine &Diag) {
std::vector<StringRef> Args = {Argv0, "-cc1modbuildd", BasePath.str()};
-
std::string ErrorBuffer;
- // Will wait until module build daemon has forked and parent process. There
- // is extra work that needs to be done for Windows when using ExecuteNoWait
- llvm::sys::ExecuteAndWait(Argv0, Args, std::nullopt, {}, 0, 0, &ErrorBuffer);
+ llvm::sys::ExecuteNoWait(Argv0, Args, std::nullopt, {}, 0, &ErrorBuffer,
+ nullptr, nullptr, /*DetachProcess*/ true);
if (!ErrorBuffer.empty())
return llvm::make_error<StringError>(ErrorBuffer, inconvertibleErrorCode());
diff --git a/clang/test/ModuleBuildDaemon/launch.c b/clang/test/ModuleBuildDaemon/launch.c
index d1bd8af7f244fa..2ee0f3f1367ba8 100644
--- a/clang/test/ModuleBuildDaemon/launch.c
+++ b/clang/test/ModuleBuildDaemon/launch.c
@@ -1,13 +1,11 @@
// Check that the module build daemon can create a unix socket
// REQUIRES: !system-windows
-
-// RUN: if pgrep -f "cc1modbuildd mbd-launch"; then pkill -f "cc1modbuildd mbd-launch"; fi
// RUN: rm -rf mbd-launch %t
-// RUN: %clang -cc1modbuildd mbd-launch -v
+// The module build daemon relies on llvm::sys::ExecuteNoWait to be detached from the
+// terminal so when using -cc1modbuildd the command needs to be killed manually
+// RUN: timeout --preserve-status --signal=SIGTERM 2 %clang -cc1modbuildd mbd-launch -v
// RUN: cat mbd-launch/mbd.out | FileCheck %s
// CHECK: mbd created and binded to socket at: mbd-launch/mbd.sock
-
-// RUN: if pgrep -f "cc1modbuildd mbd-launch"; then pkill -f "cc1modbuildd mbd-launch"; fi
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index ea441bdfbfb042..de2658080939f1 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -76,7 +76,7 @@ class ModuleBuildDaemonServer {
~ModuleBuildDaemonServer() { shutdownDaemon(); }
- int forkDaemon();
+ int setupDaemonEnv();
int createDaemonSocket();
int listenForClients();
@@ -98,9 +98,6 @@ class ModuleBuildDaemonServer {
}
private:
-#ifndef _WIN32
- pid_t Pid = -1;
-#endif
std::atomic<int> ListenSocketFD = -1;
};
@@ -115,18 +112,7 @@ void handleSignal(int Signal) {
#ifndef _WIN32
// Forks and detaches process, creating module build daemon
-int ModuleBuildDaemonServer::forkDaemon() {
-
- pid_t pid = fork();
-
- if (pid < 0) {
- exit(EXIT_FAILURE);
- }
- if (pid > 0) {
- exit(EXIT_SUCCESS);
- }
-
- Pid = getpid();
+int ModuleBuildDaemonServer::setupDaemonEnv() {
close(STDIN_FILENO);
close(STDOUT_FILENO);
@@ -143,10 +129,6 @@ int ModuleBuildDaemonServer::forkDaemon() {
errs() << "failed to ignore SIGHUP" << '\n';
exit(EXIT_FAILURE);
}
- if (setsid() == -1) {
- errs() << "setsid failed" << '\n';
- exit(EXIT_FAILURE);
- }
return EXIT_SUCCESS;
}
@@ -274,7 +256,6 @@ int ModuleBuildDaemonServer::listenForClients() {
// The arguments <path> and -v are optional. By default <path> follows the
// format: /tmp/clang-<BLAKE3HashOfClangFullVersion>.
//
-
int cc1modbuildd_main(ArrayRef<const char *> Argv) {
std::string BasePath;
@@ -306,7 +287,7 @@ int cc1modbuildd_main(ArrayRef<const char *> Argv) {
DaemonPtr = &Daemon;
#ifndef _WIN32
- Daemon.forkDaemon();
+ Daemon.setupDaemonEnv();
Daemon.createDaemonSocket();
Daemon.listenForClients();
#endif
>From cba9f3a97647389e5874bcd87655d17f5790e112 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Sun, 17 Dec 2023 23:49:24 -0500
Subject: [PATCH 10/27] Transition socket code to raw_socket_stream
---
.../clang/Tooling/ModuleBuildDaemon/Client.h | 10 +-
.../ModuleBuildDaemon/SocketMsgSupport.h | 24 ++-
.../Tooling/ModuleBuildDaemon/SocketSupport.h | 27 ---
.../clang/Tooling/ModuleBuildDaemon/Utils.h | 2 +-
.../Tooling/ModuleBuildDaemon/CMakeLists.txt | 2 +-
.../lib/Tooling/ModuleBuildDaemon/Client.cpp | 83 ++++-----
.../ModuleBuildDaemon/SocketMsgSupport.cpp | 77 +++++++++
.../ModuleBuildDaemon/SocketSupport.cpp | 111 ------------
clang/test/ModuleBuildDaemon/launch.c | 6 +-
clang/tools/driver/cc1modbuildd_main.cpp | 163 +++++++-----------
10 files changed, 214 insertions(+), 291 deletions(-)
delete mode 100644 clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
create mode 100644 clang/lib/Tooling/ModuleBuildDaemon/SocketMsgSupport.cpp
delete mode 100644 clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
index 045fc7ffb4f258..0d25b571475513 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
@@ -14,17 +14,19 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_socket_stream.h"
namespace clang::tooling::cc1modbuildd {
-llvm::Error attemptHandshake(int SocketFD, clang::DiagnosticsEngine &Diag);
+llvm::Error attemptHandshake(llvm::raw_socket_stream &Client,
+ DiagnosticsEngine &Diag);
llvm::Error spawnModuleBuildDaemon(llvm::StringRef BasePath, const char *Argv0,
clang::DiagnosticsEngine &Diag);
-llvm::Expected<int> getModuleBuildDaemon(const char *Argv0,
- llvm::StringRef BasePath,
- clang::DiagnosticsEngine &Diag);
+llvm::Expected<std::unique_ptr<llvm::raw_socket_stream>>
+getModuleBuildDaemon(const char *Argv0, llvm::StringRef BasePath,
+ clang::DiagnosticsEngine &Diag);
void spawnModuleBuildDaemonAndHandshake(const clang::CompilerInvocation &Clang,
clang::DiagnosticsEngine &Diag,
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
index b8d6717b02cb5a..f851dd8a5529b5 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
@@ -10,7 +10,6 @@
#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
#include "clang/Tooling/ModuleBuildDaemon/Client.h"
-#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
namespace clang::tooling::cc1modbuildd {
@@ -32,6 +31,12 @@ struct HandshakeMsg : public BaseMsg {
: BaseMsg(Action, Status) {}
};
+Expected<std::unique_ptr<llvm::raw_socket_stream>>
+connectToSocket(StringRef SocketPath);
+llvm::Error readFromSocket(llvm::raw_socket_stream &Connection,
+ std::string &BufferConsumer);
+void writeToSocket(llvm::raw_socket_stream &Socket, llvm::StringRef Buffer);
+
template <typename T> std::string getBufferFromSocketMsg(T Msg) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
@@ -49,9 +54,9 @@ llvm::Expected<T> getSocketMsgFromBuffer(llvm::StringRef Buffer) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
- T ClientRequest;
+ T Request;
llvm::yaml::Input YamlIn(Buffer);
- YamlIn >> ClientRequest;
+ YamlIn >> Request;
// YamlIn.error() dumps an error message if there is one
if (YamlIn.error()) {
@@ -60,15 +65,16 @@ llvm::Expected<T> getSocketMsgFromBuffer(llvm::StringRef Buffer) {
llvm::inconvertibleErrorCode());
}
- return ClientRequest;
+ return Request;
}
-template <typename T> llvm::Expected<T> readSocketMsgFromSocket(int FD) {
+template <typename T>
+llvm::Expected<T> readSocketMsgFromSocket(llvm::raw_socket_stream &Socket) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
std::string BufferConsumer;
- if (llvm::Error ReadErr = readFromSocket(FD, BufferConsumer))
+ if (llvm::Error ReadErr = readFromSocket(Socket, BufferConsumer))
return std::move(ReadErr);
// Wait for response from module build daemon
@@ -79,13 +85,13 @@ template <typename T> llvm::Expected<T> readSocketMsgFromSocket(int FD) {
return std::move(*MaybeResponse);
}
-template <typename T> llvm::Error writeSocketMsgToSocket(T Msg, int FD) {
+template <typename T>
+llvm::Error writeSocketMsgToSocket(llvm::raw_socket_stream &Socket, T Msg) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
std::string Buffer = getBufferFromSocketMsg(Msg);
- if (llvm::Error Err = writeToSocket(Buffer, FD))
- return std::move(Err);
+ writeToSocket(Socket, Buffer);
return llvm::Error::success();
}
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
deleted file mode 100644
index 55a09c142a6543..00000000000000
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
+++ /dev/null
@@ -1,27 +0,0 @@
-//===-------------------------- SocketSupport.h ---------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
-#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
-
-#include "clang/Frontend/CompilerInstance.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/YAMLParser.h"
-#include "llvm/Support/YAMLTraits.h"
-
-namespace clang::tooling::cc1modbuildd {
-
-llvm::Expected<int> createSocket();
-llvm::Expected<int> connectToSocket(llvm::StringRef SocketPath);
-llvm::Error readFromSocket(int FD, std::string &BufferConsumer);
-llvm::Error writeToSocket(llvm::StringRef Buffer, int WriteFD);
-
-} // namespace clang::tooling::cc1modbuildd
-
-#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
index 4dfde73a1143fe..f86228e8001077 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
@@ -21,6 +21,7 @@
#define SOCKET_FILE_NAME "mbd.sock"
#define STDOUT_FILE_NAME "mbd.out"
#define STDERR_FILE_NAME "mbd.err"
+#define MODULE_BUILD_DAEMON_FLAG "-cc1modbuildd"
namespace clang::tooling::cc1modbuildd {
@@ -33,5 +34,4 @@ llvm::Error makeStringError(llvm::Error Err, std::string Msg);
std::string getBasePath();
} // namespace clang::tooling::cc1modbuildd
-
#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt b/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
index 9c1f5dc1aa2c0c..2d42727039c9da 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
+++ b/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
@@ -4,6 +4,6 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangModuleBuildDaemon
Client.cpp
- SocketSupport.cpp
+ SocketMsgSupport.cpp
Utils.cpp
)
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
index be26049cce84a0..9ec66cb80ea1dc 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
@@ -11,7 +11,6 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
-#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/ScopeExit.h"
@@ -20,9 +19,6 @@
#include "llvm/Support/BLAKE3.h"
#include "llvm/Support/Program.h"
-// TODO: Make portable
-#if LLVM_ON_UNIX
-
#include <cerrno>
#include <filesystem>
#include <fstream>
@@ -39,26 +35,37 @@ using namespace llvm;
namespace clang::tooling::cc1modbuildd {
-llvm::Error attemptHandshake(int SocketFD, DiagnosticsEngine &Diag) {
+llvm::Error attemptHandshake(raw_socket_stream &Client,
+ DiagnosticsEngine &Diag) {
HandshakeMsg Request{ActionType::HANDSHAKE, StatusType::REQUEST};
std::string Buffer = getBufferFromSocketMsg(Request);
// Send HandshakeMsg to module build daemon
- if (llvm::Error Err = writeToSocket(Buffer, SocketFD))
- return std::move(Err);
+ Client << Buffer;
+ Client.flush();
+ Buffer.clear();
// Receive response from module build daemon
- Expected<HandshakeMsg> MaybeServerResponse =
- readSocketMsgFromSocket<HandshakeMsg>(SocketFD);
- if (!MaybeServerResponse)
- return std::move(MaybeServerResponse.takeError());
- HandshakeMsg ServerResponse = std::move(*MaybeServerResponse);
+ if (llvm::Error ReadErr = readFromSocket(Client, Buffer)) {
+ // TODO: Add context such as "Daemon failed to read buffer from socket" to
+ // error message
+ return std::move(ReadErr);
+ }
+
+ Expected<HandshakeMsg> MaybeHandshakeResponse =
+ getSocketMsgFromBuffer<HandshakeMsg>(Buffer);
+ if (!MaybeHandshakeResponse) {
+ // TODO: Add context such as "Failed to convert buffer to HandshakeMsg" to
+ // error message
+ return std::move(MaybeHandshakeResponse.takeError());
+ }
- assert(ServerResponse.MsgAction == ActionType::HANDSHAKE &&
+ HandshakeMsg HandshakeResponse = std::move(*MaybeHandshakeResponse);
+ assert(HandshakeResponse.MsgAction == ActionType::HANDSHAKE &&
"Response ActionType should only ever be HANDSHAKE");
- if (ServerResponse.MsgStatus == StatusType::SUCCESS) {
+ if (HandshakeResponse.MsgStatus == StatusType::SUCCESS) {
return llvm::Error::success();
}
@@ -67,10 +74,9 @@ llvm::Error attemptHandshake(int SocketFD, DiagnosticsEngine &Diag) {
inconvertibleErrorCode());
}
-llvm::Error spawnModuleBuildDaemon(StringRef BasePath, const char *Argv0,
- DiagnosticsEngine &Diag) {
+llvm::Error spawnModuleBuildDaemon(const char *Argv0, DiagnosticsEngine &Diag) {
- std::vector<StringRef> Args = {Argv0, "-cc1modbuildd", BasePath.str()};
+ std::vector<StringRef> Args = {Argv0, "-cc1modbuildd"};
std::string ErrorBuffer;
llvm::sys::ExecuteNoWait(Argv0, Args, std::nullopt, {}, 0, &ErrorBuffer,
nullptr, nullptr, /*DetachProcess*/ true);
@@ -82,26 +88,27 @@ llvm::Error spawnModuleBuildDaemon(StringRef BasePath, const char *Argv0,
return llvm::Error::success();
}
-Expected<int> getModuleBuildDaemon(const char *Argv0, StringRef BasePath,
- DiagnosticsEngine &Diag) {
+Expected<std::unique_ptr<raw_socket_stream>>
+getModuleBuildDaemon(const char *Argv0, StringRef BasePath,
+ DiagnosticsEngine &Diag) {
SmallString<128> SocketPath = BasePath;
llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME);
if (llvm::sys::fs::exists(SocketPath)) {
- Expected<int> MaybeFD = connectToSocket(SocketPath);
- if (MaybeFD) {
- return std::move(*MaybeFD);
+ Expected<std::unique_ptr<raw_socket_stream>> MaybeClient =
+ connectToSocket(SocketPath);
+ if (MaybeClient) {
+ return std::move(*MaybeClient);
}
- consumeError(MaybeFD.takeError());
+ consumeError(MaybeClient.takeError());
}
- if (llvm::Error Err = spawnModuleBuildDaemon(BasePath, Argv0, Diag))
+ if (llvm::Error Err = spawnModuleBuildDaemon(Argv0, Diag))
return std::move(Err);
const unsigned int MICROSEC_IN_SEC = 1000000;
- constexpr unsigned int MAX_WAIT_TIME = 30 * MICROSEC_IN_SEC;
-
+ const constexpr unsigned int MAX_WAIT_TIME = 30 * MICROSEC_IN_SEC;
unsigned int CumulativeTime = 0;
unsigned int WaitTime = 10;
@@ -110,16 +117,16 @@ Expected<int> getModuleBuildDaemon(const char *Argv0, StringRef BasePath,
usleep(WaitTime);
if (llvm::sys::fs::exists(SocketPath)) {
- Expected<int> MaybeFD = connectToSocket(SocketPath);
- if (MaybeFD) {
+ Expected<std::unique_ptr<raw_socket_stream>> MaybeClient =
+ connectToSocket(SocketPath);
+ if (MaybeClient) {
Diag.Report(diag::remark_mbd_connection) << SocketPath;
- return std::move(*MaybeFD);
+ return std::move(*MaybeClient);
}
- consumeError(MaybeFD.takeError());
+ consumeError(MaybeClient.takeError());
}
CumulativeTime += WaitTime;
- // Exponential backoff
WaitTime = WaitTime * 2;
}
@@ -153,14 +160,14 @@ void spawnModuleBuildDaemonAndHandshake(const CompilerInvocation &Clang,
}
// If module build daemon does not exist spawn module build daemon
- Expected<int> MaybeDaemonFD = getModuleBuildDaemon(Argv0, BasePath, Diag);
- if (!MaybeDaemonFD) {
- Diag.Report(diag::err_mbd_connect) << MaybeDaemonFD.takeError();
+ Expected<std::unique_ptr<raw_socket_stream>> MaybeClient =
+ getModuleBuildDaemon(Argv0, BasePath, Diag);
+ if (!MaybeClient) {
+ Diag.Report(diag::err_mbd_connect) << MaybeClient.takeError();
return;
}
- int DaemonFD = std::move(*MaybeDaemonFD);
-
- if (llvm::Error HandshakeErr = attemptHandshake(DaemonFD, Diag)) {
+ raw_socket_stream &Client = **MaybeClient;
+ if (llvm::Error HandshakeErr = attemptHandshake(Client, Diag)) {
Diag.Report(diag::err_mbd_handshake) << std::move(HandshakeErr);
return;
}
@@ -170,5 +177,3 @@ void spawnModuleBuildDaemonAndHandshake(const CompilerInvocation &Clang,
}
} // namespace clang::tooling::cc1modbuildd
-
-#endif // LLVM_ON_UNIX
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketMsgSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketMsgSupport.cpp
new file mode 100644
index 00000000000000..a8c1f2954df288
--- /dev/null
+++ b/clang/lib/Tooling/ModuleBuildDaemon/SocketMsgSupport.cpp
@@ -0,0 +1,77 @@
+//===------------------------ SocketMsgSupport.cpp ------------------------===//
+//
+// 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 "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
+#include "clang/Basic/Version.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Tooling/ModuleBuildDaemon/Client.h"
+#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/BLAKE3.h"
+
+#include <cerrno>
+#include <filesystem>
+#include <fstream>
+#include <signal.h>
+#include <spawn.h>
+#include <string>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+using namespace llvm;
+
+namespace clang::tooling::cc1modbuildd {
+
+Expected<std::unique_ptr<raw_socket_stream>>
+connectToSocket(StringRef SocketPath) {
+
+ Expected<std::unique_ptr<raw_socket_stream>> MaybeClient =
+ raw_socket_stream::createConnectedUnix(SocketPath);
+ if (!MaybeClient)
+ return std::move(MaybeClient.takeError());
+
+ return std::move(*MaybeClient);
+}
+
+llvm::Error readFromSocket(raw_socket_stream &Connection,
+ std::string &BufferConsumer) {
+
+ char Buffer[MAX_BUFFER];
+ ssize_t n;
+
+ while ((n = Connection.read(Buffer, MAX_BUFFER)) > 0) {
+ BufferConsumer.append(Buffer, n);
+ // Read until \n... encountered (last line of YAML document)
+ if (BufferConsumer.find("\n...") != std::string::npos)
+ break;
+ }
+
+ if (n < 0) {
+ // TODO: now that I am using raw_socket_stream look into if I still should
+ // be using errno
+ std::string Msg = "socket read error: " + std::string(strerror(errno));
+ return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
+ }
+ if (n == 0)
+ return llvm::make_error<StringError>("EOF", inconvertibleErrorCode());
+ return llvm::Error::success();
+}
+
+// TODO: Need to add error handling for a write
+void writeToSocket(raw_socket_stream &Socket, llvm::StringRef Buffer) {
+ Socket << Buffer;
+ Socket.flush();
+}
+
+} // namespace clang::tooling::cc1modbuildd
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
deleted file mode 100644
index 1ac7acefe1c247..00000000000000
--- a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-//===------------------------- SocketSupport.cpp --------------------------===//
-//
-// 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 "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
-#include "clang/Basic/Version.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Tooling/ModuleBuildDaemon/Client.h"
-#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/ScopeExit.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Config/llvm-config.h"
-#include "llvm/Support/BLAKE3.h"
-
-// TODO: Make portable
-#if LLVM_ON_UNIX
-
-#include <cerrno>
-#include <filesystem>
-#include <fstream>
-#include <signal.h>
-#include <spawn.h>
-#include <string>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-using namespace llvm;
-
-namespace clang::tooling::cc1modbuildd {
-
-Expected<int> createSocket() {
- int FD;
- if ((FD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
- std::string Msg = "socket create error: " + std::string(strerror(errno));
- return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
- }
- return FD;
-}
-
-Expected<int> connectToSocket(StringRef SocketPath) {
-
- Expected<int> MaybeFD = createSocket();
- if (!MaybeFD)
- return std::move(MaybeFD.takeError());
-
- int FD = std::move(*MaybeFD);
-
- struct sockaddr_un Addr;
- memset(&Addr, 0, sizeof(Addr));
- Addr.sun_family = AF_UNIX;
- strncpy(Addr.sun_path, SocketPath.str().c_str(), sizeof(Addr.sun_path) - 1);
-
- if (connect(FD, (struct sockaddr *)&Addr, sizeof(Addr)) == -1) {
- close(FD);
- std::string Msg = "socket connect error: " + std::string(strerror(errno));
- return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
- }
- return FD;
-}
-
-llvm::Error readFromSocket(int FD, std::string &BufferConsumer) {
-
- char Buffer[MAX_BUFFER];
- ssize_t n;
-
- while ((n = read(FD, Buffer, MAX_BUFFER)) > 0) {
-
- BufferConsumer.append(Buffer, n);
- // Read until \n... encountered (last line of YAML document)
- if (BufferConsumer.find("\n...") != std::string::npos)
- break;
- }
-
- if (n < 0) {
- std::string Msg = "socket read error: " + std::string(strerror(errno));
- return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
- }
- if (n == 0)
- return llvm::make_error<StringError>("EOF", inconvertibleErrorCode());
- return llvm::Error::success();
-}
-
-llvm::Error writeToSocket(StringRef Buffer, int WriteFD) {
-
- ssize_t BytesToWrite = static_cast<ssize_t>(Buffer.size());
- const char *Bytes = Buffer.data();
-
- while (BytesToWrite) {
- ssize_t BytesWritten = write(WriteFD, Bytes, BytesToWrite);
- if (BytesWritten == -1) {
- std::string Msg = "socket write error: " + std::string(strerror(errno));
- return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
- }
-
- BytesToWrite -= BytesWritten;
- Bytes += BytesWritten;
- }
- return llvm::Error::success();
-}
-
-} // namespace clang::tooling::cc1modbuildd
-
-#endif // LLVM_ON_UNIX
diff --git a/clang/test/ModuleBuildDaemon/launch.c b/clang/test/ModuleBuildDaemon/launch.c
index 2ee0f3f1367ba8..48436ca7140ad3 100644
--- a/clang/test/ModuleBuildDaemon/launch.c
+++ b/clang/test/ModuleBuildDaemon/launch.c
@@ -3,8 +3,10 @@
// REQUIRES: !system-windows
// RUN: rm -rf mbd-launch %t
-// The module build daemon relies on llvm::sys::ExecuteNoWait to be detached from the
-// terminal so when using -cc1modbuildd the command needs to be killed manually
+// The module build daemon relies on a call to llvm::sys::ExecuteNoWait by the
+// frontend where Detached == true to be detached from the terminal so when
+// using -cc1modbuildd directly the command needs to be killed manually
+
// RUN: timeout --preserve-status --signal=SIGTERM 2 %clang -cc1modbuildd mbd-launch -v
// RUN: cat mbd-launch/mbd.out | FileCheck %s
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index de2658080939f1..ff1060a0bbcdba 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
-#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
@@ -40,7 +39,7 @@ using namespace clang::tooling::cc1modbuildd;
#include <unistd.h>
#endif
-// Create unbuffered STDOUT stream so that any logging done by module build
+// Create unbuffered STDOUT stream so that any logging done by the module build
// daemon can be viewed without having to terminate the process
static raw_fd_ostream &unbuff_outs() {
static raw_fd_ostream S(fileno(stdout), false, true);
@@ -54,11 +53,6 @@ static void printVerboseLog(const llvm::Twine &message) {
}
}
-struct ClientConnection {
- int ClientFD;
- std::string Buffer;
-};
-
namespace {
class ModuleBuildDaemonServer {
@@ -67,7 +61,7 @@ class ModuleBuildDaemonServer {
SmallString<256> STDERR;
SmallString<256> STDOUT;
- ModuleBuildDaemonServer(StringRef Path, ArrayRef<const char *> Argv)
+ ModuleBuildDaemonServer(StringRef Path)
: SocketPath(Path), STDERR(Path), STDOUT(Path) {
llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME);
llvm::sys::path::append(STDOUT, STDOUT_FILE_NAME);
@@ -80,25 +74,18 @@ class ModuleBuildDaemonServer {
int createDaemonSocket();
int listenForClients();
- static void handleClient(ClientConnection Connection);
+ static void handleClient(std::shared_ptr<raw_socket_stream> Connection);
// TODO: modify so when shutdownDaemon is called the daemon stops accepting
// new client connections and waits for all existing client connections to
// terminate before closing the file descriptor and exiting
void shutdownDaemon() {
-#ifdef _WIN32
- // TODO: implement windows version
-#else
- int SocketFD = ListenSocketFD.load();
- unlink(SocketPath.c_str());
- shutdown(SocketFD, SHUT_RD);
- close(SocketFD);
- _Exit(EXIT_SUCCESS);
-#endif
+ ServerListener.value().~ListeningSocket();
+ exit(EXIT_SUCCESS);
}
private:
- std::atomic<int> ListenSocketFD = -1;
+ std::optional<llvm::ListeningSocket> ServerListener;
};
// Required to handle SIGTERM by calling Shutdown
@@ -110,13 +97,14 @@ void handleSignal(int Signal) {
}
} // namespace
-#ifndef _WIN32
-// Forks and detaches process, creating module build daemon
+// Sets up file descriptors and signals for module build daemon
int ModuleBuildDaemonServer::setupDaemonEnv() {
+#ifdef _WIN32
+ freopen("NUL", "r", stdin);
+#else
close(STDIN_FILENO);
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
+#endif
freopen(STDOUT.c_str(), "a", stdout);
freopen(STDERR.c_str(), "a", stderr);
@@ -129,116 +117,92 @@ int ModuleBuildDaemonServer::setupDaemonEnv() {
errs() << "failed to ignore SIGHUP" << '\n';
exit(EXIT_FAILURE);
}
-
return EXIT_SUCCESS;
}
-// TODO: Make portable
// Creates unix socket for IPC with module build daemon
int ModuleBuildDaemonServer::createDaemonSocket() {
- // New socket
- int SocketFD = socket(AF_UNIX, SOCK_STREAM, 0);
-
- if (SocketFD == -1) {
- std::perror("Socket create error: ");
- exit(EXIT_FAILURE);
+ Expected<ListeningSocket> MaybeServerListener =
+ llvm::ListeningSocket::createUnix(SocketPath);
+
+ if (llvm::Error Err = MaybeServerListener.takeError()) {
+
+ llvm::handleAllErrors(std::move(Err), [&](const llvm::StringError &SE) {
+ // If the socket address is already in use, exit because another module
+ // build daemon has successfully launched. When translation units are
+ // compiled in parallel, until the socket file is created, all clang
+ // invocations will try to spawn a module build daemon.
+ if (std::error_code EC = SE.convertToErrorCode();
+ EC == std::errc::address_in_use) {
+ exit(EXIT_SUCCESS);
+ } else {
+ llvm::errs() << "ERROR: " << EC.message() << '\n';
+ exit(EXIT_FAILURE);
+ }
+ });
}
- struct sockaddr_un addr;
- memset(&addr, 0, sizeof(struct sockaddr_un));
- addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, SocketPath.c_str(), sizeof(addr.sun_path) - 1);
-
- // bind to local address
- if (bind(SocketFD, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
-
- // If the socket address is already in use, exit because another module
- // build daemon has successfully launched. When translation units are
- // compiled in parallel, until the socket file is created, all clang
- // invocations will spawn a module build daemon.
- if (errno == EADDRINUSE) {
- close(SocketFD);
- exit(EXIT_SUCCESS);
- }
- std::perror("Socket bind error: ");
- exit(EXIT_FAILURE);
- }
printVerboseLog("mbd created and binded to socket at: " + SocketPath);
-
- // set socket to accept incoming connection request
- unsigned MaxBacklog = llvm::hardware_concurrency().compute_thread_count();
- if (listen(SocketFD, MaxBacklog) == -1) {
- std::perror("Socket listen error: ");
- exit(EXIT_FAILURE);
- }
-
- ListenSocketFD.store(SocketFD);
+ ServerListener.emplace(std::move(*MaybeServerListener));
return 0;
}
-// Function submitted to thread pool with each client connection. Not
-// responsible for closing client connections
-// TODO: Setup something like ScopedHandle to auto close client on return
-void ModuleBuildDaemonServer::handleClient(ClientConnection Connection) {
+#include <cstddef>
+
+// Function submitted to thread pool with each frontend connection. Not
+// responsible for closing frontend socket connections
+void ModuleBuildDaemonServer::handleClient(
+ std::shared_ptr<llvm::raw_socket_stream> MovableConnection) {
+
+ llvm::raw_socket_stream &Connection = *MovableConnection;
+ std::string Buffer;
// Read handshake from client
- if (llvm::Error ReadErr =
- readFromSocket(Connection.ClientFD, Connection.Buffer)) {
+ if (llvm::Error ReadErr = readFromSocket(Connection, Buffer)) {
writeError(std::move(ReadErr), "Daemon failed to read buffer from socket");
- close(Connection.ClientFD);
return;
}
// Wait for response from module build daemon
Expected<HandshakeMsg> MaybeHandshakeMsg =
- getSocketMsgFromBuffer<HandshakeMsg>(Connection.Buffer);
+ getSocketMsgFromBuffer<HandshakeMsg>(Buffer);
if (!MaybeHandshakeMsg) {
writeError(MaybeHandshakeMsg.takeError(),
"Failed to convert buffer to HandshakeMsg: ");
- close(Connection.ClientFD);
return;
}
// Have received HandshakeMsg - send HandshakeMsg response to clang invocation
HandshakeMsg Msg(ActionType::HANDSHAKE, StatusType::SUCCESS);
- if (llvm::Error WriteErr = writeSocketMsgToSocket(Msg, Connection.ClientFD)) {
+ if (llvm::Error WriteErr = writeSocketMsgToSocket(Connection, Msg)) {
writeError(std::move(WriteErr),
"Failed to notify client that handshake was received");
- close(Connection.ClientFD);
return;
}
- // Remove HandshakeMsg from Buffer in preperation for next read. Not currently
- // necessary but will be once Daemon increases communication
- size_t Position = Connection.Buffer.find("...\n");
- if (Position != std::string::npos)
- Connection.Buffer = Connection.Buffer.substr(Position + 4);
-
- close(Connection.ClientFD);
return;
}
int ModuleBuildDaemonServer::listenForClients() {
llvm::ThreadPool Pool;
- int Client;
-
while (true) {
- if ((Client = accept(ListenSocketFD.load(), NULL, NULL)) == -1) {
- std::perror("Socket accept error: ");
+ Expected<std::unique_ptr<raw_socket_stream>> MaybeConnection =
+ ServerListener.value().accept();
+
+ if (llvm::Error Err = MaybeConnection.takeError()) {
+ llvm::handleAllErrors(std::move(Err), [&](const llvm::StringError &SE) {
+ llvm::errs() << "ERROR: " << SE.getMessage() << '\n';
+ });
continue;
}
-
- ClientConnection Connection;
- Connection.ClientFD = Client;
-
+ std::shared_ptr<raw_socket_stream> Connection(std::move(*MaybeConnection));
Pool.async(handleClient, Connection);
}
return 0;
}
-#endif // LLVM_ON_UNIX
// Module build daemon is spawned with the following command line:
//
@@ -259,6 +223,8 @@ int ModuleBuildDaemonServer::listenForClients() {
int cc1modbuildd_main(ArrayRef<const char *> Argv) {
std::string BasePath;
+ // command line argument parsing. -cc1modbuildd is sliced away when passing
+ // Argv to cc1modbuildd_main
if (!Argv.empty()) {
if (find(Argv, StringRef("-v")) != Argv.end())
@@ -266,31 +232,34 @@ int cc1modbuildd_main(ArrayRef<const char *> Argv) {
if (strcmp(Argv[0], "-v") != 0)
BasePath = Argv[0];
- } else
+ else
+ BasePath = getBasePath();
+
+ } else {
BasePath = getBasePath();
+ }
- // TODO: Make portable. On most unix platforms a socket address cannot be over
- // 108 characters but that may not always be the case. Functionality will be
- // provided by llvm/Support/Sockets once that is implemented
+ // TODO: Max length may vary across different platforms. Incoming llvm/Support
+ // for sockets will help make this portable. On most unix platforms a socket
+ // address cannot be over 108 characters. The socket file, mbd.sock, takes up
+ // 8 characters leaving 100 characters left for the user/system
const int MAX_ADDR = 108;
if (BasePath.length() >= MAX_ADDR - std::string(SOCKET_FILE_NAME).length()) {
- errs() << "Provided socket path" + BasePath +
- " is too long. Socket path much be equal to or less then 100 "
- "characters. Module build daemon will not be spawned.";
+ errs() << "Socket path '" + BasePath +
+ "' is too long. Socket path much be equal to or less then "
+ "100 characters. Module build daemon will not be spawned.";
return 1;
}
llvm::sys::fs::create_directories(BasePath);
- ModuleBuildDaemonServer Daemon(BasePath, Argv);
+ ModuleBuildDaemonServer Daemon(BasePath);
// Used to handle signals
DaemonPtr = &Daemon;
-#ifndef _WIN32
Daemon.setupDaemonEnv();
Daemon.createDaemonSocket();
Daemon.listenForClients();
-#endif
- return 0;
+ return EXIT_SUCCESS;
}
>From bbab8839b4543fe816dcd86ca60dd4da237900a0 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Thu, 28 Dec 2023 11:20:08 -0500
Subject: [PATCH 11/27] Various refactors
---
.../clang/Basic/DiagnosticFrontendKinds.td | 11 +-
.../{Client.h => Frontend.h} | 27 ++--
.../{SocketMsgSupport.h => SocketSupport.h} | 98 ++++++--------
.../clang/Tooling/ModuleBuildDaemon/Utils.h | 30 +++--
.../Tooling/ModuleBuildDaemon/CMakeLists.txt | 4 +-
.../{Client.cpp => Frontend.cpp} | 111 +++++++---------
.../ModuleBuildDaemon/SocketMsgSupport.cpp | 77 -----------
.../ModuleBuildDaemon/SocketSupport.cpp | 60 +++++++++
clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp | 28 ++--
clang/test/ModuleBuildDaemon/handshake.c | 3 +-
clang/test/ModuleBuildDaemon/launch.c | 2 +-
clang/tools/driver/cc1_main.cpp | 4 +-
clang/tools/driver/cc1modbuildd_main.cpp | 124 +++++++-----------
13 files changed, 246 insertions(+), 333 deletions(-)
rename clang/include/clang/Tooling/ModuleBuildDaemon/{Client.h => Frontend.h} (55%)
rename clang/include/clang/Tooling/ModuleBuildDaemon/{SocketMsgSupport.h => SocketSupport.h} (54%)
rename clang/lib/Tooling/ModuleBuildDaemon/{Client.cpp => Frontend.cpp} (57%)
delete mode 100644 clang/lib/Tooling/ModuleBuildDaemon/SocketMsgSupport.cpp
create mode 100644 clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 4e6c477d7bf93d..a858bc163de0c3 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -262,16 +262,15 @@ def err_test_module_file_extension_version : Error<
"test module file extension '%0' has different version (%1.%2) than expected "
"(%3.%4)">;
-def err_unix_socket_addr_length :
- Error<"Socket address %0 is %1 characters long which is larger then the max length of %2">,
- DefaultFatal;
-
// Module Build Daemon
+def err_basepath_length :
+ Error<"BasePath '%0' is longer then the max length of %1">,
+ DefaultFatal;
def err_mbd_handshake :
- Error<"Attempt to hadshake with daemon has failed: %0">,
+ Error<"Attempt to handshake with module build daemon has failed: %0">,
DefaultFatal;
def err_mbd_connect :
- Error<"Attempt to connect to daemon has failed: %0">,
+ Error<"Attempt to connect to module build daemon has failed: %0">,
DefaultFatal;
def remark_mbd_spawn :
Remark<"Successfully spawned module build daemon">,
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Frontend.h
similarity index 55%
rename from clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
rename to clang/include/clang/Tooling/ModuleBuildDaemon/Frontend.h
index 0d25b571475513..727738ef77b1bd 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Frontend.h
@@ -1,4 +1,4 @@
-//===------------------------------ Client.h ------------------------------===//
+//===----------------------------- Frontend.h -----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,31 +6,30 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_CLIENT_H
-#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_CLIENT_H
+#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_FRONTEND_H
+#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_FRONTEND_H
-#include "clang/Frontend/CompilerInstance.h"
-#include "llvm/ADT/ArrayRef.h"
+#include "clang/Frontend/CompilerInvocation.h"
#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/YAMLParser.h"
-#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_socket_stream.h"
namespace clang::tooling::cc1modbuildd {
llvm::Error attemptHandshake(llvm::raw_socket_stream &Client,
- DiagnosticsEngine &Diag);
+ clang::DiagnosticsEngine &Diag);
-llvm::Error spawnModuleBuildDaemon(llvm::StringRef BasePath, const char *Argv0,
- clang::DiagnosticsEngine &Diag);
+llvm::Error spawnModuleBuildDaemon(const clang::CompilerInvocation &Clang,
+ const char *Argv0,
+ clang::DiagnosticsEngine &Diag,
+ std::string BasePath);
llvm::Expected<std::unique_ptr<llvm::raw_socket_stream>>
-getModuleBuildDaemon(const char *Argv0, llvm::StringRef BasePath,
- clang::DiagnosticsEngine &Diag);
+getModuleBuildDaemon(const clang::CompilerInvocation &Clang, const char *Argv0,
+ clang::DiagnosticsEngine &Diag, llvm::StringRef BasePath);
void spawnModuleBuildDaemonAndHandshake(const clang::CompilerInvocation &Clang,
- clang::DiagnosticsEngine &Diag,
- const char *Argv0);
+ const char *Argv0,
+ clang::DiagnosticsEngine &Diag);
} // namespace clang::tooling::cc1modbuildd
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
similarity index 54%
rename from clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
rename to clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
index f851dd8a5529b5..00623b41323e60 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
@@ -9,7 +9,11 @@
#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
-#include "clang/Tooling/ModuleBuildDaemon/Client.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_socket_stream.h"
namespace clang::tooling::cc1modbuildd {
@@ -31,86 +35,68 @@ struct HandshakeMsg : public BaseMsg {
: BaseMsg(Action, Status) {}
};
-Expected<std::unique_ptr<llvm::raw_socket_stream>>
-connectToSocket(StringRef SocketPath);
-llvm::Error readFromSocket(llvm::raw_socket_stream &Connection,
- std::string &BufferConsumer);
-void writeToSocket(llvm::raw_socket_stream &Socket, llvm::StringRef Buffer);
+llvm::Expected<std::unique_ptr<llvm::raw_socket_stream>>
+connectToSocket(llvm::StringRef SocketPath);
+llvm::Expected<std::string>
+readBufferFromSocket(llvm::raw_socket_stream &Socket);
+llvm::Error writeBufferToSocket(llvm::raw_socket_stream &Socket,
+ llvm::StringRef Buffer);
-template <typename T> std::string getBufferFromSocketMsg(T Msg) {
- static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
- "T must inherit from cc1modbuildd::BaseMsg");
+template <typename T> std::string convertMsgStructToBuffer(T MsgStruct) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value);
std::string Buffer;
llvm::raw_string_ostream OS(Buffer);
llvm::yaml::Output YamlOut(OS);
- YamlOut << Msg;
+ // TODO confirm yaml::Output does not have any error messages
+ YamlOut << MsgStruct;
+
return Buffer;
}
template <typename T>
-llvm::Expected<T> getSocketMsgFromBuffer(llvm::StringRef Buffer) {
- static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
- "T must inherit from cc1modbuildd::BaseMsg");
+llvm::Expected<T> convertBufferToMsgStruct(llvm::StringRef Buffer) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value);
- T Request;
+ T MsgStruct;
llvm::yaml::Input YamlIn(Buffer);
- YamlIn >> Request;
+ YamlIn >> MsgStruct;
// YamlIn.error() dumps an error message if there is one
- if (YamlIn.error()) {
- std::string Msg = "Syntax or semantic error during YAML parsing";
- return llvm::make_error<llvm::StringError>(Msg,
- llvm::inconvertibleErrorCode());
- }
+ if (YamlIn.error())
+ return llvm::make_error<llvm::StringError>(
+ "Syntax or semantic error during YAML parsing",
+ llvm::inconvertibleErrorCode());
- return Request;
-}
-
-template <typename T>
-llvm::Expected<T> readSocketMsgFromSocket(llvm::raw_socket_stream &Socket) {
- static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
- "T must inherit from cc1modbuildd::BaseMsg");
-
- std::string BufferConsumer;
- if (llvm::Error ReadErr = readFromSocket(Socket, BufferConsumer))
- return std::move(ReadErr);
-
- // Wait for response from module build daemon
- llvm::Expected<T> MaybeResponse =
- getSocketMsgFromBuffer<T>(std::move(BufferConsumer).c_str());
- if (!MaybeResponse)
- return std::move(MaybeResponse.takeError());
- return std::move(*MaybeResponse);
+ return MsgStruct;
}
template <typename T>
-llvm::Error writeSocketMsgToSocket(llvm::raw_socket_stream &Socket, T Msg) {
- static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
- "T must inherit from cc1modbuildd::BaseMsg");
+llvm::Expected<T> readMsgStructFromSocket(llvm::raw_socket_stream &Socket) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value);
- std::string Buffer = getBufferFromSocketMsg(Msg);
- writeToSocket(Socket, Buffer);
+ llvm::Expected<std::string> MaybeBuffer = readBufferFromSocket(Socket);
+ if (!MaybeBuffer) {
+ return std::move(MaybeBuffer.takeError());
+ }
+ std::string Buffer = std::move(*MaybeBuffer);
- return llvm::Error::success();
+ llvm::Expected<T> MaybeMsgStruct = convertBufferToMsgStruct<T>(Buffer);
+ if (!MaybeMsgStruct)
+ return std::move(MaybeMsgStruct.takeError());
+ return std::move(*MaybeMsgStruct);
}
template <typename T>
-llvm::Expected<int>
-connectAndWriteSocketMsgToSocket(T Msg, llvm::StringRef SocketPath) {
- static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
- "T must inherit from cc1modbuildd::BaseMsg");
-
- llvm::Expected<int> MaybeFD = connectToSocket(SocketPath);
- if (!MaybeFD)
- return std::move(MaybeFD.takeError());
- int FD = std::move(*MaybeFD);
+llvm::Error writeMsgStructToSocket(llvm::raw_socket_stream &Socket,
+ T MsgStruct) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value);
- if (llvm::Error Err = writeSocketMsgToSocket(Msg, FD))
+ std::string Buffer = convertMsgStructToBuffer(MsgStruct);
+ if (llvm::Error Err = writeBufferToSocket(Socket, Buffer))
return std::move(Err);
-
- return FD;
+ return llvm::Error::success();
}
} // namespace clang::tooling::cc1modbuildd
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
index f86228e8001077..643a1ec8bd32ee 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
@@ -6,8 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Functions required by both the module build daemon (server) and clang
-// invocation (client)
+// Functions required by both the frontend and the module build daemon
//
//===----------------------------------------------------------------------===//
@@ -15,23 +14,30 @@
#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
#include "llvm/Support/Error.h"
-#include <string>
-#define MAX_BUFFER 4096
-#define SOCKET_FILE_NAME "mbd.sock"
-#define STDOUT_FILE_NAME "mbd.out"
-#define STDERR_FILE_NAME "mbd.err"
-#define MODULE_BUILD_DAEMON_FLAG "-cc1modbuildd"
+#include <string>
+#include <sys/un.h>
namespace clang::tooling::cc1modbuildd {
-void writeError(llvm::Error Err, std::string Msg);
-std::string getFullErrorMsg(llvm::Error Err, std::string Msg);
-llvm::Error makeStringError(llvm::Error Err, std::string Msg);
+constexpr const std::string_view SOCKET_FILE_NAME = "mbd.sock";
+constexpr const std::string_view STDOUT_FILE_NAME = "mbd.out";
+constexpr const std::string_view STDERR_FILE_NAME = "mbd.err";
+constexpr const std::string_view MODULE_BUILD_DAEMON_FLAG = "-cc1modbuildd";
+
+// A llvm::raw_socket_stream uses sockaddr_un
+constexpr const size_t SOCKET_ADDR_MAX_LENGTH = sizeof(sockaddr_un::sun_path);
+
+constexpr const size_t BASEPATH_MAX_LENGTH =
+ SOCKET_ADDR_MAX_LENGTH - std::string_view(SOCKET_FILE_NAME).length();
// Get a temprary location where the daemon can store log files and a socket
// address. Of the format /tmp/clang-<BLAKE3HashOfClangFullVersion>/
std::string getBasePath();
+// Check if the user provided BasePath is short enough
+bool validBasePathLength(llvm::StringRef);
+
} // namespace clang::tooling::cc1modbuildd
-#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
+
+#endif
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt b/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
index 2d42727039c9da..c043557206ef9e 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
+++ b/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
)
add_clang_library(clangModuleBuildDaemon
- Client.cpp
- SocketMsgSupport.cpp
+ Frontend.cpp
+ SocketSupport.cpp
Utils.cpp
)
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp
similarity index 57%
rename from clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
rename to clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp
index 9ec66cb80ea1dc..ac31dba06b4dc1 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp
@@ -1,4 +1,4 @@
-//===----------------------------- Client.cpp -----------------------------===//
+//===---------------------------- Frontend.cpp ----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,30 +6,24 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Tooling/ModuleBuildDaemon/Client.h"
-#include "clang/Basic/Version.h"
+#include "clang/Tooling/ModuleBuildDaemon/Frontend.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendDiagnostic.h"
-#include "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
+#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/BLAKE3.h"
#include "llvm/Support/Program.h"
#include <cerrno>
+#include <chrono>
#include <filesystem>
#include <fstream>
#include <optional>
-#include <signal.h>
-#include <spawn.h>
#include <string>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-#include <unistd.h>
+#include <thread>
using namespace llvm;
@@ -38,49 +32,45 @@ namespace clang::tooling::cc1modbuildd {
llvm::Error attemptHandshake(raw_socket_stream &Client,
DiagnosticsEngine &Diag) {
- HandshakeMsg Request{ActionType::HANDSHAKE, StatusType::REQUEST};
- std::string Buffer = getBufferFromSocketMsg(Request);
-
// Send HandshakeMsg to module build daemon
- Client << Buffer;
- Client.flush();
-
- Buffer.clear();
- // Receive response from module build daemon
- if (llvm::Error ReadErr = readFromSocket(Client, Buffer)) {
- // TODO: Add context such as "Daemon failed to read buffer from socket" to
- // error message
- return std::move(ReadErr);
- }
+ HandshakeMsg Request{ActionType::HANDSHAKE, StatusType::REQUEST};
+ if (llvm::Error Err = writeMsgStructToSocket(Client, Request))
+ return std::move(Err);
- Expected<HandshakeMsg> MaybeHandshakeResponse =
- getSocketMsgFromBuffer<HandshakeMsg>(Buffer);
- if (!MaybeHandshakeResponse) {
- // TODO: Add context such as "Failed to convert buffer to HandshakeMsg" to
- // error message
- return std::move(MaybeHandshakeResponse.takeError());
+ // Read response from module build daemon
+ Expected<HandshakeMsg> MaybeResponse =
+ readMsgStructFromSocket<HandshakeMsg>(Client);
+ if (!MaybeResponse) {
+ return std::move(MaybeResponse.takeError());
}
+ HandshakeMsg Response = std::move(*MaybeResponse);
- HandshakeMsg HandshakeResponse = std::move(*MaybeHandshakeResponse);
- assert(HandshakeResponse.MsgAction == ActionType::HANDSHAKE &&
- "Response ActionType should only ever be HANDSHAKE");
+ assert(Response.MsgAction == ActionType::HANDSHAKE &&
+ "The response ActionType should only ever be HANDSHAKE");
- if (HandshakeResponse.MsgStatus == StatusType::SUCCESS) {
+ if (Response.MsgStatus == StatusType::SUCCESS) {
return llvm::Error::success();
}
return llvm::make_error<StringError>(
- "Received failed handshake response from module build daemon",
- inconvertibleErrorCode());
+ "Received handshake response 'FAILURE' from module build daemon",
+ std::make_error_code(std::errc::operation_not_permitted));
}
-llvm::Error spawnModuleBuildDaemon(const char *Argv0, DiagnosticsEngine &Diag) {
+llvm::Error spawnModuleBuildDaemon(const CompilerInvocation &Clang,
+ const char *Argv0, DiagnosticsEngine &Diag,
+ std::string BasePath) {
+
+ std::vector<StringRef> Args = {Argv0, MODULE_BUILD_DAEMON_FLAG};
+ if (!Clang.getFrontendOpts().ModuleBuildDaemonPath.empty())
+ Args.push_back(BasePath.c_str());
- std::vector<StringRef> Args = {Argv0, "-cc1modbuildd"};
std::string ErrorBuffer;
llvm::sys::ExecuteNoWait(Argv0, Args, std::nullopt, {}, 0, &ErrorBuffer,
nullptr, nullptr, /*DetachProcess*/ true);
+ // llvm::sys::ExecuteNoWait can fail for a variety of reasons which can't be
+ // generalized to one error code
if (!ErrorBuffer.empty())
return llvm::make_error<StringError>(ErrorBuffer, inconvertibleErrorCode());
@@ -89,8 +79,8 @@ llvm::Error spawnModuleBuildDaemon(const char *Argv0, DiagnosticsEngine &Diag) {
}
Expected<std::unique_ptr<raw_socket_stream>>
-getModuleBuildDaemon(const char *Argv0, StringRef BasePath,
- DiagnosticsEngine &Diag) {
+getModuleBuildDaemon(const CompilerInvocation &Clang, const char *Argv0,
+ DiagnosticsEngine &Diag, StringRef BasePath) {
SmallString<128> SocketPath = BasePath;
llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME);
@@ -104,17 +94,18 @@ getModuleBuildDaemon(const char *Argv0, StringRef BasePath,
consumeError(MaybeClient.takeError());
}
- if (llvm::Error Err = spawnModuleBuildDaemon(Argv0, Diag))
+ if (llvm::Error Err =
+ spawnModuleBuildDaemon(Clang, Argv0, Diag, BasePath.str()))
return std::move(Err);
- const unsigned int MICROSEC_IN_SEC = 1000000;
- const constexpr unsigned int MAX_WAIT_TIME = 30 * MICROSEC_IN_SEC;
+ constexpr const unsigned int MICROSEC_IN_SEC = 1000000;
+ constexpr const unsigned int MAX_WAIT_TIME = 30 * MICROSEC_IN_SEC;
unsigned int CumulativeTime = 0;
unsigned int WaitTime = 10;
while (CumulativeTime <= MAX_WAIT_TIME) {
// Wait a bit then check to see if the module build daemon has initialized
- usleep(WaitTime);
+ std::this_thread::sleep_for(std::chrono::microseconds(WaitTime));
if (llvm::sys::fs::exists(SocketPath)) {
Expected<std::unique_ptr<raw_socket_stream>> MaybeClient =
@@ -130,38 +121,34 @@ getModuleBuildDaemon(const char *Argv0, StringRef BasePath,
WaitTime = WaitTime * 2;
}
- // After waiting 30 seconds give up
+ // After waiting around 30 seconds give up and return an error
return llvm::make_error<StringError>(
- "Module build daemon could not be spawned", inconvertibleErrorCode());
+ "Could not connect to module build daemon",
+ std::make_error_code(std::errc::no_such_process));
}
void spawnModuleBuildDaemonAndHandshake(const CompilerInvocation &Clang,
- DiagnosticsEngine &Diag,
- const char *Argv0) {
+ const char *Argv0,
+ DiagnosticsEngine &Diag) {
// The module build daemon stores all output files and its socket address
// under BasePath. Either set BasePath to a user provided option or create an
// appropriate BasePath based on the BLAKE3 hash of the full clang version
std::string BasePath;
- if (!Clang.getFrontendOpts().ModuleBuildDaemonPath.empty())
- BasePath = Clang.getFrontendOpts().ModuleBuildDaemonPath;
- else
+ if (Clang.getFrontendOpts().ModuleBuildDaemonPath.empty())
BasePath = getBasePath();
-
- // TODO: Max length may vary across different platforms. Incoming llvm/Support
- // for sockets will help make this portable. On most unix platforms a socket
- // address cannot be over 108 characters. The socket file, mbd.sock, takes up
- // 8 characters leaving 100 characters left for the user/system
- int MAX_ADDR = 108;
- if (BasePath.length() >= MAX_ADDR - std::string(SOCKET_FILE_NAME).length()) {
- Diag.Report(diag::err_unix_socket_addr_length)
- << BasePath << BasePath.length() << 100;
- return;
+ else {
+ // Get user provided BasePath and confirm it is short enough
+ BasePath = Clang.getFrontendOpts().ModuleBuildDaemonPath;
+ if (!validBasePathLength(BasePath)) {
+ Diag.Report(diag::err_basepath_length) << BasePath << BASEPATH_MAX_LENGTH;
+ return;
+ }
}
// If module build daemon does not exist spawn module build daemon
Expected<std::unique_ptr<raw_socket_stream>> MaybeClient =
- getModuleBuildDaemon(Argv0, BasePath, Diag);
+ getModuleBuildDaemon(Clang, Argv0, Diag, BasePath);
if (!MaybeClient) {
Diag.Report(diag::err_mbd_connect) << MaybeClient.takeError();
return;
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketMsgSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketMsgSupport.cpp
deleted file mode 100644
index a8c1f2954df288..00000000000000
--- a/clang/lib/Tooling/ModuleBuildDaemon/SocketMsgSupport.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-//===------------------------ SocketMsgSupport.cpp ------------------------===//
-//
-// 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 "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
-#include "clang/Basic/Version.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Tooling/ModuleBuildDaemon/Client.h"
-#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/ScopeExit.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Config/llvm-config.h"
-#include "llvm/Support/BLAKE3.h"
-
-#include <cerrno>
-#include <filesystem>
-#include <fstream>
-#include <signal.h>
-#include <spawn.h>
-#include <string>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-using namespace llvm;
-
-namespace clang::tooling::cc1modbuildd {
-
-Expected<std::unique_ptr<raw_socket_stream>>
-connectToSocket(StringRef SocketPath) {
-
- Expected<std::unique_ptr<raw_socket_stream>> MaybeClient =
- raw_socket_stream::createConnectedUnix(SocketPath);
- if (!MaybeClient)
- return std::move(MaybeClient.takeError());
-
- return std::move(*MaybeClient);
-}
-
-llvm::Error readFromSocket(raw_socket_stream &Connection,
- std::string &BufferConsumer) {
-
- char Buffer[MAX_BUFFER];
- ssize_t n;
-
- while ((n = Connection.read(Buffer, MAX_BUFFER)) > 0) {
- BufferConsumer.append(Buffer, n);
- // Read until \n... encountered (last line of YAML document)
- if (BufferConsumer.find("\n...") != std::string::npos)
- break;
- }
-
- if (n < 0) {
- // TODO: now that I am using raw_socket_stream look into if I still should
- // be using errno
- std::string Msg = "socket read error: " + std::string(strerror(errno));
- return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
- }
- if (n == 0)
- return llvm::make_error<StringError>("EOF", inconvertibleErrorCode());
- return llvm::Error::success();
-}
-
-// TODO: Need to add error handling for a write
-void writeToSocket(raw_socket_stream &Socket, llvm::StringRef Buffer) {
- Socket << Buffer;
- Socket.flush();
-}
-
-} // namespace clang::tooling::cc1modbuildd
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
new file mode 100644
index 00000000000000..a122ab26c125fa
--- /dev/null
+++ b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
@@ -0,0 +1,60 @@
+//===------------------------ SocketMsgSupport.cpp ------------------------===//
+//
+// 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 "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_socket_stream.h"
+
+#include <memory>
+#include <string>
+
+using namespace llvm;
+
+namespace clang::tooling::cc1modbuildd {
+
+Expected<std::unique_ptr<raw_socket_stream>>
+connectToSocket(StringRef SocketPath) {
+
+ Expected<std::unique_ptr<raw_socket_stream>> MaybeClient =
+ raw_socket_stream::createConnectedUnix(SocketPath);
+ if (!MaybeClient)
+ return std::move(MaybeClient.takeError());
+
+ return std::move(*MaybeClient);
+}
+
+Expected<std::string> readBufferFromSocket(raw_socket_stream &Socket) {
+
+ constexpr const unsigned short MAX_BUFFER = 4096;
+ char Buffer[MAX_BUFFER];
+ std::string ReturnBuffer;
+
+ ssize_t n = 0;
+ while ((n = Socket.read(Buffer, MAX_BUFFER)) > 0) {
+ ReturnBuffer.append(Buffer, n);
+ // Read until \n... encountered which is the last line of a YAML document
+ if (ReturnBuffer.find("\n...") != std::string::npos)
+ break;
+ }
+
+ if (Socket.has_error())
+ return make_error<StringError>("Failed socket read", Socket.error());
+ return ReturnBuffer;
+}
+
+Error writeBufferToSocket(raw_socket_stream &Socket, StringRef Buffer) {
+ Socket << Buffer;
+
+ if (Socket.has_error())
+ return make_error<StringError>("Failed socket write", Socket.error());
+
+ Socket.flush();
+ return Error::success();
+}
+
+} // namespace clang::tooling::cc1modbuildd
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
index dcef183a06c078..93fc14c4ba23dc 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
@@ -21,25 +21,6 @@ using namespace llvm;
namespace clang::tooling::cc1modbuildd {
-void writeError(llvm::Error Err, std::string Msg) {
- handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
- errs() << Msg << EIB.message() << '\n';
- });
-}
-
-std::string getFullErrorMsg(llvm::Error Err, std::string Msg) {
- std::string ErrMessage;
- handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
- ErrMessage = Msg + EIB.message();
- });
- return ErrMessage;
-}
-
-llvm::Error makeStringError(llvm::Error Err, std::string Msg) {
- std::string ErrMsg = getFullErrorMsg(std::move(Err), Msg);
- return llvm::make_error<StringError>(ErrMsg, inconvertibleErrorCode());
-}
-
std::string getBasePath() {
llvm::BLAKE3 Hash;
Hash.update(clang::getClangFullVersion());
@@ -56,4 +37,13 @@ std::string getBasePath() {
return BasePath.c_str();
}
+bool validBasePathLength(llvm::StringRef Address) {
+ // Confirm that the user provided BasePath is short enough to allow the socket
+ // address to fit within the alloted space
+ if (Address.str().length() > BASEPATH_MAX_LENGTH) {
+ return false;
+ }
+ return true;
+}
+
} // namespace clang::tooling::cc1modbuildd
\ No newline at end of file
diff --git a/clang/test/ModuleBuildDaemon/handshake.c b/clang/test/ModuleBuildDaemon/handshake.c
index 976cac47c1bcef..c29b3ee74e9dbc 100644
--- a/clang/test/ModuleBuildDaemon/handshake.c
+++ b/clang/test/ModuleBuildDaemon/handshake.c
@@ -1,3 +1,4 @@
+// Check that a clang invocation can spawn and handshake with a module build daemon
// REQUIRES: !system-windows
// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
@@ -7,7 +8,6 @@
//--- main.c
int main() {return 0;}
-// Add '|| true' to ensure RUN command never fails so that daemon shutdown command is always run
// RUN: %clang -fmodule-build-daemon=mbd-handshake -Rmodule-build-daemon %t/main.c &> %t/output-new || true
// RUN: %clang -fmodule-build-daemon=mbd-handshake -Rmodule-build-daemon %t/main.c &> %t/output-existing || true
// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
@@ -15,7 +15,6 @@ int main() {return 0;}
// RUN: cat %t/output-new | FileCheck %s
// RUN: cat %t/output-existing | FileCheck %s --check-prefix=CHECK-EXIST
-// Check that a clang invocation can spawn and handshake with a module build daemon
// CHECK: remark: Successfully spawned module build daemon [-Rmodule-build-daemon]
// CHECK: remark: Successfully connected to module build daemon at mbd-handshake/mbd.sock [-Rmodule-build-daemon]
// CHECK: remark: Successfully completed handshake with module build daemon [-Rmodule-build-daemon]
diff --git a/clang/test/ModuleBuildDaemon/launch.c b/clang/test/ModuleBuildDaemon/launch.c
index 48436ca7140ad3..9aea7bc46d4205 100644
--- a/clang/test/ModuleBuildDaemon/launch.c
+++ b/clang/test/ModuleBuildDaemon/launch.c
@@ -4,7 +4,7 @@
// RUN: rm -rf mbd-launch %t
// The module build daemon relies on a call to llvm::sys::ExecuteNoWait by the
-// frontend where Detached == true to be detached from the terminal so when
+// frontend where Detached = true to be detached from the terminal so when
// using -cc1modbuildd directly the command needs to be killed manually
// RUN: timeout --preserve-status --signal=SIGTERM 2 %clang -cc1modbuildd mbd-launch -v
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index 1161ffa6e5a602..b07801a2dbc21a 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -25,7 +25,7 @@
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
#include "clang/FrontendTool/Utils.h"
-#include "clang/Tooling/ModuleBuildDaemon/Client.h"
+#include "clang/Tooling/ModuleBuildDaemon/Frontend.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/LinkAllPasses.h"
@@ -292,7 +292,7 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
// Handle module build daemon functionality if enabled
if (Clang->getFrontendOpts().ModuleBuildDaemon) {
clang::tooling::cc1modbuildd::spawnModuleBuildDaemonAndHandshake(
- Clang->getInvocation(), Clang->getDiagnostics(), Argv0);
+ Clang->getInvocation(), Argv0, Clang->getDiagnostics());
}
// Execute the frontend actions.
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index ff1060a0bbcdba..77a9d3e20c4998 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -6,39 +6,27 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
+#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/Support/Program.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/ThreadPool.h"
-#include "llvm/Support/Threading.h"
-#include "llvm/Support/YAMLParser.h"
-#include "llvm/Support/YAMLTraits.h"
-using namespace llvm;
-using namespace clang::tooling::cc1modbuildd;
-
-#include <errno.h>
+#include <csignal>
+#include <cstdbool>
#include <fstream>
-#include <mutex>
#include <optional>
-#include <sstream>
-#include <stdbool.h>
#include <string>
-#include <type_traits>
-#include <unordered_map>
#ifdef _WIN32
#include <windows.h>
#else
-#include <signal.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/un.h>
#include <unistd.h>
#endif
+using namespace llvm;
+using namespace clang::tooling::cc1modbuildd;
+
// Create unbuffered STDOUT stream so that any logging done by the module build
// daemon can be viewed without having to terminate the process
static raw_fd_ostream &unbuff_outs() {
@@ -47,7 +35,7 @@ static raw_fd_ostream &unbuff_outs() {
}
static bool VerboseLog = false;
-static void printVerboseLog(const llvm::Twine &message) {
+static void verboseLog(const llvm::Twine &message) {
if (VerboseLog) {
unbuff_outs() << message << '\n';
}
@@ -74,7 +62,7 @@ class ModuleBuildDaemonServer {
int createDaemonSocket();
int listenForClients();
- static void handleClient(std::shared_ptr<raw_socket_stream> Connection);
+ static void handleConnection(std::shared_ptr<raw_socket_stream> Connection);
// TODO: modify so when shutdownDaemon is called the daemon stops accepting
// new client connections and waits for all existing client connections to
@@ -120,7 +108,7 @@ int ModuleBuildDaemonServer::setupDaemonEnv() {
return EXIT_SUCCESS;
}
-// Creates unix socket for IPC with module build daemon
+// Creates unix socket for IPC with frontends
int ModuleBuildDaemonServer::createDaemonSocket() {
Expected<ListeningSocket> MaybeServerListener =
@@ -129,58 +117,47 @@ int ModuleBuildDaemonServer::createDaemonSocket() {
if (llvm::Error Err = MaybeServerListener.takeError()) {
llvm::handleAllErrors(std::move(Err), [&](const llvm::StringError &SE) {
- // If the socket address is already in use, exit because another module
- // build daemon has successfully launched. When translation units are
- // compiled in parallel, until the socket file is created, all clang
- // invocations will try to spawn a module build daemon.
- if (std::error_code EC = SE.convertToErrorCode();
- EC == std::errc::address_in_use) {
+ std::error_code EC = SE.convertToErrorCode();
+ // Exit successfully if the socket address is already in use. When
+ // translation units are compiled in parallel, until the socket file is
+ // created, all clang invocations will try to spawn a module build daemon.
+ if (EC == std::errc::address_in_use) {
exit(EXIT_SUCCESS);
} else {
- llvm::errs() << "ERROR: " << EC.message() << '\n';
+ llvm::errs() << "MBD failed to create unix socket\n" << EC.message();
exit(EXIT_FAILURE);
}
});
}
- printVerboseLog("mbd created and binded to socket at: " + SocketPath);
+ verboseLog("mbd created and binded to socket at: " + SocketPath);
ServerListener.emplace(std::move(*MaybeServerListener));
return 0;
}
-#include <cstddef>
-
// Function submitted to thread pool with each frontend connection. Not
// responsible for closing frontend socket connections
-void ModuleBuildDaemonServer::handleClient(
+void ModuleBuildDaemonServer::handleConnection(
std::shared_ptr<llvm::raw_socket_stream> MovableConnection) {
llvm::raw_socket_stream &Connection = *MovableConnection;
- std::string Buffer;
-
- // Read handshake from client
- if (llvm::Error ReadErr = readFromSocket(Connection, Buffer)) {
- writeError(std::move(ReadErr), "Daemon failed to read buffer from socket");
- return;
- }
- // Wait for response from module build daemon
+ // Read request from frontend
Expected<HandshakeMsg> MaybeHandshakeMsg =
- getSocketMsgFromBuffer<HandshakeMsg>(Buffer);
+ readMsgStructFromSocket<HandshakeMsg>(Connection);
if (!MaybeHandshakeMsg) {
- writeError(MaybeHandshakeMsg.takeError(),
- "Failed to convert buffer to HandshakeMsg: ");
+ errs() << "MBD failed to read frontend request\n"
+ << toString(MaybeHandshakeMsg.takeError());
return;
}
- // Have received HandshakeMsg - send HandshakeMsg response to clang invocation
+ // Send response to frontend
HandshakeMsg Msg(ActionType::HANDSHAKE, StatusType::SUCCESS);
- if (llvm::Error WriteErr = writeSocketMsgToSocket(Connection, Msg)) {
- writeError(std::move(WriteErr),
- "Failed to notify client that handshake was received");
+ if (llvm::Error WriteErr = writeMsgStructToSocket(Connection, Msg)) {
+ errs() << "MBD failed to respond to frontend request\n"
+ << toString(std::move(WriteErr));
return;
}
-
return;
}
@@ -191,15 +168,14 @@ int ModuleBuildDaemonServer::listenForClients() {
Expected<std::unique_ptr<raw_socket_stream>> MaybeConnection =
ServerListener.value().accept();
-
if (llvm::Error Err = MaybeConnection.takeError()) {
- llvm::handleAllErrors(std::move(Err), [&](const llvm::StringError &SE) {
- llvm::errs() << "ERROR: " << SE.getMessage() << '\n';
- });
+ errs() << "MBD failed to accept incoming connection\n"
+ << toString(std::move(Err));
continue;
}
+
std::shared_ptr<raw_socket_stream> Connection(std::move(*MaybeConnection));
- Pool.async(handleClient, Connection);
+ Pool.async(handleConnection, Connection);
}
return 0;
}
@@ -217,37 +193,25 @@ int ModuleBuildDaemonServer::listenForClients() {
// Provides verbose debug information.
//
// NOTES
-// The arguments <path> and -v are optional. By default <path> follows the
-// format: /tmp/clang-<BLAKE3HashOfClangFullVersion>.
+// The arguments <path> and -v are optional. If <path> is not provided then
+// BasePath will be /tmp/clang-<BLAKE3HashOfClangFullVersion>
//
int cc1modbuildd_main(ArrayRef<const char *> Argv) {
- std::string BasePath;
- // command line argument parsing. -cc1modbuildd is sliced away when passing
- // Argv to cc1modbuildd_main
- if (!Argv.empty()) {
+ // -cc1modbuildd is sliced away when Argv is pased to cc1modbuildd_main
+ if (find(Argv, StringRef("-v")) != Argv.end())
+ VerboseLog = true;
- if (find(Argv, StringRef("-v")) != Argv.end())
- VerboseLog = true;
-
- if (strcmp(Argv[0], "-v") != 0)
- BasePath = Argv[0];
- else
- BasePath = getBasePath();
-
- } else {
+ std::string BasePath;
+ // If an argument exists and it is not -v then it must be a BasePath
+ if (!Argv.empty() && strcmp(Argv[0], "-v") != 0)
+ BasePath = Argv[0];
+ else
BasePath = getBasePath();
- }
- // TODO: Max length may vary across different platforms. Incoming llvm/Support
- // for sockets will help make this portable. On most unix platforms a socket
- // address cannot be over 108 characters. The socket file, mbd.sock, takes up
- // 8 characters leaving 100 characters left for the user/system
- const int MAX_ADDR = 108;
- if (BasePath.length() >= MAX_ADDR - std::string(SOCKET_FILE_NAME).length()) {
- errs() << "Socket path '" + BasePath +
- "' is too long. Socket path much be equal to or less then "
- "100 characters. Module build daemon will not be spawned.";
+ if (!validBasePathLength(BasePath)) {
+ errs() << "BasePath '" << BasePath << "' is longer then the max length of "
+ << std::to_string(BASEPATH_MAX_LENGTH) << '\n';
return 1;
}
>From fa7d4f6c5aa44fcda2de461d09af58c48346f2a9 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Thu, 4 Jan 2024 13:02:45 -0500
Subject: [PATCH 12/27] Fix windows build issue
---
.../clang/Tooling/ModuleBuildDaemon/Utils.h | 9 +++++++++
clang/tools/driver/cc1modbuildd_main.cpp | 17 ++++++++++-------
clang/tools/driver/driver.cpp | 2 --
3 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
index 643a1ec8bd32ee..d6eb06b8a021dd 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
@@ -16,7 +16,16 @@
#include "llvm/Support/Error.h"
#include <string>
+
+#ifdef _WIN32
+// winsock2.h must be included before afunix.h
+// clang-format off
+#include <winsock2.h>
+#include <afunix.h>
+// clang-format on
+#else
#include <sys/un.h>
+#endif
namespace clang::tooling::cc1modbuildd {
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index 77a9d3e20c4998..e78ed9ca46f330 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -10,6 +10,7 @@
#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/Signals.h"
#include "llvm/Support/ThreadPool.h"
#include <csignal>
@@ -76,9 +77,8 @@ class ModuleBuildDaemonServer {
std::optional<llvm::ListeningSocket> ServerListener;
};
-// Required to handle SIGTERM by calling Shutdown
ModuleBuildDaemonServer *DaemonPtr = nullptr;
-void handleSignal(int Signal) {
+void handleSignal() {
if (DaemonPtr != nullptr) {
DaemonPtr->shutdownDaemon();
}
@@ -97,14 +97,17 @@ int ModuleBuildDaemonServer::setupDaemonEnv() {
freopen(STDOUT.c_str(), "a", stdout);
freopen(STDERR.c_str(), "a", stderr);
- if (signal(SIGTERM, handleSignal) == SIG_ERR) {
- errs() << "failed to handle SIGTERM" << '\n';
- exit(EXIT_FAILURE);
- }
+ llvm::sys::SetInterruptFunction(handleSignal);
+
+// TODO: Figure out how to do this on windows
+#ifdef SIGHUP
+ // Overides llvm::sys::SetInterruptFunction
if (signal(SIGHUP, SIG_IGN) == SIG_ERR) {
- errs() << "failed to ignore SIGHUP" << '\n';
+ errs() << "failed to handle SIGHUP" << '\n';
exit(EXIT_FAILURE);
}
+#endif
+
return EXIT_SUCCESS;
}
diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp
index 7413eb5aa9c18b..48c480ca7f9b60 100644
--- a/clang/tools/driver/driver.cpp
+++ b/clang/tools/driver/driver.cpp
@@ -212,9 +212,7 @@ extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv,
const char *Argv0, void *MainAddr,
const llvm::ToolContext &);
-#if LLVM_ON_UNIX
extern int cc1modbuildd_main(ArrayRef<const char *> Argv);
-#endif
static void insertTargetAndModeArgs(const ParsedClangName &NameParts,
SmallVectorImpl<const char *> &ArgVector,
>From c7b1be9adc775eda5149ab48f5cbfa4439ebbbda Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Fri, 5 Jan 2024 00:51:54 -0500
Subject: [PATCH 13/27] Address feedback
- Remove unecessary string_view construct
- Add LLVM_PREFERRED_TYPE(bool) to ModuleBuildDaemon
- Remove unnecessary const when specifing constexpr
---
clang/include/clang/Frontend/FrontendOptions.h | 3 ++-
.../clang/Tooling/ModuleBuildDaemon/Utils.h | 14 +++++++-------
clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp | 4 ++--
3 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index 8bdbb69e720e60..607f7a21cb9702 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -383,7 +383,8 @@ class FrontendOptions {
LLVM_PREFERRED_TYPE(bool)
unsigned ModulesShareFileManager : 1;
- /// Connect to module build daemon
+ /// Connect to module build daemon.
+ LLVM_PREFERRED_TYPE(bool)
unsigned ModuleBuildDaemon : 1;
CodeCompleteOptions CodeCompleteOpts;
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
index d6eb06b8a021dd..6482db82716bf1 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
@@ -29,16 +29,16 @@
namespace clang::tooling::cc1modbuildd {
-constexpr const std::string_view SOCKET_FILE_NAME = "mbd.sock";
-constexpr const std::string_view STDOUT_FILE_NAME = "mbd.out";
-constexpr const std::string_view STDERR_FILE_NAME = "mbd.err";
-constexpr const std::string_view MODULE_BUILD_DAEMON_FLAG = "-cc1modbuildd";
+constexpr std::string_view SOCKET_FILE_NAME = "mbd.sock";
+constexpr std::string_view STDOUT_FILE_NAME = "mbd.out";
+constexpr std::string_view STDERR_FILE_NAME = "mbd.err";
+constexpr std::string_view MODULE_BUILD_DAEMON_FLAG = "-cc1modbuildd";
// A llvm::raw_socket_stream uses sockaddr_un
-constexpr const size_t SOCKET_ADDR_MAX_LENGTH = sizeof(sockaddr_un::sun_path);
+constexpr size_t SOCKET_ADDR_MAX_LENGTH = sizeof(sockaddr_un::sun_path);
-constexpr const size_t BASEPATH_MAX_LENGTH =
- SOCKET_ADDR_MAX_LENGTH - std::string_view(SOCKET_FILE_NAME).length();
+constexpr size_t BASEPATH_MAX_LENGTH =
+ SOCKET_ADDR_MAX_LENGTH - SOCKET_FILE_NAME.length();
// Get a temprary location where the daemon can store log files and a socket
// address. Of the format /tmp/clang-<BLAKE3HashOfClangFullVersion>/
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp
index ac31dba06b4dc1..8b5331366ba643 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp
@@ -98,8 +98,8 @@ getModuleBuildDaemon(const CompilerInvocation &Clang, const char *Argv0,
spawnModuleBuildDaemon(Clang, Argv0, Diag, BasePath.str()))
return std::move(Err);
- constexpr const unsigned int MICROSEC_IN_SEC = 1000000;
- constexpr const unsigned int MAX_WAIT_TIME = 30 * MICROSEC_IN_SEC;
+ constexpr unsigned int MICROSEC_IN_SEC = 1000000;
+ constexpr unsigned int MAX_WAIT_TIME = 30 * MICROSEC_IN_SEC;
unsigned int CumulativeTime = 0;
unsigned int WaitTime = 10;
>From d369eca0328ab82a1ee05a3f8fa9480a963d4af9 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Fri, 5 Jan 2024 11:39:00 -0500
Subject: [PATCH 14/27] Add NOMINMAX to windows build
---
clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
index 6482db82716bf1..461b850ce08255 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
@@ -18,6 +18,9 @@
#include <string>
#ifdef _WIN32
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
// winsock2.h must be included before afunix.h
// clang-format off
#include <winsock2.h>
>From f8ffcf819bdd6503d2f8d5d82c38450df43817cd Mon Sep 17 00:00:00 2001
From: Connor Sughrue <cpsughrue at gmail.com>
Date: Tue, 23 Jan 2024 01:38:36 -0500
Subject: [PATCH 15/27] windows support
---
.../Tooling/ModuleBuildDaemon/Frontend.cpp | 2 +-
clang/test/ModuleBuildDaemon/handshake.c | 4 +-
clang/test/ModuleBuildDaemon/launch.c | 17 ++-
clang/tools/driver/cc1modbuildd_main.cpp | 47 ++++--
llvm/include/llvm/Support/raw_socket_stream.h | 5 +-
llvm/lib/Support/raw_socket_stream.cpp | 141 ++++++++++++------
6 files changed, 149 insertions(+), 67 deletions(-)
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp
index 8b5331366ba643..cf73e27f246bba 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp
@@ -123,7 +123,7 @@ getModuleBuildDaemon(const CompilerInvocation &Clang, const char *Argv0,
// After waiting around 30 seconds give up and return an error
return llvm::make_error<StringError>(
- "Could not connect to module build daemon",
+ "Max wait time exceeded: ",
std::make_error_code(std::errc::no_such_process));
}
diff --git a/clang/test/ModuleBuildDaemon/handshake.c b/clang/test/ModuleBuildDaemon/handshake.c
index c29b3ee74e9dbc..de75c3a7dd6274 100644
--- a/clang/test/ModuleBuildDaemon/handshake.c
+++ b/clang/test/ModuleBuildDaemon/handshake.c
@@ -12,8 +12,8 @@ int main() {return 0;}
// RUN: %clang -fmodule-build-daemon=mbd-handshake -Rmodule-build-daemon %t/main.c &> %t/output-existing || true
// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
-// RUN: cat %t/output-new | FileCheck %s
-// RUN: cat %t/output-existing | FileCheck %s --check-prefix=CHECK-EXIST
+// RUN: cat %t/output-new | sed 's:\\\\\?:/:g' | FileCheck %s
+// RUN: cat %t/output-existing | sed 's:\\\\\?:/:g' | FileCheck %s --check-prefix=CHECK-EXIST
// CHECK: remark: Successfully spawned module build daemon [-Rmodule-build-daemon]
// CHECK: remark: Successfully connected to module build daemon at mbd-handshake/mbd.sock [-Rmodule-build-daemon]
diff --git a/clang/test/ModuleBuildDaemon/launch.c b/clang/test/ModuleBuildDaemon/launch.c
index 9aea7bc46d4205..6b0946f8388dfd 100644
--- a/clang/test/ModuleBuildDaemon/launch.c
+++ b/clang/test/ModuleBuildDaemon/launch.c
@@ -1,13 +1,18 @@
// Check that the module build daemon can create a unix socket
-// REQUIRES: !system-windows
// RUN: rm -rf mbd-launch %t
-// The module build daemon relies on a call to llvm::sys::ExecuteNoWait by the
-// frontend where Detached = true to be detached from the terminal so when
-// using -cc1modbuildd directly the command needs to be killed manually
+// timeout should exit with status 124 which is treated as a failure on
+// windows. Ideally we would be like to check the exit code and only return true
+// if it equals 124 but global bash sysmbols like $? are not surported by lit
-// RUN: timeout --preserve-status --signal=SIGTERM 2 %clang -cc1modbuildd mbd-launch -v
-// RUN: cat mbd-launch/mbd.out | FileCheck %s
+// RUN: timeout --signal=SIGTERM 2 %clang -cc1modbuildd mbd-launch -v || true
+// RUN: cat mbd-launch/mbd.out | sed 's:\\\\\?:/:g' | FileCheck %s
// CHECK: mbd created and binded to socket at: mbd-launch/mbd.sock
+
+// Make sure socket file is removed when daemon exits
+// [ ! -f "mbd-launch/mbd.socker" ]
+
+// Make sure mbd.err is empty
+// RUN: [ ! -s "mbd-launch/mbd.err" ]
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index e78ed9ca46f330..aa1dd83cba4ca2 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -18,6 +18,7 @@
#include <fstream>
#include <optional>
#include <string>
+#include <system_error>
#ifdef _WIN32
#include <windows.h>
@@ -68,12 +69,10 @@ class ModuleBuildDaemonServer {
// TODO: modify so when shutdownDaemon is called the daemon stops accepting
// new client connections and waits for all existing client connections to
// terminate before closing the file descriptor and exiting
- void shutdownDaemon() {
- ServerListener.value().~ListeningSocket();
- exit(EXIT_SUCCESS);
- }
+ void shutdownDaemon() { RunServiceLoop = false; }
private:
+ bool RunServiceLoop = true;
std::optional<llvm::ListeningSocket> ServerListener;
};
@@ -118,7 +117,6 @@ int ModuleBuildDaemonServer::createDaemonSocket() {
llvm::ListeningSocket::createUnix(SocketPath);
if (llvm::Error Err = MaybeServerListener.takeError()) {
-
llvm::handleAllErrors(std::move(Err), [&](const llvm::StringError &SE) {
std::error_code EC = SE.convertToErrorCode();
// Exit successfully if the socket address is already in use. When
@@ -127,7 +125,8 @@ int ModuleBuildDaemonServer::createDaemonSocket() {
if (EC == std::errc::address_in_use) {
exit(EXIT_SUCCESS);
} else {
- llvm::errs() << "MBD failed to create unix socket\n" << EC.message();
+ llvm::errs() << "MBD failed to create unix socket: " << SE.message()
+ << EC.message() << '\n';
exit(EXIT_FAILURE);
}
});
@@ -149,16 +148,16 @@ void ModuleBuildDaemonServer::handleConnection(
Expected<HandshakeMsg> MaybeHandshakeMsg =
readMsgStructFromSocket<HandshakeMsg>(Connection);
if (!MaybeHandshakeMsg) {
- errs() << "MBD failed to read frontend request\n"
- << toString(MaybeHandshakeMsg.takeError());
+ errs() << "MBD failed to read frontend request: "
+ << llvm::toString(MaybeHandshakeMsg.takeError()) << '\n';
return;
}
// Send response to frontend
HandshakeMsg Msg(ActionType::HANDSHAKE, StatusType::SUCCESS);
if (llvm::Error WriteErr = writeMsgStructToSocket(Connection, Msg)) {
- errs() << "MBD failed to respond to frontend request\n"
- << toString(std::move(WriteErr));
+ errs() << "MBD failed to respond to frontend request: "
+ << llvm::toString(std::move(WriteErr)) << '\n';
return;
}
return;
@@ -166,17 +165,37 @@ void ModuleBuildDaemonServer::handleConnection(
int ModuleBuildDaemonServer::listenForClients() {
+ auto StartTime = std::chrono::steady_clock::now();
llvm::ThreadPool Pool;
- while (true) {
+ while (RunServiceLoop) {
Expected<std::unique_ptr<raw_socket_stream>> MaybeConnection =
- ServerListener.value().accept();
+ ServerListener.value().accept(/*Block*/ false);
+
if (llvm::Error Err = MaybeConnection.takeError()) {
- errs() << "MBD failed to accept incoming connection\n"
- << toString(std::move(Err));
+ llvm::handleAllErrors(std::move(Err), [&](const llvm::StringError &SE) {
+ std::error_code EC = SE.convertToErrorCode();
+#ifdef _WIN32
+ if (EC.value() != WSAEWOULDBLOCK) {
+#else
+ if (EC != std::errc::operation_would_block || EC != std::errc::resource_unavailable_try_again) {
+#endif
+ errs() << "MBD failed to accept incoming connection: "
+ << SE.getMessage() << EC.message() << '\n';
+ }
+ });
+
+ auto CurrentTime = std::chrono::steady_clock::now();
+ auto ElapsedTime = std::chrono::duration_cast<std::chrono::seconds>(
+ CurrentTime - StartTime);
+ if (ElapsedTime.count() >= 15)
+ shutdownDaemon();
+
continue;
}
+ // Successfull connection - restart timer
+ StartTime = std::chrono::steady_clock::now();
std::shared_ptr<raw_socket_stream> Connection(std::move(*MaybeConnection));
Pool.async(handleConnection, Connection);
}
diff --git a/llvm/include/llvm/Support/raw_socket_stream.h b/llvm/include/llvm/Support/raw_socket_stream.h
index c219792d82465d..750b80e86247ce 100644
--- a/llvm/include/llvm/Support/raw_socket_stream.h
+++ b/llvm/include/llvm/Support/raw_socket_stream.h
@@ -42,15 +42,16 @@ class ListeningSocket {
static Expected<ListeningSocket> createUnix(
StringRef SocketPath,
int MaxBacklog = llvm::hardware_concurrency().compute_thread_count());
- Expected<std::unique_ptr<raw_socket_stream>> accept();
+ Expected<std::unique_ptr<raw_socket_stream>> accept(bool Block = true);
ListeningSocket(ListeningSocket &&LS);
~ListeningSocket();
};
+
class raw_socket_stream : public raw_fd_stream {
uint64_t current_pos() const override { return 0; }
#ifdef _WIN32
WSABalancer _;
-#endif // _WIN32
+#endif
public:
raw_socket_stream(int SocketFD);
diff --git a/llvm/lib/Support/raw_socket_stream.cpp b/llvm/lib/Support/raw_socket_stream.cpp
index a65865bcede12d..ea7cbe7e1408bf 100644
--- a/llvm/lib/Support/raw_socket_stream.cpp
+++ b/llvm/lib/Support/raw_socket_stream.cpp
@@ -16,12 +16,12 @@
#include "llvm/Support/Error.h"
#ifndef _WIN32
+#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#else
#include "llvm/Support/Windows/WindowsSupport.h"
-// winsock2.h must be included before afunix.h. Briefly turn off clang-format to
-// avoid error.
+// winsock2.h must be included before afunix.h
// clang-format off
#include <winsock2.h>
#include <afunix.h>
@@ -45,7 +45,6 @@ WSABalancer::WSABalancer() {
}
WSABalancer::~WSABalancer() { WSACleanup(); }
-
#endif // _WIN32
static std::error_code getLastSocketErrorCode() {
@@ -62,6 +61,7 @@ ListeningSocket::ListeningSocket(int SocketFD, StringRef SocketPath)
ListeningSocket::ListeningSocket(ListeningSocket &&LS)
: FD(LS.FD), SocketPath(LS.SocketPath) {
LS.FD = -1;
+ LS.SocketPath.clear();
}
Expected<ListeningSocket> ListeningSocket::createUnix(StringRef SocketPath,
@@ -69,52 +69,110 @@ Expected<ListeningSocket> ListeningSocket::createUnix(StringRef SocketPath,
#ifdef _WIN32
WSABalancer _;
- SOCKET MaybeWinsocket = socket(AF_UNIX, SOCK_STREAM, 0);
- if (MaybeWinsocket == INVALID_SOCKET) {
+ SOCKET MaybeSocket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (MaybeSocket == INVALID_SOCKET) {
#else
- int MaybeWinsocket = socket(AF_UNIX, SOCK_STREAM, 0);
- if (MaybeWinsocket == -1) {
+ int MaybeSocket = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (MaybeSocket == -1) {
#endif
- return llvm::make_error<StringError>(getLastSocketErrorCode(),
- "socket create failed");
+ return llvm::make_error<StringError>("Socket create failed: ",
+ getLastSocketErrorCode());
}
struct sockaddr_un Addr;
- memset(&Addr, 0, sizeof(Addr));
+ ::memset(&Addr, 0, sizeof(Addr));
Addr.sun_family = AF_UNIX;
- strncpy(Addr.sun_path, SocketPath.str().c_str(), sizeof(Addr.sun_path) - 1);
+ ::strncpy(Addr.sun_path, SocketPath.str().c_str(), sizeof(Addr.sun_path) - 1);
- if (bind(MaybeWinsocket, (struct sockaddr *)&Addr, sizeof(Addr)) == -1) {
- std::error_code Err = getLastSocketErrorCode();
- if (Err == std::errc::address_in_use)
- ::close(MaybeWinsocket);
- return llvm::make_error<StringError>(Err, "Bind error");
+ if (::bind(MaybeSocket, (struct sockaddr *)&Addr, sizeof(Addr)) == -1) {
+ ::close(MaybeSocket);
+ return llvm::make_error<StringError>("Bind error: ",
+ getLastSocketErrorCode());
}
- if (listen(MaybeWinsocket, MaxBacklog) == -1) {
- return llvm::make_error<StringError>(getLastSocketErrorCode(),
- "Listen error");
+
+ if (::listen(MaybeSocket, MaxBacklog) == -1) {
+ return llvm::make_error<StringError>("Listen error: ",
+ getLastSocketErrorCode());
+ ::close(MaybeSocket);
}
+
int UnixSocket;
#ifdef _WIN32
- UnixSocket = _open_osfhandle(MaybeWinsocket, 0);
+ UnixSocket = _open_osfhandle(MaybeSocket, 0);
#else
- UnixSocket = MaybeWinsocket;
+ UnixSocket = MaybeSocket;
#endif // _WIN32
return ListeningSocket{UnixSocket, SocketPath};
}
-Expected<std::unique_ptr<raw_socket_stream>> ListeningSocket::accept() {
- int AcceptFD;
+Expected<std::unique_ptr<raw_socket_stream>>
+ListeningSocket::accept(bool Block) {
+
+ std::error_code AcceptEC;
+
#ifdef _WIN32
SOCKET WinServerSock = _get_osfhandle(FD);
+ if (WinServerSock == INVALID_SOCKET)
+ return llvm::make_error<StringError>("Failed to get file handle: ",
+ getLastSocketErrorCode());
+
+ // Set to non-blocking if required
+ if (!Block) {
+ u_long BlockingMode = 1;
+ if (ioctlsocket(WinServerSock, FIONBIO, &BlockingMode) == SOCKET_ERROR)
+ return llvm::make_error<StringError>(
+ "Failed to set socket to non-blocking: ", getLastSocketErrorCode());
+ }
+
SOCKET WinAcceptSock = ::accept(WinServerSock, NULL, NULL);
- AcceptFD = _open_osfhandle(WinAcceptSock, 0);
+ if (WinAcceptSock == INVALID_SOCKET)
+ AcceptEC = getLastSocketErrorCode();
+
+ // Restore to blocking if required
+ if (!Block) {
+ u_long BlockingMode = 0;
+ if (ioctlsocket(WinServerSock, FIONBIO, &BlockingMode) == SOCKET_ERROR)
+ return llvm::make_error<StringError>(
+ "Failed to reset socket to blocking: ", getLastSocketErrorCode());
+ }
+
+ if (WinAcceptSock == INVALID_SOCKET)
+ return llvm::make_error<StringError>("Accept Failed: ", AcceptEC);
+
+ int AcceptFD = _open_osfhandle(WinAcceptSock, 0);
+ if (AcceptFD == -1)
+ return llvm::make_error<StringError>(
+ "Failed to get file descriptor from handle: ",
+ getLastSocketErrorCode());
#else
- AcceptFD = ::accept(FD, NULL, NULL);
-#endif //_WIN32
+ int Flags = ::fcntl(FD, F_GETFL, 0);
+ if (Flags == -1)
+ return llvm::make_error<StringError>(
+ "Failed to get file descriptor flags: ", getLastSocketErrorCode());
+
+ // Set to non-blocking if required
+ if (!Block) {
+ if (::fcntl(FD, F_SETFL, Flags | O_NONBLOCK) == -1)
+ return llvm::make_error<StringError>(
+ "Failed to set socket to non-blocking: ", getLastSocketErrorCode());
+ }
+
+ int AcceptFD = ::accept(FD, NULL, NULL);
if (AcceptFD == -1)
- return llvm::make_error<StringError>(getLastSocketErrorCode(),
- "Accept failed");
+ AcceptEC = getLastSocketErrorCode();
+
+ // Restore to blocking if required
+ if (!Block) {
+ if (::fcntl(FD, F_SETFL, Flags) == -1)
+ return llvm::make_error<StringError>(
+ "Failed to reset socket to blocking: ", getLastSocketErrorCode());
+ }
+
+ if (AcceptFD == -1)
+ return llvm::make_error<StringError>("Accept Failed: ", AcceptEC);
+
+#endif //_WIN32
+
return std::make_unique<raw_socket_stream>(AcceptFD);
}
@@ -122,19 +180,19 @@ ListeningSocket::~ListeningSocket() {
if (FD == -1)
return;
::close(FD);
- unlink(SocketPath.c_str());
+ ::unlink(SocketPath.c_str());
}
static Expected<int> GetSocketFD(StringRef SocketPath) {
#ifdef _WIN32
- SOCKET MaybeWinsocket = socket(AF_UNIX, SOCK_STREAM, 0);
- if (MaybeWinsocket == INVALID_SOCKET) {
+ SOCKET MaybeSocket = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (MaybeSocket == INVALID_SOCKET) {
#else
- int MaybeWinsocket = socket(AF_UNIX, SOCK_STREAM, 0);
- if (MaybeWinsocket == -1) {
+ int MaybeSocket = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (MaybeSocket == -1) {
#endif // _WIN32
- return llvm::make_error<StringError>(getLastSocketErrorCode(),
- "Create socket failed");
+ return llvm::make_error<StringError>("Create socket failed: ",
+ getLastSocketErrorCode());
}
struct sockaddr_un Addr;
@@ -142,15 +200,14 @@ static Expected<int> GetSocketFD(StringRef SocketPath) {
Addr.sun_family = AF_UNIX;
strncpy(Addr.sun_path, SocketPath.str().c_str(), sizeof(Addr.sun_path) - 1);
- int status = connect(MaybeWinsocket, (struct sockaddr *)&Addr, sizeof(Addr));
- if (status == -1) {
- return llvm::make_error<StringError>(getLastSocketErrorCode(),
- "Connect socket failed");
- }
+ if (::connect(MaybeSocket, (struct sockaddr *)&Addr, sizeof(Addr)) == -1)
+ return llvm::make_error<StringError>("Connect socket failed: ",
+ getLastSocketErrorCode());
+
#ifdef _WIN32
- return _open_osfhandle(MaybeWinsocket, 0);
+ return _open_osfhandle(MaybeSocket, 0);
#else
- return MaybeWinsocket;
+ return MaybeSocket;
#endif // _WIN32
}
>From a5d4e6b269c0d2e17d74fb39e97c8a758d356c96 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Tue, 23 Jan 2024 22:30:57 -0500
Subject: [PATCH 16/27] Fix signal handling to use std::signal
---
clang/test/ModuleBuildDaemon/launch.c | 10 +++++-----
clang/tools/driver/cc1modbuildd_main.cpp | 22 ++++++++++++++--------
2 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/clang/test/ModuleBuildDaemon/launch.c b/clang/test/ModuleBuildDaemon/launch.c
index 6b0946f8388dfd..2bfea6aec9dccd 100644
--- a/clang/test/ModuleBuildDaemon/launch.c
+++ b/clang/test/ModuleBuildDaemon/launch.c
@@ -2,17 +2,17 @@
// RUN: rm -rf mbd-launch %t
-// timeout should exit with status 124 which is treated as a failure on
+// timeout should exit with status 124 which is treated as a failure by lit on
// windows. Ideally we would be like to check the exit code and only return true
-// if it equals 124 but global bash sysmbols like $? are not surported by lit
+// if it equals 124 but lit does not support global bash sysmbols like $?
// RUN: timeout --signal=SIGTERM 2 %clang -cc1modbuildd mbd-launch -v || true
// RUN: cat mbd-launch/mbd.out | sed 's:\\\\\?:/:g' | FileCheck %s
// CHECK: mbd created and binded to socket at: mbd-launch/mbd.sock
-// Make sure socket file is removed when daemon exits
-// [ ! -f "mbd-launch/mbd.socker" ]
+// Make sure mbd.sock does not exist
+// RUN: [ ! -f "mbd-launch/mbd.sock" ] && true || false
// Make sure mbd.err is empty
-// RUN: [ ! -s "mbd-launch/mbd.err" ]
+// RUN: [ ! -s "mbd-launch/mbd.err" ] && true || false
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index aa1dd83cba4ca2..1d7adb369157f3 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -22,8 +22,6 @@
#ifdef _WIN32
#include <windows.h>
-#else
-#include <unistd.h>
#endif
using namespace llvm;
@@ -77,10 +75,11 @@ class ModuleBuildDaemonServer {
};
ModuleBuildDaemonServer *DaemonPtr = nullptr;
-void handleSignal() {
- if (DaemonPtr != nullptr) {
+void handleSignal(int) {
+ if (DaemonPtr != nullptr)
DaemonPtr->shutdownDaemon();
- }
+ else
+ exit(EXIT_SUCCESS);
}
} // namespace
@@ -96,12 +95,19 @@ int ModuleBuildDaemonServer::setupDaemonEnv() {
freopen(STDOUT.c_str(), "a", stdout);
freopen(STDERR.c_str(), "a", stderr);
- llvm::sys::SetInterruptFunction(handleSignal);
+ if (std::signal(SIGTERM, handleSignal) == SIG_ERR) {
+ errs() << "failed to handle SIGTERM" << '\n';
+ exit(EXIT_FAILURE);
+ }
+
+ if (std::signal(SIGINT, handleSignal) == SIG_ERR) {
+ errs() << "failed to handle SIGINT" << '\n';
+ exit(EXIT_FAILURE);
+ }
// TODO: Figure out how to do this on windows
#ifdef SIGHUP
- // Overides llvm::sys::SetInterruptFunction
- if (signal(SIGHUP, SIG_IGN) == SIG_ERR) {
+ if (::signal(SIGHUP, SIG_IGN) == SIG_ERR) {
errs() << "failed to handle SIGHUP" << '\n';
exit(EXIT_FAILURE);
}
>From ffa6dc3102e6c7c655a87f6b541a9d3b75109673 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Wed, 24 Jan 2024 03:04:37 -0500
Subject: [PATCH 17/27] Write lit macro for killing process
---
clang/test/ModuleBuildDaemon/handshake.c | 8 +++---
clang/test/lit.cfg.py | 11 ++++++++
clang/utils/kill_process.py | 35 ++++++++++++++++++++++++
3 files changed, 50 insertions(+), 4 deletions(-)
create mode 100644 clang/utils/kill_process.py
diff --git a/clang/test/ModuleBuildDaemon/handshake.c b/clang/test/ModuleBuildDaemon/handshake.c
index de75c3a7dd6274..2397558b366479 100644
--- a/clang/test/ModuleBuildDaemon/handshake.c
+++ b/clang/test/ModuleBuildDaemon/handshake.c
@@ -1,16 +1,16 @@
// Check that a clang invocation can spawn and handshake with a module build daemon
// REQUIRES: !system-windows
-// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
-// RUN: rm -rf mbd-handshake %t
-// RUN: split-file %s %t
+// RUN: %kill-process "-cc1modbuildd mbd-handshake"
+// RUN: rm -rf mbd-handshake %t
+// RUN: split-file %s %t
//--- main.c
int main() {return 0;}
// RUN: %clang -fmodule-build-daemon=mbd-handshake -Rmodule-build-daemon %t/main.c &> %t/output-new || true
// RUN: %clang -fmodule-build-daemon=mbd-handshake -Rmodule-build-daemon %t/main.c &> %t/output-existing || true
-// RUN: if pgrep -f "cc1modbuildd mbd-handshake"; then pkill -f "cc1modbuildd mbd-handshake"; fi
+// RUN: %kill-process "-cc1modbuildd mbd-handshake"
// RUN: cat %t/output-new | sed 's:\\\\\?:/:g' | FileCheck %s
// RUN: cat %t/output-existing | sed 's:\\\\\?:/:g' | FileCheck %s --check-prefix=CHECK-EXIST
diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py
index 271372b928ac55..7a51d278da6577 100644
--- a/clang/test/lit.cfg.py
+++ b/clang/test/lit.cfg.py
@@ -203,6 +203,17 @@ def have_host_clang_repl_cuda():
)
)
+config.substitutions.append(
+ (
+ "%kill-process",
+ '"%s" %s'
+ % (
+ config.python_executable,
+ os.path.join(config.clang_src_dir, "utils", "kill_process.py"),
+ ),
+ )
+)
+
config.substitutions.append(("%host_cc", config.host_cc))
config.substitutions.append(("%host_cxx", config.host_cxx))
diff --git a/clang/utils/kill_process.py b/clang/utils/kill_process.py
new file mode 100644
index 00000000000000..2da54092c7c47c
--- /dev/null
+++ b/clang/utils/kill_process.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+
+# Kills all processes whos command line match provided pattern
+
+import os
+import psutil
+import signal
+import sys
+
+
+def main():
+ if len(sys.argv) == 1:
+ sys.stderr.write("Error: no search pattern provided")
+ sys.exit(1)
+
+ search_pattern = sys.argv[1]
+
+ for process in psutil.process_iter(['pid', 'name', 'cmdline']):
+ if 'clang' in process.info['name']:
+
+ PID = []
+ if search_pattern in ' '.join(process.info['cmdline']):
+ PID.append(process.info['pid'])
+
+ if len(PID) == 0:
+ return
+
+ if len(PID) > 1:
+ sys.stderr.write("Error: more then one process matches search pattern")
+
+ os.kill(PID[0], signal.SIGTERM)
+
+
+if __name__ == "__main__":
+ main()
>From 6ca8b2023c3a336b006cb94311d0fdcc1c4dc6c9 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Wed, 24 Jan 2024 03:17:20 -0500
Subject: [PATCH 18/27] Remove unix requirement from handshake test
---
clang/test/ModuleBuildDaemon/handshake.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/test/ModuleBuildDaemon/handshake.c b/clang/test/ModuleBuildDaemon/handshake.c
index 2397558b366479..059ecf6fae188c 100644
--- a/clang/test/ModuleBuildDaemon/handshake.c
+++ b/clang/test/ModuleBuildDaemon/handshake.c
@@ -1,5 +1,4 @@
// Check that a clang invocation can spawn and handshake with a module build daemon
-// REQUIRES: !system-windows
// RUN: %kill-process "-cc1modbuildd mbd-handshake"
// RUN: rm -rf mbd-handshake %t
>From 0f3236077b6cab991df6006808691cd70ec4c044 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Wed, 24 Jan 2024 03:21:00 -0500
Subject: [PATCH 19/27] Correct handling of address in use error during bind
---
.../Tooling/ModuleBuildDaemon/SocketSupport.h | 3 +-
.../clang/Tooling/ModuleBuildDaemon/Utils.h | 3 +
.../ModuleBuildDaemon/SocketSupport.cpp | 16 ++++--
clang/tools/driver/cc1modbuildd_main.cpp | 7 ++-
llvm/lib/Support/raw_socket_stream.cpp | 56 +++++++++----------
5 files changed, 46 insertions(+), 39 deletions(-)
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
index 00623b41323e60..94a10359c61471 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
@@ -77,9 +77,8 @@ llvm::Expected<T> readMsgStructFromSocket(llvm::raw_socket_stream &Socket) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value);
llvm::Expected<std::string> MaybeBuffer = readBufferFromSocket(Socket);
- if (!MaybeBuffer) {
+ if (!MaybeBuffer)
return std::move(MaybeBuffer.takeError());
- }
std::string Buffer = std::move(*MaybeBuffer);
llvm::Expected<T> MaybeMsgStruct = convertBufferToMsgStruct<T>(Buffer);
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
index 461b850ce08255..77fa4aee1e2ca9 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
@@ -43,6 +43,9 @@ constexpr size_t SOCKET_ADDR_MAX_LENGTH = sizeof(sockaddr_un::sun_path);
constexpr size_t BASEPATH_MAX_LENGTH =
SOCKET_ADDR_MAX_LENGTH - SOCKET_FILE_NAME.length();
+// How long should the module build daemon sit ideal before exiting
+constexpr int TimeoutSec = 15;
+
// Get a temprary location where the daemon can store log files and a socket
// address. Of the format /tmp/clang-<BLAKE3HashOfClangFullVersion>/
std::string getBasePath();
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
index a122ab26c125fa..08ddfff8c7f387 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
@@ -30,7 +30,7 @@ connectToSocket(StringRef SocketPath) {
Expected<std::string> readBufferFromSocket(raw_socket_stream &Socket) {
- constexpr const unsigned short MAX_BUFFER = 4096;
+ constexpr unsigned short MAX_BUFFER = 4096;
char Buffer[MAX_BUFFER];
std::string ReturnBuffer;
@@ -42,16 +42,22 @@ Expected<std::string> readBufferFromSocket(raw_socket_stream &Socket) {
break;
}
- if (Socket.has_error())
- return make_error<StringError>("Failed socket read", Socket.error());
+ if (Socket.has_error()) {
+ std::error_code EC = Socket.error();
+ Socket.clear_error();
+ return make_error<StringError>("Failed socket read: ", EC);
+ }
return ReturnBuffer;
}
Error writeBufferToSocket(raw_socket_stream &Socket, StringRef Buffer) {
Socket << Buffer;
- if (Socket.has_error())
- return make_error<StringError>("Failed socket write", Socket.error());
+ if (Socket.has_error()) {
+ std::error_code EC = Socket.error();
+ Socket.clear_error();
+ return make_error<StringError>("Failed socket write: ", EC);
+ }
Socket.flush();
return Error::success();
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index 1d7adb369157f3..7d04e1c036aa28 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -128,7 +128,11 @@ int ModuleBuildDaemonServer::createDaemonSocket() {
// Exit successfully if the socket address is already in use. When
// translation units are compiled in parallel, until the socket file is
// created, all clang invocations will try to spawn a module build daemon.
+#ifdef _WIN32
+ if (EC.value() == WSAEADDRINUSE) {
+#else
if (EC == std::errc::address_in_use) {
+#endif
exit(EXIT_SUCCESS);
} else {
llvm::errs() << "MBD failed to create unix socket: " << SE.message()
@@ -194,9 +198,8 @@ int ModuleBuildDaemonServer::listenForClients() {
auto CurrentTime = std::chrono::steady_clock::now();
auto ElapsedTime = std::chrono::duration_cast<std::chrono::seconds>(
CurrentTime - StartTime);
- if (ElapsedTime.count() >= 15)
+ if (ElapsedTime.count() >= TimeoutSec)
shutdownDaemon();
-
continue;
}
diff --git a/llvm/lib/Support/raw_socket_stream.cpp b/llvm/lib/Support/raw_socket_stream.cpp
index ea7cbe7e1408bf..31132060b236b3 100644
--- a/llvm/lib/Support/raw_socket_stream.cpp
+++ b/llvm/lib/Support/raw_socket_stream.cpp
@@ -108,70 +108,66 @@ Expected<ListeningSocket> ListeningSocket::createUnix(StringRef SocketPath,
Expected<std::unique_ptr<raw_socket_stream>>
ListeningSocket::accept(bool Block) {
- std::error_code AcceptEC;
-
#ifdef _WIN32
SOCKET WinServerSock = _get_osfhandle(FD);
if (WinServerSock == INVALID_SOCKET)
return llvm::make_error<StringError>("Failed to get file handle: ",
getLastSocketErrorCode());
+#else
+ int Flags = ::fcntl(FD, F_GETFL, 0);
+ if (Flags == -1)
+ return llvm::make_error<StringError>(
+ "Failed to get file descriptor flags: ", getLastSocketErrorCode());
+#endif
// Set to non-blocking if required
if (!Block) {
+#ifdef _WIN32
u_long BlockingMode = 1;
if (ioctlsocket(WinServerSock, FIONBIO, &BlockingMode) == SOCKET_ERROR)
+#else
+ if (::fcntl(FD, F_SETFL, Flags | O_NONBLOCK) == -1)
+#endif
return llvm::make_error<StringError>(
"Failed to set socket to non-blocking: ", getLastSocketErrorCode());
}
+#ifdef _WIN32
SOCKET WinAcceptSock = ::accept(WinServerSock, NULL, NULL);
if (WinAcceptSock == INVALID_SOCKET)
+#else
+ int AcceptFD = ::accept(FD, NULL, NULL);
+ std::error_code AcceptEC;
+ if (AcceptFD == -1)
+#endif
AcceptEC = getLastSocketErrorCode();
// Restore to blocking if required
if (!Block) {
+#ifdef _WIN32
u_long BlockingMode = 0;
if (ioctlsocket(WinServerSock, FIONBIO, &BlockingMode) == SOCKET_ERROR)
+#else
+ if (::fcntl(FD, F_SETFL, Flags) == -1)
+#endif
return llvm::make_error<StringError>(
"Failed to reset socket to blocking: ", getLastSocketErrorCode());
}
+#ifdef _WIN32
if (WinAcceptSock == INVALID_SOCKET)
+#else
+ if (AcceptFD == -1)
+#endif
return llvm::make_error<StringError>("Accept Failed: ", AcceptEC);
+#ifdef _WIN32
int AcceptFD = _open_osfhandle(WinAcceptSock, 0);
if (AcceptFD == -1)
return llvm::make_error<StringError>(
"Failed to get file descriptor from handle: ",
getLastSocketErrorCode());
-#else
- int Flags = ::fcntl(FD, F_GETFL, 0);
- if (Flags == -1)
- return llvm::make_error<StringError>(
- "Failed to get file descriptor flags: ", getLastSocketErrorCode());
-
- // Set to non-blocking if required
- if (!Block) {
- if (::fcntl(FD, F_SETFL, Flags | O_NONBLOCK) == -1)
- return llvm::make_error<StringError>(
- "Failed to set socket to non-blocking: ", getLastSocketErrorCode());
- }
-
- int AcceptFD = ::accept(FD, NULL, NULL);
- if (AcceptFD == -1)
- AcceptEC = getLastSocketErrorCode();
-
- // Restore to blocking if required
- if (!Block) {
- if (::fcntl(FD, F_SETFL, Flags) == -1)
- return llvm::make_error<StringError>(
- "Failed to reset socket to blocking: ", getLastSocketErrorCode());
- }
-
- if (AcceptFD == -1)
- return llvm::make_error<StringError>("Accept Failed: ", AcceptEC);
-
-#endif //_WIN32
+#endif
return std::make_unique<raw_socket_stream>(AcceptFD);
}
>From 62a8e8f8e536cab2d7e9ed91a3051fd8e8b8816f Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Wed, 24 Jan 2024 04:38:13 -0500
Subject: [PATCH 20/27] fix formatting and windows build issue
---
clang/utils/kill_process.py | 11 +++++------
llvm/lib/Support/raw_socket_stream.cpp | 2 +-
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/clang/utils/kill_process.py b/clang/utils/kill_process.py
index 2da54092c7c47c..be2932a5c6e0f0 100644
--- a/clang/utils/kill_process.py
+++ b/clang/utils/kill_process.py
@@ -15,16 +15,15 @@ def main():
search_pattern = sys.argv[1]
- for process in psutil.process_iter(['pid', 'name', 'cmdline']):
- if 'clang' in process.info['name']:
-
+ for process in psutil.process_iter(["pid", "name", "cmdline"]):
+ if "clang" in process.info["name"]:
PID = []
- if search_pattern in ' '.join(process.info['cmdline']):
- PID.append(process.info['pid'])
+ if search_pattern in " ".join(process.info["cmdline"]):
+ PID.append(process.info["pid"])
if len(PID) == 0:
return
-
+
if len(PID) > 1:
sys.stderr.write("Error: more then one process matches search pattern")
diff --git a/llvm/lib/Support/raw_socket_stream.cpp b/llvm/lib/Support/raw_socket_stream.cpp
index 31132060b236b3..be1e8c2c39a235 100644
--- a/llvm/lib/Support/raw_socket_stream.cpp
+++ b/llvm/lib/Support/raw_socket_stream.cpp
@@ -132,12 +132,12 @@ ListeningSocket::accept(bool Block) {
"Failed to set socket to non-blocking: ", getLastSocketErrorCode());
}
+ std::error_code AcceptEC;
#ifdef _WIN32
SOCKET WinAcceptSock = ::accept(WinServerSock, NULL, NULL);
if (WinAcceptSock == INVALID_SOCKET)
#else
int AcceptFD = ::accept(FD, NULL, NULL);
- std::error_code AcceptEC;
if (AcceptFD == -1)
#endif
AcceptEC = getLastSocketErrorCode();
>From cb5d11c1d1f900ced87a280e4d3b140ff896a24e Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Wed, 31 Jan 2024 09:01:14 -0500
Subject: [PATCH 21/27] Rename test launch.c to daemon-launch.c
---
clang/test/ModuleBuildDaemon/{launch.c => daemon-launch.c} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename clang/test/ModuleBuildDaemon/{launch.c => daemon-launch.c} (100%)
diff --git a/clang/test/ModuleBuildDaemon/launch.c b/clang/test/ModuleBuildDaemon/daemon-launch.c
similarity index 100%
rename from clang/test/ModuleBuildDaemon/launch.c
rename to clang/test/ModuleBuildDaemon/daemon-launch.c
>From ebbf4c4a7d6a40881ceed98e4cdee6acda0ae661 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Wed, 31 Jan 2024 09:01:38 -0500
Subject: [PATCH 22/27] Add missing header file
---
clang/tools/driver/cc1modbuildd_main.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index 7d04e1c036aa28..7fead1edd6ac3b 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -22,6 +22,8 @@
#ifdef _WIN32
#include <windows.h>
+#else
+#include <unistd.h>
#endif
using namespace llvm;
>From acc9239f2d737854bbeb96881899e7ef657ea197 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Wed, 31 Jan 2024 09:53:05 -0500
Subject: [PATCH 23/27] Rewrite kill_process to only use python3 standard
library
---
clang/utils/kill_process.py | 44 +++++++++++++++++++++++++++----------
1 file changed, 32 insertions(+), 12 deletions(-)
diff --git a/clang/utils/kill_process.py b/clang/utils/kill_process.py
index be2932a5c6e0f0..32c7cb1e33108b 100644
--- a/clang/utils/kill_process.py
+++ b/clang/utils/kill_process.py
@@ -3,31 +3,51 @@
# Kills all processes whos command line match provided pattern
import os
-import psutil
import signal
+import subprocess
import sys
def main():
if len(sys.argv) == 1:
- sys.stderr.write("Error: no search pattern provided")
+ sys.stderr.write("Error: no search pattern provided\n")
sys.exit(1)
search_pattern = sys.argv[1]
+ sys.stdout.write(
+ f"Searching for process with pattern '{search_pattern}' in command line\n"
+ )
- for process in psutil.process_iter(["pid", "name", "cmdline"]):
- if "clang" in process.info["name"]:
- PID = []
- if search_pattern in " ".join(process.info["cmdline"]):
- PID.append(process.info["pid"])
+ ps_output = subprocess.check_output(["ps", "aux"], universal_newlines=True)
+ ps_lines = ps_output.split("\n")
- if len(PID) == 0:
- return
+ PIDs = []
- if len(PID) > 1:
- sys.stderr.write("Error: more then one process matches search pattern")
+ for line in ps_lines:
+ # command line for instance of kill_process.py will match search pattern but should not be killed
+ if "clang" in line and "kill_process" not in line:
+ ps_line_components = line.split()
- os.kill(PID[0], signal.SIGTERM)
+ # output of ps aux
+ # 0 1 2 3 4 5 6 7 8 9 10
+ # USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
+
+ # skip line without COMMAND
+ if len(ps_line_components) < 11:
+ continue
+
+ cmdline = " ".join(ps_line_components[10:])
+ if search_pattern in cmdline:
+ PIDs.append(int(ps_line_components[1]))
+
+ if len(PIDs) == 0:
+ sys.stdout.write("No processes matching search pattern were found\n")
+ return
+
+ sys.stdout.write(f"Killing PIDs {PIDs}\n")
+
+ for PID in PIDs:
+ os.kill(PID, signal.SIGTERM)
if __name__ == "__main__":
>From 51c5dfa0183c410a9ad7a9374bc2a69aef9b0cfe Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Wed, 31 Jan 2024 15:55:20 -0500
Subject: [PATCH 24/27] Revert raw_socket_stream to main and add function to
shutdown ListeningSocket
---
clang/tools/driver/cc1modbuildd_main.cpp | 36 ++---
llvm/include/llvm/Support/raw_socket_stream.h | 6 +-
llvm/lib/Support/raw_socket_stream.cpp | 143 ++++++------------
3 files changed, 63 insertions(+), 122 deletions(-)
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index 7fead1edd6ac3b..07a11ff67b7fb2 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -69,19 +69,22 @@ class ModuleBuildDaemonServer {
// TODO: modify so when shutdownDaemon is called the daemon stops accepting
// new client connections and waits for all existing client connections to
// terminate before closing the file descriptor and exiting
- void shutdownDaemon() { RunServiceLoop = false; }
+ void shutdownDaemon() {
+ RunServiceLoop = false;
+ if(ServerListener.has_value())
+ ServerListener.value().shutdown();
+ }
private:
bool RunServiceLoop = true;
std::optional<llvm::ListeningSocket> ServerListener;
};
+// Used to handle signals
ModuleBuildDaemonServer *DaemonPtr = nullptr;
-void handleSignal(int) {
- if (DaemonPtr != nullptr)
- DaemonPtr->shutdownDaemon();
- else
- exit(EXIT_SUCCESS);
+void handleSignal(int) {
+ DaemonPtr->shutdownDaemon();
+ exit(EXIT_SUCCESS);
}
} // namespace
@@ -177,36 +180,21 @@ void ModuleBuildDaemonServer::handleConnection(
int ModuleBuildDaemonServer::listenForClients() {
- auto StartTime = std::chrono::steady_clock::now();
llvm::ThreadPool Pool;
while (RunServiceLoop) {
Expected<std::unique_ptr<raw_socket_stream>> MaybeConnection =
- ServerListener.value().accept(/*Block*/ false);
+ ServerListener.value().accept();
if (llvm::Error Err = MaybeConnection.takeError()) {
llvm::handleAllErrors(std::move(Err), [&](const llvm::StringError &SE) {
- std::error_code EC = SE.convertToErrorCode();
-#ifdef _WIN32
- if (EC.value() != WSAEWOULDBLOCK) {
-#else
- if (EC != std::errc::operation_would_block || EC != std::errc::resource_unavailable_try_again) {
-#endif
- errs() << "MBD failed to accept incoming connection: "
- << SE.getMessage() << EC.message() << '\n';
- }
+ errs() << "MBD failed to accept incoming connection: "
+ << SE.getMessage() << SE.convertToErrorCode().message() << '\n';
});
- auto CurrentTime = std::chrono::steady_clock::now();
- auto ElapsedTime = std::chrono::duration_cast<std::chrono::seconds>(
- CurrentTime - StartTime);
- if (ElapsedTime.count() >= TimeoutSec)
- shutdownDaemon();
continue;
}
- // Successfull connection - restart timer
- StartTime = std::chrono::steady_clock::now();
std::shared_ptr<raw_socket_stream> Connection(std::move(*MaybeConnection));
Pool.async(handleConnection, Connection);
}
diff --git a/llvm/include/llvm/Support/raw_socket_stream.h b/llvm/include/llvm/Support/raw_socket_stream.h
index 750b80e86247ce..2b4d890a9e4f09 100644
--- a/llvm/include/llvm/Support/raw_socket_stream.h
+++ b/llvm/include/llvm/Support/raw_socket_stream.h
@@ -42,16 +42,16 @@ class ListeningSocket {
static Expected<ListeningSocket> createUnix(
StringRef SocketPath,
int MaxBacklog = llvm::hardware_concurrency().compute_thread_count());
- Expected<std::unique_ptr<raw_socket_stream>> accept(bool Block = true);
+ Expected<std::unique_ptr<raw_socket_stream>> accept();
ListeningSocket(ListeningSocket &&LS);
+ void shutdown();
~ListeningSocket();
};
-
class raw_socket_stream : public raw_fd_stream {
uint64_t current_pos() const override { return 0; }
#ifdef _WIN32
WSABalancer _;
-#endif
+#endif // _WIN32
public:
raw_socket_stream(int SocketFD);
diff --git a/llvm/lib/Support/raw_socket_stream.cpp b/llvm/lib/Support/raw_socket_stream.cpp
index be1e8c2c39a235..1e43125803419f 100644
--- a/llvm/lib/Support/raw_socket_stream.cpp
+++ b/llvm/lib/Support/raw_socket_stream.cpp
@@ -16,12 +16,12 @@
#include "llvm/Support/Error.h"
#ifndef _WIN32
-#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#else
#include "llvm/Support/Windows/WindowsSupport.h"
-// winsock2.h must be included before afunix.h
+// winsock2.h must be included before afunix.h. Briefly turn off clang-format to
+// avoid error.
// clang-format off
#include <winsock2.h>
#include <afunix.h>
@@ -45,6 +45,7 @@ WSABalancer::WSABalancer() {
}
WSABalancer::~WSABalancer() { WSACleanup(); }
+
#endif // _WIN32
static std::error_code getLastSocketErrorCode() {
@@ -61,7 +62,6 @@ ListeningSocket::ListeningSocket(int SocketFD, StringRef SocketPath)
ListeningSocket::ListeningSocket(ListeningSocket &&LS)
: FD(LS.FD), SocketPath(LS.SocketPath) {
LS.FD = -1;
- LS.SocketPath.clear();
}
Expected<ListeningSocket> ListeningSocket::createUnix(StringRef SocketPath,
@@ -69,126 +69,78 @@ Expected<ListeningSocket> ListeningSocket::createUnix(StringRef SocketPath,
#ifdef _WIN32
WSABalancer _;
- SOCKET MaybeSocket = socket(AF_UNIX, SOCK_STREAM, 0);
- if (MaybeSocket == INVALID_SOCKET) {
+ SOCKET MaybeWinsocket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (MaybeWinsocket == INVALID_SOCKET) {
#else
- int MaybeSocket = ::socket(AF_UNIX, SOCK_STREAM, 0);
- if (MaybeSocket == -1) {
+ int MaybeWinsocket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (MaybeWinsocket == -1) {
#endif
- return llvm::make_error<StringError>("Socket create failed: ",
- getLastSocketErrorCode());
+ return llvm::make_error<StringError>(getLastSocketErrorCode(),
+ "socket create failed");
}
struct sockaddr_un Addr;
- ::memset(&Addr, 0, sizeof(Addr));
+ memset(&Addr, 0, sizeof(Addr));
Addr.sun_family = AF_UNIX;
- ::strncpy(Addr.sun_path, SocketPath.str().c_str(), sizeof(Addr.sun_path) - 1);
+ strncpy(Addr.sun_path, SocketPath.str().c_str(), sizeof(Addr.sun_path) - 1);
- if (::bind(MaybeSocket, (struct sockaddr *)&Addr, sizeof(Addr)) == -1) {
- ::close(MaybeSocket);
- return llvm::make_error<StringError>("Bind error: ",
- getLastSocketErrorCode());
+ if (bind(MaybeWinsocket, (struct sockaddr *)&Addr, sizeof(Addr)) == -1) {
+ std::error_code Err = getLastSocketErrorCode();
+ if (Err == std::errc::address_in_use)
+ ::close(MaybeWinsocket);
+ return llvm::make_error<StringError>(Err, "Bind error");
}
-
- if (::listen(MaybeSocket, MaxBacklog) == -1) {
- return llvm::make_error<StringError>("Listen error: ",
- getLastSocketErrorCode());
- ::close(MaybeSocket);
+ if (listen(MaybeWinsocket, MaxBacklog) == -1) {
+ return llvm::make_error<StringError>(getLastSocketErrorCode(),
+ "Listen error");
}
-
int UnixSocket;
#ifdef _WIN32
- UnixSocket = _open_osfhandle(MaybeSocket, 0);
+ UnixSocket = _open_osfhandle(MaybeWinsocket, 0);
#else
- UnixSocket = MaybeSocket;
+ UnixSocket = MaybeWinsocket;
#endif // _WIN32
return ListeningSocket{UnixSocket, SocketPath};
}
-Expected<std::unique_ptr<raw_socket_stream>>
-ListeningSocket::accept(bool Block) {
-
+Expected<std::unique_ptr<raw_socket_stream>> ListeningSocket::accept() {
+ int AcceptFD;
#ifdef _WIN32
SOCKET WinServerSock = _get_osfhandle(FD);
- if (WinServerSock == INVALID_SOCKET)
- return llvm::make_error<StringError>("Failed to get file handle: ",
- getLastSocketErrorCode());
-#else
- int Flags = ::fcntl(FD, F_GETFL, 0);
- if (Flags == -1)
- return llvm::make_error<StringError>(
- "Failed to get file descriptor flags: ", getLastSocketErrorCode());
-#endif
-
- // Set to non-blocking if required
- if (!Block) {
-#ifdef _WIN32
- u_long BlockingMode = 1;
- if (ioctlsocket(WinServerSock, FIONBIO, &BlockingMode) == SOCKET_ERROR)
-#else
- if (::fcntl(FD, F_SETFL, Flags | O_NONBLOCK) == -1)
-#endif
- return llvm::make_error<StringError>(
- "Failed to set socket to non-blocking: ", getLastSocketErrorCode());
- }
-
- std::error_code AcceptEC;
-#ifdef _WIN32
SOCKET WinAcceptSock = ::accept(WinServerSock, NULL, NULL);
- if (WinAcceptSock == INVALID_SOCKET)
+ AcceptFD = _open_osfhandle(WinAcceptSock, 0);
#else
- int AcceptFD = ::accept(FD, NULL, NULL);
+ AcceptFD = ::accept(FD, NULL, NULL);
+#endif //_WIN32
if (AcceptFD == -1)
-#endif
- AcceptEC = getLastSocketErrorCode();
-
- // Restore to blocking if required
- if (!Block) {
-#ifdef _WIN32
- u_long BlockingMode = 0;
- if (ioctlsocket(WinServerSock, FIONBIO, &BlockingMode) == SOCKET_ERROR)
-#else
- if (::fcntl(FD, F_SETFL, Flags) == -1)
-#endif
- return llvm::make_error<StringError>(
- "Failed to reset socket to blocking: ", getLastSocketErrorCode());
- }
-
-#ifdef _WIN32
- if (WinAcceptSock == INVALID_SOCKET)
-#else
- if (AcceptFD == -1)
-#endif
- return llvm::make_error<StringError>("Accept Failed: ", AcceptEC);
-
-#ifdef _WIN32
- int AcceptFD = _open_osfhandle(WinAcceptSock, 0);
- if (AcceptFD == -1)
- return llvm::make_error<StringError>(
- "Failed to get file descriptor from handle: ",
- getLastSocketErrorCode());
-#endif
-
+ return llvm::make_error<StringError>(getLastSocketErrorCode(),
+ "Accept failed");
return std::make_unique<raw_socket_stream>(AcceptFD);
}
-ListeningSocket::~ListeningSocket() {
+void ListeningSocket::shutdown() {
if (FD == -1)
return;
::close(FD);
::unlink(SocketPath.c_str());
+
+ FD = -1;
+}
+
+ListeningSocket::~ListeningSocket() {
+ shutdown();
}
static Expected<int> GetSocketFD(StringRef SocketPath) {
#ifdef _WIN32
- SOCKET MaybeSocket = ::socket(AF_UNIX, SOCK_STREAM, 0);
- if (MaybeSocket == INVALID_SOCKET) {
+ SOCKET MaybeWinsocket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (MaybeWinsocket == INVALID_SOCKET) {
#else
- int MaybeSocket = ::socket(AF_UNIX, SOCK_STREAM, 0);
- if (MaybeSocket == -1) {
+ int MaybeWinsocket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (MaybeWinsocket == -1) {
#endif // _WIN32
- return llvm::make_error<StringError>("Create socket failed: ",
- getLastSocketErrorCode());
+ return llvm::make_error<StringError>(getLastSocketErrorCode(),
+ "Create socket failed");
}
struct sockaddr_un Addr;
@@ -196,14 +148,15 @@ static Expected<int> GetSocketFD(StringRef SocketPath) {
Addr.sun_family = AF_UNIX;
strncpy(Addr.sun_path, SocketPath.str().c_str(), sizeof(Addr.sun_path) - 1);
- if (::connect(MaybeSocket, (struct sockaddr *)&Addr, sizeof(Addr)) == -1)
- return llvm::make_error<StringError>("Connect socket failed: ",
- getLastSocketErrorCode());
-
+ int status = connect(MaybeWinsocket, (struct sockaddr *)&Addr, sizeof(Addr));
+ if (status == -1) {
+ return llvm::make_error<StringError>(getLastSocketErrorCode(),
+ "Connect socket failed");
+ }
#ifdef _WIN32
- return _open_osfhandle(MaybeSocket, 0);
+ return _open_osfhandle(MaybeWinsocket, 0);
#else
- return MaybeSocket;
+ return MaybeWinsocket;
#endif // _WIN32
}
>From bc893feaebad68bf18501c1a90debd76d3277ff0 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Wed, 31 Jan 2024 18:40:59 -0500
Subject: [PATCH 25/27] Add timeout to raw_socket_stream accept
---
clang/tools/driver/cc1modbuildd_main.cpp | 11 ++++--
llvm/include/llvm/Support/raw_socket_stream.h | 7 +++-
llvm/lib/Support/raw_socket_stream.cpp | 38 ++++++++++++++++---
3 files changed, 47 insertions(+), 9 deletions(-)
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index 07a11ff67b7fb2..dbef51b788fb25 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -184,12 +184,17 @@ int ModuleBuildDaemonServer::listenForClients() {
while (RunServiceLoop) {
Expected<std::unique_ptr<raw_socket_stream>> MaybeConnection =
- ServerListener.value().accept();
+ ServerListener.value().accept({TimeoutSec, 0});
if (llvm::Error Err = MaybeConnection.takeError()) {
+
llvm::handleAllErrors(std::move(Err), [&](const llvm::StringError &SE) {
- errs() << "MBD failed to accept incoming connection: "
- << SE.getMessage() << SE.convertToErrorCode().message() << '\n';
+ std::error_code EC = SE.convertToErrorCode();
+ if (EC == std::errc::timed_out)
+ RunServiceLoop = false;
+ else
+ errs() << "MBD failed to accept incoming connection: "
+ << SE.getMessage() << ": " << EC.message() << '\n';
});
continue;
diff --git a/llvm/include/llvm/Support/raw_socket_stream.h b/llvm/include/llvm/Support/raw_socket_stream.h
index 2b4d890a9e4f09..a8d9361cb0a315 100644
--- a/llvm/include/llvm/Support/raw_socket_stream.h
+++ b/llvm/include/llvm/Support/raw_socket_stream.h
@@ -42,7 +42,12 @@ class ListeningSocket {
static Expected<ListeningSocket> createUnix(
StringRef SocketPath,
int MaxBacklog = llvm::hardware_concurrency().compute_thread_count());
- Expected<std::unique_ptr<raw_socket_stream>> accept();
+
+ // The use of a default parameter was choosen over std::optional to more
+ // closely resemble the accept system call. A user should be able to call
+ // ListeningSocket.accept() rather then ListeningSocket.accept(std::nullopt)
+ // if they do not want accept to ever timeout
+ Expected<std::unique_ptr<raw_socket_stream>> accept(timeval TV = {-1, -1});
ListeningSocket(ListeningSocket &&LS);
void shutdown();
~ListeningSocket();
diff --git a/llvm/lib/Support/raw_socket_stream.cpp b/llvm/lib/Support/raw_socket_stream.cpp
index 1e43125803419f..efc8f813d05faf 100644
--- a/llvm/lib/Support/raw_socket_stream.cpp
+++ b/llvm/lib/Support/raw_socket_stream.cpp
@@ -103,18 +103,46 @@ Expected<ListeningSocket> ListeningSocket::createUnix(StringRef SocketPath,
return ListeningSocket{UnixSocket, SocketPath};
}
-Expected<std::unique_ptr<raw_socket_stream>> ListeningSocket::accept() {
+Expected<std::unique_ptr<raw_socket_stream>>
+ListeningSocket::accept(timeval TV) {
+
+ int SelectStatus;
int AcceptFD;
+
#ifdef _WIN32
SOCKET WinServerSock = _get_osfhandle(FD);
- SOCKET WinAcceptSock = ::accept(WinServerSock, NULL, NULL);
- AcceptFD = _open_osfhandle(WinAcceptSock, 0);
+#endif
+
+ fd_set Readfds;
+ if (TV.tv_sec != -1 && TV.tv_usec != -1) {
+ FD_ZERO(&Readfds);
+#ifdef _WIN32
+ FD_SET(WinServerSock, &Readfds);
+#else
+ FD_SET(FD, &Readfds);
+#endif
+ SelectStatus = ::select(FD + 1, &Readfds, NULL, NULL, &TV);
+ } else
+ SelectStatus = ::select(FD + 1, &Readfds, NULL, NULL, NULL);
+
+ if (SelectStatus == -1)
+ return llvm::make_error<StringError>(getLastSocketErrorCode(),
+ "Select failed");
+ else if (SelectStatus) {
+#ifdef _WIN32
+ SOCKET WinAcceptSock = ::accept(WinServerSock, NULL, NULL);
+ AcceptFD = _open_osfhandle(WinAcceptSock, 0);
#else
- AcceptFD = ::accept(FD, NULL, NULL);
-#endif //_WIN32
+ AcceptFD = ::accept(FD, NULL, NULL);
+#endif
+ } else
+ return llvm::make_error<StringError>(
+ std::make_error_code(std::errc::timed_out), "Accept timed out");
+
if (AcceptFD == -1)
return llvm::make_error<StringError>(getLastSocketErrorCode(),
"Accept failed");
+
return std::make_unique<raw_socket_stream>(AcceptFD);
}
>From f5dd6d48169f713691ba72605052444c5ba63f21 Mon Sep 17 00:00:00 2001
From: Connor Sughrue <cpsughrue at gmail.com>
Date: Thu, 1 Feb 2024 00:38:38 -0500
Subject: [PATCH 26/27] work on windows implementation for kill_process.py
---
clang/utils/kill_process.py | 60 +++++++++++++++++++++++++++++--------
1 file changed, 47 insertions(+), 13 deletions(-)
diff --git a/clang/utils/kill_process.py b/clang/utils/kill_process.py
index 32c7cb1e33108b..ec4a343af8256b 100644
--- a/clang/utils/kill_process.py
+++ b/clang/utils/kill_process.py
@@ -6,17 +6,32 @@
import signal
import subprocess
import sys
+import platform
-def main():
- if len(sys.argv) == 1:
- sys.stderr.write("Error: no search pattern provided\n")
- sys.exit(1)
+def windows_impl(search_pattern):
- search_pattern = sys.argv[1]
- sys.stdout.write(
- f"Searching for process with pattern '{search_pattern}' in command line\n"
- )
+ cmd = "wmic process get ProcessId,CommandLine"
+ output = subprocess.check_output(cmd, shell=True, universal_newlines=True)
+ lines = output.split("\n")
+
+ PIDs = []
+
+ for line in lines:
+ if "clang" in line and "kill_process" not in line and "llvm-lit" not in line:
+
+ # creates something like ['..\\llvm-project\\build\\bin\\clang.exe -cc1modbuildd mbd-launch -v', '16260']
+ parts = line.rsplit(None, 1)
+
+ if len(parts) == 2:
+ command_line, pid = parts
+ if search_pattern in command_line:
+ PIDs.append(int(pid))
+
+ return PIDs
+
+
+def unix_impl(search_pattern):
ps_output = subprocess.check_output(["ps", "aux"], universal_newlines=True)
ps_lines = ps_output.split("\n")
@@ -24,8 +39,7 @@ def main():
PIDs = []
for line in ps_lines:
- # command line for instance of kill_process.py will match search pattern but should not be killed
- if "clang" in line and "kill_process" not in line:
+ if "clang" in line and "kill_process" not in line and "llvm-lit" not in line:
ps_line_components = line.split()
# output of ps aux
@@ -36,16 +50,36 @@ def main():
if len(ps_line_components) < 11:
continue
- cmdline = " ".join(ps_line_components[10:])
- if search_pattern in cmdline:
+ command_line = " ".join(ps_line_components[10:])
+ if search_pattern in command_line:
PIDs.append(int(ps_line_components[1]))
+ return PIDs
+
+
+def main():
+ if len(sys.argv) == 1:
+ sys.stderr.write("Error: no search pattern provided\n")
+ sys.exit(1)
+
+ search_pattern = sys.argv[1]
+ sys.stdout.write(
+ f"Searching for process with pattern '{search_pattern}' in command line\n"
+ )
+
+ system_name = platform.system()
+
+ PIDs = []
+ if system_name == "Windows":
+ PIDs = windows_impl(search_pattern)
+ else:
+ PIDs = unix_impl(search_pattern)
+
if len(PIDs) == 0:
sys.stdout.write("No processes matching search pattern were found\n")
return
sys.stdout.write(f"Killing PIDs {PIDs}\n")
-
for PID in PIDs:
os.kill(PID, signal.SIGTERM)
>From 414a803635f727fb3791f450d9bcad8feef98149 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Thu, 1 Feb 2024 01:53:52 -0500
Subject: [PATCH 27/27] Make run service loop false
---
clang/tools/driver/cc1modbuildd_main.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index dbef51b788fb25..61b9383bf1547d 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -76,7 +76,7 @@ class ModuleBuildDaemonServer {
}
private:
- bool RunServiceLoop = true;
+ std::atomic<bool> RunServiceLoop = true;
std::optional<llvm::ListeningSocket> ServerListener;
};
More information about the cfe-commits
mailing list