I'm trying to display some system diagnostic information in a console app so once I know it is displayed I can send it SMTP email.
when I call this all it displays is
system.diagnoistics.performancecounter
system.diagnoistics.performancecounter
public static void GetUsageInformation()
{
cpu = new PerformanceCounter();
cpu.CategoryName = "Processor";
cpu.CounterName = "% Processor Time";
cpu.InstanceName = "_Total";
ram = new PerformanceCounter("Memory", "Available Mbytes");
Console.WriteLine(cpu);
Console.WriteLine(ram);
}
Can you provide some assistance with that I'm doing incorrectly here? I'm sure it's super simple like everything else I've run into the last few days.
What is happening here is Console.WriteLine is displaying the string representation of your PerformanceCounter objects, obtained by Console.WriteLine() calling ctr.ToString() internally, which is indeed System.Diagnostics.PerformanceCounter. What I believe you want is the string representation of the Properties of your PerformanceCounter classes.
You can either WriteLine the properties directly, ala...
Console.WriteLine(cpu.CategoryName);
Console.WriteLine(cpu.CounterName);
// etc...
Or use reflection. This will get you started...
PropertyInfo[] properties = ctr.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
Console.Write(property.Name + ":\t");
Console.WriteLine(property.GetValue(ctr).ToString());
}
Related
I'm using this right now: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process?view=net-6.0
but I cannot limit the process memory and CPU usage
public static void Launch(string[] args)
{
// make sure installPath is in double perentheses
string steamPath = config["steamPath"].ToString();
string arg = "";
foreach (string s in args){arg += $"{s} ";}
Process ExternalProcess = new Process();
ExternalProcess.StartInfo.FileName = steamPath;
ExternalProcess.StartInfo.Arguments = arg;
ExternalProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
ExternalProcess.Start();
ExternalProcess.WaitForExit();
ExternalProcess.Close();
}
If setting constraints on memory usage are crucial, you might look into github.com/lowleveldesign/process-governor. It's a commandline tool, so it should be relatively easy to be executed by your C# program. It also allows limiting of CPU usage as well, so might perhaps be a one-stop solution you might be looking for... –
MySkullCaveIsADarkPlace
I am integrating IronPython scripts to run under a C# engine. The C# engine builds a dictionary of 'ScriptValue' objects and passes it to the IronPython Script which then uses the objects to do calculations. The 'ScriptValue' object is in a separate class library and implements 'MarshalByRefObject' and is a simple .net object (stores double and bool values only). The script run happens frequently.
First Attempt:
I instantiated the IronPython engine and ran the scripts. As the runs progress I could see that memory usage was increasing at a fast rate. Eventually after a day or running the application crashed with an out of memory exception. I tried both keeping an instance of the IronPythonEngine alive and restarting a new instance on each run. I also tried shutting down the IronPython Engine but memory would increase consistently.
Second Attempt:
After researching this a lot, suggestions came up to try running the engine in a separate AppDomain and unloading the AppDomain once you are done running the scripts. I then implemented this and create a new AppDomain and unload it once a run has completed. This appears to help to a certain degree but the memory leak persists albeit it creeps up at a slower rate.
I did various memory profiling and it seems like IronPython or somewhere in DLR land unmanaged memory is not getting freed and this creeps up overtime. Managed memory seems to be getting cleared as the AppDomain is unloaded.
The C# engine itself is rather complex and interacts with MS SQL, IronPython, a Data Historian and an Asset Database. I won't go into the specifics of this as I have been able to recreate the issue by taking out all the additional components into a simple Windows Forms application.
The code I am running at the moment under a timer is:
private void RunEngine()
{
ScriptEngine pythonEngine = null;
AppDomain sandbox = null;
ScriptSource source = null;
ScriptScope scope = null;
dynamic subClass = null;
ObjectOperations ops = null;
dynamic instance = null;
dynamic result = null;
Dictionary<string, ScriptValue> scriptInputValues = GetIronPythonScriptInputAttributeValues();
Dictionary<string, ScriptValue> scriptOutputValues = GetIronPythonScriptOutputAttributes();
// Setup PythonEngine options
Dictionary<string, object> options = new Dictionary<string, object>();
//options["Debug"] = ScriptingRuntimeHelpers.True;
options["ExceptionDetail"] = ScriptingRuntimeHelpers.True;
options["ShowClrExceptions"] = ScriptingRuntimeHelpers.True;
// Create a sandbox to run the IronPython scripts in
sandbox = AppDomain.CreateDomain("IronPythonSandbox",
AppDomain.CurrentDomain.Evidence,
new AppDomainSetup() { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory, ApplicationName = "IronPythonSandbox" },
new PermissionSet(PermissionState.Unrestricted));
// Create the python engine
pythonEngine = Python.CreateEngine(sandbox, options);
source = pythonEngine.CreateScriptSourceFromFile(#"\\server2\Projects\Customer\Development\Scripts\calculation.py");
var compiled = source.Compile();
scope = pythonEngine.CreateScope();
//source.Execute(scope);
compiled.Execute(scope);
subClass = scope.GetVariableHandle("Calculate");
ops = pythonEngine.Operations;
instance = ops.Invoke(subClass, scriptInputValues, scriptOutputValues);
result = instance.Unwrap();
if (scriptInputValues?.Count > 0) { scriptInputValues.Clear(); scriptInputValues = null; }
if (scriptOutputValues?.Count > 0) { scriptOutputValues.Clear(); scriptOutputValues = null; }
result = null;
instance = null;
ops = null;
subClass = null;
scope = null;
source = null;
pythonEngine?.Runtime?.Shutdown();
pythonEngine = null;
if (sandbox != null) { AppDomain.Unload(sandbox); }
sandbox = null;
}
I have stripped down the script into bare bones now to test the memory issue and it is like this and does not carry out any actual calculations as such.
import clr
import sys
# Import integration library to allow for access to the required .Net object types
sys.path.append(r"C:\Program Files\Company\RTCM Worker") # Include the path to the .Net Library
clr.AddReference('RTCM.Worker.IPy.Integration.Library.dll')
import RTCM.Worker.IPy.Integration.Library
import System
from System.Collections.Generic import Dictionary
sys.path.append(r"\\server2\Projects\Customer\Development\Scripts") # Include the path to the module
from constants import *
from sharedfunctions import *
import math
def Calculate(scriptInputValues, scriptOutputValues):
returnValue = True
try:
# Parameter validations
if returnValue: # Only proceed with the calculation if all inputs are valid
## Script logging related objects
#ENABLE_SCRIPTLOGGING = scriptOutputValues[C_EnableScriptLogging].Value
#SCRIPT_LOG = scriptOutputValues[C_ScriptLog].Value
# Get all the required input parameter values
AMB_TEMP = scriptInputValues[C_AmbientTemperature].Value
GND_AIR = scriptInputValues[C_GroundAir].Value
MAX_DESIGN_TEMP = scriptInputValues[C_MaximumDesignTemperature].Value
g = scriptInputValues[C_RatingCalculationConstants_g].Value
CONDUCTOR_DIA = scriptInputValues[C_ConductorDIA].Value
WIND_SPEED = scriptInputValues[C_WindSpeed].Value # From lookup table and no conversion needed as this is in m/s
DEFAULT_WIND_ANGLE = scriptInputValues[C_WindBearing].Value
SIGMA = scriptInputValues[C_Rating_Calculation_Constants_SIGMA].Value
CONDUCTOR_EMISSIVITY = scriptInputValues[C_ConductorEmissivity].Value
SOLAR_ABSORPTION = scriptInputValues[C_SolarAbsorption].Value
SOLAR_DIRECT = scriptInputValues[C_SolarDirect].Value
GROUND_REFLECTIVITY = scriptInputValues[C_GroundReflectivity].Value
SOLAR_DIFFUSE = scriptInputValues[C_SolarDiffuse].Value
CONDUCTOR_SKIN_EFFECT = scriptInputValues[C_ConductorSkinEffect].Value
CONDUCTOR_MAG_EFFECT = scriptInputValues[C_ConductorMAGEffect].Value
CONDUCTOR_DC_RESISTANCE = scriptInputValues[C_ConductorDCResistance].Value
CONDUCTOR_ALPHA = scriptInputValues[C_ConductorAlpha].Value
# Destroy all referenced objects
del AMB_TEMP
del GND_AIR
del MAX_DESIGN_TEMP
del g
del CONDUCTOR_DIA
del WIND_SPEED
del DEFAULT_WIND_ANGLE
del SIGMA
del CONDUCTOR_EMISSIVITY
del SOLAR_ABSORPTION
del SOLAR_DIRECT
del GROUND_REFLECTIVITY
del SOLAR_DIFFUSE
del CONDUCTOR_SKIN_EFFECT
del CONDUCTOR_MAG_EFFECT
del CONDUCTOR_DC_RESISTANCE
del CONDUCTOR_ALPHA
del scriptInputValues
del scriptOutputValues
returnValue = True
except System.Exception as ex:
returnValue = False
return returnValue;
Some screenshots of memory creeping up over time and you will notice unmanaged memory is creeping up:
Start of run
some time later
some time later
I am running out of options now. Are there any other suggestions on things to try?
A few other things I tried:
Setting LightweightScopes to true and it did not help.
Deleting objects referenced in the IronPython script using the del keyword and it did not help.
Let me know if you want to know any additional details around my setup.
I had the exact same issue with memory leakage per execution of an IronPython 2.7.5 script within a C# engine.
You should manually disconnect from your MarshalByRef objects at the end of each script execution or else you may be holding on to objects. If within your MarshalByRef object(s), you have overriden the InitializeLifetimeService to prevent remoting exceptions, it is mandatory to manually disconnect as shown here:
System.Runtime.Remoting.RemotingServices.Disconnect(MarshalByRefObj obj)
Hopefully you've had success with removing Debug options from the engine, I'd like to know if that worked for you.
I am currently looking for a way to get the current CPU/RAM/Disk usage in a C# web application using .NET CORE.
For CPU and ram usage, I use PerformanceCounter Class from System.Diagnostics.
This is the codes:
PerformanceCounter cpuCounter;
PerformanceCounter ramCounter;
cpuCounter = new PerformanceCounter();
cpuCounter.CategoryName = "Processor";
cpuCounter.CounterName = "% Processor Time";
cpuCounter.InstanceName = "_Total";
ramCounter = new PerformanceCounter("Memory", "Available MBytes");
public string getCurrentCpuUsage(){
cpuCounter.NextValue()+"%";
}
public string getAvailableRAM(){
ramCounter.NextValue()+"MB";
}
For disk usage, I am using the DriveInfo class. This is the codes:
using System;
using System.IO;
class Info {
public static void Main() {
DriveInfo[] drives = DriveInfo.GetDrives();
foreach (DriveInfo drive in drives) {
//There are more attributes you can use.
//Check the MSDN link for a complete example.
Console.WriteLine(drive.Name);
if (drive.IsReady) Console.WriteLine(drive.TotalSize);
}
}
}
Unfortunately .NET Core does not support the DriveInfo and PerformanceCounter classes, hence the code above do not work.
Does anyone know how I can get the current CPU/RAM/Disk usage in a C# web application using .NET CORE?
You can use PerformnceCounter in the System.Diagnostics.PerformanceCounter package
for example, the next code will give you the total processor usage percent
var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total",true);
var value = cpuCounter.NextValue();
//Note: In most cases you need to call .NextValue() twice to be able to get the real value
if (Math.Abs(value) <= 0.00)
value = cpuCounter.NextValue();
Console.WriteLine(value);
you can do the same for all OS registered Performance Counters.
Update:
I'm not sure if there is something I should do after creating a new instance of the PerformanceCounter class, but sometimes when I get the next value it comes as 0.
So I've decided to make one instance of PerformanceCounter in at the application level.
e.g.
public static class DiagnosticHelpers
{
static float _systemCPU;
public static float SystemCPU
{
get
{
lock (locker)
{
return _systemCPU;
}
}
}
private static readonly object locker = new object();
static DiagnosticHelpers()
{
SystemCPU = 0;
Task.Run(() =>
{
var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total", true);
cpuCounter.NextValue(); //prime the counter
while (true)
{
Thread.Sleep(1000); /wait at least 1 second before the first real read
lock (locker)
{
_systemCPU = cpuCounter.NextValue();
}
}
});
}
}
Processor information is available via System.Diagnostics:
var proc = Process.GetCurrentProcess();
var mem = proc.WorkingSet64;
var cpu = proc.TotalProcessorTime;
Console.WriteLine("My process used working set {0:n3} K of working set and CPU {1:n} msec",
mem / 1024.0, cpu.TotalMilliseconds);
DriveInfo is available for Core by adding the System.IO.FileSystem.DriveInfo package
For Windows i'm using this
var memorieLines= GetWmicOutput("OS get FreePhysicalMemory,TotalVisibleMemorySize /Value").Split("\n");
var freeMemory= memorielines[0].Split("=", StringSplitOptions.RemoveEmptyEntries)[1];
var totalMemory = memorielines[1].Split("=", StringSplitOptions.RemoveEmptyEntries)[1];
var cpuLines = GetWmicOutput("CPU get Name,LoadPercentage /Value").Split("\n");
var CpuUse = cpuLines[0].Split("=", StringSplitOptions.RemoveEmptyEntries)[1];
var CpuName = cpuLines[1].Split("=", StringSplitOptions.RemoveEmptyEntries)[1];
private string GetWmicOutput(string query, bool redirectStandardOutput = true)
{
var info = new ProcessStartInfo("wmic");
info.Arguments = query;
info.RedirectStandardOutput = redirectStandardOutput;
var output = string.Empty;
using (var process = Process.Start(info))
{
output = process.StandardOutput.ReadToEnd();
}
return output.Trim();
}
For the disk infos you can use this query :
LOGICALDISK get Caption,DeviceID,FileSystem,FreeSpace,Size /Value
if you want a better output formatting give a look to this article : https://www.petri.com/command-line-wmi-part-3
Add this nuget package to your project by double clicking project.
<ItemGroup>
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="6.0.0" />
</ItemGroup>
When you run the code, you will get an error like below.
Performance counters cannot be initialized! System.UnauthorizedAccessException: Access to the registry key 'Global' is denied.
To solve this error, you have to add your application pool user to "Performance Monitor Users" group.
Open command line in administrator mode, then run this command.
net localgroup "Performance Monitor Users" "IIS APPPOOL\MYAPPPOOL" /add
MYAPPPOOL will be replaced with your real app pool name.
Then restart the machine if iis restart does not solve.
I want to make a control which is very similar to the one used in Windows Task Manager to show the CPU Performance something like a changing vertical Guage
You can get the CPU usage values via ManagementObjectSearcher using WMI
To get the value, you do something like this
var info = ManagementObjectSearcher(#"\\localhost\root\CIMV2","SELECT * FROM Win32_PerfFormattedData_PerfOS_Processor WHERE Name=\"_Total\"");
ManagementObject queryObj = info.Get().Cast<ManagementObject>().First();
var cpuUsage = Convert.ToInt32(queryObj["PercentIdleTime"]);
Alternatively you can use the System.Diagnostics PerformanceCounter instead
PerformanceCounter cpu = new PerformanceCounter(){
CategoryName = "Processor",
CounterName = "% Processor Time",
InstanceName = "_Total"
};
var yourCpuUsageValue = cpu.NextValue();
Got it...used a Progress Bar to show the values and used PerformanceCounter to get the cpu usage values.
Thanks
Guys, I have the following code:
using System.Diagnostics;
private PerformanceCounter diskRead = new PerformanceCounter();
private PerformanceCounter diskWrite = new PerformanceCounter();
diskRead.CategoryName = "PhysicalDisk";
diskRead.CounterName = "Disk Reads/sec";
diskRead.InstanceName = "_Total";
diskWrite.CategoryName = "PhysicalDisk";
diskWrite.CounterName = "Disk Writes/sec";
diskWrite.InstanceName = "_Total";
This code keeps track of Disk Reads per second and Disk Writes per second and it works fine. My question is, how do I keep track of reads and writes of one specific drive? I have 3 hard drives in my computer and right now its returning a total of all 3 drives combined. How can I specifically chose which drive I want to monitor?
You should replace "_Total" with the appropriate drive number:
diskRead.InstanceName = "0";
Should've checked that. You need to specify the name like "0 C: D:". Yikes.
Edit 2:
You can get the names with
var cat = new System.Diagnostics.PerformanceCounterCategory("PhysicalDisk");
var instNames = cat.GetInstanceNames();
And it is probaly safe to filter out the names that start with a number. (_Total is also in the list).
Use a specific InstanceName, not _Total. Use Perfmon.exe to find the instance names.