Index: include/lldb/Target/Process.h =================================================================== --- include/lldb/Target/Process.h +++ include/lldb/Target/Process.h @@ -2607,7 +2607,7 @@ bool RunPreResumeActions(); void ClearPreResumeActions(); - + void ClearPreResumeAction(PreResumeActionCallback callback, void *baton); ProcessRunLock &GetRunLock(); @@ -3145,6 +3145,8 @@ Error StopForDestroyOrDetach(lldb::EventSP &exit_event_sp); + virtual Error UpdateAutomaticSignalFiltering(); + bool StateChangedIsExternallyHijacked(); void LoadOperatingSystemPlugin(bool flush); Index: include/lldb/Target/UnixSignals.h =================================================================== --- include/lldb/Target/UnixSignals.h +++ include/lldb/Target/UnixSignals.h @@ -14,11 +14,13 @@ // C++ Includes #include #include +#include // Other libraries and framework includes // Project includes #include "lldb/Utility/ConstString.h" #include "lldb/lldb-private.h" +#include "llvm/ADT/Optional.h" namespace lldb_private { @@ -88,6 +90,19 @@ void RemoveSignal(int signo); + // Returns a current version of the data stored in this class. + // Version gets incremented each time Set... method is called. + uint64_t GetVersion() const; + + // Returns a vector of signals that meet criteria provided in arguments. + // Each should_[suppress|stop|notify] flag can be + // None - no filtering by this flag + // true - only signals that have it set to true are returned + // false - only signals that have it set to true are returned + std::vector GetFilteredSignals(llvm::Optional should_suppress, + llvm::Optional should_stop, + llvm::Optional should_notify); + protected: //------------------------------------------------------------------ // Classes that inherit from UnixSignals can see and modify these @@ -111,6 +126,12 @@ collection m_signals; + // This version gets incremented every time something is changing in + // this class, including when we call AddSignal from the constructor. + // So after the object is constructed m_version is going to be > 0 + // if it has at least one signal registered in it. + uint64_t m_version = 0; + // GDBRemote signals need to be copyable. UnixSignals(const UnixSignals &rhs); Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -347,6 +347,8 @@ bool GetEchoSupported(); + bool GetQPassSignalsSupported(); + bool GetAugmentedLibrariesSVR4ReadSupported(); bool GetQXferFeaturesReadSupported(); @@ -450,6 +452,9 @@ void ServeSymbolLookups(lldb_private::Process *process); + // Sends QPassSignals packet to the server with given signals to ignore. + Error SendSignalsToIgnore(llvm::ArrayRef signals); + //------------------------------------------------------------------ /// Return the feature set supported by the gdb-remote server. /// @@ -527,6 +532,7 @@ LazyBool m_supports_jThreadExtendedInfo; LazyBool m_supports_jLoadedDynamicLibrariesInfos; LazyBool m_supports_jGetSharedCacheInfo; + LazyBool m_supports_QPassSignals; bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1, m_supports_qUserName : 1, m_supports_qGroupName : 1, Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -87,6 +87,7 @@ m_supports_jThreadExtendedInfo(eLazyBoolCalculate), m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate), m_supports_jGetSharedCacheInfo(eLazyBoolCalculate), + m_supports_QPassSignals(eLazyBoolCalculate), m_supports_qProcessInfoPID(true), m_supports_qfProcessInfo(true), m_supports_qUserName(true), m_supports_qGroupName(true), m_supports_qThreadStopInfo(true), m_supports_z0(true), @@ -150,6 +151,13 @@ return m_supports_qEcho == eLazyBoolYes; } +bool GDBRemoteCommunicationClient::GetQPassSignalsSupported() { + if (m_supports_QPassSignals == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_QPassSignals == eLazyBoolYes; +} + bool GDBRemoteCommunicationClient::GetAugmentedLibrariesSVR4ReadSupported() { if (m_supports_augmented_libraries_svr4_read == eLazyBoolCalculate) { GetRemoteQSupported(); @@ -419,6 +427,11 @@ else m_supports_qEcho = eLazyBoolNo; + if (::strstr(response_cstr, "QPassSignals+")) + m_supports_QPassSignals = eLazyBoolYes; + else + m_supports_QPassSignals = eLazyBoolNo; + const char *packet_size_str = ::strstr(response_cstr, "PacketSize="); if (packet_size_str) { StringExtractorGDBRemote packet_response(packet_size_str + @@ -3569,6 +3582,26 @@ : nullptr; } +Error GDBRemoteCommunicationClient::SendSignalsToIgnore( + llvm::ArrayRef signals) { + // Format packet: + // QPassSignals:;...; + auto range = llvm::make_range(signals.begin(), signals.end()); + std::string packet = formatv("QPassSignals:{0:$[;]@(x-2)}", range).str(); + + StringExtractorGDBRemote response; + auto send_status = SendPacketAndWaitForResponse(packet, response, false); + + if (send_status != GDBRemoteCommunication::PacketResult::Success) + return Error("Sending QPassSignals packet failed"); + + if (response.IsOKResponse()) { + return Error(); + } else { + return Error("Unknown error happened during sending QPassSignals packet."); + } +} + Error GDBRemoteCommunicationClient::ConfigureRemoteStructuredData( const ConstString &type_name, const StructuredData::ObjectSP &config_sp) { Error error; Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.h =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -397,12 +397,15 @@ lldb::addr_t base_addr, bool value_is_offset); + Error UpdateAutomaticSignalFiltering() override; + private: //------------------------------------------------------------------ // For ProcessGDBRemote only //------------------------------------------------------------------ std::string m_partial_profile_data; std::map m_thread_id_to_used_usec_map; + uint64_t m_last_signals_version = 0; static bool NewThreadNotifyBreakpointHit(void *baton, StoppointCallbackContext *context, Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -80,8 +80,8 @@ #include "lldb/Host/Host.h" #include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/Threading.h" +#include "llvm/Support/raw_ostream.h" #define DEBUGSERVER_BASENAME "debugserver" using namespace lldb; @@ -3739,6 +3739,43 @@ return false; } +Error ProcessGDBRemote::UpdateAutomaticSignalFiltering() { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + LLDB_LOG(log, "Check if need to update ignored signals"); + + // QPassSignals package is not supported by the server, + // there is no way we can ignore any signals on server side. + if (!m_gdb_comm.GetQPassSignalsSupported()) + return Error(); + + // No signals, nothing to send. + if (m_unix_signals_sp == nullptr) + return Error(); + + // Signals' version hasn't changed, no need to send anything. + uint64_t new_signals_version = m_unix_signals_sp->GetVersion(); + if (new_signals_version == m_last_signals_version) { + LLDB_LOG(log, "Signals' version hasn't changed. version={0}", + m_last_signals_version); + return Error(); + } + + auto signals_to_ignore = + m_unix_signals_sp->GetFilteredSignals(false, false, false); + Error error = m_gdb_comm.SendSignalsToIgnore(signals_to_ignore); + + LLDB_LOG(log, + "Signals' version changed. old version={0}, new version={1}, " + "signals ignored={2}, update result={3}", + m_last_signals_version, new_signals_version, + signals_to_ignore.size(), error); + + if (error.Success()) + m_last_signals_version = new_signals_version; + + return error; +} + bool ProcessGDBRemote::StartNoticingNewThreads() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (m_thread_create_bp_sp) { Index: source/Target/Process.cpp =================================================================== --- source/Target/Process.cpp +++ source/Target/Process.cpp @@ -2778,6 +2778,10 @@ if (!m_os_ap) LoadOperatingSystemPlugin(false); + // We successfully launched the process and stopped, + // now it the right time to set up signal filters before resuming. + UpdateAutomaticSignalFiltering(); + // Note, the stop event was consumed above, but not handled. This // was done // to give DidLaunch a chance to run. The target is either stopped @@ -3257,6 +3261,10 @@ m_mod_id.GetStopID(), StateAsCString(m_public_state.GetValue()), StateAsCString(m_private_state.GetValue())); + // If signals handing status changed we might want to update + // our signal filters before resuming. + UpdateAutomaticSignalFiltering(); + Error error(WillResume()); // Tell the process it is about to resume before the thread list if (error.Success()) { @@ -6219,3 +6227,9 @@ find_it->second->HandleArrivalOfStructuredData(*this, type_name, object_sp); return true; } + +Error Process::UpdateAutomaticSignalFiltering() { + // Default implementation does nothign. + // No automatic signal filtering to speak of. + return Error(); +} Index: source/Target/UnixSignals.cpp =================================================================== --- source/Target/UnixSignals.cpp +++ source/Target/UnixSignals.cpp @@ -123,12 +123,14 @@ Signal new_signal(name, default_suppress, default_stop, default_notify, description, alias); m_signals.insert(std::make_pair(signo, new_signal)); + ++m_version; } void UnixSignals::RemoveSignal(int signo) { collection::iterator pos = m_signals.find(signo); if (pos != m_signals.end()) m_signals.erase(pos); + ++m_version; } const char *UnixSignals::GetSignalAsCString(int signo) const { @@ -217,6 +219,7 @@ collection::iterator pos = m_signals.find(signo); if (pos != m_signals.end()) { pos->second.m_suppress = value; + ++m_version; return true; } return false; @@ -240,6 +243,7 @@ collection::iterator pos = m_signals.find(signo); if (pos != m_signals.end()) { pos->second.m_stop = value; + ++m_version; return true; } return false; @@ -263,6 +267,7 @@ collection::iterator pos = m_signals.find(signo); if (pos != m_signals.end()) { pos->second.m_notify = value; + ++m_version; return true; } return false; @@ -284,3 +289,37 @@ std::advance(it, index); return it->first; } + +uint64_t UnixSignals::GetVersion() const { return m_version; } + +std::vector +UnixSignals::GetFilteredSignals(llvm::Optional should_suppress, + llvm::Optional should_stop, + llvm::Optional should_notify) { + std::vector result; + for (int32_t signo = GetFirstSignalNumber(); + signo != LLDB_INVALID_SIGNAL_NUMBER; + signo = GetNextSignalNumber(signo)) { + + bool signal_suppress = false; + bool signal_stop = false; + bool signal_notify = false; + GetSignalInfo(signo, signal_suppress, signal_stop, signal_notify); + + // If any of filtering conditions are not met, + // we move on to the next signal. + if (should_suppress.hasValue() && + signal_suppress != should_suppress.getValue()) + continue; + + if (should_stop.hasValue() && signal_stop != should_stop.getValue()) + continue; + + if (should_notify.hasValue() && signal_notify != should_notify.getValue()) + continue; + + result.push_back(signo); + } + + return result; +} Index: unittests/CMakeLists.txt =================================================================== --- unittests/CMakeLists.txt +++ unittests/CMakeLists.txt @@ -63,6 +63,7 @@ add_subdirectory(Platform) add_subdirectory(Process) add_subdirectory(ScriptInterpreter) +add_subdirectory(Signals) add_subdirectory(Symbol) add_subdirectory(SymbolFile) add_subdirectory(Target) Index: unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp =================================================================== --- unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp +++ unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp @@ -307,3 +307,27 @@ << ss.GetString(); ASSERT_EQ(10, num_packets); } + +TEST_F(GDBRemoteCommunicationClientTest, SendSignalsToIgnore) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + const lldb::tid_t tid = 0x47; + const uint32_t reg_num = 4; + std::future result = std::async(std::launch::async, [&] { + return client.SendSignalsToIgnore({2, 3, 5, 7, 0xB, 0xD, 0x11}); + }); + + HandlePacket(server, "QPassSignals:02;03;05;07;0b;0d;11", "OK"); + EXPECT_TRUE(result.get().Success()); + + result = std::async(std::launch::async, [&] { + return client.SendSignalsToIgnore(std::vector()); + }); + + HandlePacket(server, "QPassSignals:", "OK"); + EXPECT_TRUE(result.get().Success()); +} Index: unittests/Signals/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/Signals/CMakeLists.txt @@ -0,0 +1,6 @@ +add_lldb_unittest(SignalsTests + UnixSignalsTest.cpp + + LINK_LIBS + lldbTarget + ) Index: unittests/Signals/UnixSignalsTest.cpp =================================================================== --- /dev/null +++ unittests/Signals/UnixSignalsTest.cpp @@ -0,0 +1,140 @@ +//===-- UnixSignalsTest.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include + +#include "gtest/gtest.h" + +#include "lldb/Target/UnixSignals.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace lldb; +using namespace lldb_private; +using llvm::None; + +class TestSignals : public UnixSignals { +public: + TestSignals() { + m_signals.clear(); + AddSignal(2, "SIG2", false, true, true, "DESC2"); + AddSignal(4, "SIG4", true, false, true, "DESC4"); + AddSignal(8, "SIG8", true, true, true, "DESC8"); + AddSignal(16, "SIG16", true, false, false, "DESC16"); + } +}; + +void AssertEqArrays(llvm::ArrayRef expected, + llvm::ArrayRef observed, const char *file, + int line) { + std::string location = llvm::formatv("{0}:{1}", file, line); + ASSERT_EQ(expected.size(), observed.size()) << location; + + for (size_t i = 0; i < observed.size(); ++i) { + ASSERT_EQ(expected[i], observed[i]) + << "array index: " << i << "location:" << location; + } +} + +#define ASSERT_EQ_ARRAYS(expected, observed) \ + AssertEqArrays((expected), (observed), __FILE__, __LINE__); + +TEST(UnixSignalsTest, Iteration) { + TestSignals signals; + + EXPECT_EQ(4, signals.GetNumSignals()); + EXPECT_EQ(2, signals.GetFirstSignalNumber()); + EXPECT_EQ(4, signals.GetNextSignalNumber(2)); + EXPECT_EQ(8, signals.GetNextSignalNumber(4)); + EXPECT_EQ(16, signals.GetNextSignalNumber(8)); + EXPECT_EQ(LLDB_INVALID_SIGNAL_NUMBER, signals.GetNextSignalNumber(16)); +} + +TEST(UnixSignalsTest, GetInfo) { + TestSignals signals; + + bool should_suppress = false, should_stop = false, should_notify = false; + int32_t signo = 4; + std::string name = + signals.GetSignalInfo(signo, should_suppress, should_stop, should_notify); + EXPECT_EQ("SIG4", name); + EXPECT_EQ(true, should_suppress); + EXPECT_EQ(false, should_stop); + EXPECT_EQ(true, should_notify); + + EXPECT_EQ(true, signals.GetShouldSuppress(signo)); + EXPECT_EQ(false, signals.GetShouldStop(signo)); + EXPECT_EQ(true, signals.GetShouldNotify(signo)); + EXPECT_EQ(name, signals.GetSignalAsCString(signo)); +} + +TEST(UnixSignalsTest, VersionChange) { + TestSignals signals; + + int32_t signo = 8; + uint64_t ver = signals.GetVersion(); + EXPECT_GT(ver, 0ull); + EXPECT_EQ(true, signals.GetShouldSuppress(signo)); + EXPECT_EQ(true, signals.GetShouldStop(signo)); + EXPECT_EQ(true, signals.GetShouldNotify(signo)); + + EXPECT_EQ(signals.GetVersion(), ver); + + signals.SetShouldSuppress(signo, false); + EXPECT_LT(ver, signals.GetVersion()); + ver = signals.GetVersion(); + + signals.SetShouldStop(signo, true); + EXPECT_LT(ver, signals.GetVersion()); + ver = signals.GetVersion(); + + signals.SetShouldNotify(signo, false); + EXPECT_LT(ver, signals.GetVersion()); + ver = signals.GetVersion(); + + EXPECT_EQ(false, signals.GetShouldSuppress(signo)); + EXPECT_EQ(true, signals.GetShouldStop(signo)); + EXPECT_EQ(false, signals.GetShouldNotify(signo)); + + EXPECT_EQ(ver, signals.GetVersion()); +} + +TEST(UnixSignalsTest, GetFilteredSignals) { + TestSignals signals; + + auto all_signals = signals.GetFilteredSignals(None, None, None); + std::vector expected = {2, 4, 8, 16}; + ASSERT_EQ_ARRAYS(expected, all_signals); + + auto supressed = signals.GetFilteredSignals(true, None, None); + expected = {4, 8, 16}; + ASSERT_EQ_ARRAYS(expected, supressed); + + auto not_supressed = signals.GetFilteredSignals(false, None, None); + expected = {2}; + ASSERT_EQ_ARRAYS(expected, not_supressed); + + auto stopped = signals.GetFilteredSignals(None, true, None); + expected = {2, 8}; + ASSERT_EQ_ARRAYS(expected, stopped); + + auto not_stopped = signals.GetFilteredSignals(None, false, None); + expected = {4, 16}; + ASSERT_EQ_ARRAYS(expected, not_stopped); + + auto notified = signals.GetFilteredSignals(None, None, true); + expected = {2, 4, 8}; + ASSERT_EQ_ARRAYS(expected, notified); + + auto not_notified = signals.GetFilteredSignals(None, None, false); + expected = {16}; + ASSERT_EQ_ARRAYS(expected, not_notified); + + auto signal4 = signals.GetFilteredSignals(true, false, true); + expected = {4}; + ASSERT_EQ_ARRAYS(expected, signal4); +}