Kirk's latest work on watchpoints.
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/tcf_agent/framework/errors.c b/org.eclipse.cdt.debug.edc.windows.agent/src/tcf_agent/framework/errors.c
index 4d74712..1eb6066 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/tcf_agent/framework/errors.c
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/tcf_agent/framework/errors.c
@@ -350,6 +350,12 @@
case ERR_HWBRK_INVALID_SIZE: return "Invalid size";
case ERR_HWBRK_NO_RESOURCES: return "No resources available";
case ERR_HWBRK_NOT_ALIGNED: return "Invalid address alignment";
+ case ERR_HWBRK_INVALID_MODE: return "Invalid hardware breakpoint mode";
+ case ERR_PGPRO_UNSET: return "Cannot set page protection";
+ case ERR_PGPRO_UNRESET: return "Cannot reset page protection";
+ case ERR_PGPRO_UNCHANGED: return "Cannot change page protection";
+ case ERR_PGPRO_INVALID_MODE: return "Invalid page protection mode";
+ case ERR_PGPRO_NO_ADDR: return "No page containing address";
default:
if (err >= ERR_MESSAGE_MIN && err <= ERR_MESSAGE_MAX) {
if (is_dispatch_thread()) {
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/tcf_agent/framework/errors.h b/org.eclipse.cdt.debug.edc.windows.agent/src/tcf_agent/framework/errors.h
index 80f90f7..bbca6bb 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/tcf_agent/framework/errors.h
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/tcf_agent/framework/errors.h
@@ -56,6 +56,12 @@
#define ERR_HWBRK_INVALID_SIZE (STD_ERR_BASE + 30)
#define ERR_HWBRK_NO_RESOURCES (STD_ERR_BASE + 31)
#define ERR_HWBRK_NOT_ALIGNED (STD_ERR_BASE + 32)
+#define ERR_HWBRK_INVALID_MODE (STD_ERR_BASE + 33)
+#define ERR_PGPRO_UNSET (STD_ERR_BASE + 34)
+#define ERR_PGPRO_UNRESET (STD_ERR_BASE + 35)
+#define ERR_PGPRO_UNCHANGED (STD_ERR_BASE + 36)
+#define ERR_PGPRO_INVALID_MODE (STD_ERR_BASE + 37)
+#define ERR_PGPRO_NO_ADDR (STD_ERR_BASE + 38)
typedef struct ErrorReportItem {
char * name;
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/tcf_agent/services/discovery.c b/org.eclipse.cdt.debug.edc.windows.agent/src/tcf_agent/services/discovery.c
index fa9211f..cb34237 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/tcf_agent/services/discovery.c
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/tcf_agent/services/discovery.c
@@ -59,7 +59,7 @@
return 0;
}
-static void command_sync(char * token, Channel * c) {
+static void command_sync(const char* token, Channel * c) {
if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
@@ -100,7 +100,7 @@
peer_server_addprop((PeerServer *)x, loc_strdup(name), json_read_alloc_string(inp));
}
-static void command_redirect(char * token, Channel * c) {
+static void command_redirect(const char * token, Channel * c) {
PeerServer * ps = NULL;
int free_ps = 0;
@@ -135,7 +135,7 @@
if (free_ps) peer_server_free(ps);
}
-static void command_get_peers(char * token, Channel * c) {
+static void command_get_peers(const char * token, Channel * c) {
if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
write_stringz(&c->out, "R");
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/BreakpointsService.cpp b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/BreakpointsService.cpp
index 9a0c180..347d920 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/BreakpointsService.cpp
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/BreakpointsService.cpp
@@ -22,7 +22,6 @@
#include "EventClientNotifier.h"
#include "Logger.h"
-#include "WinHWBkptMgr.h"
#include "WinProcess.h"
#include "WinThread.h"
@@ -32,6 +31,10 @@
bool BreakpointsService::sServiceInstalled = false;
+inline bool TBreakpoint::SameProcess(const HANDLE& procH) const
+ { return process && process->GetProcessHandle() == procH; }
+
+
//
// The static function reads the context ids for a breakpoint
//
@@ -58,11 +61,11 @@
if (::strcmp(nm, "BreakpointType") == 0) {
std::string type = inpStream.readString();
if (type.compare("Auto") == 0) {
- attrs->bpType = type_Auto;
+ attrs->requestedType = type_Auto;
} else if (type.compare("Software") == 0) {
- attrs->bpType = type_Software;
+ attrs->requestedType = type_Software;
} else if (type.compare("Hardware") == 0) {
- attrs->bpType = type_Hardware;
+ attrs->requestedType = type_Hardware;
}
return;
}
@@ -167,7 +170,7 @@
}
int err;
- if (ACCESSMODE_EXECUTE == breakInfo->accessMode)
+ if (breakInfo->IsTypeBreakpoint())
err = InsertBreak(breakInfo);
else
err = InsertWatch(breakInfo);
@@ -207,7 +210,7 @@
TBreakpoint* bp = it->second;
sBreakPointMap.erase(it->first);
- if (ACCESSMODE_EXECUTE == bp->accessMode)
+ if (bp->IsTypeBreakpoint())
err = ClearBreak(bp);
else
err = ClearWatch(bp);
@@ -237,15 +240,17 @@
const HANDLE& procHandle = bpInfo->process->GetProcessHandle();
// save byte at breakpoint address.
- if (! ::ReadProcessMemory(procHandle, (LPCVOID)bpInfo->address, (LPVOID)&bpInfo->originalByte, 1, NULL))
+ if (FAILED(::ReadProcessMemory(procHandle, (LPCVOID)bpInfo->address,
+ (LPVOID)&bpInfo->originalByte, 1, NULL)))
return ::GetLastError();
// write breakpoint (INT3) to memory
- if (! ::WriteProcessMemory(procHandle, (LPVOID)bpInfo->address, (LPVOID)&sBreakInst, 1, NULL))
+ if (FAILED(::WriteProcessMemory(procHandle, (LPVOID)bpInfo->address,
+ (LPVOID)&sBreakInst, 1, NULL)))
return ::GetLastError();
// make sure original instruction isn't already in cache
- if (! ::FlushInstructionCache(procHandle, (LPCVOID)bpInfo->address, 1))
+ if (FAILED(::FlushInstructionCache(procHandle, (LPCVOID)bpInfo->address, 1)))
return ::GetLastError();
return 0;
@@ -255,14 +260,18 @@
* Clear a breakpoint from a process.
*/
int BreakpointsService::ClearBreak(const TBreakpoint* bpInfo) {
+ if (!bpInfo->process)
+ return 0; // process may have cleaned up after itself upon exit
+
const HANDLE& procHandle = bpInfo->process->GetProcessHandle();
// write original byte back over top of (INT3) in memory
- if (! ::WriteProcessMemory(procHandle, (LPVOID)bpInfo->address, (LPVOID)&bpInfo->originalByte, 1, NULL))
+ if (FAILED(::WriteProcessMemory(procHandle, (LPVOID)bpInfo->address,
+ (LPVOID)&bpInfo->originalByte, 1, NULL)))
return ::GetLastError();
// make sure bp instruction isn't already in cache
- if (! ::FlushInstructionCache(procHandle, (LPCVOID)bpInfo->address, 1))
+ if (FAILED(::FlushInstructionCache(procHandle, (LPCVOID)bpInfo->address, 1)))
return ::GetLastError();
return 0;
@@ -271,15 +280,17 @@
/*
* establish a hardware watchpoint using the hardware debug registers
*/
-int BreakpointsService::InsertWatch(TBreakpoint* bpInfo) {
- return WinHWBkptMgr::SetHardwareBreak(bpInfo);
+int BreakpointsService::InsertWatch(TBreakpoint* wpInfo) {
+ return wpInfo->process->SetWatchpoint(wpInfo);
}
/*
* clear the hardware debug register for a given watchpoint
*/
-int BreakpointsService::ClearWatch(TBreakpoint* bpInfo) {
- return WinHWBkptMgr::ClearHardwareBreak(bpInfo);
+int BreakpointsService::ClearWatch(TBreakpoint* wpInfo) {
+ if (!wpInfo->process)
+ return 0; // process may have cleaned up after itself upon exit
+ return wpInfo->process->ClearWatchpoint(wpInfo);
}
/*
@@ -290,8 +301,7 @@
TID2BreakpointMap::iterator iter;
for (iter = sBreakPointMap.begin(); iter != sBreakPointMap.end(); iter++) {
TBreakpoint* bp = iter->second;
- if (ACCESSMODE_EXECUTE == bp->accessMode
- && bp->process && processHandle == bp->process->GetProcessHandle() // same process
+ if (bp->IsTypeBreakpoint() && bp->SameProcess(processHandle)
&& address == bp->address)
{
return bp;
@@ -310,8 +320,7 @@
TID2BreakpointMap::iterator iter;
for (iter = sBreakPointMap.begin(); iter != sBreakPointMap.end(); iter++) {
TBreakpoint* wp = iter->second;
- if (ACCESSMODE_EXECUTE != wp->accessMode
- && wp->process && processHandle == wp->process->GetProcessHandle() // same process
+ if (wp->IsTypeWatchpoint() && wp->SameProcess(processHandle)
&& wp->address <= address && address < wp->address+wp->size)
{
return wp;
@@ -321,7 +330,9 @@
}
/*
- * Given a memory buffer read from a process, remove any breakpoint instructions added by debugger in the buffer.
+ * Given a memory buffer read from a process, remove any breakpoint
+ * instructions added by debugger in the buffer.
+ * ASSUMPTION: caller has temporarily diabled page-protection if necessary
*/
void BreakpointsService::RemoveBreakpointsFromMemoryRead(
const HANDLE& processHandle, ContextAddress address, char* memBuffer,
@@ -329,8 +340,7 @@
TID2BreakpointMap::iterator iter;
for (iter = sBreakPointMap.begin(); iter != sBreakPointMap.end(); iter++) {
TBreakpoint* bp = iter->second;
- if (ACCESSMODE_EXECUTE == bp->accessMode
- && bp->process && processHandle == bp->process->GetProcessHandle() // same process
+ if (bp->IsTypeBreakpoint() && bp->SameProcess(processHandle)
&& address <= bp->address && address+size > bp->address) // bp falls in the buffer
{
char *byteToChange = memBuffer + (bp->address - address);
@@ -340,7 +350,9 @@
}
/*
- * Given a memory buffer that has been writen to a process, re-inssert any breakpoints in the buffer range.
+ * Given a memory buffer that has been writen to a process,
+ * re-inssert any breakpoints in the buffer range.
+ * ASSUMPTION: caller has temporarily diabled page-protection if necessary
*/
void BreakpointsService::ReInsertBreakpointsAfterMemoryWrite(
const HANDLE& processHandle, ContextAddress address, char* memBuffer,
@@ -348,11 +360,23 @@
TID2BreakpointMap::iterator iter;
for (iter = sBreakPointMap.begin(); iter != sBreakPointMap.end(); iter++) {
TBreakpoint* bp = iter->second;
- if (ACCESSMODE_EXECUTE == bp->accessMode
- && bp->process && processHandle == bp->process->GetProcessHandle() // same process
+ if (bp->IsTypeBreakpoint() && bp->SameProcess(processHandle)
&& address <= bp->address && address+size > bp->address) // bp falls in the buffer
{
InsertBreak(bp);
}
}
}
+
+
+int BreakpointsService::RemoveProcessFromBreakpoints(const WinProcess* process) {
+ TID2BreakpointMap::iterator iter;
+ for (iter = sBreakPointMap.begin(); iter != sBreakPointMap.end(); iter++) {
+ TBreakpoint*& bp = iter->second;
+ if (bp && bp->process == process) {
+ bp->process = NULL;
+ bp->agentBreakID = AGENT_BREAK_UNSET;
+ }
+ }
+ return 0;
+}
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/BreakpointsService.h b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/BreakpointsService.h
index adb3740..f5a4e8f 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/BreakpointsService.h
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/BreakpointsService.h
@@ -36,8 +36,8 @@
ACCESSMODE_CHANGE = 0x08,
};
-typedef unsigned char HWBreakID;
-#define HWBREAK_NOT_SET 0
+typedef short AgentBreakID; // 32768 ought to be enough watchpoints
+#define AGENT_BREAK_UNSET (-1)
//
// To store the breakpoint info
@@ -51,17 +51,24 @@
unsigned char originalByte; // original byte where we set the bp.
unsigned char size; // for windows-x86, max is well below 255
- TCFBreakpointType bpType; // ignored; always software for bkpt
- // and always hardware for watchpoint
+ TCFBreakpointType requestedType;
+ TCFBreakpointType actualType;
TCFAccessMode accessMode; // _EXECUTE means bkpt; o/w -> watchpoint
- HWBreakID hwBreakID;
+ AgentBreakID agentBreakID;
TBreakpoint()
: process(NULL), address(0), enabled(true), originalByte(0),
- size(0), bpType(type_Auto), accessMode(ACCESSMODE_READ),
- hwBreakID(HWBREAK_NOT_SET)
+ size(0), requestedType(type_Auto), actualType(type_Auto),
+ accessMode(ACCESSMODE_READ), agentBreakID(AGENT_BREAK_UNSET)
{}
+
+ bool SameProcess(const HANDLE& procH) const;
+
+ inline bool IsTypeBreakpoint() const
+ { return ACCESSMODE_EXECUTE == accessMode; }
+ inline bool IsTypeWatchpoint() const
+ { return ACCESSMODE_EXECUTE != accessMode; }
};
typedef std::map<std::string, TBreakpoint*> TID2BreakpointMap;
@@ -89,6 +96,8 @@
static int InsertWatch(TBreakpoint*);
static int ClearWatch(TBreakpoint*);
+ static int RemoveProcessFromBreakpoints(const WinProcess*);
+
static TBreakpoint* FindBreakpointByAddress(const HANDLE& processHandle,
const ContextAddress);
static TBreakpoint* FindWatchpointByAddress(const HANDLE& processHandle,
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/ResumeContextAction.cpp b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/ResumeContextAction.cpp
index 421f090..68d6563 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/ResumeContextAction.cpp
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/ResumeContextAction.cpp
@@ -32,15 +32,16 @@
void ResumeContextAction::Run() {
// Single step over a breakpoint (if any) at current PC.
//
- TBreakpoint* bpAtStartAddress = BreakpointsService::FindBreakpointByAddress(process_.GetProcessHandle(), thread_.GetPCAddress());
+ TBreakpoint* bpAtStartAddress
+ = BreakpointsService::FindBreakpointByAddress(process_.GetProcessHandle(), thread_.GetPCAddress());
if (bpAtStartAddress) {
BreakpointsService::ClearBreak(bpAtStartAddress);
thread_.EnableSingleStep();
- }
- else if (resumeMode_ == RM_STEP_INTO)
+ } else if (resumeMode_ == RM_STEP_INTO)
thread_.EnableSingleStep();
- ContinueDebugEvent(process_.GetOSID(), thread_.GetOSID(), thread_.GetContinueStatus());
+ thread_.MarkResumed();
+ ::ContinueDebugEvent(process_.GetOSID(), thread_.GetOSID(), thread_.GetContinueStatus());
// notify host the Resume action is done before
// any suspend event is reported.
@@ -49,7 +50,7 @@
if (bpAtStartAddress) {
DEBUG_EVENT debugEvent;
- if (WaitForDebugEvent(&debugEvent, 200 /* ms */)) {
+ if (::WaitForDebugEvent(&debugEvent, 200 /* ms */)) {
// Done executing single instruction.
BreakpointsService::InsertBreak(bpAtStartAddress); // restore the bp
@@ -57,23 +58,18 @@
if (resumeMode_ == RM_STEP_INTO)
// single-step done, report suspend event to host.
process_.GetMonitor()->HandleDebugEvent(debugEvent);
- else {
+ else
// other resume modes
// Ignore the SINGLE_STEP event, go on to Resume again
- ContinueDebugEvent(process_.GetOSID(), thread_.GetOSID(), DBG_CONTINUE);
- }
- }
- else {
+ ::ContinueDebugEvent(process_.GetOSID(), thread_.GetOSID(), DBG_CONTINUE);
+ } else {
// Other exceptions
// Handle the event, say, report to host.
process_.GetMonitor()->HandleDebugEvent(debugEvent);
}
- }
- else {
+ } else {
trace(LOG_ALWAYS, "Failed to execute one instruction. Error: %d", GetLastError());
}
}
- else {
- // the event loop of monitor thread would catch and report event.
- }
+ // else the event loop of monitor thread would catch and report event.
}
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/SettingsService.cpp b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/SettingsService.cpp
index 0ec7b51..0a682ad 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/SettingsService.cpp
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/SettingsService.cpp
@@ -213,9 +213,9 @@
return enableDebugStringLogging;
}
-bool SettingsService::reportExceptionEvent(const DEBUG_EVENT& debugEvent)
+bool SettingsService::reportExceptionEvent(const EXCEPTION_DEBUG_INFO& excInfo)
{
- switch (debugEvent.u.Exception.ExceptionRecord.ExceptionCode) {
+ switch (excInfo.ExceptionRecord.ExceptionCode) {
// as a debugger we always report these
case EXCEPTION_SINGLE_STEP:
@@ -282,8 +282,9 @@
// it's not one of the typical exceptions and we have no setting for it.
// if it's first chance, don't report it. give it back for the process/os
// to handle. otherwise we'll report it to the debugger.
- trace(LOG_ALWAYS, "\t -- first chance:%d, flags:%d", debugEvent.u.Exception.dwFirstChance, debugEvent.u.Exception.ExceptionRecord.ExceptionFlags);
- if (debugEvent.u.Exception.dwFirstChance)
+ trace(LOG_ALWAYS, "\t -- first chance:%d, flags:0x8.8%x",
+ excInfo.dwFirstChance, excInfo.ExceptionRecord.ExceptionFlags);
+ if (excInfo.dwFirstChance)
return false;
}
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/SettingsService.h b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/SettingsService.h
index 151dccb..e80568b 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/SettingsService.h
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/SettingsService.h
@@ -36,6 +36,6 @@
static bool reportDebugEventForModule(std::string module);
static bool reportDebugStringEvents();
- static bool reportExceptionEvent(const DEBUG_EVENT& debugEvent);
+ static bool reportExceptionEvent(const EXCEPTION_DEBUG_INFO&);
static void debugSessionEnds();
};
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinDebugMonitor.cpp b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinDebugMonitor.cpp
index 1379b35..32c63d3 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinDebugMonitor.cpp
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinDebugMonitor.cpp
@@ -537,14 +537,12 @@
void WinDebugMonitor::HandleExceptionEvent(DEBUG_EVENT& debugEvent)
{
- EXCEPTION_RECORD &e = debugEvent.u.Exception.ExceptionRecord;
+ const EXCEPTION_DEBUG_INFO& excInfo = debugEvent.u.Exception;
+ const EXCEPTION_RECORD& e = excInfo.ExceptionRecord;
+ const DWORD& excCode = e.ExceptionCode;
LogTrace("DebugProcessMonitor::HandleExceptionEvent", "Exception in thread %d at 0x%x: code 0x%x (%s)",
debugEvent.dwThreadId, e.ExceptionAddress, e.ExceptionCode,
- GetDebugExceptionDescription(debugEvent.u.Exception).c_str());
-
- WinThread* thread = WinThread::GetThreadByID(debugEvent.dwProcessId, debugEvent.dwThreadId);
- if (!thread)
- assert(false);
+ GetDebugExceptionDescription(excInfo).c_str());
// ignore initial breakpoint in NTDLL!DbgBreakPoint for newly launched process if necessary
if (!handledInitialDebugBreakpoint && e.ExceptionCode == EXCEPTION_BREAKPOINT)
@@ -557,12 +555,38 @@
return;
}
- if (thread && SettingsService::reportExceptionEvent(debugEvent))
+ bool potentialProtectedPage = excCode == EXCEPTION_ACCESS_VIOLATION;
+ bool reportException = SettingsService::reportExceptionEvent(excInfo);
+ bool handled = false;
+ if (reportException || potentialProtectedPage)
{
- trace(LOG_ALWAYS, "\t -- reported to host debugger.");
- thread->HandleException(debugEvent);
+ // wait to search for a threadByID until necessary
+ WinThread* thread = WinThread::GetThreadByID(debugEvent.dwProcessId, debugEvent.dwThreadId);
+ if (!thread)
+ assert(false);
+
+ if (thread && potentialProtectedPage)
+ {
+ trace(LOG_ALWAYS, "\t -- checking potential protected page.");
+ handled = thread->HandlePotentialProtctedPage(debugEvent);
+ }
+
+ if (thread && !handled)
+ {
+ if (reportException)
+ {
+ trace(LOG_ALWAYS, "\t -- reported to host debugger.");
+ thread->HandleException(debugEvent);
+ handled = true;
+ }
+ else
+ {
+ thread->MarkResumed();
+ }
+ }
}
- else
+
+ if (!handled)
{
trace(LOG_ALWAYS, "\t -- ignored.");
// Note DBG_EXCEPTION_NOT_HANDLED is used here.
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinHWBkptMgr.cpp b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinHWBkptMgr.cpp
index 74f70d4..3c3e7b1 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinHWBkptMgr.cpp
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinHWBkptMgr.cpp
@@ -64,18 +64,14 @@
#include "WinProcess.h"
#include "WinThread.h"
-static const WinHWBkptMgr::Mode
- sDR7ModeBitsMap[9]
- = { WinHWBkptMgr::invalid,
- WinHWBkptMgr::invalid,
- WinHWBkptMgr::write,
- WinHWBkptMgr::readwrite,
- WinHWBkptMgr::execute,
- WinHWBkptMgr::invalid,
- WinHWBkptMgr::invalid,
- WinHWBkptMgr::invalid,
- WinHWBkptMgr::invalid
- };
+inline
+WinHWBkptMgr::Mode WinHWBkptMgr::sDR7ModeBitsMap(unsigned char i) {
+static const Mode map[9]
+ = { invalid, invalid, write, readwrite, execute,
+ invalid, invalid, invalid, invalid
+ };
+ return map[i];
+}
// invalid bits in the following 2 masks
#define XFF 0xFF
@@ -96,9 +92,12 @@
#define SB4 0x0C
#define SB8 0x08
-static const WinHWBkptMgr::SizeBits
- sDR7SizeBitsMap[9]
- = {XFF, SB1, SB2, XFF, SB4, XFF, XFF, XFF, SB8};
+inline
+WinHWBkptMgr::SizeBits WinHWBkptMgr::sDR7SizeBitsMap(unsigned char i) {
+static const WinHWBkptMgr::SizeBits map[9]
+ = {XFF, SB1, SB2, XFF, SB4, XFF, XFF, XFF, SB8};
+ return map[i];
+}
/*
* the following table represents a mapping of
@@ -158,8 +157,9 @@
{ 4,XFF,XFF,XFF,XFF,XFF,XFF,XFF}, // 32
};
-WinHWBkptMgr::DRMask WinHWBkptMgr::inUse = 0;
-WinHWBkptMgr::HWBreakInfo WinHWBkptMgr::hwBkpt[4];
+WinHWBkptMgr::WinHWBkptMgr() {
+ inUse = 0;
+}
unsigned char WinHWBkptMgr::InUseCount() {
return (DR0 & inUse)
@@ -198,6 +198,12 @@
return ERR_HWBRK_NOT_SET;
}
+ if (bp->accessMode > ACCESSMODE_CHANGE)
+ return ERR_HWBRK_INVALID_MODE;
+ Mode hwMode = sDR7ModeBitsMap(bp->accessMode);
+ if (hwMode == invalid)
+ return ERR_HWBRK_INVALID_MODE;
+
unsigned char locSize = bp->size;
if (locSize > 32)
return ERR_HWBRK_INVALID_SIZE;
@@ -212,9 +218,10 @@
return ERR_HWBRK_NO_RESOURCES;
unsigned char wp = GetUnusedWatchpoint();
- hwBkpt[wp].address = locAddr;
- hwBkpt[wp].size = locSize;
- hwBkpt[wp].accessMode = sDR7ModeBitsMap[bp->accessMode];
+ if (wp == XFF) // shouldn't happen with inUse check above
+ return ERR_HWBRK_NO_RESOURCES;
+ hwBkpt[wp].tcfBp = bp;
+ hwBkpt[wp].accessMode = hwMode;
for (;reqdRegs > 0;--reqdRegs) {
DRMask dr = UseUnusedRegister();
hwBkpt[wp].inUse |= dr;
@@ -231,7 +238,7 @@
}
// these are shifted inside based on the reg used
- DRFlags dr7bits = sDR7SizeBitsMap[drSz] | hwBkpt[wp].accessMode;
+ DRFlags dr7bits = sDR7SizeBitsMap(drSz) | hwBkpt[wp].accessMode;
bp->process->SetDebugRegister(dr, locAddr, dr7bits);
@@ -239,14 +246,16 @@
locSize -= drSz;
}
- bp->hwBreakID = wp;
+ bp->agentBreakID = wp;
return 0;
}
int WinHWBkptMgr::ClearHardwareBreak(TBreakpoint* bp) {
if (!bp->process)
return ERR_INV_CONTEXT;
- unsigned char& wp = bp->hwBreakID;
+ AgentBreakID& wp = bp->agentBreakID;
+ if (wp == AGENT_BREAK_UNSET)
+ return ERR_HWBRK_NOT_SET;
for (int i = 0; i < MAX_HWBP; ++i) {
DRMask dr = 1 << i;
if (hwBkpt[wp].inUse & dr) {
@@ -255,7 +264,9 @@
}
}
+ hwBkpt[wp].tcfBp = NULL;
+ hwBkpt[wp].inUse = 0;
bp->process = NULL;
- wp = 0;
+ wp = AGENT_BREAK_UNSET;
return 0;
}
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinHWBkptMgr.h b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinHWBkptMgr.h
index eee839e..930f303 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinHWBkptMgr.h
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinHWBkptMgr.h
@@ -65,42 +65,44 @@
struct TBreakpoint;
class WinHWBkptMgr {
- public:
+
+ friend class WinProcess;
+ friend class WinThread;
+
+ WinHWBkptMgr();
typedef unsigned char DRMask;
typedef unsigned char DRFlags;
- static int SetHardwareBreak(TBreakpoint*);
- static int ClearHardwareBreak(TBreakpoint*);
-
- public: // would like these to be private, but static initializers require otherwise
+ int SetHardwareBreak(TBreakpoint*);
+ int ClearHardwareBreak(TBreakpoint*);
typedef unsigned char Mode;
typedef unsigned char SizeBits;
- static const Mode execute = 0x00;
+ static const Mode execute = 0x00;
static const Mode write = 0x01;
// 0x02 is defined to mean break on I/O read/write, unsupported
static const Mode readwrite = 0x03;
- static const Mode invalid = 0x04;
+ static const Mode invalid = 0x04;
- private:
-
- typedef struct {
- ContextAddress address; // address of the full HW break
+ struct HWBreakInfo {
+ TBreakpoint* tcfBp;
DRMask inUse; // mask of the regs used for this hw-break
Mode accessMode; // accessMode to use for all regs used
- unsigned char size; // combined size of HW breaks in bits of all DRegs
- } HWBreakInfo;
+ HWBreakInfo() : tcfBp(NULL), inUse(0) {}
+ };
- static DRMask inUse;
- static unsigned char InUseCount();
- static unsigned char UnusedCount();
- static DRMask UseUnusedRegister();
+ DRMask inUse;
+ unsigned char InUseCount();
+ unsigned char UnusedCount();
+ DRMask UseUnusedRegister();
+ HWBreakInfo hwBkpt[MAX_HWBP];
+ unsigned char GetUnusedWatchpoint();
- static HWBreakInfo hwBkpt[MAX_HWBP];
- static unsigned char GetUnusedWatchpoint();
+ static Mode sDR7ModeBitsMap(unsigned char i);
+ static SizeBits sDR7SizeBitsMap(unsigned char i);
};
#endif /* WINHWBKPTMGR_H_ */
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinProcess.cpp b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinProcess.cpp
index fe082de..feeae09 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinProcess.cpp
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinProcess.cpp
@@ -75,6 +75,7 @@
}
WinProcess::~WinProcess(void) {
+ BreakpointsService::RemoveProcessFromBreakpoints(this);
processIDMap.erase(GetOSID());
@@ -96,8 +97,11 @@
return iter->second;
}
-int WinProcess::ReadMemory(const ReadWriteMemoryParams& params) throw (AgentException) {
- int result = 0;
+int WinProcess::ReadMemory(const ReadWriteMemoryParams& params)
+ throw (AgentException) {
+ int result = hwPageProtMgr.DisableForPagesContainingAddress(
+ processHandle_, params.address, params.size, PAGE_READONLY);
+ bool reprotect = (ERR_PGPRO_NO_ADDR != result);
BOOL success
= ::ReadProcessMemory(processHandle_, (LPCVOID) params.address,
@@ -105,22 +109,37 @@
if (!success)
result = ::GetLastError();
else
- BreakpointsService::RemoveBreakpointsFromMemoryRead(processHandle_, params.address, params.memBuffer, params.size);
+ BreakpointsService::RemoveBreakpointsFromMemoryRead(processHandle_,
+ params.address, params.memBuffer, params.size),
+ result = 0;
+
+ if (reprotect)
+ hwPageProtMgr.EnableForPagesContainingAddress(processHandle_,
+ params.address, params.size, PAGE_READONLY);
return result;
}
-int WinProcess::WriteMemory(const ReadWriteMemoryParams& params) throw (AgentException) {
- int result = 0;
+int WinProcess::WriteMemory(const ReadWriteMemoryParams& params)
+ throw (AgentException) {
+ int result = hwPageProtMgr.DisableForPagesContainingAddress(
+ processHandle_, params.address, params.size);
+ bool reprotect = (ERR_PGPRO_NO_ADDR != result);
BOOL success
= ::WriteProcessMemory(processHandle_, (LPVOID) params.address,
params.memBuffer, params.size, params.sizeTransferred);
if (!success)
result = ::GetLastError();
- else {
- BreakpointsService::ReInsertBreakpointsAfterMemoryWrite(processHandle_, params.address, params.memBuffer, params.size);
- }
+ else
+ BreakpointsService::ReInsertBreakpointsAfterMemoryWrite(
+ processHandle_, params.address, params.memBuffer, params.size),
+ result = 0;
+
+ if (reprotect)
+ hwPageProtMgr.EnableForPagesContainingAddress(processHandle_,
+ params.address, params.size);
+
return result;
}
@@ -145,6 +164,34 @@
return executablesByAddress_;
}
+int WinProcess::SetWatchpoint(TBreakpoint* wp) {
+ int err = ERR_OTHER;
+ if (wp->requestedType != type_Software // i.e. user-forced softare type
+ && wp->accessMode != ACCESSMODE_READ) // can't do read-only WPs w/hw-bkpts
+ err = hwBkptMgr.SetHardwareBreak(wp);
+ if (err == 0)
+ wp->actualType = type_Hardware;
+ else if (wp->requestedType != type_Hardware) {
+ err = hwPageProtMgr.SetPageProtectWatchpoint(wp);
+ if (err == 0)
+ wp->actualType = type_Software;
+ }
+ return err;
+}
+
+int WinProcess::ClearWatchpoint(TBreakpoint* wp) {
+ int err;
+ if (wp->actualType == type_Hardware)
+ err = hwBkptMgr.ClearHardwareBreak(wp);
+ else
+ err = hwPageProtMgr.ClearPageProtectWatchpoint(wp);
+ return err;
+}
+
+size_t WinProcess::SoftwareWatchpointCount() const {
+ return hwPageProtMgr.watchpoints.size();
+}
+
void WinProcess::SetDebugRegister(WinHWBkptMgr::DRMask drMask, ContextAddress addr, WinHWBkptMgr::DRFlags flags) {
const std::list<Context*>& children = GetChildren();
std::list<Context*>::const_iterator c;
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinProcess.h b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinProcess.h
index 57356e7..49423a3 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinProcess.h
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinProcess.h
@@ -10,14 +10,16 @@
*******************************************************************************/
#pragma once
+#include "stdafx.h"
+
#include <vector>
#include <set>
-#include "stdafx.h"
-#include "ProcessContext.h"
-#include "WinHWBkptMgr.h"
-#include "WinDebugMonitor.h"
-class WinThread;
+#include "ProcessContext.h"
+#include "WinDebugMonitor.h"
+#include "WinHWPageProtMgr.h"
+#include "WinThread.h"
+
class WinDebugMonitor;
class WinProcess : public ProcessContext {
@@ -45,11 +47,18 @@
std::map<int, Properties>& GetExecutablesByAddress();
- void SetDebugRegister(WinHWBkptMgr::DRMask, ContextAddress, WinHWBkptMgr::DRFlags);
- void ClearDebugRegister(WinHWBkptMgr::DRMask);
+ int SetWatchpoint(TBreakpoint*);
+ int ClearWatchpoint(TBreakpoint*);
+ size_t SoftwareWatchpointCount() const;
+
private:
void Initialize();
+ friend int WinHWBkptMgr::SetHardwareBreak(TBreakpoint*);
+ void SetDebugRegister(WinHWBkptMgr::DRMask, ContextAddress, WinHWBkptMgr::DRFlags);
+ friend int WinHWBkptMgr::ClearHardwareBreak(TBreakpoint*);
+ void ClearDebugRegister(WinHWBkptMgr::DRMask);
+
bool isRoot_;
HANDLE processHandle_;
std::string processName_;
@@ -57,4 +66,10 @@
std::map<int, Properties> executablesByAddress_;
static std::map<int, WinProcess*> processIDMap;
+
+ WinHWBkptMgr hwBkptMgr;
+
+ friend bool WinThread::HandlePotentialProtctedPage(const DEBUG_EVENT&);
+ friend int WinThread::HandleRestoringProtectedPage();
+ WinHWPageProtMgr hwPageProtMgr;
};
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinThread.cpp b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinThread.cpp
index 83494cb..f4ce51b 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinThread.cpp
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinThread.cpp
@@ -50,6 +50,8 @@
isSuspended_ = false;
isTerminating_ = false;
isUserSuspended_ = false;
+ isWatchpointStepping_ = false;
+ wpSteppingSavedWP = NULL;
// just to ensure that new threads are resumed with DBG_CONTINUE. this is normally set/changed
// in HandleException
@@ -137,22 +139,117 @@
threadContextValid_ = false;
}
-void WinThread::HandleException(DEBUG_EVENT& debugEvent) {
- exceptionInfo_ = debugEvent.u.Exception;
+void WinThread::MarkResumed() {
+ isSuspended_ = false;
+ threadContextValid_ = false;
+}
+
+int WinThread::HandleRestoringProtectedPage() {
+ isWatchpointStepping_ = false;
+ const HANDLE& procH = parentProcess_.GetProcessHandle();
+ WinHWPageProtMgr& protMgr = parentProcess_.hwPageProtMgr;
+ return protMgr.EnableForPagesContainingAddress(procH, wpSteppingAddr_);
+}
+
+void WinThread::SendWatchpointNotification(const TBreakpoint* wp) {
+ std::ostringstream wpAddr; wpAddr << std::hex << wp->address;
+ EventClientNotifier::SendContextSuspended(this,
+ GetPCAddress(), REASON_WATCHPOINT, wpAddr.str());
+}
+
+void WinThread::HandleException(const DEBUG_EVENT& debugEvent) {
MarkSuspended();
EnsureValidContextInfo();
- if (threadContextInfo_.Dr6 & 0xF) { // one will be set if a HW bkpt triggered
- HandleHardwareBreak();
+ bool hardwareBreakTriggered = threadContextInfo_.Dr6 & 0xF;
+ exceptionInfo_ = debugEvent.u.Exception;
+ if (isWatchpointStepping_) {
+ HandleRestoringProtectedPage();
+ if (!wpSteppingSavedWP && !hardwareBreakTriggered
+ && exceptionInfo_.ExceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP)
+ {
+ MarkResumed();
+ ::ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
+ return;
+ }
+ }
- // always reset to this for the next time through.
- threadContextInfo_.Dr6 = 0xFFFF0FF0;
- ::SetThreadContext(handle_, &threadContextInfo_);
+ if (hardwareBreakTriggered) {
+ HandleHardwareBreak();
+ } else if (wpSteppingSavedWP) {
+ SendWatchpointNotification(wpSteppingSavedWP);
} else {
AdjustPC();
EventClientNotifier::SendContextSuspended(this,
GetPCAddress(), GetSuspendReason(), GetExceptionMessage());
}
+ wpSteppingSavedWP = NULL;
+}
+
+void WinThread::HandleHardwareBreak() {
+ const HANDLE& procH = parentProcess_.GetProcessHandle();
+ ContextAddress* dr = (ContextAddress*)&(threadContextInfo_.Dr0);
+ for (int i = 0; i < 4; ++i) {
+ if (threadContextInfo_.Dr6 & (1 << i)) {
+ TBreakpoint* wp
+ = BreakpointsService::FindWatchpointByAddress(procH, dr[i]);
+ if (wp) {
+ SendWatchpointNotification(wp);
+ break;
+ }
+ }
+ }
+
+ // always reset to this for the next time through.
+ threadContextInfo_.Dr6 = 0xFFFF0FF0;
+ ::SetThreadContext(handle_, &threadContextInfo_);
+}
+
+bool WinThread::HandlePotentialProtctedPage(const DEBUG_EVENT& debugEvent) {
+ if (!parentProcess_.SoftwareWatchpointCount())
+ return false;
+
+ exceptionInfo_ = debugEvent.u.Exception;
+ WinHWPageProtMgr& protMgr = parentProcess_.hwPageProtMgr;
+ wpSteppingAddr_ = exceptionInfo_.ExceptionRecord.ExceptionInformation[1];
+ if (!protMgr.IsAddressInProtectedPage(wpSteppingAddr_))
+ return false;
+
+ isWatchpointStepping_ = true;
+
+ const HANDLE& procH = parentProcess_.GetProcessHandle();
+ protMgr.DisableForPagesContainingAddress(procH, wpSteppingAddr_);
+ TBreakpoint* wp = protMgr.FindWatchpointForAddress(procH, wpSteppingAddr_);
+ if (wp) {
+ bool accessW = exceptionInfo_.ExceptionRecord.ExceptionInformation[0] ? true : false;
+ if ((accessW && ACCESSMODE_WRITE & wp->accessMode)
+ || (!accessW && ACCESSMODE_READ & wp->accessMode)) {
+ wpSteppingSavedWP = wp;
+ }
+ }
+
+ /*
+ * regardless of whether or not a WP was found & saved
single-step,
+ * and then WinThread::HandleException() will handle with the state
+ * member vars set herein.
+ * the point in doing this: if a page-protection access violation
+ * occurred at the WP address, it did so because the exception
+ * occurred before the instr was executed, and thus before the EIP
+ * moved. by performing single-step unprotected & executing the
+ * instr that caused the access/protection violation, the EIP ends
+ * up where it would after the access, just like a HW bkpt trigger.
+ * and if the page-protection access violation occurred at an
+ * address other than the watchpoint, we still need to step once
+ * here in unprotected mode, and then re-protect the page and
+ * continue upon handling the single-step in HandleException.
+ */
+
+ MarkSuspended(); // needed so EnsureValidContextInfo() works
+ EnsureValidContextInfo(); // needed so EnableSingleStep() works
+ EnableSingleStep(); // needed so watchpoint stepping works
+ MarkResumed(); // needed to prevent inadvertent context info access
+ ::ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
+ return true;
}
/*
@@ -532,7 +629,7 @@
break;
case DR3:
threadContextInfo_.Dr3 = addr;
- DR7 = (DR7 & ~0xF0000030) | (dr7bits<<28) | 0x40;
+ DR7 = (DR7 & ~0xF00000C0) | (dr7bits<<28) | 0x40;
break;
default:
return;
@@ -556,24 +653,6 @@
return;
}
::SetThreadContext(handle_, &threadContextInfo_);
- ::SetThreadContext(handle_, &threadContextInfo_);
if (!suspended)
Resume();
}
-
-void WinThread::HandleHardwareBreak() {
- const HANDLE& procHandle = parentProcess_.GetProcessHandle();
- ContextAddress* dr = (ContextAddress*)&(threadContextInfo_.Dr0);
- for (int i = 0; i < 4; ++i) {
- if (threadContextInfo_.Dr6 & (1 << i)) {
- TBreakpoint* wp
- = BreakpointsService::FindWatchpointByAddress(procHandle, dr[i]);
- if (wp) {
- std::ostringstream wpAddr; wpAddr << std::hex << wp->address;
- EventClientNotifier::SendContextSuspended(this,
- GetPCAddress(), REASON_WATCHPOINT, wpAddr.str());
- break;
- }
- }
- }
-}
diff --git a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinThread.h b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinThread.h
index a525c2a..ff96362 100644
--- a/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinThread.h
+++ b/org.eclipse.cdt.debug.edc.windows.agent/src/win_agent/WinThread.h
@@ -60,14 +60,17 @@
void PrepareForTermination(const AgentActionParams&) throw (AgentException);
- void HandleException(DEBUG_EVENT& debugEvent);
+ void HandleException(const DEBUG_EVENT& debugEvent);
void HandleExecutableEvent(bool isLoaded, const std::string& exePath,
unsigned long baseAddress, unsigned long codeSize);
+ bool HandlePotentialProtctedPage(const DEBUG_EVENT&);
+ int HandleRestoringProtectedPage();
/* Address where suspend is reported */
ContextAddress GetPCAddress() const;
void EnableSingleStep();
+ void MarkResumed();
DWORD GetContinueStatus() const;
@@ -95,6 +98,7 @@
void* GetRegisterValueBuffer(const std::string& regName) const;
void HandleHardwareBreak();
+ void SendWatchpointNotification(const TBreakpoint*);
std::pair<int, int> threadLookupPair_;
@@ -102,6 +106,14 @@
bool isSuspended_;
bool isTerminating_;
bool isUserSuspended_;
+
+ bool isWatchpointStepping_;
+ DWORD wpSteppingAddr_; // only valid if isWatchpointStepping_
+
+ // if a page-protection occurs, this will hold the
+ // watchpoint associated with the address, if any
+ TBreakpoint* wpSteppingSavedWP; // only valid if isWatchpointStepping_
+
CONTEXT threadContextInfo_;
EXCEPTION_DEBUG_INFO exceptionInfo_;
std::map<std::string, std::string> registerValueCache_;