blob: 505038c4612c51bc195ccf47a99a708957fc2cf8 [file] [log] [blame]
// Copyright 2020 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 "context.h"
#include "src/developer/debug/shared/logging/logging.h"
#include "src/developer/debug/zxdb/client/process.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/client/setting_schema_definition.h"
#include "src/developer/debug/zxdb/client/thread.h"
#include "src/developer/debug/zxdb/debug_adapter/handlers/request_attach.h"
#include "src/developer/debug/zxdb/debug_adapter/handlers/request_breakpoint.h"
#include "src/developer/debug/zxdb/debug_adapter/handlers/request_continue.h"
#include "src/developer/debug/zxdb/debug_adapter/handlers/request_launch.h"
#include "src/developer/debug/zxdb/debug_adapter/handlers/request_next.h"
#include "src/developer/debug/zxdb/debug_adapter/handlers/request_pause.h"
#include "src/developer/debug/zxdb/debug_adapter/handlers/request_stacktrace.h"
#include "src/developer/debug/zxdb/debug_adapter/handlers/request_threads.h"
#include "src/developer/debug/zxdb/debug_adapter/server.h"
namespace zxdb {
DebugAdapterContext::DebugAdapterContext(Session* session, debug_ipc::StreamBuffer* stream)
: session_(session), dap_(dap::Session::create()) {
reader_ = std::make_shared<DebugAdapterReader>(stream);
writer_ = std::make_shared<DebugAdapterWriter>(stream);
dap_->registerHandler([this](const dap::InitializeRequest& req) {
DEBUG_LOG(DebugAdapter) << "InitializeRequest received";
dap::InitializeResponse response;
response.supportsFunctionBreakpoints = false;
response.supportsConfigurationDoneRequest = true;
response.supportsEvaluateForHovers = false;
if (req.supportsInvalidatedEvent) {
this->supports_invalidate_event_ = req.supportsInvalidatedEvent.value();
}
if (req.supportsRunInTerminalRequest) {
this->supports_run_in_terminal_ = req.supportsRunInTerminalRequest.value();
}
return response;
});
dap_->registerSentHandler([this](const dap::ResponseOrError<dap::InitializeResponse>& response) {
DEBUG_LOG(DebugAdapter) << "InitializeResponse sent";
// Set up events and handlers now. All messages should be sent only after Initialize response
// is sent. Setting up earlier would lead to events and responses being sent before Initialize
// request is processed.
Init();
dap_->send(dap::InitializedEvent());
});
dap_->onError([](const char* msg) { FX_LOGS(ERROR) << "dap::Session error:" << msg << "\r\n"; });
dap_->connect(reader_, writer_);
}
DebugAdapterContext::~DebugAdapterContext() {
if (init_done_) {
session()->thread_observers().RemoveObserver(this);
session()->process_observers().RemoveObserver(this);
}
}
void DebugAdapterContext::Init() {
// Register handlers with dap module.
dap_->registerHandler([](const dap::DisconnectRequest& req) {
DEBUG_LOG(DebugAdapter) << "DisconnectRequest received";
return dap::DisconnectResponse();
});
dap_->registerHandler([this](const dap::LaunchRequestZxdb& req) {
DEBUG_LOG(DebugAdapter) << "LaunchRequest received";
return OnRequestLaunch(this, req);
});
dap_->registerHandler([](const dap::SetExceptionBreakpointsRequest& req) {
DEBUG_LOG(DebugAdapter) << "SetExceptionBreakpointsRequest received";
dap::SetExceptionBreakpointsResponse response;
return response;
});
dap_->registerHandler([this](const dap::SetBreakpointsRequest& req) {
DEBUG_LOG(DebugAdapter) << "SetBreakpointsRequest received";
return OnRequestBreakpoint(this, req);
});
dap_->registerHandler([](const dap::ConfigurationDoneRequest& req) {
DEBUG_LOG(DebugAdapter) << "ConfigurationDoneRequest received";
return dap::ConfigurationDoneResponse();
});
dap_->registerHandler([this](const dap::AttachRequestZxdb& req) {
DEBUG_LOG(DebugAdapter) << "AttachRequest received";
return OnRequestAttach(this, req);
});
dap_->registerHandler([this](const dap::ThreadsRequest& req) {
DEBUG_LOG(DebugAdapter) << "ThreadRequest received";
return OnRequestThreads(this, req);
});
dap_->registerHandler(
[this](const dap::PauseRequest& req,
std::function<void(dap::ResponseOrError<dap::PauseResponse>)> callback) {
DEBUG_LOG(DebugAdapter) << "PauseRequest received";
OnRequestPause(this, req, callback);
});
dap_->registerHandler([this](const dap::ContinueRequest& req) {
DEBUG_LOG(DebugAdapter) << "ContinueRequest received";
return OnRequestContinue(this, req);
});
dap_->registerHandler(
[this](const dap::NextRequest& req,
std::function<void(dap::ResponseOrError<dap::NextResponse>)> callback) {
DEBUG_LOG(DebugAdapter) << "NextRequest received";
OnRequestNext(this, req, callback);
});
dap_->registerHandler(
[this](const dap::StackTraceRequest& req,
std::function<void(dap::ResponseOrError<dap::StackTraceResponse>)> callback) {
DEBUG_LOG(DebugAdapter) << "StackTraceRequest received";
OnRequestStackTrace(this, req, callback);
});
// Register to zxdb session events
session()->thread_observers().AddObserver(this);
session()->process_observers().AddObserver(this);
init_done_ = true;
}
void DebugAdapterContext::OnStreamReadable() {
auto payload = dap_->getPayload();
if (payload) {
payload();
}
}
void DebugAdapterContext::DidCreateThread(Thread* thread) {
dap::ThreadEvent event;
event.reason = "started";
event.threadId = thread->GetKoid();
dap_->send(event);
}
void DebugAdapterContext::WillDestroyThread(Thread* thread) {
dap::ThreadEvent event;
event.reason = "exited";
event.threadId = thread->GetKoid();
dap_->send(event);
}
void DebugAdapterContext::OnThreadStopped(Thread* thread, const StopInfo& info) {
dap::StoppedEvent event;
switch (info.exception_type) {
case debug_ipc::ExceptionType::kSoftwareBreakpoint:
case debug_ipc::ExceptionType::kHardwareBreakpoint:
event.reason = "breakpoint";
event.description = "Breakpoint hit";
break;
case debug_ipc::ExceptionType::kSingleStep:
event.reason = "step";
break;
case debug_ipc::ExceptionType::kPolicyError:
event.reason = "exception";
event.description = "Policy error";
break;
case debug_ipc::ExceptionType::kPageFault:
event.reason = "exception";
event.description = "Page fault";
break;
case debug_ipc::ExceptionType::kUndefinedInstruction:
event.reason = "exception";
event.description = "Undefined Instruction";
break;
case debug_ipc::ExceptionType::kUnalignedAccess:
event.reason = "exception";
event.description = "Unaligned Access";
break;
default:
event.reason = "unknown";
}
event.threadId = thread->GetKoid();
dap_->send(event);
}
void DebugAdapterContext::OnThreadFramesInvalidated(Thread* thread) {
DeleteFrameIdsForThread(thread);
if (supports_invalidate_event_) {
dap::InvalidatedEvent event;
event.threadId = thread->GetKoid();
dap_->send(event);
}
}
void DebugAdapterContext::DidCreateProcess(Process* process, bool autoattached_to_new_process,
uint64_t timestamp) {
dap::ProcessEvent event;
event.name = process->GetName();
event.isLocalProcess = false;
switch (process->start_type()) {
case Process::StartType::kAttach:
event.startMethod = "attach";
break;
case Process::StartType::kComponent:
case Process::StartType::kLaunch:
event.startMethod = "launch";
break;
}
bool pause_on_attach =
session()->system().settings().GetBool(ClientSettings::System::kPauseOnAttach);
if (autoattached_to_new_process && pause_on_attach) {
event.startMethod = "attachForSuspendedLaunch";
}
dap_->send(event);
}
Target* DebugAdapterContext::GetCurrentTarget() {
auto targets = session()->system().GetTargets();
if (targets.size() > 0) {
// Currently debug adapter supports only one target. The default target is used to attach
// process.
return targets[0];
}
return nullptr;
}
Process* DebugAdapterContext::GetCurrentProcess() {
auto target = GetCurrentTarget();
if (target) {
return target->GetProcess();
}
return nullptr;
}
Thread* DebugAdapterContext::GetThread(uint64_t koid) {
Thread* match = nullptr;
auto process = GetCurrentProcess();
if (process) {
auto threads = process->GetThreads();
for (auto t : threads) {
if (koid == t->GetKoid()) {
match = t;
break;
}
}
}
return match;
}
Err DebugAdapterContext::CheckStoppedThread(Thread* thread) {
if (!thread) {
return Err("Invalid thread.");
}
if (thread->GetState() != debug_ipc::ThreadRecord::State::kBlocked &&
thread->GetState() != debug_ipc::ThreadRecord::State::kCoreDump &&
thread->GetState() != debug_ipc::ThreadRecord::State::kSuspended) {
return Err("Thread should be suspended but thread %llu is %s.",
static_cast<unsigned long long>(thread->GetKoid()),
debug_ipc::ThreadRecord::StateToString(thread->GetState()));
}
return Err();
}
int DebugAdapterContext::IdForFrame(Frame* frame, int stack_index) {
FrameRecord record = {};
record.thread_koid = frame->GetThread()->GetKoid();
record.stack_index = stack_index;
for (auto const& it : id_to_frame_) {
if (it.second.thread_koid == record.thread_koid && it.second.stack_index == stack_index) {
return it.first;
}
}
int current_frame_id = next_frame_id_++;
id_to_frame_[current_frame_id] = record;
return current_frame_id;
}
Frame* DebugAdapterContext::FrameforId(int id) {
// id - 0 is invalid
if (!id) {
return nullptr;
}
if (auto it = id_to_frame_.find(id); it != id_to_frame_.end()) {
Thread* thread = GetThread(it->second.thread_koid);
if (!thread) {
return nullptr;
}
if (thread->GetStack().size() <= static_cast<size_t>(it->second.stack_index)) {
return nullptr;
}
return thread->GetStack()[it->second.stack_index];
}
// Not found
return nullptr;
}
void DebugAdapterContext::DeleteFrameIdsForThread(Thread* thread) {
auto thread_koid = thread->GetKoid();
for (auto it = id_to_frame_.begin(); it != id_to_frame_.end(); it++) {
if (it->second.thread_koid == thread_koid) {
id_to_frame_.erase(it);
}
}
}
} // namespace zxdb