15

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

System.Diagnostics.PerformanceCounterCategory.GetCategories()

这似乎有效,直到您检查列表并发现其中一些丢失。我发现的第一个缺失是 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();
        }
    }
}

这现在给了我 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();
        }
    }
}

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

但是,根据安装了 PowerShell/Windows SDK 的用户,这并不是一个真正的选择。

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

4

2 回答 2

6

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

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

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

Core-provider 内置在 Windows 深处,并与 Performance 子系统紧密连接,以提供诸如 Process、System 等的计数器。如果您无法通过 看到此类计数器PerformanceCounterCategory,则很可能您有一些深层次的问题与您的 Windows 安装以及事件日志中的至少一些错误有关。我认为这不是你的情况。

Extensibility-providers 通过记录在案的Perflib 接口与 Performance 子系统接口,以提供所有其他计数器。需要注意的是,某些 Windows 功能的计数器是通过可扩展性提供程序注册的,主要 MS 产品(如 SQL Server、.NET Framework 等)的计数器也是如此。因此,核心提供程序并不是针对 MS 和可扩展性所做的一切- 第 3 方的供应商。

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

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

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

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

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

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

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

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

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

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

    Debug.WriteLine((new PerformanceCounterCategory("BranchCache")).ReadCategory().Keys.Count);
    

    您应该没有错误并21写入调试输出。

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

为确保我所描述的内容适用于您通过 缺少的PerformanceCounterCategory.GetCategories()计数器,请检查缺少的计数器是否显示typeperf -qx在类别中,其名称与注册表中配置的提供程序相关联HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers

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

于 2016-10-31T13:08:28.883 回答
1

性能计数器(和类别)按语言环境注册。也就是说,您可以根据语言为它们取不同的名称。

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

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

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

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

于 2013-04-10T12:39:24.760 回答