blob: 0177022ba1c4ec09d928a1b85ceb5eea32e9d796 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <array>
#include <memory>
#include <queue>
#include "src/lib/files/unique_fd.h"
#include "src/lib/fxl/macros.h"
#include "src/lib/fxl/strings/string_view.h"
#include "src/lib/fxl/time/time_delta.h"
#include "garnet/lib/inferior_control/exception_port.h"
#include "garnet/lib/inferior_control/process.h"
#include "garnet/lib/inferior_control/server.h"
#include "garnet/lib/inferior_control/thread.h"
#include "cmd_handler.h"
#include "io_loop.h"
namespace debugserver {
// Server for Remote Serial Protocol support.
// This implements the main loop and handles commands received over a TCP port
// (from gdb or lldb, or any other debugger that supports RSP really).
//
// NOTE: This class is generally not thread safe. Care must be taken when
// calling methods such as set_current_process(), SetCurrentThread(), and
// QueueNotification() which modify its internal state.
class RspServer final : public inferior_control::ServerWithIO {
public:
// The default timeout interval used when sending notifications.
constexpr static int64_t kDefaultTimeoutSeconds = 30;
// |port| is the tcp port to listen on.
// |initial_attach_pid|, if not ZX_KOID_INVALID, is the koid of a process to
// attach to in Run() before entering the main loop.
// |argv| is passed to the inferior if ran from scratch.
explicit RspServer(uint16_t port, zx_koid_t initial_attach_pid,
debugger_utils::Argv argv);
void set_inferior_argv(const debugger_utils::Argv& argv) {
inferior_argv_ = argv;
}
const debugger_utils::Argv& inferior_argv() const { return inferior_argv_; }
// Starts the main loop. This will first block and wait for an incoming
// connection. Once there is a connection, this will start an event loop for
// handling commands.
bool Run() override;
// Queue a notification packet and send it out if there are no currently
// queued notifications. The GDB Remote Protocol defines a specific
// control-flow for notification packets, such that each notification packet
// will be pending until the remote end acknowledges it. There can be only
// one pending notification at a time.
//
// A notification will time out if the remote end does not acknowledge it
// within |timeout|. If a notification times out, it will be sent again.
void QueueNotification(
const fxl::StringView& name, const fxl::StringView& event,
const fxl::TimeDelta& timeout =
fxl::TimeDelta::FromSeconds(kDefaultTimeoutSeconds));
// Wrapper of QueueNotification for "Stop" notifications.
void QueueStopNotification(
const fxl::StringView& event,
const fxl::TimeDelta& timeout =
fxl::TimeDelta::FromSeconds(kDefaultTimeoutSeconds));
// Set |parameter| to |value|. Return true if success.
bool SetParameter(const fxl::StringView& parameter,
const fxl::StringView& value);
// Store the value of |parameter| in |*value|. Return true if success.
bool GetParameter(const fxl::StringView& parameter, std::string* value);
private:
// Maximum number of characters in the outbound buffer.
constexpr static size_t kMaxBufferSize = 4096;
// Represents a pending notification packet.
struct PendingNotification {
PendingNotification(const fxl::StringView& name,
const fxl::StringView& event,
const fxl::TimeDelta& timeout);
std::string name;
std::string event;
fxl::TimeDelta timeout;
};
// Listens for incoming connections on port |port_|. Once a connection is
// accepted, returns true and stores the client socket in |client_sock_|,
// and the server socket in |server_sock_|. Returns false if an error occurs.
bool Listen();
// Send an acknowledgment packet. If |ack| is true, then a '+' ACK will be
// sent to indicate that a packet was received correctly, or '-' to request
// retransmission. This method blocks until the syscall to write to the socket
// returns.
void SendAck(bool ack);
// Posts an asynchronous task on the message loop to send a packet over the
// wire. |data| will be wrapped in a GDB Remote Protocol packet after
// computing the checksum. If |notify| is true, then a notification packet
// will be sent (where the first byte of the packet equals '%'), otherwise a
// regular packet will be sent (first byte is '$').
void PostWriteTask(bool notify, const fxl::StringView& data);
// Convenience helpers for PostWriteTask
void PostPacketWriteTask(const fxl::StringView& data);
void PostPendingNotificationWriteTask();
// If |pending_notification_| is NULL, this pops the next lined-up
// notification from |notify_queue_| and assigns it as the new pending
// notification and sends it to the remote device.
//
// Returns true, if the next notification was posted. Returns false if the
// next notification was not posted because either there is still a pending
// unacknowledged notification or the notification queue is empty.
bool TryPostNextNotification();
// Post a timeout handler for |pending_notification_|.
void PostNotificationTimeoutHandler();
// IOLoop::Delegate overrides.
void OnBytesRead(const fxl::StringView& bytes) override;
void OnDisconnected() override;
void OnIOError() override;
// Process::Delegate overrides.
void OnThreadStarting(inferior_control::Process* process,
inferior_control::Thread* thread, zx_handle_t eport,
const zx_exception_context_t& context) override;
void OnThreadExiting(inferior_control::Process* process,
inferior_control::Thread* thread, zx_handle_t eport,
const zx_exception_context_t& context) override;
void OnProcessTermination(inferior_control::Process* process) override;
void OnArchitecturalException(inferior_control::Process* process,
inferior_control::Thread* thread,
zx_handle_t eport, const zx_excp_type_t type,
const zx_exception_context_t& context) override;
void OnSyntheticException(inferior_control::Process* process,
inferior_control::Thread* thread,
zx_handle_t eport, zx_excp_type_t type,
const zx_exception_context_t& context) override;
void ExceptionHelper(inferior_control::Process* process,
inferior_control::Thread* thread, zx_excp_type_t type,
const zx_exception_context_t& context);
// TCP port number that we will listen on.
uint16_t port_;
// If this is non-ZX_KOID_INVALID, attach to this process before
// entering the main loop.
zx_koid_t initial_attach_pid_;
// The argv to pass to the inferior.
debugger_utils::Argv inferior_argv_;
// File descriptor for the socket used for listening for incoming
// connections (e.g. from gdb or lldb).
fxl::UniqueFD server_sock_;
// Buffer used for writing outgoing bytes.
std::array<char, kMaxBufferSize> out_buffer_;
// The CommandHandler that is responsible for interpreting received command
// packets and routing them to the correct handler.
CommandHandler command_handler_;
// The current queue of notifications that have not been sent out yet.
std::queue<std::unique_ptr<PendingNotification>> notify_queue_;
// The currently pending notification that has been sent out but has NOT been
// acknowledged by the remote end yet.
std::unique_ptr<PendingNotification> pending_notification_;
FXL_DISALLOW_COPY_AND_ASSIGN(RspServer);
};
} // namespace debugserver