15

I am using this class as a base class for a category of tests that launch a process and give it some input and wait for it to become idle before giving it more input.

public abstract class TestProcessLaunchingBase
{
    protected PerformanceCounter PerfCounter { get; set; }

    protected void WaitForProcessIdle()
    {
        while (true)
        {
            float oldValue = PerfCounter.NextValue();

            Thread.Sleep(1000);

            float nextValue = PerfCounter.NextValue();

            if (nextValue == 0)
                break;
        }
    }

    protected void FindSpawnedProcessPerfCounter(int processId)
    {
        PerformanceCounterCategory cat = new PerformanceCounterCategory("Process");
        string[] instances = cat.GetInstanceNames();
        foreach (string instance in instances)
        {
            using (PerformanceCounter cnt = new PerformanceCounter("Process", "ID Process", instance, true))
            {
                int val = (int)cnt.RawValue;
                if (val == processId)
                {
                    PerfCounter = new PerformanceCounter("Process", "% Processor Time", instance);
                    break;
                }
            }

        }

        Assert.IsNotNull(PerfCounter, "Failed to perf counter");
    }
}

These tests occasionally fail because PerfCounter.NextValue() throws an

System.InvalidOperationException Instance 'foobar#2' does not exist in the specified Category

It seems like the instance name of the performance counter is not persistent.

If there are three foobar processes they might have instance names

  • foobar pid 5331
  • foobar #1 pid 5332
  • foobar #2 pid 5333

It seems like if pid 5332 exits foobar #2 becomes foobar #1.

Questions:

  1. Is this a documented behavior ? Can you not persistent a performance counter ? Do you have to look it up every time ?

  2. Alternatively, is there a performance counter that can give Processor Time for all processes named foobar

parapura rajkumar
  • 24,045
  • 1
  • 55
  • 85

2 Answers2

9

I already faced this issue in the past. The ProcessName#InstanceNumber pattern for the instance name was clearly a poor choice from Microsoft, you know why :)

So basically you have two choices:

1) Create a new PerformanceCounter instance each time, using your FindSpawnedProcessPerfCounter method.

2) Follow the steps described in KB281884 to change the pattern from ProcessName#InstanceNumber to ProcessName_ProcessID.

The problem of the first solution is that it requires some CPU time to build a new instance each time.

The problem of the second solution is that the registry modification also impacts all programs that are also using this performance counter. And it requires to modify the registry before launching your app.

Last option you have, is to not use Performance counters at all. If you are only interested in the ProcessorTime information, there are some Kernel32 functions you could call using P/Invoke to retrieve it.

EDIT:

The Process class also provides UserProcessorTime and PrivilegedProcessorTime (kernel processor time) properties. Both return a TimeSpan instance (= amount of time), so to retrieve a percentage of processor time, you'll have to do some computation by yourself (involving the refresh period and the processor times).

ken2k
  • 48,145
  • 10
  • 116
  • 176
  • P/Invoke: `GetProcessTimes` gives user and kernel mode times for a given process. One needs to obtain process handle with `OpenProcess` first, and it definitely is not going to change if another instance of this application closes down. – Roman R. Aug 13 '12 at 17:43
0

If you must use performance counters, my approach would be to re-create my performance counters when an exception is encountered.

When an exception is thrown, dispose of your old performance counters and then create new performance counter instances using the process Id for all instances of your foobar test. There is a nice stackoverflow post over here: Performance Counter by Process ID instead of name?

I am assuming by your statement "launch a process and give it some input", that you are holding onto the process id after launching it. So you always have a collection of running test processes which you want to monitor.

With this technique you will only be incurring a performance penalty when you hit an exception.

As @ken2k already pointed out, you can change the naming convention of the performance counters for consistency. In my experience performance counters can encounter any number exceptions sometimes for seemingly unexpected reasons. So even if you do change the performance counters name, it might be nice to be able to re-create your performance counters if necessary.

Community
  • 1
  • 1
BenSwayne
  • 16,810
  • 3
  • 58
  • 75
  • I don't think it's a good idea to fix the issue by recreating an instance on exception. With OP's example: if process of ID 5333 exits, instance "foobar #2" throws an exception, fine. But if process of ID 5332 exits, using "foobar #1" doesn't throw any exception: it just "points" to process of ID 5333 instead. No exception, wrong values. – ken2k Aug 20 '12 at 16:49
  • @ken2k, I was implying plural in my post. ie: recreating the collection of performance counters, not just the one that threw the exception. Perhaps I wasn't clear enough. Ultimately he will want to either tie the results to a process ID or aggregate those values. If aggregating, he doesn't need to care that foobar#1 changed as long as it is still a test process. If he wants to tie the data to Process IDs he needs to do the above or consider your suggestion for using the .Net Process class, etc.. – BenSwayne Aug 20 '12 at 17:17
  • How do you even determine what #number a particular process is? It's not the session id of the process. Like... how does w3wp.exe 1 know it's 1, and how does the other instance know it's #2? How does Windows resequencing them? Does #2 just start writing to #1's data when #1 exits? Or do the counters 'rename' themselves? This seems like a piss poor design and an impossible mess to try to associate a process with it's own perf counters. – Triynko Apr 27 '21 at 03:24