diff --git a/src/alloy/runtime/debugger.cc b/src/alloy/runtime/debugger.cc new file mode 100644 index 000000000..8ce4c02b4 --- /dev/null +++ b/src/alloy/runtime/debugger.cc @@ -0,0 +1,40 @@ +/** + ****************************************************************************** + * 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. * + ****************************************************************************** + */ + +#include + +using namespace alloy; +using namespace alloy::runtime; + + +Breakpoint::Breakpoint(Type type, uint64_t address) : + type_(type), address_(address) { +} + +Breakpoint::~Breakpoint() { +} + +Debugger::Debugger(Runtime* runtime) : + runtime_(runtime) { +} + +Debugger::~Debugger() { +} + +int Debugger::AddBreakpoint(Breakpoint* breakpoint) { + return 1; +} + +int Debugger::RemoveBreakpoint(Breakpoint* breakpoint) { + return 1; +} + +int Debugger::RemoveAllBreakpoints() { + return 1; +} diff --git a/src/alloy/runtime/debugger.h b/src/alloy/runtime/debugger.h new file mode 100644 index 000000000..b371c85e3 --- /dev/null +++ b/src/alloy/runtime/debugger.h @@ -0,0 +1,61 @@ +/** + ****************************************************************************** + * 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 ALLOY_RUNTIME_DEBUGGER_H_ +#define ALLOY_RUNTIME_DEBUGGER_H_ + +#include + + +namespace alloy { +namespace runtime { + +class Runtime; + + +class Breakpoint { +public: + enum Type { + TEMP_TYPE, + CODE_TYPE, + }; +public: + Breakpoint(Type type, uint64_t address); + ~Breakpoint(); + + Type type() const { return type_; } + uint64_t address() const { return address_; } + +private: + Type type_; + uint64_t address_; +}; + + +class Debugger { +public: + Debugger(Runtime* runtime); + ~Debugger(); + + Runtime* runtime() const { return runtime_; } + + int AddBreakpoint(Breakpoint* breakpoint); + int RemoveBreakpoint(Breakpoint* breakpoint); + int RemoveAllBreakpoints(); + +private: + Runtime* runtime_; +}; + + +} // namespace runtime +} // namespace alloy + + +#endif // ALLOY_RUNTIME_DEBUGGER_H_ diff --git a/src/alloy/runtime/runtime.cc b/src/alloy/runtime/runtime.cc index d27bec265..2f9b2e2d3 100644 --- a/src/alloy/runtime/runtime.cc +++ b/src/alloy/runtime/runtime.cc @@ -25,7 +25,7 @@ DEFINE_string(runtime_backend, "any", Runtime::Runtime(Memory* memory) : - memory_(memory), backend_(0), frontend_(0), + memory_(memory), debugger_(0), backend_(0), frontend_(0), access_callbacks_(0) { tracing::Initialize(); modules_lock_ = AllocMutex(10000); @@ -51,6 +51,7 @@ Runtime::~Runtime() { delete frontend_; delete backend_; + delete debugger_; tracing::Flush(); } @@ -67,6 +68,9 @@ int Runtime::Initialize(Frontend* frontend, Backend* backend) { return result; } + // Create debugger first. Other types hook up to it. + debugger_ = new Debugger(this); + if (frontend_ || backend_) { return 1; } diff --git a/src/alloy/runtime/runtime.h b/src/alloy/runtime/runtime.h index dc3e4be4f..4bc8b3730 100644 --- a/src/alloy/runtime/runtime.h +++ b/src/alloy/runtime/runtime.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ public: virtual ~Runtime(); Memory* memory() const { return memory_; } + Debugger* debugger() const { return debugger_; } frontend::Frontend* frontend() const { return frontend_; } backend::Backend* backend() const { return backend_; } RegisterAccessCallbacks* access_callbacks() const { @@ -62,6 +64,8 @@ private: protected: Memory* memory_; + Debugger* debugger_; + frontend::Frontend* frontend_; backend::Backend* backend_; diff --git a/src/alloy/runtime/simple/sources.gypi b/src/alloy/runtime/simple/sources.gypi deleted file mode 100644 index 4ca63f1e4..000000000 --- a/src/alloy/runtime/simple/sources.gypi +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2013 Ben Vanik. All Rights Reserved. -{ - 'sources': [ - 'simple_context.cc', - 'simple_context.h', - 'simple_memory.cc', - 'simple_memory.h', - 'simple_runtime.cc', - 'simple_runtime.h', - 'simple_thread_state.cc', - 'simple_thread_state.h', - ], -} diff --git a/src/alloy/runtime/sources.gypi b/src/alloy/runtime/sources.gypi index 65392b319..d4ef940ab 100644 --- a/src/alloy/runtime/sources.gypi +++ b/src/alloy/runtime/sources.gypi @@ -3,6 +3,8 @@ 'sources': [ 'debug_info.cc', 'debug_info.h', + 'debugger.cc', + 'debugger.h', 'entry_table.cc', 'entry_table.h', 'function.cc', @@ -22,6 +24,5 @@ ], 'includes': [ - #'simple/sources.gypi', ], } diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index a9dc25d8e..8dbd17528 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -11,6 +11,7 @@ #include +#include #include #include #include @@ -55,6 +56,7 @@ Processor::Processor(Emulator* emulator) : runtime_(0), memory_(emulator->memory()), interrupt_thread_lock_(NULL), interrupt_thread_state_(NULL), interrupt_thread_block_(0), + breakpoints_lock_(0), DebugTarget(emulator->debug_server()) { InitializeIfNeeded(); @@ -64,6 +66,15 @@ Processor::Processor(Emulator* emulator) : Processor::~Processor() { emulator_->debug_server()->RemoveTarget("cpu"); + xe_mutex_lock(breakpoints_lock_); + for (auto it = breakpoints_.begin(); it != breakpoints_.end(); ++it) { + Breakpoint* breakpoint = it->second; + delete breakpoint; + } + breakpoints_.clear(); + xe_mutex_unlock(breakpoints_lock_); + xe_mutex_free(breakpoints_lock_); + if (interrupt_thread_block_) { memory_->HeapFree(interrupt_thread_block_, 2048); delete interrupt_thread_state_; @@ -98,6 +109,8 @@ int Processor::Setup() { 0, 2048, MEMORY_FLAG_ZERO); interrupt_thread_state_->context()->r[13] = interrupt_thread_block_; + breakpoints_lock_ = xe_mutex_alloc(10000); + return 0; } @@ -280,12 +293,94 @@ json_t* Processor::OnDebugRequest( return fn_json; } else if (xestrcmpa(command, "add_breakpoints") == 0) { - // breakpoints: [] + // breakpoints: [{}] + json_t* breakpoints_json = json_object_get(request, "breakpoints"); + if (!breakpoints_json || !json_is_array(breakpoints_json)) { + succeeded = false; + return json_string("Breakpoints not specified"); + } + if (!json_array_size(breakpoints_json)) { + // No-op; + return json_null(); + } + for (size_t n = 0; n < json_array_size(breakpoints_json); n++) { + json_t* breakpoint_json = json_array_get(breakpoints_json, n); + if (!breakpoint_json || !json_is_object(breakpoint_json)) { + succeeded = false; + return json_string("Invalid breakpoint type"); + } + + json_t* breakpoint_id_json = json_object_get(breakpoint_json, "id"); + json_t* type_json = json_object_get(breakpoint_json, "type"); + json_t* address_json = json_object_get(breakpoint_json, "address"); + if (!breakpoint_id_json || !json_is_string(breakpoint_id_json) || + !type_json || !json_is_string(type_json) || + !address_json || !json_is_number(address_json)) { + succeeded = false; + return json_string("Invalid breakpoint members"); + } + const char* breakpoint_id = json_string_value(breakpoint_id_json); + const char* type_str = json_string_value(type_json); + uint64_t address = (uint64_t)json_number_value(address_json); + Breakpoint::Type type; + if (xestrcmpa(type_str, "temp") == 0) { + type = Breakpoint::TEMP_TYPE; + } else if (xestrcmpa(type_str, "code") == 0) { + type = Breakpoint::CODE_TYPE; + } else { + succeeded = false; + return json_string("Unknown breakpoint type"); + } + + Breakpoint* breakpoint = new Breakpoint( + type, address); + xe_mutex_lock(breakpoints_lock_); + breakpoints_[breakpoint_id] = breakpoint; + int result = runtime_->debugger()->AddBreakpoint(breakpoint); + xe_mutex_unlock(breakpoints_lock_); + if (result) { + succeeded = false; + return json_string("Error adding breakpoint"); + } + } return json_null(); } else if (xestrcmpa(command, "remove_breakpoints") == 0) { // breakpointIds: ['id'] + json_t* breakpoint_ids_json = json_object_get(request, "breakpointIds"); + if (!breakpoint_ids_json || !json_is_array(breakpoint_ids_json)) { + succeeded = false; + return json_string("Breakpoint IDs not specified"); + } + if (!json_array_size(breakpoint_ids_json)) { + // No-op; + return json_null(); + } + for (size_t n = 0; n < json_array_size(breakpoint_ids_json); n++) { + json_t* breakpoint_id_json = json_array_get(breakpoint_ids_json, n); + if (!breakpoint_id_json || !json_is_string(breakpoint_id_json)) { + succeeded = false; + return json_string("Invalid breakpoint ID type"); + } + const char* breakpoint_id = json_string_value(breakpoint_id_json); + xe_mutex_lock(breakpoints_lock_); + Breakpoint* breakpoint = breakpoints_[breakpoint_id]; + if (breakpoint) { + breakpoints_.erase(breakpoint_id); + runtime_->debugger()->RemoveBreakpoint(breakpoint); + delete breakpoint; + } + xe_mutex_unlock(breakpoints_lock_); + } return json_null(); } else if (xestrcmpa(command, "remove_all_breakpoints") == 0) { + xe_mutex_lock(breakpoints_lock_); + runtime_->debugger()->RemoveAllBreakpoints(); + for (auto it = breakpoints_.begin(); it != breakpoints_.end(); ++it) { + Breakpoint* breakpoint = it->second; + delete breakpoint; + } + breakpoints_.clear(); + xe_mutex_unlock(breakpoints_lock_); return json_null(); } else { succeeded = false; diff --git a/src/xenia/cpu/processor.h b/src/xenia/cpu/processor.h index 2fb378495..72b4d1963 100644 --- a/src/xenia/cpu/processor.h +++ b/src/xenia/cpu/processor.h @@ -16,6 +16,7 @@ #include +XEDECLARECLASS2(alloy, runtime, Breakpoint); XEDECLARECLASS1(xe, Emulator); XEDECLARECLASS1(xe, ExportResolver); XEDECLARECLASS2(xe, cpu, XenonMemory); @@ -69,6 +70,10 @@ private: xe_mutex_t* interrupt_thread_lock_; XenonThreadState* interrupt_thread_state_; uint64_t interrupt_thread_block_; + + xe_mutex_t* breakpoints_lock_; + typedef std::unordered_map BreakpointMap; + BreakpointMap breakpoints_; };