Skip to content

Commit 06bbd4b

Browse files
committed
[feat] adds assembly tracking to coverage tool
includes several style improvements
1 parent d7af447 commit 06bbd4b

File tree

10 files changed

+127
-53
lines changed

10 files changed

+127
-53
lines changed

VSharp.CoverageInstrumenter/profiler/ILRewriter.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,7 +1254,8 @@ HRESULT RewriteIL(
12541254
ModuleID moduleID,
12551255
mdMethodDef methodDef,
12561256
int methodId,
1257-
bool isMain)
1257+
bool isMain,
1258+
bool isTestRun)
12581259
{
12591260
ILRewriter rewriter(pICorProfilerInfo, pICorProfilerFunctionControl, moduleID, methodDef);
12601261
auto pilr = &rewriter;
@@ -1263,7 +1264,7 @@ HRESULT RewriteIL(
12631264

12641265
vsharp::ProbeCall* enterMethod;
12651266
vsharp::ProbeCall* leaveMethod;
1266-
if (isMain) {
1267+
if (isMain || isTestRun) {
12671268
enterMethod = covProb->EnterMain;
12681269
leaveMethod = covProb->LeaveMain;
12691270
}

VSharp.CoverageInstrumenter/profiler/ILRewriter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ HRESULT RewriteIL(
115115
ModuleID moduleID,
116116
mdMethodDef methodDef,
117117
int methodId,
118-
bool isMain);
118+
bool isMain,
119+
bool isTestRun);
119120

120121
bool NeedFullInstrumentation(const WCHAR *moduleName, int moduleSize, mdMethodDef method);
121122

VSharp.CoverageInstrumenter/profiler/corProfiler.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ HRESULT STDMETHODCALLTYPE CorProfiler::Initialize(IUnknown *pICorProfilerInfoUnk
5656
#define IfFailRet(EXPR) do { HRESULT hr = (EXPR); if(FAILED(hr)) { return (hr); } } while (0)
5757
IfFailRet(this->corProfilerInfo->SetEventMask(eventMask));
5858

59-
6059
profilerState = new ProfilerState((ICorProfilerInfo8*)this);
6160

6261
LOG(tout << "Initialize finished" << std::endl);
@@ -221,7 +220,8 @@ HRESULT STDMETHODCALLTYPE CorProfiler::JITCompilationStarted(FunctionID function
221220

222221
UNUSED(fIsSafeToBlock);
223222
auto instrument = new Instrumenter(*corProfilerInfo);
224-
HRESULT hr = instrument->instrument(functionId);
223+
auto methodName = GetFunctionName(functionId);
224+
HRESULT hr = instrument->instrument(functionId, methodName);
225225
delete instrument;
226226

227227
std::atomic_fetch_sub(&shutdownBlockingRequestsCount, 1);

VSharp.CoverageInstrumenter/profiler/coverageTracker.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ struct MethodInfo {
2828
WCHAR *assemblyName;
2929
ULONG moduleNameLength;
3030
WCHAR *moduleName;
31+
std::string methodName;
3132

3233
void serialize(std::vector<char>& buffer) const;
3334
};
@@ -57,8 +58,6 @@ class CoverageTracker {
5758

5859
private:
5960
bool collectMainOnly;
60-
std::mutex collectedMethodsMutex;
61-
std::vector<MethodInfo> collectedMethods;
6261
std::mutex visitedMethodsMutex;
6362
std::set<int> visitedMethods;
6463
ThreadStorage<CoverageHistory*>* trackedCoverage;
@@ -67,6 +66,8 @@ class CoverageTracker {
6766
std::vector<std::vector<char>> serializedCoverage;
6867
std::vector<int> serializedCoverageThreadIds;
6968
public:
69+
std::mutex collectedMethodsMutex;
70+
std::vector<MethodInfo> collectedMethods;
7071
explicit CoverageTracker(ThreadTracker* threadTracker, ThreadInfo* threadInfo, bool collectMainOnly);
7172
bool isCollectMainOnly() const;
7273
void addCoverage(OFFSET offset, CoverageEvent event, int methodId);

VSharp.CoverageInstrumenter/profiler/instrumenter.cpp

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,21 @@ bool vsharp::IsMain(const WCHAR *moduleName, int moduleSize, mdMethodDef method)
6767
return true;
6868
}
6969

70-
bool vsharp::InstrumentationIsNeeded(const WCHAR *moduleName, int moduleSize, mdMethodDef method) {
71-
return IsMain(moduleName, moduleSize, method) || !profilerState->collectMainOnly;
70+
bool isApprovedAssembly(const WCHAR *assemblyName, int assemblyNameLength) {
71+
for (auto approved : profilerState->approvedAssemblies) {
72+
if (assemblyNameLength - 1 == approved.length() && std::string(assemblyName, assemblyName + assemblyNameLength - 1) == approved)
73+
return true;
74+
}
75+
return false;
76+
}
77+
78+
// TODO: simplify the condition by adding two main modes as coverage tool config
79+
bool vsharp::InstrumentationIsNeeded(const WCHAR* assemblyName, int assemblySize, const WCHAR *moduleName, int moduleSize, mdMethodDef method) {
80+
return IsMain(moduleName, moduleSize, method) || (!profilerState->isTestExpected && !profilerState->collectMainOnly)
81+
|| (profilerState->isTestExpected && isApprovedAssembly(moduleName, moduleSize));
7282
}
7383

74-
HRESULT Instrumenter::doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength) {
84+
HRESULT Instrumenter::doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength, bool isTestRun) {
7585
HRESULT hr;
7686
CComPtr<IMetaDataImport> metadataImport;
7787
CComPtr<IMetaDataEmit> metadataEmit;
@@ -93,13 +103,14 @@ HRESULT Instrumenter::doInstrumentation(ModuleID oldModuleId, size_t methodId, c
93103
m_moduleId,
94104
m_jittedToken,
95105
methodId,
96-
IsMain(moduleName, moduleNameLength, m_jittedToken)
106+
IsMain(moduleName, moduleNameLength, m_jittedToken),
107+
isTestRun
97108
);
98109

99110
return S_OK;
100111
}
101112

102-
HRESULT Instrumenter::instrument(FunctionID functionId) {
113+
HRESULT Instrumenter::instrument(FunctionID functionId, std::string methodName) {
103114
HRESULT hr = S_OK;
104115
ModuleID newModuleId;
105116
ClassID classId;
@@ -119,34 +130,37 @@ HRESULT Instrumenter::instrument(FunctionID functionId) {
119130
WCHAR *assemblyName = new WCHAR[assemblyNameLength];
120131
IfFailRet(m_profilerInfo.GetAssemblyInfo(assembly, assemblyNameLength, &assemblyNameLength, assemblyName, &appDomainId, &startModuleId));
121132

133+
// checking if this method was rewritten before
134+
if (instrumentedMethods.find({ m_jittedToken, newModuleId }) != instrumentedMethods.end()) {
135+
// LOG(tout << "repeated JIT of " << m_jittedToken << "! skipped" << std::endl);
136+
return S_OK;
137+
}
138+
139+
LOG(tout << "JIT of " << std::string(assemblyName, assemblyName + assemblyNameLength - 1) << '.' << methodName << std::endl);
140+
122141
// skipping non-main methods
123-
if (!InstrumentationIsNeeded(moduleName, moduleNameLength, m_jittedToken)) {
142+
if (!InstrumentationIsNeeded(assemblyName, assemblyNameLength, moduleName, moduleNameLength, m_jittedToken)) {
124143
return S_OK;
125144
}
126145

127146
if (profilerState->collectMainOnly) {
128147
profilerState->mainFunctionId = functionId;
129148
}
130149

131-
// checking if this method was rewritten before
132-
if (instrumentedMethods.find({ m_jittedToken, newModuleId }) != instrumentedMethods.end()) {
133-
// LOG(tout << "repeated JIT of " << m_jittedToken << "! skipped" << std::endl);
134-
return S_OK;
135-
}
136-
137150
mutex.lock();
138151
size_t currentMethodId = profilerState->coverageTracker->collectMethod({
139152
m_jittedToken,
140153
assemblyNameLength,
141154
assemblyName,
142155
moduleNameLength,
143-
moduleName}
156+
moduleName,
157+
methodName}
144158
);
145159
instrumentedMethods.insert({m_jittedToken, newModuleId});
146160
mutex.unlock();
147161
ModuleID oldModuleId = m_moduleId;
148162
m_moduleId = newModuleId;
149-
hr = doInstrumentation(oldModuleId, currentMethodId, moduleName, moduleNameLength);
163+
hr = doInstrumentation(oldModuleId, currentMethodId, moduleName, moduleNameLength, profilerState->isTestExpected);
150164

151165
return hr;
152166
}

VSharp.CoverageInstrumenter/profiler/instrumenter.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@ class Instrumenter {
1919
char *m_signatureTokens;
2020
unsigned m_signatureTokensLength;
2121
std::mutex mutex;
22-
HRESULT doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength);
22+
HRESULT doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength, bool isTestRun);
2323

2424
public:
2525
explicit Instrumenter(ICorProfilerInfo8 &profilerInfo);
2626
~Instrumenter();
2727

28-
HRESULT instrument(FunctionID functionId);
28+
HRESULT instrument(FunctionID functionId, std::string methodName);
2929
};
3030

3131
bool IsMain(const WCHAR *moduleName, int moduleSize, mdMethodDef method);
32-
bool InstrumentationIsNeeded(const WCHAR *moduleName, int moduleSize, mdMethodDef method);
32+
bool InstrumentationIsNeeded(const WCHAR* assemblyName, int assemblySize, const WCHAR *moduleName, int moduleSize, mdMethodDef method);
3333

3434
}
3535

VSharp.CoverageInstrumenter/profiler/logging.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ std::ofstream tout;
66
std::recursive_mutex logMutex;
77

88
void open_log(const char *&logName) {
9-
tout.open(logName, std::ios_base::app);
9+
tout.open(logName, std::ios_base::trunc);
1010
}
1111

1212
void close_log() {

VSharp.CoverageInstrumenter/profiler/probes.cpp

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,17 @@ CoverageProbes* vsharp::getProbes() {
3535

3636
void vsharp::InitializeProbes() {
3737
auto covProbes = vsharp::getProbes();
38-
covProbes->Coverage = new ProbeCall((INT_PTR) &Track_Coverage);
39-
covProbes->Branch = new ProbeCall((INT_PTR) &Branch);
40-
covProbes->Enter = new ProbeCall((INT_PTR) &Track_Enter);
41-
covProbes->EnterMain = new ProbeCall((INT_PTR) &Track_EnterMain);
42-
covProbes->Leave = new ProbeCall((INT_PTR) &Track_Leave);
43-
covProbes->LeaveMain = new ProbeCall((INT_PTR) &Track_LeaveMain);
44-
covProbes->Finalize_Call = new ProbeCall((INT_PTR) &Finalize_Call);
45-
covProbes->Call = new ProbeCall((INT_PTR) &Track_Call);
46-
covProbes->Tailcall = new ProbeCall((INT_PTR) &Track_Tailcall);
47-
covProbes->Stsfld = new ProbeCall((INT_PTR) &Track_Stsfld);
48-
covProbes->Throw = new ProbeCall((INT_PTR) &Track_Throw);
38+
covProbes->Coverage = new ProbeCall((INT_PTR) &Track_Coverage);
39+
covProbes->Branch = new ProbeCall((INT_PTR) &Branch);
40+
covProbes->Enter = new ProbeCall((INT_PTR) &Track_Enter);
41+
covProbes->EnterMain = new ProbeCall((INT_PTR) &Track_EnterMain);
42+
covProbes->Leave = new ProbeCall((INT_PTR) &Track_Leave);
43+
covProbes->LeaveMain = new ProbeCall((INT_PTR) &Track_LeaveMain);
44+
covProbes->Finalize_Call = new ProbeCall((INT_PTR) &Finalize_Call);
45+
covProbes->Call = new ProbeCall((INT_PTR) &Track_Call);
46+
covProbes->Tailcall = new ProbeCall((INT_PTR) &Track_Tailcall);
47+
covProbes->Stsfld = new ProbeCall((INT_PTR) &Track_Stsfld);
48+
covProbes->Throw = new ProbeCall((INT_PTR) &Track_Throw);
4949
LOG(tout << "probes initialized" << std::endl);
5050
}
5151

@@ -84,12 +84,25 @@ void vsharp::Track_Tailcall(OFFSET offset, int methodId) {
8484
profilerState->coverageTracker->addCoverage(offset, Tailcall, methodId);
8585
}
8686

87+
void printMethod(std::string message, int methodId) {
88+
auto tracker = profilerState->coverageTracker;
89+
LOG(
90+
tracker->collectedMethodsMutex.lock();
91+
auto method = tracker->collectedMethods[methodId];
92+
auto wl = method.assemblyNameLength;
93+
auto ws = method.assemblyName;
94+
tout << message << ' ' << std::string(ws, ws + wl - 1) << '.' << method.methodName << std::endl;
95+
tracker->collectedMethodsMutex.unlock();
96+
);
97+
}
98+
8799
void vsharp::Track_Enter(OFFSET offset, int methodId, int isSpontaneous) {
88100
if (!profilerState->threadTracker->isCurrentThreadTracked()) return;
101+
printMethod("Entered", methodId);
89102
if (profilerState->threadTracker->isPossibleStackOverflow()) {
90103
LOG(tout << "Possible stack overflow: " << methodId);
91104
}
92-
LOG(tout << "Track_Enter: " << methodId);
105+
// LOG(tout << "Track_Enter: " << methodId);
93106
if (!profilerState->coverageTracker->isCollectMainOnly())
94107
profilerState->coverageTracker->addCoverage(offset, Enter, methodId);
95108
profilerState->threadTracker->stackBalanceUp();
@@ -100,9 +113,11 @@ void vsharp::Track_EnterMain(OFFSET offset, int methodId, int isSpontaneous) {
100113
// Recursive enter
101114
LOG(tout << "(recursive) Track_EnterMain: " << methodId);
102115
profilerState->threadTracker->stackBalanceUp();
116+
profilerState->coverageTracker->addCoverage(offset, Enter, methodId);
103117
return;
104118
}
105-
LOG(tout << "Track_EnterMain: " << methodId);
119+
printMethod("Entered Main", methodId);
120+
// LOG(tout << "Track_EnterMain: " << methodId);
106121
profilerState->threadTracker->trackCurrentThread();
107122
profilerState->threadTracker->stackBalanceUp();
108123
profilerState->coverageTracker->addCoverage(offset, EnterMain, methodId);
@@ -118,8 +133,9 @@ void vsharp::Track_Leave(OFFSET offset, int methodId) {
118133

119134
void vsharp::Track_LeaveMain(OFFSET offset, int methodId) {
120135
if (!profilerState->threadTracker->isCurrentThreadTracked()) return;
136+
printMethod("Left Main", methodId);
121137
profilerState->coverageTracker->addCoverage(offset, LeaveMain, methodId);
122-
LOG(tout << "Track_LeaveMain: " << methodId);
138+
// LOG(tout << "Track_LeaveMain: " << methodId);
123139
if (profilerState->threadTracker->stackBalanceDown()) {
124140
// first main frame is not yet reached
125141
return;

VSharp.CoverageInstrumenter/profiler/profilerState.cpp

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,51 @@ void ConvertToWCHAR(const char *str, std::u16string &result) {
1313
result = conv16.from_bytes(str);
1414
}
1515

16+
bool IsEnvVarPresent(const char *name) {
17+
return std::getenv(name) != nullptr;
18+
}
19+
20+
char *CheckEnvVarAndGet(const char *name) {
21+
auto str = std::getenv(name);
22+
profiler_assert(str);
23+
return str;
24+
}
25+
1626
bool ProfilerState::isCorrectFunctionId(FunctionID id) {
1727
return incorrectFunctionId != id;
1828
}
1929

2030
ProfilerState::ProfilerState(ICorProfilerInfo8 *corProfilerInfo) {
21-
const char* isPassive = std::getenv("COVERAGE_TOOL_ENABLE_PASSIVE");
31+
isPassiveRun = IsEnvVarPresent("COVERAGE_TOOL_ENABLE_PASSIVE");
32+
isTestExpected = IsEnvVarPresent("COVERAGE_TOOL_EXPECT_TEST_SUITE");
33+
collectMainOnly = IsEnvVarPresent("COVERAGE_TOOL_INSTRUMENT_MAIN_ONLY");
34+
35+
mainMethodInfo.moduleName = nullptr; // mainMethod is not yet specified
2236

2337
#ifdef _LOGGING
24-
const char* name = isPassive == nullptr ? "lastrun.log" : "lastcoverage.log";
38+
const char *name = isPassiveRun ? "lastrun.log" : "lastcoverage.log";
2539
open_log(name);
2640
#endif
2741

2842
InitializeProbes();
29-
if (isPassive != nullptr) {
43+
44+
if (isPassiveRun) {
3045
LOG(tout << "WORKING IN PASSIVE MODE" << std::endl);
46+
passiveResultPath = CheckEnvVarAndGet("COVERAGE_TOOL_RESULT_NAME");
47+
}
3148

32-
isPassiveRun = true;
33-
collectMainOnly = true;
49+
if (isTestExpected) {
50+
ReadTestAssemblies(CheckEnvVarAndGet("COVERAGE_TOOL_ASSEMBLY_PATHS_FILE"));
51+
LOG(tout << "RECEIVED ASSEMBLIES TO INSTRUMENT" << std::endl);
52+
}
3453

54+
if (IsEnvVarPresent("COVERAGE_TOOL_SPECIFY_MAIN_METHOD")) {
3555
std::u16string assemblyNameU16;
3656
std::u16string moduleNameU16;
3757

38-
ConvertToWCHAR(std::getenv("COVERAGE_TOOL_METHOD_ASSEMBLY_NAME"), assemblyNameU16);
39-
ConvertToWCHAR(std::getenv("COVERAGE_TOOL_METHOD_MODULE_NAME"), moduleNameU16);
40-
int mainToken = std::stoi(std::getenv("COVERAGE_TOOL_METHOD_TOKEN"));
58+
ConvertToWCHAR(CheckEnvVarAndGet("COVERAGE_TOOL_METHOD_ASSEMBLY_NAME"), assemblyNameU16);
59+
ConvertToWCHAR(CheckEnvVarAndGet("COVERAGE_TOOL_METHOD_MODULE_NAME"), moduleNameU16);
60+
int mainToken = std::stoi(CheckEnvVarAndGet("COVERAGE_TOOL_METHOD_TOKEN"));
4161

4262
size_t mainAssemblyNameLength = assemblyNameU16.size();
4363
auto* mainAssemblyName = new WCHAR[mainAssemblyNameLength];
@@ -54,11 +74,6 @@ ProfilerState::ProfilerState(ICorProfilerInfo8 *corProfilerInfo) {
5474
(ULONG) mainModuleNameLength,
5575
mainModuleName
5676
};
57-
passiveResultPath = std::getenv("COVERAGE_TOOL_RESULT_NAME");
58-
}
59-
60-
if (std::getenv("COVERAGE_TOOL_INSTRUMENT_MAIN_ONLY")) {
61-
collectMainOnly = true;
6277
}
6378

6479
mainFunctionId = -1;
@@ -67,6 +82,26 @@ ProfilerState::ProfilerState(ICorProfilerInfo8 *corProfilerInfo) {
6782
coverageTracker = new CoverageTracker(threadTracker, threadInfo, collectMainOnly);
6883
}
6984

85+
void vsharp::ProfilerState::ReadTestAssemblies(const char *path)
86+
{
87+
std::ifstream assembliesFile;
88+
assembliesFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
89+
90+
try {
91+
assembliesFile.open(path, std::ios_base::in);
92+
std::string assembly;
93+
94+
while (assembliesFile >> assembly)
95+
approvedAssemblies.push_back(assembly);
96+
97+
assembliesFile.close();
98+
}
99+
catch (std::ifstream::failure e) {
100+
LOG(tout << "FAILURE DURING THE READ OF ASSEMBLIES FILE! TOTAL RETRIEVED: " << approvedAssemblies.size()
101+
<< "\nCONTINUING WITH RETRIEVED DATA");
102+
}
103+
}
104+
70105
void vsharp::ProfilerState::setEntryMain(char *assemblyName, int assemblyNameLength, char *moduleName, int moduleNameLength, int methodToken) {
71106
auto* wcharAssemblyName = new WCHAR[assemblyNameLength];
72107
memcpy(wcharAssemblyName, assemblyName, assemblyNameLength * sizeof(WCHAR));

VSharp.CoverageInstrumenter/profiler/profilerState.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,27 @@
33

44
#include "threadTracker.h"
55
#include "coverageTracker.h"
6+
#include <set>
67

78
namespace vsharp {
89

910
class ProfilerState {
1011
private:
1112
static const FunctionID incorrectFunctionId = 0;
13+
14+
void ReadTestAssemblies(const char *path);
1215
public:
1316
ThreadTracker* threadTracker;
1417
CoverageTracker* coverageTracker;
1518
ThreadInfo* threadInfo;
1619

1720
bool isPassiveRun = false;
18-
bool collectMainOnly = true;
21+
bool collectMainOnly = false;
1922
bool isFinished = false;
23+
bool isTestExpected = false;
2024
char *passiveResultPath = nullptr;
25+
std::vector<std::string> approvedAssemblies;
26+
2127
MethodInfo mainMethodInfo;
2228
FunctionID mainFunctionId;
2329

0 commit comments

Comments
 (0)