Skip to content
This repository was archived by the owner on Nov 15, 2021. It is now read-only.
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
14 changes: 12 additions & 2 deletions main/OpenCover.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -484,15 +484,25 @@ from @class in module.Classes.Where(c => !c.ShouldSerializeSkippedDueTo())

private static bool GetFullOutputFile(CommandLineParser parser, out string outputFile)
{
outputFile = Path.Combine(Environment.CurrentDirectory, Environment.ExpandEnvironmentVariables(parser.OutputFile));
if (!Directory.Exists(Path.GetDirectoryName(outputFile)))
try
{
outputFile = Path.Combine(Environment.CurrentDirectory, Environment.ExpandEnvironmentVariables(parser.OutputFile));
}
catch (Exception ex)
{
outputFile = null;
System.Console.WriteLine("Invalid `outputFile` supplied: {0}", ex.Message);
return false;
}
if (!Directory.Exists(Path.GetDirectoryName(outputFile) ?? string.Empty))
{
System.Console.WriteLine("Output folder does not exist; please create it and make sure appropriate permissions are set.");
return false;
}
return true;
}


private static IFilter BuildFilter(CommandLineParser parser)
{
var filter = Filter.BuildFilter(parser);
Expand Down
Binary file modified main/OpenCover.Documentation/Usage.pdf
Binary file not shown.
963 changes: 492 additions & 471 deletions main/OpenCover.Documentation/Usage.rtf

Large diffs are not rendered by default.

64 changes: 64 additions & 0 deletions main/OpenCover.Framework/CommandLineParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Reflection;
using System.Text;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using OpenCover.Framework.Model;
Expand Down Expand Up @@ -58,6 +59,32 @@ public enum ServiceEnvironment
ByName
}

/// <summary>
/// SafeMode values
/// </summary>
public enum SafeMode
{
/// <summary>
/// SafeMode is on (default)
/// </summary>
On,

/// <summary>
/// SafeMode is on (default)
/// </summary>
Yes = On,

/// <summary>
/// SafeMode is off
/// </summary>
Off,

/// <summary>
/// SafeMode is off
/// </summary>
No = Off
}

/// <summary>
/// Parse the command line arguments and set the appropriate properties
/// </summary>
Expand All @@ -84,6 +111,8 @@ public CommandLineParser(string[] arguments)
RegExFilters = false;
Registration = Registration.Normal;
PrintVersion = false;
ExcludeDirs = new string[0];
SafeMode = true;
}

/// <summary>
Expand Down Expand Up @@ -111,6 +140,7 @@ public string Usage()
builder.AppendLine(" [-excludebyattribute:<filter>[;<filter>][;<filter>]]");
builder.AppendLine(" [-excludebyfile:<filter>[;<filter>][;<filter>]]");
builder.AppendLine(" [-coverbytest:<filter>[;<filter>][;<filter>]]");
builder.AppendLine(" [[\"]-excludedirs:<excludedir>[;<excludedir>][;<excludedir>][\"]]");
builder.AppendLine(" [-hideskipped:File|Filter|Attribute|MissingPdb|All,[File|Filter|Attribute|MissingPdb|All]]");
builder.AppendLine(" [-log:[Off|Fatal|Error|Warn|Info|Debug|Verbose|All]]");
builder.AppendLine(" [-service[:byname]]");
Expand Down Expand Up @@ -169,6 +199,16 @@ public void ExtractAndValidateArguments()
case "searchdirs":
SearchDirs = GetArgumentValue("searchdirs").Split(';');
break;
case "excludedirs":
ExcludeDirs =
GetArgumentValue("excludedirs")
.Split(';')
.Where(_ => _ != null)
.Select(_ => Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), _)))
.Where(Directory.Exists)
.Distinct()
.ToArray();
break;
case "targetargs":
TargetArgs = GetArgumentValue("targetargs");
break;
Expand Down Expand Up @@ -252,6 +292,9 @@ public void ExtractAndValidateArguments()
case "skipautoprops":
SkipAutoImplementedProperties = true;
break;
case "safemode":
SafeMode = ExtractSafeMode(GetArgumentValue("safemode")) == Framework.SafeMode.On;
break;
case "?":
PrintUsage = true;
break;
Expand Down Expand Up @@ -329,6 +372,16 @@ private static List<SkippedMethod> ExtractSkipped(string skippedArg)
return list.Distinct().ToList();
}

private static SafeMode ExtractSafeMode(string safeModeArg)
{
SafeMode result;
if (!Enum.TryParse(safeModeArg, true, out result))
{
throw new InvalidOperationException(string.Format("The safemode option {0} is not valid", safeModeArg));
}
return result;
}

private TimeSpan ParseTimeoutValue(string timeoutValue)
{
var match = Regex.Match(timeoutValue, @"((?<minutes>\d+)m)?((?<seconds>\d+)s)?");
Expand Down Expand Up @@ -384,6 +437,12 @@ private void ValidateArguments()
/// </summary>
public bool Register { get; private set; }

/// <summary>
/// Set when we should not use thread based buffers.
/// May not be as performant in some circumstances but avoids data loss
/// </summary>
public bool SafeMode { get; private set; }

/// <summary>
/// the switch -register with the user argument was supplied i.e. -register:user
/// </summary>
Expand All @@ -409,6 +468,11 @@ private void ValidateArguments()
/// </summary>
public string[] SearchDirs { get; private set; }

/// <summary>
/// Assemblies loaded form these dirs will be excluded
/// </summary>
public string[] ExcludeDirs { get; private set; }

/// <summary>
/// The arguments that are to be passed to the Target
/// </summary>
Expand Down
26 changes: 26 additions & 0 deletions main/OpenCover.Framework/Filter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,27 @@ public bool InstrumentProcess(string processName)
return true; // not excluded and no inclusion filters
}

readonly IList<string> _excludePaths = new List<string>();

/// <summary>
/// Add a folder to the list that modules in these folders (and their children) should be excluded
/// </summary>
/// <param name="excludedPath"></param>
public void AddExcludedFolder(string excludedPath)
{
_excludePaths.Add(excludedPath.ToLowerInvariant());
}

/// <summary>
/// Should we use this module based on it's path
/// </summary>
/// <param name="modulePath"></param>
/// <returns></returns>
public bool UseModule(string modulePath)
{
return _excludePaths.All(path => !modulePath.ToLowerInvariant().StartsWith(path));
}

/// <summary>
/// Create a filter entity from parser parameters
/// </summary>
Expand Down Expand Up @@ -415,6 +436,11 @@ public static IFilter BuildFilter(CommandLineParser parser)
filter.AddAttributeExclusionFilters(parser.AttributeExclusionFilters.ToArray());
filter.AddFileExclusionFilters(parser.FileExclusionFilters.ToArray());
filter.AddTestFileFilters(parser.TestFilters.ToArray());
foreach (var excludeDir in parser.ExcludeDirs)
{
filter.AddExcludedFolder(excludeDir);
}


return filter;
}
Expand Down
12 changes: 12 additions & 0 deletions main/OpenCover.Framework/ICommandLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public interface ICommandLine
/// </summary>
string[] SearchDirs { get; }

/// <summary>
/// Assemblies loaded form these dirs will be excluded
/// </summary>
string[] ExcludeDirs { get; }


/// <summary>
/// If specified then results to be merged by matching hash
/// </summary>
Expand All @@ -49,6 +55,12 @@ public interface ICommandLine
/// </summary>
bool TraceByTest { get; }

/// <summary>
/// Set when we should not use thread based buffers.
/// May not be as performant in some circumstances but avoids data loss
/// </summary>
bool SafeMode { get; }

/// <summary>
/// The type of profiler registration
/// </summary>
Expand Down
13 changes: 13 additions & 0 deletions main/OpenCover.Framework/IFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ public interface IFilter
/// <param name="processName"></param>
/// <returns></returns>
bool InstrumentProcess(string processName);

/// <summary>
/// Add a folder to the list that modules in these folders (and their children) should be excluded
/// </summary>
/// <param name="excludedPath"></param>
void AddExcludedFolder(string excludedPath);

/// <summary>
/// Should we use this module based on it's path
/// </summary>
/// <param name="modulePath"></param>
/// <returns></returns>
bool UseModule(string modulePath);
}

}
15 changes: 13 additions & 2 deletions main/OpenCover.Framework/Manager/IMemoryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,9 @@ public interface IMemoryManager : IDisposable
/// <summary>
/// Get the list of all allocated blocks
/// </summary>
IList<ManagedBufferBlock> GetBlocks { get; }
IReadOnlyList<ManagedBufferBlock> GetBlocks { get; }

/// <summary>
/// Deactivate a <see cref="ManagedBufferBlock"/>
/// </summary>
/// <param name="bufferId"></param>
void DeactivateMemoryBuffer(uint bufferId);
Expand All @@ -74,5 +73,17 @@ public interface IMemoryManager : IDisposable
/// Remove all deactivated blocks
/// </summary>
void RemoveDeactivatedBlock(ManagedBufferBlock block);

/// <summary>
/// Wait some time for the blocks to close
/// </summary>
/// <param name="bufferWaitCount"></param>
void WaitForBlocksToClose(int bufferWaitCount);

/// <summary>
/// Fetch the remaining data from the active blocks
/// </summary>
/// <param name="processBuffer"></param>
void FetchRemainingBufferData(Action<byte[]> processBuffer);
}
}
53 changes: 50 additions & 3 deletions main/OpenCover.Framework/Manager/MemoryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;
using log4net;

namespace OpenCover.Framework.Manager
{
Expand All @@ -20,6 +21,8 @@ public class MemoryManager : IMemoryManager
private readonly object _lockObject = new object();
private uint _bufferId = 1;

private static readonly ILog DebugLogger = LogManager.GetLogger("DebugLogger");

private readonly IList<ManagedBufferBlock> _blocks = new List<ManagedBufferBlock>();

/// <summary>
Expand Down Expand Up @@ -313,15 +316,15 @@ public ManagedBufferBlock AllocateMemoryBuffer(int bufferSize, out uint bufferId


/// <summary>
/// get a pair of communication+memory blocks
/// Get the list of all allocated blocks
/// </summary>
public IList<ManagedBufferBlock> GetBlocks
public IReadOnlyList<ManagedBufferBlock> GetBlocks
{
get
{
lock (_lockObject)
{
return _blocks;
return _blocks.ToArray();
}
}
}
Expand Down Expand Up @@ -356,6 +359,50 @@ public void RemoveDeactivatedBlock(ManagedBufferBlock block)
}
}

public void WaitForBlocksToClose(int bufferWaitCount)
{
// we need to let the profilers dump the thread buffers over before they close - max 15s (ish)
var i = 0;
var count = -1;
while (i < bufferWaitCount && count != 0)
{
lock (_lockObject)
{
count = _blocks.Count(b => b.Active);
}
if (count > 0)
{
DebugLogger.InfoFormat("Waiting for {0} processes to close", count);
Thread.Sleep(500);
}
i++;
}
}

public void FetchRemainingBufferData(Action<byte[]> processBuffer)
{
lock (_lockObject)
{
// grab anything left in the main buffers
var activeBlocks = _blocks.Where(b => b.Active).ToArray();
foreach (var block in activeBlocks)
{
var memoryBlock = block.MemoryBlock;
var data = new byte[memoryBlock.BufferSize];
memoryBlock.StreamAccessorResults.Seek(0, SeekOrigin.Begin);
memoryBlock.StreamAccessorResults.Read(data, 0, memoryBlock.BufferSize);

// process the extracted data
processBuffer(data);

// now clean them down
block.CommunicationBlock.Do(x => x.Dispose());
block.MemoryBlock.Do(x => x.Dispose());
_blocks.RemoveAt(_blocks.IndexOf(block));
}
}
}

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
Expand Down
21 changes: 4 additions & 17 deletions main/OpenCover.Framework/Manager/ProfilerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ private void SetProfilerAttributesOnDictionary(string profilerKey, string profil

if (_commandLine.TraceByTest)
dictionary[@"OpenCover_Profiler_TraceByTest"] = "1";
if (_commandLine.SafeMode)
dictionary[@"OpenCover_Profiler_SafeMode"] = "1";

dictionary["Cor_Profiler"] = ProfilerGuid;
dictionary["Cor_Enable_Profiling"] = "1";
Expand Down Expand Up @@ -230,24 +232,9 @@ private void ProcessMessages(WaitHandle[] handles)
}
} while (_continueWait);

// we need to let the profilers dump the thread buffers over before they close - max 15s (ish)
var i = 0;
while (i < BufferWaitCount && _memoryManager.GetBlocks.Any(b => b.Active))
{
DebugLogger.InfoFormat("Waiting for {0} processes to close",
_memoryManager.GetBlocks.Count(b => b.Active));
Thread.Sleep(500);
i++;
}
_memoryManager.WaitForBlocksToClose(BufferWaitCount);

// grab anything left in the main buffers
foreach (var block in _memoryManager.GetBlocks.Where(b => b.Active).Select(b => b.MemoryBlock))
{
var data = new byte[block.BufferSize];
block.StreamAccessorResults.Seek(0, SeekOrigin.Begin);
block.StreamAccessorResults.Read(data, 0, block.BufferSize);
_messageQueue.Enqueue(data);
}
_memoryManager.FetchRemainingBufferData(data => _messageQueue.Enqueue(data));

lock (SyncRoot)
{
Expand Down
Loading