Skip to content

Commit

Permalink
Use a seperate hits file per process/thread in order to use a seperat…
Browse files Browse the repository at this point in the history
…e writer per process/thread to reduce contention when multiple multi threaded programs are generating hits concurrently.
  • Loading branch information
Jonathan Gaillard committed Oct 4, 2013
1 parent 2f2eedb commit c558624
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 19 deletions.
3 changes: 3 additions & 0 deletions Gaillard.SharpCover.Tests/ProgramTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ public void Covered()

File.WriteAllText("testConfig.json", config);

//write some extraneous hit files to make sure they dont affect run
File.WriteAllText(Program.HITS_FILENAME_PREFIX, "doesnt matter");

Assert.AreEqual(0, Program.Main(new []{ "instrument", "testConfig.json" }));

Process.Start(testTargetExePath).WaitForExit();
Expand Down
20 changes: 15 additions & 5 deletions Gaillard.SharpCover/Counter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,30 @@
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Diagnostics;

[assembly: AssemblyVersion("1.0.1.*")]
[assembly: AssemblyVersion("1.0.2.*")]

namespace Gaillard.SharpCover
{
public static class Counter
{
[ThreadStatic]
private static HashSet<int> indexes;

[ThreadStatic]
private static BinaryWriter writer;
private static readonly ISet<int> indexes = new HashSet<int>();

public static void Count(string path, int index)
[ThreadStatic]
private static string path;

public static void Count(string pathPrefix, int index)
{
if (writer == null) {
writer = new BinaryWriter(File.Open(path, FileMode.Append));
if (path == null) {
path = pathPrefix + "|" + Process.GetCurrentProcess().Id + "|" + Thread.CurrentThread.ManagedThreadId;
indexes = new HashSet<int>();
writer = new BinaryWriter(File.Open(path, FileMode.CreateNew));
}

if (indexes.Add(index))
Expand Down
34 changes: 20 additions & 14 deletions Gaillard.SharpCover/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;

[assembly: AssemblyVersion("1.0.1.*")]
[assembly: AssemblyVersion("1.0.2.*")]

namespace Gaillard.SharpCover
{
public static class Program
{
public const string RESULTS_FILENAME = "coverageResults.txt", MISS_PREFIX = "MISS ! ";
private const string KNOWNS_FILENAME = "coverageKnowns", HITS_FILENAME = "coverageHits";
public const string RESULTS_FILENAME = "coverageResults.txt", MISS_PREFIX = "MISS ! ", HITS_FILENAME_PREFIX = "coverageHits";
private const string KNOWNS_FILENAME = "coverageKnowns";
private static readonly MethodInfo countMethodInfo = typeof(Counter).GetMethod("Count");

//immutable
Expand All @@ -27,7 +27,7 @@ private sealed class InstrumentConfig
TypeExclude,
MethodInclude,
MethodExclude,
HitsPath = Path.Combine(Directory.GetCurrentDirectory(), HITS_FILENAME);
HitsPathPrefix = Path.Combine(Directory.GetCurrentDirectory(), HITS_FILENAME_PREFIX);
private readonly IDictionary<string, IEnumerable<int>> methodOffsetExcludes = new Dictionary<string, IEnumerable<int>>();
private readonly IDictionary<string, IEnumerable<string>> methodLineExcludes = new Dictionary<string, IEnumerable<string>>();

Expand Down Expand Up @@ -100,7 +100,7 @@ private static void Instrument(Instruction instruction,

writer.WriteLine(line);

var pathParamLoadInstruction = worker.Create(OpCodes.Ldstr, config.HitsPath);
var pathParamLoadInstruction = worker.Create(OpCodes.Ldstr, config.HitsPathPrefix);
var lineParamLoadInstruction = worker.Create(OpCodes.Ldc_I4, instrumentIndex);
var registerInstruction = worker.Create(OpCodes.Call, countReference);

Expand Down Expand Up @@ -211,11 +211,15 @@ private static void Instrument(string assemblyPath, InstrumentConfig config, Tex

private static int Check()
{
var currentDirectory = Directory.GetCurrentDirectory();

var hits = new HashSet<int>();
using (var hitsStream = File.OpenRead(HITS_FILENAME))
using (var hitsReader = new BinaryReader(hitsStream)) {
while(hitsStream.Position < hitsStream.Length)
hits.Add(hitsReader.ReadInt32());
foreach (var hitsPath in Directory.GetFiles(currentDirectory, HITS_FILENAME_PREFIX + "*")) {
using (var hitsStream = File.OpenRead(hitsPath))
using (var hitsReader = new BinaryReader(hitsStream)) {
while (hitsStream.Position < hitsStream.Length)
hits.Add (hitsReader.ReadInt32());
}
}

var missCount = 0;
Expand All @@ -234,7 +238,9 @@ private static int Check()
}
}

File.Delete(HITS_FILENAME);
//cleanup to leave only results file
foreach (var hitsPath in Directory.GetFiles(currentDirectory, HITS_FILENAME_PREFIX + "*"))
File.Delete(hitsPath);
File.Delete(KNOWNS_FILENAME);

var missRatio = (double)missCount / (double)knownIndex;
Expand All @@ -251,14 +257,14 @@ public static int Main(string[] args)
if (args[0] == "instrument") {
var config = new InstrumentConfig(args[1]);

//so it exists regardless and is deleted
File.WriteAllText(KNOWNS_FILENAME, string.Empty);
File.WriteAllText(config.HitsPath, string.Empty);
//delete existing hit files generatig during program exercising
foreach (var hitsPath in Directory.GetFiles(Directory.GetCurrentDirectory(), HITS_FILENAME_PREFIX + "*"))
File.Delete(hitsPath);

//used to track the line index of the instrumented instruction in the knowns file
var instrumentIndex = 0;

using (var writer = new StreamWriter(KNOWNS_FILENAME, true)) {
using (var writer = new StreamWriter(KNOWNS_FILENAME)) {//overwrites
foreach (var assemblyPath in config.AssemblyPaths)
Instrument(assemblyPath, config, writer, ref instrumentIndex);
}
Expand Down

0 comments on commit c558624

Please sign in to comment.