Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 如何在delphi程序中找出谁创建了所有线程?_Multithreading_Delphi_Debugging - Fatal编程技术网

Multithreading 如何在delphi程序中找出谁创建了所有线程?

Multithreading 如何在delphi程序中找出谁创建了所有线程?,multithreading,delphi,debugging,Multithreading,Delphi,Debugging,如果您有一组干净的Delphi代码,并且所有线程都是使用TThread创建的,那么您可以在构造函数方法(TThread.Create)中设置一个断点,并找出谁创建了您的线程。您甚至可以尝试使用delphitthread对象中内置的特性命名所有线程,该特性允许您为每个线程设置调试名称 但是,您如何识别持久的、难以找到的额外线程,这些线程仍然是匿名的(没有调试名称),并且在应用程序启动时出现在模块初始化期间。我可以单步完成模块初始化,但我无法确定可能创建线程的所有源模块(例如,900多个模块初始化部

如果您有一组干净的Delphi代码,并且所有线程都是使用TThread创建的,那么您可以在构造函数方法(TThread.Create)中设置一个断点,并找出谁创建了您的线程。您甚至可以尝试使用delphitthread对象中内置的特性命名所有线程,该特性允许您为每个线程设置调试名称

但是,您如何识别持久的、难以找到的额外线程,这些线程仍然是匿名的(没有调试名称),并且在应用程序启动时出现在模块初始化期间。我可以单步完成模块初始化,但我无法确定可能创建线程的所有源模块(例如,900多个模块初始化部分已经完成),而且我还没有找到添加调试消息的方法(使用断点属性和消息)这将在初始化时转储每个单元名称。创造性地使用System.pas中设置的断点和日志消息,使我能够在调试简单的应用程序时做一些事情,但应用程序越复杂,我越感到线程的盲目性,无论是在应用程序运行期间创建的线程,还是在模块初始化时创建的线程(这是在您进入项目dpr中的第一行代码之前)

我想知道您可能发现了哪些高级技术来识别和找出谁创建了一个特定的线程这是内置在DelphiIDE中的,我想我们可以在windows api函数上设置断点,比如BeginThread本身,但我不认为我可以在delphi中这样做

更新:我不知道您可以在windows.pas的实现部分为外部windows dll(如kernel32.dll)设置断点

更新2:David H的答案似乎是通用的最佳方案。我还研究了我现在正在编写的一个小助手代码库,它维护一个以前见过的线程ID字典,并根据创建时间为其他未命名的线程分配一些调试名称(在我们注意到新线程存在之前,我们正在调用什么函数)。我认为这将帮助我缩小40多个编号的线程的范围,以便它们都能被命名,即使其中一些线程是在外部C/C++DLL或COM进程中创建的

…我想我们可以在windows api函数上设置断点,如 BeginThread本身。但我认为我在Delphi中做不到这一点

当然可以

  • 启用项目、选项、Delphi编译器、编译、调试、使用debug.dcus(这是在Delphi XE中找到它的方法,在不同的Delphi版本中,确切位置可能不同)

  • 重新编译

  • 打开系统单元并在BeginThread函数中的Result:=CreateThread…上放置断点

  • 运行程序并等待断点被触发

  • 打开CPU窗口(查看、调试窗口、CPU窗口、整个CPU)

CPU窗口将显示如下内容:

System.pas.16559: Result := CreateThread(SecurityAttributes, StackSize, @ThreadWrapper, P,
00406A97 8B4508           mov eax,[ebp+$08]
00406A9A 50               push eax
00406A9B 8B450C           mov eax,[ebp+$0c]
00406A9E 50               push eax
00406A9F 53               push ebx
00406AA0 B81C6A4000       mov eax,$00406a1c
00406AA5 50               push eax
00406AA6 8B45F8           mov eax,[ebp-$08]
00406AA9 50               push eax
00406AAA 8B45FC           mov eax,[ebp-$04]
00406AAD 50               push eax
00406AAE E855BBFFFF       call CreateThread
00406AB3 8BF0             mov esi,eax
  • 单击“调用CreateThread”行上的CPU窗口

  • 按F4

  • 按F7

您将被定位在调度表中:

CreateThread:
00402608 FF2594AA4F00     jmp dword ptr [$004faa94]
0040260E 8BC0             mov eax,eax
  • 按F5将断点放在这里

  • 重新运行程序(Ctrl-F2,F9)

每次创建线程时都会触发断点。断点将出现在WindowsAPI.INC中的

function CreateThread(SecurityAttributes: Pointer; StackSize: LongWord;
                     ThreadFunc: TThreadFunc; Parameter: Pointer;
                     CreationFlags: LongWord; var ThreadId: LongWord): Integer; stdcall;
  external kernel name 'CreateThread';
(至少在Delphi XE中)


您可能仍然会错过一些线程创建调用。例如,我不知道此方法是否会捕获由Direct X在内部创建的线程。

我可能希望使用诸如和之类的工具,但有很多工具可以提供帮助

我不相信Delphi使用的是Turbo调试器,而且Delphi完全能够在内核32入口点上设置断点,比如CreateThread

我将在启用调试DCU的情况下运行,并在Windows.pas中的CreateThread实现上设置断点。一旦在那里中断,切换到CPU窗口并进入例程。您将看到一个
JMP DWORD PTR[address]
指令。请跳过此步骤,嘿,普雷斯托,您现在正在内核32中调试。您可以在此处设置断点

现在,如果您重置应用程序并再次开始调试,您将中断来自您的进程的对kernel32.CreateThread的所有调用。检查调用堆栈将告诉您是如何实现的。如下所示:

System.pas.16559: Result := CreateThread(SecurityAttributes, StackSize, @ThreadWrapper, P,
00406A97 8B4508           mov eax,[ebp+$08]
00406A9A 50               push eax
00406A9B 8B450C           mov eax,[ebp+$0c]
00406A9E 50               push eax
00406A9F 53               push ebx
00406AA0 B81C6A4000       mov eax,$00406a1c
00406AA5 50               push eax
00406AA6 8B45F8           mov eax,[ebp-$08]
00406AA9 50               push eax
00406AAA 8B45FC           mov eax,[ebp-$04]
00406AAD 50               push eax
00406AAE E855BBFFFF       call CreateThread
00406AB3 8BF0             mov esi,eax


最后,我不确定你为什么会被你的应用程序创建线程所困扰。大多数大小合适的应用程序都会创建大量线程——这样做是完全正常的。你遇到了什么问题?

实际上,BeginThread是System.pas中的一个函数,尽管它是一个“私有”函数(在接口部分中没有函数声明)因此,使用debug dcu,您可以简单地在BeginThread函数中设置一个断点,并从此处检查堆栈跟踪

另一个选项是使用madCodeHook或KBSM(IIRC)钩住BeginThread函数。在钩住函数中,您可以使用以下内容:

  UseOurStuff := Assigned(Parameter) and IsInstanceOfType(Parameter, TThread);
  if Assigned(Parameter) then
    if UseOurStuff then
      ThreadClassName := Instance.ClassName
    else
      ThreadClassName := 'Non-object Parameter thread'
  else
    ThreadClassName := 'NIL Parameter thread';
因此,您可以记录正在创建的所有线程,无论它们来自何处。您唯一缺少的是通过直接调用Windows API CreateThread创建的线程。但您可以使用相同的挂接技术来处理这些调用

更新


哦,IsInstanceOfType是我们的库函数之一,但它基本上需要一个非类型指针,并检查它是否引用给定类的对象。

这只获取来自Delphi的对CreateThread的调用。我会深入一层,在kernel32中设置一个断点,然后您可以捕获来自其他源的调用,例如。Win32/COM库。这就是我试图在中表达的内容