I need to call accurate CPU usage of a single process

Solaris17 picture Solaris17 · Dec 11, 2011 · Viewed 12.9k times · Source

The Trick is I also need to be able to do it on multi core machines. My education in C# is a tad broken. I have managed the following code. Can anyone help me out? Iv tried using the "_Total" flag and I have tried modifying some other code snippets that looked like they tried to detect the amount of cores. I was told however they did not include HT and only supported physical not logical processors. I was trying to get it to do both. Apparently their is a way to manually do this using

    ("Process", "% Processor Time", "1" process.ProcessName))
    ("Process", "% Processor Time", "2" process.ProcessName))
    ("Process", "% Processor Time", "3" process.ProcessName))

etc. But I have found out the hardware that doesnt work if the cores dont exist. I was hoping I could come across something more flexible. Iv been working on this for days hours and hours at a time and im going to pull my hair out.

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Collections;
using System.IO;

namespace Program_CPU_Monitor
{
    class Program
    {
        static void Main(string[] args)
        {
            StreamWriter log;
            log = File.AppendText("c:\\CPUMON.txt");
            log.WriteLine("");
            log.WriteLine("**Started logging Program CPU Monitor (2.6.0.63)**");
            log.Close();
            Console.Title = "Program CPU Monitor 2.6.0.63";
            Console.WriteLine("Monitoring Program CPU & Memory usage...(1-min intervals)");
            Console.WriteLine("Monitoring will start when Program is detected as running.");
            Console.WriteLine("Please type in program name without the '.EXE', For example 'TESV' or 'calc'.");
            Console.WriteLine("The program name is case sensative. Without the proper case it will not work.");
            Console.WriteLine("This program will leave a log of the display called 'CPUMON.txt' on drive C:/.");
            Console.WriteLine("Please type program name...");
            Console.WriteLine(""); 
            string procName = Console.ReadLine();

            while (true)
            {

                Process[] runningNow = Process.GetProcesses();

                foreach (Process process in runningNow)
                {
                    using (PerformanceCounter pcProcess = new PerformanceCounter("Process", "% Processor Time", process.ProcessName))
                    using (PerformanceCounter memProcess = new PerformanceCounter("Memory", "Available MBytes"))
                    {
                        if (process.ProcessName == procName)
                        {
                            pcProcess.NextValue();
                            Thread.Sleep(60000);
                            StreamWriter OurStream;
                            OurStream = File.AppendText("c:\\CPUMON.txt");
                            Console.WriteLine("");
                            OurStream.WriteLine("");
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("Process: '{0}' CPU Usage: {1}%", process.ProcessName, pcProcess.NextValue());
                            OurStream.WriteLine("Process: '{0}' CPU Usage: {1}%", process.ProcessName, pcProcess.NextValue());
                            Console.ForegroundColor = ConsoleColor.Green;
                            Console.WriteLine("Process: '{0}' RAM Free: {1}MB", process.ProcessName, memProcess.NextValue());
                            OurStream.WriteLine("Process: '{0}' RAM Free: {1}MB", process.ProcessName, memProcess.NextValue());
                            Console.ForegroundColor = ConsoleColor.Cyan;
                            Console.WriteLine(string.Format("Recorded: '{0}' at {1}", procName, DateTime.Now.ToString()));
                            OurStream.WriteLine(string.Format("Recorded: '{0}' at {1}", procName, DateTime.Now.ToString()));
                            OurStream.Close();

                        }
                    }
                }

            }

        }

    }

}

EDIT:: I made the following changes to the code to fix my issues per advice and genereal fiddeling around.

    foreach (Process process in runningNow)
            {
                using (PerformanceCounter cpuUsage = new PerformanceCounter("Process", "% Processor Time", "_Total"))
                using (PerformanceCounter pcProcess = new PerformanceCounter("Process", "% Processor Time", process.ProcessName))
                using (PerformanceCounter memProcess = new PerformanceCounter("Memory", "Available MBytes"))
                {
                    if (process.ProcessName == procName)
                    {
                        StreamWriter OurStream;
                        OurStream = File.AppendText("c:\\CPUMON.txt");
                        Console.WriteLine("");
                        OurStream.WriteLine("");

                        // Prime the Performance Counters
                        pcProcess.NextValue();
                        cpuUsage.NextValue();
                        Thread.Sleep(100);
                        isprimed = true;

                        double cpuUse = Math.Round(pcProcess.NextValue() / cpuUsage.NextValue() * 100, 2);

                        // Check for Not-A-Number (Division by Zero)
                        if (Double.IsNaN(cpuUse))
                            cpuUse = 0;

                        //Get CPU Usage
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("Process: `{0}' CPU Usage: {1}%", process.ProcessName, Convert.ToInt32(cpuUse));
                        OurStream.WriteLine("Process: `{0}' CPU Usage: {1}%", process.ProcessName, Convert.ToInt32(cpuUse));

                        // Get Process Memory Usage
                        Console.ForegroundColor = ConsoleColor.Green;
                        double memUseage = process.PrivateMemorySize64 / 1048576;
                        Console.WriteLine("Process: `{0}' Memory Usage: {1}MB", process.ProcessName, memUseage);
                        OurStream.WriteLine("Process: `{0}' Memory Usage: {1}MB", process.ProcessName, memUseage);

                        // Get Total RAM free
                        Console.ForegroundColor = ConsoleColor.Cyan;
                        float mem = memProcess.NextValue();
                        Console.WriteLine("During: `{0}' RAM Free: {1}MB", process.ProcessName, mem);
                        OurStream.WriteLine("During: `{0}' RAM Free: {1}MB", process.ProcessName, mem);

                        //Record and close stream
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        System.DateTime newDate = System.DateTime.Now;
                        Console.WriteLine("Recorded: {0}", newDate);
                        OurStream.WriteLine("Recorded: {0}", newDate);
                        OurStream.Close();
                        Thread.Sleep(59900);

Answer

Scott Chamberlain picture Scott Chamberlain · Dec 11, 2011

You can only read the performance counters every 100 ms or the timesilce will be too small for it to get a accurate reading, if you read more than once every 100ms it will always report 0 or 100% usage. Because you call NextValue() twice (once for the file, once for your stream) the second reading will be the usage since the previous reading the line before.

Change your code to this:

foreach (Process process in runningNow.Where(x => x.ProcessName == procName)
{
    using (PerformanceCounter pcProcess = new PerformanceCounter("Process", "% Processor Time", process.ProcessName))
    using (PerformanceCounter memProcess = new PerformanceCounter("Memory", "Available MBytes"))
    {
        pcProcess.NextValue();
        Thread.Sleep(60000);
        StreamWriter OurStream;
        OurStream = File.AppendText("c:\\CPUMON.txt");
        Console.WriteLine("");
        OurStream.WriteLine("");
        Console.ForegroundColor = ConsoleColor.Red;
        float cpuUseage = pcProcess.NextValue();
        Console.WriteLine("Process: '{0}' CPU Usage: {1}%", process.ProcessName, cpuUseage);
        OurStream.WriteLine("Process: '{0}' CPU Usage: {1}%", process.ProcessName, cpuUseage);
        Console.ForegroundColor = ConsoleColor.Green;
        float memUseage = memProcess.NextValue();
        Console.WriteLine("Process: '{0}' RAM Free: {1}MB", process.ProcessName, memUseage);
        OurStream.WriteLine("Process: '{0}' RAM Free: {1}MB", process.ProcessName, memUseage);
    }
}

There may be other issues causing you problems but calling NextValue twice is the first one that jumped out at me.


Explanation:

The reason behind NextValue only reporting 0 or 100% when you request NextValue too fast is the fact that if you are currently executing code or not is a boolean factor.

So what the performance counter is doing is asking the question:

Between the last time the performance counter took a reading and right now, what % of time slices had code executing from the process X?

The size of those time slices the performance counter works with is 100ms so if you go below 100ms you are basically asking

Did the last time slice that was recorded by the performance counter have code from the process X executing?

and the only two answers you can get to that question are "No" (0%) or "Yes" (100%).