using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using SuperSocket.Common; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Metadata; namespace SuperSocket.SocketEngine { class ProcessPerformanceCounterHelper : IDisposable { private PerformanceCounter m_CpuUsagePC; private PerformanceCounter m_ThreadCountPC; private PerformanceCounter m_WorkingSetPC; private int m_CpuCores = 1; private Process m_Process; public ProcessPerformanceCounterHelper(Process process) { m_Process = process; m_CpuCores = Environment.ProcessorCount; //Windows .Net, to avoid same name process issue if (!Platform.IsMono) RegisterSameNameProcesses(process); SetupPerformanceCounters(); } private void RegisterSameNameProcesses(Process process) { foreach (var p in Process.GetProcessesByName(process.ProcessName).Where(x => x.Id != process.Id)) { p.EnableRaisingEvents = true; p.Exited += new EventHandler(SameNameProcess_Exited); } } //When find a same name process exit, re-initialize the performance counters //because the performance counters' instance names could have been changed void SameNameProcess_Exited(object sender, EventArgs e) { SetupPerformanceCounters(); } private void SetupPerformanceCounters() { var isUnix = Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX; var instanceName = string.Empty; if (isUnix || Platform.IsMono) instanceName = string.Format("{0}/{1}", m_Process.Id, m_Process.ProcessName); else instanceName = GetPerformanceCounterInstanceName(m_Process); // the process has exited if (string.IsNullOrEmpty(instanceName)) return; SetupPerformanceCounters(instanceName); } private void SetupPerformanceCounters(string instanceName) { m_CpuUsagePC = new PerformanceCounter("Process", "% Processor Time", instanceName); m_ThreadCountPC = new PerformanceCounter("Process", "Thread Count", instanceName); m_WorkingSetPC = new PerformanceCounter("Process", "Working Set", instanceName); } //This method is only used in windows private static string GetPerformanceCounterInstanceName(Process process) { var processId = process.Id; var processCategory = new PerformanceCounterCategory("Process"); var runnedInstances = processCategory.GetInstanceNames(); foreach (string runnedInstance in runnedInstances) { if (!runnedInstance.StartsWith(process.ProcessName, StringComparison.OrdinalIgnoreCase)) continue; if (process.HasExited) return string.Empty; using (var performanceCounter = new PerformanceCounter("Process", "ID Process", runnedInstance, true)) { var counterProcessId = 0; try { counterProcessId = (int)performanceCounter.RawValue; } catch //that process has been shutdown { continue; } if (counterProcessId == processId) { return runnedInstance; } } } return process.ProcessName; } public void Collect(StatusInfoCollection statusCollection) { int availableWorkingThreads, availableCompletionPortThreads; ThreadPool.GetAvailableThreads(out availableWorkingThreads, out availableCompletionPortThreads); int maxWorkingThreads; int maxCompletionPortThreads; ThreadPool.GetMaxThreads(out maxWorkingThreads, out maxCompletionPortThreads); var retry = false; while (true) { try { statusCollection[StatusInfoKeys.AvailableWorkingThreads] = availableWorkingThreads; statusCollection[StatusInfoKeys.AvailableCompletionPortThreads] = availableCompletionPortThreads; statusCollection[StatusInfoKeys.MaxCompletionPortThreads] = maxCompletionPortThreads; statusCollection[StatusInfoKeys.MaxWorkingThreads] = maxWorkingThreads; statusCollection[StatusInfoKeys.TotalThreadCount] = (int)m_ThreadCountPC.NextValue(); statusCollection[StatusInfoKeys.CpuUsage] = m_CpuUsagePC.NextValue() / m_CpuCores; statusCollection[StatusInfoKeys.MemoryUsage] = (long)m_WorkingSetPC.NextValue(); break; } catch (InvalidOperationException e) { //Only re-get performance counter one time if (retry) throw e; //Only re-get performance counter for .NET/Windows if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX || Platform.IsMono) throw e; //If a same name process exited, this process's performance counters instance name could be changed, //so if the old performance counter cannot be access, get the performance counter's name again var newInstanceName = GetPerformanceCounterInstanceName(m_Process); if (string.IsNullOrEmpty(newInstanceName)) break; SetupPerformanceCounters(newInstanceName); retry = true; } } } public void Dispose() { if (m_CpuUsagePC != null) { m_CpuUsagePC.Close(); m_CpuUsagePC = null; } if (m_ThreadCountPC != null) { m_ThreadCountPC.Close(); m_ThreadCountPC = null; } if (m_WorkingSetPC != null) { m_WorkingSetPC.Close(); m_WorkingSetPC = null; } } } }