c# - PerformanceCounterCategory.GetCategories 与 Perfmon 不一致

标签 c# .net performancecounter perfmon

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

System.Diagnostics.PerformanceCounterCategory.GetCategories()

这似乎有效,直到您检查列表,并发现其中一些丢失了。我发现缺少的第一个是 ReadyBoost 缓存。这是因为该项目被设置为在“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();
        }
    }
}

这现在给了我 3236 个结果。因为它获取了系统中的所有性能计数器。所以我想我需要做的就是过滤掉那些实际上是性能计数器的东西,让我只剩下类别。然而, PerformanceCounter 似乎没有一个只采用名称的构造函数(因为这不是唯一的),也没有一个采用索引值的构造函数。我发现了一个名为 Performance Data Helper 的 Win32 API,但这似乎也没有我想要的功能。所以。如果我有性能计数器索引,我如何在 C# 中获取该索引的 PerformanceCounterCategory? PerfMon 做到了,所以它一定是可能的。有什么方法可以解析索引“魔数(Magic Number)”以找出哪个是哪个?

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

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

然而,取决于安装了 PowerShell/Windows SDK 的用户,这并不是一个真正的选择。

任何人都有任何想法?是否有一些绝密的第 3 版性能计数器类别隐藏在注册表中的其他位置,我需要追踪?我不仅没有尝试的想法,而且我也没有尝试的坏想法。我可以在 perfmon 上使用任何 secret 命令行开关来让它列出所有类别吗?

最佳答案

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

幕后制作,PerformanceCounterCategory使用 Registry Functions获取有关当前在性能子系统中注册的类别(也称为对象)、实例和计数器的信息。您可以通过查看 PerformanceCounterCategory 的代码来验证这一点。与 ILSpy。

计数器可以通过两种类型的提供者注册:“核心”-提供者和“可扩展性”-提供者。这些名字是我发明的,因为没有更好的选择。

Core-providers 内置于 Windows 深处,并与 Performance 子系统密切交互,以提供诸如 Process、System 等计数器。如果您无法通过 PerformanceCounterCategory 看到此类计数器 | ,很可能您的 Windows 安装存在一些深层问题,至少是 these errors 的某些问题。在您的事件日志中。我认为这不是你的情况。

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

如果您看不透PerformanceCounterCategory通过 Perflib 注册的计数器,首先可能是它们的提供程序在系统中配置不正确或配置已损坏。在这种情况下,您的事件日志中应该有一些在性能计数器加载或性能库可用性部分中定义的错误 these docs .我认为这不是你的情况。

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

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

Here是描述 Perflib v2 提供者的这种自注册的文档的相关部分:

Your provider must call the CounterInitialize and CounterCleanup functions. The CounterInitialize calls the PerfStartProvider function to register the provider and also calls the PerfSetCounterSetInfo function to initialize the counter set. The CounterCleanup calls the PerfStopProvider function to remove the provider's registration.



总结 ,有两种不同的列出类别的方式,实例和计数器。一种是查询Registry Functions,列出查询时注册的所有项目。另一种方法是查看注册表中写入的描述提供程序的配置信息,无论它们在查询时是否已在性能子系统中注册。

在实践中,您需要结合使用这两种方式,因为您只能通过查询注册表函数来获取类别的实例,并且您只能通过查询注册表中编写的配置来获取尚未注册的提供者的类别和计数器.

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

您可以使用 BranchCache 测试以上是否适用于您。类别。下面的示例在 Win 7 上进行了测试。
  • 确保显示名称为 BranchCache 的 Windows 服务已启动,然后运行此 C# 代码:
    Debug.WriteLine((new PerformanceCounterCategory("BranchCache")).ReadCategory().Keys.Count);
    

    你应该没有错误和 21写入调试输出。
  • 现在停止 BranchCache服务并再次运行 C# 代码。您将收到一个异常,因为该类别不再注册到性能子系统,因此 PerformanceCounterCategory找不到它。

  • 为了确保我描述的内容适用于您通过 PerformanceCounterCategory.GetCategories() 丢失的计数器, 检查丢失的计数器是否显示为 typeperf -qx在名称与注册表中配置的提供商相关联的类别上,位于 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers 下.

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

    关于c# - PerformanceCounterCategory.GetCategories 与 Perfmon 不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15925092/

    相关文章:

    c# - 您如何获得计算机的 RAM 总量?

    c# 不相关对象之间的类型转换

    c# - 获取属性名称的 Linq 表达式和扩展方法

    .net - Excel Interop条件格式

    c# - 如何使用 ASP.NET MVC 5.0 的 ASP.NET Identity 实现密码重置?

    c++ - 如何使用性能计数器控制从文件中读取?

    azure - 如何在azure中启用性能计数器

    c# - 如何从 WebBrowser 控件获取 XML (RAW/SOURCE)

    c# - 无法使用 ADO.NET 建立与 SQL Server 的连接

    c# - 如何学习 LINQ 中的查询