Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 66 additions & 1 deletion src/System.Management.Automation/engine/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
using System.Threading;
using Microsoft.PowerShell.Commands;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;

using TypeTable = System.Management.Automation.Runspaces.TypeTable;

Expand Down Expand Up @@ -1548,6 +1547,72 @@ internal static bool IsSessionRestricted(ExecutionContext context)
}
return true;
}

/// <summary>
/// Determine whether the environment variable is set and how.
/// </summary>
/// <param name="name">The name of the environment variable.</param>
/// <param name="defaultValue">If the environment variable is not set, use this as the default value.</param>
/// <returns>A boolean representing the value of the environment variable.</returns>
internal static bool GetEnvironmentVariableAsBool(string name, bool defaultValue)
{
var str = Environment.GetEnvironmentVariable(name);
if (string.IsNullOrEmpty(str))
{
return defaultValue;
}

var boolStr = str.AsSpan();

if (boolStr.Length == 1)
Copy link
Collaborator

@jborean93 jborean93 Sep 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you possibly use SequenceEqual possibly with or without StringComparer.OrdinalIgnoreCase to avoid the individual length and char checks. I assume .NET has all the necessary logic to make that performant and you could make this a lot simpler by doing something like

ReadOnlySpan<char> true1 = ['1'];
ReadOnlySpan<char> trueYes = ['y', 'e', 's'];
ReadOnlySpan<char> trueTrue = ['t', 'r', 'u', 'e'];

// Repeat for false checks

var boolStr = str.AsSpan()
if (boolStr.SequenceEquals(true1, StringComparer.OrdinalIgnoreCase) ||
    boolStr.SequenceEquals(trueYes, StringComparer.OrdinalIgnoreCase ||
    ...)
{
    return true;
}

// Repeat for false checks and default
...

Not sure if stackalloc'ing one big span with all the options and slicing it would be more efficient or not but that's probably in micro-optimising territory.

Edit: Ah I see this has been copied from elsewhere.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is copied from Telemetry.cs. I will keep it as is in this PR as this one will be included in the next preview and the following RC. But I will submit a separate PR to make this optimization change.

{
if (boolStr[0] == '1')
{
return true;
}

if (boolStr[0] == '0')
{
return false;
}
}

if (boolStr.Length == 3 &&
(boolStr[0] == 'y' || boolStr[0] == 'Y') &&
(boolStr[1] == 'e' || boolStr[1] == 'E') &&
(boolStr[2] == 's' || boolStr[2] == 'S'))
{
return true;
}

if (boolStr.Length == 2 &&
(boolStr[0] == 'n' || boolStr[0] == 'N') &&
(boolStr[1] == 'o' || boolStr[1] == 'O'))
{
return false;
}

if (boolStr.Length == 4 &&
(boolStr[0] == 't' || boolStr[0] == 'T') &&
(boolStr[1] == 'r' || boolStr[1] == 'R') &&
(boolStr[2] == 'u' || boolStr[2] == 'U') &&
(boolStr[3] == 'e' || boolStr[3] == 'E'))
{
return true;
}

if (boolStr.Length == 5 &&
(boolStr[0] == 'f' || boolStr[0] == 'F') &&
(boolStr[1] == 'a' || boolStr[1] == 'A') &&
(boolStr[2] == 'l' || boolStr[2] == 'L') &&
(boolStr[3] == 's' || boolStr[3] == 'S') &&
(boolStr[4] == 'e' || boolStr[4] == 'E'))
{
return false;
}

return defaultValue;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.Win32.SafeHandles;

Expand Down Expand Up @@ -500,13 +499,14 @@ static RemoteSessionNamedPipeServer()
{
s_syncObject = new object();

// All PowerShell instances will start with the named pipe
// and listener created and running.
IPCNamedPipeServerEnabled = true;

CreateIPCNamedPipeServerSingleton();
// Unless opt-out, all PowerShell instances will start with the named-pipe listener created and running.
IPCNamedPipeServerEnabled = !Utils.GetEnvironmentVariableAsBool(name: "POWERSHELL_DIAGNOSTICS_OPTOUT", defaultValue: false);

CreateProcessExitHandler();
if (IPCNamedPipeServerEnabled)
{
CreateIPCNamedPipeServerSingleton();
CreateProcessExitHandler();
}
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1411,7 +1411,7 @@ All WinRM sessions connected to PowerShell session configurations, such as Micro
<value>Multiple processes were found with this name {0}. Use the process Id to specify a single process to enter.</value>
</data>
<data name="EnterPSHostProcessNoPowerShell" xml:space="preserve">
<value>Cannot enter process with Id '{0}' because it has not loaded the PowerShell engine.</value>
<value>Cannot enter process with Id '{0}' because it has not loaded the PowerShell engine or the named-pipe listener was disabled.</value>
</data>
<data name="EnterPSHostProcessNoProcessFoundWithId" xml:space="preserve">
<value>No process was found with Id: {0}.</value>
Expand Down
69 changes: 1 addition & 68 deletions src/System.Management.Automation/utils/Telemetry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Linq;
using System.Management.Automation;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Threading;

using Microsoft.ApplicationInsights;
Expand Down Expand Up @@ -177,7 +176,7 @@ public static class ApplicationInsightsTelemetry
static ApplicationInsightsTelemetry()
{
// If we can't send telemetry, there's no reason to do any of this
CanSendTelemetry = !GetEnvironmentVariableAsBool(name: _telemetryOptoutEnvVar, defaultValue: false);
CanSendTelemetry = !Utils.GetEnvironmentVariableAsBool(name: _telemetryOptoutEnvVar, defaultValue: false);
if (CanSendTelemetry)
{
s_sessionId = Guid.NewGuid().ToString();
Expand Down Expand Up @@ -643,72 +642,6 @@ static ApplicationInsightsTelemetry()
}
}

/// <summary>
/// Determine whether the environment variable is set and how.
/// </summary>
/// <param name="name">The name of the environment variable.</param>
/// <param name="defaultValue">If the environment variable is not set, use this as the default value.</param>
/// <returns>A boolean representing the value of the environment variable.</returns>
private static bool GetEnvironmentVariableAsBool(string name, bool defaultValue)
{
var str = Environment.GetEnvironmentVariable(name);
if (string.IsNullOrEmpty(str))
{
return defaultValue;
}

var boolStr = str.AsSpan();

if (boolStr.Length == 1)
{
if (boolStr[0] == '1')
{
return true;
}

if (boolStr[0] == '0')
{
return false;
}
}

if (boolStr.Length == 3 &&
(boolStr[0] == 'y' || boolStr[0] == 'Y') &&
(boolStr[1] == 'e' || boolStr[1] == 'E') &&
(boolStr[2] == 's' || boolStr[2] == 'S'))
{
return true;
}

if (boolStr.Length == 2 &&
(boolStr[0] == 'n' || boolStr[0] == 'N') &&
(boolStr[1] == 'o' || boolStr[1] == 'O'))
{
return false;
}

if (boolStr.Length == 4 &&
(boolStr[0] == 't' || boolStr[0] == 'T') &&
(boolStr[1] == 'r' || boolStr[1] == 'R') &&
(boolStr[2] == 'u' || boolStr[2] == 'U') &&
(boolStr[3] == 'e' || boolStr[3] == 'E'))
{
return true;
}

if (boolStr.Length == 5 &&
(boolStr[0] == 'f' || boolStr[0] == 'F') &&
(boolStr[1] == 'a' || boolStr[1] == 'A') &&
(boolStr[2] == 'l' || boolStr[2] == 'L') &&
(boolStr[3] == 's' || boolStr[3] == 'S') &&
(boolStr[4] == 'e' || boolStr[4] == 'E'))
{
return false;
}

return defaultValue;
}

/// <summary>
/// Send module load telemetry as a metric.
/// For modules we send the module name (if allowed), and the version.
Expand Down
Loading