diff --git a/src/alloy/backend/ivm/ivm_function.cc b/src/alloy/backend/ivm/ivm_function.cc index b0aacc1fb..03f574312 100644 --- a/src/alloy/backend/ivm/ivm_function.cc +++ b/src/alloy/backend/ivm/ivm_function.cc @@ -34,6 +34,14 @@ void IVMFunction::Setup(TranslationContext& ctx) { intcodes_ = (IntCode*)ctx.intcode_arena->CloneContents(); } +int IVMFunction::AddBreakpointImpl(Breakpoint* breakpoint) { + return 0; +} + +int IVMFunction::RemoveBreakpointImpl(Breakpoint* breakpoint) { + return 0; +} + int IVMFunction::CallImpl(ThreadState* thread_state, uint64_t return_address) { // Setup register file on stack. size_t register_file_size = register_count_ * sizeof(Register); diff --git a/src/alloy/backend/ivm/ivm_function.h b/src/alloy/backend/ivm/ivm_function.h index 369fd123a..7e7147bbe 100644 --- a/src/alloy/backend/ivm/ivm_function.h +++ b/src/alloy/backend/ivm/ivm_function.h @@ -29,6 +29,8 @@ public: void Setup(TranslationContext& ctx); protected: + virtual int AddBreakpointImpl(runtime::Breakpoint* breakpoint); + virtual int RemoveBreakpointImpl(runtime::Breakpoint* breakpoint); virtual int CallImpl(runtime::ThreadState* thread_state, uint64_t return_address); diff --git a/src/alloy/runtime/debugger.cc b/src/alloy/runtime/debugger.cc index 28f9c51c0..bc348eda8 100644 --- a/src/alloy/runtime/debugger.cc +++ b/src/alloy/runtime/debugger.cc @@ -9,6 +9,9 @@ #include +#include +#include + using namespace alloy; using namespace alloy::runtime; @@ -22,15 +25,109 @@ Breakpoint::~Breakpoint() { Debugger::Debugger(Runtime* runtime) : runtime_(runtime) { + breakpoints_lock_ = AllocMutex(); } Debugger::~Debugger() { + FreeMutex(breakpoints_lock_); } int Debugger::AddBreakpoint(Breakpoint* breakpoint) { + // Add to breakpoints map. + LockMutex(breakpoints_lock_); + breakpoints_.insert( + std::pair(breakpoint->address(), breakpoint)); + UnlockMutex(breakpoints_lock_); + + // Find all functions that contain the breakpoint address. + auto fns = runtime_->FindFunctionsWithAddress(breakpoint->address()); + + // Add. + for (auto it = fns.begin(); it != fns.end(); ++it) { + Function* fn = *it; + if (fn->AddBreakpoint(breakpoint)) { + return 1; + } + } + return 0; } int Debugger::RemoveBreakpoint(Breakpoint* breakpoint) { + // Remove from breakpoint map. + LockMutex(breakpoints_lock_); + auto range = breakpoints_.equal_range(breakpoint->address()); + if (range.first == range.second) { + UnlockMutex(breakpoints_lock_); + return 1; + } + bool found = false; + for (auto it = range.first; it != range.second; ++it) { + if (it->second == breakpoint) { + breakpoints_.erase(it); + found = true; + break; + } + } + UnlockMutex(breakpoints_lock_); + if (!found) { + return 1; + } + + // Find all functions that have the breakpoint set. + auto fns = runtime_->FindFunctionsWithAddress(breakpoint->address()); + + // Remove. + for (auto it = fns.begin(); it != fns.end(); ++it) { + Function* fn = *it; + fn->RemoveBreakpoint(breakpoint); + } + return 0; } + +void Debugger::FindBreakpoints( + uint64_t address, std::vector& out_breakpoints) { + out_breakpoints.clear(); + + LockMutex(breakpoints_lock_); + + auto range = breakpoints_.equal_range(address); + if (range.first == range.second) { + UnlockMutex(breakpoints_lock_); + return; + } + + for (auto it = range.first; it != range.second; ++it) { + Breakpoint* breakpoint = it->second; + out_breakpoints.push_back(breakpoint); + } + + UnlockMutex(breakpoints_lock_); +} + +void Debugger::OnFunctionDefined(FunctionInfo* symbol_info, + Function* function) { + // Man, I'd love not to take this lock. + std::vector breakpoints; + LockMutex(breakpoints_lock_); + for (uint64_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); + } + } + UnlockMutex(breakpoints_lock_); + + if (breakpoints.size()) { + // Breakpoints to add! + for (auto it = breakpoints.begin(); it != breakpoints.end(); ++it) { + function->AddBreakpoint(*it); + } + } +} diff --git a/src/alloy/runtime/debugger.h b/src/alloy/runtime/debugger.h index afe007a8a..22497bf97 100644 --- a/src/alloy/runtime/debugger.h +++ b/src/alloy/runtime/debugger.h @@ -12,10 +12,14 @@ #include +#include + namespace alloy { namespace runtime { +class Function; +class FunctionInfo; class Runtime; @@ -47,9 +51,17 @@ public: int AddBreakpoint(Breakpoint* breakpoint); int RemoveBreakpoint(Breakpoint* breakpoint); + void FindBreakpoints( + uint64_t address, std::vector& out_breakpoints); + + void OnFunctionDefined(FunctionInfo* symbol_info, Function* function); private: Runtime* runtime_; + + Mutex* breakpoints_lock_; + typedef std::multimap BreakpointsMultimap; + BreakpointsMultimap breakpoints_; }; diff --git a/src/alloy/runtime/entry_table.cc b/src/alloy/runtime/entry_table.cc index 1363f206c..cf6da5d70 100644 --- a/src/alloy/runtime/entry_table.cc +++ b/src/alloy/runtime/entry_table.cc @@ -63,6 +63,7 @@ Entry::Status EntryTable::GetOrCreate(uint64_t address, Entry** out_entry) { // Create and return for initialization. entry = new Entry(); entry->address = address; + entry->end_address = 0; entry->status = Entry::STATUS_COMPILING; entry->function = 0; map_[address] = entry; @@ -72,3 +73,19 @@ Entry::Status EntryTable::GetOrCreate(uint64_t address, Entry** out_entry) { *out_entry = entry; return status; } + +std::vector EntryTable::FindWithAddress(uint64_t address) { + std::vector fns; + LockMutex(lock_); + for (auto it = map_.begin(); it != map_.end(); ++it) { + Entry* entry = it->second; + if (address >= entry->address && + address <= entry->end_address) { + if (entry->status == Entry::STATUS_READY) { + fns.push_back(entry->function); + } + } + } + UnlockMutex(lock_); + return fns; +} diff --git a/src/alloy/runtime/entry_table.h b/src/alloy/runtime/entry_table.h index 1f1148ae0..e9f1ca9f2 100644 --- a/src/alloy/runtime/entry_table.h +++ b/src/alloy/runtime/entry_table.h @@ -28,6 +28,7 @@ typedef struct Entry_t { } Status; uint64_t address; + uint64_t end_address; Status status; Function* function; } Entry; @@ -41,6 +42,8 @@ public: Entry* Get(uint64_t address); Entry::Status GetOrCreate(uint64_t address, Entry** out_entry); + std::vector FindWithAddress(uint64_t address); + private: // TODO(benvanik): replace with a better data structure. Mutex* lock_; diff --git a/src/alloy/runtime/function.cc b/src/alloy/runtime/function.cc index ca968da36..af3e9bf74 100644 --- a/src/alloy/runtime/function.cc +++ b/src/alloy/runtime/function.cc @@ -18,9 +18,43 @@ using namespace alloy::runtime; Function::Function(Type type, uint64_t address) : type_(type), address_(address), debug_info_(0) { + // TODO(benvanik): create on demand? + lock_ = AllocMutex(); } Function::~Function() { + FreeMutex(lock_); +} + +int Function::AddBreakpoint(Breakpoint* breakpoint) { + LockMutex(lock_); + bool found = false; + for (auto it = breakpoints_.begin(); it != breakpoints_.end(); ++it) { + if (*it == breakpoint) { + found = true; + } + } + if (!found) { + breakpoints_.push_back(breakpoint); + AddBreakpointImpl(breakpoint); + } + UnlockMutex(lock_); + return found ? 1 : 0; +} + +int Function::RemoveBreakpoint(Breakpoint* breakpoint) { + LockMutex(lock_); + bool found = false; + for (auto it = breakpoints_.begin(); it != breakpoints_.end(); ++it) { + if (*it == breakpoint) { + breakpoints_.erase(it); + RemoveBreakpointImpl(breakpoint); + found = true; + break; + } + } + UnlockMutex(lock_); + return found ? 0 : 1; } int Function::Call(ThreadState* thread_state, uint64_t return_address) { diff --git a/src/alloy/runtime/function.h b/src/alloy/runtime/function.h index abe51956b..8c9d95748 100644 --- a/src/alloy/runtime/function.h +++ b/src/alloy/runtime/function.h @@ -17,6 +17,7 @@ namespace alloy { namespace runtime { +class Breakpoint; class FunctionInfo; class ThreadState; @@ -38,15 +39,24 @@ public: DebugInfo* debug_info() const { return debug_info_; } void set_debug_info(DebugInfo* debug_info) { debug_info_ = debug_info; } + int AddBreakpoint(Breakpoint* breakpoint); + int RemoveBreakpoint(Breakpoint* breakpoint); + int Call(ThreadState* thread_state, uint64_t return_address); protected: + virtual int AddBreakpointImpl(Breakpoint* breakpoint) { return 0; } + virtual int RemoveBreakpointImpl(Breakpoint* breakpoint) { return 0; } virtual int CallImpl(ThreadState* thread_state, uint64_t return_address) = 0; protected: Type type_; uint64_t address_; DebugInfo* debug_info_; + + // TODO(benvanik): move elsewhere? DebugData? + Mutex* lock_; + std::vector breakpoints_; }; diff --git a/src/alloy/runtime/runtime.cc b/src/alloy/runtime/runtime.cc index 2f9b2e2d3..360e18184 100644 --- a/src/alloy/runtime/runtime.cc +++ b/src/alloy/runtime/runtime.cc @@ -143,6 +143,10 @@ Runtime::ModuleList Runtime::GetModules() { return clone; } +std::vector Runtime::FindFunctionsWithAddress(uint64_t address) { + return entry_table_.FindWithAddress(address); +} + int Runtime::ResolveFunction(uint64_t address, Function** out_function) { *out_function = NULL; Entry* entry; @@ -162,6 +166,7 @@ int Runtime::ResolveFunction(uint64_t address, Function** out_function) { entry->status = Entry::STATUS_FAILED; return result; } + entry->end_address = symbol_info->end_address(); status = entry->status = Entry::STATUS_READY; } if (status == Entry::STATUS_READY) { @@ -240,6 +245,10 @@ int Runtime::DemandFunction( return result; } symbol_info->set_function(function); + + // Before we give the symbol back to the rest, let the debugger know. + debugger_->OnFunctionDefined(symbol_info, function); + symbol_info->set_status(SymbolInfo::STATUS_DEFINED); symbol_status = symbol_info->status(); } diff --git a/src/alloy/runtime/runtime.h b/src/alloy/runtime/runtime.h index 4bc8b3730..3ccd82fb6 100644 --- a/src/alloy/runtime/runtime.h +++ b/src/alloy/runtime/runtime.h @@ -48,6 +48,8 @@ public: Module* GetModule(const char* name); ModuleList GetModules(); + std::vector FindFunctionsWithAddress(uint64_t address); + int LookupFunctionInfo(uint64_t address, FunctionInfo** out_symbol_info); int LookupFunctionInfo(Module* module, uint64_t address, FunctionInfo** out_symbol_info);