Skip to content

Commit

Permalink
A first pass at branch coverage - only conditional branches are instr…
Browse files Browse the repository at this point in the history
…umented
  • Loading branch information
sawilde committed Jul 28, 2011
1 parent 4f3c3b3 commit 50023e0
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 11 deletions.
23 changes: 23 additions & 0 deletions main/OpenCover.Integration.Test/BranchTests.cs
@@ -0,0 +1,23 @@
using System;
using System.IO;
using NUnit.Framework;

namespace OpenCover.Integration.Test
{
public class BranchTests : ProfilerBaseFixture
{
[Test]
public void Execute_SimpleIf()
{
_filter.AddFilter("+[OpenCover.Test]OpenCover.Test.Integration.SimpleBranchTests*");
ExecuteProfiler32((info) =>
{
info.FileName = Path.Combine(Environment.CurrentDirectory,
@"..\..\..\tools\NUnit-2.5.9.10348\bin\net-2.0\nunit-console-x86.exe");
info.Arguments =
"OpenCover.Test.dll /noshadow /run:OpenCover.Test.Integration.SimpleBranchTests.SimpleIf";
info.WorkingDirectory = Environment.CurrentDirectory;
});
}
}
}
Expand Up @@ -50,6 +50,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="BranchTests.cs" />
<Compile Include="ExceptionTests.cs" />
<Compile Include="ProfilerBaseFixture.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand Down
6 changes: 3 additions & 3 deletions main/OpenCover.Integration.Test/ProfilerBaseFixture.cs
Expand Up @@ -16,7 +16,7 @@ namespace OpenCover.Integration.Test
[TestFixture]
public abstract class ProfilerBaseFixture
{
private IFilter _filter;
protected IFilter _filter;
private Mock<ICommandLine> _commandLine;
private IPersistance _persistance;

Expand All @@ -40,9 +40,9 @@ public void SetUp()

protected void ExecuteProfiler32(Action<ProcessStartInfo> testProcess)
{
ProfilerRegistration.Register(true);
//ProfilerRegistration.Register(true);
ExecuteProfiler(testProcess);
ProfilerRegistration.Unregister(true);
//ProfilerRegistration.Unregister(true);
}

private void ExecuteProfiler(Action<ProcessStartInfo> testProcess)
Expand Down
34 changes: 26 additions & 8 deletions main/OpenCover.Profiler/CodeCoverage.cpp
Expand Up @@ -97,6 +97,16 @@ static void __fastcall SequencePointVisit(ULONG seq)
CCodeCoverage::g_pProfiler->m_host.AddVisitPoint(seq);
}

/// <summary>An unmanaged callback that can be called from .NET that has a three I4 parameter</summary>
/// <remarks>
/// void (__fastcall *pt)(long) = &SequencePointVisit ;
/// mdSignature pmsig = GetUnmanagedMethodSignatureToken_I4(moduleId);
/// </remarks>
static void __fastcall BranchPointVisit(ULONG offset, ULONG seq)
{
//CCodeCoverage::g_pProfiler->m_host.AddVisitPoint(seq);
ATLTRACE(_T("BranchPointVisit %d, %d"), offset, seq);
}

/// <summary>Handle <c>ICorProfilerCallback::JITCompilationStarted</c></summary>
/// <remarks>The 'workhorse' </remarks>
Expand Down Expand Up @@ -125,29 +135,37 @@ HRESULT STDMETHODCALLTYPE CCodeCoverage::JITCompilationStarted(

IMAGE_COR_ILMETHOD* pMethod = (IMAGE_COR_ILMETHOD*)pMethodHeader;

void (__fastcall *pt)(ULONG) = &SequencePointVisit ;
mdSignature pmsig = GetUnmanagedMethodSignatureToken_I4(moduleId);
void (__fastcall *spt)(ULONG) = &SequencePointVisit ;
mdSignature spvsig = GetUnmanagedMethodSignatureToken_I4(moduleId);

void (__fastcall *bpt)(ULONG, ULONG) = &BranchPointVisit ;
mdSignature bpvsig = GetUnmanagedMethodSignatureToken_I4I4(moduleId);

Method instumentedMethod(pMethod);
instumentedMethod.IncrementStackSize(2);
instumentedMethod.IncrementStackSize(3);

ATLTRACE(_T("Instrumenting..."));
//points.clear();
for ( std::vector<SequencePoint>::iterator it = points.begin(); it != points.end(); it++)
points.clear();
for (std::vector<SequencePoint>::iterator it = points.begin(); it != points.end(); it++)
{
//ATLTRACE(_T("SEQPT %02d IL_%04X"), i, ppPoints[i]->Offset);
InstructionList instructions;
instructions.push_back(new Instruction(CEE_LDC_I4, (*it).UniqueId));
#if _WIN64
instructions.push_back(new Instruction(CEE_LDC_I8, (ULONGLONG)pt));
instructions.push_back(new Instruction(CEE_LDC_I8, (ULONGLONG)spt));
#else
instructions.push_back(new Instruction(CEE_LDC_I4, (ULONG)pt));
instructions.push_back(new Instruction(CEE_LDC_I4, (ULONG)spt));
#endif
instructions.push_back(new Instruction(CEE_CALLI, pmsig));
instructions.push_back(new Instruction(CEE_CALLI, spvsig));

instumentedMethod.InsertSequenceInstructionsAtOriginalOffset((*it).Offset, instructions);
}

#if _WIN64
instumentedMethod.AddBranchCoverage(bpvsig, (ULONG)bpt);
#else
instumentedMethod.AddBranchCoverage(bpvsig, (ULONGLONG)bpt);
#endif
instumentedMethod.DumpIL();

CComPtr<IMethodMalloc> methodMalloc;
Expand Down
1 change: 1 addition & 0 deletions main/OpenCover.Profiler/CodeCoverage.h
Expand Up @@ -73,6 +73,7 @@ END_COM_MAP()

private:
mdSignature GetUnmanagedMethodSignatureToken_I4(ModuleID moduleID);
mdSignature GetUnmanagedMethodSignatureToken_I4I4(ModuleID moduleID);

public:
static CCodeCoverage* g_pProfiler;
Expand Down
19 changes: 19 additions & 0 deletions main/OpenCover.Profiler/CodeCoverage_ProfilerInfo.cpp
Expand Up @@ -60,4 +60,23 @@ mdSignature CCodeCoverage::GetUnmanagedMethodSignatureToken_I4(ModuleID moduleID
return pmsig;
}

/// <summary>Get the token for an unmamaged method having a two I4 parameters</summary>
mdSignature CCodeCoverage::GetUnmanagedMethodSignatureToken_I4I4(ModuleID moduleID)
{
static COR_SIGNATURE unmanagedCallSignature[] =
{
IMAGE_CEE_CS_CALLCONV_DEFAULT, // Default CallKind!
0x02, // Parameter count
ELEMENT_TYPE_VOID, // Return type
ELEMENT_TYPE_I4, // Parameter type (I4)
ELEMENT_TYPE_I4 // Parameter type (I4)
};

CComPtr<IMetaDataEmit> metaDataEmit;
COM_FAIL_RETURN(m_profilerInfo2->GetModuleMetaData(moduleID, ofWrite, IID_IMetaDataEmit, (IUnknown**) &metaDataEmit), 0);

mdSignature pmsig ;
COM_FAIL_RETURN(metaDataEmit->GetTokenFromSig(unmanagedCallSignature, sizeof(unmanagedCallSignature), &pmsig), 0);
return pmsig;
}

65 changes: 65 additions & 0 deletions main/OpenCover.Profiler/Method.cpp
Expand Up @@ -746,3 +746,68 @@ void Method::PopulateILMap(ULONG mapSize, COR_IL_MAP* maps)
}
}

#if _WIN64
void Method::AddBranchCoverage(mdSignature bpvsig, ULONGLONG bpt)
#else
void Method::AddBranchCoverage(mdSignature bpvsig, ULONG bpt)
#endif
{
for (InstructionListIter it = m_instructions.begin(); it != m_instructions.end(); ++it)
{
if ((*it)->m_isBranch && ((*it)->m_origOffset != -1))
{
OperationDetails &details = Operations::m_mapNameOperationDetails[(*it)->m_operation];
if (details.controlFlow == COND_BRANCH)
{
if (details.canonicalName != CEE_SWITCH)
{
Instruction *pCurrent = *it;
Instruction *pOriginalTarget = pCurrent->m_branches[0];
++it;
Instruction *pNext = *it;

InstructionList instructions;
instructions.push_back(new Instruction(CEE_LDC_I4, pCurrent->m_origOffset));
instructions.push_back(new Instruction(CEE_LDC_I4, 0));
#if _WIN64
instructions.push_back(new Instruction(CEE_LDC_I8, (ULONGLONG)bpt));
#else
instructions.push_back(new Instruction(CEE_LDC_I4, (ULONG)bpt));
#endif
instructions.push_back(new Instruction(CEE_CALLI, bpvsig));

Instruction *pJumpNext = new Instruction(CEE_BR);
pJumpNext->m_isBranch = true;
instructions.push_back(pJumpNext);

Instruction *pRecordJmp = new Instruction(CEE_LDC_I4, pCurrent->m_origOffset);
instructions.push_back(pRecordJmp);

instructions.push_back(new Instruction(CEE_LDC_I4, 1));
#if _WIN64
instructions.push_back(new Instruction(CEE_LDC_I8, (ULONGLONG)bpt));
#else
instructions.push_back(new Instruction(CEE_LDC_I4, (ULONG)bpt));
#endif
instructions.push_back(new Instruction(CEE_CALLI, bpvsig));

Instruction *pJumpTarget = new Instruction(CEE_BR);
pJumpTarget->m_isBranch = true;
instructions.push_back(pJumpTarget);

// wire up
pJumpNext->m_branches.push_back(pNext);
pJumpTarget->m_branches.push_back(pOriginalTarget);
pCurrent->m_branches[0] = pRecordJmp;

m_instructions.insert(it, instructions.begin(), instructions.end());
for (it = m_instructions.begin(); *it != pJumpTarget; ++it);
++it;
}
}
}
}
RecalculateOffsets();
}


5 changes: 5 additions & 0 deletions main/OpenCover.Profiler/Method.h
Expand Up @@ -23,6 +23,11 @@ class Method : MethodBuffer
void DumpIL();
ULONG GetILMapSize();
void PopulateILMap(ULONG mapSize, COR_IL_MAP* maps);
#if _WIN64
void AddBranchCoverage(mdSignature bpvsig, ULONGLONG bpt);
#else
void AddBranchCoverage(mdSignature bpvsig, ULONG bpt);
#endif

public:
void SetMinimumStackSize(unsigned int minimumStackSize)
Expand Down
34 changes: 34 additions & 0 deletions main/OpenCover.Test/Integration/SimpleBranchTests.cs
@@ -0,0 +1,34 @@
using NUnit.Framework;

namespace OpenCover.Test.Integration
{
[TestFixture(Description = "Theses tests are targets used for integration testing")]
[Category("Integration")]
[Explicit("Integration")]
public class SimpleBranchTests
{
[Test]
public void SimpleIf()
{
bool x = true;
if (x)
{
System.Diagnostics.Debug.WriteLine("X=SimpleIf(true)");
}
else
{
System.Diagnostics.Debug.WriteLine("X=SimpleIf(false)");
}

bool y = true;
if (!y)
{
System.Diagnostics.Debug.WriteLine("Y=SimpleIf(true)");
}
else
{
System.Diagnostics.Debug.WriteLine("Y=SimpleIf(false)");
}
}
}
}
1 change: 1 addition & 0 deletions main/OpenCover.Test/OpenCover.Test.csproj
Expand Up @@ -118,6 +118,7 @@
<Compile Include="Framework\ProfilerRegistrationTests.cs" />
<Compile Include="Framework\Service\ProfilerCommunicationTests.cs" />
<Compile Include="Framework\Symbols\CecilSymbolManagerTests.cs" />
<Compile Include="Integration\SimpleBranchTests.cs" />
<Compile Include="Integration\SimpleExceptionTests.cs" />
<Compile Include="MoqFramework\UnityAutoMockContainerBase.cs" />
<Compile Include="MoqFramework\UnityAutoMockContainerBaseTests.cs" />
Expand Down

0 comments on commit 50023e0

Please sign in to comment.