PerformanceCounterCategory.GetCategories несовместим с Perfmon

Хорошо, поэтому я в основном пытаюсь создать список установленных категорий счетчиков производительности, например тот, который вы получаете в PerfMon. Для этого я использую

System.Diagnostics.PerformanceCounterCategory.GetCategories()

который, похоже, работает, пока вы не проверите список и не узнаете, что некоторые из них отсутствуют. Первым, что я заметил, был кеш ReadyBoost. Это было связано с тем, что проект был настроен на компиляцию на "x86". Изменение этого параметра на "Любой процессор" исправило эту проблему.

Однако все еще есть некоторые из них, которые отсутствуют, например, одна из тестовых машин имеет категорию "Приложения для диспетчера авторизации" (у меня нет, и никто, кажется, не знает, почему и откуда она). Однако эта машина, эта категория счетчиков производительности отображается в PerfMon, но не при вызове метода GetCategories() из С#.

Кто-нибудь знает, почему? Есть ли более надежный способ получить 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, как представляется, не является конструктором, который принимает только имя (потому что это не уникально), и, похоже, не существует того, которое принимает значение индекса. Я обнаружил API Win32 с именем Performance Data Helper, но у меня, похоже, нет той функциональности, в которой я хочу. Так. Если у меня есть индекс производительности, как мне, на С# получить 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();
        }
    }
}

На моей машине я получаю 138 с использованием .Net framework, 117 путем разбора реестра и 157 с помощью PowerShell (что является правильным ответом).

Однако в зависимости от пользователя, у которого установлен PowerShell/Windows SDK, это не вариант.

У кого-нибудь есть идеи вообще? Есть ли какие-то сверхсекретные версии счетчиков производительности, скрытые где-то еще в реестре, что мне нужно отслеживать? У меня не просто есть идеи, чтобы попытаться, у меня не хватало плохих идей, чтобы попробовать. Есть ли какие-либо секретные ключи командной строки, которые я могу использовать для perfmon, чтобы отобразить список всех категорий?

Ответ 1

Счетчики производительности (и категории) регистрируются по языку. То есть вы можете иметь разные имена для них в зависимости от языка.

Все доступные категории производительности и их счетчики зарегистрированы в реестре Windows в разделе HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib. Вы найдете дополнительный ключ для каждого доступного языка (например, 009 для английского языка).

Внутренний метод PerformanceCounterCategory.GetCategories() - это сначала проверить категории "инвариантная культура". Если он найдет что-либо, он вернет этот набор. Таким образом, если из-за некоторой ошибки или проверки поставщика категория доступна только на одном языке, вы не получите ее в зависимости от вашего текущего языкового параметра (либо ОС, либо приложения, либо и того и другого).

Сначала я должен проверить содержимое клавиш HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\<langcode>\Counter и посмотреть, может быть, недостающая категория находится только в одном из них. Связанная с этим проблема может быть этот (поиск Google), но я еще не проверил.

Честно говоря, я не знаю "лучшего" способа получить список доступных счетчиков. Если ваша проблема - это описанная выше (или связанная), я скорее попытаюсь увидеть, чтобы ситуация была исправлена.

Ответ 2

Я думаю, вы столкнулись с тем, что я квалифицирую как ошибку .NET Framework, вызванную счетчиками Perflib v2.

За кулисами PerformanceCounterCategory использует Функции реестра, чтобы получить информацию о категориях (например, объектах), экземплярах и счетчиках, зарегистрированных в настоящее время с помощью подсистемы производительности. Вы можете проверить это, посмотрев код для PerformanceCounterCategory с помощью ILSpy.

Счетчики могут регистрироваться через двух типов поставщиков: "core" -providers и "extensibility" -providers. Эти имена были изобретены мной из-за отсутствия лучшего варианта.

Core-провайдеры глубоко встроены в Windows и тесно взаимодействуют с подсистемой Performance, чтобы обеспечить счетчики, такие как Process, System и т.д. Если вы не можете видеть этот счетчик через PerformanceCounterCategory, это очень вероятно, у вас есть некоторые проблемы с установкой Windows и по крайней мере некоторые из этих ошибок в ваших журналах событий. Я предполагаю, что это не ваш случай.

Интерфейс расширяемости-провайдера с подсистемой Performance через документированный интерфейс Perlib чтобы предоставить все остальные счетчики. Важно отметить, что счетчики для некоторых функций Windows регистрируются через распространители-распространители, а также счетчики для основных продуктов MS, таких как SQL Server,.NET Framework и т.д. Таким образом, это не то, что основные поставщики - это все, что сделано MS и расширяемость -провайдеры для третьих сторон.

Если вы не можете просмотреть счетчики PerformanceCounterCategory, зарегистрированные через Perflib, сначала может быть, что их провайдер неправильно настроен в системе или что конфигурация была нарушена. В этом случае вы должны иметь в своем журнале событий некоторые ошибки, определенные в разделах "Доступность производительности" или "Доступность библиотеки производительности" из этих документов. Я предполагаю, что это не ваш случай.

Вторая причина связана с тем, как провайдеры Perflib работают за кулисами. Для регистрации своих счетчиков требуются два основных шага. Первый шаг - написать конфигурацию для поставщиков в реестре, используя LodCtr.exe. Я предполагаю, что это было сделано автоматически для вас установщиками счетчиков, в которые вы заинтересованы, и что конфигурация правильная, тем более, что если возникли проблемы с этой конфигурацией, скорее всего, некоторые из вышеупомянутых ошибок в событии Журнал. Второй шаг - фактически зарегистрировать поставщика Perflib подсистемой Performance.

Теперь мы приближаемся к проблеме. Регистрация выполняется по-разному для поставщиков Perflib v1 и v2. Для v1 код для провайдеров записывается в DLL, на которые ссылается конфигурация реестра, написанная на первом шаге, и загружается самой системой. Таким образом, регистрация провайдера Perflib v1 происходит автоматически, когда система считывает информацию о конфигурации из реестра и загружает библиотеки DLL. Для поставщиков Perflib v2 все по-другому. Код для поставщиков больше не выполняется непосредственно системой, а приложением/службой, связанными с поставщиками. Поэтому, если вы пишете приложение, которое создает пользовательские поставщики/счетчики, используя Perflib v2, ваше приложение также запускает код для сбора данных для этих поставщиков, и он будет взаимодействовать с подсистемой Performance документированным способом. Проблема заключается в том, что код, выполняющий регистрацию поставщиков Perflib v2 с системой, теперь должен запускаться приложением, на котором размещается код провайдера (в отличие от автоматического запуска системы как для Perflib v1). Например, если приложение является сервисом Windows, а служба еще не запущена, поставщики не будут зарегистрированы в подсистеме Performance, и их счетчики не будут видны (пока) через функции реестра / PerformanceCounterCategory.

Здесь - соответствующая часть документа, описывающая эту саморегистрацию для поставщиков Perlib v2:

Ваш провайдер должен вызывать функции CounterInitialize и CounterCleanup. CounterInitialize вызывает функцию PerfStartProvider для регистрации провайдера, а также вызывает функцию PerfSetCounterSetInfo для инициализации набора счетчиков. CounterCleanup вызывает функцию PerfStopProvider для удаления регистрации провайдера.

В заключение, существует два разных способа перечисления категорий, экземпляров и счетчиков. Один из них - запрос функций реестра и список всех элементов, зарегистрированных во время запроса. Другим является просмотр информации о конфигурации, записанной в реестре, описывающей поставщиков независимо от того, зарегистрированы они или нет в подсистеме Performance во время запроса.

На практике вам нужно будет использовать комбинацию из двух способов, потому что вы можете получать экземпляры только для категорий, запрашивая функции реестра, и вы можете получать только категории и счетчики для поставщиков, которые еще не были зарегистрированы, запросив написанные конфигурации в реестре.

К сожалению, PerformanceCounterCategory запрашивает только функции реестра и поэтому не может получить информацию о поставщиках Perflib v2, которые еще не зарегистрированы. Вы можете видеть этих поставщиков с помощью других средств, например, через Performance Monitor MMC (который за кулисами использует API PDH, который может показывать комбо зарегистрированных и еще не зарегистрированных счетчиков) или typeperf.exe -qx.

Вы можете протестировать вышеизложенное применительно к вам с категорией BranchCache. Пример ниже был протестирован на Win 7.

  • Убедитесь, что служба Windows с отображаемым именем BranchCache запущена, а затем запустите этот код С#:

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

    Вы должны получить ошибку и 21, записанные на вывод отладки.

  • Теперь остановите службу BranchCache и запустите код С# еще раз. Вы получите исключение, так как категория больше не регистрируется в подсистеме Performance и поэтому PerformanceCounterCategory не находит ее.

Чтобы обеспечить то, что я описал, применимо к счетчикам, которые вам не хватает через PerformanceCounterCategory.GetCategories(), проверьте, чтобы отсутствующие счетчики отображались typeperf -qx в категориях с именами, связанными с поставщиками, настроенными в реестре где-то в разделе HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers.

Решение состоит в том, чтобы написать оболочку С# для PDH API и получить вашу информацию таким образом. Это нетривиально, особенно если вы не привыкли к работе с родными взаимодействиями. WMI также представляется допустимым вариантом (я попробовал быстрый список объектов производительности через PowerShell, и кажется, что возвращаются счетчики для всех поставщиков), но пока вам не нужно знать, как взаимодействовать с собственным кодом, вам нужно знать WMI, что также нетривиально.