Add attribute to allow LoggedTest to collect dump on failure (#905)
* Add attribute for collecting dumps for failed LoggedTest
This commit is contained in:
parent
e21dc21b40
commit
2283bd63f5
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright(c) .NET Foundation.All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
/// <summary>
|
||||
/// Capture the memory dump upon test failure.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This currently only works in Windows environments
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public class CollectDumpAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public static partial class DumpCollector
|
||||
{
|
||||
private static class Windows
|
||||
{
|
||||
internal static void Collect(Process process, string outputFile)
|
||||
{
|
||||
// Open the file for writing
|
||||
using (var stream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
|
||||
{
|
||||
// Dump the process!
|
||||
var exceptionInfo = new NativeMethods.MINIDUMP_EXCEPTION_INFORMATION();
|
||||
if (!NativeMethods.MiniDumpWriteDump(process.Handle, (uint)process.Id, stream.SafeFileHandle, NativeMethods.MINIDUMP_TYPE.MiniDumpWithFullMemory, ref exceptionInfo, IntPtr.Zero, IntPtr.Zero))
|
||||
{
|
||||
var err = Marshal.GetHRForLastWin32Error();
|
||||
Marshal.ThrowExceptionForHR(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class NativeMethods
|
||||
{
|
||||
[DllImport("Dbghelp.dll")]
|
||||
public static extern bool MiniDumpWriteDump(IntPtr hProcess, uint ProcessId, SafeFileHandle hFile, MINIDUMP_TYPE DumpType, ref MINIDUMP_EXCEPTION_INFORMATION ExceptionParam, IntPtr UserStreamParam, IntPtr CallbackParam);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct MINIDUMP_EXCEPTION_INFORMATION
|
||||
{
|
||||
public uint ThreadId;
|
||||
public IntPtr ExceptionPointers;
|
||||
public int ClientPointers;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum MINIDUMP_TYPE : uint
|
||||
{
|
||||
MiniDumpNormal = 0,
|
||||
MiniDumpWithDataSegs = 1 << 0,
|
||||
MiniDumpWithFullMemory = 1 << 1,
|
||||
MiniDumpWithHandleData = 1 << 2,
|
||||
MiniDumpFilterMemory = 1 << 3,
|
||||
MiniDumpScanMemory = 1 << 4,
|
||||
MiniDumpWithUnloadedModules = 1 << 5,
|
||||
MiniDumpWithIndirectlyReferencedMemory = 1 << 6,
|
||||
MiniDumpFilterModulePaths = 1 << 7,
|
||||
MiniDumpWithProcessThreadData = 1 << 8,
|
||||
MiniDumpWithPrivateReadWriteMemory = 1 << 9,
|
||||
MiniDumpWithoutOptionalData = 1 << 10,
|
||||
MiniDumpWithFullMemoryInfo = 1 << 11,
|
||||
MiniDumpWithThreadInfo = 1 << 12,
|
||||
MiniDumpWithCodeSegs = 1 << 13,
|
||||
MiniDumpWithoutAuxiliaryState = 1 << 14,
|
||||
MiniDumpWithFullAuxiliaryState = 1 << 15,
|
||||
MiniDumpWithPrivateWriteCopyMemory = 1 << 16,
|
||||
MiniDumpIgnoreInaccessibleMemory = 1 << 17,
|
||||
MiniDumpWithTokenInformation = 1 << 18,
|
||||
MiniDumpWithModuleHeaders = 1 << 19,
|
||||
MiniDumpFilterTriage = 1 << 20,
|
||||
MiniDumpWithAvxXStateContext = 1 << 21,
|
||||
MiniDumpWithIptTrace = 1 << 22,
|
||||
MiniDumpValidTypeFlags = (-1) ^ ((~1) << 22)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public static partial class DumpCollector
|
||||
{
|
||||
public static void Collect(Process process, string fileName)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
Windows.Collect(process, fileName);
|
||||
}
|
||||
// No implementations yet for macOS and Linux
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
|
@ -15,6 +17,7 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
private readonly RetryContext _retryContext;
|
||||
private readonly bool _collectDumpOnFailure;
|
||||
|
||||
public LoggedTestInvoker(
|
||||
ITest test,
|
||||
|
|
@ -27,11 +30,13 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource,
|
||||
ITestOutputHelper output,
|
||||
RetryContext retryContext)
|
||||
RetryContext retryContext,
|
||||
bool collectDumpOnFailure)
|
||||
: base(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, beforeAfterAttributes, aggregator, cancellationTokenSource)
|
||||
{
|
||||
_output = output;
|
||||
_retryContext = retryContext;
|
||||
_collectDumpOnFailure = collectDumpOnFailure;
|
||||
}
|
||||
|
||||
protected override object CreateTestClass()
|
||||
|
|
@ -63,5 +68,25 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
|
||||
return testClass;
|
||||
}
|
||||
|
||||
protected override object CallTestMethod(object testClassInstance)
|
||||
{
|
||||
try
|
||||
{
|
||||
return base.CallTestMethod(testClassInstance);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (_collectDumpOnFailure && testClassInstance is LoggedTestBase loggedTestBase)
|
||||
{
|
||||
var path = Path.Combine(loggedTestBase.ResolvedLogOutputDirectory, loggedTestBase.ResolvedTestMethodName + ".dmp");
|
||||
var process = Process.GetCurrentProcess();
|
||||
|
||||
DumpCollector.Collect(process, path);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,9 +50,11 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
private async Task<decimal> InvokeTestMethodAsync(ExceptionAggregator aggregator, ITestOutputHelper output)
|
||||
{
|
||||
var retryAttribute = GetRetryAttribute(TestMethod);
|
||||
var collectDump = TestMethod.GetCustomAttribute<CollectDumpAttribute>() != null;
|
||||
|
||||
if (!typeof(LoggedTestBase).IsAssignableFrom(TestClass) || retryAttribute == null)
|
||||
{
|
||||
return await new LoggedTestInvoker(Test, MessageBus, TestClass, ConstructorArguments, TestMethod, TestMethodArguments, BeforeAfterAttributes, aggregator, CancellationTokenSource, output, null).RunAsync();
|
||||
return await new LoggedTestInvoker(Test, MessageBus, TestClass, ConstructorArguments, TestMethod, TestMethodArguments, BeforeAfterAttributes, aggregator, CancellationTokenSource, output, null, collectDump).RunAsync();
|
||||
}
|
||||
|
||||
var retryPredicateMethodName = retryAttribute.RetryPredicateName;
|
||||
|
|
@ -75,7 +77,7 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
};
|
||||
|
||||
var retryAggregator = new ExceptionAggregator();
|
||||
var loggedTestInvoker = new LoggedTestInvoker(Test, MessageBus, TestClass, ConstructorArguments, TestMethod, TestMethodArguments, BeforeAfterAttributes, retryAggregator, CancellationTokenSource, output, retryContext);
|
||||
var loggedTestInvoker = new LoggedTestInvoker(Test, MessageBus, TestClass, ConstructorArguments, TestMethod, TestMethodArguments, BeforeAfterAttributes, retryAggregator, CancellationTokenSource, output, retryContext, collectDump);
|
||||
var totalTime = 0.0M;
|
||||
|
||||
do
|
||||
|
|
@ -95,7 +97,6 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
return totalTime;
|
||||
}
|
||||
|
||||
|
||||
private RetryTestAttribute GetRetryAttribute(MethodInfo methodInfo)
|
||||
{
|
||||
var os = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? OperatingSystems.MacOSX
|
||||
|
|
|
|||
Loading…
Reference in New Issue