I need to call accurate CPU usage of a single process
Asked Answered
E

1

7

The Trick is I also need to be able to do it on multicore machines. My education in C# is a tad broken. I have managed the following code. Can anyone help me out? I have 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 doesn't work if the cores don't exist. I was hoping I could come across something more flexible. I have been working on this for days hours and hours at a time and I'm 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 general fiddling 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);
Emancipator answered 11/12, 2011 at 6:24 Comment(0)
K
17

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%).

Keefe answered 11/12, 2011 at 9:20 Comment(7)
Thanks so much! I said the post was helpful Im not sure how the rep system works here so im unsure if their is anyway I can give you credit. I was wondering what do you mean by well move my whats out of my loop? Sorry in all honestly I might bombard you with questions. Like I said my education is broken when it comes to languages a few out dated books and chapters here and their.Emancipator
Hmm Sorry to bother you one last time. I tried this on a 4 core machine and higher usage processes still report usage over 100% is their a way to fix this?Emancipator
Sorry, I have not used performance counters that recently, I recently learned the fact about the 100ms thing my self and did some more research on it. I would recommend creating a small example program that can re-create the problem, then ask a new question on this site with the code of the example program so people can see what your issue is.Keefe
I edtited the original post to include my corrected changes. Program works fine now. Thanks for all the help.Emancipator
Looks good, glad I could help.The only other thing I could recommend is maybe go a little higher than Sleep(100) (110 maybe?) to make sure you cross that 100ms requirement. Also if you look in to how to do a Parallel.ForEach and put the writes to the file in the localFinally (you will need to add a little locking for the file write as multiple threads will try to write to the file at once) you do not need to wait for each process to be enumerated in order, you can get a snapshot of all of them at the same time.Keefe
@Emancipator After re-reading your answer, I don't think the Parallel is necessary, but I still think you should increase the sleep.Keefe
Thanks I'll increase the sleep.Emancipator

© 2022 - 2024 — McMap. All rights reserved.