首页 > 解决方案 > WinDbg:如何避免地址/指令断点被错误的线程击中?

问题描述

在不正确的线程中命中动态一次性断点

我正在尝试监视具有不同 DLL 的应用程序中的所有 malloc/free 使用情况,其中一些我无法管理。

我的想法是在 malloc 中放置一个断点,它发出所需的分配大小,并在返回地址上创建一个断点并发出返回的指针。那个动态使用的断点给我带来了问题。它不断受到不同线程的影响,即使在断点定义中,我特别说明了它适用于哪个线程。

这是一个展示基本问题的简单应用程序。

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <windows.h>
#include <thread>


void malloc_loop( int num_iterations ) {
    for (int i=0; i<num_iterations; i++) {
        size_t size = rand() % 200;
        char *p = (char*)malloc(size);
        free(p);
    }
}


int main() {
    int num_iterations=10000000;
    int i;

    srand(time(NULL));

    DebugBreak();

    std::thread r1(&malloc_loop,num_iterations);
    std::thread r2(&malloc_loop,num_iterations);
    std::thread r3(&malloc_loop,num_iterations);
    std::thread r4(&malloc_loop,num_iterations);

    DebugBreak();

    r1.join();
    r2.join();
    r3.join();
    r4.join();

    return 0;
}

如果我在调试器(windbg 或 cdb)中运行应用程序,我会转到第二个断点(在创建工作线程之后),并设置一个断点,如

~4 bp40 ucrtbase!malloc ".printf \"[0x%08x] malloc(%d): \", dwo(@esp), dwo(@esp+4); ~4 bp499 /1 dwo(@esp) \".printf \\\"0x%08x\\\\n\\\", @eax; gc\"; gc "

它应该打印[<return_addr>] malloc(<alloc_size>):然后在返回时创建一个打印指针值的断点。

这应该只导致一个工作线程(在我创建我的进程之前该进程包含 4 个线程)中断。

如果我继续使用该断点,会话将运行一会儿,然后在前一个 malloc 的返回地址上中断,但在错误的线程中(它也不会运行我的“处理程序”代码,它应该自动继续)。

这是一个典型的会话

0:000> ~
.  0  Id: 3010.5afc Suspend: 1 Teb: 01169000 Unfrozen
   1  Id: 3010.2b98 Suspend: 1 Teb: 0116c000 Unfrozen
   2  Id: 3010.4810 Suspend: 1 Teb: 0116f000 Unfrozen
   3  Id: 3010.6498 Suspend: 1 Teb: 01172000 Unfrozen
   4  Id: 3010.4160 Suspend: 1 Teb: 01175000 Unfrozen
   5  Id: 3010.3ce4 Suspend: 1 Teb: 01178000 Unfrozen
   6  Id: 3010.4c80 Suspend: 1 Teb: 0117b000 Unfrozen
   7  Id: 3010.5630 Suspend: 1 Teb: 0117e000 Unfrozen
0:000> ~4 bp40 ucrtbase!malloc ".printf \"[0x%08x] malloc(%d): \", dwo(@esp), dwo(@esp+4); ~4 bp499 /1 dwo(@esp) \".printf \\\"0x%08x\\\\n\\\", @eax; gc\"; gc "
0:000> g
[0x00d7105b] malloc(88): 0x013c1568
[0x00d7105b] malloc(26): 0x013c6ea8
[0x00d7105b] malloc(157): 0x013c0958
[0x00d7105b] malloc(132): [0x00d7105b] malloc(132): breakpoint 499 exists, redefining
[0x00d7105b] malloc(132): breakpoint 499 exists, redefining
0x013c12d0
[0x00d7105b] malloc(169): [0x00d7105b] malloc(169): breakpoint 499 exists, redefining
0x013c0d20
[0x00d7105b] malloc(154): 0x013c12d0
(3010.5630): Break instruction exception - code 80000003 (first chance)
eax=013c6ea8 ebx=013c0fb0 ecx=43b388fd edx=40000060 esi=00d719c0 edi=013c0fb0
eip=00d7105b esp=01dffb40 ebp=01dffb50 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
malloc_rand_loop_mt+0x105b:
00d7105b 83c404          add     esp,4

请注意,到达断点的线程 (5630)不是我指定的线程 (4160)。我可以g从这里一遍又一遍地继续(),并且每经过 5-20 次,就会发生这种“误击”。

我是否误解了断点应该如何工作?

我正在为特定线程创建一次性断点。“错误命中”看起来像是在上一个断点 (#40) 的相应返回地址处,但它没有运行任何处理程序代码。

断点是否有可能在完全创建之前被部分创建(并命中)(以及施加的线程 ID 限制和处理程序代码)?

我可以创建一个禁用的断点,配置它,然后启用它吗?(我在文档中没有看到描述创建禁用断点的参考资料。)

我也对重新定义警告感到有些困扰,“断点 499 存在,正在重新定义”,因为断点在创建时不应该存在(同一个线程如何从 malloc 中调用 malloc)?我不知道该怎么做。

任何人都可以帮助我了解调试器中发生了什么或如何避免其他线程的“错误命中”?

标签: multithreadingdebuggingwindbg

解决方案


推荐阅读