Hardware breakpoints

转载 2007年09月14日 17:32:00

This is a debugging helper class which lets you set breakpoints on the fly from within code. This is mainly useful for the case where you have a variable that you know is getting trashed, but you have no idea who is trashing it. You can cause the debugger to break in at the very moment the variable is changed. The really cool thing is that this makes use of the Intel Pentium's built-in debug registers, which means that it really will stop no matter what code is executing, even if it's down in the NT kernel, in a different thread, or whatever.

(Visual C++ has the ability to set a breakpoint when a variable's value changes, but these breakpoints can be very hard to use, especially on local variables.)

Usage

The class is called CBreakpoint. If, for example, you have a DWORD called "x" and you want to break the next time it's written to, do this:

DWORD x = 1;

CBreakpoint bp;
bp.Set(&x, sizeof(x), CBreakpoint::Write);

As your code continues to execute, if Visual C++ stops at a location where you didn't have a VC++ breakpoint set, it's possible that the CBreakpoint triggered.  If it did, the assembly instruction immediately before the instruction pointer is the one that caused it to trigger.

The breakpoint will be cleared automatically when the CBreakpoint object falls out of scope.  Or, to clear it explicitly, do this:

bp.Clear();

You can also break the moment any code even tries to read the value of the variable x! (Actually, this will break when someone tries to read or write it.)

bp.Set(&x, sizeof(x), CBreakpoint::Read);

Notes

This code is Intel-specific.  It will run on Win95/98/ME and on WinNT/2000/XP, as long as the machine is running an Intel or compatible chip (e.g. not Alpha).

There are certain limitations when the breakpoint is triggered inside system code (these same limitations apply to hardware breakpoints set by Visual C++ or any other debugger):

  • On WinNT/2000/XP, some system code runs in ring 3 (with user privileges), and other system code runs in ring 0.  If a hardware breakpoint triggers inside ring 0 code, the debugger will stop, but not exactly when the data write happens -- it will stop after execution transitions back to ring 3 code.  In practice, this is not a problem at all -- when the breakpoint triggers, it's usually quite easy to figure out what happened.
  • On Win95/98/ME, the situation is not as good.  All system code is treated by the debugger as untouchable.  Worse, whenever any hardware breakpoint triggers inside system code, you'll never see the breakpoint trigger.  For example, if a call to strcpy() would cause your breakpoint to trigger, you'll see it, because strcpy() is just a regular part of your application.  But if a call to lstrcpy() would cause your breakpoint to trigger, you won't see it, because lstrcpy() is system code.

You can do "bp.Clear()" from the QuickWatch dialog to clear a breakpoint from within the debugger.

The second parameter is the number of bytes to watch. This must be either 1, 2, or 4. Also, the variable being watched must be aligned appropriately (e.g. if you're watching 4 bytes, they must be DWORD-aligned).  If you don't do this, you'll get an error when you try to set the breakpoint.  There is a limit of 4 hardware breakpoints. Because of this (and also because of common sense), don't check in code that uses breakpoints -- just write the code temporarily to find bugs, but delete it before checking in your source into your source control system.

It's important to pass in the address that you actually want to watch. Consider these two similar but different cases:

long *my_array;
my_array = new long[3];
CBreakpoint bpArrayPtr, bpArrayFirstElement;
// break if someone changes the POINTER
bpArrayPtr.Set(&my_array, sizeof(long*), CBreakpoint::Write);
// break if someone changes the FIRST ELEMENT POINTED TO
bpArrayFirstElement.Set(&my_array[0], sizeof(long), CBreakpoint::Write);

How does it work?

The Intel x86 CPUs have some special registers which are intended for debugging use only.  By storing special values into these registers, a program can ask the CPU to execute an INT 1 (interrupt 1) instruction immediately whenever a specified memory location is read from or written to.  (They can also stop when a memory address is about to be executed as code, but my CBreakpoint class doesn't use that functionality.)

INT 1 also happens to be the interrupt that's executed by the CPU after a debugger asks the CPU to single-step one assembly line of the program.  And by good fortune, when Visual C++ encounters an INT 1 that it wasn't expecting to see (as is the case when a CBreakpoint breakpoint is triggered), it responds gracefully, stopping the debugger at the appropriate instruction.

#ifdef _DEBUG

class CBreakpoint
{
public:
 CBreakpoint() { m_index = -1; }
 ~CBreakpoint() { Clear(); }

 // The enum values correspond to the values used by the Intel Pentium,
 // so don't change them!
 enum Condition { Write = 1, Read /* or write! */ = 3 };

 void Set(void* address, int len /* 1, 2, or 4 */, Condition when);
 void Clear();

protected:

 inline void SetBits(unsigned long& dw, int lowBit, int bits, int newValue)
 {
  int mask = (1 << bits) - 1; // e.g. 1 becomes 0001, 2 becomes 0011, 3 becomes 0111

  dw = (dw & ~(mask << lowBit)) | (newValue << lowBit);
 }

 int m_index; // -1 means not set; 0-3 means we've set that hardware bp
};

#endif // _DEBUG

 

#include <windows.h>
#include <assert.h>
#include "breakpoint.h"

#ifdef _DEBUG

void CBreakpoint::Set(void* address, int len, Condition when)
{
 // make sure this breakpoint isn't already set
 assert(m_index == -1);

 CONTEXT cxt;
 HANDLE thisThread = GetCurrentThread();

 switch (len)
 {
 case 1: len = 0; break;
 case 2: len = 1; break;
 case 4: len = 3; break;
 default: assert(false); // invalid length
 }

 // The only registers we care about are the debug registers
 cxt.ContextFlags = CONTEXT_DEBUG_REGISTERS;

 // Read the register values
 if (!GetThreadContext(thisThread, &cxt))
  assert(false);

 // Find an available hardware register
 for (m_index = 0; m_index < 4; ++m_index)
 {
  if ((cxt.Dr7 & (1 << (m_index*2))) == 0)
   break;
 }
 assert(m_index < 4); // All hardware breakpoint registers are already being used

 switch (m_index)
 {
 case 0: cxt.Dr0 = (DWORD) address; break;
 case 1: cxt.Dr1 = (DWORD) address; break;
 case 2: cxt.Dr2 = (DWORD) address; break;
 case 3: cxt.Dr3 = (DWORD) address; break;
 default: assert(false); // m_index has bogus value
 }

 SetBits(cxt.Dr7, 16 + (m_index*4), 2, when);
 SetBits(cxt.Dr7, 18 + (m_index*4), 2, len);
 SetBits(cxt.Dr7, m_index*2,        1, 1);

 // Write out the new debug registers
 if (!SetThreadContext(thisThread, &cxt))
  assert(false);
}


void CBreakpoint::Clear()
{
 if (m_index != -1)
 {
  CONTEXT cxt;
  HANDLE thisThread = GetCurrentThread();

  // The only registers we care about are the debug registers
  cxt.ContextFlags = CONTEXT_DEBUG_REGISTERS;

  // Read the register values
  if (!GetThreadContext(thisThread, &cxt))
   assert(false);

  // Zero out the debug register settings for this breakpoint
  assert(m_index >= 0 && m_index < 4); // m_index has bogus value
  SetBits(cxt.Dr7, m_index*2, 1, 0);

  // Write out the new debug registers
  if (!SetThreadContext(thisThread, &cxt))
   assert(false);

  m_index = -1;
 }
}

#endif // _DEBUG

相关文章推荐

Hardware breakpoints

 IntroductionI decided to write this article about hardware breakpoints for the following reasons:Vi...

DOM Breakpoints Test

  • 2016年12月06日 11:40
  • 205KB
  • 下载

One or more breakpoints cannot be set and have been disabled...

从师姐那里拷贝过来的程序居然不能调试,很郁闷,哈哈,多亏看到网上的帖子,这里也贴一下吧。 转自:http://blog.csdn.net/train_z/archive/2008/04/30/234...

charles通过breakpoints修改web请求

说明: charles有一个breakpoints功能,可以截断、修改客户端的request、repsponse请求。如在charles返回repsponse请求时,我们可以修改response,从...

Xcode – power of breakpoints

转自:http://www.albertopasca.it/whiletrue/2013/06/xcode-power-of-breakpoints/ A collection of use...
  • scsxhny
  • scsxhny
  • 2013年07月02日 15:58
  • 439

Debug error:"One or more breakpoints cannot be set and have been disabled.Execution will stop at the

Debug error:"One or more breakpoints cannot be set and have been disabled.Execution will stop at the...
  • artmcu
  • artmcu
  • 2012年09月28日 08:48
  • 696

Android应用程序访问linux驱动第二步:实现并测试hardware层

不管是出于什么样地考虑,android系统终究是提供了hardware层来封装了对Linux的驱动的访问,同时为上层提供了一个统一的硬件接口和硬件形态。一.Hardware概述在Hardware层中的...

<7> Android HAL hardware.c f分析 Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析

前面在介绍Android系统的开机画面时提到,Android设备的显示屏被抽象为一个帧缓冲区,而Android系统中的SurfaceFlinger服务就是通过向这个帧缓冲区写入内容来绘制应用程序的用户...

T440s 的 Hardware Password Manager是干嘛的

在BIOS的item Specific Help中说是enables the central management of hardware passwords. Enabled to register...

【pySpark教程】Big Data, Hardware trends, and Spark(二)

Big Data, Hardware trends, and Spark 本博客是【pySpark教程】系列的文章。 是 Berkeley 的 Python Spark公开课的学习笔记(...
  • cyh24
  • cyh24
  • 2016年02月13日 21:28
  • 3829
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Hardware breakpoints
举报原因:
原因补充:

(最多只允许输入30个字)