65.9K
CodeProject 正在变化。 阅读更多。
Home

在 C# WPF 中监控进程统计信息

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (24投票s)

2009年7月25日

CPOL

3分钟阅读

viewsIcon

132303

downloadIcon

7814

在本文中,我将解释如何以统计数据和图表的形式监控任何实例的性能。

引言

在本文中,我将解释如何监控任何实例的性能。在示例应用程序中(其输出如下所示),我将使用 regedit 进程,但是您可以使用任何实例并通过不同的方式监控其统计信息。基于此,您还可以显示统计图表。

Process_Statistics2.jpg - Click to enlarge image

在 Windows 任务管理器中,我们通常观察到的与特定进程相关的内存是应用程序工作集内存。这是由进程加载的所有虚拟内存页使用的内存,并且由于任何原因都没有被交换到磁盘。我们可以使用“工作集 - 专用”PerformanceCounter 查看此内存。

对于物理内存使用情况,我们可以使用“工作集”PerformanceCounter 进行监控。我们还可以使用System.Diagnostics 命名空间的Process.WorkingSet64 属性获取此值。但是,您必须在使用Process 类属性之前调用Process.Refresh 方法,否则它可能无法提供准确的值。推荐的方法是使用性能计数器。

类似地,要查看进程的专用内存大小,我们有“专用字节”PerformanceCounter,以及System.Diagnostics 命名空间中相同的Process.PrivateMemorySize64 属性值。

对于与特定实例相关的处理器时间,我们在Process 类中具有不同的属性。我们还有不同的性能计数器用于处理器时间的用法,例如“% 处理器时间”、“% 用户时间”和“% 特权时间”。对于处理器时间的性能计数器,第一次它将返回零值。为了避免这种情况,先调用PerformanceCounterNextValue(),然后进行一些延迟(例如一秒,因为计数器值的提供者通常每秒更新一次),然后再次调用NextValue(),因为它将计算这两个值。通过这种方式,您将获得PerformanceCounter 的正确值。我将在示例应用程序中演示这一点。

Using the Code

在这里,我用 C# WPF 写了一个示例应用程序。它将显示System.Diagnostics 命名空间中进程类的不同属性值以及PerformanceCounter 值。它将显示与内存和处理器相关的“regedit”进程统计信息。它将每秒更新这些统计值(您可以更改 regedit 实例并对任何进程使用此示例代码)。MemoryandProcessCheck() 函数中有一段代码显示进程的所有可能的计数器(此示例代码中的 regedit。我在本文结尾处写下了 regedit 进程的所有计数器)。如果您想查看 regedit 实例的所有可能的计数器,请调整Window1.xamllistView1 的外观并将其可见性属性设置为visible。(如果您想使用 regedit 之外的其他进程来观察所有这些功能,只需更改文件开头的进程名称即可)。最后,我还显示图表以显示此特定实例的内存和处理器使用情况。对于图表显示,我使用的是Microsoft.Research.DynamicDataDisplay 命名空间。在运行此示例应用程序之前,请打开 regedit 或运行您想要查看性能统计信息的特定实例。完整的源代码,请下载附加的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
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>

public partial class Window1 : Window
{
//instance name for which you want to monitor Statistics
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)
{
    //create child thread for statistics display
    Thread MonitorThd = new Thread(this.StatisticsMonitoringThread);
    MonitorThd.Start();

    // MemoryandProcessCheck();
}

public void StatisticsMonitoringThread(object obj)
{
    MonitorMemoryandProcess();
}

//query whether the specified process running or not
private Process CurrentlyRunning(string sProcessName)
{
    //get a list of all running processes on current system
    Process[] Processes = Process.GetProcesses();

    //Iterate to every process to check if it is out required process
    foreach (Process SingleProcess in Processes)
    {

        if (SingleProcess.ProcessName.Contains(sProcessName))
        {
            //process found 
            return SingleProcess;
        }
    }

    //Process not found
    return null;
}

//main function for all processing
private bool MonitorMemoryandProcess()
{ 
    string ProcessStatus = null;
    string[] str = new string[10];

    Process ReqProcess;

    try
    { 
        GC.GetTotalMemory(true); // how much GC total use 
        ReqProcess = CurrentlyRunning(sProcName);

    do
    {
        if (ReqProcess != null)
        {
            // Refresh the current process property values.
            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);// 1 second wait

        // Dispatcher.Invoke(new ClearListViewFromOutside(ClearListView));

        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); //infinite loop

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

    //first clear the previous value and than add new one
    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)
{
    //forcefully destroy the application
    Environment.Exit(0);
}

private bool MemoryandProcessCheck()
{
    Process ReqProcess;
   try{

    GC.GetTotalMemory(true); // how much GC total use
    ReqProcess = CurrentlyRunning(sProcName); 

    if (ReqProcess != null)
    {
        // calculate the CPU load
        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();

        //if there is no wait for 1 second after the first call 
        //to NextValue() than processing time value will not be correct.
        System.Threading.Thread.Sleep(1000);// 1 second wait

        if (!ReqProcess.HasExited)
        {
            // Refresh the process property values.
            ReqProcess.Refresh();

            // Display process statistics related to memory.
            listView1.Items.Add(ReqProcess.ProcessName);
            listView1.Items.Add("******************************");
            listView1.Items.Add("Working Set: " + 
            	(WorkingSetMemoryCounter.NextValue() / 1024) + 
            	"K"); // more efficient. update quickly as compare to 
            		//ReqProcess.WorkingSet64 if Process's Refresh() did not call
            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"); // usually same with PagedMemorySize64
            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"); //same as peakWorkingSet
            listView1.Items.Add("Peak physical memory usage: " + 
            	ReqProcess.PeakWorkingSet64 / 1024 + "K");

             listView1.Items.Add("Thread Count: " + 
             	ThreadCountCounter.NextValue()); // how many threads are
            listView1.Items.Add("Handle Count: " + 
            	HandleCountCounter.NextValue()); // how many handles

             //The amount of system memory, in bytes, allocated for the 
             //associated process that can be written to the virtual memory paging file.
            listView1.Items.Add("Page System Memory Size: " + 
            	ReqProcess.PagedSystemMemorySize64 / 1024 + "K");

            //The amount of system memory, in bytes, allocated for the 
            //associated process that cannot be written to the virtual memory paging file.
            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");
            }

            //Display process statistics related to Processor
            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);

            //code to see all the possible Counter of a process. 
            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);
                    }
                }
            }
    //end test code to see all possible counter of specific process 
        }
    }
    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; }
}
}

您可以在下面的图片中看到,当您在注册表中搜索某些内容时,它通常会占用多少内存和处理器时间。

Process_Statistics1.jpg - Click to enlarge image

在进程类别中,“regedit”实例的计数器是

  • 实例:regedit
  • 计数器:% 处理器时间
  • 计数器:% 用户时间
  • 计数器:% 特权时间
  • 计数器:虚拟字节峰值
  • 计数器:虚拟字节
  • 计数器:每秒页错误
  • 计数器:工作集峰值
  • 计数器:工作集
  • 计数器:页面文件字节峰值
  • 计数器:页面文件字节
  • 计数器:专用字节
  • 计数器:线程数
  • 计数器:基本优先级
  • 计数器:已用时间
  • 计数器:进程 ID
  • 计数器:创建进程 ID
  • 计数器:池分页字节
  • 计数器:池非分页字节
  • 计数器:句柄数
  • 计数器:每秒 I/O 读取操作
  • 计数器:每秒 I/O 写入操作
  • 计数器:每秒 I/O 数据操作
  • 计数器:每秒 I/O 其他操作
  • 计数器:每秒 I/O 读取字节
  • 计数器:每秒 I/O 写入字节
  • 计数器:每秒 I/O 数据字节
  • 计数器:每秒 I/O 其他字节
  • 计数器:工作集 - 专用

历史

  • 2009 年 7 月 25 日:初始发布
© . All rights reserved.