Files
mirror-processhacker/2.x/trunk/ProcessHacker/thrdstk.c
T
wj32 36a4284b8d converted all annotations to SAL 2.0
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@5548 21ef857c-d57f-4fe0-8362-d861dc6d29cd
2013-11-10 06:25:03 +00:00

653 lines
22 KiB
C

/*
* Process Hacker -
* thread stack viewer
*
* Copyright (C) 2010-2012 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
#include <phapp.h>
#include <kphuser.h>
#include <settings.h>
#include <phplug.h>
#define WM_PH_COMPLETED (WM_APP + 301)
#define WM_PH_STATUS_UPDATE (WM_APP + 302)
typedef struct _THREAD_STACK_CONTEXT
{
HANDLE ProcessId;
HANDLE ThreadId;
HANDLE ThreadHandle;
HWND ListViewHandle;
PPH_SYMBOL_PROVIDER SymbolProvider;
BOOLEAN CustomWalk;
BOOLEAN StopWalk;
PPH_LIST List;
PPH_LIST NewList;
HWND ProgressWindowHandle;
NTSTATUS WalkStatus;
PPH_STRING StatusMessage;
PH_QUEUED_LOCK StatusLock;
} THREAD_STACK_CONTEXT, *PTHREAD_STACK_CONTEXT;
typedef struct _THREAD_STACK_ITEM
{
PH_THREAD_STACK_FRAME StackFrame;
ULONG Index;
PPH_STRING Symbol;
} THREAD_STACK_ITEM, *PTHREAD_STACK_ITEM;
INT_PTR CALLBACK PhpThreadStackDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
VOID PhpFreeThreadStackItem(
_In_ PTHREAD_STACK_ITEM StackItem
);
NTSTATUS PhpRefreshThreadStack(
_In_ HWND hwnd,
_In_ PTHREAD_STACK_CONTEXT ThreadStackContext
);
INT_PTR CALLBACK PhpThreadStackProgressDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
static RECT MinimumSize = { -1, -1, -1, -1 };
VOID PhShowThreadStackDialog(
_In_ HWND ParentWindowHandle,
_In_ HANDLE ProcessId,
_In_ HANDLE ThreadId,
_In_ PPH_SYMBOL_PROVIDER SymbolProvider
)
{
NTSTATUS status;
THREAD_STACK_CONTEXT threadStackContext;
HANDLE threadHandle = NULL;
// If the user is trying to view a system thread stack
// but KProcessHacker is not loaded, show an error message.
if (ProcessId == SYSTEM_PROCESS_ID && !KphIsConnected())
{
PhShowError(ParentWindowHandle, KPH_ERROR_MESSAGE);
return;
}
memset(&threadStackContext, 0, sizeof(THREAD_STACK_CONTEXT));
threadStackContext.ProcessId = ProcessId;
threadStackContext.ThreadId = ThreadId;
threadStackContext.SymbolProvider = SymbolProvider;
if (!NT_SUCCESS(status = PhOpenThread(
&threadHandle,
THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME,
ThreadId
)))
{
if (KphIsConnected())
{
status = PhOpenThread(
&threadHandle,
ThreadQueryAccess,
ThreadId
);
}
}
if (!NT_SUCCESS(status))
{
PhShowStatus(ParentWindowHandle, L"Unable to open the thread", status, 0);
return;
}
threadStackContext.ThreadHandle = threadHandle;
threadStackContext.List = PhCreateList(10);
threadStackContext.NewList = PhCreateList(10);
PhInitializeQueuedLock(&threadStackContext.StatusLock);
DialogBoxParam(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_THRDSTACK),
ParentWindowHandle,
PhpThreadStackDlgProc,
(LPARAM)&threadStackContext
);
PhSwapReference(&threadStackContext.StatusMessage, NULL);
PhDereferenceObject(threadStackContext.NewList);
PhDereferenceObject(threadStackContext.List);
if (threadStackContext.ThreadHandle)
NtClose(threadStackContext.ThreadHandle);
}
static INT_PTR CALLBACK PhpThreadStackDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
NTSTATUS status;
PTHREAD_STACK_CONTEXT threadStackContext;
PPH_STRING title;
HWND lvHandle;
PPH_LAYOUT_MANAGER layoutManager;
threadStackContext = (PTHREAD_STACK_CONTEXT)lParam;
SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)threadStackContext);
title = PhFormatString(L"Stack - thread %u", (ULONG)threadStackContext->ThreadId);
SetWindowText(hwndDlg, title->Buffer);
PhDereferenceObject(title);
lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 30, L" ");
PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 300, L"Name");
PhSetListViewStyle(lvHandle, FALSE, TRUE);
PhSetControlTheme(lvHandle, L"explorer");
PhLoadListViewColumnsFromSetting(L"ThreadStackListViewColumns", lvHandle);
threadStackContext->ListViewHandle = lvHandle;
layoutManager = PhAllocate(sizeof(PH_LAYOUT_MANAGER));
PhInitializeLayoutManager(layoutManager, hwndDlg);
SetProp(hwndDlg, L"LayoutManager", (HANDLE)layoutManager);
PhAddLayoutItem(layoutManager, lvHandle, NULL,
PH_ANCHOR_ALL);
PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDC_COPY),
NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDC_REFRESH),
NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDOK),
NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
if (MinimumSize.left == -1)
{
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = 190;
rect.bottom = 120;
MapDialogRect(hwndDlg, &rect);
MinimumSize = rect;
MinimumSize.left = 0;
}
PhLoadWindowPlacementFromSetting(NULL, L"ThreadStackWindowSize", hwndDlg);
PhCenterWindow(hwndDlg, GetParent(hwndDlg));
if (PhPluginsEnabled)
{
PH_PLUGIN_THREAD_STACK_CONTROL control;
control.Type = PluginThreadStackInitializing;
control.UniqueKey = threadStackContext;
control.u.Initializing.ProcessId = threadStackContext->ProcessId;
control.u.Initializing.ThreadId = threadStackContext->ThreadId;
control.u.Initializing.ThreadHandle = threadStackContext->ThreadHandle;
control.u.Initializing.SymbolProvider = threadStackContext->SymbolProvider;
control.u.Initializing.CustomWalk = FALSE;
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control);
threadStackContext->CustomWalk = control.u.Initializing.CustomWalk;
}
status = PhpRefreshThreadStack(hwndDlg, threadStackContext);
if (status == STATUS_ABANDONED)
EndDialog(hwndDlg, IDCANCEL);
else if (!NT_SUCCESS(status))
PhShowStatus(hwndDlg, L"Unable to load the stack", status, 0);
}
break;
case WM_DESTROY:
{
PPH_LAYOUT_MANAGER layoutManager;
PTHREAD_STACK_CONTEXT threadStackContext;
ULONG i;
layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager");
PhDeleteLayoutManager(layoutManager);
PhFree(layoutManager);
threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
if (PhPluginsEnabled)
{
PH_PLUGIN_THREAD_STACK_CONTROL control;
control.Type = PluginThreadStackUninitializing;
control.UniqueKey = threadStackContext;
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control);
}
for (i = 0; i < threadStackContext->List->Count; i++)
PhpFreeThreadStackItem(threadStackContext->List->Items[i]);
PhSaveListViewColumnsToSetting(L"ThreadStackListViewColumns", GetDlgItem(hwndDlg, IDC_LIST));
PhSaveWindowPlacementToSetting(NULL, L"ThreadStackWindowSize", hwndDlg);
RemoveProp(hwndDlg, PhMakeContextAtom());
RemoveProp(hwndDlg, L"LayoutManager");
}
break;
case WM_COMMAND:
{
INT id = LOWORD(wParam);
switch (id)
{
case IDCANCEL: // Esc and X button to close
case IDOK:
EndDialog(hwndDlg, IDOK);
break;
case IDC_REFRESH:
{
NTSTATUS status;
if (!NT_SUCCESS(status = PhpRefreshThreadStack(
hwndDlg,
(PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom())
)))
{
PhShowStatus(hwndDlg, L"Unable to load the stack", status, 0);
}
}
break;
case IDC_COPY:
{
HWND lvHandle;
lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
if (ListView_GetSelectedCount(lvHandle) == 0)
PhSetStateAllListViewItems(lvHandle, LVIS_SELECTED, LVIS_SELECTED);
PhCopyListView(lvHandle);
SetFocus(lvHandle);
}
break;
}
}
break;
case WM_NOTIFY:
{
LPNMHDR header = (LPNMHDR)lParam;
switch (header->code)
{
case LVN_GETINFOTIP:
{
LPNMLVGETINFOTIP getInfoTip = (LPNMLVGETINFOTIP)header;
HWND lvHandle;
PTHREAD_STACK_CONTEXT threadStackContext;
lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
if (header->hwndFrom == lvHandle)
{
PTHREAD_STACK_ITEM stackItem;
PPH_THREAD_STACK_FRAME stackFrame;
if (PhGetListViewItemParam(lvHandle, getInfoTip->iItem, &stackItem))
{
PH_STRING_BUILDER stringBuilder;
PPH_STRING fileName;
PH_SYMBOL_LINE_INFORMATION lineInfo;
stackFrame = &stackItem->StackFrame;
PhInitializeStringBuilder(&stringBuilder, 40);
// There are no params for kernel-mode stack traces.
if ((ULONG_PTR)stackFrame->PcAddress <= PhSystemBasicInformation.MaximumUserModeAddress)
{
PhAppendFormatStringBuilder(
&stringBuilder,
L"Parameters: 0x%Ix, 0x%Ix, 0x%Ix, 0x%Ix\n",
stackFrame->Params[0],
stackFrame->Params[1],
stackFrame->Params[2],
stackFrame->Params[3]
);
}
if (PhGetLineFromAddress(
threadStackContext->SymbolProvider,
(ULONG64)stackFrame->PcAddress,
&fileName,
NULL,
&lineInfo
))
{
PhAppendFormatStringBuilder(
&stringBuilder,
L"File: %s: line %u\n",
fileName->Buffer,
lineInfo.LineNumber
);
PhDereferenceObject(fileName);
}
if (stringBuilder.String->Length != 0)
PhRemoveStringBuilder(&stringBuilder, stringBuilder.String->Length / 2 - 1, 1);
if (PhPluginsEnabled)
{
PH_PLUGIN_THREAD_STACK_CONTROL control;
control.Type = PluginThreadStackGetTooltip;
control.UniqueKey = threadStackContext;
control.u.GetTooltip.StackFrame = stackFrame;
control.u.GetTooltip.StringBuilder = &stringBuilder;
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control);
}
PhCopyListViewInfoTip(getInfoTip, &stringBuilder.String->sr);
PhDeleteStringBuilder(&stringBuilder);
}
}
}
break;
}
}
break;
case WM_SIZE:
{
PPH_LAYOUT_MANAGER layoutManager;
layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager");
PhLayoutManagerLayout(layoutManager);
}
break;
case WM_SIZING:
{
PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom);
}
break;
}
return FALSE;
}
static VOID PhpFreeThreadStackItem(
_In_ PTHREAD_STACK_ITEM StackItem
)
{
PhSwapReference(&StackItem->Symbol, NULL);
PhFree(StackItem);
}
static NTSTATUS PhpRefreshThreadStack(
_In_ HWND hwnd,
_In_ PTHREAD_STACK_CONTEXT ThreadStackContext
)
{
ULONG i;
ThreadStackContext->StopWalk = FALSE;
PhSwapReference2(&ThreadStackContext->StatusMessage, PhCreateString(L"Loading stack..."));
DialogBoxParam(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_PROGRESS),
hwnd,
PhpThreadStackProgressDlgProc,
(LPARAM)ThreadStackContext
);
if (!ThreadStackContext->StopWalk && NT_SUCCESS(ThreadStackContext->WalkStatus))
{
for (i = 0; i < ThreadStackContext->List->Count; i++)
PhpFreeThreadStackItem(ThreadStackContext->List->Items[i]);
PhDereferenceObject(ThreadStackContext->List);
ThreadStackContext->List = ThreadStackContext->NewList;
ThreadStackContext->NewList = PhCreateList(10);
ListView_DeleteAllItems(ThreadStackContext->ListViewHandle);
SendMessage(ThreadStackContext->ListViewHandle, WM_SETREDRAW, FALSE, 0);
for (i = 0; i < ThreadStackContext->List->Count; i++)
{
PTHREAD_STACK_ITEM item = ThreadStackContext->List->Items[i];
INT lvItemIndex;
WCHAR integerString[PH_INT32_STR_LEN_1];
PhPrintUInt32(integerString, item->Index);
lvItemIndex = PhAddListViewItem(ThreadStackContext->ListViewHandle, MAXINT, integerString, item);
PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 1, PhGetStringOrDefault(item->Symbol, L"???"));
}
SendMessage(ThreadStackContext->ListViewHandle, WM_SETREDRAW, TRUE, 0);
InvalidateRect(ThreadStackContext->ListViewHandle, NULL, FALSE);
}
else
{
for (i = 0; i < ThreadStackContext->NewList->Count; i++)
PhpFreeThreadStackItem(ThreadStackContext->NewList->Items[i]);
PhClearList(ThreadStackContext->NewList);
}
if (ThreadStackContext->StopWalk)
return STATUS_ABANDONED;
return ThreadStackContext->WalkStatus;
}
static BOOLEAN NTAPI PhpWalkThreadStackCallback(
_In_ PPH_THREAD_STACK_FRAME StackFrame,
_In_opt_ PVOID Context
)
{
PTHREAD_STACK_CONTEXT threadStackContext = (PTHREAD_STACK_CONTEXT)Context;
PPH_STRING symbol;
PTHREAD_STACK_ITEM item;
if (threadStackContext->StopWalk)
return FALSE;
PhAcquireQueuedLockExclusive(&threadStackContext->StatusLock);
PhSwapReference2(&threadStackContext->StatusMessage,
PhFormatString(L"Processing frame %u...", threadStackContext->NewList->Count));
PhReleaseQueuedLockExclusive(&threadStackContext->StatusLock);
PostMessage(threadStackContext->ProgressWindowHandle, WM_PH_STATUS_UPDATE, 0, 0);
symbol = PhGetSymbolFromAddress(
threadStackContext->SymbolProvider,
(ULONG64)StackFrame->PcAddress,
NULL,
NULL,
NULL,
NULL
);
item = PhAllocate(sizeof(THREAD_STACK_ITEM));
item->StackFrame = *StackFrame;
item->Index = threadStackContext->NewList->Count;
if (PhPluginsEnabled)
{
PH_PLUGIN_THREAD_STACK_CONTROL control;
control.Type = PluginThreadStackResolveSymbol;
control.UniqueKey = threadStackContext;
control.u.ResolveSymbol.StackFrame = StackFrame;
control.u.ResolveSymbol.Symbol = symbol;
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control);
symbol = control.u.ResolveSymbol.Symbol;
}
item->Symbol = symbol;
PhAddItemList(threadStackContext->NewList, item);
return TRUE;
}
static NTSTATUS PhpRefreshThreadStackThreadStart(
_In_ PVOID Parameter
)
{
NTSTATUS status;
PTHREAD_STACK_CONTEXT threadStackContext = Parameter;
CLIENT_ID clientId;
BOOLEAN defaultWalk;
clientId.UniqueProcess = threadStackContext->ProcessId;
clientId.UniqueThread = threadStackContext->ThreadId;
defaultWalk = TRUE;
if (threadStackContext->CustomWalk)
{
PH_PLUGIN_THREAD_STACK_CONTROL control;
control.Type = PluginThreadStackWalkStack;
control.UniqueKey = threadStackContext;
control.u.WalkStack.Status = STATUS_UNSUCCESSFUL;
control.u.WalkStack.ThreadHandle = threadStackContext->ThreadHandle;
control.u.WalkStack.ProcessHandle = threadStackContext->SymbolProvider->ProcessHandle;
control.u.WalkStack.ClientId = &clientId;
control.u.WalkStack.Flags = PH_WALK_I386_STACK | PH_WALK_AMD64_STACK | PH_WALK_KERNEL_STACK;
control.u.WalkStack.Callback = PhpWalkThreadStackCallback;
control.u.WalkStack.CallbackContext = threadStackContext;
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control);
status = control.u.WalkStack.Status;
if (NT_SUCCESS(status))
defaultWalk = FALSE;
}
if (defaultWalk)
{
status = PhWalkThreadStack(
threadStackContext->ThreadHandle,
threadStackContext->SymbolProvider->ProcessHandle,
&clientId,
PH_WALK_I386_STACK | PH_WALK_AMD64_STACK | PH_WALK_KERNEL_STACK,
PhpWalkThreadStackCallback,
threadStackContext
);
}
if (threadStackContext->NewList->Count != 0)
status = STATUS_SUCCESS;
threadStackContext->WalkStatus = status;
PostMessage(threadStackContext->ProgressWindowHandle, WM_PH_COMPLETED, 0, 0);
return STATUS_SUCCESS;
}
static INT_PTR CALLBACK PhpThreadStackProgressDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
PTHREAD_STACK_CONTEXT threadStackContext;
HANDLE threadHandle;
threadStackContext = (PTHREAD_STACK_CONTEXT)lParam;
SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)threadStackContext);
threadStackContext->ProgressWindowHandle = hwndDlg;
if (threadHandle = PhCreateThread(0, PhpRefreshThreadStackThreadStart, threadStackContext))
{
NtClose(threadHandle);
}
else
{
threadStackContext->WalkStatus = STATUS_UNSUCCESSFUL;
EndDialog(hwndDlg, IDOK);
break;
}
PhCenterWindow(hwndDlg, GetParent(hwndDlg));
PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_PROGRESS), PBS_MARQUEE, PBS_MARQUEE);
SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75);
SetWindowText(hwndDlg, L"Loading stack...");
}
break;
case WM_DESTROY:
{
RemoveProp(hwndDlg, PhMakeContextAtom());
}
break;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDCANCEL:
{
PTHREAD_STACK_CONTEXT threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE);
threadStackContext->StopWalk = TRUE;
}
break;
}
}
break;
case WM_PH_COMPLETED:
{
EndDialog(hwndDlg, IDOK);
}
break;
case WM_PH_STATUS_UPDATE:
{
PTHREAD_STACK_CONTEXT threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
PPH_STRING message;
PhAcquireQueuedLockExclusive(&threadStackContext->StatusLock);
message = threadStackContext->StatusMessage;
PhReferenceObject(message);
PhReleaseQueuedLockExclusive(&threadStackContext->StatusLock);
SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, message->Buffer);
PhDereferenceObject(message);
}
break;
}
return 0;
}