/* * Process Hacker - * processes and system performance information provider * * Copyright (C) 2009 Flavio Erlich * Copyright (C) 2008-2009 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 . */ using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices; using ProcessHacker.Common; using ProcessHacker.Common.Messaging; using ProcessHacker.Native; using ProcessHacker.Native.Api; using ProcessHacker.Native.Image; using ProcessHacker.Native.Objects; using ProcessHacker.Native.Security; namespace ProcessHacker { public class ImageVersionInfo { public ImageVersionInfo() { } public ImageVersionInfo(FileVersionInfo info) { this.CompanyName = info.CompanyName; this.FileDescription = info.FileDescription; this.FileName = info.FileName; this.FileVersion = info.FileVersion; this.ProductName = info.ProductName; } public string CompanyName { get; set; } public string FileDescription { get; set; } public string FileName { get; set; } public string FileVersion { get; set; } public string ProductName { get; set; } } public class ProcessItem : ICloneable { public object Clone() { return this.MemberwiseClone(); } public int RunId; public int Pid; public Icon Icon; public Icon LargeIcon; public string CmdLine; public float CpuUsage; public string FileName; public ImageVersionInfo VersionInfo; public string Name; public string Username; public string JobName; public string Integrity; public int IntegrityLevel; public SystemProcessInformation Process; public DateTime CreateTime; public TokenElevationType ElevationType; public bool HasParent; public bool IsBeingDebugged; public bool IsDotNet; public bool IsElevated; public bool IsInJob; public bool IsInSignificantJob; public bool IsPacked; public bool IsPosix; public bool IsWow64; public int SessionId; public int ParentPid; public VerifyResult VerifyResult; public string VerifySignerName; public int ImportFunctions; public int ImportModules; public bool JustProcessed; public int ProcessingAttempts; public ProcessHandle ProcessQueryHandle; public Int64Delta CpuKernelDelta; public Int64Delta CpuUserDelta; public Int64Delta IoReadDelta; public Int64Delta IoWriteDelta; public Int64Delta IoOtherDelta; public CircularBuffer CpuKernelHistory; public CircularBuffer CpuUserHistory; public CircularBuffer IoReadHistory; public CircularBuffer IoWriteHistory; public CircularBuffer IoOtherHistory; public CircularBuffer IoReadOtherHistory; public CircularBuffer PrivateMemoryHistory; public CircularBuffer WorkingSetHistory; } public class ProcessSystemProvider : Provider { public class ProcessQueryMessage : Message { public int Stage; public int Pid; public string FileName; public TokenElevationType ElevationType; public bool IsElevated; public string Integrity; public int IntegrityLevel; public string JobName; public bool IsInJob; public bool IsInSignificantJob; public bool IsWow64; public Icon Icon; public Icon LargeIcon; public ImageVersionInfo VersionInfo; public string CmdLine; public bool IsDotNet; public bool IsPacked; public bool IsPosix; public VerifyResult VerifyResult; public string VerifySignerName; public int ImportFunctions; public int ImportModules; } public delegate void ProcessQueryDelegate(int stage, int pid); public event ProcessQueryDelegate ProcessQueryComplete; public event ProcessQueryDelegate ProcessQueryReceived; private SystemBasicInformation _system; public SystemBasicInformation System { get { return _system; } } private SystemPerformanceInformation _performance; public SystemPerformanceInformation Performance { get { return _performance; } } private readonly int _processorPerfArraySize; private readonly MemoryAlloc _processorPerfBuffer; private readonly SystemProcessorPerformanceInformation[] _processorPerfArray; public SystemProcessorPerformanceInformation[] ProcessorPerfArray { get { return _processorPerfArray; } } private SystemProcessorPerformanceInformation _processorPerf; public SystemProcessorPerformanceInformation ProcessorPerf { get { return _processorPerf; } } public float CurrentCpuKernelUsage { get; private set; } public float CurrentCpuUserUsage { get; private set; } public float CurrentCpuUsage { get { return this.CurrentCpuKernelUsage + this.CurrentCpuUserUsage; } } public int PidWithMostIoActivity { get; private set; } public int PidWithMostCpuUsage { get; private set; } public Int64Delta CpuKernelDelta { get { return _cpuKernelDelta; } } public Int64Delta CpuUserDelta { get { return _cpuUserDelta; } } public Int64Delta CpuOtherDelta { get { return _cpuOtherDelta; } } public Int64Delta[] CpuKernelDeltas { get { return _cpuKernelDeltas; } } public Int64Delta[] CpuUserDeltas { get { return _cpuUserDeltas; } } public Int64Delta[] CpuOtherDeltas { get { return _cpuOtherDeltas; } } public Int64Delta IoReadDelta { get { return _ioReadDelta; } } public Int64Delta IoWriteDelta { get { return _ioWriteDelta; } } public Int64Delta IoOtherDelta { get { return _ioOtherDelta; } } public int HistoryMaxSize { get { return _historyMaxSize; } set { _historyMaxSize = value; } } public IList IoReadHistory { get { return _ioReadHistory; } } public IList IoWriteHistory { get { return _ioWriteHistory; } } public IList IoOtherHistory { get { return _ioOtherHistory; } } public IList IoReadOtherHistory { get { return _ioReadOtherHistory; } } public IList CpuKernelHistory { get { return _cpuKernelHistory; } } public IList CpuUserHistory { get { return _cpuUserHistory; } } public IList CpuOtherHistory { get { return _cpuOtherHistory; } } public IList[] CpusKernelHistory { get { return _cpusKernelHistory; } } public IList[] CpusUserHistory { get { return _cpusUserHistory; } } public IList[] CpusOtherHistory { get { return _cpusOtherHistory; } } public CircularList CommitHistory { get { return _commitHistory; } } public CircularList PhysicalMemoryHistory { get { return _physicalMemoryHistory; } } public IList TimeHistory { get { return _timeHistory; } } public IList MostCpuHistory { get { return _cpuMostUsageHistory; } } public IList MostIoHistory { get { return _ioMostUsageHistory; } } private delegate ProcessQueryMessage QueryProcessDelegate(int pid, string fileName, bool useCache); private readonly MessageQueue _messageQueue = new MessageQueue(); private readonly Dictionary _fileResults = new Dictionary(); private Int64Delta _ioReadDelta; private Int64Delta _ioWriteDelta; private Int64Delta _ioOtherDelta; private Int64Delta _cpuKernelDelta; private Int64Delta _cpuUserDelta; private Int64Delta _cpuOtherDelta; private readonly Int64Delta[] _cpuKernelDeltas; private readonly Int64Delta[] _cpuUserDeltas; private readonly Int64Delta[] _cpuOtherDeltas; private int _historyMaxSize = 100; private readonly CircularBuffer _ioReadHistory; private readonly CircularBuffer _ioWriteHistory; private readonly CircularBuffer _ioOtherHistory; private readonly CircularBuffer _ioReadOtherHistory; private readonly CircularBuffer _cpuKernelHistory; private readonly CircularBuffer _cpuUserHistory; private readonly CircularBuffer _cpuOtherHistory; private readonly CircularBuffer[] _cpusKernelHistory; private readonly CircularBuffer[] _cpusUserHistory; private readonly CircularBuffer[] _cpusOtherHistory; private readonly CircularList _commitHistory; private readonly CircularList _physicalMemoryHistory; private readonly CircularBuffer _timeHistory; private readonly CircularBuffer _cpuMostUsageHistory; private readonly CircularBuffer _ioMostUsageHistory; private SystemProcess _dpcs = new SystemProcess { Name = "DPCs", Process = new SystemProcessInformation { ProcessId = -2, InheritedFromProcessId = 0, SessionId = -1 } }; private SystemProcess _interrupts = new SystemProcess { Name = "Interrupts", Process = new SystemProcessInformation { ProcessId = -3, InheritedFromProcessId = 0, SessionId = -1 } }; public ProcessSystemProvider() { this.Name = "ProcessSystemProvider"; // Add the file processing results listener. _messageQueue.AddListener(new MessageQueueListener(message => { if (this.Dictionary.ContainsKey(message.Pid)) { ProcessItem item = this.Dictionary[message.Pid]; this.FillPqResult(item, message); item.JustProcessed = true; } })); SystemBasicInformation basic; int retLen; Win32.NtQuerySystemInformation( SystemInformationClass.SystemBasicInformation, out basic, SystemBasicInformation.SizeOf, out retLen ); _system = basic; _processorPerfArraySize = SystemProcessorPerformanceInformation.SizeOf * _system.NumberOfProcessors; _processorPerfBuffer = new MemoryAlloc(_processorPerfArraySize); _processorPerfArray = new SystemProcessorPerformanceInformation[_system.NumberOfProcessors]; this.UpdateProcessorPerf(); // Initialize the deltas _cpuKernelDelta = new Int64Delta(this.ProcessorPerf.KernelTime); _cpuUserDelta = new Int64Delta(this.ProcessorPerf.UserTime); _cpuOtherDelta = new Int64Delta(this.ProcessorPerf.IdleTime + this.ProcessorPerf.DpcTime + this.ProcessorPerf.InterruptTime); _ioReadDelta = new Int64Delta(this.Performance.IoReadTransferCount); _ioWriteDelta = new Int64Delta(this.Performance.IoWriteTransferCount); _ioOtherDelta = new Int64Delta(this.Performance.IoOtherTransferCount); // Initialize history _cpuKernelHistory = new CircularBuffer(_historyMaxSize); _cpuUserHistory = new CircularBuffer(_historyMaxSize); _cpuOtherHistory = new CircularBuffer(_historyMaxSize); _ioReadHistory = new CircularBuffer(_historyMaxSize); _ioWriteHistory = new CircularBuffer(_historyMaxSize); _ioOtherHistory = new CircularBuffer(_historyMaxSize); _ioReadOtherHistory = new CircularBuffer(_historyMaxSize); _commitHistory = new CircularList(_historyMaxSize); _physicalMemoryHistory = new CircularList(_historyMaxSize); _timeHistory = new CircularBuffer(_historyMaxSize); _ioMostUsageHistory = new CircularBuffer(_historyMaxSize); _cpuMostUsageHistory = new CircularBuffer(_historyMaxSize); // Initialize deltas and history for the CPUs _cpuKernelDeltas = new Int64Delta[this.System.NumberOfProcessors]; _cpuUserDeltas = new Int64Delta[this.System.NumberOfProcessors]; _cpuOtherDeltas = new Int64Delta[this.System.NumberOfProcessors]; _cpusKernelHistory = new CircularBuffer[this.System.NumberOfProcessors]; _cpusUserHistory = new CircularBuffer[this.System.NumberOfProcessors]; _cpusOtherHistory = new CircularBuffer[this.System.NumberOfProcessors]; for (int i = 0; i < this.System.NumberOfProcessors; i++) { Int64Delta.Update(ref _cpuKernelDeltas[i], this.ProcessorPerfArray[i].KernelTime); Int64Delta.Update(ref _cpuUserDeltas[i], this.ProcessorPerfArray[i].UserTime); Int64Delta.Update( ref _cpuOtherDeltas[i], this.ProcessorPerfArray[i].IdleTime + this.ProcessorPerfArray[i].DpcTime + this.ProcessorPerfArray[i].InterruptTime ); _cpusKernelHistory[i] = new CircularBuffer(_historyMaxSize); _cpusUserHistory[i] = new CircularBuffer(_historyMaxSize); _cpusOtherHistory[i] = new CircularBuffer(_historyMaxSize); } _cpuKernelHistory.Add(0); _commitHistory.Add(0); _physicalMemoryHistory.Add(0); } public SystemProcess DpcsProcess { get { return _dpcs; } } public SystemProcess InterruptsProcess { get { return _interrupts; } } private void UpdateCb(CircularBuffer cb, T value) { if (cb.Size != _historyMaxSize) cb.Resize(_historyMaxSize); cb.Add(value); } private void UpdateList(CircularList cb, T value) { //if (cb.Max != this.HistoryMaxSize) cb.Max = this.HistoryMaxSize; cb.Add(value); } private void UpdateProcessorPerf() { int retLen; Win32.NtQuerySystemInformation( SystemInformationClass.SystemProcessorPerformanceInformation, _processorPerfBuffer, _processorPerfArraySize, out retLen ); _processorPerf = new SystemProcessorPerformanceInformation(); // Thanks to: // http://www.netperf.org/svn/netperf2/trunk/src/netcpu_ntperf.c // for the critical information: // "KernelTime needs to be fixed-up; it includes both idle & true kernel time". // This is why I love free software. for (int i = 0; i < _processorPerfArray.Length; i++) { var cpuPerf = _processorPerfBuffer.ReadStruct(0, SystemProcessorPerformanceInformation.SizeOf, i); cpuPerf.KernelTime -= cpuPerf.IdleTime + cpuPerf.DpcTime + cpuPerf.InterruptTime; _processorPerf.DpcTime += cpuPerf.DpcTime; _processorPerf.IdleTime += cpuPerf.IdleTime; _processorPerf.InterruptCount += cpuPerf.InterruptCount; _processorPerf.InterruptTime += cpuPerf.InterruptTime; _processorPerf.KernelTime += cpuPerf.KernelTime; _processorPerf.UserTime += cpuPerf.UserTime; _processorPerfArray[i] = cpuPerf; } } private void UpdatePerformance() { int retLen; Win32.NtQuerySystemInformation( SystemInformationClass.SystemPerformanceInformation, out _performance, SystemPerformanceInformation.SizeOf, out retLen ); } private ProcessQueryMessage QueryProcessStage1(int pid, string fileName, bool forced) { return QueryProcessStage1(pid, fileName, forced, true); } /// /// Stage 1 Process Querying - gets the process file name, icon and command line. /// private ProcessQueryMessage QueryProcessStage1(int pid, string fileName, bool forced, bool addToQueue) { ProcessQueryMessage fpResult = new ProcessQueryMessage { Pid = pid, Stage = 0x1 }; if (string.IsNullOrEmpty(fileName)) fileName = GetFileName(pid); fpResult.FileName = fileName; try { using (ProcessHandle queryLimitedHandle = new ProcessHandle(pid, Program.MinProcessQueryRights)) { try { // Get a handle to the process' token and get its // elevation type, and integrity. using (TokenHandle thandle = queryLimitedHandle.GetToken(TokenAccess.Query)) { try { fpResult.ElevationType = thandle.ElevationType; } catch { } try { fpResult.IsElevated = thandle.IsElevated; } catch { } // Try to get the integrity level. fpResult.Integrity = thandle.GetIntegrity(out fpResult.IntegrityLevel); } } catch { } // Is the process running under WOW64? if (OSVersion.Architecture == OSArch.Amd64) { try { fpResult.IsWow64 = queryLimitedHandle.IsWow64; } catch { } } // Get the process' job if we have KProcessHacker. if (KProcessHacker2.Instance != null) { try { using (JobObjectHandle jhandle = queryLimitedHandle.GetJobObject(JobObjectAccess.Query)) { JobObjectBasicLimitInformation limits = jhandle.BasicLimitInformation; fpResult.IsInJob = true; fpResult.JobName = jhandle.ObjectName; // This is what Process Explorer does... if (limits.LimitFlags != JobObjectLimitFlags.SilentBreakawayOk) { fpResult.IsInSignificantJob = true; } } } catch (Exception ex) { Logging.Log(ex); fpResult.IsInJob = false; fpResult.IsInSignificantJob = false; } } try { fpResult.IsInJob = queryLimitedHandle.IsInJob(); } catch { } if (pid > 4) { fpResult.CmdLine = queryLimitedHandle.CommandLine; fpResult.IsPosix = queryLimitedHandle.IsPosix; } } } catch { } if (!string.IsNullOrEmpty(fileName)) { try { fpResult.Icon = FileUtils.GetFileIcon(fileName); fpResult.LargeIcon = FileUtils.GetFileIcon(fileName, true); } catch { } try { fpResult.VersionInfo = new ImageVersionInfo(FileVersionInfo.GetVersionInfo(fileName)); } catch { } } if (addToQueue) _messageQueue.Enqueue(fpResult); WorkQueue.GlobalQueueWorkItemTag( new QueryProcessDelegate(this.QueryProcessStage1a), "process-stage1a", pid, fileName, forced ); WorkQueue.GlobalQueueWorkItemTag( new QueryProcessDelegate(this.QueryProcessStage2), "process-stage2", pid, fileName, forced ); if (this.ProcessQueryComplete != null) this.ProcessQueryComplete(fpResult.Stage, pid); return fpResult; } /// /// Stage 1A Process Querying - gets whether the process is managed. /// private ProcessQueryMessage QueryProcessStage1a(int pid, string fileName, bool forced) { ProcessQueryMessage fpResult = new ProcessQueryMessage { Pid = pid, Stage = 0x1a }; if (pid > 4) { try { fpResult.IsDotNet = false; //PhUtils.IsDotNetProcess(pid); } catch { } } _messageQueue.Enqueue(fpResult); if (this.ProcessQueryComplete != null) this.ProcessQueryComplete(fpResult.Stage, pid); return fpResult; } /// /// Stage 2 Process Querying - gets whether the process file is packed or signed. /// private ProcessQueryMessage QueryProcessStage2(int pid, string fileName, bool forced) { ProcessQueryMessage fpResult = new ProcessQueryMessage { Pid = pid, Stage = 0x2, IsPacked = false }; if (string.IsNullOrEmpty(fileName)) return null; // Don't process the file if it is too big (above 32MB). try { if ((new System.IO.FileInfo(fileName)).Length > 32 * 1024 * 1024) return null; } catch { return null; } // Find out if it's packed. // An image is packed if: // 1. It references less than 3 libraries // 2. It imports less than 5 functions // or: // 1. The function-to-library ratio is lower than 4 // (on average less than 4 functions are imported from each library) // 2. It references more than 3 libraries but less than 14 libraries. if (!string.IsNullOrEmpty(fileName) && (Settings.Instance.VerifySignatures || forced)) { try { using (MappedImage mappedImage = new MappedImage(fileName)) { int libraryTotal = mappedImage.Imports.Count; int funcTotal = 0; for (int i = 0; i < mappedImage.Imports.Count; i++) funcTotal += mappedImage.Imports[i].Count; fpResult.ImportModules = libraryTotal; fpResult.ImportFunctions = funcTotal; if ( libraryTotal < 3 && funcTotal < 5 || ((float)funcTotal / libraryTotal < 4) && libraryTotal > 3 && libraryTotal < 30 ) fpResult.IsPacked = true; } } catch (AccessViolationException) { if (pid > 4) fpResult.IsPacked = true; } catch { } } try { if (Settings.Instance.VerifySignatures || forced) { if (!string.IsNullOrEmpty(fileName)) { string uniName = global::System.IO.Path.GetFullPath(fileName).ToLowerInvariant(); // No lock needed; verify results are never removed, only added. if (!forced && _fileResults.ContainsKey(uniName)) { fpResult.VerifyResult = _fileResults[uniName]; } else { try { fpResult.VerifyResult = Cryptography.VerifyFile(fileName, out fpResult.VerifySignerName); } catch { fpResult.VerifyResult = VerifyResult.NoSignature; } if (!_fileResults.ContainsKey(uniName)) _fileResults.Add(uniName, fpResult.VerifyResult); else _fileResults[uniName] = fpResult.VerifyResult; } } } } catch { } _messageQueue.Enqueue(fpResult); if (this.ProcessQueryComplete != null) this.ProcessQueryComplete(fpResult.Stage, pid); return fpResult; } private static string GetFileName(int pid) { string fileName = null; if (pid != 4) { if (OSVersion.IsAbove(WindowsVersion.XP)) { fileName = FileUtils.GetVistaFileName(pid); } else { try { using (ProcessHandle phandle = new ProcessHandle(pid, Program.MinProcessQueryRights)) { // First try to get the native file name, to prevent PEB file name spoofing. try { fileName = phandle.ImageFileName; } catch { } // If we couldn't get it or we couldn't resolve the \Device prefix, we'll use the Win32 variant. if ((string.IsNullOrEmpty(fileName) || fileName.StartsWith("\\", StringComparison.OrdinalIgnoreCase)) && OSVersion.HasWin32ImageFileName) { fileName = phandle.GetImageFileNameWin32(); } } } catch { } } if (string.IsNullOrEmpty(fileName) || fileName.StartsWith("\\Device\\", StringComparison.OrdinalIgnoreCase)) { try { using (ProcessHandle phandle = new ProcessHandle(pid, ProcessAccess.QueryInformation | ProcessAccess.VmRead)) { // We can try to use the PEB. try { fileName = FileUtils.GetFileName(phandle.GetPebString(PebOffset.ImagePathName)); } catch { } // If all else failed, we get the main module file name. try { fileName = phandle.MainModule.FileName; } catch { } } } catch { } } } else { fileName = Windows.KernelFileName; } return fileName; } public void QueueProcessQuery(int pid) { WorkQueue.GlobalQueueWorkItemTag( new QueryProcessDelegate(this.QueryProcessStage1), "process-stage1", pid, this.Dictionary[pid].FileName, true ); } private void FillPqResult(ProcessItem item, ProcessQueryMessage result) { switch (result.Stage) { case 0x1: item.FileName = result.FileName; item.ElevationType = result.ElevationType; item.IsElevated = result.IsElevated; item.Integrity = result.Integrity; item.IntegrityLevel = result.IntegrityLevel; item.IsWow64 = result.IsWow64; item.IsInJob = result.IsInJob; item.JobName = result.JobName; item.IsInSignificantJob = result.IsInSignificantJob; item.Icon = result.Icon; item.LargeIcon = result.LargeIcon; item.VersionInfo = result.VersionInfo; item.CmdLine = result.CmdLine; item.IsPosix = result.IsPosix; break; case 0x1a: item.IsDotNet = result.IsDotNet; if (item.IsDotNet) item.IsPacked = false; break; case 0x2: item.IsPacked = !(item.IsDotNet || result.IsDotNet) && result.IsPacked; item.VerifyResult = result.VerifyResult; item.VerifySignerName = result.VerifySignerName; item.ImportFunctions = result.ImportFunctions; item.ImportModules = result.ImportModules; break; default: Logging.Log(Logging.Importance.Warning, "Unknown stage " + result.Stage.ToString("x")); break; } if (this.ProcessQueryReceived != null) this.ProcessQueryReceived(result.Stage, result.Pid); } protected override void Update() { this.UpdatePerformance(); this.UpdateProcessorPerf(); if (this.RunCount % 3 == 0) FileUtils.RefreshFileNamePrefixes(); Dictionary tsProcesses = null; var procs = Windows.GetProcesses(); Dictionary newdictionary = new Dictionary(this.Dictionary); Win32.WtsEnumProcessesFastData wtsEnumData = new Win32.WtsEnumProcessesFastData(); _cpuKernelDelta.Update(_processorPerf.KernelTime); _cpuUserDelta.Update(_processorPerf.UserTime); _cpuOtherDelta.Update( _processorPerf.IdleTime + _processorPerf.DpcTime + _processorPerf.InterruptTime); long sysKernelTime = _cpuKernelDelta.Delta; long sysUserTime = _cpuUserDelta.Delta; long otherTime = _cpuOtherDelta.Delta; if (sysKernelTime + sysUserTime + otherTime == 0) { Logging.Log(Logging.Importance.Warning, "Total systimes are 0, returning!"); return; } _ioReadDelta.Update(_performance.IoReadTransferCount); _ioWriteDelta.Update(_performance.IoWriteTransferCount); _ioOtherDelta.Update(_performance.IoOtherTransferCount); if (_processorPerf.KernelTime != 0 && _processorPerf.UserTime != 0) { this.CurrentCpuKernelUsage = (float)sysKernelTime / (sysKernelTime + sysUserTime + otherTime); this.CurrentCpuUserUsage = (float)sysUserTime / (sysKernelTime + sysUserTime + otherTime); UpdateCb(_cpuKernelHistory, this.CurrentCpuKernelUsage); UpdateCb(_cpuUserHistory, this.CurrentCpuUsage); UpdateCb(_cpuOtherHistory, (float)otherTime / (sysKernelTime + sysUserTime + otherTime)); } for (int i = 0; i < this.System.NumberOfProcessors; i++) { Int64Delta.Update(ref _cpuKernelDeltas[i], _processorPerfArray[i].KernelTime); Int64Delta.Update(ref _cpuUserDeltas[i], _processorPerfArray[i].UserTime); Int64Delta.Update(ref _cpuOtherDeltas[i], _processorPerfArray[i].IdleTime + _processorPerfArray[i].DpcTime + _processorPerfArray[i].InterruptTime); long cpuKernelTime = _cpuKernelDeltas[i].Delta; long cpuUserTime = _cpuUserDeltas[i].Delta; long cpuOtherTime = _cpuOtherDeltas[i].Delta; UpdateCb(_cpusKernelHistory[i], (float)cpuKernelTime / (cpuKernelTime + cpuUserTime + cpuOtherTime)); UpdateCb(_cpusUserHistory[i], (float)cpuUserTime / (cpuKernelTime + cpuUserTime + cpuOtherTime)); UpdateCb(_cpusOtherHistory[i], (float)cpuOtherTime / (cpuKernelTime + cpuUserTime + cpuOtherTime)); } // Prevent a massive spike in the I/O graph when the program is starting. if (this.RunCount < 3) { _ioReadDelta.Update(_ioReadDelta.Value); _ioWriteDelta.Update(_ioWriteDelta.Value); _ioOtherDelta.Update(_ioOtherDelta.Value); } UpdateCb(_ioReadHistory, _ioReadDelta.Delta); UpdateCb(_ioWriteHistory, _ioWriteDelta.Delta); UpdateCb(_ioOtherHistory, _ioOtherDelta.Delta); UpdateCb(_ioReadOtherHistory, _ioReadDelta.Delta + _ioOtherDelta.Delta); this.UpdateList(this.CommitHistory, (int)this.Performance.CommittedPages); MEMORYSTATUSEX ex = new MEMORYSTATUSEX(); if (GlobalMemoryStatusEx(ex)) this.UpdateList(this.PhysicalMemoryHistory, ex.memoryLoad); // set System Idle Process CPU time if (procs.ContainsKey(0)) { SystemProcess proc = procs[0]; proc.Process.KernelTime = _processorPerf.IdleTime; procs[0] = proc; } // add fake processes (DPCs and Interrupts) _dpcs.Process.KernelTime = _processorPerf.DpcTime; procs.Add(-2, _dpcs); _interrupts.Process.KernelTime = _processorPerf.InterruptTime; procs.Add(-3, _interrupts); float mostCPUUsage = 0; long mostIOActivity = 0; // look for dead processes foreach (int pid in Dictionary.Keys) { if (!procs.ContainsKey(pid)) { ProcessItem item = this.Dictionary[pid]; this.OnDictionaryRemoved(item); if (item.ProcessQueryHandle != null) item.ProcessQueryHandle.Dispose(); if (item.Icon != null) Win32.DestroyIcon(item.Icon.Handle); if (item.LargeIcon != null) Win32.DestroyIcon(item.LargeIcon.Handle); newdictionary.Remove(pid); } } // Receive any processing results. _messageQueue.Listen(); // look for new processes foreach (int pid in procs.Keys) { var processInfo = procs[pid].Process; if (!Dictionary.ContainsKey(pid)) { // Set up basic process information. ProcessItem item = new ProcessItem { RunId = this.RunCount, Pid = pid, Process = processInfo, SessionId = processInfo.SessionId, ProcessingAttempts = 1, Name = procs[pid].Name, // Create the delta and history managers. CpuKernelDelta = new Int64Delta(processInfo.KernelTime), CpuUserDelta = new Int64Delta(processInfo.UserTime), IoReadDelta = new Int64Delta((long)processInfo.IoCounters.ReadTransferCount), IoWriteDelta = new Int64Delta((long)processInfo.IoCounters.WriteTransferCount), IoOtherDelta = new Int64Delta((long)processInfo.IoCounters.OtherTransferCount), CpuKernelHistory = new CircularBuffer(this._historyMaxSize), CpuUserHistory = new CircularBuffer(this._historyMaxSize), IoReadHistory = new CircularBuffer(this._historyMaxSize), IoWriteHistory = new CircularBuffer(this._historyMaxSize), IoOtherHistory = new CircularBuffer(this._historyMaxSize), IoReadOtherHistory = new CircularBuffer(this._historyMaxSize), PrivateMemoryHistory = new CircularBuffer(this._historyMaxSize), WorkingSetHistory = new CircularBuffer(this._historyMaxSize) }; try { item.ProcessQueryHandle = new ProcessHandle(pid, (ProcessAccess)StandardRights.MaximumAllowed); } catch { } // HACK: Shouldn't happen, but it does - sometimes // the process name is null. if (item.ProcessQueryHandle != null) { if (string.IsNullOrEmpty(item.Name)) { try { item.Name = item.ProcessQueryHandle.MainModule.BaseName; } catch { item.Name = string.Empty; } } } // Get the process' creation time and check the parent process ID. try { item.CreateTime = DateTime.FromFileTime(processInfo.CreateTime); } catch { } if (pid > 0) { item.ParentPid = processInfo.InheritedFromProcessId; item.HasParent = true; if (!procs.ContainsKey(item.ParentPid) || item.ParentPid == pid) { item.HasParent = false; } else if (procs.ContainsKey(item.ParentPid)) { // Check the parent's creation time to see if it's actually the parent. ulong parentStartTime = (ulong)procs[item.ParentPid].Process.CreateTime; ulong thisStartTime = (ulong)processInfo.CreateTime; if (parentStartTime > thisStartTime) item.HasParent = false; } // Get the process' token's username. if (item.ProcessQueryHandle != null) { try { using (TokenHandle thandle = item.ProcessQueryHandle.GetToken(TokenAccess.Query)) { try { using (Sid sid = thandle.User) item.Username = sid.GetFullName(true); } catch { } } } catch { } } // Get a process handle with QUERY_INFORMATION access, and see if it's being debugged. if (item.ProcessQueryHandle != null) { try { item.IsBeingDebugged = item.ProcessQueryHandle.IsBeingDebugged; } catch { } } } // Update the process name if it's a fake process. switch (pid) { case 0: item.Name = "System Idle Process"; break; case -2: item.ParentPid = 0; item.HasParent = true; break; case -3: item.ParentPid = 0; item.HasParent = true; break; } // If this is not the first run, we process the item immediately. if (this.RunCount > 0) { this.FillPqResult(item, this.QueryProcessStage1(pid, null, false, false)); } else { if (pid > 0) { WorkQueue.GlobalQueueWorkItemTag( new QueryProcessDelegate(this.QueryProcessStage1), "process-stage1", pid, item.FileName, false); } } // Set the username for System Idle Process and System. if (pid == 0 || pid == 4) { // TODO: Potential localization problem. Need to create // a well-known SID and use that. item.Username = "NT AUTHORITY\\SYSTEM"; } // If we didn't get a username, try to use Terminal Services // to get the SID of the process' token's user. if (pid > 4 && string.IsNullOrEmpty(item.Username)) { if (tsProcesses == null) { // Delay loading until this point. tsProcesses = new Dictionary(); wtsEnumData = Win32.TSEnumProcessesFast(); for (int i = 0; i < wtsEnumData.PIDs.Length; i++) tsProcesses.Add(wtsEnumData.PIDs[i], wtsEnumData.SIDs[i]); } try { item.Username = Sid.FromPointer(tsProcesses[pid]).GetFullName(true); } catch { } } newdictionary.Add(pid, item); this.OnDictionaryAdded(item); } // look for modified processes else { ProcessItem item = this.Dictionary[pid]; bool fullUpdate = false; // Update process performance information. item.CpuKernelDelta.Update(processInfo.KernelTime); item.CpuUserDelta.Update(processInfo.UserTime); item.IoReadDelta.Update((long)processInfo.IoCounters.ReadTransferCount); item.IoWriteDelta.Update((long)processInfo.IoCounters.WriteTransferCount); item.IoOtherDelta.Update((long)processInfo.IoCounters.OtherTransferCount); UpdateCb(item.CpuKernelHistory, (float)item.CpuKernelDelta.Delta / (sysKernelTime + sysUserTime + otherTime)); UpdateCb(item.CpuUserHistory, (float)item.CpuUserDelta.Delta / (sysKernelTime + sysUserTime + otherTime)); UpdateCb(item.IoReadHistory, item.IoReadDelta.Delta); UpdateCb(item.IoWriteHistory, item.IoWriteDelta.Delta); UpdateCb(item.IoOtherHistory, item.IoOtherDelta.Delta); UpdateCb(item.IoReadOtherHistory, item.IoReadDelta.Delta + item.IoOtherDelta.Delta); UpdateCb(item.PrivateMemoryHistory, processInfo.VirtualMemoryCounters.PrivatePageCount.ToInt64()); UpdateCb(item.WorkingSetHistory, processInfo.VirtualMemoryCounters.WorkingSetSize.ToInt64()); // Update the struct. item.Process = processInfo; // Update CPU usage, and update PIDs with most activity. try { item.CpuUsage = (float)(item.CpuUserDelta.Delta + item.CpuKernelDelta.Delta) * 100 / (sysKernelTime + sysUserTime + otherTime); // HACK. if (item.CpuUsage > 400.0f) item.CpuUsage /= 8.0f; else if (item.CpuUsage > 200.0f) item.CpuUsage /= 4.0f; else if (item.CpuUsage > 100.0f) item.CpuUsage /= 2.0f; if (pid != 0 && item.CpuUsage > mostCPUUsage) { mostCPUUsage = item.CpuUsage; this.PidWithMostCpuUsage = pid; } if (pid != 0 && (item.IoReadDelta.Delta + item.IoWriteDelta.Delta) > mostIOActivity) { mostIOActivity = item.IoReadDelta.Delta + item.IoWriteDelta.Delta; this.PidWithMostIoActivity = pid; } } catch { } // Determine whether the process is being debugged. if (item.ProcessQueryHandle != null) { try { bool isBeingDebugged = item.ProcessQueryHandle.IsBeingDebugged; if (isBeingDebugged != item.IsBeingDebugged) { item.IsBeingDebugged = isBeingDebugged; fullUpdate = true; } } catch { } } // Processes sometimes mistakenly get labeled as packed. // Try again if it is packed. if (pid > 0) { if (item.IsPacked && item.ProcessingAttempts < 3) { WorkQueue.GlobalQueueWorkItemTag( new QueryProcessDelegate(this.QueryProcessStage2), "process-stage2", pid, item.FileName, true ); item.ProcessingAttempts++; } } if (item.JustProcessed) fullUpdate = true; // If we need a full update, call the dictionary modified // event so the process tree updates the process' // highlighting color. if (fullUpdate) { this.OnDictionaryModified(null, item); } item.JustProcessed = false; } } try { UpdateCb(_cpuMostUsageHistory, newdictionary[this.PidWithMostCpuUsage].Name + ": " + newdictionary[this.PidWithMostCpuUsage].CpuUsage.ToString("N2") + "%"); } catch { UpdateCb(_cpuMostUsageHistory, ""); } try { UpdateCb(_ioMostUsageHistory, newdictionary[this.PidWithMostIoActivity].Name + ": " + "R+O: " + Utils.FormatSize( newdictionary[this.PidWithMostIoActivity].IoReadOtherHistory[0]) + ", W: " + Utils.FormatSize( newdictionary[this.PidWithMostIoActivity].IoWriteHistory[0])); } catch { UpdateCb(_ioMostUsageHistory, ""); } UpdateCb(_timeHistory, DateTime.Now); Dictionary = newdictionary; if (wtsEnumData.Memory != null) wtsEnumData.Memory.Dispose(); } [DllImport("kernel32.dll", SetLastError = true), System.Security.SuppressUnmanagedCodeSecurity] public static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX buffer); } [StructLayout(LayoutKind.Sequential)] public class MEMORYSTATUSEX { public static readonly int SizeOf = Marshal.SizeOf(typeof(MEMORYSTATUSEX)); private int length; public int memoryLoad; public ulong totalPhys; public ulong availPhys; public ulong totalPageFile; public ulong availPageFile; public ulong totalVirtual; public ulong availVirtual; public ulong availExtendedVirtual; internal MEMORYSTATUSEX() { this.length = SizeOf; } } }