Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you find out who creates all your threads in a delphi program?

If you have a clean set of Delphi code, and all threads are created using TThread, you could set a breakpoint in the constructor method(s) (TThread.Create) and find out who created your threads. You could even try to name all your threads using the feature built into the Delphi TThread object that lets you set a debug-name for each thread.

But how do you identify the persistent, hard-to-find extra threads, that are still anonymous (no debug name) and which appear, say, during module initialization time, as the application starts. I can single-step through module-initialization, but I am unable to determine all the source modules (out of say, 900+ module initialization sections that are done) that are likely to create threads, and I have not figured out a way to add a debug-message (using breakpoint properties and messages) that would dump each unit name while it initializes. Creative use of breakpoints set in System.pas, with logging-messages allows me to do some things when debugging trivially simple applications, but the more complex my application grows, the more I feel blind-sided by threads, both those created during the middle of an application runs, and those created at module-init time (that is before you step into the first line of the code in your project dpr).

I would like to know what advanced techniques you might have found to identify and figure out who created a particular thread. If we were using a debugger like GDB instead of a debugger like the delphi debugger kernel (Turbo Debugger?) that is built into the delphi IDE, I think we could set a breakpoint on a windows api function like BeginThread itself. But I don't think I can do that in Delphi.

Update: I didn't know you could set a breakpoint in the implementation section of windows.pas for external windows dlls like kernel32.dll.

Update 2: It seems that David H's answer is the best idea for general use. I also am looking into a little helper code library that I'm writing right now that maintains a dictionary of thread ids that have been seen before, and which assigns some debug names to otherwise unnamed threads, based on their time of creation (what function we were calling just before we notice the new thread exists). I think this will help me to narrow down my 40+ numbered threads so that they all get named, even though some of them are created in external C/C++ dlls, or by COM processes.

like image 319
Warren P Avatar asked Jan 31 '11 20:01

Warren P


3 Answers

I'd probably look to use tools like Process Explorer and madExcept, but there are lots of tools around that can be helpful.

I don't believe that Delphi uses Turbo Debugger. What's more Delphi is perfectly capable of setting break points on kernel32 entry points like CreateThread.

I would run with Debug DCUs enabled and set a break point on the implementation of CreateThread in Windows.pas. Once you break there switch to the CPU window and step into the routine. You'll see a JMP DWORD PTR [address] instruction. Step over this and hey presto, you are now debugging in kernel32. You can set a breakpoint here.

Now if you reset your app and start debugging again you will break at all calls to kernel32.CreateThread that originate from your process. Inspection of the call stack will tell you how you got there. It looks something like this:

enter image description here

Finally, I'm not certain why you are troubled by your app creating threads. Most decent sized apps create plenty of threads – it is perfectly normal to do so. What problems are you encountering?

like image 56
David Heffernan Avatar answered Sep 30 '22 14:09

David Heffernan


... I think we could set a breakpoint on a windows api function like BeginThread itself. But I don't think I can do that in Delphi.

Sure you can.

  • Enable Project, Options, Delphi Compiler, Compiling, Debugging, Use debug .dcus. (That's the way to find it in Delphi XE, exact location may differ in different Delphi versions).

  • Recompile.

  • Open the System unit and put breakpoint on Result := CreateThread ... in the BeginThread function.

  • Run the program and wait until the breakpoint is triggered.

  • Open CPU window (View, Debug Windows, CPU Windows, Entire CPU).

CPU window will display something like this:

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
  • Click into CPU window on the line 'call CreateThread'.

  • Press F4.

  • Press F7.

You will be positioned in the dispatch table:

CreateThread:
00402608 FF2594AA4F00     jmp dword ptr [$004faa94]
0040260E 8BC0             mov eax,eax
  • Press F5 to put breakpoint here.

  • Rerun the program (Ctrl-F2, F9).

The breakpoint will be triggered every time a thread is created. Breakpoint will appear to happen in WindowsAPIs.INC at

function CreateThread(SecurityAttributes: Pointer; StackSize: LongWord;
                     ThreadFunc: TThreadFunc; Parameter: Pointer;
                     CreationFlags: LongWord; var ThreadId: LongWord): Integer; stdcall;
  external kernel name 'CreateThread';

(at least in Delphi XE).

You may still miss some thread creation calls. I don't know if this method will catch threads internally created by Direct X, for example.

like image 45
gabr Avatar answered Sep 30 '22 13:09

gabr


Actually BeginThread is a function in System.pas, albeit a "private" one (no function declaration in the interface section). So, using debug dcu's you could simply set a breakpoint in the BeginThread function and examine the stack trace from there.

Another option is to hook the BeginThread function using madCodeHook or KBSM (IIRC). Inside the hooking function you can use something like:

  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';

so you can log all threads being created, regardless where they are coming from. The only ones you'd be missing are threads created by calling the Windows API CreateThread directly. But you can use the same hooking techniques to get your hands on those calls.

Update

Oh, IsInstanceOfType is one of our library functions but it basically takes an untyped pointer and checks to see if it references an object of the given class.

like image 36
Marjan Venema Avatar answered Sep 30 '22 12:09

Marjan Venema