草庐IT

c++ - 在多线程应用程序中设置硬件断点不会触发

coder 2024-06-13 原文

我写了一个小调试器来分析和查找某些问题。现在我实现了一个硬件断点来检测被覆盖的内存地址的访问。当我用测试过程运行我的调试器时,一切正常。当我访问该地址时,断点将触发并记录调用堆栈。问题是,当我对运行多个线程的应用程序运行相同的程序时。我正在将断点复制到创建的每个线程以及主线程中。没有一个函数报告错误,一切看起来都很好,但是当访问该地址时,断点永远不会触发。

所以我想知道是否有一些文档对此进行了描述,或者在多线程应用程序的情况下是否还有其他我必须做的事情。

设置断点的函数是这样的:

#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 发送给我的事件。其中一个事件是 CREATE_THREAD_DEBUG_EVENT,每当创建新线程时,我都会用它来设置硬件断点。

问题是,这个事件的通知是在线程实际启动之前发出的。所以 Windows 在发送此事件后第一次设置上下文,这当然会覆盖我之前设置的任何上下文数据。

我现在实现的解决方案是,当 CREATE_THREAD_DEBUG_EVENT 出现时,我在线程的起始地址放置一个软件断点,这样第一条指令就是我的断点。当我收到断点事件时,我恢复原始代码并安装硬件断点,现在可以正常触发。

如果有更好的解决方案,我会洗耳恭听。 :)

关于c++ - 在多线程应用程序中设置硬件断点不会触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32837019/

有关c++ - 在多线程应用程序中设置硬件断点不会触发的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  3. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  4. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  5. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  6. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  7. ruby - Highline 询问方法不会使用同一行 - 2

    设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

  8. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  9. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  10. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

随机推荐