Log Windows product type and installation type, and Linux libc version (#8688)
* Installation type * Product Type * Libc Release and Version * Catch all * Fix test * Fix mac test * Extract class * Remove CharSet * Remove extraneous assignment * Missing space * Typo * Fix comment XML * CR feedback
This commit is contained in:
parent
6944a40878
commit
f9c40ce94d
4 changed files with 221 additions and 1 deletions
134
src/dotnet/Telemetry/ExternalTelemetryProperties.cs
Normal file
134
src/dotnet/Telemetry/ExternalTelemetryProperties.cs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security;
|
||||||
|
using Microsoft.DotNet.PlatformAbstractions;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
|
{
|
||||||
|
// Some properties we need for telemetry, that don't yet have suitable
|
||||||
|
// public API
|
||||||
|
internal static class ExternalTelemetryProperties
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// For Windows, returns the OS installation type, eg. "Nano Server", "Server Core", "Server", or "Client".
|
||||||
|
/// For Unix, or on error, currently returns empty string.
|
||||||
|
/// </summary>
|
||||||
|
internal static string GetInstallationType()
|
||||||
|
{
|
||||||
|
if (RuntimeEnvironment.OperatingSystemPlatform != Platform.Windows)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const string Key = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion";
|
||||||
|
const string ValueName = @"InstallationType";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (string)Registry.GetValue(Key, ValueName, defaultValue: "");
|
||||||
|
}
|
||||||
|
// Catch everything: this is for telemetry only.
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.Assert(e is ArgumentException | e is SecurityException | e is InvalidCastException);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false)]
|
||||||
|
private static extern bool GetProductInfo(uint dwOSMajorVersion, uint dwOSMinorVersion, uint dwSpMajorVersion, uint dwSpMinorVersion, out uint pdwReturnedProductType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For Windows, returns the product type, loosely the SKU, as encoded by GetProductInfo().
|
||||||
|
/// For example, Enterprise is "4" (0x4) and Professional is "48" (0x30)
|
||||||
|
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724358(v=vs.85).aspx for the full list.
|
||||||
|
/// We're not attempting to decode the value on the client side as new Windows releases may add new values.
|
||||||
|
/// For Unix, or on error, returns an empty string.
|
||||||
|
/// </summary>
|
||||||
|
internal static string GetProductType()
|
||||||
|
{
|
||||||
|
if (RuntimeEnvironment.OperatingSystemPlatform != Platform.Windows)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (GetProductInfo((uint)Environment.OSVersion.Version.Major, (uint)Environment.OSVersion.Version.Minor, 0, 0, out uint productType))
|
||||||
|
{
|
||||||
|
return productType.ToString("D", CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Catch everything: this is for telemetry only
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.Assert(false, $"Unexpected exception from GetProductInfo: ${e.GetType().Name}: ${e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("libc", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern IntPtr gnu_get_libc_release();
|
||||||
|
|
||||||
|
[DllImport("libc", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern IntPtr gnu_get_libc_version();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If gnulibc is available, returns the release, such as "stable".
|
||||||
|
/// If the libc is musl, currently returns empty string.
|
||||||
|
/// Otherwise returns empty string.
|
||||||
|
/// </summary>
|
||||||
|
internal static string GetLibcRelease()
|
||||||
|
{
|
||||||
|
if (RuntimeEnvironment.OperatingSystemPlatform == Platform.Windows)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Marshal.PtrToStringUTF8(gnu_get_libc_release());
|
||||||
|
}
|
||||||
|
// Catch everything: this is for telemetry only
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.Assert(e is DllNotFoundException || e is EntryPointNotFoundException);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If gnulibc is available, returns the version, such as "2.22".
|
||||||
|
/// If the libc is musl, currently returns empty string. (In future could run "ldd -version".)
|
||||||
|
/// Otherwise returns empty string.
|
||||||
|
/// </summary>
|
||||||
|
internal static string GetLibcVersion()
|
||||||
|
{
|
||||||
|
if (RuntimeEnvironment.OperatingSystemPlatform == Platform.Windows)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Marshal.PtrToStringUTF8(gnu_get_libc_version());
|
||||||
|
}
|
||||||
|
// Catch everything: this is for telemetry only
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.Assert(e is DllNotFoundException || e is EntryPointNotFoundException);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,10 +6,14 @@ using System.Collections.Generic;
|
||||||
using Microsoft.DotNet.Cli.Utils;
|
using Microsoft.DotNet.Cli.Utils;
|
||||||
using Microsoft.DotNet.PlatformAbstractions;
|
using Microsoft.DotNet.PlatformAbstractions;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Security;
|
||||||
using Microsoft.DotNet.Configurer;
|
using Microsoft.DotNet.Configurer;
|
||||||
|
using Microsoft.Win32;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
|
using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
|
||||||
using RuntimeInformation = System.Runtime.InteropServices.RuntimeInformation;
|
using RuntimeInformation = System.Runtime.InteropServices.RuntimeInformation;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Cli.Telemetry
|
namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
{
|
{
|
||||||
|
@ -43,6 +47,10 @@ namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
private const string MachineId = "Machine ID";
|
private const string MachineId = "Machine ID";
|
||||||
private const string DockerContainer = "Docker Container";
|
private const string DockerContainer = "Docker Container";
|
||||||
private const string KernelVersion = "Kernel Version";
|
private const string KernelVersion = "Kernel Version";
|
||||||
|
private const string InstallationType = "Installation Type";
|
||||||
|
private const string ProductType = "Product Type";
|
||||||
|
private const string LibcRelease = "Libc Release";
|
||||||
|
private const string LibcVersion = "Libc Version";
|
||||||
|
|
||||||
private const string TelemetryProfileEnvironmentVariable = "DOTNET_CLI_TELEMETRY_PROFILE";
|
private const string TelemetryProfileEnvironmentVariable = "DOTNET_CLI_TELEMETRY_PROFILE";
|
||||||
private const string CannotFindMacAddress = "Unknown";
|
private const string CannotFindMacAddress = "Unknown";
|
||||||
|
@ -62,7 +70,11 @@ namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
{DockerContainer, _userLevelCacheWriter.RunWithCache(IsDockerContainerCacheKey, () => _dockerContainerDetector.IsDockerContainer().ToString("G") )},
|
{DockerContainer, _userLevelCacheWriter.RunWithCache(IsDockerContainerCacheKey, () => _dockerContainerDetector.IsDockerContainer().ToString("G") )},
|
||||||
{CurrentPathHash, _hasher(_getCurrentDirectory())},
|
{CurrentPathHash, _hasher(_getCurrentDirectory())},
|
||||||
{MachineId, _userLevelCacheWriter.RunWithCache(MachineIdCacheKey, GetMachineId)},
|
{MachineId, _userLevelCacheWriter.RunWithCache(MachineIdCacheKey, GetMachineId)},
|
||||||
{KernelVersion, GetKernelVersion()}
|
{KernelVersion, GetKernelVersion()},
|
||||||
|
{InstallationType, ExternalTelemetryProperties.GetInstallationType()},
|
||||||
|
{ProductType, ExternalTelemetryProperties.GetProductType()},
|
||||||
|
{LibcRelease, ExternalTelemetryProperties.GetLibcRelease()},
|
||||||
|
{LibcVersion, ExternalTelemetryProperties.GetLibcVersion()}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using Microsoft.DotNet.PlatformAbstractions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
public class UnixOnlyFactAttribute : FactAttribute
|
||||||
|
{
|
||||||
|
public UnixOnlyFactAttribute()
|
||||||
|
{
|
||||||
|
if (RuntimeEnvironment.OperatingSystemPlatform == Platform.Windows)
|
||||||
|
{
|
||||||
|
this.Skip = "This test requires Unix to run";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,6 +52,61 @@ namespace Microsoft.DotNet.Tests
|
||||||
unitUnderTest.GetTelemetryCommonProperties()["Kernel Version"].Should().Be(RuntimeInformation.OSDescription);
|
unitUnderTest.GetTelemetryCommonProperties()["Kernel Version"].Should().Be(RuntimeInformation.OSDescription);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[WindowsOnlyFact]
|
||||||
|
public void TelemetryCommonPropertiesShouldContainWindowsInstallType()
|
||||||
|
{
|
||||||
|
var unitUnderTest = new TelemetryCommonProperties(getMACAddress: () => null, userLevelCacheWriter: new NothingCache());
|
||||||
|
unitUnderTest.GetTelemetryCommonProperties()["Installation Type"].Should().NotBeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnixOnlyFact]
|
||||||
|
public void TelemetryCommonPropertiesShouldContainEmptyWindowsInstallType()
|
||||||
|
{
|
||||||
|
var unitUnderTest = new TelemetryCommonProperties(getMACAddress: () => null, userLevelCacheWriter: new NothingCache());
|
||||||
|
unitUnderTest.GetTelemetryCommonProperties()["Installation Type"].Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[WindowsOnlyFact]
|
||||||
|
public void TelemetryCommonPropertiesShouldContainWindowsProductType()
|
||||||
|
{
|
||||||
|
var unitUnderTest = new TelemetryCommonProperties(getMACAddress: () => null, userLevelCacheWriter: new NothingCache());
|
||||||
|
unitUnderTest.GetTelemetryCommonProperties()["Product Type"].Should().NotBeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnixOnlyFact]
|
||||||
|
public void TelemetryCommonPropertiesShouldContainEmptyWindowsProductType()
|
||||||
|
{
|
||||||
|
var unitUnderTest = new TelemetryCommonProperties(getMACAddress: () => null, userLevelCacheWriter: new NothingCache());
|
||||||
|
unitUnderTest.GetTelemetryCommonProperties()["Product Type"].Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[WindowsOnlyFact]
|
||||||
|
public void TelemetryCommonPropertiesShouldContainEmptyLibcReleaseAndVersion()
|
||||||
|
{
|
||||||
|
var unitUnderTest = new TelemetryCommonProperties(getMACAddress: () => null, userLevelCacheWriter: new NothingCache());
|
||||||
|
unitUnderTest.GetTelemetryCommonProperties()["Libc Release"].Should().BeEmpty();
|
||||||
|
unitUnderTest.GetTelemetryCommonProperties()["Libc Version"].Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MacOsOnlyFact]
|
||||||
|
public void TelemetryCommonPropertiesShouldContainEmptyLibcReleaseAndVersion2()
|
||||||
|
{
|
||||||
|
var unitUnderTest = new TelemetryCommonProperties(getMACAddress: () => null, userLevelCacheWriter: new NothingCache());
|
||||||
|
unitUnderTest.GetTelemetryCommonProperties()["Libc Release"].Should().BeEmpty();
|
||||||
|
unitUnderTest.GetTelemetryCommonProperties()["Libc Version"].Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[LinuxOnlyFact]
|
||||||
|
public void TelemetryCommonPropertiesShouldContainLibcReleaseAndVersion()
|
||||||
|
{
|
||||||
|
if (!RuntimeEnvironment.OperatingSystem.Contains("Alpine", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var unitUnderTest = new TelemetryCommonProperties(getMACAddress: () => null, userLevelCacheWriter: new NothingCache());
|
||||||
|
unitUnderTest.GetTelemetryCommonProperties()["Libc Release"].Should().NotBeEmpty();
|
||||||
|
unitUnderTest.GetTelemetryCommonProperties()["Libc Version"].Should().NotBeEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class NothingCache : IUserLevelCacheWriter
|
private class NothingCache : IUserLevelCacheWriter
|
||||||
{
|
{
|
||||||
public string RunWithCache(string cacheKey, Func<string> getValueToCache)
|
public string RunWithCache(string cacheKey, Func<string> getValueToCache)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue