#include "pch.h" #include "ProcessManager.h" #include "Keys.h" #include "ProcessInfo.h" #include "ThreadInfo.h" #include "Processes.h" #include #include "DriverHelper.h" using namespace WinSys; struct ProcessManager::Impl { using ProcessMap = std::unordered_map>; using ThreadMap = std::unordered_map>; // processes std::unordered_map> _processesById; std::vector> _processes; std::vector> _terminatedProcesses; std::vector> _newProcesses; ProcessMap _processesByKey; // threads std::vector> _threads; std::vector> _newThreads; std::vector> _terminatedThreads; std::unordered_map> _threadsById; ThreadMap _threadsByKey; LARGE_INTEGER _prevTicks{}; static uint32_t _totalProcessors; static bool _isElevated; Impl() { if (_totalProcessors == 0) { _totalProcessors = ::GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); _isElevated = Process::OpenById(::GetCurrentProcessId())->IsElevated(); } } size_t EnumProcesses(bool includeThreads, uint32_t pid); std::shared_ptr BuildProcessInfo(const SYSTEM_PROCESS_INFORMATION* info, bool includeThreads, ThreadMap& threadsByKey, int64_t delta, std::shared_ptr pi, bool extended); std::vector>& GetProcesses() { return _processes; } const std::vector>& GetProcesses() const { return _processes; } std::shared_ptr GetProcessInfo(int index) const; std::shared_ptr GetProcessById(uint32_t pid) const { auto it = _processesById.find(pid); // 查找以 key 为键的键值对 return it == _processesById.end() ? nullptr : it->second; } std::shared_ptr GetProcessByKey(const ProcessOrThreadKey& key) const; const std::vector>& GetTerminatedProcesses() const; const std::vector>& GetNewProcesses() const; std::vector>& GetThreads() { return _threads; } const std::vector>& GetThreads() const { return _threads; } size_t GetProcessCount() const { return _processes.size(); } std::shared_ptr GetThreadInfo(int index) const { return _threads[index]; } std::shared_ptr GetThreadByKey(const ProcessOrThreadKey& key) const { auto it = _threadsByKey.find(key); return it == _threadsByKey.end() ? nullptr : it->second; } const std::vector>& GetTerminatedThreads() const { return _terminatedThreads; } const std::vector>& GetNewThreads() const { return _newThreads; } size_t GetThreadCount() const { return _threads.size(); } }; uint32_t ProcessManager::Impl::_totalProcessors; bool ProcessManager::Impl::_isElevated; ProcessManager::ProcessManager() :_impl(std::make_unique()){ } ProcessManager::~ProcessManager() = default; std::shared_ptr ProcessManager::GetProcessInfo(int index) const { return _impl->GetProcessInfo(index); } std::shared_ptr ProcessManager::GetProcessById(uint32_t pid) const { return _impl->GetProcessById(pid); } std::shared_ptr ProcessManager::GetProcessByKey(const ProcessOrThreadKey& key) const { return _impl->GetProcessByKey(key); } const std::vector>& ProcessManager::GetTerminatedProcesses() const { return _impl->GetTerminatedProcesses(); } const std::vector>& ProcessManager::GetNewProcesses() const { return _impl->GetNewProcesses(); } std::vector>& ProcessManager::GetProcesses() { return _impl->GetProcesses(); } const std::vector>& ProcessManager::GetProcesses() const { return _impl->GetProcesses(); } std::vector>& ProcessManager::GetThreads() { return _impl->GetThreads(); } const std::vector>& ProcessManager::GetThreads() const { return _impl->GetThreads(); } size_t ProcessManager::EnumProcesses() { return _impl->EnumProcesses(false, 0); } std::shared_ptr ProcessManager::GetThreadInfo(int index) const { return _impl->GetThreadInfo(index); } std::shared_ptr ProcessManager::GetThreadByKey(const ProcessOrThreadKey& key) const { return _impl->GetThreadByKey(key); } const std::vector>& ProcessManager::GetTerminatedThreads() const { return _impl->GetTerminatedThreads(); } const std::vector>& ProcessManager::GetNewThreads() const { return _impl->GetNewThreads(); } size_t ProcessManager::GetThreadCount() const { return _impl->GetThreadCount(); } size_t WinSys::ProcessManager::GetProcessCount() const { return _impl->GetProcesses().size(); } std::wstring ProcessManager::GetProcessNameById(uint32_t pid) const { if (pid == 0) return L""; auto pi = GetProcessById(pid); return pi ? pi->GetImageName(): L""; } size_t ProcessManager::EnumProcessAndThreads(uint32_t pid) { return _impl->EnumProcesses(true, pid); } size_t ProcessManager::Impl::EnumProcesses(bool includeThreads, uint32_t pid) { std::vector> processes; processes.reserve(_processes.empty() ? 512 : _processes.size() + 10); ProcessMap processesByKey; processesByKey.reserve(_processes.size() == 0 ? 512 : _processes.size() + 10); _processesById.clear(); _processesById.reserve(_processes.capacity()); _newProcesses.clear(); ThreadMap threadsByKey; if (includeThreads) { threadsByKey.reserve(4096); _newThreads.clear(); if (_threads.empty()) _newThreads.reserve(4096); _threads.clear(); _threadsById.clear(); } int size = 1 << 22; wil::unique_virtualalloc_ptr buffer((BYTE*)::VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); if (!buffer) return 0; ULONG len; // get timing info as close as possible to the API call LARGE_INTEGER ticks; ::QueryPerformanceCounter(&ticks); auto delta = ticks.QuadPart - _prevTicks.QuadPart; // Δ用来表示增量符号 NTSTATUS status; bool extended; if (_isElevated && IsWindows8OrGreater()) { status = NtQuerySystemInformation(SystemFullProcessInformation, buffer.get(), size, &len); if (status == STATUS_INVALID_INFO_CLASS) { status = NtQuerySystemInformation(SystemExtendedProcessInformation, buffer.get(), size, &len); extended = false; } else extended = true; } else { extended = false; status = NtQuerySystemInformation(SystemExtendedProcessInformation, buffer.get(), size, &len); } if (NT_SUCCESS(status)) { auto p = reinterpret_cast(buffer.get()); for (;;) { if (pid == 0 || pid == HandleToULong(p->UniqueProcessId)) { ProcessOrThreadKey key = { p->CreateTime.QuadPart,HandleToUlong(p->UniqueProcessId) }; std::shared_ptr pi; if (auto it = _processesByKey.find(key); it == _processesByKey.end()) { // new process pi = BuildProcessInfo(p, includeThreads, threadsByKey, delta, pi, extended); _newProcesses.push_back(pi); pi->CPU = 0; } else { const auto& pi2 = it->second; auto cpu = delta == 0 ? 0 : (int32_t)((p->KernelTime.QuadPart + p->UserTime.QuadPart - pi2->UserTime - pi2->KernelTime) * 1000000 / delta / _totalProcessors); pi = BuildProcessInfo(p, includeThreads, threadsByKey, delta, pi2, extended); pi->CPU = cpu; // remove the known processes _processesByKey.erase(key); } processes.push_back(pi); // // add process to maps // processesByKey.insert({ key,pi }); _processesById.insert({ pi->Id,pi }); } if (p->NextEntryOffset == 0) break; p = reinterpret_cast((BYTE*)p + p->NextEntryOffset); } } _processes = std::move(processes); // // remaining processes are terminated ones // _terminatedProcesses.clear(); _terminatedProcesses.reserve(_processesByKey.size()); for (const auto& [key, pi] : _processesByKey) _terminatedProcesses.push_back(pi); _processesByKey = std::move(processesByKey); if (includeThreads) { _terminatedThreads.clear(); _terminatedThreads.clear(); for (const auto& [key, ti] : _threadsByKey) _terminatedThreads.push_back(ti); _threadsByKey = std::move(threadsByKey); } _prevTicks = ticks; return static_cast(_processes.size()); } #ifdef __cplusplus #if _MSC_VER >= 1300 #define TYPE_ALIGNMENT( t ) __alignof(t) // 返回指定类型的对齐方式 #endif #else #define TYPE_ALIGNMENT( t ) \ FIELD_OFFSET( struct { char x; t test; }, test ) #endif std::shared_ptr ProcessManager::Impl::GetProcessInfo(int index) const { return _processes[index]; } std::shared_ptr ProcessManager::Impl::GetProcessByKey(const ProcessOrThreadKey& key) const { auto it = _processesByKey.find(key); return it == _processesByKey.end() ? nullptr : it->second; } const std::vector>& ProcessManager::Impl::GetTerminatedProcesses() const { return _terminatedProcesses; } const std::vector>& ProcessManager::Impl::GetNewProcesses() const { return _newProcesses; } std::shared_ptr ProcessManager::Impl::BuildProcessInfo( const SYSTEM_PROCESS_INFORMATION* info, bool includeThreads, ThreadMap& threadsByKey, int64_t delta, std::shared_ptr pi, bool extended) { if (pi == nullptr) { pi = std::make_shared(); pi->Id = HandleToULong(info->UniqueProcessId); pi->SessionId = info->SessionId; pi->CreateTime = info->CreateTime.QuadPart; pi->Key.Created = pi->CreateTime; pi->Key.Id = pi->Id; pi->ParentId = HandleToULong(info->InheritedFromUniqueProcessId); pi->ClearThreads(); auto name = info->UniqueProcessId == 0 ? L"(Idle)" : std::wstring(info->ImageName.Buffer, info->ImageName.Length / sizeof(WCHAR)); if (info->UniqueProcessId > 0) { pi->EProcess = DriverHelper::GetEprocess(info->UniqueProcessId); } if (extended && info->UniqueProcessId > 0) { auto ext = (SYSTEM_PROCESS_INFORMATION_EXTENSION*)((BYTE*)info + FIELD_OFFSET(SYSTEM_PROCESS_INFORMATION, Threads) + sizeof(SYSTEM_EXTENDED_THREAD_INFORMATION) * info->NumberOfThreads); pi->JobObjectId = ext->JobObjectId; auto index = name.rfind(L'\\'); ::memcpy(pi->UserSid, (BYTE*)info + ext->UserSidOffset, sizeof(pi->UserSid)); pi->_processName = index == std::wstring::npos ? name : name.substr(index + 1); pi->_nativeImagePath = name; if (ext->PackageFullNameOffset > 0) { pi->_packageFullName = (const wchar_t*)((BYTE*)ext + ext->PackageFullNameOffset); } } else { pi->_processName = name; pi->JobObjectId = 0; } } pi->ThreadCount = info->NumberOfThreads; pi->BasePriority = info->BasePriority; pi->UserTime = info->UserTime.QuadPart; pi->KernelTime = info->KernelTime.QuadPart; pi->HandleCount = info->HandleCount; pi->PageFaultCount = info->PageFaultCount; pi->PeakThreads = info->NumberOfThreadsHighWatermark; pi->PeakVirtualSize = info->PeakVirtualSize; pi->VirtualSize = info->VirtualSize; pi->WorkingSetSize = info->WorkingSetSize; pi->PeakWorkingSetSize = info->PeakWorkingSetSize; pi->PagefileUsage = info->PagefileUsage; pi->OtherOperationCount = info->OtherOperationCount.QuadPart; pi->ReadOperationCount = info->ReadOperationCount.QuadPart; pi->WriteOperationCount = info->WriteOperationCount.QuadPart; pi->HardFaultCount = info->HardFaultCount; pi->OtherTransferCount = info->OtherTransferCount.QuadPart; pi->ReadTransferCount = info->ReadTransferCount.QuadPart; pi->WriteTransferCount = info->WriteTransferCount.QuadPart; pi->PeakPagefileUsage = info->PeakPagefileUsage; pi->CycleTime = info->CycleTime; pi->NonPagedPoolUsage = info->QuotaNonPagedPoolUsage; pi->PagedPoolUsage = info->QuotaPagedPoolUsage; pi->PeakNonPagedPoolUsage = info->QuotaPeakNonPagedPoolUsage; pi->PeakPagedPoolUsage = info->QuotaPeakPagedPoolUsage; pi->PrivatePageCount = info->PrivatePageCount; if (includeThreads && pi->Id > 0) { auto threadCount = info->NumberOfThreads; for (ULONG i = 0; i < threadCount; i++) { auto tinfo = (SYSTEM_EXTENDED_THREAD_INFORMATION*)info->Threads + i; const auto& baseInfo = tinfo->ThreadInfo; ProcessOrThreadKey key = { baseInfo.CreateTime.QuadPart, HandleToULong(baseInfo.ClientId.UniqueThread) }; std::shared_ptr thread; std::shared_ptr ti2; bool newobject = true; int64_t cpuTime; if (auto it = _threadsByKey.find(key); it != _threadsByKey.end()) { thread = it->second; cpuTime = thread->UserTime + thread->KernelTime; newobject = false; } if (newobject) { thread = std::make_shared(); thread->_processName = pi->GetImageName(); thread->Id = HandleToULong(baseInfo.ClientId.UniqueThread); thread->ProcessId = HandleToULong(baseInfo.ClientId.UniqueProcess); thread->CreateTime = baseInfo.CreateTime.QuadPart; thread->StartAddress = baseInfo.StartAddress; thread->StackBase = tinfo->StackBase; thread->StackLimit = tinfo->StackLimit; thread->Win32StartAddress = tinfo->Win32StartAddress; thread->TebBase = tinfo->TebBase; thread->Key = key; } thread->KernelTime = baseInfo.KernelTime.QuadPart; thread->UserTime = baseInfo.UserTime.QuadPart; thread->Priority = baseInfo.Priority; thread->BasePriority = baseInfo.BasePriority; thread->ThreadState = (ThreadState)baseInfo.ThreadState; thread->WaitReason = (WaitReason)baseInfo.WaitReason; thread->WaitTime = baseInfo.WaitTime; thread->ContextSwitches = baseInfo.ContextSwitches; if ((ThreadState)baseInfo.ThreadState != ThreadState::Waiting || (WaitReason)baseInfo.WaitReason != WaitReason::Suspended) { pi->IsSuspended = false; } pi->AddThread(thread); _threads.push_back(thread); if (newobject) { // new thread thread->CPU = 0; _newThreads.push_back(thread); } else { thread->CPU = delta == 0 ? 0 : (int32_t)((thread->KernelTime + thread->UserTime - cpuTime) * 1000000LL / delta / 1/*_totalProcessors*/); _threadsByKey.erase(thread->Key); } threadsByKey.insert(std::make_pair(thread->Key, thread)); _threadsById.insert(std::make_pair(thread->Id, thread)); } } return pi; }