193 changes: 193 additions & 0 deletions lldb/source/Host/posix/MainLoopPosix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
//===-- MainLoopPosix.cpp ---------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/Host/posix/MainLoopPosix.h"

#include <vector>

#include "lldb/Core/Error.h"

using namespace lldb;
using namespace lldb_private;

static sig_atomic_t g_signal_flags[NSIG];

static void
SignalHandler(int signo, siginfo_t *info, void *)
{
assert(signo < NSIG);
g_signal_flags[signo] = 1;
}


MainLoopPosix::~MainLoopPosix()
{
assert(m_read_fds.size() == 0);
assert(m_signals.size() == 0);
}

MainLoopPosix::ReadHandleUP
MainLoopPosix::RegisterReadObject(const IOObjectSP &object_sp, const Callback &callback, Error &error)
{
if (!object_sp || !object_sp->IsValid())
{
error.SetErrorString("IO object is not valid.");
return nullptr;
}

const bool inserted = m_read_fds.insert({object_sp->GetWaitableHandle(), callback}).second;
if (! inserted)
{
error.SetErrorStringWithFormat("File descriptor %d already monitored.",
object_sp->GetWaitableHandle());
return nullptr;
}

return CreateReadHandle(object_sp);
}

// We shall block the signal, then install the signal handler. The signal will be unblocked in
// the Run() function to check for signal delivery.
MainLoopPosix::SignalHandleUP
MainLoopPosix::RegisterSignal(int signo, const Callback &callback, Error &error)
{
if (m_signals.find(signo) != m_signals.end())
{
error.SetErrorStringWithFormat("Signal %d already monitored.", signo);
return nullptr;
}

SignalInfo info;
info.callback = callback;
struct sigaction new_action;
new_action.sa_sigaction = &SignalHandler;
new_action.sa_flags = SA_SIGINFO;
sigemptyset(&new_action.sa_mask);
sigaddset(&new_action.sa_mask, signo);

sigset_t old_set;
if (int ret = pthread_sigmask(SIG_BLOCK, &new_action.sa_mask, &old_set))
{
error.SetErrorStringWithFormat("pthread_sigmask failed with error %d\n", ret);
return nullptr;
}

info.was_blocked = sigismember(&old_set, signo);
if (sigaction(signo, &new_action, &info.old_action) == -1)
{
error.SetErrorToErrno();
if (!info.was_blocked)
pthread_sigmask(SIG_UNBLOCK, &new_action.sa_mask, nullptr);
return nullptr;
}

m_signals.insert({signo, info});
g_signal_flags[signo] = 0;

return SignalHandleUP(new SignalHandle(*this, signo));
}

void
MainLoopPosix::UnregisterReadObject(const lldb::IOObjectSP &object_sp)
{
bool erased = m_read_fds.erase(object_sp->GetWaitableHandle());
(void) erased;
assert(erased);
}

void
MainLoopPosix::UnregisterSignal(int signo)
{
// We undo the actions of RegisterSignal on a best-effort basis.
auto it = m_signals.find(signo);
assert(it != m_signals.end());

sigaction(signo, &it->second.old_action, nullptr);

sigset_t set;
sigemptyset(&set);
sigaddset(&set, signo);
pthread_sigmask(it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK, &set, nullptr);

m_signals.erase(it);
}

Error
MainLoopPosix::Run()
{
std::vector<int> signals;
sigset_t sigmask;
std::vector<int> read_fds;
fd_set read_fd_set;
m_terminate_request = false;

// run until termination or until we run out of things to listen to
while (! m_terminate_request && (!m_read_fds.empty() || !m_signals.empty()))
{
// To avoid problems with callbacks changing the things we're supposed to listen to, we
// will store the *real* list of events separately.
signals.clear();
read_fds.clear();
FD_ZERO(&read_fd_set);
int nfds = 0;

if (int ret = pthread_sigmask(SIG_SETMASK, nullptr, &sigmask))
return Error("pthread_sigmask failed with error %d\n", ret);

for (const auto &fd: m_read_fds)
{
read_fds.push_back(fd.first);
FD_SET(fd.first, &read_fd_set);
nfds = std::max(nfds, fd.first+1);
}

for (const auto &sig: m_signals)
{
signals.push_back(sig.first);
sigdelset(&sigmask, sig.first);
}

if (pselect(nfds, &read_fd_set, nullptr, nullptr, nullptr, &sigmask) == -1 && errno != EINTR)
return Error(errno, eErrorTypePOSIX);

for (int sig: signals)
{
if (g_signal_flags[sig] == 0)
continue; // No signal
g_signal_flags[sig] = 0;

auto it = m_signals.find(sig);
if (it == m_signals.end())
continue; // Signal must have gotten unregistered in the meantime

it->second.callback(*this); // Do the work

if (m_terminate_request)
return Error();
}

for (int fd: read_fds)
{
if (!FD_ISSET(fd, &read_fd_set))
continue; // Not ready

auto it = m_read_fds.find(fd);
if (it == m_read_fds.end())
continue; // File descriptor must have gotten unregistered in the meantime

it->second(*this); // Do the work

if (m_terminate_request)
return Error();
}
}
return Error();
}


Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ GDBRemoteCommunicationServer::SendOKResponse ()
}

bool
GDBRemoteCommunicationServer::HandshakeWithClient(Error *error_ptr)
GDBRemoteCommunicationServer::HandshakeWithClient()
{
return GetAck() == PacketResult::Success;
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class GDBRemoteCommunicationServer : public GDBRemoteCommunication
// After connecting, do a little handshake with the client to make sure
// we are at least communicating
bool
HandshakeWithClient (Error *error_ptr);
HandshakeWithClient ();

protected:
std::map<StringExtractorGDBRemote::ServerPacketType, PacketHandler> m_packet_handlers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ namespace
// GDBRemoteCommunicationServerLLGS constructor
//----------------------------------------------------------------------
GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS(
const lldb::PlatformSP& platform_sp) :
const lldb::PlatformSP& platform_sp,
MainLoop &mainloop) :
GDBRemoteCommunicationServerCommon ("gdb-remote.server", "gdb-remote.server.rx_packet"),
m_platform_sp (platform_sp),
m_async_thread (LLDB_INVALID_HOST_THREAD),
m_mainloop (mainloop),
m_current_tid (LLDB_INVALID_THREAD_ID),
m_continue_tid (LLDB_INVALID_THREAD_ID),
m_debugged_process_mutex (Mutex::eMutexTypeRecursive),
Expand All @@ -88,7 +89,8 @@ GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS(
m_active_auxv_buffer_sp (),
m_saved_registers_mutex (),
m_saved_registers_map (),
m_next_saved_registers_id (1)
m_next_saved_registers_id (1),
m_handshake_completed (false)
{
assert(platform_sp);
RegisterPacketHandlers();
Expand Down Expand Up @@ -782,6 +784,58 @@ GDBRemoteCommunicationServerLLGS::DidExec (NativeProcessProtocol *process)
ClearProcessSpecificData ();
}

void
GDBRemoteCommunicationServerLLGS::DataAvailableCallback ()
{
Log *log (GetLogIfAnyCategoriesSet(GDBR_LOG_COMM));

if (! m_handshake_completed)
{
if (! HandshakeWithClient())
{
if(log)
log->Printf("GDBRemoteCommunicationServerLLGS::%s handshake with client failed, exiting",
__FUNCTION__);
m_read_handle_up.reset();
m_mainloop.RequestTermination();
return;
}
m_handshake_completed = true;
}

bool interrupt = false;
bool done = false;
Error error;
while (true)
{
const PacketResult result = GetPacketAndSendResponse (0, error, interrupt, done);
if (result == PacketResult::ErrorReplyTimeout)
break; // No more packets in the queue

if ((result != PacketResult::Success))
{
if(log)
log->Printf("GDBRemoteCommunicationServerLLGS::%s processing a packet failed: %s",
__FUNCTION__, error.AsCString());
m_read_handle_up.reset();
m_mainloop.RequestTermination();
break;
}
}
}

Error
GDBRemoteCommunicationServerLLGS::InitializeConnection (std::unique_ptr<Connection> &&connection)
{
IOObjectSP read_object_sp = connection->GetReadObject();
GDBRemoteCommunicationServer::SetConnection(connection.release());

Error error;
m_read_handle_up = m_mainloop.RegisterReadObject(read_object_sp,
[this] (MainLoopBase &) { DataAvailableCallback(); }, error);
return error;
}

GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerLLGS::SendONotification (const char *buffer, uint32_t len)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@
#include "lldb/Core/Communication.h"
#include "lldb/Host/Mutex.h"
#include "lldb/Host/common/NativeProcessProtocol.h"
#include "lldb/Host/MainLoop.h"

// Project includes
#include "GDBRemoteCommunicationServerCommon.h"

class StringExtractorGDBRemote;

namespace lldb_private {

namespace process_gdb_remote {

class ProcessGDBRemote;
Expand All @@ -38,7 +40,7 @@ class GDBRemoteCommunicationServerLLGS :
//------------------------------------------------------------------
// Constructors and Destructors
//------------------------------------------------------------------
GDBRemoteCommunicationServerLLGS(const lldb::PlatformSP& platform_sp);
GDBRemoteCommunicationServerLLGS(const lldb::PlatformSP& platform_sp, MainLoop &mainloop);

virtual
~GDBRemoteCommunicationServerLLGS();
Expand Down Expand Up @@ -111,9 +113,13 @@ class GDBRemoteCommunicationServerLLGS :
void
DidExec (NativeProcessProtocol *process) override;

Error
InitializeConnection (std::unique_ptr<Connection> &&connection);

protected:
lldb::PlatformSP m_platform_sp;
lldb::thread_t m_async_thread;
MainLoop &m_mainloop;
MainLoop::ReadHandleUP m_read_handle_up;
lldb::tid_t m_current_tid;
lldb::tid_t m_continue_tid;
Mutex m_debugged_process_mutex;
Expand All @@ -124,6 +130,7 @@ class GDBRemoteCommunicationServerLLGS :
Mutex m_saved_registers_mutex;
std::unordered_map<uint32_t, lldb::DataBufferSP> m_saved_registers_map;
uint32_t m_next_saved_registers_id;
bool m_handshake_completed : 1;

PacketResult
SendONotification (const char *buffer, uint32_t len);
Expand Down Expand Up @@ -295,6 +302,9 @@ class GDBRemoteCommunicationServerLLGS :
void
RegisterPacketHandlers ();

void
DataAvailableCallback ();

//------------------------------------------------------------------
// For GDBRemoteCommunicationServerLLGS only
//------------------------------------------------------------------
Expand Down
98 changes: 37 additions & 61 deletions lldb/tools/lldb-server/lldb-gdbserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,21 @@ signal_handler(int signo)
case SIGPIPE:
g_sigpipe_received = 1;
break;
case SIGHUP:
++g_sighup_received_count;

// For now, swallow SIGHUP.
if (log)
log->Printf ("lldb-server:%s swallowing SIGHUP (receive count=%d)", __FUNCTION__, g_sighup_received_count);
signal (SIGHUP, signal_handler);
break;
}
}

static void
sighup_handler(MainLoopBase &mainloop)
{
++g_sighup_received_count;

Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
if (log)
log->Printf ("lldb-server:%s swallowing SIGHUP (receive count=%d)", __FUNCTION__, g_sighup_received_count);

if (g_sighup_received_count >= 2)
mainloop.RequestTermination();
}
#endif // #ifndef _WIN32

static void
Expand Down Expand Up @@ -338,7 +343,7 @@ writePortToPipe(int unnamed_pipe_fd, const uint16_t port)
}

void
ConnectToRemote(GDBRemoteCommunicationServerLLGS &gdb_server,
ConnectToRemote(MainLoop &mainloop, GDBRemoteCommunicationServerLLGS &gdb_server,
bool reverse_connect, const char *const host_and_port,
const char *const progname, const char *const subcommand,
const char *const named_pipe_path, int unnamed_pipe_fd)
Expand Down Expand Up @@ -372,6 +377,8 @@ ConnectToRemote(GDBRemoteCommunicationServerLLGS &gdb_server,
exit (1);
}

std::unique_ptr<ConnectionFileDescriptor> connection_up;

if (reverse_connect)
{
// llgs will connect to the gdb-remote client.
Expand All @@ -388,8 +395,7 @@ ConnectToRemote(GDBRemoteCommunicationServerLLGS &gdb_server,
snprintf(connection_url, sizeof(connection_url), "connect://%s", final_host_and_port.c_str ());

// Create the connection.
std::unique_ptr<ConnectionFileDescriptor> connection_up (new ConnectionFileDescriptor ());
connection_up.reset (new ConnectionFileDescriptor ());
connection_up.reset(new ConnectionFileDescriptor);
auto connection_result = connection_up->Connect (connection_url, &error);
if (connection_result != eConnectionStatusSuccess)
{
Expand All @@ -401,10 +407,6 @@ ConnectToRemote(GDBRemoteCommunicationServerLLGS &gdb_server,
fprintf (stderr, "error: failed to connect to client at '%s': %s", connection_url, error.AsCString ());
exit (-1);
}

// We're connected.
printf ("Connection established.\n");
gdb_server.SetConnection (connection_up.release());
}
else
{
Expand Down Expand Up @@ -461,57 +463,21 @@ ConnectToRemote(GDBRemoteCommunicationServerLLGS &gdb_server,

// Ensure we connected.
if (s_listen_connection_up)
{
printf ("Connection established '%s'\n", s_listen_connection_up->GetURI().c_str());
gdb_server.SetConnection (s_listen_connection_up.release());
}
connection_up = std::move(s_listen_connection_up);
else
{
fprintf (stderr, "failed to connect to '%s': %s\n", final_host_and_port.c_str (), error.AsCString ());
display_usage (progname, subcommand);
exit (1);
}
}
}

if (gdb_server.IsConnected())
{
// After we connected, we need to get an initial ack from...
if (gdb_server.HandshakeWithClient(&error))
error = gdb_server.InitializeConnection (std::move(connection_up));
if (error.Fail())
{
// We'll use a half a second timeout interval so that an exit conditions can
// be checked that often.
const uint32_t TIMEOUT_USEC = 500000;

bool interrupt = false;
bool done = false;
while (!interrupt && !done && (g_sighup_received_count < 2))
{
const GDBRemoteCommunication::PacketResult result = gdb_server.GetPacketAndSendResponse (TIMEOUT_USEC, error, interrupt, done);
if ((result != GDBRemoteCommunication::PacketResult::Success) &&
(result != GDBRemoteCommunication::PacketResult::ErrorReplyTimeout))
{
// We're bailing out - we only support successful handling and timeouts.
fprintf(stderr, "leaving packet loop due to PacketResult %d\n", result);
break;
}
}

if (error.Fail())
{
fprintf(stderr, "error: %s\n", error.AsCString());
}
}
else
{
fprintf(stderr, "error: handshake with client failed\n");
fprintf(stderr, "Failed to initialize connection: %s\n", error.AsCString());
exit(-1);
}
}
else
{
fprintf (stderr, "no connection information provided, unable to run\n");
display_usage (progname, subcommand);
exit (1);
printf ("Connection established.\n");
}
}

Expand All @@ -521,10 +487,12 @@ ConnectToRemote(GDBRemoteCommunicationServerLLGS &gdb_server,
int
main_gdbserver (int argc, char *argv[])
{
Error error;
MainLoop mainloop;
#ifndef _WIN32
// Setup signal handlers first thing.
signal (SIGPIPE, signal_handler);
signal (SIGHUP, signal_handler);
MainLoop::SignalHandleUP sighup_handle = mainloop.RegisterSignal(SIGHUP, sighup_handler, error);
#endif
#ifdef __linux__
// Block delivery of SIGCHLD on linux. NativeProcessLinux will read it using signalfd.
Expand All @@ -539,7 +507,6 @@ main_gdbserver (int argc, char *argv[])
argc--;
argv++;
int long_option_index = 0;
Error error;
int ch;
std::string platform_name;
std::string attach_target;
Expand Down Expand Up @@ -670,7 +637,7 @@ main_gdbserver (int argc, char *argv[])
// Setup the platform that GDBRemoteCommunicationServerLLGS will use.
lldb::PlatformSP platform_sp = setup_platform (platform_name);

GDBRemoteCommunicationServerLLGS gdb_server (platform_sp);
GDBRemoteCommunicationServerLLGS gdb_server (platform_sp, mainloop);

const char *const host_and_port = argv[0];
argc -= 1;
Expand All @@ -688,10 +655,19 @@ main_gdbserver (int argc, char *argv[])
// Print version info.
printf("%s-%s", LLGS_PROGRAM_NAME, LLGS_VERSION_STR);

ConnectToRemote(gdb_server, reverse_connect,
ConnectToRemote(mainloop, gdb_server, reverse_connect,
host_and_port, progname, subcommand,
named_pipe_path.c_str(), unnamed_pipe_fd);


if (! gdb_server.IsConnected())
{
fprintf (stderr, "no connection information provided, unable to run\n");
display_usage (progname, subcommand);
return 1;
}

mainloop.Run();
fprintf(stderr, "lldb-server exiting...\n");

return 0;
Expand Down
2 changes: 1 addition & 1 deletion lldb/tools/lldb-server/lldb-platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ main_platform (int argc, char *argv[])
if (platform.IsConnected())
{
// After we connected, we need to get an initial ack from...
if (platform.HandshakeWithClient(&error))
if (platform.HandshakeWithClient())
{
bool interrupt = false;
bool done = false;
Expand Down