3

This is my function for enumerating processes on windows box and calculating percentage of CPU usage for each process but results are not correct.

CPU usage does't add up to 100% but more like to 120% or 130% and I don't know what I'm doing wrong. It seems like it calculats right CPU usage for varoius apps like firefox, VS2010, office,.. but has problems with System Idle Process.

public List<ProcInfo> GetRunningProcesses()
{
    List<ProcInfo> allProcesses = new List<ProcInfo>();
    UInt64 currentProcessCpuTime = 0;
    UInt64 allProcessCpuTime = 0;

    SelectQuery wmiQuery = new SelectQuery("SELECT Name, Description, ProcessId, KernelModeTime, UserModeTime FROM Win32_Process");
    ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(connectionScope, wmiQuery);
    ManagementObjectCollection moc = oSearcher.Get();

    foreach (ManagementObject mo in moc)
    {
        allProcessCpuTime += (UInt64)mo["KernelModeTime"] + (UInt64)mo["UserModeTime"];
    }

    foreach (ManagementObject mo in moc)
    {
        currentProcessCpuTime = (UInt64)mo["KernelModeTime"] + (UInt64)mo["UserModeTime"];
        allProcesses.Add(new ProcInfo((string)mo["Name"], (string)mo["Description"], (UInt32)mo["ProcessId"], (currentProcessCpuTime / (double)allProcessCpuTime * 100));
    }

    return allProcesses;
}

EDIT:

I found that my function is all wrong.

I'm starting a bounty for the best working solution. Solution needs to work for local and remote system and should be fast.

Primoz
  • 4,079
  • 17
  • 56
  • 67
  • If you are not forced to use WMI, there is another way using perf counters: http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c – Simon Mourier Dec 15 '10 at 21:40
  • 1
    Is it possible that between iterations of your loops the values are changing? That is, one item has processor time at one point of the loop and then another gets time during the next iteration? Just a thought... – Mark Avenius Dec 15 '10 at 21:44
  • I agree with Mark - you should take a snapshot of the perfomance indicators (preferably in parallel if you can to maximize your chance of getting them all at the same time) THEN do math on them. They are in a state of flucuation. – Ryan Bennett Dec 15 '10 at 21:55
  • @Simon Mourier: I need to query remote machines, can I do this using perf counters ? @Mark Avenius: As far as I know, when I call oSearcher.Get(); the values are set in stone and they don't refresh and change. – Primoz Dec 15 '10 at 22:02
  • Sure you can, if you have proper rights (same with WMI or anything else by the way, security is somewhat consistent :-). – Simon Mourier Dec 15 '10 at 22:04
  • If WMI works, probably perf counters will work too. Am I right ? – Primoz Dec 15 '10 at 22:38

3 Answers3

3

Here is a C# code with performance counters:

public static void DumpProcessesCpu(string machineName)
{
    List<PerformanceCounter> counters = new List<PerformanceCounter>();

    foreach (Process process in Process.GetProcesses(machineName))
    {
        PerformanceCounter processorTimeCounter = new PerformanceCounter(
            "Process",
            "% Processor Time",
            process.ProcessName,
            machineName);

        processorTimeCounter.NextValue();
        counters.Add(processorTimeCounter);
    }

    Thread.Sleep(1000); // 1 second wait, needed to get a sample

    foreach (PerformanceCounter processorTimeCounter in counters)
    {
        Console.WriteLine("Process:{0} CPU% {1}",
            processorTimeCounter.InstanceName,
            processorTimeCounter.NextValue());
    }
}

It's inspired from here: https://learn.microsoft.com/en-us/archive/blogs/bclteam/how-to-read-performance-counters-ryan-byington You can't really be faster than this, the reason why is explained in the article. Basically, you'll have to read the value twice to get it right, so you need to wait between samples.

However, depending on what you want to do, for example, suppose you want to write a "remote task manager", you can code all this in a background task (thread) and regularly update the values so the end-user will not really see the delay between samples.

Michel de Ruiter
  • 7,131
  • 5
  • 49
  • 74
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
1

Here is a C# block of code tested and validated and thanks to fejesjoco, I used his code and made the test to get it to work.

public class CPUUtilizationTests
{

    [Test]
    public void TestPercentProcessorTime()
    {
        Assert.That(PercentProcessorTime("Idle"), Is.Not.GreaterThan(100.0));
    }

    public float PercentProcessorTime(string processName)
    {
        var mos = new ManagementObjectSearcher("SELECT * FROM Win32_PerfRawData_PerfProc_Process");
        var run1 = mos.Get().Cast<ManagementObject>().ToDictionary(mo => mo.Properties["Name"].Value, mo => mo);
        System.Threading.Thread.Sleep(1000); // can be an arbitrary number
        var run2 = mos.Get().Cast<ManagementObject>().ToDictionary(mo => mo.Properties["Name"].Value, mo => mo);

        if (!run2.ContainsKey(processName)) throw new Exception(string.Format("Process not found: {0}", processName));

        string percentageProcessorTime = "PercentProcessorTime";
        string total = "_Total";

        ulong percentageDiff = (ulong)run2[processName][percentageProcessorTime] - (ulong)run1[processName][percentageProcessorTime];
        ulong totalDiff = (ulong)run2[total][percentageProcessorTime] - (ulong)run1[total][percentageProcessorTime];

        return ((float)percentageDiff / (float)totalDiff)*100.0f;
    }

}
1
        var mos = new ManagementObjectSearcher("SELECT * FROM Win32_PerfRawData_PerfProc_Process");
        var run1 = mos.Get().Cast<ManagementObject>().ToDictionary(mo => mo.Properties["Name"].Value, mo => (ulong)mo.Properties["PercentProcessorTime"].Value);
        Thread.Sleep(570); // can be an arbitrary number
        var run2 = mos.Get().Cast<ManagementObject>().ToDictionary(mo => mo.Properties["Name"].Value, mo => (ulong)mo.Properties["PercentProcessorTime"].Value);

        var total = run2["_Total"] - run1["_Total"];

        foreach (var kvp in run1)
        {
            var proc = kvp.Key;
            var p1 = kvp.Value;
            if (run2.ContainsKey(proc))
            {
                var p2 = run2[proc];
                Console.WriteLine("{0}: {1:P}", proc, (double)(p2 - p1) / total);
            }
        }
fejesjoco
  • 11,763
  • 3
  • 35
  • 65
  • +1, Really helpful specially when `PerformanceCounter` can have terrible performance: https://stackoverflow.com/questions/37819786/why-is-obtaining-process-related-data-using-performance-counters-causing-so-many – MaYaN Jun 14 '16 at 23:03
  • Please don't post code without an explanation, why are you taking two readings ? – kofifus Sep 15 '20 at 05:41