[debugger] Make debug agent persists upon disconnects.
Before the debug agent would be bound to a connection, so the moment the
connection got lost, the agent would be destroyed and all the
attachments/breakpoints would be lost, somewhat defeating the purpose of
reconnecting.
With this change, the agent works independent of a connection, so can
connect/disconnect multiple times without loosing state.
IMPORTANT: While the agent doesn't loose state, this setup will actually
"leak" as the newly connected agent won't know about the current
state of the debug agent (it assumes a "clean" debug agent).
While this is somewhat a regression, it's not that bad as
the default setup is to quit on exit so the agent will exit
anyway upon disconnection.
That change will come in a subsequent CL(s).
TEST=Manual with debug mode activated agent and using `quit-on-exit`
false config, which enables an agent to remain upon disconnection.
Change-Id: If78776a9a215e42613446ec773b5ccfd24c7cfee
diff --git a/src/developer/debug/debug_agent/debug_agent.cc b/src/developer/debug/debug_agent/debug_agent.cc
index 48a60a1..26e53e4 100644
--- a/src/developer/debug/debug_agent/debug_agent.cc
+++ b/src/developer/debug/debug_agent/debug_agent.cc
@@ -8,6 +8,7 @@
#include <lib/async-loop/cpp/loop.h>
#include <lib/sys/cpp/termination_reason.h>
#include <zircon/features.h>
+#include <zircon/status.h>
#include <zircon/syscalls/debug.h>
#include <zircon/syscalls/exception.h>
@@ -39,14 +40,28 @@
} // namespace
-DebugAgent::DebugAgent(debug_ipc::StreamBuffer* stream,
- std::shared_ptr<sys::ServiceDirectory> services)
- : stream_(stream), services_(services), weak_factory_(this) {}
+DebugAgent::DebugAgent(std::shared_ptr<sys::ServiceDirectory> services)
+ : services_(services), weak_factory_(this) {}
DebugAgent::~DebugAgent() = default;
fxl::WeakPtr<DebugAgent> DebugAgent::GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
+void DebugAgent::Connect(debug_ipc::StreamBuffer* stream) {
+ FXL_DCHECK(!stream_) << "A debug agent should not be connected twice!";
+ stream_ = stream;
+}
+
+void DebugAgent::Disconnect() {
+ FXL_DCHECK(stream_);
+ stream_ = nullptr;
+}
+
+debug_ipc::StreamBuffer* DebugAgent::stream() {
+ FXL_DCHECK(stream_);
+ return stream_;
+}
+
void DebugAgent::RemoveDebuggedProcess(zx_koid_t process_koid) {
auto found = procs_.find(process_koid);
if (found == procs_.end())
@@ -160,6 +175,8 @@
reply.status = AddDebuggedJob(job_koid, std::move(job));
}
+ DEBUG_LOG(Agent) << "Attaching to job " << job_koid << ": " << zx_status_get_string(reply.status);
+
// Send the reply.
debug_ipc::MessageWriter writer;
debug_ipc::WriteReply(reply, transaction_id, &writer);
diff --git a/src/developer/debug/debug_agent/debug_agent.h b/src/developer/debug/debug_agent/debug_agent.h
index b816a35..8f7cba1 100644
--- a/src/developer/debug/debug_agent/debug_agent.h
+++ b/src/developer/debug/debug_agent/debug_agent.h
@@ -35,13 +35,18 @@
// The stream must outlive this class. It will be used to send data to the
// client. It will not be read (that's the job of the provider of the
// RemoteAPI).
- explicit DebugAgent(debug_ipc::StreamBuffer* stream,
- std::shared_ptr<sys::ServiceDirectory> services);
+ explicit DebugAgent(std::shared_ptr<sys::ServiceDirectory> services);
~DebugAgent();
fxl::WeakPtr<DebugAgent> GetWeakPtr();
- debug_ipc::StreamBuffer* stream() { return stream_; }
+ // Connects the debug agent to a stream buffer.
+ // The buffer can be disconnected and the debug agent will remain intact until the moment a new
+ // buffer is connected and messages start flowing through again.
+ void Connect(debug_ipc::StreamBuffer*);
+ void Disconnect();
+
+ debug_ipc::StreamBuffer* stream();
void RemoveDebuggedProcess(zx_koid_t process_koid);
@@ -132,7 +137,7 @@
void OnComponentTerminated(int64_t return_code, const ComponentDescription& description,
fuchsia::sys::TerminationReason reason);
- debug_ipc::StreamBuffer* stream_;
+ debug_ipc::StreamBuffer* stream_ = nullptr;
std::shared_ptr<sys::ServiceDirectory> services_;
diff --git a/src/developer/debug/debug_agent/integration_tests/mock_stream_backend.cc b/src/developer/debug/debug_agent/integration_tests/mock_stream_backend.cc
index bba6e135..0567e2d 100644
--- a/src/developer/debug/debug_agent/integration_tests/mock_stream_backend.cc
+++ b/src/developer/debug/debug_agent/integration_tests/mock_stream_backend.cc
@@ -16,7 +16,8 @@
// think it's correctly connected to a client.
stream_.set_writer(this);
auto services = sys::ServiceDirectory::CreateFromNamespace();
- agent_ = std::make_unique<DebugAgent>(&stream_, std::move(services));
+ agent_ = std::make_unique<DebugAgent>(std::move(services));
+ agent_->Connect(&stream_);
}
size_t MockStreamBackend::ConsumeStreamBufferData(const char* data, size_t len) {
diff --git a/src/developer/debug/debug_agent/main.cc b/src/developer/debug/debug_agent/main.cc
index 26888b0..7baaea5 100644
--- a/src/developer/debug/debug_agent/main.cc
+++ b/src/developer/debug/debug_agent/main.cc
@@ -10,6 +10,7 @@
#include <memory>
#include <thread>
+#include "src/developer/debug/debug_agent/debug_agent.h"
#include "src/developer/debug/debug_agent/socket_connection.h"
#include "src/developer/debug/debug_agent/unwind.h"
#include "src/developer/debug/shared/logging/logging.h"
@@ -128,6 +129,11 @@
return 1;
}
+ // The debug agent is independent on whether it's connected or not.
+ // The following loop will attempt to patch a stream to the debug agent in order to enable
+ // communication.
+ debug_agent::DebugAgent debug_agent(services);
+
while (true) {
// Start a new thread that will listen on a socket from an incoming connection from a
// client. In the meantime, the main thread will block waiting for something to be posted
@@ -138,8 +144,11 @@
// the loop, the code will clean the connection thread and create another or exit the loop,
// according to the current agent's configuration.
{
- std::thread conn_thread(&debug_agent::SocketServer::Run, &server, message_loop.get(),
- options.port, services);
+ debug_agent::SocketServer::ConnectionConfig conn_config;
+ conn_config.message_loop = message_loop.get();
+ conn_config.debug_agent = &debug_agent;
+ conn_config.port = options.port;
+ std::thread conn_thread(&debug_agent::SocketServer::Run, &server, std::move(conn_config));
message_loop->Run();
@@ -148,8 +157,7 @@
}
// See if the debug agent was told to exit.
- auto* agent = server.GetDebugAgent();
- if (!agent || agent->should_quit())
+ if (debug_agent.should_quit())
break;
// Prepare for another connection.
diff --git a/src/developer/debug/debug_agent/socket_connection.cc b/src/developer/debug/debug_agent/socket_connection.cc
index 5f69e85..26ef928 100644
--- a/src/developer/debug/debug_agent/socket_connection.cc
+++ b/src/developer/debug/debug_agent/socket_connection.cc
@@ -8,18 +8,13 @@
#include <sys/socket.h>
#include <unistd.h>
+#include "src/developer/debug/debug_agent/debug_agent.h"
#include "src/developer/debug/shared/logging/logging.h"
namespace debug_agent {
// Socket Server -----------------------------------------------------------------------------------
-const DebugAgent* SocketServer::GetDebugAgent() const {
- if (!connected())
- return nullptr;
- return connection_->agent();
-}
-
bool SocketServer::Init(uint16_t port) {
server_socket_.reset(socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP));
if (!server_socket_.is_valid()) {
@@ -44,22 +39,32 @@
return true;
}
-void SocketServer::Run(debug_ipc::MessageLoop* main_thread_loop, int port,
- std::shared_ptr<sys::ServiceDirectory> services) {
+void SocketServer::Run(ConnectionConfig config) {
// Wait for one connection.
- printf("Waiting on port %d for zxdb connection...\n", port);
+ printf("Waiting on port %d for zxdb connection...\n", config.port);
fflush(stdout);
- connection_ = std::make_unique<SocketConnection>(services);
- if (!connection_->Accept(main_thread_loop, server_socket_.get()))
+ connection_ = std::make_unique<SocketConnection>(config.debug_agent);
+ if (!connection_->Accept(config.message_loop, server_socket_.get()))
return;
printf("Connection established.\n");
}
-void SocketServer::Reset() { connection_.reset(); }
+void SocketServer::Reset() {
+ connection_.reset();
+}
// SocketConnection --------------------------------------------------------------------------------
+SocketConnection::SocketConnection(debug_agent::DebugAgent* agent) : debug_agent_(agent) {}
+
+SocketConnection::~SocketConnection() {
+ if (!connected_)
+ return;
+ FXL_DCHECK(debug_agent_) << "A debug agent should be set when resetting the connection.";
+ debug_agent_->Disconnect();
+}
+
bool SocketConnection::Accept(debug_ipc::MessageLoop* main_thread_loop, int server_fd) {
sockaddr_in6 addr;
memset(&addr, 0, sizeof(addr));
@@ -77,7 +82,8 @@
}
// We need to post the agent initialization to the other thread.
- main_thread_loop->PostTask(FROM_HERE, [this, client = std::move(client)]() mutable {
+ main_thread_loop->PostTask(FROM_HERE, [this, debug_agent = debug_agent_,
+ client = std::move(client)]() mutable {
if (!buffer_.Init(std::move(client))) {
FXL_LOG(ERROR) << "Error waiting for data.";
debug_ipc::MessageLoop::Current()->QuitNow();
@@ -85,8 +91,7 @@
}
// Route data from the router_buffer -> RemoteAPIAdapter -> DebugAgent.
- agent_ = std::make_unique<debug_agent::DebugAgent>(&buffer_.stream(), services_);
- adapter_ = std::make_unique<debug_agent::RemoteAPIAdapter>(agent_.get(), &buffer_.stream());
+ adapter_ = std::make_unique<debug_agent::RemoteAPIAdapter>(debug_agent, &buffer_.stream());
buffer_.set_data_available_callback(
[adapter = adapter_.get()]() { adapter->OnStreamReadable(); });
@@ -96,9 +101,13 @@
DEBUG_LOG(Agent) << "Connection lost.";
debug_ipc::MessageLoop::Current()->QuitNow();
});
+
+ // Connect the buffer into the agent.
+ debug_agent->Connect(&buffer_.stream());
});
printf("Accepted connection.\n");
+ connected_ = true;
return true;
}
diff --git a/src/developer/debug/debug_agent/socket_connection.h b/src/developer/debug/debug_agent/socket_connection.h
index 2bd2318..478aa4b 100644
--- a/src/developer/debug/debug_agent/socket_connection.h
+++ b/src/developer/debug/debug_agent/socket_connection.h
@@ -2,12 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
#include "lib/sys/cpp/service_directory.h"
-#include "src/developer/debug/debug_agent/debug_agent.h"
#include "src/developer/debug/debug_agent/remote_api_adapter.h"
#include "src/developer/debug/shared/buffered_fd.h"
+#include "src/developer/debug/shared/message_loop_target.h"
namespace debug_agent {
+class DebugAgent;
class SocketConnection;
// SocketServer ------------------------------------------------------------------------------------
@@ -33,12 +34,18 @@
public:
SocketServer() = default;
+ // All fields of |config| must be correctly set.
+ //
// IMPORTANT: Only this can be called on another thread.
// We use |main_thread_loop| to post a task that actually creates the debug agent on
// the main thread after the connection has been made. This is because the agent has a
// lot of assumptions of being run on the thread of the message loop.
- void Run(debug_ipc::MessageLoop* main_thread_loop, int port,
- std::shared_ptr<sys::ServiceDirectory> services);
+ struct ConnectionConfig {
+ debug_ipc::MessageLoopTarget* message_loop = nullptr;
+ debug_agent::DebugAgent* debug_agent = nullptr;
+ int port = 0;
+ };
+ void Run(ConnectionConfig config);
// IMPORTANT: All others can only be called on the main thread.
@@ -47,7 +54,6 @@
void Reset();
bool connected() const { return !!connection_; }
- const DebugAgent* GetDebugAgent() const;
private:
fxl::UniqueFD server_socket_;
@@ -62,22 +68,20 @@
// It owns a DebugAgent, which is currently always associated to one connection.
class SocketConnection {
public:
- SocketConnection(std::shared_ptr<sys::ServiceDirectory> services) : services_(services) {}
- ~SocketConnection() {}
+ SocketConnection(debug_agent::DebugAgent*);
+ ~SocketConnection();
// |main_thread_loop| is used for posting a task that creates the debug agent after accepting a
// a connection. This is because the debug agent assumes it's running on the message loop's
// thread.
bool Accept(debug_ipc::MessageLoop* main_thread_loop, int server_fd);
- const debug_agent::DebugAgent* agent() const { return agent_.get(); }
-
private:
- std::shared_ptr<sys::ServiceDirectory> services_;
- debug_ipc::BufferedFD buffer_;
+ debug_agent::DebugAgent* debug_agent_ = nullptr;
- std::unique_ptr<debug_agent::DebugAgent> agent_;
std::unique_ptr<debug_agent::RemoteAPIAdapter> adapter_;
+ debug_ipc::BufferedFD buffer_;
+ bool connected_ = false;
FXL_DISALLOW_COPY_AND_ASSIGN(SocketConnection);
};
diff --git a/src/developer/debug/zxdb/client/system.cc b/src/developer/debug/zxdb/client/system.cc
index 01c6e44..c14a88b 100644
--- a/src/developer/debug/zxdb/client/system.cc
+++ b/src/developer/debug/zxdb/client/system.cc
@@ -63,7 +63,7 @@
schema->AddList(ClientSettings::System::kSymbolPaths, kSymbolPathsDescription, {});
schema->AddBool(ClientSettings::System::kPauseOnLaunch, kPauseOnLaunchDescription, false);
schema->AddBool(ClientSettings::System::kPauseOnAttach, kPauseOnAttachDescription, false);
- schema->AddBool(ClientSettings::System::kQuitAgentOnExit, kQuitAgentOnExitDescription, false);
+ schema->AddBool(ClientSettings::System::kQuitAgentOnExit, kQuitAgentOnExitDescription, true);
schema->AddBool(ClientSettings::System::kShowStdout, kShowStdoutDescription, true);
schema->AddList(ClientSettings::System::kSymbolServers, kSymbolServersDescription, {});
schema->AddString(ClientSettings::System::kSymbolCache, kSymbolCacheDescription, "");