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;
}
}
}