Moving debugger.
This commit is contained in:
45
src/xenia/debug/breakpoint.h
Normal file
45
src/xenia/debug/breakpoint.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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 <string>
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
||||
class Breakpoint {
|
||||
public:
|
||||
enum Type {
|
||||
TEMP_TYPE,
|
||||
CODE_TYPE,
|
||||
};
|
||||
|
||||
public:
|
||||
Breakpoint(Type type, uint32_t address);
|
||||
~Breakpoint();
|
||||
|
||||
Type type() const { return type_; }
|
||||
uint32_t address() const { return address_; }
|
||||
|
||||
const char* id() const { return id_.c_str(); }
|
||||
void set_id(const char* id) { id_ = std::string(id); }
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
uint32_t address_;
|
||||
|
||||
std::string id_;
|
||||
};
|
||||
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_BREAKPOINT_H_
|
||||
18
src/xenia/debug/debug_server.cc
Normal file
18
src/xenia/debug/debug_server.cc
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/debug/debug_server.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
||||
//
|
||||
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
21
src/xenia/debug/debug_server.h
Normal file
21
src/xenia/debug/debug_server.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_DEBUG_DEBUG_SERVER_H_
|
||||
#define XENIA_DEBUG_DEBUG_SERVER_H_
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
||||
//
|
||||
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_DEBUG_SERVER_H_
|
||||
201
src/xenia/debug/debugger.cc
Normal file
201
src/xenia/debug/debugger.cc
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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 "xenia/debug/debugger.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "xenia/cpu/function.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
||||
using xe::cpu::ThreadState;
|
||||
|
||||
Breakpoint::Breakpoint(Type type, uint32_t address)
|
||||
: type_(type), address_(address) {}
|
||||
|
||||
Breakpoint::~Breakpoint() = default;
|
||||
|
||||
Debugger::Debugger(cpu::Processor* processor) : processor_(processor) {}
|
||||
|
||||
Debugger::~Debugger() = default;
|
||||
|
||||
int Debugger::SuspendAllThreads(uint32_t timeout_ms) {
|
||||
std::lock_guard<std::mutex> guard(threads_lock_);
|
||||
|
||||
int result = 0;
|
||||
for (auto thread_state : threads_) {
|
||||
if (thread_state.second->Suspend(timeout_ms)) {
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int Debugger::ResumeThread(uint32_t thread_id) {
|
||||
std::lock_guard<std::mutex> guard(threads_lock_);
|
||||
|
||||
auto it = threads_.find(thread_id);
|
||||
if (it == threads_.end()) {
|
||||
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();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int Debugger::ResumeAllThreads(bool force) {
|
||||
std::lock_guard<std::mutex> guard(threads_lock_);
|
||||
|
||||
int result = 0;
|
||||
for (auto thread_state : threads_) {
|
||||
if (thread_state.second->Resume(force)) {
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Debugger::ForEachThread(std::function<void(ThreadState*)> callback) {
|
||||
std::lock_guard<std::mutex> guard(threads_lock_);
|
||||
|
||||
for (auto thread_state : threads_) {
|
||||
callback(thread_state.second);
|
||||
}
|
||||
}
|
||||
|
||||
int Debugger::AddBreakpoint(Breakpoint* breakpoint) {
|
||||
// Add to breakpoints map.
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(breakpoints_lock_);
|
||||
breakpoints_.insert(
|
||||
std::pair<uint32_t, Breakpoint*>(breakpoint->address(), breakpoint));
|
||||
}
|
||||
|
||||
// Find all functions that contain the breakpoint address.
|
||||
auto fns = processor_->FindFunctionsWithAddress(breakpoint->address());
|
||||
|
||||
// Add.
|
||||
for (auto fn : fns) {
|
||||
if (fn->AddBreakpoint(breakpoint)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Debugger::RemoveBreakpoint(Breakpoint* breakpoint) {
|
||||
// Remove from breakpoint map.
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(breakpoints_lock_);
|
||||
auto range = breakpoints_.equal_range(breakpoint->address());
|
||||
if (range.first == range.second) {
|
||||
return 1;
|
||||
}
|
||||
bool found = false;
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
if (it->second == breakpoint) {
|
||||
breakpoints_.erase(it);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Find all functions that have the breakpoint set.
|
||||
auto fns = processor_->FindFunctionsWithAddress(breakpoint->address());
|
||||
|
||||
// Remove.
|
||||
for (auto fn : fns) {
|
||||
fn->RemoveBreakpoint(breakpoint);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Debugger::FindBreakpoints(uint32_t address,
|
||||
std::vector<Breakpoint*>& out_breakpoints) {
|
||||
std::lock_guard<std::mutex> guard(breakpoints_lock_);
|
||||
|
||||
out_breakpoints.clear();
|
||||
|
||||
auto range = breakpoints_.equal_range(address);
|
||||
if (range.first == range.second) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
Breakpoint* breakpoint = it->second;
|
||||
out_breakpoints.push_back(breakpoint);
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::OnThreadCreated(ThreadState* thread_state) {
|
||||
std::lock_guard<std::mutex> guard(threads_lock_);
|
||||
threads_[thread_state->thread_id()] = thread_state;
|
||||
}
|
||||
|
||||
void Debugger::OnThreadDestroyed(ThreadState* thread_state) {
|
||||
std::lock_guard<std::mutex> guard(threads_lock_);
|
||||
auto it = threads_.find(thread_state->thread_id());
|
||||
if (it != threads_.end()) {
|
||||
threads_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::OnFunctionDefined(cpu::FunctionInfo* symbol_info,
|
||||
cpu::Function* function) {
|
||||
// Man, I'd love not to take this lock.
|
||||
std::vector<Breakpoint*> breakpoints;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(breakpoints_lock_);
|
||||
for (uint32_t address = symbol_info->address();
|
||||
address <= symbol_info->end_address(); address += 4) {
|
||||
auto range = breakpoints_.equal_range(address);
|
||||
if (range.first == range.second) {
|
||||
continue;
|
||||
}
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
Breakpoint* breakpoint = it->second;
|
||||
breakpoints.push_back(breakpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (breakpoints.size()) {
|
||||
// Breakpoints to add!
|
||||
for (auto breakpoint : breakpoints) {
|
||||
function->AddBreakpoint(breakpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
105
src/xenia/debug/debugger.h
Normal file
105
src/xenia/debug/debugger.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_DEBUGGER_H_
|
||||
#define XENIA_DEBUG_DEBUGGER_H_
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "xenia/base/delegate.h"
|
||||
#include "xenia/cpu/thread_state.h"
|
||||
#include "xenia/debug/breakpoint.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
class Function;
|
||||
class FunctionInfo;
|
||||
class Processor;
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
||||
class Debugger;
|
||||
|
||||
class DebugEvent {
|
||||
public:
|
||||
DebugEvent(Debugger* debugger) : debugger_(debugger) {}
|
||||
virtual ~DebugEvent() = default;
|
||||
Debugger* debugger() const { return debugger_; }
|
||||
|
||||
protected:
|
||||
Debugger* debugger_;
|
||||
};
|
||||
|
||||
class BreakpointHitEvent : public DebugEvent {
|
||||
public:
|
||||
BreakpointHitEvent(Debugger* debugger, cpu::ThreadState* thread_state,
|
||||
Breakpoint* breakpoint)
|
||||
: DebugEvent(debugger),
|
||||
thread_state_(thread_state),
|
||||
breakpoint_(breakpoint) {}
|
||||
~BreakpointHitEvent() override = default;
|
||||
cpu::ThreadState* thread_state() const { return thread_state_; }
|
||||
Breakpoint* breakpoint() const { return breakpoint_; }
|
||||
|
||||
protected:
|
||||
cpu::ThreadState* thread_state_;
|
||||
Breakpoint* breakpoint_;
|
||||
};
|
||||
|
||||
class Debugger {
|
||||
public:
|
||||
Debugger(cpu::Processor* processor);
|
||||
~Debugger();
|
||||
|
||||
cpu::Processor* processor() const { return processor_; }
|
||||
|
||||
int SuspendAllThreads(uint32_t timeout_ms = UINT_MAX);
|
||||
int ResumeThread(uint32_t thread_id);
|
||||
int ResumeAllThreads(bool force = false);
|
||||
|
||||
void ForEachThread(std::function<void(cpu::ThreadState*)> callback);
|
||||
|
||||
int AddBreakpoint(Breakpoint* breakpoint);
|
||||
int RemoveBreakpoint(Breakpoint* breakpoint);
|
||||
void FindBreakpoints(uint32_t address,
|
||||
std::vector<Breakpoint*>& out_breakpoints);
|
||||
|
||||
// TODO(benvanik): utility functions for modification (make function ignored,
|
||||
// etc).
|
||||
|
||||
void OnThreadCreated(cpu::ThreadState* thread_state);
|
||||
void OnThreadDestroyed(cpu::ThreadState* thread_state);
|
||||
void OnFunctionDefined(cpu::FunctionInfo* symbol_info,
|
||||
cpu::Function* function);
|
||||
|
||||
void OnBreakpointHit(cpu::ThreadState* thread_state, Breakpoint* breakpoint);
|
||||
|
||||
public:
|
||||
Delegate<BreakpointHitEvent> breakpoint_hit;
|
||||
|
||||
private:
|
||||
cpu::Processor* processor_;
|
||||
|
||||
std::mutex threads_lock_;
|
||||
std::unordered_map<uint32_t, cpu::ThreadState*> threads_;
|
||||
|
||||
std::mutex breakpoints_lock_;
|
||||
std::multimap<uint32_t, Breakpoint*> breakpoints_;
|
||||
};
|
||||
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_DEBUGGER_H_
|
||||
11
src/xenia/debug/sources.gypi
Normal file
11
src/xenia/debug/sources.gypi
Normal file
@@ -0,0 +1,11 @@
|
||||
# Copyright 2015 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'debug_server.cc',
|
||||
'debug_server.h',
|
||||
'debugger.cc',
|
||||
'debugger.h',
|
||||
'trace_writer.cc',
|
||||
'trace_writer.h',
|
||||
],
|
||||
}
|
||||
18
src/xenia/debug/trace_writer.cc
Normal file
18
src/xenia/debug/trace_writer.cc
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/debug/trace_writer.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
||||
//
|
||||
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
29
src/xenia/debug/trace_writer.h
Normal file
29
src/xenia/debug/trace_writer.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_DEBUG_TRACE_WRITER_H_
|
||||
#define XENIA_DEBUG_TRACE_WRITER_H_
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
||||
enum TraceFlags {
|
||||
kTraceFunctionInfo = 1 << 0,
|
||||
kTraceInstructionReferences = 1 << 1,
|
||||
kTraceInstructionResults = 1 << 2,
|
||||
};
|
||||
|
||||
class TraceWriter {
|
||||
public:
|
||||
};
|
||||
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_TRACE_WRITER_H_
|
||||
Reference in New Issue
Block a user