| User Guide | Source | Back to projects |
| Profiler.zip | The full source code. You'll need VB6 and VC++ 6 to build this. You'll also need MASM 6 if you wish to modify the assembler file. |
Profiler.dll is deployed in MTS (Component Services) where it can get it hands on the middle-tier components of interest:
Profiler inspects every Visual Basic DLL in the host process. They're identified by the fact that they reference the Visual Basic runtime library (MSVBVM60.DLL) in the imports section of the PE header. Profiler uses the Windows symbolic debug API (dbghelp.dll) to enumerate symbols in each DLL and examines every procedure looking for the following standard prologues:
push ebp mov ebp, esp push ecx push ecx | push ebp mov ebp, esp push XX | push ebp mov ebp, esp sub esp, XX |
and the following epilogues:
pop edi pop esi pop ebx leave ret | pop edi pop esi pop ebx leave ret XXXX | pop edi pop esi leave ret XXXX | xor eax, eax leave ret XXXX | mov esp, ebp pop ebp ret XXXX |
If a match is found, stubs are created and the original prologue and epilogue are overwritten with jumps to the stubs. The prologue stub ends by jumping back into the original procedure. The stub contains an extra call instruction which performs the logging e.g.
push ebp
call _Enter
mov ebp, esp
push ecx
push ecx
jmp ...
The epilogue stub begins with an extra call e.g.
call _Leave
pop edi
pop esi
pop ebx
leave
ret
Otherwise, the stubs are exact copies of the original code. This adds three jumps and two calls to every procedure plus the logging functions which run to 22 instructions each. This is negligible even compared to the simplest VB property get.
The stubs are located at a known offset within the logging data structure, enabling the logging functions to locate the procedure's counters relative to the stacked return address. CPU cycles are calculated using the Pentium rdtsc instruction which transfers an internal 64-bit cycle counter into EDX:EAX.
To maintain the exclusive counters, the profiler needs to keep track of the call stack for each thread. When an instrumented procedure is called, the profiler applies the necessary correction to the calling procedure's exclusive time. Although it handles threads to some extent, the profiler will not make allowances for context switches so it is best to profile one thing at a time!
I think it is possible to attach to a process as a debugger and "inject" a DLL into that process. It would then be possible to profile any VB application, not just code hosted in MTS. Of course, you could add Instrument and Report buttons to your own form, effectively bypassing Profiler.EXE, and use the DLL directly, in your own application.
| Copyright © Andrew Holme, 2003. |
|