草庐IT

c# - PerformanceCounterCategory.GetCategories 与 Perfmon 不一致

coder 2024-05-23 原文

好的,所以我基本上是在尝试创建一个已安装的性能计数器类别列表,就像您在 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# - PerformanceCounterCategory.GetCategories 与 Perfmon 不一致的更多相关文章

    1. c# - 如何在 ruby​​ 中调用 C# dll? - 2

      如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

    2. C# 到 Ruby sha1 base64 编码 - 2

      我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

    3. 基于C#实现简易绘图工具【100010177】 - 2

      C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

    4. ruby - Ruby gsub 替换中的行为不一致? - 2

      两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio

    5. c# - C# 中的 Flatten Ruby 方法 - 2

      我如何做Ruby方法"Flatten"RubyMethod在C#中。此方法将锯齿状数组展平为一维数组。例如:s=[1,2,3]#=>[1,2,3]t=[4,5,6,[7,8]]#=>[4,5,6,[7,8]]a=[s,t,9,10]#=>[[1,2,3],[4,5,6,[7,8]],9,10]a.flatten#=>[1,2,3,4,5,6,7,8,9,10 最佳答案 递归解决方案:IEnumerableFlatten(IEnumerablearray){foreach(variteminarray){if(itemisIEnume

    6. ruby - 可以像在 C# 中使用#region 一样在 Ruby 中使用 begin/end 吗? - 2

      我最近从C#转向了Ruby,我发现自己无法制作可折叠的标记代码区域。我只是想到做这种事情应该没问题:classExamplebegin#agroupofmethodsdefmethod1..enddefmethod2..endenddefmethod3..endend...但是这样做真的可以吗?method1和method2最终与method3是同一种东西吗?还是有一些我还没有见过的用于执行此操作的Ruby惯用语? 最佳答案 正如其他人所说,这不会改变方法定义。但是,如果要标记方法组,为什么不使用Ruby语义来标记它们呢?您可以使用

    7. c# - Ruby 等效于 C# Linq 聚合方法 - 2

      什么是Linq聚合方法的ruby​​等价物。它的工作原理是这样的varfactorial=new[]{1,2,3,4,5}.Aggregate((acc,i)=>acc*i);每次将数组序列中的值传递给lambda时,变量acc都会累积。 最佳答案 这在数学以及几乎所有编程语言中通常称为折叠。它是更普遍的变形概念的一个实例。Ruby从Smalltalk中继承了这个特性的名称,它被称为inject:into:(像aCollectioninject:aStartValueinto:aBlock一样使用。)所以,在Ruby中,它称为inj

    8. c# - 先学什么? - 2

      关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭8年前。Improvethisquestion几年前我去学校学习编程,毕业后我找到了一份系统管理方面的工作,这就是我职业生涯的方向。我想重新开始某种开发,并且一直在“玩”C#和ASP.NET,但我已经听到很多关于其他"new"语言的讨论(新的意思是它们是新的)我)喜欢Ruby和F#。我想我想知道我是否在浪费时间学习主要的MS语言,而不是成为一名通才。很长一段时间没有离开开发社区(如果我曾经离开过的话)让我在潮流中挣扎,我不想落在时代的

    9. c# - 在 C# 中重现 Ruby OpenSSL private_encrypt 输出 - 2

      我有一个简单的Ruby脚本,我用它在某些HTTPheader上执行private_encrypt以签署要发送到ruby​​RESTAPI的Web请求,该API会根据Base64编码字符串测试Base64编码字符串生成而不是解码Base64和解密数据然后测试原始字符串。我使用的脚本是require"openssl"require"base64"path_to_cert=ARGV[0].dupplain_text=Base64.decode64(ARGV[1].dup)private_key=OpenSSL::PKey::RSA.new(File.read(path_to_cert))pu

    10. ruby - Mongoid 3 中 Rails 模型的强一致性 - 2

      我希望特定模型的所有数据库交互都通过集群中的mongo主节点,因此我将模型设置为使用强一致性。classPhotoincludeMongoid::Documentwithconsistency::strongfield:number,type:Integer#let'ssayaphotonumberisuniqueinthedbvalidate:unique_numberend但这似乎不起作用,因为当我保存两张非常靠近的照片时,我仍然遇到验证错误。photo1#dbhasnumber=1forthisobjectphoto1.update_attributes(number:2)pho

    随机推荐