mirror of
https://github.com/BeneficialCode/WinArk
synced 2026-08-09 12:00:31 +00:00
776 lines
21 KiB
C++
776 lines
21 KiB
C++
#include "stdafx.h"
|
|
#include "EtwView.h"
|
|
#include "resource.h"
|
|
#include "TraceManager.h"
|
|
#include "FormatHelper.h"
|
|
#include <execution>
|
|
#include "ClipboardHelper.h"
|
|
#include "SortHelper.h"
|
|
#include "EventsDlg.h"
|
|
#include "FiltersDlg.h"
|
|
#include "FilterFactory.h"
|
|
#include "CallStackDlg.h"
|
|
#include "EventPropertiesDlg.h"
|
|
#include "SymbolManager.h"
|
|
#include "Helpers.h"
|
|
#include <filesystem>
|
|
#include "SymbolHelper.h"
|
|
#include <unordered_set>
|
|
#include <DriverHelper.h>
|
|
#include "QuickFindDlg.h"
|
|
#include "SerializerFactory.h"
|
|
|
|
CEtwView::CEtwView(IMainFrame* frame) :CViewBase(frame) {
|
|
}
|
|
|
|
void CEtwView::Activate(bool active) {
|
|
m_IsActive = active;
|
|
if (active) {
|
|
UpdateEventStatus();
|
|
UpdateUI();
|
|
}
|
|
}
|
|
|
|
void CEtwView::AddEvent(std::shared_ptr<EventData> data) {
|
|
{
|
|
std::lock_guard lock(m_EventsLock);
|
|
m_TempEvents.push_back(data);
|
|
}
|
|
}
|
|
|
|
void CEtwView::StartMonitoring(TraceManager& tm, bool start) {
|
|
if (start) {
|
|
std::vector<KernelEventTypes> types;
|
|
std::vector<std::wstring> categories;
|
|
for (auto& cat : m_EventsConfig.GetCategories()) {
|
|
auto c = KernelEventCategory::GetCategory(cat.Name.c_str());
|
|
ATLASSERT(c);
|
|
types.push_back(c->EnableFlag);
|
|
categories.push_back(c->Name);
|
|
}
|
|
std::initializer_list<KernelEventTypes> events(types.data(), types.data() + types.size());
|
|
tm.SetKernelEventTypes(events);
|
|
tm.SetKernelEventStacks(std::initializer_list<std::wstring>(categories.data(), categories.data() + categories.size()));
|
|
ApplyFilters(m_FilterConfig);
|
|
}
|
|
else {
|
|
m_IsDraining = true;
|
|
}
|
|
m_IsMonitoring = start;
|
|
}
|
|
|
|
CString CEtwView::GetColumnText(HWND, int row, int col) const {
|
|
auto item = m_Events[row].get();
|
|
|
|
CString text;
|
|
switch (col)
|
|
{
|
|
case 0:// index
|
|
text.Format(L"%7u", item->GetIndex());
|
|
if (item->GetStackEventData())
|
|
text += L" *";
|
|
break;
|
|
|
|
case 1:// Time
|
|
{
|
|
auto ts = item->GetTimeStamp();
|
|
return FormatHelper::FormatTime(ts);
|
|
}
|
|
|
|
case 3:// PID
|
|
{
|
|
DWORD tid = -1;
|
|
DWORD pid = -1;
|
|
if (item->GetEventName() == L"Thread/CSwitch") {
|
|
tid = item->GetProperty(L"NewThreadId")->GetValue<DWORD>();
|
|
HANDLE hThread = ::OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid);
|
|
if (hThread != NULL) {
|
|
pid = GetProcessIdOfThread(hThread);
|
|
::CloseHandle(hThread);
|
|
}
|
|
}
|
|
else if (item->GetEventName() == L"PerfInfo/SysClEnter") {
|
|
tid = _threadById[item->GetProcessorNumber()];
|
|
pid = _processById[tid];
|
|
}
|
|
else {
|
|
pid = item->GetProcessId();
|
|
}
|
|
if (pid != (DWORD)-1 && pid != 0)
|
|
text.Format(L"%u (0x%X)", pid, pid);
|
|
break;
|
|
}
|
|
|
|
case 5:// TID
|
|
{
|
|
DWORD tid = -1;
|
|
if (item->GetEventName() == L"Thread/CSwitch") {
|
|
tid = item->GetProperty(L"NewThreadId")->GetValue<DWORD>();
|
|
}
|
|
else if (item->GetEventName() == L"PerfInfo/SysClEnter") {
|
|
tid = _threadById[item->GetProcessorNumber()];
|
|
}
|
|
else {
|
|
tid = item->GetThreadId();
|
|
}
|
|
if (tid != (DWORD)-1 && tid != 0)
|
|
text.Format(L"%u (0x%X)", tid, tid);
|
|
|
|
break;
|
|
}
|
|
|
|
case 6:// Details
|
|
return GetEventDetails(item).c_str();
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
int CEtwView::GetRowImage(HWND, int row) const {
|
|
auto& evt = m_Events[row];
|
|
if (auto it = s_IconsMap.find(evt->GetEventName()); it != s_IconsMap.end())
|
|
return it->second;
|
|
|
|
auto pos = evt->GetEventName().find(L'/');
|
|
if (pos != std::wstring::npos) {
|
|
if (auto it = s_IconsMap.find(evt->GetEventName().substr(0, pos)); it != s_IconsMap.end())
|
|
return it->second;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
PCWSTR CEtwView::GetColumnTextPointer(HWND, int row, int col) const {
|
|
auto item = m_Events[row].get();
|
|
switch (col)
|
|
{
|
|
case 2: return item->GetEventName().c_str();
|
|
case 4:
|
|
if (item->GetEventName() == L"PerfInfo/SysClEnter") {
|
|
DWORD tid = _threadById[item->GetProcessorNumber()];
|
|
DWORD pid = _processById[tid];
|
|
return _processNameById[pid].c_str();
|
|
}
|
|
return item->GetProcessName().c_str();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool CEtwView::OnRightClickList(HWND, int index, int col, POINT& pt) {
|
|
if (index >= 0) {
|
|
CMenu menu;
|
|
menu.LoadMenu(IDR_ETW_CONTEXT);
|
|
GetFrame()->TrackPopupMenu(menu.GetSubMenu(0), *this, &pt);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CEtwView::OnDoubleClickList(HWND, int row, int col, POINT& pt) {
|
|
SendMessage(WM_COMMAND, ID_EVENT_PROPERTIES);
|
|
return true;
|
|
}
|
|
|
|
DWORD CEtwView::AddNewProcess(DWORD tid) const{
|
|
DWORD pid = -1;
|
|
HANDLE hThread = ::OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid);
|
|
if (hThread != NULL) {
|
|
pid = GetProcessIdOfThread(hThread);
|
|
::CloseHandle(hThread);
|
|
}
|
|
if (pid != -1&&pid!=0) {
|
|
std::unique_ptr<WinSys::Process> process;
|
|
_processById.insert({ tid,pid });
|
|
auto hProcess = DriverHelper::OpenProcess(pid, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ);
|
|
if (!hProcess)
|
|
hProcess = DriverHelper::OpenProcess(pid, PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ);
|
|
if (hProcess) {
|
|
process.reset(new WinSys::Process(hProcess));
|
|
std::wstring procName = process->GetName();
|
|
_processNameById.insert({ pid,procName });
|
|
}
|
|
}
|
|
return pid;
|
|
}
|
|
|
|
std::wstring CEtwView::ProcessSpecialEvent(EventData* data) const {
|
|
std::wstring details;
|
|
CString text;
|
|
auto& name = data->GetEventName();
|
|
if (name == L"Process/Start") {
|
|
text.Format(L"PID: %u; Image: %s; Command Line: %s",
|
|
data->GetProperty(L"ProcessId")->GetValue<DWORD>(),
|
|
CString(data->GetProperty(L"ImageFileName")->GetAnsiString()),
|
|
data->GetProperty(L"CommandLine")->GetUnicodeString());
|
|
details = std::move(text);
|
|
}
|
|
if (name == L"PerfInfo/SysClEnter") {
|
|
DWORD64 address;
|
|
auto prop = data->GetProperty(L"SysCallAddress");
|
|
if (prop->GetLength() == 4)
|
|
address = prop->GetValue<DWORD>();
|
|
else
|
|
address = prop->GetValue<DWORD64>();
|
|
DWORD64 offset = 0;
|
|
auto symbol = SymbolHelper::GetSymbolFromAddress(address, &offset);
|
|
if (symbol) {
|
|
auto sym = symbol->GetSymbolInfo();
|
|
CStringA text;
|
|
text.Format("%s", sym->Name);
|
|
details = Helpers::StringToWstring(std::string(text));
|
|
}
|
|
}
|
|
if (name == L"Thread/CSwitch") {
|
|
UCHAR processorNumber = data->GetProcessorNumber();
|
|
DWORD tid = data->GetProperty(L"NewThreadId")->GetValue<DWORD>();
|
|
if (tid != -1) {
|
|
_threadById[processorNumber] = tid;
|
|
}
|
|
if (_processById.count(tid) == 0) {
|
|
DWORD pid = AddNewProcess(tid);
|
|
if (pid != -1 && pid != 0) {
|
|
data->SetProcessId(pid);
|
|
}
|
|
}
|
|
tid = data->GetProperty(L"OldThreadId")->GetValue<DWORD>();
|
|
if (_processById.count(tid) == 0) {
|
|
AddNewProcess(tid);
|
|
}
|
|
auto state = data->GetProperty(L"OldThreadState")->GetValue<uint8_t>();
|
|
if (_processById.count(tid) != 0 && state == (uint8_t)WinSys::ThreadState::Terminated) {
|
|
if (_processNameById.count(_processById[tid]) != 0) {
|
|
_processNameById.erase(_processById[tid]);
|
|
}
|
|
_processById.erase(tid);
|
|
}
|
|
}
|
|
return details;
|
|
}
|
|
|
|
std::wstring CEtwView::GetEventDetails(EventData* data) const {
|
|
auto details = ProcessSpecialEvent(data);
|
|
if (details.empty()) {
|
|
for (auto& prop : data->GetProperties()) {
|
|
if (prop.Name.substr(0, 8) != L"Reserved" && prop.Name.substr(0, 4) != L"TTID") {
|
|
auto value = FormatHelper::FormatProperty(data, prop);
|
|
if (value.empty())
|
|
value = data->FormatProperty(prop);
|
|
if (!value.empty()) {
|
|
if (value.size() > 102)
|
|
value = value.substr(0, 100) + L"...";
|
|
details += prop.Name + L": " + value + L";";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return details;
|
|
}
|
|
|
|
void CEtwView::UpdateEventStatus() {
|
|
CString text;
|
|
text.Format(L"Events: %u", (uint32_t)m_Events.size());
|
|
GetFrame()->SetPaneText(2, text);
|
|
}
|
|
|
|
bool CEtwView::IsSortable(HWND, int col) const {
|
|
return col != 8 && (!m_IsMonitoring || GetFrame()->GetTraceManager().IsPaused());
|
|
}
|
|
|
|
void CEtwView::DoSort(const SortInfo* si) {
|
|
auto compare = [&](auto& i1, auto& i2) {
|
|
switch (si->SortColumn)
|
|
{
|
|
case 0: return SortHelper::SortNumbers(i1->GetIndex(), i2->GetIndex(), si->SortAscending);
|
|
case 1: return SortHelper::SortNumbers(i1->GetTimeStamp(), i2->GetTimeStamp(), si->SortAscending);
|
|
case 2: return SortHelper::SortStrings(i1->GetEventName(), i2->GetEventName(), si->SortAscending);
|
|
case 3: return SortHelper::SortNumbers(i1->GetProcessId(), i2->GetProcessId(), si->SortAscending);
|
|
case 4: return SortHelper::SortStrings(i1->GetProcessName(), i2->GetProcessName(), si->SortAscending);
|
|
case 5: return SortHelper::SortNumbers(i1->GetThreadId(), i2->GetThreadId(), si->SortAscending);
|
|
case 6: return SortHelper::SortNumbers(i1->GetEventDescriptor().Opcode, i2->GetEventDescriptor().Opcode, si->SortAscending);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
if (m_Events.size() < 20000)
|
|
std::sort(m_Events.begin(), m_Events.end(), compare);
|
|
else {
|
|
// C++ 17引入了执行策略
|
|
// std::execution::par使算法在多个线程中执行,并且线程各自具有自己的顺序任务。即并行但不并发。
|
|
// 并行在操作系统中是指,一组程序按独立异步的速度执行,不等于时间上的重叠(同一个时刻发生)。
|
|
// 并发是指:在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行)。
|
|
std::sort(std::execution::par, m_Events.begin(), m_Events.end(), compare);
|
|
}
|
|
}
|
|
|
|
BOOL CEtwView::PreTransalteMessage(MSG* pMsg) {
|
|
pMsg;
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD CEtwView::OnPrePaint(int, LPNMCUSTOMDRAW) {
|
|
return CDRF_NOTIFYITEMDRAW;
|
|
}
|
|
|
|
DWORD CEtwView::OnSubItemPrePaint(int, LPNMCUSTOMDRAW cd) {
|
|
auto lcd = (LPNMLVCUSTOMDRAW)cd;
|
|
auto cm = GetColumnManager(m_List);
|
|
auto sub = cm->GetRealColumn(lcd->iSubItem);
|
|
lcd->clrTextBk = CLR_INVALID;
|
|
|
|
if ((cm->GetColumn(sub).Flags & ColumnFlags::Numeric) == ColumnFlags::Numeric)
|
|
::SelectObject(cd->hdc, (HFONT)GetFrame()->GetMonoFont());
|
|
else
|
|
::SelectObject(cd->hdc, m_hFont);
|
|
|
|
int index = (int)cd->dwItemSpec;
|
|
if (sub == 8 && (m_List.GetItemState(index, LVIS_SELECTED) & LVIS_SELECTED) == 0) {
|
|
auto& item = m_Events[index];
|
|
auto start = (size_t)0;
|
|
auto details = GetEventDetails(item.get());
|
|
bool bold = false;
|
|
CDCHandle dc(cd->hdc);
|
|
std::wstring str;
|
|
int x = cd->rc.left + 6, y = cd->rc.top, right = cd->rc.right;
|
|
SIZE size;
|
|
for (;;) {
|
|
auto pos = details.find(L";", start);
|
|
if (pos == std::wstring::npos)
|
|
break;
|
|
str = details.substr(start, pos - start);
|
|
|
|
auto colon = str.find(L":");
|
|
dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
|
|
dc.GetTextExtent(str.c_str(), (int)colon + 1, &size);
|
|
if (x + size.cx > right)
|
|
break;
|
|
|
|
dc.TextOutW(x, y, str.c_str(), (int)colon + 1);
|
|
x += size.cx;
|
|
|
|
dc.SetTextColor(RGB(0, 0, 255));
|
|
dc.GetTextExtent(str.data() + colon + 1, (int)str.size() - (int)colon - 1, &size);
|
|
if (x + size.cx > right)
|
|
break;
|
|
dc.TextOutW(x, y, str.data() + colon + 1, (int)str.size() - (int)colon - 1);
|
|
x += size.cx + 4;
|
|
|
|
start = pos + 1;
|
|
}
|
|
return CDRF_SKIPDEFAULT;
|
|
}
|
|
|
|
return CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW;
|
|
}
|
|
|
|
DWORD CEtwView::OnItemPrePaint(int, LPNMCUSTOMDRAW cd) {
|
|
m_hFont = (HFONT)::GetCurrentObject(cd->hdc, OBJ_FONT);
|
|
return CDRF_NOTIFYSUBITEMDRAW;
|
|
}
|
|
|
|
CImageList CEtwView::GetEventImageList() {
|
|
return s_Images;
|
|
}
|
|
|
|
int CEtwView::GetImageFromEventName(PCWSTR name) {
|
|
if (auto it = s_IconsMap.find(name); it != s_IconsMap.end())
|
|
return it->second;
|
|
|
|
auto pos = ::wcschr(name, L'/');
|
|
if (pos) {
|
|
if (auto it = s_IconsMap.find(std::wstring(name, pos)); it != s_IconsMap.end())
|
|
return it->second;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void CEtwView::OnFinalMessage(HWND) {
|
|
GetFrame()->ViewDestroyed(this);
|
|
delete this;
|
|
}
|
|
|
|
LRESULT CEtwView::OnCreate(UINT, WPARAM, LPARAM, BOOL&) {
|
|
m_hWndClient = m_List.Create(*this, rcDefault, nullptr,
|
|
WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
|
|
LVS_REPORT | LVS_SHOWSELALWAYS | LVS_OWNERDATA | LVS_SINGLESEL | LVS_SHAREIMAGELISTS);
|
|
m_List.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP | LVS_EX_LABELTIP |
|
|
LVS_EX_DOUBLEBUFFER);
|
|
|
|
auto processes = KernelEventCategory::GetCategory(L"Process");
|
|
ATLASSERT(processes);
|
|
|
|
// init event
|
|
EventConfigCategory cat;
|
|
cat.Name = processes->Name;
|
|
m_EventsConfig.AddCategory(cat);
|
|
|
|
// init filters
|
|
FilterDescription desc;
|
|
desc.Name = L"Process Id";
|
|
desc.Action = FilterAction::Exclude;
|
|
desc.Parameters = std::to_wstring(::GetCurrentProcessId());
|
|
m_FilterConfig.AddFilter(desc);
|
|
|
|
if (s_Images == nullptr) {
|
|
s_Images.Create(16, 16, ILC_COLOR32, 8, 8);
|
|
struct {
|
|
int icon;
|
|
PCWSTR type;
|
|
}icons[] = {
|
|
{IDI_GENERIC,nullptr},
|
|
{ IDI_HEAP2, L"PageFault/VirtualAlloc" },
|
|
{ IDI_HEAP2, L"Virtual Memory" },
|
|
{ IDI_GEAR, L"Process" },
|
|
{ IDI_PROCESS_NEW, L"Process/Start" },
|
|
{ IDI_PROCESS_DELETE, L"Process/Terminate" },
|
|
{ IDI_PROCESS_DELETE, L"Process/End" },
|
|
{ IDI_THREAD, L"Thread" },
|
|
{ IDI_THREAD_NEW, L"Thread/Start" },
|
|
{ IDI_THREAD_DELETE, L"Thread/End" },
|
|
{ IDI_DLL, L"Image" },
|
|
{ IDI_DLL_LOAD, L"Image/Load" },
|
|
{ IDI_DLL_UNLOAD, L"Image/UnLoad" },
|
|
{ IDI_NETWORK, L"TCP" },
|
|
{ IDI_NETWORK, L"UDP" },
|
|
{ IDI_NETWORK, L"TcpIp" },
|
|
{ IDI_NETWORK, L"UdpIp" },
|
|
{ IDI_REGISTRY, L"Registry" },
|
|
{ IDI_FILE, L"FileIo" },
|
|
{ IDI_FILE, L"File" },
|
|
{ IDI_HANDLE, L"Objects" },
|
|
{ IDI_HANDLE, L"Object" },
|
|
{ IDI_OBJECT, L"Object/CreateHandle" },
|
|
{ IDI_OBJECT, L"Object/CloseHandle" },
|
|
{ IDI_OBJECT, L"Handles" },
|
|
{ IDI_DISK, L"DiskIo" },
|
|
{ IDI_DISK, L"Disk I/O" },
|
|
{ IDI_MEMORY, L"PageFault" },
|
|
{ IDI_MEMORY, L"Page Fault" },
|
|
{ IDI_HEAP, L"Pool" },
|
|
{ IDI_HEAP, L"Kernel Pool" },
|
|
};
|
|
|
|
int index = 0;
|
|
for (auto entry : icons) {
|
|
s_Images.AddIcon(AtlLoadIconImage(entry.icon, 0, 16, 16));
|
|
if (entry.type)
|
|
s_IconsMap.insert({ entry.type,index });
|
|
index++;
|
|
}
|
|
}
|
|
|
|
m_List.SetImageList(s_Images, LVSIL_SMALL);
|
|
|
|
auto cm = GetColumnManager(m_List);
|
|
cm->AddColumn(L"#", 0, 100, ColumnFlags::Numeric | ColumnFlags::Visible);
|
|
cm->AddColumn(L"Time", LVCFMT_RIGHT, 180, ColumnFlags::Numeric | ColumnFlags::Visible);
|
|
cm->AddColumn(L"Event", LVCFMT_LEFT, 160);
|
|
cm->AddColumn(L"PID", LVCFMT_RIGHT, 120, ColumnFlags::Numeric | ColumnFlags::Visible);
|
|
cm->AddColumn(L"Process Name", LVCFMT_LEFT, 150);
|
|
cm->AddColumn(L"TID", LVCFMT_RIGHT, 120, ColumnFlags::Numeric | ColumnFlags::Visible);
|
|
cm->AddColumn(L"Details", LVCFMT_LEFT, 700);
|
|
|
|
cm->UpdateColumns();
|
|
|
|
m_TempEvents.reserve(1 << 12);
|
|
m_Events.reserve(1 << 16);
|
|
SetTimer(1, 1000, nullptr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnDestroy(UINT, WPARAM, LPARAM, BOOL& handled) {
|
|
KillTimer(1);
|
|
handled = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnForwardMsg(UINT, WPARAM, LPARAM lp, BOOL& handled) {
|
|
auto msg = (MSG*)lp;
|
|
LRESULT result;
|
|
handled = ProcessWindowMessage(msg->hwnd, msg->message, msg->wParam, msg->lParam, result, 1);
|
|
return result;
|
|
}
|
|
|
|
LRESULT CEtwView::OnTimer(UINT, WPARAM id, LPARAM, BOOL&) {
|
|
if (m_IsMonitoring && id == 1) {
|
|
if (!m_TempEvents.empty()) {
|
|
std::lock_guard lock(m_EventsLock);
|
|
m_Events.insert(m_Events.end(), m_TempEvents.begin(), m_TempEvents.end());
|
|
m_OrgEvents.insert(m_OrgEvents.end(), m_TempEvents.begin(), m_TempEvents.end());
|
|
m_TempEvents.clear();
|
|
}
|
|
else if (!m_IsMonitoring) {
|
|
m_IsDraining = false;
|
|
}
|
|
UpdateList(m_List, static_cast<int>(m_Events.size()));
|
|
if (m_AutoScroll)
|
|
m_List.EnsureVisible(m_List.GetItemCount() - 1, FALSE);
|
|
if (m_IsActive)
|
|
UpdateEventStatus();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnClear(WORD, WORD, HWND, BOOL&) {
|
|
CWaitCursor wait;
|
|
m_List.SetItemCount(0);
|
|
std::lock_guard lock(m_EventsLock);
|
|
m_Events.clear();
|
|
m_TempEvents.clear();
|
|
m_OrgEvents.clear();
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnCallStack(WORD, WORD, HWND, BOOL&) {
|
|
auto selected = m_List.GetSelectedIndex();
|
|
if (selected < 0)
|
|
return 0;
|
|
|
|
auto data = m_Events[selected].get();
|
|
if (data->GetStackEventData() == nullptr) {
|
|
AtlMessageBox(*this, L"Call stack not available for this event", IDS_TITLE, MB_ICONEXCLAMATION);
|
|
return 0;
|
|
}
|
|
|
|
CCallStackDlg dlg(data);
|
|
dlg.DoModal();
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnEventProperties(WORD, WORD, HWND, BOOL&) {
|
|
auto selected = m_List.GetSelectedIndex();
|
|
if (selected < 0)
|
|
return 0;
|
|
|
|
auto data = m_Events[selected].get();
|
|
CEventPropertiesDlg dlg(data);
|
|
dlg.DoModal();
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnClose(UINT, WPARAM, LPARAM, BOOL& handled) {
|
|
if (m_IsMonitoring) {
|
|
AtlMessageBox(nullptr, L"Cannot close tab while monitoring is active", IDS_TITLE, MB_ICONWARNING);
|
|
handled = TRUE;
|
|
}
|
|
handled = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnCopy(WORD, WORD, HWND, BOOL&) {
|
|
auto selected = m_List.GetSelectedIndex();
|
|
ATLASSERT(selected >= 0);
|
|
CString text, item;
|
|
for (int c = 0;; c++) {
|
|
if (!m_List.GetItemText(selected, c, item))
|
|
break;
|
|
text += item + L",";
|
|
}
|
|
ClipboardHelper::CopyText(*this, text.Left(text.GetLength() - 1));
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnSave(WORD, WORD, HWND, BOOL&) {
|
|
CSimpleFileDialog dlg(FALSE, L"pmx", nullptr, OFN_EXPLORER | OFN_OVERWRITEPROMPT | OFN_ENABLESIZING,
|
|
L"ProcMonX files (*.pmx)\0*.pmx\0CSV Files (*csv)\0*.csv\0", *this);
|
|
if (dlg.DoModal() == IDOK) {
|
|
CString filename(dlg.m_szFileTitle);
|
|
auto ext = filename.ReverseFind(L'.');
|
|
if (ext > 0) {
|
|
auto serializer = SerializerFactory::CreateFromExtension(filename.Mid(ext + 1));
|
|
if (serializer) {
|
|
EventDataSerializerOptions options;
|
|
CWaitCursor wait;
|
|
auto eventsCopy = m_OrgEvents;
|
|
if (!serializer->Save(eventsCopy, options, dlg.m_szFileName))
|
|
AtlMessageBox(*this, L"Failed to save data", IDS_TITLE, MB_ICONERROR);
|
|
return 0;
|
|
}
|
|
}
|
|
AtlMessageBox(*this, L"Unknown file extension", IDS_TITLE, MB_ICONEXCLAMATION);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnCopyAll(WORD, WORD, HWND, BOOL&) {
|
|
ATLASSERT(!m_IsMonitoring || GetFrame()->GetTraceManager().IsPaused());
|
|
CWaitCursor wait;
|
|
auto count = m_List.GetItemCount();
|
|
CString text, item;
|
|
for (int i = 0; i < count; i++) {
|
|
for (int c = 0;; c++) {
|
|
if (!m_List.GetItemText(i, c, item))
|
|
break;
|
|
text += item + L",";
|
|
}
|
|
text.SetAt(text.GetLength() - 1, L'\n');
|
|
}
|
|
ClipboardHelper::CopyText(*this, text);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnConfigureEvents(WORD, WORD, HWND, BOOL&) {
|
|
CEventsDlg dlg(m_EventsConfig);
|
|
if (dlg.DoModal() == IDOK) {
|
|
if (m_IsMonitoring) {
|
|
// update trace manager
|
|
auto& tm = GetFrame()->GetTraceManager();
|
|
std::vector<KernelEventTypes> types;
|
|
std::vector<std::wstring> categories;
|
|
DWORD flags = 0;
|
|
for (auto& cat : m_EventsConfig.GetCategories()) {
|
|
auto c = KernelEventCategory::GetCategory(cat.Name.c_str());
|
|
ATLASSERT(c);
|
|
types.push_back(c->EnableFlag);
|
|
categories.push_back(c->Name);
|
|
flags |= (DWORD)c->EnableFlag;
|
|
}
|
|
std::initializer_list<KernelEventTypes> events(types.data(), types.data() + types.size());
|
|
tm.SetKernelEventTypes(events);
|
|
tm.SetKernelEventStacks(std::initializer_list<std::wstring>(categories.data(),categories.data() + categories.size()));
|
|
bool isWin8Plus = ::IsWindows8OrGreater();
|
|
|
|
if (isWin8Plus) {
|
|
tm.UpdateEventConfig();
|
|
}
|
|
else {
|
|
tm.Stop();
|
|
tm.Pause(false);
|
|
tm.Start([&](auto data) {
|
|
this->AddEvent(data);
|
|
}, flags);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnAutoScroll(WORD, WORD, HWND, BOOL&) {
|
|
m_AutoScroll = !m_AutoScroll;
|
|
GetFrame()->GetUpdateUI()->UISetCheck(ID_VIEW_AUTOSCROLL, m_AutoScroll);
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnConfigFilters(WORD, WORD, HWND, BOOL&) {
|
|
CFiltersDlg dlg(m_FilterConfig);
|
|
if (dlg.DoModal() == IDOK) {
|
|
// update filters
|
|
auto& tm = GetFrame()->GetTraceManager();
|
|
auto paused = tm.IsRunning() && tm.IsPaused();
|
|
if (!paused)
|
|
tm.Pause(true);
|
|
ApplyFilters(m_FilterConfig);
|
|
if (!paused)
|
|
tm.Pause(false);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnItemChanged(int, LPNMHDR, BOOL&) {
|
|
UpdateUI();
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CEtwView::OnFindNext(WORD, WORD, HWND, BOOL&) {
|
|
auto& options = CQuickFindDlg::GetSearchOptions();
|
|
auto& text = CQuickFindDlg::GetSearchText();
|
|
|
|
auto step = options.SearchDown ? 1 : -1;
|
|
auto count = (int)m_Events.size();
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
auto selected = m_List.GetSelectedIndex() + step;
|
|
if (selected < 0)
|
|
selected = count - 1;
|
|
else if (selected >= count)
|
|
selected = 0;
|
|
|
|
auto start = selected;
|
|
auto search = text;
|
|
auto cs = options.CaseSensitive;
|
|
if (!cs)
|
|
search.MakeLower();
|
|
|
|
// perfrom search
|
|
bool found = false;
|
|
do {
|
|
auto& evt = *m_Events[selected];
|
|
if (options.SearchProcesses) {
|
|
CString text = evt.GetProcessName().c_str();
|
|
if (!cs)
|
|
text.MakeLower();
|
|
if (text.Find(search) >= 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (options.SearchEvents) {
|
|
CString text = evt.GetEventName().c_str();
|
|
if (!cs)
|
|
text.MakeLower();
|
|
if (text.Find(search) >= 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (options.SearchDetails) {
|
|
CString text = GetEventDetails(&evt).c_str();
|
|
if (!cs)
|
|
text.MakeLower();
|
|
if (text.Find(search) >= 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// move to the next row
|
|
selected += step;
|
|
if (selected >= count)
|
|
selected = 0;
|
|
else if (selected < 0)
|
|
selected = count - 1;
|
|
} while (selected != start);
|
|
|
|
if (found) {
|
|
m_List.SelectItem(selected);
|
|
m_List.SetFocus();
|
|
}
|
|
else
|
|
AtlMessageBox(*this, L"Text not found", IDS_TITLE, MB_ICONINFORMATION);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CEtwView::UpdateUI() {
|
|
auto ui = GetFrame()->GetUpdateUI();
|
|
auto& tm = GetFrame()->GetTraceManager();
|
|
|
|
ui->UIEnable(ID_EDIT_COPYALL, !m_IsMonitoring || tm.IsPaused());
|
|
ui->UIEnable(ID_FILE_SAVE, !m_IsMonitoring || tm.IsPaused());
|
|
ui->UISetCheck(ID_VIEW_AUTOSCROLL, m_AutoScroll);
|
|
auto selected = m_List.GetSelectedIndex();
|
|
ui->UIEnable(ID_EVENT_PROPERTIES, selected >= 0);
|
|
ui->UIEnable(ID_EDIT_COPY, selected >= 0);
|
|
ui->UIEnable(ID_EVENT_CALLSTACK, selected >= 0 && m_Events[selected]->GetStackEventData() != nullptr);
|
|
}
|
|
|
|
void CEtwView::ApplyFilters(const FilterConfiguration& config) {
|
|
auto& tm = GetFrame()->GetTraceManager();
|
|
tm.RemoveAllFilters();
|
|
for (int i = 0; i < config.GetFilterCount(); i++) {
|
|
auto desc = config.GetFilter(i);
|
|
ATLASSERT(desc);
|
|
auto filter = FilterFactory::CreateFilter(desc->Name.c_str(), desc->Compare,
|
|
desc->Parameters.c_str(),desc->Action);
|
|
ATLASSERT(filter);
|
|
if (filter) {
|
|
filter->Enable(desc->Enabled);
|
|
tm.AddFilter(filter);
|
|
}
|
|
}
|
|
} |