Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge 'main' into 'release_mdd' #1414

Merged
merged 4 commits into from
Aug 10, 2023
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
4 changes: 2 additions & 2 deletions build/version.settings.targets
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<!--SxS: These three properties should be changed at the start of a new version, VersionZeroYear should be the year
before the start of the project.-->
<MajorVersion>17</MajorVersion>
<MinorVersion>4</MinorVersion>
<VersionZeroYear>2020</VersionZeroYear>
<MinorVersion>8</MinorVersion>
<VersionZeroYear>2022</VersionZeroYear>
<!-- Note: for compatibility, we leave the default assembly version of the repo at 14.0.
If we ever decide to change this, make sure that you notify partner teams such as C++ IOT -->
<AssemblyVersion Condition="'$(AssemblyVersion)'==''">14.0.0.0</AssemblyVersion>
Expand Down
36 changes: 27 additions & 9 deletions src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -690,17 +690,11 @@ private async Task<List<LaunchCommand>> GetInitializeCommands()
LocalLaunchOptions localLaunchOptions = _launchOptions as LocalLaunchOptions;
if (this.IsCoreDump)
{
// Add executable information
this.AddExecutablePathCommand(commands);
// Load executable and core dump
this.AddExecutableAndCorePathCommand(commands);

// Important: this must occur after file-exec-and-symbols but before anything else.
// Important: this must occur after executable load but before anything else.
this.AddGetTargetArchitectureCommand(commands);

// Add core dump information (linux/mac does not support quotes around this path but spaces in the path do work)
string coreDump = this.UseUnixPathSeparators ? _launchOptions.CoreDumpPath : this.EnsureProperPathSeparators(_launchOptions.CoreDumpPath, true);
string coreDumpCommand = _launchOptions.DebuggerMIMode == MIMode.Lldb ? String.Concat("target create --core ", coreDump) : String.Concat("-target-select core ", coreDump);
string coreDumpDescription = String.Format(CultureInfo.CurrentCulture, ResourceStrings.LoadingCoreDumpMessage, _launchOptions.CoreDumpPath);
commands.Add(new LaunchCommand(coreDumpCommand, coreDumpDescription, ignoreFailures: false));
}
else if (_launchOptions.ProcessId.HasValue)
{
Expand Down Expand Up @@ -918,6 +912,30 @@ private void AddExecutablePathCommand(IList<LaunchCommand> commands)
commands.Add(new LaunchCommand("-file-exec-and-symbols " + exe, description, ignoreFailures: false, failureHandler: failureHandler));
}

private void AddExecutableAndCorePathCommand(IList<LaunchCommand> commands)
{
string command;
if (_launchOptions.DebuggerMIMode == MIMode.Lldb)
{
// LLDB requires loading the executable and the core into the same target, using one command. Quotes in the path are supported.
string exePath = this.EnsureProperPathSeparators(_launchOptions.ExePath, true);
string corePath = this.EnsureProperPathSeparators(_launchOptions.CoreDumpPath, true);
command = String.Concat("file ", exePath, " -c ", corePath);
}
else
{
// GDB requires loading the executable and core separately.
// Note: Linux/mac do not support quotes around this path, but spaces in the path do work.
this.AddExecutablePathCommand(commands);
string corePathNoQuotes = this.EnsureProperPathSeparators(_launchOptions.CoreDumpPath, true, true);
command = String.Concat("-target-select core ", corePathNoQuotes);
}

// Load core dump information
string description = String.Format(CultureInfo.CurrentCulture, ResourceStrings.LoadingCoreDumpMessage, _launchOptions.CoreDumpPath);
commands.Add(new LaunchCommand(command, description, ignoreFailures: false));
}

private void DetermineAndAddExecutablePathCommand(IList<LaunchCommand> commands, UnixShellPortLaunchOptions launchOptions)
{
// TODO: connecting to OSX via SSH doesn't work yet. Show error after connection manager dialog gets dismissed.
Expand Down
4 changes: 2 additions & 2 deletions src/SSHDebugPS/AD7/AD7Process.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal class AD7Process : IDebugProcess2, IDebugProcessSecurity2, IDebugProces
/// <summary>
/// Flags are only used in ps command scenarios. It will be set to 0 for others.
/// </summary>
private readonly uint _flags;
private readonly uint? _flags;

/// <summary>
/// Returns true if _commandLine appears to hold a real file name + args rather than just a description
Expand Down Expand Up @@ -297,7 +297,7 @@ string IDebugUnixProcess.GetProcessArchitecture()
{
// For Apple Silicon M1, it is possible that the process we are attaching to is being emulated as x86_64.
// The process is emulated if it has process flags has P_TRANSLATED (0x20000).
if (_port.IsOSX() && _systemArch == "arm64")
if (_port.IsOSX() && _systemArch == "arm64" && _flags.HasValue)
{
if ((_flags & 0x20000) != 0)
{
Expand Down
30 changes: 27 additions & 3 deletions src/SSHDebugPS/IConnection.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) Microsoft. 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.Diagnostics;
using System.Threading;
using Microsoft.DebugEngineHost;
using Microsoft.SSHDebugPS.Utilities;
Expand Down Expand Up @@ -104,13 +106,13 @@ public class Process
/// <summary>
/// Only used by the PSOutputParser
/// </summary>
public uint Flags { get; private set; }
public uint? Flags { get; private set; }
public string SystemArch { get; private set; }
public string CommandLine { get; private set; }
public string UserName { get; private set; }
public bool IsSameUser { get; private set; }

public Process(uint id, string arch, uint flags, string userName, string commandLine, bool isSameUser)
public Process(uint id, string arch, uint? flags, string userName, string commandLine, bool isSameUser)
{
this.Id = id;
this.Flags = flags;
Expand All @@ -121,15 +123,37 @@ public Process(uint id, string arch, uint flags, string userName, string command
}
}

internal static class OperatingSystemStringConverter
{
internal static PlatformID ConvertToPlatformID(string value)
{
if (!string.IsNullOrEmpty(value))
{
value = value.ToLowerInvariant();
if (value.Contains("darwin"))
{
return PlatformID.MacOSX;
} else if (value.Contains("linux"))
{
return PlatformID.Unix;
}
}
Debug.Fail($"Expected a valid platform '{value}' of darwin or linux, but falling back to linux.");
return PlatformID.Unix;
}
}

internal class SystemInformation
{
public string UserName { get; private set; }
public string Architecture { get; private set; }
public PlatformID Platform { get; private set; }

public SystemInformation(string username, string architecture)
public SystemInformation(string username, string architecture, PlatformID platform)
{
this.UserName = username;
this.Architecture = architecture;
Platform = platform;
}
}
}
48 changes: 28 additions & 20 deletions src/SSHDebugPS/PSOutputParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,28 +59,27 @@ public string Extract(string line)
// Use padding to expand column width. 10 for pid and 32 for userid as that is the max size for each
// Tested this format with different distributions of Linux and container distributions. This command (and the alternative without the flags) seems
// to be the one that works the best between standard *nix and BusyBox implementations of ps.
private const string PSCommandLineFormat = "ps{0}-o pid=pppppppppp -o flags=ffffffff -o ruser=rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr -o args";
private const string PSCommandLineFormat = "ps{0}-o pid=pppppppppp{1} -o ruser=rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr -o args";
private SystemInformation _currentSystemInformation;
private ColumnDef _pidCol;
private ColumnDef _flagsCol;
private ColumnDef _ruserCol;
private ColumnDef _argsCol;

public static string PSCommandLine = PSCommandLineFormat.FormatInvariantWithArgs(" axww ");
public static string AltPSCommandLine = PSCommandLineFormat.FormatInvariantWithArgs(" ");
// In order to determine the architecture of a process, we need to run the ps command with 'flags'.
// However, certain of distros of Linux do not support flags, so only add this for macOS.
private string PSFlagFormat => _currentSystemInformation.Platform == PlatformID.MacOSX ? " -o flags=ffffffff" : string.Empty;

public static List<Process> Parse(string output, SystemInformation systemInformation)
{
return new PSOutputParser().ParseInternal(output, systemInformation);
}
public string PSCommandLine => PSCommandLineFormat.FormatInvariantWithArgs(" axww ", PSFlagFormat);
public string AltPSCommandLine => PSCommandLineFormat.FormatInvariantWithArgs(" ", PSFlagFormat);

private PSOutputParser()
public PSOutputParser(SystemInformation systemInformation)
{
_currentSystemInformation = systemInformation;
}

private List<Process> ParseInternal(string output, SystemInformation systemInformation)
public List<Process> Parse(string output)
{
_currentSystemInformation = systemInformation;
List<Process> processList = new List<Process>();

using (var reader = new StringReader(output))
Expand Down Expand Up @@ -136,14 +135,18 @@ private bool ProcessHeaderLine(/*OPTIONAL*/ string headerLine)
if (!SkipNonWhitespace(headerLine, ref index))
return false;

_flagsCol = new ColumnDef(colStart, index);
/// <see cref PSFlagFormat/> on why this is only executed for macOS.
if (_currentSystemInformation.Platform == PlatformID.MacOSX)
{
_flagsCol = new ColumnDef(colStart, index);

if (!SkipWhitespace(headerLine, ref index))
return false;
if (!SkipWhitespace(headerLine, ref index))
return false;

colStart = index;
if (!SkipNonWhitespace(headerLine, ref index))
return false;
colStart = index;
if (!SkipNonWhitespace(headerLine, ref index))
return false;
}

_ruserCol = new ColumnDef(colStart, index);

Expand All @@ -170,10 +173,15 @@ private Process SplitPSLine(string line)
if (!uint.TryParse(pidText, NumberStyles.None, CultureInfo.InvariantCulture, out pid))
return null;

uint flags;
string flagsText = _flagsCol.Extract(line);
if (!uint.TryParse(flagsText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out flags))
return null;
uint? flags = null;
/// <see cref PSFlagFormat/> on why this is only executed for macOS.
if (_currentSystemInformation.Platform == PlatformID.MacOSX)
{
string flagsText = _flagsCol.Extract(line);
if (!uint.TryParse(flagsText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint tempFlags))
return null;
flags = tempFlags;
}

string ruser = _ruserCol.Extract(line);
string commandLine = _argsCol.Extract(line);
Expand Down
21 changes: 15 additions & 6 deletions src/SSHDebugPS/PipeConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ public override bool IsLinux()
/// <returns>SystemInformation containing username and architecture. If it was unable to obtain any of these, the value will be set to string.Empty.</returns>
public SystemInformation GetSystemInformation()
{
string commandOutput;
string errorMessage;
string commandOutput = string.Empty;
string errorMessage = string.Empty;
int exitCode;

string username = string.Empty;
Expand All @@ -108,13 +108,19 @@ public SystemInformation GetSystemInformation()
username = commandOutput;
}

string platform = string.Empty;
if (ExecuteCommand("uname", Timeout.Infinite, commandOutput: out commandOutput, errorMessage: out errorMessage, exitCode: out exitCode))
{
platform = commandOutput;
}

string architecture = string.Empty;
if (ExecuteCommand("uname -m", Timeout.Infinite, commandOutput: out commandOutput, errorMessage: out errorMessage, exitCode: out exitCode))
{
architecture = commandOutput;
}

return new SystemInformation(username, architecture);
return new SystemInformation(username, architecture, OperatingSystemStringConverter.ConvertToPlatformID(platform));
}

public override List<Process> ListProcesses()
Expand Down Expand Up @@ -152,12 +158,15 @@ private bool PSListProcess(SystemInformation systemInformation, out string error
errorMessage = string.Empty;
string commandOutput;
int exitCode;
if (!ExecuteCommand(PSOutputParser.PSCommandLine, Timeout.Infinite, out commandOutput, out errorMessage, out exitCode))

PSOutputParser psOutputParser = new PSOutputParser(systemInformation);

if (!ExecuteCommand(psOutputParser.PSCommandLine, Timeout.Infinite, out commandOutput, out errorMessage, out exitCode))
{
// Clear output and errorMessage
commandOutput = string.Empty;
errorMessage = string.Empty;
if (!ExecuteCommand(PSOutputParser.AltPSCommandLine, Timeout.Infinite, out commandOutput, out errorMessage, out exitCode))
if (!ExecuteCommand(psOutputParser.AltPSCommandLine, Timeout.Infinite, out commandOutput, out errorMessage, out exitCode))
{
if (exitCode == 127)
{
Expand All @@ -174,7 +183,7 @@ private bool PSListProcess(SystemInformation systemInformation, out string error
}
}

processes = PSOutputParser.Parse(commandOutput, systemInformation);
processes = psOutputParser.Parse(commandOutput);
return true;
}

Expand Down
15 changes: 12 additions & 3 deletions src/SSHDebugPS/SSH/SSHConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,31 @@ public override List<Process> ListProcesses()
username = usernameCommand.Output.TrimEnd('\n', '\r'); // trim line endings because 'id' command ends with a newline
}

string operatingSystem = string.Empty;
var operatingSystemCommand = _remoteSystem.Shell.ExecuteCommand("uname", Timeout.InfiniteTimeSpan);
if (operatingSystemCommand.ExitCode == 0)
{
operatingSystem = operatingSystemCommand.Output.TrimEnd('\n', '\r'); // trim line endings because 'uname' command ends with a newline
}

string architecture = string.Empty;
var architectureCommand = _remoteSystem.Shell.ExecuteCommand("uname -m", Timeout.InfiniteTimeSpan);
if (architectureCommand.ExitCode == 0)
{
architecture = architectureCommand.Output.TrimEnd('\n', '\r'); // trim line endings because 'uname -m' command ends with a newline
}

SystemInformation systemInformation = new SystemInformation(username, architecture);
SystemInformation systemInformation = new SystemInformation(username, architecture, OperatingSystemStringConverter.ConvertToPlatformID(operatingSystem));

PSOutputParser psOutputParser = new PSOutputParser(systemInformation);

var command = _remoteSystem.Shell.ExecuteCommand(PSOutputParser.PSCommandLine, Timeout.InfiniteTimeSpan);
var command = _remoteSystem.Shell.ExecuteCommand(psOutputParser.PSCommandLine, Timeout.InfiniteTimeSpan);
if (command.ExitCode != 0)
{
throw new CommandFailedException(StringResources.Error_PSFailed);
}

return PSOutputParser.Parse(command.Output, systemInformation);
return psOutputParser.Parse(command.Output);
}

/// <inheritdoc/>
Expand Down
Loading
Loading