Introduction
In this article, I will explain the performance monitoring of any instance. In the sample application (its output is shown below), I will use the regedit process, however you can use any instance and monitor its statistics using different ways. On the basis of that, you can display the statistics graphs as well.
In Windows Task Manager, the memory related to a specific process which we are usually observing is the application working set memory. This is the memory used by all the virtual memory pages that have been loaded by a process and have not been swapped to disk for any reason. We can view this memory using "Working Set - Private" PerformanceCounter
.
For physical memory usage, we can monitor this by using the "Working Set" PerformanceCounter
. We can also get this value using the Process.WorkingSet64
property of System.Diagnostics
namespace. But you must have to call Process.Refresh
method before using property of Process
class otherwise it may not give you the accurate value. The recommended way is to use performance counter.
Similarly to view the private memory size of a process, we have "Private Bytes" PerformanceCounter
and the same thing is Process.PrivateMemorySize64
property value in System.Diagnostics
namespace.
For processor time related to specific instance, we have different properties in Process
class. We also have different performance counters for usage of processor time like "% Processor Time", "% User Time" and "% Privileged Time". In case of processor time’s performance counter, first time it will give you the value zero. To avoid this thing, call PerformanceCounter
’s NextValue()
then give some delay (like one second because usually the provider of counter value updates once in a second) and then call NextValue()
again because it will calculate these two values. In this way, you will get the correct value of PerformanceCounter
. I will demonstrate this thing as well in a sample application.
Using the Code
Here I wrote a sample application in C# WPF. This will show different property values of process class in System.Diagnostics
namespace and PerformanceCounter
values as well. It will show “regedit” process statistics related to memory and processor. It will update these statistics values in every second (you can change the instance instead of regedit and use this sample code for any process). There is a piece of code in MemoryandProcessCheck()
function which displays all the possible counters of a process (regedit in this example code. I have written down all the counters of regedit process at the end of this article). Adjust the appearance of listView1
in Window1.xaml
and sets its visibility property to visible
if you want to see all the possible counters of regedit instance. (If you would like to watch all of these features with some other process instead of regedit, then just change the process name in the start of the file). Finally I am also displaying the graph to show the memory and processor usage for this specific instance. For graph/char display, I am using Microsoft.Research.DynamicDataDisplay
namespace. Open regedit before running this sample application or run that specific instance for which you want to watch performance statistics. For complete source code, please download the attached MemoryPerformanceMonitoring.zip.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Threading;
namespace MemoryPerformanceMonitoring
{
public partial class Window1 : Window
{
string sProcName = "regedit";
ObservableCollection<StatisticsData> _StatisticsCollection =
new ObservableCollection<StatisticsData>();
public ObservableCollection<StatisticsData> StatisticsCollection
{
get { return _StatisticsCollection; }
}
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Thread MonitorThd = new Thread(this.StatisticsMonitoringThread);
MonitorThd.Start();
}
public void StatisticsMonitoringThread(object obj)
{
MonitorMemoryandProcess();
}
private Process CurrentlyRunning(string sProcessName)
{
Process[] Processes = Process.GetProcesses();
foreach (Process SingleProcess in Processes)
{
if (SingleProcess.ProcessName.Contains(sProcessName))
{
return SingleProcess;
}
}
return null;
}
private bool MonitorMemoryandProcess()
{
string ProcessStatus = null;
string[] str = new string[10];
Process ReqProcess;
try
{
GC.GetTotalMemory(true);
ReqProcess = CurrentlyRunning(sProcName);
do
{
if (ReqProcess != null)
{
ReqProcess.Refresh();
if (ReqProcess.Responding)
{
ProcessStatus = "Running";
}
else
{
ProcessStatus = "Not Responding";
}
PerformanceCounter totalProcessorTimeCounter =
new PerformanceCounter("Process",
"% Processor Time", ReqProcess.ProcessName);
PerformanceCounter UserProcessorTimeCounter =
new PerformanceCounter("Process",
"% User Time", ReqProcess.ProcessName);
PerformanceCounter PrivilegedProcessorTimeCounter =
new PerformanceCounter("Process",
"% Privileged Time", ReqProcess.ProcessName);
PerformanceCounter WorkingSetMemoryCounter =
new PerformanceCounter("Process",
"Working Set", ReqProcess.ProcessName);
PerformanceCounter WorkingSetPeakMemoryCounter =
new PerformanceCounter("Process",
"Working Set Peak", ReqProcess.ProcessName);
PerformanceCounter ThreadCountCounter =
new PerformanceCounter("Process",
"Thread Count", ReqProcess.ProcessName);
PerformanceCounter WorkingSetPrivateMemoryCounter =
new PerformanceCounter("Process",
"Working Set - Private", ReqProcess.ProcessName);
PerformanceCounter HandleCountCounter =
new PerformanceCounter("Process",
"Handle Count", ReqProcess.ProcessName);
totalProcessorTimeCounter.NextValue();
UserProcessorTimeCounter.NextValue();
PrivilegedProcessorTimeCounter.NextValue();
System.Threading.Thread.Sleep(1000);
str[0] = ReqProcess.ProcessName;
str[1] = ProcessStatus;
str[2] = (WorkingSetMemoryCounter.NextValue() / 1024) + "K";
str[3] = (WorkingSetPrivateMemoryCounter.NextValue() / 1024) + "K";
str[4] = (WorkingSetPeakMemoryCounter.NextValue() / 1024) + "K";
str[5] = (ThreadCountCounter.NextValue()).ToString();
str[6] = (HandleCountCounter.NextValue()).ToString();
str[7] = (totalProcessorTimeCounter.NextValue()).ToString();
str[8] = (UserProcessorTimeCounter.NextValue()).ToString();
str[9] = (PrivilegedProcessorTimeCounter.NextValue()).ToString();
}
else
{
str[0] = sProcName;
str[1] = "Not Started";
str[2] = "";
str[3] = "";
str[4] = "";
str[5] = "";
str[6] = "";
str[7] = "";
str[8] = "";
str[9] = "";
}
Dispatcher.Invoke(new UpdateGUIOutsideFeedbackMessage
(UpdateGUIOutsideFeedbackMsg), new object[] { str });
}while (true);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Performance Monitoring Statistics Exception ",
MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
return true;
}
public delegate void UpdateGUIOutsideFeedbackMessage(string[] msg);
public void UpdateGUIOutsideFeedbackMsg(string[] msg)
{
Mutex firstMutex = new Mutex(false);
firstMutex.WaitOne();
StatisticsCollection.Clear();
_StatisticsCollection.Add(new StatisticsData
{
ProcessName = msg[0],
ProcessRunningStatus = msg[1],
WorkingSetMemory = msg[2],
WorkingSetPrivateMemory = msg[3],
WorkingSetPeak = msg[4],
ThreadCount = msg[5],
HandleCount = msg[6],
TotalProcessorTime = msg[7],
UserProcessorTime = msg[8],
PrivilegedProcessorTime = msg[9]
});
firstMutex.Close();
}
public delegate void ClearListViewFromOutside();
public void ClearListView()
{
StatisticsCollection.Clear();
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
Environment.Exit(0);
}
private bool MemoryandProcessCheck()
{
Process ReqProcess;
try{
GC.GetTotalMemory(true);
ReqProcess = CurrentlyRunning(sProcName);
if (ReqProcess != null)
{
System.TimeSpan CPULoad = (DateTime.Now - ReqProcess.StartTime);
listView1.Items.Add("CPU load: " +
(ReqProcess.TotalProcessorTime.TotalMilliseconds /
CPULoad.TotalMilliseconds) * 100);
PerformanceCounter TotalProcessorTimeCounter =
new PerformanceCounter("Process",
"% Processor Time", ReqProcess.ProcessName);
PerformanceCounter ProcessorUserTimeCounter =
new PerformanceCounter("Process",
"% User Time", ReqProcess.ProcessName);
PerformanceCounter ProcessorPrivilegedTimeCounter =
new PerformanceCounter("Process",
"% Privileged Time", ReqProcess.ProcessName);
PerformanceCounter ElapsedTimeCounter =
new PerformanceCounter("Process",
"Elapsed Time", ReqProcess.ProcessName);
PerformanceCounter VirtualBytesPeakMemoryCounter =
new PerformanceCounter("Process",
"Virtual Bytes Peak", ReqProcess.ProcessName);
PerformanceCounter VirtualBytesMemoryCounter =
new PerformanceCounter("Process",
"Virtual Bytes", ReqProcess.ProcessName);
PerformanceCounter WorkingSetMemoryCounter =
new PerformanceCounter("Process",
"Working Set", ReqProcess.ProcessName);
PerformanceCounter WorkingSetPeakMemoryCounter =
new PerformanceCounter("Process",
"Working Set Peak", ReqProcess.ProcessName);
PerformanceCounter PrivateBytesMemoryCounter =
new PerformanceCounter("Process",
"Private Bytes", ReqProcess.ProcessName);
PerformanceCounter WorkingSetPrivateMemoryCounter =
new PerformanceCounter("Process",
"Working Set - Private", ReqProcess.ProcessName);
PerformanceCounter PageFileBytesPeakMemoryCounter =
new PerformanceCounter("Process",
"Page File Bytes Peak", ReqProcess.ProcessName);
PerformanceCounter ThreadCountCounter =
new PerformanceCounter("Process",
"Thread Count", ReqProcess.ProcessName);
PerformanceCounter HandleCountCounter =
new PerformanceCounter("Process",
"Handle Count", ReqProcess.ProcessName);
TotalProcessorTimeCounter.NextValue();
ProcessorUserTimeCounter.NextValue();
ProcessorPrivilegedTimeCounter.NextValue();
System.Threading.Thread.Sleep(1000);
if (!ReqProcess.HasExited)
{
ReqProcess.Refresh();
listView1.Items.Add(ReqProcess.ProcessName);
listView1.Items.Add("******************************");
listView1.Items.Add("Working Set: " +
(WorkingSetMemoryCounter.NextValue() / 1024) +
"K");
listView1.Items.Add("Physical memory usage(Working Set memory):
" + ReqProcess.WorkingSet64 / 1024 + "K");
listView1.Items.Add("Working Set - Private: " +
(WorkingSetPrivateMemoryCounter.NextValue() / 1024) + "K");
listView1.Items.Add("Private Memory Size: " +
ReqProcess.PrivateMemorySize64 / 1024 +
"K");
listView1.Items.Add("Private Bytes: " +
(PrivateBytesMemoryCounter.NextValue() / 1024) + "K");
listView1.Items.Add("Virtual memory paging file
(Process using RAM): " + ReqProcess.PagedMemorySize64 / 1024 + "K");
listView1.Items.Add("Working Set Peak: " +
(WorkingSetPeakMemoryCounter.NextValue() / 1024) +
"K");
listView1.Items.Add("Peak physical memory usage: " +
ReqProcess.PeakWorkingSet64 / 1024 + "K");
listView1.Items.Add("Thread Count: " +
ThreadCountCounter.NextValue());
listView1.Items.Add("Handle Count: " +
HandleCountCounter.NextValue());
listView1.Items.Add("Page System Memory Size: " +
ReqProcess.PagedSystemMemorySize64 / 1024 + "K");
listView1.Items.Add("Nonpage System Memory Size: " +
ReqProcess.NonpagedSystemMemorySize64 / 1024 + "K");
listView1.Items.Add("Virtual Memory: " +
ReqProcess.VirtualMemorySize64 / 1024 + "K");
listView1.Items.Add("Virtual Bytes: " +
(VirtualBytesMemoryCounter.NextValue() / 1024) + "K");
listView1.Items.Add("Virtual Bytes Peak: " +
(VirtualBytesPeakMemoryCounter.NextValue() / 1024) + "K");
listView1.Items.Add("Peak Virtual Memory usage: " +
ReqProcess.PeakVirtualMemorySize64 / 1024 + "K");
listView1.Items.Add("Page File Bytes Peak: " +
(PageFileBytesPeakMemoryCounter.NextValue() / 1024) + "K");
listView1.Items.Add("Peak virtual memory paging file usage: " +
ReqProcess.PeakPagedMemorySize64 / 1024 + "K");
if (ReqProcess.Responding)
{
listView1.Items.Add("Status = Running");
}
else
{
listView1.Items.Add("Status = Not Responding");
}
listView1.Items.Add("%Processor Time: " +
TotalProcessorTimeCounter.NextValue());
listView1.Items.Add("%User Time: " +
ProcessorUserTimeCounter.NextValue());
listView1.Items.Add("%Privileged Time: " +
ProcessorPrivilegedTimeCounter.NextValue());
listView1.Items.Add("Elapsed Time: " +
ElapsedTimeCounter.NextValue());
listView1.Items.Add("Total processor time: " +
ReqProcess.TotalProcessorTime);
listView1.Items.Add("User processor time: " +
ReqProcess.UserProcessorTime);
listView1.Items.Add("Privileged processor time: " +
ReqProcess.PrivilegedProcessorTime);
PerformanceCounterCategory[] PCounterCtg =
PerformanceCounterCategory.GetCategories();
foreach (PerformanceCounterCategory category in PCounterCtg)
{
if (category.CategoryName != "Process")
continue;
listView1.Items.Add("");
listView1.Items.Add("Category: " + category.CategoryName);
string[] instances = category.GetInstanceNames();
if (instances.Length == 0)
{
foreach (PerformanceCounter PCounter in category.GetCounters())
listView1.Items.Add(" Counter: " + PCounter.CounterName);
}
else
{
foreach (string instance in instances)
{
if (!(instance.Equals(sProcName)))
continue;
listView1.Items.Add(" Instance: " + instance);
if (category.InstanceExists(instance))
foreach
(PerformanceCounter Pctr in category.GetCounters(instance))
listView1.Items.Add(" Counter: " + Pctr.CounterName);
}
}
}
}
}
else
{
listView1.Items.Add("");
listView1.Items.Add("Process " + sProcName + " is not started. ");
}
}
catch (Exception ex)
{
listView1.Items.Add("Process check exception: " + ex.Message);
return false;
}
return true;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Process viewProcess = null;
viewProcess = CurrentlyRunning(sProcName);
if (viewProcess != null)
{
Window2 w2 = new Window2("Process", "Working Set -
Private", sProcName, "Memory (Private Working Set)");
w2.Show();
}
else
{
MessageBox.Show(sProcName + " instance is not running.",
"Memory Graph Exception", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void button2_Click(object sender, RoutedEventArgs e)
{
Process viewProcess = null;
viewProcess = CurrentlyRunning(sProcName);
if (viewProcess != null)
{
Window2 w2 = new Window2("Process",
"% Processor Time", sProcName, "%Processor Time (Total)");
w2.Show();
}
else
{
MessageBox.Show(sProcName + " instance is not running.",
"Processor Graph Exception", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
public class StatisticsData
{
public string ProcessName { get; set; }
public string ProcessRunningStatus { get; set; }
public string WorkingSetMemory { get; set; }
public string WorkingSetPrivateMemory { get; set; }
public string WorkingSetPeak { get; set; }
public string ThreadCount { get; set; }
public string HandleCount { get; set; }
public string TotalProcessorTime { get; set; }
public string UserProcessorTime { get; set; }
public string PrivilegedProcessorTime { get; set; }
}
}
You can see in the image below how much memory and Processor time it usually takes when you search something in the registry.
In process category, counters of ‘regedit’ instance are:
- Instance: regedit
- Counter: % Processor Time
- Counter: % User Time
- Counter: % Privileged Time
- Counter: Virtual Bytes Peak
- Counter: Virtual Bytes
- Counter: Page Faults/sec
- Counter: Working Set Peak
- Counter: Working Set
- Counter: Page File Bytes Peak
- Counter: Page File Bytes
- Counter: Private Bytes
- Counter: Thread Count
- Counter: Priority Base
- Counter: Elapsed Time
- Counter: ID Process
- Counter: Creating Process ID
- Counter: Pool Paged Bytes
- Counter: Pool Nonpaged Bytes
- Counter: Handle Count
- Counter: IO Read Operations/sec
- Counter: IO Write Operations/sec
- Counter: IO Data Operations/sec
- Counter: IO Other Operations/sec
- Counter: IO Read Bytes/sec
- Counter: IO Write Bytes/sec
- Counter: IO Data Bytes/sec
- Counter: IO Other Bytes/sec
- Counter: Working Set - Private
History
- 25th July, 2009: Initial post