2011-01-31 10 views
7

Si tiene un conjunto limpio de código Delphi, y todos los subprocesos se crean con TThread, puede establecer un punto de interrupción en los métodos del constructor (TThread.Create) y descubrir quién creó sus subprocesos. Incluso podría tratar de nombrar todos sus hilos utilizando la característica integrada en el objeto Delphi TThread que le permite establecer un nombre de depuración para cada hilo.¿Cómo puede averiguar quién crea todos sus hilos en un programa delphi?

Pero, ¿cómo identifica los subprocesos persistentes y difíciles de encontrar, que siguen siendo anónimos (sin nombre de depuración) y que aparecen, por ejemplo, durante la inicialización del módulo, cuando se inicia la aplicación. Puedo pasar un solo paso a través de la inicialización del módulo, pero no puedo determinar todos los módulos fuente (de, digamos, más de 900 secciones de inicialización del módulo que están hechos) que pueden crear hilos, y no he encontrado una manera de agregue un mensaje de depuración (usando propiedades y mensajes de punto de interrupción) que volcará el nombre de cada unidad mientras se inicializa. El uso creativo de los puntos de interrupción establecidos en System.pas, con mensajes de registro me permite hacer algunas cosas al depurar aplicaciones trivialmente simples, pero cuanto más compleja es la aplicación, más me siento ciego por los hilos, tanto los creados durante el medio de una aplicación se ejecuta, y las creadas en el módulo-init time (eso es antes de que ingrese en la primera línea del código en su proyecto dpr).

Me gustaría saber qué técnicas avanzadas podrías haber encontrado para identificar y descubrir quién creó un hilo en particular. Si utilizáramos un depurador como GDB en lugar de un depurador como el delphi debugger kernel (Turbo Debugger?) Que está integrado en el delphi IDE, creo que podríamos establecer un punto de interrupción en una API de Windows como BeginThread. Pero no creo que pueda hacer eso en Delphi.

Actualización: No sabía que podía establecer un punto de interrupción en la sección de implementación de windows.pas para windows dlls externos como kernel32.dll.

Actualización 2: Parece que la respuesta de David H es la mejor idea para el uso general. También estoy buscando en una pequeña biblioteca de código auxiliar que estoy escribiendo ahora que mantiene un diccionario de identificadores de subprocesos que se han visto antes, y que asigna algunos nombres de depuración a subprocesos sin nombre, en función de su tiempo de creación (qué función estábamos llamando justo antes de notar que el nuevo hilo existe). Creo que esto me ayudará a restringir mis más de 40 hilos numerados para que todos sean nombrados, aunque algunos de ellos se creen en dlls C/C++ externos o mediante procesos COM.

+0

Para información, kernel32 no es parte del kernel. Sé que esto es confuso. El kernel de Windows reside en ntoskrnl.exe. kernel32 es una DLL en modo de usuario, aunque estoy seguro de que pasa el trabajo al modo kernel para muchas de sus funciones. Ciertamente no se puede depurar el código del modo kernel con el depurador Delphi –

+0

. Lo sabía realmente en algún lugar en la parte posterior de mi cabeza. Pero para ser menos descuidado he corregido mi actualización. –

+0

Noto que un subproceso se crea mediante una llamada COM CoInitializeSecurity. Estaba un poco sorprendido por eso. –

Respuesta

9

Probablemente usaría herramientas como Process Explorer y madExcept, pero hay muchas herramientas que pueden ser útiles.

No creo que Delphi use Turbo Debugger. Además, Delphi es perfectamente capaz de establecer puntos de interrupción en los puntos de entrada kernel32 como CreateThread.

Me gustaría ejecutar con Debug DCU habilitado y establecer un punto de interrupción en la implementación de CreateThread en Windows.pas. Una vez que se rompe, cambie a la ventana de la CPU y entre en la rutina. Verá una instrucción JMP DWORD PTR [address]. Paso sobre esto y listo, ahora estás depurando kernel32. Puede establecer un punto de interrupción aquí.

Ahora, si restablece su aplicación y comienza a depurar nuevamente, interrumpirá todas las llamadas a kernel32.CreateThread que se originan en su proceso. La inspección de la pila de llamadas le dirá cómo llegó allí. Se ve algo como esto:

enter image description here

Por último, no estoy seguro de por qué usted está preocupado por su aplicación para la creación de hilos. La mayoría de las aplicaciones de tamaño decente crean muchos hilos, es perfectamente normal hacerlo. ¿Qué problemas te encuentras?

+0

Cool. ¡No tenía idea de que pudieras hacer eso! –

+0

+1. Muy buen truco. –

+0

@Mason Gracias. ¡Pensé que todos sabían esto! El depurador Delphi es definitivamente mejor en cada lanzamiento. –

6

... Creo que podríamos establecer un punto de interrupción en una función api de Windows como BeginThread. Pero no creo que pueda hacer eso en Delphi.

Seguro que puedes.

  • Habilitar proyecto, opciones, compilador Delphi, compilar, depurar, usar depuración .dcus. (Esa es la manera de encontrarlo en Delphi XE, la ubicación exacta puede diferir en las diferentes versiones de Delphi).

  • Recompilar.

  • Abra la unidad del sistema y coloque el punto de interrupción en Resultado: = CreateThread ... en la función BeginThread.

  • Ejecute el programa y espere hasta que se desencadene el punto de interrupción.

  • Abre la ventana de la CPU (Ver, depurar Windows, CPU de Windows, CPU completa).

ventana CPU mostrará algo como esto:

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 
  • Haga clic en la ventana de la CPU en la línea de 'llamar CreateThread'.

  • Presione F4.

  • Presione F7.

Se lo colocará en la mesa de despacho:

CreateThread: 
00402608 FF2594AA4F00  jmp dword ptr [$004faa94] 
0040260E 8BC0    mov eax,eax 
  • Presione F5 para poner punto crítico aquí.

  • Vuelva a ejecutar el programa (Ctrl-F2, F9).

El punto de interrupción se activará cada vez que se cree un hilo. El punto de interrupción aparecerá en WindowsAPIs.INC en

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

(al menos en Delphi XE).

Usted puede todavía perder algunas llamadas de creación de hilo. No sé si este método atrapará hilos creados internamente por Direct X, por ejemplo.

+0

que solo recibe las llamadas a CreateThread que se originan en Delphi. Me gustaría ir un nivel más profundo y establecer un punto de interrupción en Kernel32 - entonces puede captar las llamadas de otras fuentes - por ejemplo, Bibliotecas Win32/COM. Eso es lo que estaba tratando de expresar en mi respuesta, ¡aunque has entrado en muchos más detalles que yo! –

+0

Wow. Eso es aún más detallado. Amo los detalles. –

+0

que no atrapa algunos de los creados por (creo) DevExpress, y sospecho que se pierden todos los creados a través de COM. Pero atrapa todo lo que usa el código Delphi para crearlos, de modo que obtienes todo lo que puedes hacer con algo útil. –

1

En realidad BeginThread es una función en System.pas, aunque sea una "privada" (sin declaración de función en la sección de interfaz). Entonces, usando debug dcu's, simplemente podría establecer un punto de interrupción en la función BeginThread y examinar el seguimiento de la pila desde allí.

Otra opción es conectar la función BeginThread usando madCodeHook o KBSM (IIRC).Dentro de la función de enganche se puede usar algo como:

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

para que pueda registrar todos los hilos están creando, sin importar dónde están viniendo. Los únicos que te faltarán son los hilos creados al invocar directamente a la API de Windows CreateThread. Pero puede usar las mismas técnicas de enganche para tener en sus manos esas llamadas.

actualización

Oh, IsInstanceOfType es una de nuestras funciones de biblioteca pero básicamente se toma un puntero sin tipo y comprueba para ver si se hace referencia a un objeto de la clase dada.

+0

No hay necesidad de enganchar, ¡simplemente configure un punto de interrupción en kernel32! –

+0

Enganchar podría ser útil, pero en mi caso, la respuesta de David es más simple. –

+0

@David: si solo quieres hacerlo una vez, estoy de acuerdo. Si está interesado en la creación/destrucción de hilos como una cuestión de rutina (registro/seguimiento de una aplicación en vivo), necesita ganchos. @Warren probablemente solo lo necesite una vez, pero cuando agrega o cambia componentes, tener el código de enganche en su lugar, podría ser mucho más fácil si combina eso con un poco de registro. –

Cuestiones relacionadas