Как вы можете узнать, кто создает все ваши потоки в программе delphi?

Если у вас есть чистый набор кода Delphi, и все потоки создаются с помощью TThread, вы можете установить точку останова в методе (- ах) конструктора (TThread.Create) и узнайте, кто создал ваши потоки. Вы даже можете попытаться назвать все свои потоки, используя функцию, встроенную в объект Delphi TThread, которая позволяет установить имя отладки для каждого потока.

но как вы определяете постоянные, труднодоступные дополнительные потоки, которые все еще анонимны (без имени отладки) и которые появляются, скажем, во время инициализации модуля, когда приложение запускается. Я могу сделать один шаг через инициализацию модуля, но я не могу определить все исходные модули (скажем, 900+ разделов инициализации модуля, которые сделаны), которые, вероятно, создадут потоки, и я не понял, как добавить отладочное сообщение (используя свойства точки останова и сообщения), которое будет сбрасывать каждое имя блока во время его инициализации. Творческое использование точек останова, установленных в системе.pas, с журналированием-сообщения позволяют мне нужно делать некоторые вещи при отладке тривиально простых приложений, но чем сложнее мое приложение растет, тем больше я чувствую себя слепым по потокам, как созданным в середине запуска приложения, так и созданным во время ввода модуля (то есть до того, как вы шагнете в первую строку кода в своем проекте dpr).

Я хотел бы знать, какие передовые методы вы могли бы найти, чтобы определить и выяснить, кто создал конкретный поток. Если бы мы использовали как отладчик GDB вместо отладчика, как ядра отладчика Дельфи (Турбо отладчик?), который встроен в Delphi IDE, я думаю, мы могли бы установить точку останова на функции Windows api, такой как BeginThread. Но я не думаю, что смогу сделать это в Дельфах.

Update: я не знал, что вы можете установить точку останова в разделе реализации windows.pas для внешних библиотек Windows, таких как kernel32.файл DLL.

Update 2: кажется, что ответ Дэвида H-лучшая идея для general использовать. Я также просматриваю небольшую вспомогательную библиотеку кода, которую я пишу прямо сейчас, которая поддерживает словарь идентификаторов потоков, которые были замечены раньше, и которая назначает некоторые имена отладки в противном случае неназванным потокам, основываясь на их времени создания (какую функцию мы вызывали непосредственно перед тем, как мы заметили, что новый поток существует). Я думаю, что это поможет мне сузить мои 40 + нумерованные потоки, чтобы они все были названы, хотя некоторые из них созданы во внешних библиотеках C/C++ или COM процессы.

3 ответов


Я, вероятно, буду использовать такие инструменты, как Процесс Explorer и madExcept, но есть много инструментов, которые могут быть полезны.

Я не верю, что Delphi использует Turbo Debugger. Более того, Delphi отлично способен устанавливать точки останова на точках входа kernel32, таких как CreateThread.

Я бы запустил с включенным DCUs отладки и установил точку останова на реализации CreateThread в Windows.первенство. Как только вы сломаете там переключатель к окну CPU и шаг в рутину. Вы увидите JMP DWORD PTR [address] инструкция. Перешагните через это и Эй presto, теперь вы отлаживаете в kernel32. Здесь вы можете установить точку останова.

теперь, если вы сбросите приложение и снова начнете отладку, вы нарушите все вызовы kernel32.CreateThread, которые происходят из вашего процесса. Проверка стека вызовов расскажет вам, как вы туда попали. Это выглядит примерно так:

enter image description here

наконец, я не уверен, почему вас беспокоит ваше приложение, создающее потоки. Большинство приложений приличного размера создают много потоков – это совершенно нормально. С какими проблемами вы сталкиваетесь?


... Я думаю, мы могли бы установить точку останова на функции Windows api, как BeginThread себя. Но я не думаю, что смогу сделать это в Дельфах.

конечно, вы можете.

  • включить проект, параметры, компилятор Delphi, компиляцию, отладку, использовать debug .каждом из модулей DCU. (Это способ найти его в Delphi XE, точное местоположение может отличаться в разных версиях Delphi).

  • перекомпиляции.

  • открыть Системный блок и поставить точку останова на результат: = CreateThread ... в функции BeginThread.

  • запустите программу и дождитесь срабатывания точки останова.

  • открыть окно процессора (просмотр, отладка окон, Окна процессора, весь процессор).

окно CPU отобразит что-то вроде этого:

System.pas.16559: Result := CreateThread(SecurityAttributes, StackSize, @ThreadWrapper, P,
00406A97 8B4508           mov eax,[ebp+]
00406A9A 50               push eax
00406A9B 8B450C           mov eax,[ebp+c]
00406A9E 50               push eax
00406A9F 53               push ebx
00406AA0 B81C6A4000       mov eax,406a1c
00406AA5 50               push eax
00406AA6 8B45F8           mov eax,[ebp-]
00406AA9 50               push eax
00406AAA 8B45FC           mov eax,[ebp-]
00406AAD 50               push eax
00406AAE E855BBFFFF       call CreateThread
00406AB3 8BF0             mov esi,eax
  • Нажмите в окно CPU на линии ' вызов Функции createthread'.

  • Нажмите Клавишу F4.

  • Нажмите Клавишу F7.

вы будете расположены в таблице отправки:

CreateThread:
00402608 FF2594AA4F00     jmp dword ptr [4faa94]
0040260E 8BC0             mov eax,eax
  • нажмите F5, чтобы поставить точку останова здесь.

  • повторно запустите программу (Ctrl-F2, F9).

точка останова будет срабатывать каждый раз при создании потока. Точка останова появится в WindowsAPIs.ИНК at

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, например.


фактически BeginThread является функцией в системе.pas, хотя и "частный" (нет объявления функции в разделе интерфейса). Таким образом, используя debug dcu, вы можете просто установить точку останова в функции BeginThread и изучить трассировку стека оттуда.

другой вариант-подключить функцию BeginThread с помощью madCodeHook или KBSM (IIRC). Внутри функции закрепления вы можете использовать что-то вроде:

  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. Но вы можете использовать те же методы подключения, чтобы получить ваши руки на этих вызовах.

обновление

Oh, IsInstanceOfType-одна из наших библиотечных функций, но она в основном принимает нетипизированный указатель и проверяет, ссылается ли он на объект данного класса.