Files
Xenia-Canary/src/xenia/debug/breakpoint.h
Ben Vanik 5d033f9cb3 A new debugger.
Lots of bugs/rough edges/etc - issues will be filed.
Old-style debugging still works (just use --emit_source_annotations to get
the helpful movs back and --break_on_instruction will still fire).
2015-09-20 21:31:05 -07:00

186 lines
5.2 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_DEBUG_BREAKPOINT_H_
#define XENIA_DEBUG_BREAKPOINT_H_
#include <functional>
#include <string>
#include <utility>
#include <vector>
#include "xenia/base/assert.h"
#include "xenia/cpu/function.h"
namespace xe {
namespace debug {
class Debugger;
class Breakpoint {
public:
enum class Type {
kStepping,
kCode,
kData,
kExport,
kGpu,
};
Breakpoint(Debugger* debugger, Type type)
: debugger_(debugger), type_(type) {}
virtual ~Breakpoint() { assert_false(installed_); }
Debugger* debugger() const { return debugger_; }
Type type() const { return type_; }
// Whether the breakpoint has been enabled by the user.
bool is_enabled() const { return enabled_; }
// Toggles the breakpoint state.
// Assumes the caller holds the global lock.
void set_enabled(bool is_enabled) {
enabled_ = is_enabled;
if (!enabled_ && installed_) {
Uninstall();
} else if (enabled_ && !installed_ && !suspend_count_) {
Install();
}
}
virtual std::string to_string() const { return "Breakpoint"; }
private:
friend class Debugger;
// Suspends the breakpoint until it is resumed with Resume.
// This preserves the user enabled state.
// Assumes the caller holds the global lock.
void Suspend() {
++suspend_count_;
if (installed_) {
Uninstall();
}
}
// Resumes a previously-suspended breakpoint, reinstalling it if required.
// Assumes the caller holds the global lock.
void Resume() {
--suspend_count_;
if (!suspend_count_ && enabled_) {
Install();
}
}
protected:
virtual void Install() { installed_ = true; }
virtual void Uninstall() { installed_ = false; }
Debugger* debugger_ = nullptr;
Type type_;
// True if patched into code.
bool installed_ = false;
// True if user enabled.
bool enabled_ = true;
// Depth of suspends (must be 0 to be installed). Defaults to suspended so
// that it's never installed unless the debugger knows about it.
int suspend_count_ = 1;
};
class CodeBreakpoint : public Breakpoint {
public:
enum class AddressType {
kGuest,
kHost,
};
CodeBreakpoint(Debugger* debugger, AddressType address_type, uint64_t address)
: CodeBreakpoint(debugger, Type::kCode, address_type, address) {}
AddressType address_type() const { return address_type_; }
uint64_t address() const { return address_; }
uint32_t guest_address() const {
assert_true(address_type_ == AddressType::kGuest);
return static_cast<uint32_t>(address_);
}
uintptr_t host_address() const {
assert_true(address_type_ == AddressType::kHost);
return static_cast<uintptr_t>(address_);
}
// TODO(benvanik): additional support:
// - conditions (expressions? etc?)
// - thread restrictions (set of thread ids)
// - hit counters
// Returns a guest function that contains the guest address, if any.
// If there are multiple functions that contain the address a random one will
// be returned. If this is a host-address code breakpoint this will attempt to
// find a function with that address and otherwise return nullptr.
xe::cpu::GuestFunction* guest_function() const;
// Returns true if this breakpoint, when installed, contains the given host
// address.
bool ContainsHostAddress(uintptr_t search_address) const;
std::string to_string() const override;
protected:
CodeBreakpoint(Debugger* debugger, Type type, AddressType address_type,
uint64_t address)
: Breakpoint(debugger, type),
address_type_(address_type),
address_(address) {}
void ForEachHostAddress(std::function<void(uintptr_t)> callback) const;
void Install() override;
void Uninstall() override;
uint16_t PatchAddress(uintptr_t host_address);
void UnpatchAddress(uintptr_t host_address, uint16_t original_bytes);
AddressType address_type_;
uint64_t address_ = 0;
// Pairs of x64 address : original byte values when installed.
// These locations have been patched and must be restored when the breakpoint
// is uninstalled.
std::vector<std::pair<uintptr_t, uint16_t>> patches_;
};
class StepBreakpoint : public CodeBreakpoint {
public:
StepBreakpoint(Debugger* debugger, AddressType address_type, uint64_t address)
: CodeBreakpoint(debugger, Type::kStepping, address_type, address) {}
};
class DataBreakpoint : public Breakpoint {
public:
explicit DataBreakpoint(Debugger* debugger)
: Breakpoint(debugger, Type::kData) {}
};
class ExportBreakpoint : public Breakpoint {
public:
explicit ExportBreakpoint(Debugger* debugger)
: Breakpoint(debugger, Type::kExport) {}
};
class GpuBreakpoint : public Breakpoint {
public:
explicit GpuBreakpoint(Debugger* debugger)
: Breakpoint(debugger, Type::kGpu) {}
};
} // namespace debug
} // namespace xe
#endif // XENIA_DEBUG_BREAKPOINT_H_