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
30 changes: 30 additions & 0 deletions src/DiffEngine.Tests/LinuxOsxProcessTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,34 @@ public async Task TryParse_singleDigit()
await Assert.That(processCommand.Process).IsEqualTo(309);
await Assert.That(processCommand.Command).IsEqualTo("System/Library/coreauthd -foo");
}

/// <summary>
/// A command with a run of three spaces in it. The removed branch went looking for exactly
/// that and truncated the command to whatever followed it.
/// </summary>
[Test]
public async Task TryParse_commandContainingRunsOfSpaces()
{
var parse = LinuxOsxProcess.TryParse("123 /usr/bin/tool file.txt", out var command);
await Assert.That(parse).IsTrue();
var processCommand = command!.Value;
await Assert.That(processCommand.Process).IsEqualTo(123);
await Assert.That(processCommand.Command).IsEqualTo("/usr/bin/tool file.txt");
}

/// <summary>
/// A PID with more digits than the command has characters. The removed branch sliced by the
/// PID's digit count, which is not an index into this string at all, so this threw
/// ArgumentOutOfRangeException - and did so out of ProcessCleanup's static constructor, which
/// makes it permanent for the process.
/// </summary>
[Test]
public async Task TryParse_longPidShortCommand()
{
var parse = LinuxOsxProcess.TryParse("1234567 /x y", out var command);
await Assert.That(parse).IsTrue();
var processCommand = command!.Value;
await Assert.That(processCommand.Process).IsEqualTo(1234567);
await Assert.That(processCommand.Command).IsEqualTo("/x y");
}
}
23 changes: 9 additions & 14 deletions src/DiffEngine/Process/LinuxOsxProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,15 @@ public static bool TryParse(string line, out ProcessCommand? processCommand)
var pidString = trim[..firstSpace];
var pid = int.Parse(pidString.ToString());

var timeAndCommandString = trim[(firstSpace + 1)..];
var multiSpaceIndex = 0;
CharSpan command;

var spaces = new CharSpan([' ',' ',' ']);
if (timeAndCommandString.IndexOf(spaces, StringComparison.InvariantCulture) > 0)
{
multiSpaceIndex = timeAndCommandString[firstSpace..].IndexOf(spaces, StringComparison.InvariantCulture);
command = timeAndCommandString[(multiSpaceIndex + 1)..].Trim();
}
else
{
command = timeAndCommandString[multiSpaceIndex..].Trim();
}
// `ps -o pid,command` has exactly one separator, so everything after the first space
// is the command. There used to be a second branch here looking for a run of three
// spaces, left over from a format that also carried TIME, and it was wrong twice over:
// it sliced by firstSpace, which is the PID's digit count and means nothing in this
// string, and then applied the index it found to the unsliced span. So a command
// containing three spaces was truncated, and a seven digit PID with a short command
// threw ArgumentOutOfRangeException - out of ProcessCleanup's static constructor,
// which makes it permanent for the process
var command = trim[(firstSpace + 1)..].Trim();

processCommand = new(command.ToString(), in pid);
return true;
Expand Down
Loading