From 6c6e181ff53bc67ee96981da179d874ecbe391de Mon Sep 17 00:00:00 2001 From: mrexodia Date: Fri, 24 Feb 2017 18:13:36 +0100 Subject: [PATCH] lazily read/write context for all threads --- GleeBug/Debugger.Loop.cpp | 15 ++++----- GleeBug/Debugger.Process.h | 18 +++++++++++ GleeBug/Debugger.Thread.Registers.GetSet.cpp | 16 +++++++-- GleeBug/Debugger.Thread.Registers.cpp | 34 +++++++++++++++++++- GleeBug/Debugger.Thread.Registers.h | 26 ++++++++++++--- GleeBug/Debugger.Thread.cpp | 14 +++----- GleeBug/Debugger.Thread.h | 2 +- TitanEngineEmulator/Emulator.h | 22 +++++++++++-- 8 files changed, 119 insertions(+), 28 deletions(-) diff --git a/GleeBug/Debugger.Loop.cpp b/GleeBug/Debugger.Loop.cpp index 3ff251b..7a68b9d 100644 --- a/GleeBug/Debugger.Loop.cpp +++ b/GleeBug/Debugger.Loop.cpp @@ -44,8 +44,6 @@ namespace GleeBug { mThread = mProcess->thread = &threadFound->second; mRegisters = &mThread->registers; - if (!mThread->RegReadContext()) - cbInternalError("ThreadInfo::RegReadContext() failed!"); } else { @@ -64,6 +62,10 @@ namespace GleeBug } } + //read register contexts + if(mProcess && !mProcess->RegReadContext()) + cbInternalError("Process::RegReadContext() failed!"); + //call the pre debug event callback cbPreDebugEvent(mDebugEvent); @@ -120,12 +122,9 @@ namespace GleeBug mThread->registers.TrapFlag = false; } - //write the register context - if (mThread) - { - if (!mThread->RegWriteContext()) - cbInternalError("ThreadInfo::RegWriteContext() failed!"); - } + //write register contexts + if(mProcess && !mProcess->RegWriteContext()) + cbInternalError("Process::RegWriteContext() failed!"); //continue the debug event if (!ContinueDebugEvent(mDebugEvent.dwProcessId, mDebugEvent.dwThreadId, mContinueStatus)) diff --git a/GleeBug/Debugger.Process.h b/GleeBug/Debugger.Process.h index fe60c9b..831fc4f 100644 --- a/GleeBug/Debugger.Process.h +++ b/GleeBug/Debugger.Process.h @@ -394,6 +394,24 @@ namespace GleeBug StepOver(std::bind(callback, debugger)); } + bool RegReadContext() + { + auto result = true; + for(auto & thread : this->threads) + if(!thread.second.RegReadContext()) + result = false; + return result; + } + + bool RegWriteContext() + { + auto result = true; + for(auto & thread : this->threads) + if(!thread.second.RegWriteContext()) + result = false; + return result; + } + private: Capstone mCapstone; }; diff --git a/GleeBug/Debugger.Thread.Registers.GetSet.cpp b/GleeBug/Debugger.Thread.Registers.GetSet.cpp index b3687cd..f861e12 100644 --- a/GleeBug/Debugger.Thread.Registers.GetSet.cpp +++ b/GleeBug/Debugger.Thread.Registers.GetSet.cpp @@ -54,8 +54,10 @@ namespace GleeBug { - ptr Registers::Get(R reg) const + ptr Registers::Get(R reg) { + handleLazyContext(); + switch (reg) { case R::DR0: @@ -244,6 +246,8 @@ namespace GleeBug void Registers::Set(R reg, ptr value) { + handleLazyContext(); + switch (reg) { case R::DR0: @@ -513,21 +517,27 @@ namespace GleeBug } } - bool Registers::GetFlag(F flag) const + bool Registers::GetFlag(F flag) { + handleLazyContext(); + return (mContext.EFlags & ptr(flag)) == ptr(flag); } void Registers::SetFlag(F flag, bool set) { + handleLazyContext(); + if (set) mContext.EFlags |= ptr(flag); else mContext.EFlags &= ~ptr(flag); } - void* Registers::getPtr(R reg) const + void* Registers::getPtr(R reg) { + handleLazyContext(); + switch (reg) { case R::DR0: diff --git a/GleeBug/Debugger.Thread.Registers.cpp b/GleeBug/Debugger.Thread.Registers.cpp index 58d15c0..050f4dc 100644 --- a/GleeBug/Debugger.Thread.Registers.cpp +++ b/GleeBug/Debugger.Thread.Registers.cpp @@ -102,13 +102,45 @@ namespace GleeBug memset(&this->mContext, 0, sizeof(CONTEXT)); } - const CONTEXT* Registers::GetContext() const + const CONTEXT* Registers::GetContext() { + handleLazyContext(); return &mContext; } void Registers::SetContext(const CONTEXT & context) { + handleLazyContext(); this->mContext = context; } + + void Registers::setContextLazy(CONTEXT* oldContext, HANDLE hThread) + { + this->mLazyOldContext = oldContext; + this->mLazyThread = hThread; + this->mLazySet = true; + this->mContext = *this->mLazyOldContext; + } + + bool Registers::handleLazyContext() + { + if(!this->mLazySet) + return true; + + if(!this->mLazyOldContext || !this->mLazyThread) //assert + __debugbreak(); + + //TODO: handle failure of GetThreadContext + auto result = false; + if(GetThreadContext(this->mLazyThread, this->mLazyOldContext)) + { + this->mContext = *this->mLazyOldContext; + result = true; + } + + this->mLazyOldContext = nullptr; + this->mLazyThread = nullptr; + this->mLazySet = false; + return result; + } }; \ No newline at end of file diff --git a/GleeBug/Debugger.Thread.Registers.h b/GleeBug/Debugger.Thread.Registers.h index f530f76..7a0f842 100644 --- a/GleeBug/Debugger.Thread.Registers.h +++ b/GleeBug/Debugger.Thread.Registers.h @@ -11,6 +11,7 @@ namespace GleeBug class Registers { friend class Register; + friend class Thread; public: /** @@ -123,7 +124,7 @@ namespace GleeBug \param reg The register to get. \return The register value. */ - ptr Get(R reg) const; + ptr Get(R reg); /** \brief Sets a given register. @@ -137,7 +138,7 @@ namespace GleeBug \param flag The flag to get. \return true if the flag is set, false otherwise. */ - bool GetFlag(F flag) const; + bool GetFlag(F flag); /** \brief Sets a flag. @@ -149,7 +150,7 @@ namespace GleeBug \brief Gets a pointer to the context object. \return This function will never return a nullptr. */ - const CONTEXT* GetContext() const; + const CONTEXT* GetContext(); /** \brief Sets the CONTEXT. @@ -160,7 +161,24 @@ namespace GleeBug private: CONTEXT mContext; - void* getPtr(R reg) const; + CONTEXT* mLazyOldContext = nullptr; + HANDLE mLazyThread = nullptr; + bool mLazySet = false; + + /** + \brief Lazily set CONTEXT. This will only actually retrieve the context if a function in this thread is called. + \param oldContext Pointer to the old context, used to determine if updates are required. + \param hThread Handle of the thread to get the context from if required. + */ + void setContextLazy(CONTEXT* oldContext, HANDLE hThread); + + /** + \brief Retrieve the actual context if setContextLazy has been called. + \return Whether retrieving the actual context was successful. + */ + bool handleLazyContext(); + + void* getPtr(R reg); }; }; diff --git a/GleeBug/Debugger.Thread.cpp b/GleeBug/Debugger.Thread.cpp index 88d355f..509c7b6 100644 --- a/GleeBug/Debugger.Thread.cpp +++ b/GleeBug/Debugger.Thread.cpp @@ -44,21 +44,17 @@ namespace GleeBug { memset(&this->mOldContext, 0, sizeof(CONTEXT)); this->mOldContext.ContextFlags = CONTEXT_ALL; //TODO: granular control over what's required - if (GetThreadContext(this->hThread, &this->mOldContext)) - { - this->registers.SetContext(this->mOldContext); - return true; - } - return false; + this->registers.setContextLazy(&this->mOldContext, this->hThread); + return true; } - bool Thread::RegWriteContext() const + bool Thread::RegWriteContext() { //check if something actually changed - if (memcmp(&this->mOldContext, this->registers.GetContext(), sizeof(CONTEXT)) == 0) + if (memcmp(&this->mOldContext, &this->registers.mContext, sizeof(CONTEXT)) == 0) return true; //update the context - return !!SetThreadContext(this->hThread, this->registers.GetContext()); + return !!SetThreadContext(this->hThread, &this->registers.mContext); } void Thread::StepInto() diff --git a/GleeBug/Debugger.Thread.h b/GleeBug/Debugger.Thread.h index 4a1b153..68bcb71 100644 --- a/GleeBug/Debugger.Thread.h +++ b/GleeBug/Debugger.Thread.h @@ -55,7 +55,7 @@ namespace GleeBug \brief Write the register context to the thread. This does nothing if the registers did not change. \return true if it succeeds, false if it fails. */ - bool RegWriteContext() const; + bool RegWriteContext(); /** \brief Step into. diff --git a/TitanEngineEmulator/Emulator.h b/TitanEngineEmulator/Emulator.h index 0229b79..183a220 100644 --- a/TitanEngineEmulator/Emulator.h +++ b/TitanEngineEmulator/Emulator.h @@ -235,6 +235,8 @@ public: auto thread = threadFromHandle(hActiveThread); if (!thread) return 0; + if(mIsRunning) + thread->RegReadContext(); return thread->registers.Get(registerFromDword(IndexOfRegister)); } @@ -243,7 +245,11 @@ public: auto thread = threadFromHandle(hActiveThread); if (!thread) return false; + if(mIsRunning) + thread->RegReadContext(); thread->registers.Set(registerFromDword(IndexOfRegister), NewRegisterValue); + if(mIsRunning) + thread->RegWriteContext(); return true; } @@ -252,6 +258,8 @@ public: auto thread = threadFromHandle(hActiveThread); if (!thread || !titcontext) return false; + if(mIsRunning) + thread->RegReadContext(); memset(titcontext, 0, sizeof(TITAN_ENGINE_CONTEXT_t)); auto context = thread->registers.GetContext(); titcontext->cax = thread->registers.Gax(); @@ -294,6 +302,8 @@ public: auto thread = threadFromHandle(hActiveThread); if (!thread || !titcontext) return false; + if(mIsRunning) + thread->RegReadContext(); thread->registers.Gax = titcontext->cax; thread->registers.Gcx = titcontext->ccx; thread->registers.Gdx = titcontext->cdx; @@ -328,6 +338,8 @@ public: context.SegCs = titcontext->cs; context.SegSs = titcontext->ss; thread->registers.SetContext(context); + if(mIsRunning) + thread->RegWriteContext(); return true; } @@ -449,11 +461,17 @@ public: { if (!mProcess) return false; - return mProcess->SetHardwareBreakpoint(bpxAddress, + if(mIsRunning) + mProcess->RegReadContext(); + if(!mProcess->SetHardwareBreakpoint(bpxAddress, (HardwareSlot)IndexOfRegister, [bpxCallBack](const BreakpointInfo & info) { (HWBPCALLBACK(bpxCallBack))((const void*)info.address); - }, hwtypeFromTitan(bpxType), hwsizeFromTitan(bpxSize)); + }, hwtypeFromTitan(bpxType), hwsizeFromTitan(bpxSize))) + return false; + if(mIsRunning) + mProcess->RegWriteContext(); + return true; } bool DeleteHardwareBreakPoint(DWORD IndexOfRegister)