Is a process hijacking your machine?
Suppose there is a process on your machine that is hijacking your processor. Perhaps it’s in an infinite loop. Or perhaps you have no idea what a particular process is doing, and you’d like to find out.
If you have a debugger installed, like Visual Studio, it’s fairly simple to do some basic inspection.
First, start Task Manager (Ctrl-Shift-Esc) and look for the process in the Processes tab. You can sort columns by clicking on the header. For example, you can see which process is eating your CPU by sorting on the CPU column. Right click on the target process and choose “Debug”.
After a warning from Task Manager, you are prompted to choose a particular debugger and the kind of debugging (like native code, T-SQL or Common Language Runtime). Just choose all the defaults and the debugger will attach to the process.
Alternatively, you can start Visual Studio and choose Tools->Debug Processes, which will list the running processes and allow you to attach the debugger. It can even attach to a process on a remote machine (if the correct components and permissions are set).
Or you can start the debugger from a Watson error dialog. (If you choose to send crash information via Watson to Microsoft then we can try to inspect the failed process and determine what went wrong.)
I attached to a VFP8 process that ran this infinite loop code which doesn’t hijack the processor in Visual FoxPro because it yields 1 second of processor time each time through the loop, so other processes (like the debugger) won’t be too sluggish.
DO WHILE INKEY(1)!= 27
DOEVENTS
ENDDO
After attaching, choose Debug->Windows->Modules to see what DLLs are loaded in the process. VFP loads a few dozen DLLs, many of which are Windows DLLs.
advapi32.dll
clbcatq.dll
comctl32.dll
comdlg32.dll
comres.dll
frxhdll.dll
gdi32.dll
GdiPlus.dll
imm32.dll
kernel32.dll
mpr.dll
MSCTF.dll
msi.dll
mslbui.dll
msvcp60.dll
msvcr70.dll
msvcrt.dll
ntdll.dll
ole32.dll
oleacc.dll
oleaut32.dll
oledlg.dll
rpcrt4.dll
shell32.dll
SHLWAPI.DLL
sxs.dll
URLMON.DLL
user32.dll
uxtheme.dll
version.dll
VFP8.exe
VFP8ENU.DLL
winmm.dll
winspool.drv
You can choose Debug->Break or you can hit F12 on the debug target application to cause an asynchronous breakpoint. (F12 actually injects a thread into the target process which causes the breakpoint.)
Choose Debug->Windows->Threads to choose various threads (each thread has its own call stack and usually the main thread is at the top) and Debug->Windows->Call stack to get more info:
> 7ffe0304()
user32.dll!77d4414d()
user32.dll!77d441b3()
VFP8.exe!00494945()
VFP8.exe!00713a13()
VFP8.exe!0048414f()
VFP8.exe!006e0fa2()
VFP8.exe!004e8180()
VFP8.exe!0082cd74()
kernel32.dll!77e7a683()
VFP8.exe!00427f34()
VFP8.exe!0054fcbf()
VFP8.exe!0046ef19()
VFP8.exe!0040177d()
ntdll.dll!77f58a3a()
ntdll.dll!77f693c7()
ntdll.dll!77f693f8()
This display doesn’t seem very useful, but Microsoft provides a public symbol server to help your debugger understand what code is being run.
You can set the environment variable _NT_SYMBOL_PATH to set the symbol path. Hit the Windows-Key Break (or right click on My Computer->Properties). Go to the Advanced tab, Environment Variables: New System Variable
Variable Name =_nt_symbol_path
Variable Value = srv*c:\symbols*https://msdl.microsoft.com/download/symbols
(You’ll need to restart VS to read this environment variable.)
Alternatively, you can set the symbol server via Visual Studio: Choose View->Solution Explorer, right click on the solution at the top and choose Properties. For Common Properties->Debug Symbol Files, put in this string for “Search these paths for symbol files”
srv*c:\symbols*https://msdl.microsoft.com/download/symbols
This indicates the web address to get the symbol files and a directory to cache them locally so they can be accessed much faster next time.
Select all in the Modules window, right click and choose to Reload symbols. (VS.Net 2003 may be a little different from VS Whidbey. You may have to Debug->Stop debugging, exit and save the solution, reattach to the same process)
Now the call stack is much more informative:
> 7ffe0304()
ntdll.dll!_ZwDelayExecution@8() Line 595 + 0xc Asm
kernel32.dll!_SleepEx@8() + 0x55
kernel32.dll!_Sleep@4() + 0xb
VFP8.exe!@EVKeyStillDown@8() - 0x188016
VFP8.exe!@EVGetNextEvent@4() + 0x80
VFP8.exe!@xeinkey_loop@28() + 0x10f
VFP8.exe!@emod@0() + 0x19feb7
VFP8.exe!@xxexpr@4() + 0x96
VFP8.exe!@FRContinueExitLoop@4() + 0xb5
VFP8.exe!@x_enddo@0() + 0x8
VFP8.exe!@EXEPostCommand@8() - 0x2315e1
VFP8.exe!@imode@0() + 0x4f
VFP8.exe!@DllWinMain2@12() + 0xb6
VFP8.exe!_DllWinMain@8() + 0x30
VFP8.exe!_WinMain@16() + 0x16
VFP8.exe!_WinMainCRTStartup() + 0x212
kernel32.dll!_BaseProcessStart@4() + 0x23
I changed the test code to do something more meaningful: execute a user form in an infinite loop. I used the Class browser as a sample.
DO WHILE INKEY(1) != 27
DO (_browser)
_obrowser.Release
DOEVENTS
ENDDO
Examining the call stack indicated that MSComCTL.OCX was in the call stack much of the time. This implements the treeview/listview controls of the class browser.
From the call stack, you can see meaningful names like KeyStillDown, EndDo, and inkey_loop.
This technique is a poor developer’s profiler: with a few asynchronous breaks you can determine where the most time is being spent.
(Keep in mind that VS is reconstructing a call stack from the optimized build of many modules and it can be very difficult and is not always correct.)
With symbols loaded, you can even put breakpoints on symbolic names. For example, @goof@4 is the symbol for goof, which is called usually when an error occurs.
BTW, Sysinternals has several tools that will allow you to see what registry entries and files are being accessed, or even to inspect various aspects of a particular process.
Happy debugging!
41045