Skip to content

Commit

Permalink
"Unvisited" default: switch branch coverage
Browse files Browse the repository at this point in the history
Goal: To join unvisited switch default: branch with earlier by compiler
inserted conditional branch.
Why? To cover in fact visited branch that is due compiler rewriting
reported as unvisited.
How: Branch instrumentation for all switch(default) and for all
conditional br instructions (if branch, not else branch!), where exists
unconditional-branch-jump-chain, is "pushed down". Last conditional jump
(if exists) is rewired to inserted instrumentation.
  • Loading branch information
Dragan committed Dec 2, 2013
1 parent a0f47a8 commit 94b2618
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 155 deletions.
68 changes: 34 additions & 34 deletions main/OpenCover.Profiler/CodeCoverage.cpp
Expand Up @@ -30,8 +30,8 @@ HRESULT STDMETHODCALLTYPE CCodeCoverage::Initialize(
RELTRACE(L" ::Initialize(...) => CLSID == %s", szGuid);
//::OutputDebugStringW(szGuid);

WCHAR szExeName[MAX_PATH];
GetModuleFileNameW(NULL, szExeName, MAX_PATH);
WCHAR szExeName[MAX_PATH];
GetModuleFileNameW(NULL, szExeName, MAX_PATH);
RELTRACE(L" ::Initialize(...) => EXE = %s", szExeName);

WCHAR szModuleName[MAX_PATH];
Expand All @@ -50,7 +50,7 @@ HRESULT STDMETHODCALLTYPE CCodeCoverage::Initialize(
if (m_profilerInfo2 == NULL) return E_FAIL;
m_profilerInfo3 = pICorProfilerInfoUnk;
#ifndef _TOOLSETV71
m_profilerInfo4 = pICorProfilerInfoUnk;
m_profilerInfo4 = pICorProfilerInfoUnk;
#endif

ZeroMemory(&m_runtimeVersion, sizeof(m_runtimeVersion));
Expand Down Expand Up @@ -82,11 +82,11 @@ HRESULT STDMETHODCALLTYPE CCodeCoverage::Initialize(

TCHAR threshold[1024] = {0};
::GetEnvironmentVariable(_T("OpenCover_Profiler_Threshold"), threshold, 1024);
m_threshold = _tcstoul(threshold, NULL, 10);
m_threshold = _tcstoul(threshold, NULL, 10);
ATLTRACE(_T(" ::Initialize(...) => threshold = %ul"), m_threshold);

TCHAR tracebyTest[1024] = {0};
::GetEnvironmentVariable(_T("OpenCover_Profiler_TraceByTest"), tracebyTest, 1024);
::GetEnvironmentVariable(_T("OpenCover_Profiler_TraceByTest"), tracebyTest, 1024);
bool tracingEnabled = _tcslen(tracebyTest) != 0;
ATLTRACE(_T(" ::Initialize(...) => tracingEnabled = %s (%s)"), tracingEnabled ? _T("true") : _T("false"), tracebyTest);

Expand All @@ -105,20 +105,20 @@ HRESULT STDMETHODCALLTYPE CCodeCoverage::Initialize(
dwMask |= COR_PRF_DISABLE_INLINING; // Disables all inlining.
dwMask |= COR_PRF_DISABLE_OPTIMIZATIONS; // Disables all code optimizations.
dwMask |= COR_PRF_USE_PROFILE_IMAGES; // Causes the native image search to look for profiler-enhanced images
if (tracingEnabled)
dwMask |= COR_PRF_MONITOR_ENTERLEAVE; // Controls the FunctionEnter, FunctionLeave, and FunctionTailcall callbacks.
if (tracingEnabled)
dwMask |= COR_PRF_MONITOR_ENTERLEAVE; // Controls the FunctionEnter, FunctionLeave, and FunctionTailcall callbacks.

if (m_useOldStyle)
dwMask |= COR_PRF_DISABLE_TRANSPARENCY_CHECKS_UNDER_FULL_TRUST; // Disables security transparency checks that are normally done during just-in-time (JIT) compilation and class loading for full-trust assemblies. This can make some instrumentation easier to perform.

#ifndef _TOOLSETV71
if (m_profilerInfo4 != NULL)
{
if (m_profilerInfo4 != NULL)
{
ATLTRACE(_T(" ::Initialize (m_profilerInfo4 OK)"));
//dwMask |= COR_PRF_ENABLE_REJIT;
//dwMask |= COR_PRF_DISABLE_ALL_NGEN_IMAGES;
}
//dwMask |= COR_PRF_ENABLE_REJIT;
//dwMask |= COR_PRF_DISABLE_ALL_NGEN_IMAGES;
}
#endif
COM_FAIL_MSG_RETURN_ERROR(m_profilerInfo2->SetEventMask(dwMask),
_T(" ::Initialize(...) => SetEventMask => 0x%X"));
Expand Down Expand Up @@ -149,9 +149,9 @@ HRESULT STDMETHODCALLTYPE CCodeCoverage::Initialize(
/// <summary>Handle <c>ICorProfilerCallback::Shutdown</c></summary>
HRESULT STDMETHODCALLTYPE CCodeCoverage::Shutdown( void)
{
WCHAR szExeName[MAX_PATH];
GetModuleFileNameW(NULL, szExeName, MAX_PATH);
RELTRACE(_T("::Shutdown - Nothing left to do but return S_OK(%s)"), szExeName);
WCHAR szExeName[MAX_PATH];
GetModuleFileNameW(NULL, szExeName, MAX_PATH);
RELTRACE(_T("::Shutdown - Nothing left to do but return S_OK(%s)"), szExeName);
g_pProfiler = NULL;
return S_OK;
}
Expand All @@ -168,7 +168,7 @@ static void __fastcall InstrumentPointVisit(ULONG seq)

void CCodeCoverage::AddVisitPoint(ULONG uniqueId)
{
m_host.AddVisitPointWithThreshold(uniqueId, m_threshold);
m_host.AddVisitPointWithThreshold(uniqueId, m_threshold);
}

static COR_SIGNATURE visitedMethodCallSignature[] =
Expand Down Expand Up @@ -468,7 +468,7 @@ HRESULT STDMETHODCALLTYPE CCodeCoverage::JITCompilationStarted(
//seqPoints.clear();
//brPoints.clear();

// Instrument method
// Instrument method
InstrumentMethod(moduleId, instumentedMethod, seqPoints, brPoints);

instumentedMethod.DumpIL();
Expand All @@ -491,14 +491,14 @@ HRESULT STDMETHODCALLTYPE CCodeCoverage::JITCompilationStarted(
if (m_runtimeVersion.usMajorVersion >= 4)
CoTaskMemFree(pMap);

// resize the threshold array
if (m_threshold != 0)
{
if (seqPoints.size() > 0)
m_host.Resize(seqPoints.back().UniqueId + 1);
if (brPoints.size() > 0)
m_host.Resize(brPoints.back().UniqueId + 1);
}
// resize the threshold array
if (m_threshold != 0)
{
if (seqPoints.size() > 0)
m_host.Resize(seqPoints.back().UniqueId + 1);
if (brPoints.size() > 0)
m_host.Resize(brPoints.back().UniqueId + 1);
}
}
}

Expand All @@ -512,9 +512,9 @@ void CCodeCoverage::InstrumentMethod(ModuleID moduleId, Method& method, std::ve
mdSignature pvsig = GetMethodSignatureToken_I4(moduleId);
void (__fastcall *pt)(ULONG) = &InstrumentPointVisit;

InstructionList instructions;
CoverageInstrumentation::InsertFunctionCall(instructions, pvsig, (FPTR)pt, seqPoints[0].UniqueId);
if (method.IsInstrumented(0, instructions)) return;
InstructionList instructions;
CoverageInstrumentation::InsertFunctionCall(instructions, pvsig, (FPTR)pt, seqPoints[0].UniqueId);
if (method.IsInstrumented(0, instructions)) return;

CoverageInstrumentation::AddBranchCoverage([pvsig, pt](InstructionList& instructions, ULONG uniqueId)->Instruction*
{
Expand All @@ -530,16 +530,16 @@ void CCodeCoverage::InstrumentMethod(ModuleID moduleId, Method& method, std::ve
{
mdMethodDef injectedVisitedMethod = RegisterSafeCuckooMethod(moduleId);

InstructionList instructions;
CoverageInstrumentation::InsertInjectedMethod(instructions, injectedVisitedMethod, seqPoints[0].UniqueId);
if (method.IsInstrumented(0, instructions)) return;
InstructionList instructions;
CoverageInstrumentation::InsertInjectedMethod(instructions, injectedVisitedMethod, seqPoints[0].UniqueId);
if (method.IsInstrumented(0, instructions)) return;

CoverageInstrumentation::AddBranchCoverage([injectedVisitedMethod](InstructionList& instructions, ULONG uniqueId)->Instruction*
{
return CoverageInstrumentation::InsertInjectedMethod(instructions, injectedVisitedMethod, uniqueId);
}, method, brPoints);
CoverageInstrumentation::AddSequenceCoverage([injectedVisitedMethod](InstructionList& instructions, ULONG uniqueId)->Instruction*
CoverageInstrumentation::AddSequenceCoverage([injectedVisitedMethod](InstructionList& instructions, ULONG uniqueId)->Instruction*
{
return CoverageInstrumentation::InsertInjectedMethod(instructions, injectedVisitedMethod, uniqueId);
}, method, seqPoints);
Expand Down
73 changes: 60 additions & 13 deletions main/OpenCover.Profiler/CoverageInstrumentation.h
Expand Up @@ -27,7 +27,7 @@ namespace CoverageInstrumentation
template<class IM>
void AddBranchCoverage(IM instrumentMethod, Method& method, std::vector<BranchPoint> points)
{
#define MOVE_TO_ENDOFBRANCH FALSE
#define MOVE_TO_ENDOFBRANCH TRUE
if (points.size() == 0) return;

for (auto it = method.m_instructions.begin(); it != method.m_instructions.end(); ++it)
Expand All @@ -45,15 +45,43 @@ namespace CoverageInstrumentation

InstructionList instructions;

// istrument branch 0 == fall-through branch =>
// switch instruction DEFAULT branch
// br(if) instruction ELSE branch
ULONG uniqueId = (*std::find_if(points.begin(), points.end(), [pCurrent, idx](BranchPoint &bp){return bp.Offset == pCurrent->m_origOffset && bp.Path == idx;})).UniqueId;

#if MOVE_TO_ENDOFBRANCH
// push-down instrumentation by following links (unconditional BR instructions)
// if two or more end-of-branch link to same instruction (branch-join-point),
// then visit counts will merge. ie:
// IL_100 branch instrumentation 1) - count visits for 1)
// IL_101 branch instrumentation 2) - count visits for 1) and 2)
// IL_102 branch instrumentation 3) - count visits for 1) and 2) and 3)
// IL_103 branch join-point
// But, at branch instrumentation visit-count doesn't really count :)
// What does count is visited or not-visited.
instructions.clear();
Instruction* pElse = instrumentMethod(instructions, uniqueId);
Instruction* toInstrument = method.EndOfBranch( pNext );
if ( toInstrument->m_prev != NULL ) // rewire last BR instruction?
toInstrument->m_prev->m_branches[0] = pElse;
method.InsertInstructionsAtOffset( toInstrument->m_offset, instructions );
Instruction* toInstrument = pNext;
if (pCurrent->m_operation == CEE_SWITCH) {
// to minimize unwanted branch join/merge
// push down only (switch)DEFAULT branch
toInstrument = method.EndOfBranch( pNext ); // push SWITCH default branch down
if ( toInstrument->m_jump != NULL )
{
_ASSERTE(toInstrument != pNext);
_ASSERTE(toInstrument->m_jump->m_isBranch && toInstrument->m_jump->m_operation==CEE_BR);
_ASSERTE(toInstrument->m_jump->m_branches.size() == 1);
_ASSERTE(toInstrument->m_jump->m_branches[0] == toInstrument);
toInstrument->m_jump->m_branches[0] = pElse; // rewire BR/jump instruction to instrumentation
}
else
{
_ASSERTE(toInstrument == pNext);
}
}
for (it = method.m_instructions.begin(); *it != toInstrument; ++it);
method.m_instructions.insert(it, instructions.begin(), instructions.end());
#else
instrumentMethod(instructions, uniqueId);

Expand All @@ -71,15 +99,34 @@ namespace CoverageInstrumentation
#if MOVE_TO_ENDOFBRANCH
instructions.clear();
Instruction* pRecordJmp = instrumentMethod(instructions, uniqueId);
Instruction* toInstrument = method.EndOfBranch( *sbit );
if ( toInstrument->m_prev == NULL ) // rewire branch direct to instrumentation?
*sbit = pRecordJmp;
else // rewire indirect via last BR instruction
toInstrument->m_prev->m_branches[0] = pRecordJmp;
method.InsertInstructionsAtOffset( toInstrument->m_offset, instructions );
Instruction* toInstrument = *sbit;
if (pCurrent->m_operation == CEE_SWITCH)
{
// to minimize unwanted join-merge
// do not push-down instrumentation for switch branches
*sbit = pRecordJmp; // rewire switch branch to instrumentation
}
else
{
toInstrument = method.EndOfBranch( *sbit );
if ( toInstrument->m_jump == NULL )
{
_ASSERTE(toInstrument == *sbit);
*sbit = pRecordJmp; // rewire branch to instrumentation
}
else
{
_ASSERTE(toInstrument != *sbit);
_ASSERTE(toInstrument->m_jump->m_isBranch && toInstrument->m_jump->m_operation==CEE_BR );
_ASSERTE(toInstrument->m_jump->m_branches.size() == 1);
_ASSERTE(toInstrument->m_jump->m_branches[0] == toInstrument);
toInstrument->m_jump->m_branches[0] = pRecordJmp; // rewire last BR to instrumentation
}
}
for (it = method.m_instructions.begin(); *it != toInstrument; ++it);
method.m_instructions.insert(it, instructions.begin(), instructions.end());
#else
Instruction *pRecordJmp = instrumentMethod(instructions, uniqueId);

Instruction *pSwitchJump = new Instruction(CEE_BR);
pSwitchJump->m_isBranch = true;
instructions.push_back(pSwitchJump);
Expand All @@ -92,9 +139,9 @@ namespace CoverageInstrumentation
#else
// *it points here at pNext
method.m_instructions.insert(it, instructions.begin(), instructions.end());
#endif
// restore 'it' position
for (it = method.m_instructions.begin(); *it != pNext; ++it);
#endif
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion main/OpenCover.Profiler/Instruction.h
Expand Up @@ -38,7 +38,7 @@ class Instruction

std::vector<long> m_branchOffsets;
InstructionList m_branches;
Instruction* m_prev;
Instruction* m_jump;
long m_origOffset;

public:
Expand Down

0 comments on commit 94b2618

Please sign in to comment.