在多线程应用程序中设置硬件断点不会触发
Posted
技术标签:
【中文标题】在多线程应用程序中设置硬件断点不会触发【英文标题】:Setting a hardwarebreakpoint in multithreaded application doesn't fire 【发布时间】:2015-09-29 06:23:16 【问题描述】:我写了一个小调试器来分析和寻找某些问题。现在我实现了一个硬件断点来检测被覆盖的内存地址的访问。当我使用测试过程运行调试器时,一切正常。当我访问该地址时,会触发断点并记录调用堆栈。问题是,当我对运行多个线程的应用程序运行相同的程序时。我将断点复制到创建的每个线程以及主线程中。没有一个函数报告错误,一切看起来都很好,但是当访问地址时,断点永远不会触发。
所以我想知道是否有一些文档对此进行了描述,或者在多线程应用程序的情况下我是否需要做一些额外的事情。
设置断点的函数是这样的:
#ifndef _HARDWARE_BREAKPOINT_H
#define _HARDWARE_BREAKPOINT_H
#include "breakpoint.h"
#define MAX_HARDWARE_BREAKPOINT 4
#define REG_DR0_BIT 1
#define REG_DR1_BIT 4
#define REG_DR2_BIT 16
#define REG_DR3_BIT 64
class HardwareBreakpoint : public Breakpoint
public:
typedef enum
REG_INVALID = -1,
REG_DR0 = 0,
REG_DR1 = 1,
REG_DR2 = 2,
REG_DR3 = 3
Register;
typedef enum
CODE,
READWRITE,
WRITE,
Type;
typedef enum
SIZE_1,
SIZE_2,
SIZE_4,
SIZE_8,
Size;
typedef struct
void *pAddress;
bool bBusy;
Type nType;
Size nSize;
Register nRegister;
Info;
public:
HardwareBreakpoint(HANDLE hThread);
virtual ~HardwareBreakpoint(void);
/**
* Sets a hardware breakpoint. If no register is free or an error occured
* REG_INVALID is returned, otherwise the hardware register for the given breakpoint.
*/
HardwareBreakpoint::Register set(void *pAddress, Type nType, Size nSize);
void remove(void *pAddress);
void remove(Register nRegister);
inline Info const *getInfo(Register nRegister) const return &mBreakpoint[nRegister];
private:
typedef Breakpoint super;
private:
Info mBreakpoint[MAX_HARDWARE_BREAKPOINT];
size_t mRegBit[MAX_HARDWARE_BREAKPOINT];
size_t mRegOffset[MAX_HARDWARE_BREAKPOINT];
;
#endif // _HARDWARE_BREAKPOINT_H
void SetBits(DWORD_PTR &dw, size_t lowBit, size_t bits, size_t newValue)
DWORD_PTR mask = (1 << bits) - 1;
dw = (dw & ~(mask << lowBit)) | (newValue << lowBit);
HardwareBreakpoint::HardwareBreakpoint(HANDLE hThread)
: super(hThread)
mRegBit[REG_DR0] = REG_DR0_BIT;
mRegBit[REG_DR1] = REG_DR1_BIT;
mRegBit[REG_DR2] = REG_DR2_BIT;
mRegBit[REG_DR3] = REG_DR3_BIT;
CONTEXT ct;
mRegOffset[REG_DR0] = reinterpret_cast<size_t>(&ct.Dr0) - reinterpret_cast<size_t>(&ct);
mRegOffset[REG_DR1] = reinterpret_cast<size_t>(&ct.Dr1) - reinterpret_cast<size_t>(&ct);
mRegOffset[REG_DR2] = reinterpret_cast<size_t>(&ct.Dr2) - reinterpret_cast<size_t>(&ct);
mRegOffset[REG_DR3] = reinterpret_cast<size_t>(&ct.Dr3) - reinterpret_cast<size_t>(&ct);
memset(&mBreakpoint[0], 0, sizeof(mBreakpoint));
for(int i = 0; i < MAX_HARDWARE_BREAKPOINT; i++)
mBreakpoint[i].nRegister = (Register)i;
HardwareBreakpoint::Register HardwareBreakpoint::set(void *pAddress, Type nType, Size nSize)
CONTEXT ct = 0;
super::setAddress(pAddress);
ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if(!GetThreadContext(getThread(), &ct))
return HardwareBreakpoint::REG_INVALID;
size_t iReg = 0;
for(int i = 0; i < MAX_HARDWARE_BREAKPOINT; i++)
if (ct.Dr7 & mRegBit[i])
mBreakpoint[i].bBusy = true;
else
mBreakpoint[i].bBusy = false;
Info *reg = NULL;
// Address already used?
for(int i = 0; i < MAX_HARDWARE_BREAKPOINT; i++)
if(mBreakpoint[i].pAddress == pAddress)
iReg = i;
reg = &mBreakpoint[i];
break;
if(reg == NULL)
for(int i = 0; i < MAX_HARDWARE_BREAKPOINT; i++)
if(!mBreakpoint[i].bBusy)
iReg = i;
reg = &mBreakpoint[i];
break;
// No free register available
if(!reg)
return HardwareBreakpoint::REG_INVALID;
*(void **)(((char *)&ct)+mRegOffset[iReg]) = pAddress;
reg->bBusy = true;
ct.Dr6 = 0;
int st = 0;
if (nType == CODE)
st = 0;
if (nType == READWRITE)
st = 3;
if (nType == WRITE)
st = 1;
int le = 0;
if (nSize == SIZE_1)
le = 0;
else if (nSize == SIZE_2)
le = 1;
else if (nSize == SIZE_4)
le = 3;
else if (nSize == SIZE_8)
le = 2;
SetBits(ct.Dr7, 16 + iReg*4, 2, st);
SetBits(ct.Dr7, 18 + iReg*4, 2, le);
SetBits(ct.Dr7, iReg*2, 1, 1);
ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if(!SetThreadContext(getThread(), &ct))
return REG_INVALID;
return reg->nRegister;
每当创建新线程CREATE_THREAD_DEBUG_EVENT
时,我都会在主调试器循环中设置断点,但查看GDB
的源代码似乎还没有完成,所以可能还为时过早?
【问题讨论】:
您缺少 Windows 标签...在 Linux 上,您将使用ptrace
哦,是的。我现在添加了标签。原来我认为在硬件中设置断点可能有问题。
看来您是在使用自己的代码自己设置硬件断点。我很好奇如果您使用的是 GDB(我相信它支持 Windows 上的硬件断点),为什么会这样
我使用的是 GDB 的源代码,因为我希望获得一些见解,但是仅仅为此进行挖掘是非常复杂的。我之所以写自己的代码,是因为这是我自己的小调试器,用于分析无法安装/运行调试器的生产问题。
【参考方案1】:
所以我终于找到了这个问题的答案。
在调试事件循环中,我正在监视 Windows 发送给我的事件。其中一个事件是CREATE_THREAD_DEBUG_EVENT
,每当创建新线程时,我都会使用它设置硬件断点。
问题是,这个事件的通知是在线程实际启动之前发出的。所以 Windows 是在发送此事件后第一次设置上下文,这当然会覆盖我之前设置的任何上下文数据。
我现在实现的解决方案是,当CREATE_THREAD_DEBUG_EVENT
来的时候,我在线程的起始地址下放了一个软件断点,这样第一条指令就是我的断点。当我收到断点事件时,我恢复原始代码并安装硬件断点,现在可以正常触发。
如果有更好的解决方案,我会全力以赴。 :)
【讨论】:
以上是关于在多线程应用程序中设置硬件断点不会触发的主要内容,如果未能解决你的问题,请参考以下文章
不能总是在 Chrome 中设置 React Native 断点
Scala,docker - 如何使用 sbt-native-packager 在多模块应用程序中设置 mainClass?