using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; namespace LiteFccCoreMain { public class PerformanceMonitor { //static System.Timers.Timer performanceLoggingTimer; public static ILogger Logger { get; set; }// = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("Performance"); /// /// by seconds, default to 60, may get changed by different log level. /// static int samplingInterval = 60; private static TimeSpan? previousTotalCpuTime; private static void Linux_LoggingPerformanceCounter() { // list extra info only for RPi3B+ //logger.Info(Bash("/opt/vc/bin/vcgencmd measure_temp")); //logger.Info(Bash("/opt/vc/bin/vcgencmd measure_clock arm")); //logger.Info(Bash("/opt/vc/bin/vcgencmd measure_volts core")); // list all processes in RPi3B+ //logger.Info(Bash("/usr/bin/top -n 1")); double cpuUsage = 0; var proc = Process.GetCurrentProcess(); if (previousTotalCpuTime.HasValue) { cpuUsage = proc.TotalProcessorTime .Subtract(previousTotalCpuTime.Value).TotalSeconds / samplingInterval; } previousTotalCpuTime = proc.TotalProcessorTime; var workingSet = proc.WorkingSet64; var npSystemMem = proc.NonpagedSystemMemorySize64; var pagedMem = proc.PagedMemorySize64; var pagedSysMem = proc.PagedSystemMemorySize64; var privateMem = proc.PrivateMemorySize64; var virMem = proc.VirtualMemorySize64; var cpu = proc.TotalProcessorTime; Logger.LogInformation("Proc: " + proc.ProcessName + "(Id: " + proc.Id + "), " + "CPU%: " + cpuUsage.ToString("0.##%") + ", workingSet: " + (workingSet / 1024.0) + "K, " + "nonPagedSysMem: " + (npSystemMem / 1024.0) + "K, " + "pagedMem: " + (pagedMem / 1024.0) + "K, " + "pagedSysMem: " + (pagedSysMem / 1024.0) + "K, " + "privateMem: " + (privateMem / 1024.0) + "K, " + "virMem: " + (virMem / 1024.0) + "K, " + "CPU total time(ms): " + cpu.TotalMilliseconds + ", handle: " + proc.HandleCount + ", thread: " + proc.Threads.Count); } private static void Windows_LoggingPerformanceCounter() { double cpuUsage = 0; var proc = Process.GetCurrentProcess(); if (previousTotalCpuTime.HasValue) { cpuUsage = proc.TotalProcessorTime .Subtract(previousTotalCpuTime.Value).TotalSeconds / samplingInterval; } previousTotalCpuTime = proc.TotalProcessorTime; int availableThreads, availableIOThreads; ThreadPool.GetAvailableThreads(out availableThreads, out availableIOThreads); var currentProcessRunningInfo = Windows_GetProcessRunningInformation(proc.ProcessName); currentProcessRunningInfo.Add("CPU%", cpuUsage.ToString("0.##%")); currentProcessRunningInfo.Add("Avai Threads-IoThreads", availableThreads.ToString() + "-" + availableIOThreads.ToString()); Logger.LogInformation(currentProcessRunningInfo.Select(s => s.Key + ": " + s.Value) .Aggregate((p, n) => p + ", " + n)); } private static Dictionary Windows_GetProcessRunningInformation(string processName) { var returnDic = new Dictionary(); Process operatedProcess = null; try { Process[] processes = System.Diagnostics.Process.GetProcessesByName(processName); // if process found, then only pick the first one as default, or set with default Null value if (processes.Length > 0) { operatedProcess = processes[0]; } if (operatedProcess != null && !operatedProcess.HasExited) { returnDic.Add("ProcName", operatedProcess.ProcessName + "(Id:" + operatedProcess.Id + ")"); returnDic.Add("VM", operatedProcess.VirtualMemorySize64.ToString()); returnDic.Add("WS", operatedProcess.WorkingSet64.ToString()); returnDic.Add("PM", operatedProcess.PrivateMemorySize64.ToString()); returnDic.Add("Thread", operatedProcess.Threads.Count.ToString()); returnDic.Add("Handle", operatedProcess.HandleCount.ToString()); //returnDic.Add("Start time", operatedProcess.StartTime.ToString("HH:mm:ss dd-MM-yyyy")); } else { returnDic.Add("Process " + processName, "not existed"); } } catch (Exception ex) { Logger.LogInformation("Exception occured in PerformanceMonitor, detail: " + ex.ToString()); returnDic.Add("GetProcessRunningInformation error", ex.ToString()); } return returnDic; } public static void Start() { /* as in some case of Threadpool drain out, the System.Timer for logging the counters may get paused, * so here using a dedicate thread to do it. * */ Thread dedicatedLoopThread = new Thread(() => { while (true) { try { if (Environment.OSVersion.Platform == PlatformID.Unix) Linux_LoggingPerformanceCounter(); else Windows_LoggingPerformanceCounter(); if (Logger.IsEnabled(LogLevel.Debug)) samplingInterval = 2; else samplingInterval = 60; Thread.Sleep(1000 * samplingInterval); } catch (Exception exxx) { Logger.LogError("Unexpected exception: " + exxx); } } }); int minThreads, minIoThreads, maxThreads, maxIoThreads; ThreadPool.GetMinThreads(out minThreads, out minIoThreads); ThreadPool.GetMaxThreads(out maxThreads, out maxIoThreads); Logger.LogInformation("Performance Logging is starting, ThreadPool MinThreads count: " + minThreads + ", MinIoThreads count: " + minIoThreads + ", MaxThreads count: " + maxThreads + ", MaxIoThreads count: " + maxIoThreads); dedicatedLoopThread.Start(); } public static string Bash(string cmd) { var escapedArgs = cmd.Replace("\"", "\\\""); var process = new Process() { StartInfo = new ProcessStartInfo { FileName = "/bin/bash", Arguments = $"-c \"{escapedArgs}\"", RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true, } }; process.Start(); string result = process.StandardOutput.ReadToEnd(); process.WaitForExit(); return result; } } }