mirror of
https://github.com/zodiacon/PoolMonX
synced 2026-06-08 18:38:49 +00:00
510 lines
15 KiB
C++
510 lines
15 KiB
C++
// PoolListCtrl.cpp : implementation file
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "PoolMonEx.h"
|
|
#include "PoolListCtrl.h"
|
|
|
|
#pragma comment(lib, "ntdll")
|
|
|
|
// CPoolListCtrl
|
|
|
|
IMPLEMENT_DYNAMIC(CPoolListCtrl, CMFCListCtrl)
|
|
|
|
CPoolListCtrl::CPoolListCtrl()
|
|
{
|
|
m_pPoolInfo = (SYSTEM_POOLTAG_INFORMATION*)::malloc(m_PoolInfoSize);
|
|
}
|
|
|
|
CPoolListCtrl::~CPoolListCtrl()
|
|
{
|
|
::free(m_pPoolInfo);
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CPoolListCtrl, CMFCListCtrl)
|
|
ON_WM_CREATE()
|
|
ON_NOTIFY(HDN_ITEMCLICKA, 0, &CPoolListCtrl::OnHdnItemclick)
|
|
ON_NOTIFY(HDN_ITEMCLICKW, 0, &CPoolListCtrl::OnHdnItemclick)
|
|
ON_WM_TIMER()
|
|
ON_COMMAND(ID_VIEW_REFRESH, &CPoolListCtrl::OnViewRefresh)
|
|
ON_COMMAND_RANGE(ID_REFRESHINTERVAL_5SECONDS, ID_REFRESHINTERVAL_5SECONDS + 3, OnIntervalChange)
|
|
ON_UPDATE_COMMAND_UI_RANGE(ID_REFRESHINTERVAL_5SECONDS, ID_REFRESHINTERVAL_5SECONDS + 3, OnIntervalChangeUpdateUI)
|
|
ON_COMMAND(ID_VIEW_DYNAMICSORT, &CPoolListCtrl::OnViewDynamicsort)
|
|
ON_UPDATE_COMMAND_UI(ID_VIEW_DYNAMICSORT, &CPoolListCtrl::OnUpdateViewDynamicsort)
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
int CPoolListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
|
|
{
|
|
if (CMFCListCtrl::OnCreate(lpCreateStruct) == -1)
|
|
return -1;
|
|
|
|
m_Images.Create(16, 16, ILC_COLOR32, 4, 2);
|
|
m_Images.Add(AfxGetApp()->LoadIconW(IDI_PAGED));
|
|
m_Images.Add(AfxGetApp()->LoadIconW(IDI_NONPAGED));
|
|
|
|
SetImageList(&m_Images, LVSIL_SMALL);
|
|
|
|
//EnableMarkSortedColumn(FALSE);
|
|
|
|
CFont font;
|
|
font.CreatePointFont(100, L"Consolas");
|
|
|
|
LOGFONT lf;
|
|
GetFont()->GetLogFont(&lf);
|
|
|
|
SetFont(&font);
|
|
font.Detach();
|
|
|
|
font.DeleteObject();
|
|
font.CreateFontIndirectW(&lf);
|
|
GetHeaderCtrl().SetFont(&font);
|
|
font.Detach();
|
|
|
|
InsertColumn(ColumnType::TagName, L"Tag", LVCFMT_CENTER, 80);
|
|
InsertColumn(ColumnType::PoolType, L"Type", LVCFMT_CENTER, 120);
|
|
InsertColumn(ColumnType::Allocs, L"Allocs", LVCFMT_RIGHT, 100);
|
|
InsertColumn(ColumnType::Frees, L"Frees", LVCFMT_RIGHT, 100);
|
|
InsertColumn(ColumnType::Diff, L"Diff", LVCFMT_RIGHT, 80);
|
|
InsertColumn(ColumnType::Usage, L"Bytes", LVCFMT_RIGHT, 100);
|
|
InsertColumn(ColumnType::UsageKB, L"KBytes", LVCFMT_RIGHT, 100);
|
|
InsertColumn(ColumnType::PerAlloc, L"B / Alloc", LVCFMT_RIGHT, 80);
|
|
InsertColumn(ColumnType::SourceName, L"Source", LVCFMT_LEFT, 150);
|
|
InsertColumn(ColumnType::SourceDescription, L"Source Description", LVCFMT_LEFT, 350);
|
|
|
|
LoadPoolTags();
|
|
|
|
BuildPoolData();
|
|
|
|
SetTimer(1, m_Interval, nullptr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CPoolListCtrl::LoadPoolTags() {
|
|
using namespace std;
|
|
|
|
auto hModule = AfxGetResourceHandle();
|
|
auto hResource = ::FindResource(hModule, MAKEINTRESOURCE(IDR_POOLTAG), L"TXT");
|
|
ASSERT(hResource);
|
|
|
|
auto hGlobal = ::LoadResource(hModule, hResource);
|
|
ASSERT(hGlobal);
|
|
|
|
auto data = static_cast<const char*>(::LockResource(hGlobal));
|
|
auto next = data;
|
|
for (; next; data = next + 1) {
|
|
next = strchr(data, '\n');
|
|
|
|
if (strncmp(data, "//", 2) == 0 || _strnicmp(data, "rem", 3) == 0
|
|
|| strncmp(data, "\r\n", 2) == 0)
|
|
continue;
|
|
|
|
// read the tag
|
|
string tag(data, data + 4);
|
|
|
|
// locate the first dash
|
|
auto dash1 = strchr(data, '-');
|
|
if (dash1 == nullptr)
|
|
continue;
|
|
|
|
// locate second dash
|
|
auto dash2 = strchr(dash1 + 1, '-');
|
|
if (dash2 == nullptr)
|
|
continue;
|
|
|
|
if (dash2 > next) {
|
|
dash2 = dash1;
|
|
dash1 = nullptr;
|
|
}
|
|
|
|
CStringA trimmedTag(tag.c_str());
|
|
//trimmedTag.Trim();
|
|
|
|
CStringW trimmedDriverName(L"");
|
|
if (dash1) {
|
|
string driverName(dash1 + 1, dash2);
|
|
trimmedDriverName = driverName.c_str();
|
|
trimmedDriverName.Trim();
|
|
}
|
|
|
|
string driverDesc(dash2 + 1, next - 1);
|
|
CStringW trimmedDriverDesc(driverDesc.c_str());
|
|
trimmedDriverDesc.Trim();
|
|
|
|
m_PoolTagMap.insert(make_pair(trimmedTag, make_pair(wstring(trimmedDriverName), wstring(trimmedDriverDesc))));
|
|
}
|
|
}
|
|
|
|
void CPoolListCtrl::BuildPoolData()
|
|
{
|
|
CWaitCursor wait;
|
|
|
|
ULONG size;
|
|
auto status = ::NtQuerySystemInformation(SystemInformationClass::SystemPoolTagInformation, m_pPoolInfo, m_PoolInfoSize, &size);
|
|
if (status == 0) {
|
|
char tag[5] = { 0 };
|
|
int count = m_pPoolInfo->Count;
|
|
m_Tags.clear();
|
|
|
|
for (auto i = 0; i < count; i++) {
|
|
const auto& item = m_pPoolInfo->TagInfo[i];
|
|
::CopyMemory(tag, &item.Tag, 4);
|
|
|
|
if (item.NonPagedAllocs > 0) {
|
|
AddItem(i, item, tag, true);
|
|
}
|
|
if (item.PagedAllocs > 0) {
|
|
AddItem(i, item, tag, false);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPoolListCtrl::UpdateItem(int n, PoolTagInfo& info, bool nonpaged)
|
|
{
|
|
const auto& item = info.Tag;
|
|
|
|
SetItemText(n, ColumnType::PoolType, nonpaged ? L"Non Paged" : L"Paged");
|
|
CString value;
|
|
value.Format(L"%u", nonpaged ? item.NonPagedAllocs : item.PagedAllocs);
|
|
SetItemText(n, ColumnType::Allocs, value);
|
|
|
|
value.Format(L"%u", nonpaged ? item.NonPagedFrees : item.PagedFrees);
|
|
SetItemText(n, ColumnType::Frees, value);
|
|
|
|
int diff;
|
|
value.Format(L"%u", nonpaged ? diff = item.NonPagedAllocs - item.NonPagedFrees : diff = item.PagedAllocs - item.PagedFrees);
|
|
SetItemText(n, ColumnType::Diff, value);
|
|
|
|
auto used = nonpaged ? item.NonPagedUsed : item.PagedUsed;
|
|
value.Format(L"%u", used);
|
|
SetItemText(n, ColumnType::Usage, value);
|
|
|
|
value.Format(L"%u KB", (used + 512) >> 10);
|
|
SetItemText(n, ColumnType::UsageKB, value);
|
|
|
|
value.Format(L"%u", diff == 0 ? 0 : used / diff);
|
|
SetItemText(n, ColumnType::PerAlloc, value);
|
|
|
|
auto driver = m_PoolTagMap.find(std::string((const char*)&item.Tag, (const char*)&item.Tag + 4));
|
|
if (driver != m_PoolTagMap.end()) {
|
|
SetItemText(n, ColumnType::SourceName, info.SourceName = driver->second.first.c_str());
|
|
SetItemText(n, ColumnType::SourceDescription, info.SourceDesc = driver->second.second.c_str());
|
|
}
|
|
}
|
|
|
|
void CPoolListCtrl::AddColor(int row, int col, COLORREF color)
|
|
{
|
|
m_CellColors.insert(std::make_pair(std::make_pair(row, col), color));
|
|
}
|
|
|
|
bool CPoolListCtrl::ProcessChanges(int row, const SYSTEM_POOLTAG& newTag, const SYSTEM_POOLTAG& oldTag, bool nonpaged)
|
|
{
|
|
if (::memcmp(&newTag, &oldTag, sizeof(newTag)) == 0)
|
|
return false;
|
|
|
|
const COLORREF PlusColor = Green, MinusColor = Red;
|
|
|
|
if (nonpaged) {
|
|
if (newTag.NonPagedAllocs > oldTag.NonPagedAllocs)
|
|
AddColor(row, ColumnType::Allocs, PlusColor);
|
|
if (newTag.NonPagedFrees > oldTag.NonPagedFrees)
|
|
AddColor(row, ColumnType::Frees, PlusColor);
|
|
if (newTag.NonPagedUsed > oldTag.NonPagedUsed) {
|
|
AddColor(row, ColumnType::Usage, PlusColor);
|
|
AddColor(row, ColumnType::UsageKB, PlusColor);
|
|
}
|
|
else if (newTag.NonPagedUsed < oldTag.NonPagedUsed) {
|
|
AddColor(row, ColumnType::Usage, MinusColor);
|
|
AddColor(row, ColumnType::UsageKB, MinusColor);
|
|
}
|
|
if (newTag.NonPagedAllocs - newTag.NonPagedFrees > oldTag.NonPagedAllocs - oldTag.NonPagedFrees)
|
|
AddColor(row, ColumnType::Diff, PlusColor);
|
|
else if (newTag.NonPagedAllocs - newTag.NonPagedFrees < oldTag.NonPagedAllocs - oldTag.NonPagedFrees)
|
|
AddColor(row, ColumnType::Diff, MinusColor);
|
|
}
|
|
else {
|
|
if (newTag.PagedAllocs > oldTag.PagedAllocs)
|
|
AddColor(row, ColumnType::Allocs, PlusColor);
|
|
if (newTag.PagedFrees > oldTag.PagedFrees)
|
|
AddColor(row, ColumnType::Frees, PlusColor);
|
|
if (newTag.PagedUsed > oldTag.PagedUsed) {
|
|
AddColor(row, ColumnType::Usage, PlusColor);
|
|
AddColor(row, ColumnType::UsageKB, PlusColor);
|
|
}
|
|
else if (newTag.PagedUsed < oldTag.PagedUsed) {
|
|
AddColor(row, ColumnType::Usage, MinusColor);
|
|
AddColor(row, ColumnType::UsageKB, MinusColor);
|
|
}
|
|
if (newTag.PagedAllocs - newTag.PagedFrees > oldTag.PagedAllocs - oldTag.PagedFrees)
|
|
AddColor(row, ColumnType::Diff, PlusColor);
|
|
else if (newTag.PagedAllocs - newTag.PagedFrees < oldTag.PagedAllocs - oldTag.PagedFrees)
|
|
AddColor(row, ColumnType::Diff, MinusColor);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int CPoolListCtrl::AddItem(int index, const SYSTEM_POOLTAG& item, PCSTR tag, bool nonpaged)
|
|
{
|
|
PoolTagInfo info = { item };
|
|
|
|
int n = InsertItem(index, CString(tag), nonpaged ? 1 : 0);
|
|
|
|
UpdateItem(n, info, nonpaged);
|
|
|
|
ULONG key = nonpaged ? (item.TagUlong | NonPagedBit) : item.TagUlong;
|
|
|
|
m_Tags.insert(std::make_pair(key, info));
|
|
|
|
SetItemData(n, key);
|
|
|
|
return n;
|
|
}
|
|
|
|
static int CompareStrings(PCWSTR s1, PCWSTR s2) {
|
|
if (s1 == s2)
|
|
return 0;
|
|
|
|
if (s1 == nullptr)
|
|
return -1;
|
|
|
|
if (s2 == nullptr)
|
|
return 1;
|
|
|
|
return ::wcscmp(s1, s2);
|
|
}
|
|
|
|
int CPoolListCtrl::OnCompareItems(LPARAM lParam1, LPARAM lParam2, int iColumn)
|
|
{
|
|
ASSERT(m_Tags.find((ULONG)lParam1) != m_Tags.end());
|
|
ASSERT(m_Tags.find((ULONG)lParam2) != m_Tags.end());
|
|
|
|
const auto& info1 = m_Tags[(ULONG)lParam1];
|
|
const auto& info2 = m_Tags[(ULONG)lParam2];
|
|
|
|
const auto& item1 = info1.Tag;
|
|
const auto& item2 = info2.Tag;
|
|
|
|
auto nonPaged1 = ((ULONG)lParam1) & NonPagedBit;
|
|
auto nonPaged2 = ((ULONG)lParam2) & NonPagedBit;
|
|
|
|
long long order;
|
|
|
|
switch (iColumn) {
|
|
case ColumnType::TagName:
|
|
order = (long long)ReverseTag(item1.TagUlong) - ReverseTag(item2.TagUlong);
|
|
break;
|
|
|
|
case ColumnType::PoolType:
|
|
if (nonPaged1 == nonPaged2)
|
|
return 0;
|
|
|
|
order = nonPaged1 ? -1 : 1;
|
|
break;
|
|
|
|
case ColumnType::Allocs:
|
|
order = (long long)(nonPaged1 ? item1.NonPagedAllocs : item1.PagedAllocs) - (nonPaged2 ? item2.NonPagedAllocs : item2.PagedAllocs);
|
|
break;
|
|
|
|
case ColumnType::Frees:
|
|
order = (long long)(nonPaged1 ? item1.NonPagedFrees : item1.PagedFrees) - (nonPaged2 ? item2.NonPagedFrees : item2.PagedFrees);
|
|
break;
|
|
|
|
case ColumnType::Diff:
|
|
{
|
|
auto diff1 = (nonPaged1 ? item1.NonPagedAllocs : item1.PagedAllocs) - (nonPaged1 ? item1.NonPagedFrees : item1.PagedFrees);
|
|
auto diff2 = (nonPaged2 ? item2.NonPagedAllocs : item2.PagedAllocs) - (nonPaged2 ? item2.NonPagedFrees : item2.PagedFrees);
|
|
order = (long long)(diff1)-diff2;
|
|
break;
|
|
}
|
|
|
|
case ColumnType::Usage:
|
|
case ColumnType::UsageKB:
|
|
order = (long long)(nonPaged1 ? item1.NonPagedUsed : item1.PagedUsed) - (nonPaged2 ? item2.NonPagedUsed : item2.PagedUsed);
|
|
break;
|
|
|
|
case ColumnType::PerAlloc: {
|
|
auto allocs1 = (nonPaged1 ? item1.NonPagedAllocs : item1.PagedAllocs);
|
|
auto allocs2 = (nonPaged2 ? item2.NonPagedAllocs : item2.PagedAllocs);
|
|
auto frees1 = (nonPaged1 ? item1.NonPagedFrees : item1.PagedFrees);
|
|
auto frees2 = (nonPaged2 ? item2.NonPagedFrees : item2.PagedFrees);
|
|
auto used1 = (nonPaged1 ? item1.NonPagedUsed : item1.PagedUsed);
|
|
auto used2 = (nonPaged2 ? item2.NonPagedUsed : item2.PagedUsed);
|
|
|
|
auto diff1 = allocs1 - frees1, diff2 = allocs2 - frees2;
|
|
|
|
order = (long long)(diff1 ? used1 / diff1 : 0) - (diff2 ? used2 / diff2 : 0);
|
|
break;
|
|
}
|
|
|
|
case ColumnType::SourceName:
|
|
return CompareStrings(info1.SourceName, info2.SourceName);
|
|
|
|
case ColumnType::SourceDescription:
|
|
return CompareStrings(info1.SourceDesc, info2.SourceDesc);
|
|
|
|
}
|
|
|
|
if (order == 0)
|
|
return 0;
|
|
|
|
return order > 0 ? 1 : -1;
|
|
}
|
|
|
|
ULONG CPoolListCtrl::ReverseTag(ULONG tagUlong)
|
|
{
|
|
BYTE* tag = (BYTE*)&tagUlong;
|
|
std::swap(tag[0], tag[3]);
|
|
std::swap(tag[1], tag[2]);
|
|
return tagUlong;
|
|
}
|
|
|
|
void CPoolListCtrl::OnHdnItemclick(NMHDR *pNMHDR, LRESULT *pResult)
|
|
{
|
|
auto phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
|
|
|
|
if (m_SortColumn != phdr->iItem) {
|
|
m_SortColumn = phdr->iItem;
|
|
m_Ascending = true;
|
|
}
|
|
else {
|
|
m_Ascending = !m_Ascending;
|
|
}
|
|
|
|
Sort(m_SortColumn, m_Ascending ? TRUE : FALSE);
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
void CPoolListCtrl::RefreshPoolData()
|
|
{
|
|
CWaitCursor wait;
|
|
|
|
ULONG size;
|
|
auto status = ::NtQuerySystemInformation(SystemInformationClass::SystemPoolTagInformation, m_pPoolInfo, m_PoolInfoSize, &size);
|
|
if (status == 0) {
|
|
char tag[5] = { 0 };
|
|
int count = m_pPoolInfo->Count;
|
|
LVFINDINFO lvfi = { LVFI_PARAM };
|
|
|
|
int row;
|
|
CRect rc;
|
|
m_Update = true;
|
|
for (const auto& cell : m_CellColors) {
|
|
row = cell.first.first;
|
|
RedrawItems(row, row);
|
|
}
|
|
m_Update = false;
|
|
|
|
LockWindowUpdate();
|
|
|
|
m_CellColors.clear();
|
|
|
|
for (auto i = 0; i < count; i++) {
|
|
PoolTagInfo info{ };
|
|
info.Tag = m_pPoolInfo->TagInfo[i];
|
|
const auto& item = info.Tag;
|
|
|
|
::CopyMemory(tag, &item.Tag, 4);
|
|
|
|
if (item.NonPagedAllocs > 0) {
|
|
// find the tag (if exists)
|
|
auto it = m_Tags.find(item.TagUlong | NonPagedBit);
|
|
if (it == m_Tags.end())
|
|
AddItem(GetItemCount(), item, tag, true);
|
|
else {
|
|
// find existing item
|
|
lvfi.lParam = item.TagUlong | NonPagedBit;
|
|
int n = FindItem(&lvfi);
|
|
ASSERT(n >= 0);
|
|
if (ProcessChanges(n, item, it->second.Tag, true)) {
|
|
UpdateItem(n, info, true);
|
|
m_Tags[(ULONG)lvfi.lParam] = info;
|
|
}
|
|
}
|
|
}
|
|
if (item.PagedAllocs > 0) {
|
|
auto it = m_Tags.find(item.TagUlong);
|
|
if (it == m_Tags.end())
|
|
AddItem(GetItemCount(), item, tag, false);
|
|
else {
|
|
// find existing item
|
|
lvfi.lParam = item.TagUlong;
|
|
int n = FindItem(&lvfi);
|
|
ASSERT(n >= 0);
|
|
if (ProcessChanges(n, item, it->second.Tag, false)) {
|
|
UpdateItem(n, info, false);
|
|
m_Tags[item.TagUlong] = info;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (m_DynamicSort && m_SortColumn >= 0)
|
|
Sort(m_SortColumn, m_Ascending);
|
|
|
|
UnlockWindowUpdate();
|
|
|
|
}
|
|
}
|
|
|
|
void CPoolListCtrl::Pause(bool pause)
|
|
{
|
|
if (pause)
|
|
KillTimer(1);
|
|
else
|
|
SetTimer(1, m_Interval, nullptr);
|
|
m_Paused = pause;
|
|
}
|
|
|
|
void CPoolListCtrl::OnTimer(UINT_PTR nIDEvent)
|
|
{
|
|
if (nIDEvent == 1) {
|
|
RefreshPoolData();
|
|
TRACE(L"refresh: %d\n", ::GetTickCount());
|
|
}
|
|
}
|
|
|
|
void CPoolListCtrl::OnViewRefresh()
|
|
{
|
|
RefreshPoolData();
|
|
}
|
|
|
|
COLORREF CPoolListCtrl::OnGetCellBkColor(int row, int col)
|
|
{
|
|
if (m_Update)
|
|
return GetBkColor();
|
|
|
|
auto it = m_CellColors.find(std::make_pair(row, col));
|
|
return it == m_CellColors.end() ? GetBkColor() : it->second;
|
|
}
|
|
|
|
void CPoolListCtrl::OnIntervalChange(UINT id)
|
|
{
|
|
m_IntervalIndex = id - ID_REFRESHINTERVAL_5SECONDS;
|
|
|
|
const int intervals[] = { 5000, 10000, 30 * 1000, 60 * 1000 };
|
|
|
|
if (!m_Paused)
|
|
SetTimer(1, intervals[m_IntervalIndex], nullptr);
|
|
}
|
|
|
|
void CPoolListCtrl::OnIntervalChangeUpdateUI(CCmdUI* pCmdUI)
|
|
{
|
|
pCmdUI->SetRadio(pCmdUI->m_nID - ID_REFRESHINTERVAL_5SECONDS == m_IntervalIndex);
|
|
}
|
|
|
|
|
|
void CPoolListCtrl::OnViewDynamicsort()
|
|
{
|
|
m_DynamicSort = !m_DynamicSort;
|
|
}
|
|
|
|
|
|
void CPoolListCtrl::OnUpdateViewDynamicsort(CCmdUI *pCmdUI)
|
|
{
|
|
pCmdUI->SetCheck(m_DynamicSort);
|
|
}
|