PerformanceCounterCategory.GetCategories与Perfmon不一致

Mik*_*kke 14 .net c# perfmon performancecounter

好的,所以我基本上试图创建一个已安装的性能计数器类别列表,就像你在PerfMon中获得的那样.为此,我正在使用

System.Diagnostics.PerformanceCounterCategory.GetCategories()
Run Code Online (Sandbox Code Playgroud)

这看起来很有效,直到你检查清单,并发现有些东西丢失了.我发现缺少的第一个是ReadyBoost Cache.这是因为该项目设置为在"x86"上编译.将此更改为"任何CPU"修复了该问题.

然而,仍然有一些缺失,例如,其中一个测试机器有一个"授权管理器应用程序"类别(我的没有,似乎没有人知道原因,或者它来自哪里)但是,在那台机器上,性能计数器类别显示在PerfMon中,但在GetCategories()从C#调用方法时则不显示.

有谁知道为什么?有更可靠的方法PerformanceCounterCategories吗?这是因为我正在使用.Net吗?我可以使用一些原生API吗?

编辑

对不起,我还是不明白.我编写这段代码或许可以更好地说明它:

using System;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.Win32;

namespace PccHack
{
    class Program
    {
        private static readonly Regex Numeric = new Regex(@"^\d+$");
        static void Main(string[] args)
        {
            var pcc1 = PerformanceCounterCategory.GetCategories();
            Console.Out.WriteLine("Getting automatically from the microsoft framework gave {0} results.", pcc1.Count());
            string[] counters;
            using (var regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009"))
            {
                counters = regKey.GetValue("Counter") as string[];
            }
            var pcc2 = counters.Where(counter => !Numeric.IsMatch(counter)).ToList();
            pcc2.Sort();
            Console.Out.WriteLine("Getting manually from the registry gave {0} results.", pcc2.Count());
            Console.In.ReadLine();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在这给了我3236个结果.因为它获得了系统中的所有性能计数器.所以我认为我需要做的就是过滤掉那些实际上是性能计数器的东西,只留下类别.然而,似乎没有PerformanceCounter的构造函数只接受名称(因为这不是唯一的),似乎也没有一个获取索引值.我发现了一个名为Performance Data Helper的Win32 API,但这似乎没有我想要的功能.所以.如果我有一个性能计数器索引,我如何在C#中获取该索引的PerformanceCounterCategory?PerfMon做到了,所以它必须是可能的.有没有办法解析索引"幻数"来找出哪个是哪个?

编辑2

好的.所以这是我的头脑.使用三种不同方法(.Net/Registry/PowerShell)的代码的最新版本:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Microsoft.Win32;
using System.Management.Automation;


namespace PccHack
{
    internal class Program
    {
        private static void Main()
        {
            var counterMap = new Dictionary<string, string>();
            using (var regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009"))
            {
                var counter = regKey.GetValue("Counter") as string[];
                for (var i = 0; i < counter.Count() - 1; i += 2)
                {
                    counterMap.Add(counter[i], counter[i + 1]);
                }
            }

            var pcc1 = PerformanceCounterCategory.GetCategories().Select(o => o.CategoryName).ToList();
            var pcc2 = new List<string>();
            // Get v1 providers
            using (var regKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\services"))
            {
                foreach (var subKeyName in regKey.GetSubKeyNames())
                {
                    using (var subKey = regKey.OpenSubKey(subKeyName))
                    {
                        if (!subKey.GetSubKeyNames().Contains("Performance")) continue;
                        using (var perfKey = subKey.OpenSubKey("Performance"))
                        {
                            var blah = (string) perfKey.GetValue("Object List");
                            if (blah != null)
                            {
                                pcc2.AddRange(blah.Split(' ').Select(b => counterMap[b]));
                            }
                        }
                    }
                }
            }
            // Get v2 providers
            using (var regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers"))
            {
                foreach (var subKeyName in regKey.GetSubKeyNames())
                {
                    using (var subKey = regKey.OpenSubKey(subKeyName))
                    {
                        foreach (var perfKeyName in subKey.GetSubKeyNames())
                        {
                            using (var perfKey = subKey.OpenSubKey(perfKeyName))
                            {
                                var blah = (string) perfKey.GetValue("NeutralName");
                                if (blah != null)
                                {
                                    pcc2.Add(blah);
                                }
                            }
                        }
                    }
                }
            }
            var ps = PowerShell.Create();

            ps.AddCommand("Get-Counter").AddParameter("listSet", "*");
            var pcc3 = ps.Invoke().Select(result => result.Members["CounterSetName"].Value.ToString()).ToList();

            pcc1.Sort();
            pcc2.Sort();
            pcc3.Sort();
            Console.Out.WriteLine("Getting automatically from the microsoft framework gave {0} results.", pcc1.Count());
            Console.Out.WriteLine("Getting manually from the registry gave {0} results.", pcc2.Count());
            Console.Out.WriteLine("Getting from PowerShell gave {0} results.", pcc3.Count());
            Console.In.ReadLine();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的机器上,我使用.Net框架获得138,通过解析注册表获得117,使用PowerShell获得157(这是正确的答案).

但是,根据已安装PowerShell/Windows SDK的用户不是一个真正的选择.

任何人都有任何想法?是否有一些绝密版本3性能计数器类别,隐藏在注册表中的其他位置,我需要跟踪?我不仅没有想法尝试,我也没有尝试过不好的想法.我是否可以在perfmon上使用任何秘密命令行开关,以便列出所有类别?

bog*_*dan 5

我认为您正在遇到由Perflib v2计数器引起的.NET Framework错误。

在后台,PerformanceCounterCategory使用注册表功能获取有关当前在Performance子系统中注册的类别(aka对象),实例和计数器的信息。您可以通过查看PerformanceCounterCategoryILSpy 的代码来验证这一点。

计数器可以通过两种类型的提供程序进行注册:“核心”提供程序和“可扩展性”提供程序。这些名称是我因缺乏更好的选择而发明的。

核心提供程序内置于Windows的深处,并与Performance子系统紧密相连,以提供诸如Process,System等的计数器。如果您看不到此类计数器PerformanceCounterCategory,很可能您遇到了一些深层次的问题Windows安装以及事件日志中至少有一些错误。我认为这不是您的情况。

可扩展性提供者通过记录的Perflib接口与Performance子系统进行接口,以提供所有其他计数器。重要的是要注意,某些Windows功能的计数器是通过可扩展性提供程序注册的,主要MS产品(例如SQL Server,.NET Framework等)的计数器也是通过注册的。 -第三方的提供者。

如果您看不到通过PerformanceCounterCategoryPerflib注册的计数器,则可能首先是由于其提供程序在系统中配置不正确或配置已被破坏。在这种情况下,您应该在事件日志中包含这些文档的“性能计数器加载”或“性能库可用性”部分中定义的一些错误。我认为这不是您的情况。

第二个原因与Perflib提供程序如何在后台工作有关。注册计数器需要两个主要步骤。第一步是使用编写注册表中提供者的配置LodCtr.exe。我认为这是由您感兴趣的计数器的安装程序为您自动完成的,并且该配置是正确的,尤其是因为如果此配置有问题,您可能会在事件中遇到一些上述错误日志。第二步是向Performance子系统实际注册Perflib提供程序。

现在我们已经接近问题了。对于Perflib v1和v2提供程序,注册的完成方式非常不同。对于v1,提供程序的代码以DLL编写,这些DLL从第一步中编写的注册表配置引用,并由系统本身加载。因此,当系统从注册表中读取配置信息并加载DLL时,Perflib v1提供程序注册会自动发生。对于Perflib v2提供程序,情况有所不同。提供者的代码不再由系统直接执行,而是由与提供者相关联的应用程序/服务执行。因此,如果您编写一个使用Perflib v2创建自定义提供程序/计数器的应用程序,则您的应用程序还将运行代码以收集这些提供程序的数据,并且将以记录的方式与Performance子系统对接。问题是 现在,必须通过托管提供程序代码的应用来触发在系统中注册Perflib v2提供程序的代码(与Perflib v1一样,系统会自动触发)。因此,例如,如果该应用程序是Windows服务,并且该服务尚未启动,则提供程序将不会在Performance子系统中注册,并且它们的计数器也不会通过注册表功能/PerformanceCounterCategory

是文档的相关部分,描述了针对Perflib v2提供程序的这种自我注册:

您的提供程序必须调用CounterInitialize和CounterCleanup函数。CounterInitialize调用PerfStartProvider函数来注册提供程序,还调用PerfSetCounterSetInfo函数来初始化计数器集。CounterCleanup调用PerfStopProvider函数以删除提供程序的注册。

总之,有两种不同的方式列出类别,实例和计数器。一种是查询注册表功能,并列出在查询时注册的所有项目。另一种方法是查看注册表中写的描述提供者的配置信息,而不管它们在查询时是否已在Performance子系统中注册。

实际上,您将需要使用两种方式的组合,因为您只能通过查询注册表功能来获取类别的实例,并且只能通过查询注册表中编写的配置来获取尚未注册的提供者的类别和计数器。 。

不幸的是,PerformanceCounterCategory仅查询注册表功能,因此无法获取有关尚未注册的Perflib v2提供程序的信息。您可以通过其他方式查看这些提供程序,例如通过Performance Monitor MMC(在后台使用PDH API,该PDH API可以显示已注册和尚未注册的计数器的组合)或typeperf.exe -qx

您可以测试以上适用于您的BranchCache类别。下面的示例在Win 7上进行了测试。

  1. 确保具有显示名称的Windows服务BranchCache已启动,然后运行以下C#代码:

    Debug.WriteLine((new PerformanceCounterCategory("BranchCache")).ReadCategory().Keys.Count);
    
    Run Code Online (Sandbox Code Playgroud)

    您应该没有任何错误,并将其21写入调试输出。

  2. 现在,停止BranchCache服务并再次运行C#代码。您将获得一个例外,因为该类别不再在Performance子系统中注册,因此PerformanceCounterCategory无法找到它。

为确保我所描述的内容适用于您缺少的计数器PerformanceCounterCategory.GetCategories(),请检查所丢失的计数器是否typeperf -qx在类别下显示,该类别的名称与在Registry中某处的Registry中配置的提供者相关HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers

解决方案是为PDH API编写一个C#包装器,然后以这种方式获取您的信息。这是不平凡的,特别是如果您不习惯处理本机交互。WMI似乎也是一个有效的选项(我尝试通过PowerShell快速列出性能对象,并且似乎返回了所有提供程序的计数器),但是虽然您不需要知道如何与本机代码交互,但是您需要知道WMI,这是也很重要。


Chr*_*n.K 2

性能计数器(和类别)按区域设置注册。也就是说,您可以根据语言为它们指定不同的名称。

所有可用的性能类别及其计数器均注册在 Windows 注册表中的 下HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib009您将找到每种可用语言(例如英语)的子密钥。

内部工作的方法PerformanceCounterCategory.GetCategories()是首先检查“不变文化”类别。如果找到任何它将返回该集合。因此,如果由于某些错误或供应商的疏忽,某个类别仅适用于一种语言,则根据您当前的语言设置(操作系统或应用程序或两者),您将无法获得该类别。

我会首先检查密钥的内容HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\<langcode>\Counter,看看丢失的类别是否仅在其中一个类别中。一个相关的问题可能是这个(Google搜索),但我没有进一步检查。

坦率地说,我不知道有什么“更好”的方法来获取可用计数器的列表。如果您的问题属于上述问题(或相关问题),我宁愿尝试解决问题。