diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj index 5d99e220260f19..8de7e12d450d6a 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj @@ -4,6 +4,8 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0;net461 $(NoWarn);CA1847 true + true + 1 Provides the System.Diagnostics.PerformanceCounter class, which allows access to Windows performance counters. Commonly Used Types: diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs b/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs index bdd47b922779d8..567a8bf433f1f7 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs @@ -45,12 +45,12 @@ internal sealed class PerformanceCounterLib private const string LanguageKeyword = "language"; private const string DllName = "netfxperf.dll"; - private const int EnglishLCID = 0x009; - private static volatile string s_computerName; private static volatile string s_iniFilePath; private static volatile string s_symbolFilePath; + private static CultureInfo s_englishCulture; + private PerformanceMonitor _performanceMonitor; private readonly string _machineName; private readonly string _perfLcid; @@ -79,6 +79,25 @@ private static object InternalSyncObject } } + private static CultureInfo EnglishCulture + { + get + { + if (s_englishCulture is null) + { + try + { + s_englishCulture = CultureInfo.GetCultureInfo("en"); + } + catch + { + s_englishCulture = CultureInfo.InvariantCulture; + } + } + return s_englishCulture; + } + } + internal PerformanceCounterLib(string machineName, string lcid) { _machineName = machineName; @@ -277,11 +296,11 @@ private static string SymbolFilePath internal static bool CategoryExists(string machine, string category) { - PerformanceCounterLib library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID)); + PerformanceCounterLib library = GetPerformanceCounterLib(machine, EnglishCulture); if (library.CategoryExists(category)) return true; - if (CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) + if (CultureInfo.CurrentCulture.Parent.Name != EnglishCulture.Name) { CultureInfo culture = CultureInfo.CurrentCulture; while (culture != CultureInfo.InvariantCulture) @@ -349,11 +368,11 @@ internal void Close() internal static bool CounterExists(string machine, string category, string counter) { - PerformanceCounterLib library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID)); + PerformanceCounterLib library = GetPerformanceCounterLib(machine, EnglishCulture); bool categoryExists = false; bool counterExists = library.CounterExists(category, counter, ref categoryExists); - if (!categoryExists && CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) + if (!categoryExists && CultureInfo.CurrentCulture.Parent.Name != EnglishCulture.Name) { CultureInfo culture = CultureInfo.CurrentCulture; while (culture != CultureInfo.InvariantCulture) @@ -753,7 +772,7 @@ internal static string[] GetCategories(string machineName) culture = culture.Parent; } - library = GetPerformanceCounterLib(machineName, new CultureInfo(EnglishLCID)); + library = GetPerformanceCounterLib(machineName, EnglishCulture); return library.GetCategories(); } @@ -772,7 +791,7 @@ internal static string GetCategoryHelp(string machine, string category) //First check the current culture for the category. This will allow //PerformanceCounterCategory.CategoryHelp to return localized strings. - if (CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) + if (CultureInfo.CurrentCulture.Parent.Name != EnglishCulture.Name) { CultureInfo culture = CultureInfo.CurrentCulture; @@ -788,7 +807,7 @@ internal static string GetCategoryHelp(string machine, string category) //We did not find the category walking up the culture hierarchy. Try looking // for the category in the default culture English. - library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID)); + library = GetPerformanceCounterLib(machine, EnglishCulture); help = library.GetCategoryHelp(category); if (help == null) @@ -808,9 +827,9 @@ private string GetCategoryHelp(string category) internal static CategorySample GetCategorySample(string machine, string category) { - PerformanceCounterLib library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID)); + PerformanceCounterLib library = GetPerformanceCounterLib(machine, EnglishCulture); CategorySample sample = library.GetCategorySample(category); - if (sample == null && CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) + if (sample == null && CultureInfo.CurrentCulture.Parent.Name != EnglishCulture.Name) { CultureInfo culture = CultureInfo.CurrentCulture; while (culture != CultureInfo.InvariantCulture) @@ -845,11 +864,11 @@ private CategorySample GetCategorySample(string category) internal static string[] GetCounters(string machine, string category) { - PerformanceCounterLib library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID)); + PerformanceCounterLib library = GetPerformanceCounterLib(machine, EnglishCulture); bool categoryExists = false; string[] counters = library.GetCounters(category, ref categoryExists); - if (!categoryExists && CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) + if (!categoryExists && CultureInfo.CurrentCulture.Parent.Name != EnglishCulture.Name) { CultureInfo culture = CultureInfo.CurrentCulture; while (culture != CultureInfo.InvariantCulture) @@ -910,7 +929,7 @@ internal static string GetCounterHelp(string machine, string category, string co //First check the current culture for the counter. This will allow //PerformanceCounter.CounterHelp to return localized strings. - if (CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) + if (CultureInfo.CurrentCulture.Parent.Name != EnglishCulture.Name) { CultureInfo culture = CultureInfo.CurrentCulture; while (culture != CultureInfo.InvariantCulture) @@ -925,7 +944,7 @@ internal static string GetCounterHelp(string machine, string category, string co //We did not find the counter walking up the culture hierarchy. Try looking // for the counter in the default culture English. - library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID)); + library = GetPerformanceCounterLib(machine, EnglishCulture); help = library.GetCounterHelp(category, counter, ref categoryExists); if (!categoryExists) @@ -990,7 +1009,8 @@ private static string[] GetLanguageIds() internal static PerformanceCounterLib GetPerformanceCounterLib(string machineName, CultureInfo culture) { - string lcidString = culture.LCID.ToString("X3", CultureInfo.InvariantCulture); + // EnglishCulture.LCID == 9 will be false only if running with Globalization Invariant Mode. Use "009" at that time as default English language identifier. + string lcidString = EnglishCulture.LCID == 9 ? culture.LCID.ToString("X3", CultureInfo.InvariantCulture) : "009"; machineName = (machineName == "." ? ComputerName : machineName).ToLowerInvariant(); @@ -1135,11 +1155,11 @@ private Hashtable GetStringTable(bool isHelp) internal static bool IsCustomCategory(string machine, string category) { - PerformanceCounterLib library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID)); + PerformanceCounterLib library = GetPerformanceCounterLib(machine, EnglishCulture); if (library.IsCustomCategory(category)) return true; - if (CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) + if (CultureInfo.CurrentCulture.Parent.Name != EnglishCulture.Name) { CultureInfo culture = CultureInfo.CurrentCulture; while (culture != CultureInfo.InvariantCulture) @@ -1174,10 +1194,10 @@ internal static PerformanceCounterCategoryType GetCategoryType(string machine, s { PerformanceCounterCategoryType categoryType = PerformanceCounterCategoryType.Unknown; - PerformanceCounterLib library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID)); + PerformanceCounterLib library = GetPerformanceCounterLib(machine, EnglishCulture); if (!library.FindCustomCategory(category, out categoryType)) { - if (CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) + if (CultureInfo.CurrentCulture.Parent.Name != EnglishCulture.Name) { CultureInfo culture = CultureInfo.CurrentCulture; while (culture != CultureInfo.InvariantCulture) diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterTests.cs b/src/libraries/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterTests.cs index bfaf5fba64b3de..c8df93e8c267b4 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterTests.cs +++ b/src/libraries/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterTests.cs @@ -2,8 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Globalization; using System.Collections; using System.Collections.Specialized; +using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.Diagnostics.Tests @@ -287,6 +289,32 @@ public static void PerformanceCounter_NextSample_MultiInstance() } } + private static bool CanRunInInvariantMode => RemoteExecutor.IsSupported && !PlatformDetection.IsNetFramework; + + [ConditionalTheory(nameof(CanRunInInvariantMode))] + [PlatformSpecific(TestPlatforms.Windows)] + [InlineData(true)] + [InlineData(false)] + public static void RunWithGlobalizationInvariantModeTest(bool predefinedCultures) + { + ProcessStartInfo psi = new ProcessStartInfo() { UseShellExecute = false }; + psi.Environment.Add("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1"); + psi.Environment.Add("DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY", predefinedCultures ? "1" : "0"); + RemoteExecutor.Invoke(() => + { + // Ensure we are running inside the Globalization invariant mode. + Assert.Equal("", CultureInfo.CurrentCulture.Name); + + // This test ensure creating PerformanceCounter object while we are running with Globalization Invariant Mode. + // PerformanceCounter used to create cultures using LCID's which fail in Globalization Invariant Mode. + // This test ensure no failure should be encountered in this case. + using (PerformanceCounter counterSample = new PerformanceCounter("Processor", "Interrupts/sec", "0", ".")) + { + Assert.Equal("Processor", counterSample.CategoryName); + } + }, new RemoteInvokeOptions { StartInfo = psi}).Dispose(); + } + public static PerformanceCounter CreateCounterWithCategory(string name, bool readOnly, PerformanceCounterCategoryType categoryType ) { var category = Helpers.CreateCategory(name, categoryType);