Files
mirror-processhacker/2.x/trunk/plugins-extra/WaitChainPlugin/main.c
T
dmex b5a24c3762 PluginExtras: sync plugin settings
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@5612 21ef857c-d57f-4fe0-8362-d861dc6d29cd
2014-02-07 09:42:46 +00:00

524 lines
18 KiB
C

/*
* Process Hacker Extra Plugins -
* Wait Chain Traversal (WCT) Plugin
*
* Copyright (C) 2013 dmex
*
* 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 "main.h"
// Wait Chain Traversal Documentation:
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms681622.aspx
static PPH_PLUGIN PluginInstance;
static PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration;
static PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration;
static PH_CALLBACK_REGISTRATION ThreadMenuInitializingCallbackRegistration;
static BOOLEAN WaitChainRegisterCallbacks(
__inout PWCT_CONTEXT Context
)
{
PCOGETCALLSTATE coGetCallStateCallback = NULL;
PCOGETACTIVATIONSTATE coGetActivationStateCallback = NULL;
Context->Ole32ModuleHandle = LoadLibrary(L"ole32.dll");
if (!Context->Ole32ModuleHandle)
return FALSE;
coGetCallStateCallback = (PCOGETCALLSTATE)GetProcAddress(Context->Ole32ModuleHandle, "CoGetCallState");
if (!coGetCallStateCallback)
return FALSE;
coGetActivationStateCallback = (PCOGETACTIVATIONSTATE)GetProcAddress(Context->Ole32ModuleHandle, "CoGetActivationState");
if (!coGetActivationStateCallback)
return FALSE;
RegisterWaitChainCOMCallback(coGetCallStateCallback, coGetActivationStateCallback);
return TRUE;
}
static VOID WaitChainCheckThread(
__inout PWCT_CONTEXT Context,
__in HANDLE ThreadId
)
{
BOOL isDeadLocked = FALSE;
ULONG nodeInfoLength = WCT_MAX_NODE_COUNT;
WAITCHAIN_NODE_INFO nodeInfoArray[WCT_MAX_NODE_COUNT];
PWCT_ROOT_NODE rootNode = NULL;
memset(nodeInfoArray, 0, sizeof(nodeInfoArray));
// Retrieve the thread wait chain.
if (!GetThreadWaitChain(
Context->WctSessionHandle,
0,
WCT_GETINFO_ALL_FLAGS,
HandleToUlong(ThreadId),
&nodeInfoLength,
nodeInfoArray,
&isDeadLocked
))
{
return;
}
// Check if the wait chain is too big for the array we passed in.
if (nodeInfoLength > WCT_MAX_NODE_COUNT)
nodeInfoLength = WCT_MAX_NODE_COUNT;
TreeNew_SetRedraw(Context->TreeNewHandle, FALSE);
TreeNew_NodesStructured(Context->TreeNewHandle);
for (ULONG i = 0; i < nodeInfoLength; i++)
{
WAITCHAIN_NODE_INFO wctNode = nodeInfoArray[i];
if (wctNode.ObjectType == WctThreadType)
{
rootNode = WeAddWindowNode(&Context->TreeContext);
rootNode->WctInfo = wctNode;
rootNode->ThreadId = UlongToHandle(wctNode.ThreadObject.ThreadId);
rootNode->ThreadIdString = PhFormatString(L"%u", wctNode.ThreadObject.ThreadId);
rootNode->ProcessIdString = PhFormatString(L"%u", wctNode.ThreadObject.ProcessId);
rootNode->WaitTimeString = PhFormatString(L"%u", wctNode.ThreadObject.WaitTime);
rootNode->ContextSwitchesString = PhFormatString(L"%u", wctNode.ThreadObject.ContextSwitches);
rootNode->TimeoutString = PhFormatString(L"%I64d", wctNode.LockObject.Timeout.QuadPart);
if (wctNode.LockObject.ObjectName[0] != '\0')
{
// -- ProcessID --
//wctNode.LockObject.ObjectName[0]
// -- ThreadID --
//wctNode.LockObject.ObjectName[2]
// -- Unknown --
//wctNode.LockObject.ObjectName[4]
if (PhIsDigitCharacter(wctNode.LockObject.ObjectName[0]))
{
rootNode->ObjectNameString = PhFormatString(L"%s", wctNode.LockObject.ObjectName);
}
//else
//{
// rootNode->ObjectNameString = PhFormatString(L"[%u, %u]",
// wctNode.LockObject.ObjectName[0],
// wctNode.LockObject.ObjectName[2]
// );
//}
}
rootNode->Node.Expanded = TRUE;
rootNode->HasChildren = TRUE;
// This is a root node.
PhAddItemList(Context->TreeContext.NodeRootList, rootNode);
}
else
{
WctAddChildWindowNode(&Context->TreeContext, rootNode, wctNode, isDeadLocked);
}
}
TreeNew_SetRedraw(Context->TreeNewHandle, TRUE);
TreeNew_NodesStructured(Context->TreeNewHandle);
}
static NTSTATUS WaitChainCallbackThread(
__in PVOID Parameter
)
{
NTSTATUS status = STATUS_SUCCESS;
PWCT_CONTEXT context = (PWCT_CONTEXT)Parameter;
if (!WaitChainRegisterCallbacks(context))
return NTSTATUS_FROM_WIN32(GetLastError());
// Synchronous WCT session
context->WctSessionHandle = OpenThreadWaitChainSession(0, NULL);
if (context->WctSessionHandle == NULL)
return NTSTATUS_FROM_WIN32(GetLastError());
if (context->IsProcessItem)
{
PVOID processes = NULL;
PSYSTEM_PROCESS_INFORMATION process = NULL;
if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
return status;
process = PH_FIRST_PROCESS(processes);
do
{
if (process->UniqueProcessId == context->ProcessItem->ProcessId)
{
for (ULONG i = 0; i < process->NumberOfThreads; i++)
{
WaitChainCheckThread(context, process->Threads[i].ClientId.UniqueThread);
}
}
} while (process = PH_NEXT_PROCESS(process));
}
else
{
WaitChainCheckThread(context, context->ThreadItem->ThreadId);
}
if (context->WctSessionHandle)
{
CloseThreadWaitChainSession(context->WctSessionHandle);
}
if (context->Ole32ModuleHandle)
{
FreeLibrary(context->Ole32ModuleHandle);
}
return status;
}
static INT_PTR CALLBACK WaitChainDlgProc(
__in HWND hwndDlg,
__in UINT uMsg,
__in WPARAM wParam,
__in LPARAM lParam
)
{
PWCT_CONTEXT context = NULL;
if (uMsg == WM_INITDIALOG)
{
context = (PWCT_CONTEXT)lParam;
SetProp(hwndDlg, L"Context", (HANDLE)context);
}
else
{
context = (PWCT_CONTEXT)GetProp(hwndDlg, L"Context");
if (uMsg == WM_DESTROY)
{
PhUnregisterDialog(hwndDlg);
PhSaveWindowPlacementToSetting(SETTING_NAME_WINDOW_POSITION, SETTING_NAME_WINDOW_SIZE, hwndDlg);
PhDeleteLayoutManager(&context->LayoutManager);
WtcDeleteWindowTree(&context->TreeContext);
RemoveProp(hwndDlg, L"Context");
PhFree(context);
}
}
if (context == NULL)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
HANDLE threadHandle = INVALID_HANDLE_VALUE;
context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_CUSTOM1);
PhRegisterDialog(hwndDlg);
WtcInitializeWindowTree(hwndDlg, context->TreeNewHandle, &context->TreeContext);
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
PhAddLayoutItem(&context->LayoutManager, context->TreeNewHandle, NULL, PH_ANCHOR_ALL);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT);
PhLoadWindowPlacementFromSetting(SETTING_NAME_WINDOW_POSITION, SETTING_NAME_WINDOW_SIZE, hwndDlg);
if (threadHandle = PhCreateThread(0, (PUSER_THREAD_START_ROUTINE)WaitChainCallbackThread, (PVOID)context))
NtClose(threadHandle);
}
break;
case WM_SIZE:
PhLayoutManagerLayout(&context->LayoutManager);
break;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDCANCEL:
case IDOK:
EndDialog(hwndDlg, IDOK);
break;
case ID_WCTSHOWCONTEXTMENU:
{
POINT point;
HMENU menu;
HMENU subMenu;
PWCT_ROOT_NODE selectedNode = NULL;
PPH_PROCESS_NODE processNode = NULL;
point.x = (SHORT)LOWORD(lParam);
point.y = (SHORT)HIWORD(lParam);
if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
{
menu = LoadMenu((HINSTANCE)PluginInstance->DllBase, MAKEINTRESOURCE(IDR_MAIN_MENU));
subMenu = GetSubMenu(menu, 0);
SetMenuDefaultItem(subMenu, ID_MENU_PROPERTIES, FALSE);
if (selectedNode->ThreadId > 0)
{
PhEnableMenuItem(subMenu, ID_MENU_GOTOTHREAD, TRUE);
PhEnableMenuItem(subMenu, ID_MENU_GOTOPROCESS, TRUE);
}
else
{
PhEnableMenuItem(subMenu, ID_MENU_GOTOTHREAD, FALSE);
PhEnableMenuItem(subMenu, ID_MENU_GOTOPROCESS, FALSE);
}
TrackPopupMenu(
subMenu,
TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON,
point.x,
point.y,
0,
hwndDlg,
NULL
);
DestroyMenu(menu);
}
}
break;
case ID_MENU_GOTOPROCESS:
{
PWCT_ROOT_NODE selectedNode = NULL;
PPH_PROCESS_NODE processNode = NULL;
if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
{
ULONG processID = _wtol(selectedNode->ProcessIdString->Buffer);
if (processNode = PhFindProcessNode(UlongToHandle(processID)))
{
ProcessHacker_SelectTabPage(PhMainWndHandle, 0);
PhSelectAndEnsureVisibleProcessNode(processNode);
}
}
}
break;
case ID_MENU_GOTOTHREAD:
{
PWCT_ROOT_NODE selectedNode = NULL;
PPH_PROCESS_ITEM processItem = NULL;
PPH_PROCESS_PROPCONTEXT propContext = NULL;
if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
{
ULONG processID = _wtol(selectedNode->ProcessIdString->Buffer);
if (processItem = PhReferenceProcessItem(UlongToHandle(processID)))
{
if (propContext = PhCreateProcessPropContext(NULL, processItem))
{
if (selectedNode->ThreadId)
{
PhSetSelectThreadIdProcessPropContext(propContext, selectedNode->ThreadId);
}
PhShowProcessProperties(propContext);
PhDereferenceObject(propContext);
}
PhDereferenceObject(processItem);
}
else
{
PhShowError(hwndDlg, L"The process does not exist.");
}
}
}
break;
case ID_MENU_COPY:
{
PPH_STRING text;
text = PhGetTreeNewText(context->TreeNewHandle, 0);
PhSetClipboardStringEx(hwndDlg, text->Buffer, text->Length);
PhDereferenceObject(text);
}
break;
}
}
break;
}
return FALSE;
}
static VOID NTAPI MenuItemCallback(
__in_opt PVOID Parameter,
__in_opt PVOID Context
)
{
PPH_PLUGIN_MENU_ITEM menuItem = (PPH_PLUGIN_MENU_ITEM)Parameter;
switch (menuItem->Id)
{
case IDD_WCT_MENUITEM:
{
DialogBoxParam(
(HINSTANCE)PluginInstance->DllBase,
MAKEINTRESOURCE(IDD_WCT_DIALOG),
NULL,
WaitChainDlgProc,
(LPARAM)menuItem->Context
);
}
break;
}
}
static VOID NTAPI ProcessMenuInitializingCallback(
__in_opt PVOID Parameter,
__in_opt PVOID Context
)
{
ULONG insertIndex = 0;
PWCT_CONTEXT context = NULL;
PPH_PLUGIN_MENU_INFORMATION menuInfo = NULL;
PPH_PROCESS_ITEM processItem = NULL;
PPH_EMENU_ITEM menuItem = NULL;
PPH_EMENU_ITEM miscMenuItem = NULL;
PPH_EMENU_ITEM wsMenuItem = NULL;
menuInfo = (PPH_PLUGIN_MENU_INFORMATION)Parameter;
if (menuInfo->u.Process.NumberOfProcesses == 1)
processItem = menuInfo->u.Process.Processes[0];
else
{
processItem = NULL;
}
if (processItem == NULL)
return;
context = (PWCT_CONTEXT)PhAllocate(sizeof(WCT_CONTEXT));
memset(context, 0, sizeof(WCT_CONTEXT));
context->IsProcessItem = TRUE;
context->ProcessItem = processItem;
miscMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Miscellaneous", 0);
if (miscMenuItem)
{
menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, IDD_WCT_MENUITEM, L"Wait Chain Traversal", context);
PhInsertEMenuItem(miscMenuItem, menuItem, -1);
// Disable menu if current process selected.
if (processItem == NULL || processItem->ProcessId == NtCurrentProcessId())
menuItem->Flags |= PH_EMENU_DISABLED;
}
}
static VOID NTAPI ThreadMenuInitializingCallback(
__in_opt PVOID Parameter,
__in_opt PVOID Context
)
{
PWCT_CONTEXT context = NULL;
PPH_PLUGIN_MENU_INFORMATION menuInfo = NULL;
PPH_THREAD_ITEM threadItem = NULL;
PPH_EMENU_ITEM menuItem = NULL;
PPH_EMENU_ITEM miscMenuItem = NULL;
menuInfo = (PPH_PLUGIN_MENU_INFORMATION)Parameter;
if (menuInfo->u.Thread.NumberOfThreads == 1)
threadItem = menuInfo->u.Thread.Threads[0];
else
threadItem = NULL;
context = (PWCT_CONTEXT)PhAllocate(sizeof(WCT_CONTEXT));
memset(context, 0, sizeof(WCT_CONTEXT));
context->IsProcessItem = FALSE;
context->ThreadItem = threadItem;
miscMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Analyze", 0);
menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, IDD_WCT_MENUITEM, L"Wait Chain Traversal", context);
PhInsertEMenuItem(miscMenuItem, menuItem, -1);
// Disable menu if current process selected.
if (threadItem == NULL || menuInfo->u.Thread.ProcessId == NtCurrentProcessId())
menuItem->Flags |= PH_EMENU_DISABLED;
}
LOGICAL DllMain(
__in HINSTANCE Instance,
__in ULONG Reason,
__reserved PVOID Reserved
)
{
switch (Reason)
{
case DLL_PROCESS_ATTACH:
{
PPH_PLUGIN_INFORMATION info;
PluginInstance = PhRegisterPlugin(SETTING_PREFIX, Instance, &info);
if (!PluginInstance)
return FALSE;
info->DisplayName = L"Wait Chain Traversal";
info->Author = L"dmex";
info->Description = L"Plugin for viewing the current thread or process Wait Chain";
info->HasOptions = FALSE;
PhRegisterCallback(
PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem),
MenuItemCallback,
NULL,
&PluginMenuItemCallbackRegistration
);
PhRegisterCallback(
PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing),
ProcessMenuInitializingCallback,
NULL,
&ProcessMenuInitializingCallbackRegistration
);
PhRegisterCallback(
PhGetGeneralCallback(GeneralCallbackThreadMenuInitializing),
ThreadMenuInitializingCallback,
NULL,
&ThreadMenuInitializingCallbackRegistration
);
{
PH_SETTING_CREATE settings[] =
{
{ StringSettingType, SETTING_NAME_TREE_LIST_COLUMNS, L"" },
{ IntegerPairSettingType, SETTING_NAME_WINDOW_POSITION, L"100,100" },
{ IntegerPairSettingType, SETTING_NAME_WINDOW_SIZE, L"690,540" }
};
PhAddSettings(settings, _countof(settings));
}
}
break;
}
return TRUE;
}