blob: f43cac4f85ce5dc7edf79906d3f26379bbccdd8e [file] [log] [blame]
// 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/zxdb/client/remote_api_test.h"
#include <inttypes.h>
#include "src/developer/debug/ipc/protocol.h"
#include "src/developer/debug/zxdb/client/frame.h"
#include "src/developer/debug/zxdb/client/mock_remote_api.h"
#include "src/developer/debug/zxdb/client/process_impl.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/system.h"
#include "src/developer/debug/zxdb/client/target_impl.h"
#include "src/developer/debug/zxdb/client/thread_impl.h"
#include "src/developer/debug/zxdb/symbols/mock_module_symbols.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace zxdb {
void RemoteAPITest::SetUp() {
session_ = std::make_unique<Session>(GetRemoteAPIImpl(), GetArch());
session_->system().settings().SetBool(ClientSettings::System::kPauseOnAttach, true);
session_->system().settings().SetBool(ClientSettings::System::kPauseOnLaunch, true);
}
void RemoteAPITest::TearDown() { session_.reset(); }
void RemoteAPITest::InjectModule(Process* process, fxl::RefPtr<ModuleSymbols> mod_sym,
const std::string& name, uint64_t load_address,
const std::string& build_id) {
session().system().GetSymbols()->InjectModuleForTesting(build_id, mod_sym.get());
std::vector<debug_ipc::Module> modules;
debug_ipc::Module load;
load.name = name;
load.base = load_address;
load.build_id = build_id;
modules.push_back(load);
// Need to convert to an actual ProcessImpl.
ProcessImpl* process_impl = session().system().ProcessImplFromKoid(process->GetKoid());
FX_CHECK(process_impl);
process_impl->OnModules(modules, {});
}
fxl::RefPtr<MockModuleSymbols> RemoteAPITest::InjectMockModule(Process* process,
uint64_t load_address) {
// Index to generate unique names for each mock module created. Must start > 0 because this is
// used to generate a load address that can't be null.
static int next_mock_module_id = 0;
next_mock_module_id++;
// Generate a load address if necessary.
int effective_load_address;
if (load_address) {
effective_load_address = load_address;
} else {
// Use our unique index as the high 32-bits, with the low bits as 0.
effective_load_address = static_cast<uint64_t>(next_mock_module_id) << 32;
}
std::string build_id = "mock_build_id_" + std::to_string(next_mock_module_id);
auto module = fxl::MakeRefCounted<MockModuleSymbols>("mock_modules.so");
InjectModule(process, module, "mock_module", effective_load_address, build_id);
return module;
}
Process* RemoteAPITest::InjectProcess(uint64_t process_koid) {
auto targets = session().system().GetTargetImpls();
if (targets.size() != 1u) {
ADD_FAILURE();
return nullptr;
}
if (targets[0]->GetState() != Target::State::kNone) {
ADD_FAILURE();
return nullptr;
}
targets[0]->CreateProcessForTesting(process_koid, "test");
return targets[0]->GetProcess();
}
Thread* RemoteAPITest::InjectThread(uint64_t process_koid, uint64_t thread_koid) {
debug_ipc::NotifyThread notify;
notify.record.id = {.process = process_koid, .thread = thread_koid};
notify.record.name = fxl::StringPrintf("test %" PRIu64, thread_koid);
notify.record.state = debug_ipc::ThreadRecord::State::kRunning;
session_->DispatchNotifyThreadStarting(notify);
return session_->ThreadImplFromKoid(notify.record.id);
}
void RemoteAPITest::InjectException(const debug_ipc::NotifyException& exception) {
session_->DispatchNotifyException(exception);
}
void RemoteAPITest::InjectExceptionWithStack(const debug_ipc::NotifyException& exception,
std::vector<std::unique_ptr<Frame>> frames,
bool has_all_frames) {
ThreadImpl* thread = session_->ThreadImplFromKoid(exception.thread.id);
FX_CHECK(thread); // Tests should always pass valid KOIDs.
// Create an exception record with a thread frame so it's valid. There must be one frame even
// though the stack will be immediately overwritten.
debug_ipc::NotifyException modified(exception);
modified.thread.stack_amount = debug_ipc::ThreadRecord::StackAmount::kMinimal;
modified.thread.frames.clear();
if (!frames.empty())
modified.thread.frames.emplace_back(frames[0]->GetAddress(), frames[0]->GetStackPointer());
// To manually set the thread state, set the general metadata which will pick up the basic flags
// and the first stack frame. Then re-set the stack frame with the information passed in by our
// caller.
thread->SetMetadata(modified.thread);
thread->GetStack().SetFramesForTest(std::move(frames), has_all_frames);
// Normal exception dispatch path, but skipping the metadata (so the metadata set above will
// stay).
session_->DispatchNotifyException(modified, false);
}
void RemoteAPITest::InjectExceptionWithStack(
uint64_t process_koid, uint64_t thread_koid, debug_ipc::ExceptionType exception_type,
std::vector<std::unique_ptr<Frame>> frames, bool has_all_frames,
const std::vector<debug_ipc::BreakpointStats>& breakpoints) {
debug_ipc::NotifyException exception;
exception.type = exception_type;
exception.thread.id = {.process = process_koid, .thread = thread_koid};
exception.thread.state = debug_ipc::ThreadRecord::State::kBlocked;
exception.thread.blocked_reason = debug_ipc::ThreadRecord::BlockedReason::kException;
exception.hit_breakpoints = breakpoints;
InjectExceptionWithStack(exception, std::move(frames), has_all_frames);
}
std::unique_ptr<RemoteAPI> RemoteAPITest::GetRemoteAPIImpl() {
auto remote_api = std::make_unique<MockRemoteAPI>();
mock_remote_api_ = remote_api.get();
return remote_api;
}
} // namespace zxdb