text: // Copyright 2018 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 "src/developer/debug/debug_agent/remote_api_adapter.h"
  
  #include "src/developer/debug/debug_agent/remote_api.h"
  #include "src/developer/debug/ipc/agent_protocol.h"
  #include "src/developer/debug/ipc/message_reader.h"
  #include "src/developer/debug/ipc/message_writer.h"
  #include "src/developer/debug/ipc/protocol.h"
  #include "src/developer/debug/shared/logging/logging.h"
  #include "src/developer/debug/shared/stream_buffer.h"
  
  namespace debug_agent {
  
  namespace {
  
  // Deserializes the request based on type, calls the given hander in the
  // RemoteAPI, and then sends the reply back to the client.
  template <typename RequestMsg, typename ReplyMsg>
  void DispatchMessage(RemoteAPIAdapter* adapter,
                       void (RemoteAPI::*handler)(const RequestMsg&, ReplyMsg*),
                       std::vector<char> data, const char* type_string) {
    debug_ipc::MessageReader reader(std::move(data));
  
    RequestMsg request;
    uint32_t transaction_id = 0;
    if (!debug_ipc::ReadRequest(&reader, &request, &transaction_id)) {
      fprintf(stderr, "Got bad debugger %sRequest, ignoring.\n", type_string);
      return;
    }
  
    ReplyMsg reply;
    (adapter->api()->*handler)(request, &reply);
  
    debug_ipc::MessageWriter writer;
    debug_ipc::WriteReply(reply, transaction_id, &writer);
  
    adapter->stream()->Write(writer.MessageComplete());
  }
  
  }  // namespace
  
  RemoteAPIAdapter::RemoteAPIAdapter(RemoteAPI* remote_api, debug_ipc::StreamBuffer* stream)
      : api_(remote_api), stream_(stream) {}
  
  RemoteAPIAdapter::~RemoteAPIAdapter() {}
  
  void RemoteAPIAdapter::OnStreamReadable() {
    while (true) {
      debug_ipc::MsgHeader header;
      size_t bytes_read = stream_->Peek(reinterpret_cast<char*>(&header), sizeof(header));
      if (bytes_read != sizeof(header))
        return;  // Don't have enough data for the header.
      if (!stream_->IsAvailable(header.size))
        return;  // Entire message hasn't arrived yet.
  
      // The message size includes the header.
      std::vector<char> buffer(header.size);
      stream_->Read(buffer.data(), header.size);
  
      // Range check the message type.
      if (header.type == debug_ipc::MsgHeader::Type::kNone ||
          header.type >= debug_ipc::MsgHeader::Type::kNumMessages) {
        fprintf(stderr, "Invalid message type %u, ignoring.\n", static_cast<unsigned>(header.type));
        return;
      }
  
  // Dispatches a message type assuming the handler function name, request
  // struct type, and reply struct type are all based on the message type name.
  // For example, MsgHeader::Type::kFoo will call:
  //   api->OnFoo(FooRequest, FooReply*);
  #define DISPATCH(msg_type)                                                     \
    case debug_ipc::MsgHeader::Type::k##msg_type:                                \
      DispatchMessage<debug_ipc::msg_type##Request, debug_ipc::msg_type##Reply>( \
          this, &RemoteAPI::On##msg_type, std::move(buffer), #msg_type);         \
      break
  
      switch (header.type) {
        DISPATCH(AddOrChangeBreakpoint);
        DISPATCH(AddressSpace);
        DISPATCH(ConfigAgent);
        DISPATCH(Detach);
        DISPATCH(JobFilter);
        DISPATCH(Hello);
        DISPATCH(Kill);
        DISPATCH(Launch);
        DISPATCH(Modules);
        DISPATCH(Pause);
        DISPATCH(ProcessStatus);
        DISPATCH(ProcessTree);
        DISPATCH(QuitAgent);
        DISPATCH(ReadMemory);
        DISPATCH(ReadRegisters);
        DISPATCH(WriteRegisters);
        DISPATCH(RemoveBreakpoint);
        DISPATCH(Resume);
        DISPATCH(Status);
        DISPATCH(SysInfo);
        DISPATCH(ThreadStatus);
        DISPATCH(Threads);
        DISPATCH(WriteMemory);
        DISPATCH(LoadInfoHandleTable);
        DISPATCH(UpdateGlobalSettings);
  
        // Attach is special (see remote_api.h): forward the raw data instead of
        // a deserialized version.
        case debug_ipc::MsgHeader::Type::kAttach:
          api_->OnAttach(std::move(buffer));
          break;
  
        // Explicitly no "default" to get warnings about unhandled message types,
        // but need to handle these "not a message" types to avoid this warning.
        case debug_ipc::MsgHeader::Type::kNone:
        case debug_ipc::MsgHeader::Type::kNumMessages:
        case debug_ipc::MsgHeader::Type::kNotifyException:
        case debug_ipc::MsgHeader::Type::kNotifyIO:
        case debug_ipc::MsgHeader::Type::kNotifyModules:
        case debug_ipc::MsgHeader::Type::kNotifyProcessExiting:
        case debug_ipc::MsgHeader::Type::kNotifyProcessStarting:
        case debug_ipc::MsgHeader::Type::kNotifyThreadStarting:
        case debug_ipc::MsgHeader::Type::kNotifyThreadExiting:
          break;  // Avoid warning
      }
  
  #undef DISPATCH
    }
  }
  
  }  // namespace debug_agent
  