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

[16.8] Fix collect dump always #2641

Merged
2 commits merged into from
Nov 19, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion scripts/build/TestPlatform.Dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<!-- this version also needs to be "statically" readable because the test fixture will inspect this file for the version
and because during the test `dotnet test` will run and re-build some of the test projects and at that time the version
from a build parameter would not be available, so I am writing this version from the build.ps1 script to keep it in sync -->
<NETTestSdkVersion>16.8.0-dev</NETTestSdkVersion>
<NETTestSdkVersion>16.8.1-dev</NETTestSdkVersion>

<MSTestFrameworkVersion>2.1.0</MSTestFrameworkVersion>
<MSTestAdapterVersion>2.1.0</MSTestAdapterVersion>
Expand Down
2 changes: 1 addition & 1 deletion scripts/build/TestPlatform.Settings.targets
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<!-- This version is read by vsts-prebuild.ps1 and is a base for the current version, this should be updated
at the start of new iteration to the goal number. This is also used to version the local packages. This version needs to be statically
readable when we read the file as xml, don't move it to a .props file, unless you change the build server process -->
<TPVersionPrefix>16.8.0</TPVersionPrefix>
<TPVersionPrefix>16.8.1</TPVersionPrefix>
</PropertyGroup>
<PropertyGroup>
<!-- Versioning is defined from the build script. Use a default dev build if it's not defined.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,10 @@ private void SessionEndedHandler(object sender, SessionEndEventArgs args)
}
else
{
this.logger.LogWarning(this.context.SessionDataCollectionContext, Resources.Resources.NotGeneratingSequenceFile);
if (this.collectProcessDumpOnTestHostHang)
{
this.logger.LogWarning(this.context.SessionDataCollectionContext, Resources.Resources.NotGeneratingSequenceFile);
}
}

if (this.uploadDumpFiles)
Expand Down Expand Up @@ -528,7 +531,7 @@ private void TestHostLaunchedHandler(object sender, TestHostLaunchedEventArgs ar
try
{
var dumpDirectory = this.GetDumpDirectory();
this.processDumpUtility.StartTriggerBasedProcessDump(args.TestHostProcessId, dumpDirectory, this.processFullDumpEnabled, this.targetFramework);
this.processDumpUtility.StartTriggerBasedProcessDump(args.TestHostProcessId, dumpDirectory, this.processFullDumpEnabled, this.targetFramework, this.collectDumpAlways);
}
catch (TestPlatformException e)
{
Expand Down Expand Up @@ -585,7 +588,15 @@ private string GetTempDirectory()
{
if (string.IsNullOrWhiteSpace(this.tempDirectory))
{
this.tempDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
// DUMP_TEMP_PATH will be used as temporary storage location
// for the dumps, this won't affect the dump uploads. Just the place where
// we store them before moving them to the final folder.

// AGENT_TEMPDIRECTORY is AzureDevops variable, which is set to path
// that is cleaned up after every job. This is preferable to use over
// just the normal temp.
var temp = Environment.GetEnvironmentVariable("VSTEST_DUMP_TEMP_PATH") ?? Environment.GetEnvironmentVariable("AGENT_TEMPDIRECTORY") ?? Path.GetTempPath();
nohwnd marked this conversation as resolved.
Show resolved Hide resolved
this.tempDirectory = Path.Combine(temp, Guid.NewGuid().ToString());
Directory.CreateDirectory(this.tempDirectory);
return this.tempDirectory;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Microsoft.TestPlatform.Extensions.BlameDataCollector
{
public interface ICrashDumper
{
void AttachToTargetProcess(int processId, string outputDirectory, DumpTypeOption dumpType);
void AttachToTargetProcess(int processId, string outputDirectory, DumpTypeOption dumpType, bool collectAlways);

void WaitForDumpToFinish();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ public interface IProcDumpArgsBuilder
/// <param name="isFullDump">
/// Is full dump enabled
/// </param>
/// <param name="collectAlways">
/// Collects the dump on process exit even when there is no exception
/// </param>
/// <returns>Arguments</returns>
string BuildTriggerBasedProcDumpArgs(int processId, string filename, IEnumerable<string> procDumpExceptionsList, bool isFullDump);
string BuildTriggerBasedProcDumpArgs(int processId, string filename, IEnumerable<string> procDumpExceptionsList, bool isFullDump, bool collectAlways);

/// <summary>
/// Arguments for procdump.exe for getting a dump in case of a testhost hang
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ public interface IProcessDumpUtility
/// <param name="targetFramework">
/// The target framework of the process
/// </param>
void StartTriggerBasedProcessDump(int processId, string testResultsDirectory, bool isFullDump, string targetFramework);
/// <param name="collectAlways">
/// Collect the dump on process exit even if there is no exception
/// </param>
void StartTriggerBasedProcessDump(int processId, string testResultsDirectory, bool isFullDump, string targetFramework, bool collectAlways);

/// <summary>
/// Launch proc dump process to capture dump in case of a testhost hang and wait for it to exit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Microsoft.TestPlatform.Extensions.BlameDataCollector
{
internal class NetClientCrashDumper : ICrashDumper
{
public void AttachToTargetProcess(int processId, string outputDirectory, DumpTypeOption dumpType)
public void AttachToTargetProcess(int processId, string outputDirectory, DumpTypeOption dumpType, bool collectAlways)
{
// we don't need to do anything directly here, we setup the env variables
// in the dumper configuration, including the path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ namespace Microsoft.TestPlatform.Extensions.BlameDataCollector
public class ProcDumpArgsBuilder : IProcDumpArgsBuilder
{
/// <inheritdoc />
public string BuildTriggerBasedProcDumpArgs(int processId, string filename, IEnumerable<string> procDumpExceptionsList, bool isFullDump)
public string BuildTriggerBasedProcDumpArgs(int processId, string filename, IEnumerable<string> procDumpExceptionsList, bool isFullDump, bool collectAlways)
{
// -accepteula: Auto accept end-user license agreement
// -e: Write a dump when the process encounters an unhandled exception. Include the 1 to create dump on first chance exceptions.
// -g: Run as a native debugger in a managed process (no interop).
// -t: Write a dump when the process terminates.
// -ma: Full dump argument.
// -f: Filter the exceptions.
StringBuilder procDumpArgument = new StringBuilder("-accepteula -e 1 -g -t ");
StringBuilder procDumpArgument = new StringBuilder($"-accepteula -e 1 -g {(collectAlways ? "-t " : string.Empty)}");
if (isFullDump)
{
procDumpArgument.Append("-ma ");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public void WaitForDumpToFinish()
}

/// <inheritdoc/>
public void AttachToTargetProcess(int processId, string outputDirectory, DumpTypeOption dumpType)
public void AttachToTargetProcess(int processId, string outputDirectory, DumpTypeOption dumpType, bool collectAlways)
{
var process = Process.GetProcessById(processId);
var outputFile = Path.Combine(outputDirectory, $"{process.ProcessName}_{process.Id}_{DateTime.Now:yyyyMMddTHHmmss}_crashdump.dmp");
Expand All @@ -96,7 +96,8 @@ public void AttachToTargetProcess(int processId, string outputDirectory, DumpTyp
processId,
this.dumpFileName,
ProcDumpExceptionsList,
isFullDump: dumpType == DumpTypeOption.Full);
isFullDump: dumpType == DumpTypeOption.Full,
collectAlways: collectAlways);

EqtTrace.Info($"ProcDumpCrashDumper.AttachToTargetProcess: Running ProcDump with arguments: '{procDumpArgs}'.");
this.procDumpProcess = this.processHelper.LaunchProcess(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ public void StartHangBasedProcessDump(int processId, string tempDirectory, bool
}

/// <inheritdoc/>
public void StartTriggerBasedProcessDump(int processId, string testResultsDirectory, bool isFullDump, string targetFramework)
public void StartTriggerBasedProcessDump(int processId, string testResultsDirectory, bool isFullDump, string targetFramework, bool collectAlways)
{
this.CrashDump(processId, testResultsDirectory, isFullDump ? DumpTypeOption.Full : DumpTypeOption.Mini, targetFramework);
this.CrashDump(processId, testResultsDirectory, isFullDump ? DumpTypeOption.Full : DumpTypeOption.Mini, targetFramework, collectAlways);
}

/// <inheritdoc/>
Expand All @@ -109,15 +109,15 @@ public void DetachFromTargetProcess(int targetProcessId)
this.crashDumper?.DetachFromTargetProcess(targetProcessId);
}

private void CrashDump(int processId, string tempDirectory, DumpTypeOption dumpType, string targetFramework)
private void CrashDump(int processId, string tempDirectory, DumpTypeOption dumpType, string targetFramework, bool collectAlways)
{
var processName = this.processHelper.GetProcessName(processId);
EqtTrace.Info($"ProcessDumpUtility.CrashDump: Creating {dumpType.ToString().ToLowerInvariant()} dump of process {processName} ({processId}) into temporary path '{tempDirectory}'.");
this.crashDumpDirectory = tempDirectory;

this.crashDumper = this.crashDumperFactory.Create(targetFramework);
ConsoleOutput.Instance.Information(false, $"Blame: Attaching crash dump utility to process {processName} ({processId}).");
this.crashDumper.AttachToTargetProcess(processId, tempDirectory, dumpType);
this.crashDumper.AttachToTargetProcess(processId, tempDirectory, dumpType, collectAlways);
}

private void HangDump(int processId, string tempDirectory, DumpTypeOption dumpType, string targetFramework, Action<string> logWarning = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ public void TriggerTestHostLaunchedHandlerShouldStartProcDumpUtilityIfProcDumpEn
this.mockDataColectionEvents.Raise(x => x.TestHostLaunched += null, new TestHostLaunchedEventArgs(this.dataCollectionContext, 1234));

// Verify StartProcessDumpCall
this.mockProcessDumpUtility.Verify(x => x.StartTriggerBasedProcessDump(1234, It.IsAny<string>(), false, It.IsAny<string>()));
this.mockProcessDumpUtility.Verify(x => x.StartTriggerBasedProcessDump(1234, It.IsAny<string>(), false, It.IsAny<string>(), false));
}

/// <summary>
Expand All @@ -497,7 +497,7 @@ public void TriggerTestHostLaunchedHandlerShouldStartProcDumpUtilityForFullDumpI
this.mockDataColectionEvents.Raise(x => x.TestHostLaunched += null, new TestHostLaunchedEventArgs(this.dataCollectionContext, 1234));

// Verify StartProcessDumpCall
this.mockProcessDumpUtility.Verify(x => x.StartTriggerBasedProcessDump(1234, It.IsAny<string>(), true, It.IsAny<string>()));
this.mockProcessDumpUtility.Verify(x => x.StartTriggerBasedProcessDump(1234, It.IsAny<string>(), true, It.IsAny<string>(), false));
}

/// <summary>
Expand Down Expand Up @@ -526,7 +526,7 @@ public void TriggerTestHostLaunchedHandlerShouldStartProcDumpUtilityForFullDumpI
this.mockDataColectionEvents.Raise(x => x.TestHostLaunched += null, new TestHostLaunchedEventArgs(this.dataCollectionContext, 1234));

// Verify StartProcessDumpCall
this.mockProcessDumpUtility.Verify(x => x.StartTriggerBasedProcessDump(1234, It.IsAny<string>(), true, It.IsAny<string>()));
this.mockProcessDumpUtility.Verify(x => x.StartTriggerBasedProcessDump(1234, It.IsAny<string>(), true, It.IsAny<string>(), false));
}

/// <summary>
Expand Down Expand Up @@ -646,7 +646,7 @@ public void TriggerTestHostLaunchedHandlerShouldCatchTestPlatFormExceptionsAndRe

// Make StartProcessDump throw exception
var tpex = new TestPlatformException("env var exception");
this.mockProcessDumpUtility.Setup(x => x.StartTriggerBasedProcessDump(1234, It.IsAny<string>(), false, It.IsAny<string>()))
this.mockProcessDumpUtility.Setup(x => x.StartTriggerBasedProcessDump(1234, It.IsAny<string>(), false, It.IsAny<string>(), false))
.Throws(tpex);

// Raise TestHostLaunched
Expand All @@ -672,7 +672,7 @@ public void TriggerTestHostLaunchedHandlerShouldCatchAllUnexpectedExceptionsAndR

// Make StartProcessDump throw exception
var ex = new Exception("start process failed");
this.mockProcessDumpUtility.Setup(x => x.StartTriggerBasedProcessDump(1234, It.IsAny<string>(), false, It.IsAny<string>()))
this.mockProcessDumpUtility.Setup(x => x.StartTriggerBasedProcessDump(1234, It.IsAny<string>(), false, It.IsAny<string>(), false))
.Throws(ex);

// Raise TestHostLaunched
Expand Down Expand Up @@ -709,9 +709,9 @@ private XmlElement GetDumpConfigurationElement(

if (collectDumpOnExit)
{
var fulldumpAttribute = xmldoc.CreateAttribute(BlameDataCollector.Constants.CollectDumpAlwaysKey);
fulldumpAttribute.Value = "true";
node.Attributes.Append(fulldumpAttribute);
var collectDumpOnExitAttribute = xmldoc.CreateAttribute(BlameDataCollector.Constants.CollectDumpAlwaysKey);
collectDumpOnExitAttribute.Value = "true";
node.Attributes.Append(collectDumpOnExitAttribute);
}

if (colectDumpOnHang)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,25 @@ public void BuildHangBasedProcDumpArgsWithFullDumpEnabledShouldCreateCorrectArgS
public void BuildTriggerBasedProcDumpArgsShouldCreateCorrectArgString()
{
var procDumpArgsBuilder = new ProcDumpArgsBuilder();
var argString = procDumpArgsBuilder.BuildTriggerBasedProcDumpArgs(this.defaultProcId, this.defaultDumpFileName, new List<string> { "a", "b" }, false);
Assert.AreEqual("-accepteula -e 1 -g -t -f a -f b 1234 dump.dmp", argString);
var argString = procDumpArgsBuilder.BuildTriggerBasedProcDumpArgs(this.defaultProcId, this.defaultDumpFileName, new List<string> { "a", "b" }, false, false);
Assert.AreEqual("-accepteula -e 1 -g -f a -f b 1234 dump.dmp", argString);
}

[TestMethod]
public void BuildTriggerProcDumpArgsWithFullDumpEnabledShouldCreateCorrectArgString()
{
var procDumpArgsBuilder = new ProcDumpArgsBuilder();
var argString = procDumpArgsBuilder.BuildTriggerBasedProcDumpArgs(this.defaultProcId, this.defaultDumpFileName, new List<string> { "a", "b" }, true);
var argString = procDumpArgsBuilder.BuildTriggerBasedProcDumpArgs(this.defaultProcId, this.defaultDumpFileName, new List<string> { "a", "b" }, true, false);
Assert.AreEqual("-accepteula -e 1 -g -ma -f a -f b 1234 dump.dmp", argString);
}

[TestMethod]
public void BuildTriggerProcDumpArgsWithAlwaysCollectShouldCreateCorrectArgString()
{
var procDumpArgsBuilder = new ProcDumpArgsBuilder();
var argString = procDumpArgsBuilder.BuildTriggerBasedProcDumpArgs(this.defaultProcId, this.defaultDumpFileName, new List<string> { "a", "b" }, true, collectAlways: true);

// adds -t for collect on every process exit
Assert.AreEqual("-accepteula -e 1 -g -t -ma -f a -f b 1234 dump.dmp", argString);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void GetDumpFileWillThrowExceptionIfNoDumpfile()
this.mockHangDumperFactory.Object,
this.mockCrashDumperFactory.Object);

processDumpUtility.StartTriggerBasedProcessDump(processId, testResultsDirectory, false, ".NETCoreApp,Version=v5.0");
processDumpUtility.StartTriggerBasedProcessDump(processId, testResultsDirectory, false, ".NETCoreApp,Version=v5.0", false);

var ex = Assert.ThrowsException<FileNotFoundException>(() => processDumpUtility.GetDumpFiles());
Assert.AreEqual(ex.Message, Resources.Resources.DumpFileNotGeneratedErrorMessage);
Expand Down