[release/6.0] Backport Fix PerformanceCounter's when running with Globalization Invar...
authorTarek Mahmoud Sayed <tarekms@microsoft.com>
Thu, 10 Mar 2022 20:36:32 +0000 (12:36 -0800)
committerGitHub <noreply@github.com>
Thu, 10 Mar 2022 20:36:32 +0000 (12:36 -0800)
* [dotnet/runtime] Backport Fix PerformanceCounter's when running with Globalization Invariant Mode (PR #65414)

* Add GeneratePackageOnBuild & ServicingVersion project properties

src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj
src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs
src/libraries/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterTests.cs

index 5d99e22..8de7e12 100644 (file)
@@ -4,6 +4,8 @@
     <TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0;net461</TargetFrameworks>
     <NoWarn>$(NoWarn);CA1847</NoWarn>
     <IsPackable>true</IsPackable>
+    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+    <ServicingVersion>1</ServicingVersion>
     <PackageDescription>Provides the System.Diagnostics.PerformanceCounter class, which allows access to Windows performance counters.
 
 Commonly Used Types:
index bdd47b9..567a8bf 100644 (file)
@@ -45,12 +45,12 @@ namespace System.Diagnostics
         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 @@ namespace System.Diagnostics
             }
         }
 
+        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 @@ namespace System.Diagnostics
 
         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 @@ namespace System.Diagnostics
 
         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 @@ namespace System.Diagnostics
                 culture = culture.Parent;
             }
 
-            library = GetPerformanceCounterLib(machineName, new CultureInfo(EnglishLCID));
+            library = GetPerformanceCounterLib(machineName, EnglishCulture);
             return library.GetCategories();
         }
 
@@ -772,7 +791,7 @@ namespace System.Diagnostics
 
             //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 @@ namespace System.Diagnostics
 
             //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 @@ namespace System.Diagnostics
 
         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 @@ namespace System.Diagnostics
 
         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 @@ namespace System.Diagnostics
 
             //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 @@ namespace System.Diagnostics
 
             //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 @@ namespace System.Diagnostics
 
         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 @@ namespace System.Diagnostics
 
         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 @@ namespace System.Diagnostics
         {
             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)
index bfaf5fb..c8df93e 100644 (file)
@@ -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 @@ namespace System.Diagnostics.Tests
             }
         }
 
+        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);