2012-08-09 13 views
12

Sto utilizzando questa classe come classe base per una categoria di test che avvia un processo e gli diamo un input e attendi che diventi inattivo prima di dargli più input.Il nome dell'istanza del contatore delle prestazioni di un processo può cambiare anche se il processo non è terminato

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"); 
    } 
} 

Questi test di tanto in tanto vengono a mancare perché PerfCounter.NextValue() genera

System.InvalidOperationException grado 'foobar # 2' non esiste nella Categoria specificata

Sembra che il nome dell'istanza del contatore delle prestazioni non è persistente.

se ci sono tre processi foobar potrebbero avere nomi di istanza

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

Sembra che se pid 5332 uscite foobar n. 2 diventa foobar n. 1.

Domande:

  1. È questo un comportamento documentato? Non riesci a mantenere un contatore di prestazioni? Devi cercare ogni volta?

  2. In alternativa, c'è un contatore delle prestazioni che può dare Tempo processore per tutti i processi denominati foobar

risposta

9

Ho già affrontato questo problema in passato. Il modello ProcessName#InstanceNumber per il nome dell'istanza era chiaramente una pessima scelta da Microsoft, è sapere perché :)

Quindi sostanzialmente si hanno due scelte:

1) Creare una nuova istanza PerformanceCounter ogni volta, utilizzando il metodo di FindSpawnedProcessPerfCounter.

2) Seguire i passaggi descritti in KB281884 per modificare il modello da ProcessName#InstanceNumber a ProcessName_ProcessID.

Il problema della prima soluzione è che richiede un po 'di tempo di CPU per creare ogni volta una nuova istanza.

Il problema della seconda soluzione è che la modifica del Registro di sistema influisce anche su tutti i programmi che utilizzano questo contatore delle prestazioni. E richiede di modificare il registro prima di avviare la tua app.

L'ultima opzione disponibile è quella di non utilizzare i contatori delle prestazioni. Se sei interessato solo alle informazioni ProcessorTime, ci sono alcune funzioni di Kernel32 che puoi chiamare usando P/Invoke per recuperarlo.

EDIT:

La classe Process fornisce anche UserProcessorTime e PrivilegedProcessorTime (kernel processor time) proprietà. Entrambi restituiscono un'istanza TimeSpan (= quantità di tempo), quindi per recuperare una percentuale del tempo del processore, dovrai eseguire un calcolo autonomo (che coinvolge il periodo di aggiornamento e i tempi del processore).

+0

P/Invoke: 'GetProcessTimes' fornisce i tempi di modalità utente e kernel per un determinato processo. Prima è necessario ottenere un handle di processo con 'OpenProcess', e sicuramente non cambierà se si chiude un'altra istanza di questa applicazione. –

0

Se è necessario utilizzare i contatori delle prestazioni, il mio approccio sarebbe quello di ricreare i contatori delle prestazioni quando si verifica un'eccezione.

Quando viene generata un'eccezione, eliminare i vecchi contatori delle prestazioni e quindi creare nuove istanze del contatore delle prestazioni utilizzando l'ID processo per tutte le istanze del test foobar. C'è un bel post stackoverflow qui: Performance Counter by Process ID instead of name?

Sto assumendo dalla tua affermazione "lanciare un processo e dargli un po 'di input", che stai trattenendo l'id del processo dopo averlo lanciato. Quindi hai sempre una raccolta di processi di test in esecuzione che vuoi monitorare.

Con questa tecnica si incorre in una penalità di prestazioni solo quando si colpisce un'eccezione.

Come già sottolineato da @ken2k, è possibile modificare la convenzione di denominazione dei contatori delle prestazioni per coerenza. Nella mia esperienza, i contatori di prestazioni possono riscontrare eccezioni di numero a volte per motivi apparentemente inaspettati. Pertanto, anche se si modifica il nome dei contatori delle prestazioni, potrebbe essere utile poter ricreare i contatori delle prestazioni, se necessario.

+0

Non penso che sia una buona idea risolvere il problema ricreando un'istanza sull'eccezione. Con l'esempio di OP: se il processo di ID 5333 termina, l'istanza "foobar # 2" genera un'eccezione, bene. Ma se il processo dell'ID 5332 termina, l'uso di "foobar # 1" non genera alcuna eccezione: semplicemente "punta" al processo dell'ID 5333. Nessuna eccezione, valori errati. – ken2k

+0

@ ken2k, stavo insinuando al plurale nel mio post. vale a dire: ricreare la collezione di contatori delle prestazioni, non solo quella che ha generato l'eccezione. Forse non ero abbastanza chiaro. Alla fine vorrebbe sia legare i risultati a un ID di processo o aggregare quei valori. Se aggrega, non ha bisogno di preoccuparsi che il # 1 di foobar sia cambiato fintanto che è ancora un processo di test. Se desidera legare i dati agli ID di processo, deve eseguire quanto sopra o considerare il tuo suggerimento per l'utilizzo della classe .Net Process, ecc. – BenSwayne

Problemi correlati