Functions
Functions are essential to programmers, and one of the reasons is because they provide a way of re-using and structuring code. For example, I might write some mathematical code which uses the sin function. In some other code I might use sin again. But I only have to write the sin calculation code once.
Look at the following stack trace:
Each line has three parts:
module!function+displacement (e.g. ntdll.dll!NtWaitForMultipleObjects+0xc)
The stack trace works bottom-up; so
BaseThreadInitThunk
decided to call mainCRTStartup
, which called another function which eventually called the TestHang
function. Note that the displacement values (0xe, 0xf, 0x117, etc.) indicate the return address, not the point at which the function was called. so TestHang
will jump back to main
plus 44 bytes when it finishes running. That's also why you see KiFastSystemCallRet
at the top of the user-mode stack trace.Note that you'll often see these other lines:
module+offset (e.g. somedll.dll+0x1234) address (e.g. 0x12345678)
This usually indicates symbols are missing or something unusual (like .NET's JIT).
Kernel-mode vs. user-mode
In most versions of Windows Process Hacker will show you the kernel-mode stack trace as well. This is not usually very important, but can be useful sometimes. The kernel-mode part of the stack trace usually begins with the module being the kernel; in this case it's
ntkrnlpx.exe
. On most machines look for ntkrnlpa.exe
and ntoskrnl.exe
.Most of the time you will see names such as
KeWaitForSingleObject
or KeWaitForMultipleObjects
in the kernel-mode stack trace. This indicates that the thread is waiting for something to happen and is not running anything. The reason it is common to see this is simply because most threads running in most proceses simply don't do very much; they wait for some external event, process it, and get back to waiting. It's most likely you'll catch the thread while it's sleeping.Here's what it looks like when a thread is doing some heavy processing in user-mode:
Compare this with the first stack trace. You'll see that there's no ntdll.dll stack frame because the thread isn't making a system call (or calling any of ntdll.dll's functions). The first kernel-mode frame has
HalpIpiHandler
. The reason this appears is due to the way in which Process Hacker gets kernel-mode stack traces: it queues a special kernel-mode APC to the thread which performs a stack trace inside the thread itself. So in this case you can see Process Hacker had to interrupt this particular program (which the kernel did with an interrupt) to get its kernel-mode stack trace. This type of stack trace indicates that the program (which is eating away at your available CPU time) is some kind of infinite loop or is simply taking a very long time to do whatever it's doing (e.g. audio/video encoding).