blob: be7516fb10cb787d5cfc806cb29c24caf33399dd [file] [log] [blame]
// Copyright 2019 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.
#include <lib/fit/function.h>
#include <string>
#include "src/developer/debug/shared/buffered_fd.h"
#include "src/developer/debug/shared/platform_message_loop.h"
#include "src/developer/debug/zxdb/client/breakpoint.h"
#include "src/developer/debug/zxdb/client/process.h"
#include "src/developer/debug/zxdb/client/process_observer.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/client/target_observer.h"
#include "src/developer/debug/zxdb/client/thread.h"
#include "src/developer/debug/zxdb/client/thread_observer.h"
#include "src/developer/debug/zxdb/common/err.h"
#include "tools/fidlcat/lib/zx_channel_params.h"
namespace fidlcat {
class InterceptionWorkflow;
namespace internal {
class InterceptingThreadObserver : public zxdb::ThreadObserver,
public BreakpointRegisterer {
explicit InterceptingThreadObserver(InterceptionWorkflow* workflow)
: workflow_(workflow) {}
InterceptingThreadObserver(const InterceptingThreadObserver&) = delete;
InterceptingThreadObserver& operator=(const InterceptingThreadObserver&) =
virtual void OnThreadStopped(
zxdb::Thread* thread, debug_ipc::NotifyException::Type type,
const std::vector<fxl::WeakPtr<zxdb::Breakpoint>>& hit_breakpoints)
virtual ~InterceptingThreadObserver() {}
virtual void Register(int64_t koid,
std::function<void(zxdb::Thread*)>&& cb) override;
virtual void CreateNewBreakpoint(zxdb::BreakpointSettings& settings) override;
InterceptionWorkflow* workflow_;
std::map<int64_t, std::function<void(zxdb::Thread*)>> breakpoint_map_;
class InterceptingProcessObserver : public zxdb::ProcessObserver {
explicit InterceptingProcessObserver(InterceptionWorkflow* workflow)
: dispatcher_(workflow) {}
InterceptingProcessObserver(const InterceptingProcessObserver&) = delete;
InterceptingProcessObserver& operator=(const InterceptingProcessObserver&) =
virtual void DidCreateThread(zxdb::Process* process,
zxdb::Thread* thread) override {
virtual ~InterceptingProcessObserver() {}
InterceptingThreadObserver& thread_observer() { return dispatcher_; }
InterceptingThreadObserver dispatcher_;
class InterceptingTargetObserver : public zxdb::TargetObserver {
explicit InterceptingTargetObserver(InterceptionWorkflow* workflow)
: dispatcher_(workflow), workflow_(workflow) {}
InterceptingTargetObserver(const InterceptingTargetObserver&) = delete;
InterceptingTargetObserver& operator=(const InterceptingTargetObserver&) =
virtual void DidCreateProcess(zxdb::Target* target, zxdb::Process* process,
bool autoattached_to_new_process) override;
virtual void WillDestroyProcess(zxdb::Target* target, zxdb::Process* process,
DestroyReason reason, int exit_code) override;
virtual ~InterceptingTargetObserver() {}
InterceptingProcessObserver& process_observer() { return dispatcher_; }
InterceptingProcessObserver dispatcher_;
InterceptionWorkflow* workflow_;
} // namespace internal
using SimpleErrorFunction = std::function<void(const zxdb::Err&)>;
// Controls the interactions with the debug agent.
// Most of the operations on this API are synchronous. They expect a loop
// running in another thread to deal with the actions, and waits for the loop to
// complete the actions before returning from the method calls. In fidlcat,
// Go() is called in a separate thread to start the loop. The other operations
// - Initialize, Connect, Attach, etc - post tasks to that loop that are
// executed by the other thread.
class InterceptionWorkflow {
friend class internal::InterceptingThreadObserver;
friend class DataForZxChannelTest;
friend class ProcessController;
// For testing, you can provide your own |session| and |loop|
InterceptionWorkflow(zxdb::Session* session,
debug_ipc::PlatformMessageLoop* loop);
// Some initialization steps:
// - Set the paths for the zxdb client to look for symbols.
// - Make sure that the data are routed from the client to the session
void Initialize(const std::vector<std::string>& symbol_paths);
// Connect the workflow to the host/port pair given. |and_then| is posted to
// the loop on completion.
void Connect(const std::string& host, uint16_t port,
SimpleErrorFunction and_then);
// Attach the workflow to the given koid. Must be connected. |and_then| is
// posted to the loop on completion.
void Attach(uint64_t process_koid, SimpleErrorFunction and_then);
// Detach from one target. session() keeps track of details about the Target
// object; this just reduces the number of targets to which we are attached by
// one, and shuts down if we hit 0.
void Detach();
// Run the given |command| and attach to it. Must be connected. |and_then|
// is posted to the loop on completion.
void Launch(const std::vector<std::string>& command,
SimpleErrorFunction and_then);
// Sets breakpoints for the various methods we intercept (zx_channel_*, etc)
// for the given |target|
void SetBreakpoints(zxdb::Target* target = nullptr);
// Sets breakpoints for the various methods we intercept (zx_channel_*, etc)
// for the process with given |process_koid|.
void SetBreakpoints(uint64_t process_koid);
// Sets the user-callback to be run when we intercept a zx_channel_write call.
void SetZxChannelWriteCallback(ZxChannelCallback&& callback) {
zx_channel_write_callback_ = std::move(callback);
// Sets the user-callback to be run when we intercept a zx_channel_read call.
void SetZxChannelReadCallback(ZxChannelCallback&& callback) {
zx_channel_read_callback_ = std::move(callback);
// Starts running the loop. Returns when loop is (asynchronously) terminated.
void Go();
void Shutdown() {
session()->Disconnect([this](const zxdb::Err& err) {
loop_->PostTask(FROM_HERE, [this]() { loop_->QuitNow(); });
zxdb::Session* session() const { return session_; }
InterceptionWorkflow(const InterceptionWorkflow&) = delete;
InterceptionWorkflow& operator=(const InterceptionWorkflow&) = delete;
zxdb::Target* GetTarget(uint64_t process_koid = ULLONG_MAX);
template <class T>
void OnZxChannelAction(zxdb::Thread* thread);
void AddObserver(zxdb::Target* target);
debug_ipc::BufferedFD buffer_;
zxdb::Session* session_;
bool delete_session_;
debug_ipc::PlatformMessageLoop* loop_;
bool delete_loop_;
// -1 means "we already shut down".
int target_count_;
internal::InterceptingTargetObserver observer_;
ZxChannelCallback zx_channel_write_callback_;
ZxChannelCallback zx_channel_read_callback_;
static const char kZxChannelWriteName[];
static const char kZxChannelReadName[];
} // namespace fidlcat