117 lines
3.7 KiB
C++
117 lines
3.7 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2015 Ben Vanik. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "xenia/cpu/breakpoint.h"
|
|
|
|
#include "xenia/base/string_util.h"
|
|
#include "xenia/cpu/backend/backend.h"
|
|
#include "xenia/cpu/backend/code_cache.h"
|
|
|
|
namespace xe {
|
|
namespace cpu {
|
|
|
|
Breakpoint::Breakpoint(Processor* processor, AddressType address_type,
|
|
uint64_t address, HitCallback hit_callback)
|
|
: processor_(processor),
|
|
address_type_(address_type),
|
|
address_(address),
|
|
hit_callback_(hit_callback) {}
|
|
|
|
Breakpoint::~Breakpoint() { assert_false(installed_); }
|
|
|
|
void Breakpoint::Install() {
|
|
assert_false(installed_);
|
|
processor_->backend()->InstallBreakpoint(this);
|
|
installed_ = true;
|
|
}
|
|
|
|
void Breakpoint::Uninstall() {
|
|
assert_true(installed_);
|
|
processor_->backend()->UninstallBreakpoint(this);
|
|
installed_ = false;
|
|
}
|
|
|
|
std::string Breakpoint::to_string() const {
|
|
if (address_type_ == AddressType::kGuest) {
|
|
auto str =
|
|
std::string("PPC ") + xe::string_util::to_hex_string(guest_address());
|
|
auto functions = processor_->FindFunctionsWithAddress(guest_address());
|
|
if (functions.empty()) {
|
|
return str;
|
|
}
|
|
str += " " + functions[0]->name();
|
|
return str;
|
|
} else {
|
|
return std::string("x64 ") + xe::string_util::to_hex_string(host_address());
|
|
}
|
|
}
|
|
|
|
GuestFunction* Breakpoint::guest_function() const {
|
|
if (address_type_ == AddressType::kGuest) {
|
|
auto functions = processor_->FindFunctionsWithAddress(guest_address());
|
|
if (functions.empty()) {
|
|
return nullptr;
|
|
} else if (functions[0]->is_guest()) {
|
|
return static_cast<xe::cpu::GuestFunction*>(functions[0]);
|
|
}
|
|
return nullptr;
|
|
} else {
|
|
return processor_->backend()->code_cache()->LookupFunction(host_address());
|
|
}
|
|
}
|
|
|
|
bool Breakpoint::ContainsHostAddress(uintptr_t search_address) const {
|
|
bool contains = false;
|
|
ForEachHostAddress([&contains, search_address](uintptr_t host_address) {
|
|
if (host_address == search_address) {
|
|
contains = true;
|
|
}
|
|
});
|
|
return contains;
|
|
}
|
|
|
|
void Breakpoint::ForEachHostAddress(
|
|
std::function<void(uintptr_t)> callback) const {
|
|
if (address_type_ == AddressType::kGuest) {
|
|
auto guest_address = this->guest_address();
|
|
|
|
// Lookup all functions that contain this guest address and patch them.
|
|
auto functions = processor_->FindFunctionsWithAddress(guest_address);
|
|
|
|
if (functions.empty()) {
|
|
// If function does not exist demand it, as we need someplace to put our
|
|
// breakpoint. Note that this follows the same resolution rules as the
|
|
// JIT, so what's returned is the function the JIT would have jumped to.
|
|
auto fn = processor_->ResolveFunction(guest_address);
|
|
if (!fn) {
|
|
// TODO(benvanik): error out better with 'invalid breakpoint'?
|
|
assert_not_null(fn);
|
|
return;
|
|
}
|
|
functions.push_back(fn);
|
|
}
|
|
assert_false(functions.empty());
|
|
|
|
for (auto function : functions) {
|
|
// TODO(benvanik): other function types.
|
|
assert_true(function->is_guest());
|
|
auto guest_function = reinterpret_cast<GuestFunction*>(function);
|
|
uintptr_t host_address =
|
|
guest_function->MapGuestAddressToMachineCode(guest_address);
|
|
assert_not_zero(host_address);
|
|
callback(host_address);
|
|
}
|
|
} else {
|
|
// Direct host address patching.
|
|
callback(host_address());
|
|
}
|
|
}
|
|
|
|
} // namespace cpu
|
|
} // namespace xe
|