Breakpoint hits reaching all the way to UI.

Nasty json only hackery right now, but fixable to support other protocols.
This commit is contained in:
Ben Vanik
2013-12-23 14:01:13 -08:00
parent a0256fac45
commit 475ddc1fcf
19 changed files with 284 additions and 9 deletions

View File

@@ -14,6 +14,7 @@
#include <xenia/common.h>
#include <alloy/arena.h>
#include <alloy/delegate.h>
#include <alloy/mutex.h>
#include <alloy/string_buffer.h>

76
src/alloy/delegate.h Normal file
View File

@@ -0,0 +1,76 @@
/**
******************************************************************************
* 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_DELEGATE_H_
#define ALLOY_DELEGATE_H_
#include <functional>
#include <alloy/core.h>
#include <alloy/mutex.h>
namespace alloy {
// TODO(benvanik): go lockfree, and don't hold the lock while emitting.
template <typename T>
class Delegate {
public:
Delegate() {
lock_ = AllocMutex();
}
~Delegate() {
FreeMutex(lock_);
}
typedef std::function<void(T&)> listener_t;
void AddListener(listener_t const& listener) {
LockMutex(lock_);
listeners_.push_back(listener);
UnlockMutex(lock_);
}
void RemoveListener(listener_t const& listener) {
LockMutex(lock_);
for (auto it = listeners_.begin(); it != listeners_.end(); ++it) {
if (it == listener) {
listeners_.erase(it);
break;
}
}
UnlockMutex(lock_);
}
void RemoveAllListeners() {
LockMutex(lock_);
listeners_.clear();
UnlockMutex(lock_);
}
void operator()(T& e) const {
LockMutex(lock_);
for (auto &listener : listeners_) {
listener(e);
}
UnlockMutex(lock_);
}
private:
alloy::Mutex* lock_;
std::vector<listener_t> listeners_;
};
} // namespace alloy
#endif // ALLOY_DELEGATE_H_

View File

@@ -65,7 +65,7 @@ typedef union {
#pragma pack(push, 4)
typedef struct XECACHEALIGN64 PPCContext_s {
// Most frequently used registers first.
uint64_t r[32]; // General purpose registers
uint64_t r[32]; // General purpose registers
uint64_t lr; // Link register
uint64_t ctr; // Count register
@@ -190,6 +190,7 @@ typedef struct XECACHEALIGN64 PPCContext_s {
uint8_t* membase;
runtime::Runtime* runtime;
runtime::ThreadState* thread_state;
uint32_t suspend_flag;
void SetRegFromString(const char* name, const char* value);
bool CompareRegWithString(const char* name, const char* value,

View File

@@ -10,7 +10,7 @@
#ifndef ALLOY_MUTEX_H_
#define ALLOY_MUTEX_H_
#include <alloy/core.h>
#include <xenia/common.h>
namespace alloy {

View File

@@ -25,11 +25,55 @@ Breakpoint::~Breakpoint() {
Debugger::Debugger(Runtime* runtime) :
runtime_(runtime) {
threads_lock_ = AllocMutex();
breakpoints_lock_ = AllocMutex();
}
Debugger::~Debugger() {
FreeMutex(breakpoints_lock_);
FreeMutex(threads_lock_);
}
int Debugger::SuspendAllThreads(uint32_t timeout_ms) {
int result = 0;
LockMutex(threads_lock_);
for (auto it = threads_.begin(); it != threads_.end(); ++it) {
ThreadState* thread_state = it->second;
if (thread_state->Suspend(timeout_ms)) {
result = 1;
}
}
UnlockMutex(threads_lock_);
return result;
}
int Debugger::ResumeThread(uint32_t thread_id) {
LockMutex(threads_lock_);
auto it = threads_.find(thread_id);
if (it == threads_.end()) {
UnlockMutex(threads_lock_);
return 1;
}
// Found thread. Note that it could be deleted as soon as we unlock.
ThreadState* thread_state = it->second;
int result = thread_state->Resume();
UnlockMutex(threads_lock_);
return result;
}
int Debugger::ResumeAllThreads() {
int result = 0;
LockMutex(threads_lock_);
for (auto it = threads_.begin(); it != threads_.end(); ++it) {
ThreadState* thread_state = it->second;
if (thread_state->Resume()) {
result = 1;
}
}
UnlockMutex(threads_lock_);
return result;
}
int Debugger::AddBreakpoint(Breakpoint* breakpoint) {
@@ -106,6 +150,21 @@ void Debugger::FindBreakpoints(
UnlockMutex(breakpoints_lock_);
}
void Debugger::OnThreadCreated(ThreadState* thread_state) {
LockMutex(threads_lock_);
threads_[thread_state->thread_id()] = thread_state;
UnlockMutex(threads_lock_);
}
void Debugger::OnThreadDestroyed(ThreadState* thread_state) {
LockMutex(threads_lock_);
auto it = threads_.find(thread_state->thread_id());
if (it != threads_.end()) {
threads_.erase(it);
}
UnlockMutex(threads_lock_);
}
void Debugger::OnFunctionDefined(FunctionInfo* symbol_info,
Function* function) {
// Man, I'd love not to take this lock.
@@ -134,5 +193,12 @@ void Debugger::OnFunctionDefined(FunctionInfo* symbol_info,
void Debugger::OnBreakpointHit(
ThreadState* thread_state, Breakpoint* breakpoint) {
//
// Suspend all threads immediately.
SuspendAllThreads();
// Notify listeners.
BreakpointHitEvent e(this, thread_state, breakpoint);
breakpoint_hit(e);
// Note that we stay suspended.
}

View File

@@ -18,6 +18,7 @@
namespace alloy {
namespace runtime {
class Debugger;
class Function;
class FunctionInfo;
class Runtime;
@@ -37,9 +38,40 @@ public:
Type type() const { return type_; }
uint64_t address() const { return address_; }
const char* id() const { return id_.c_str(); }
void set_id(const char* id) { id_ = id; }
private:
Type type_;
uint64_t address_;
std::string id_;
};
class DebugEvent {
public:
DebugEvent(Debugger* debugger) :
debugger_(debugger) {}
virtual ~DebugEvent() {}
Debugger* debugger() const { return debugger_; }
protected:
Debugger* debugger_;
};
class BreakpointHitEvent : public DebugEvent {
public:
BreakpointHitEvent(
Debugger* debugger, ThreadState* thread_state, Breakpoint* breakpoint) :
thread_state_(thread_state), breakpoint_(breakpoint),
DebugEvent(debugger) {}
virtual ~BreakpointHitEvent() {}
ThreadState* thread_state() const { return thread_state_; }
Breakpoint* breakpoint() const { return breakpoint_; }
protected:
ThreadState* thread_state_;
Breakpoint* breakpoint_;
};
@@ -50,20 +82,34 @@ public:
Runtime* runtime() const { return runtime_; }
int SuspendAllThreads(uint32_t timeout_ms = UINT_MAX);
int ResumeThread(uint32_t thread_id);
int ResumeAllThreads();
int AddBreakpoint(Breakpoint* breakpoint);
int RemoveBreakpoint(Breakpoint* breakpoint);
void FindBreakpoints(
uint64_t address, std::vector<Breakpoint*>& out_breakpoints);
void OnThreadCreated(ThreadState* thread_state);
void OnThreadDestroyed(ThreadState* thread_state);
void OnFunctionDefined(FunctionInfo* symbol_info, Function* function);
void OnBreakpointHit(ThreadState* thread_state, Breakpoint* breakpoint);
public:
Delegate<BreakpointHitEvent> breakpoint_hit;
private:
Runtime* runtime_;
Mutex* threads_lock_;
typedef std::unordered_map<uint32_t, ThreadState*> ThreadMap;
ThreadMap threads_;
Mutex* breakpoints_lock_;
typedef std::multimap<uint64_t, Breakpoint*> BreakpointsMultimap;
BreakpointsMultimap breakpoints_;
typedef std::multimap<uint64_t, Breakpoint*> BreakpointMultimap;
BreakpointMultimap breakpoints_;
};

View File

@@ -32,6 +32,9 @@ public:
void* backend_data() const { return backend_data_; }
void* raw_context() const { return raw_context_; }
virtual int Suspend(uint32_t timeout_ms = UINT_MAX) = 0;
virtual int Resume() = 0;
static void Bind(ThreadState* thread_state);
static ThreadState* Get();
static uint32_t GetThreadID();

View File

@@ -7,6 +7,7 @@
'arena.cc',
'arena.h',
'core.h',
'delegate.h',
'memory.cc',
'memory.h',
'mutex.h',