From 9943ed9c85a4c37ee06fdcda56e606f626f1375a Mon Sep 17 00:00:00 2001 From: DanielELog Date: Sat, 3 Feb 2024 19:15:41 +0300 Subject: [PATCH 1/6] [feat] adds assembly tracking to coverage tool includes several style improvements --- .../profiler/ILRewriter.cpp | 5 +- .../profiler/ILRewriter.h | 3 +- .../profiler/corProfiler.cpp | 4 +- .../profiler/coverageTracker.h | 5 +- .../profiler/instrumenter.cpp | 42 ++++++++----- .../profiler/instrumenter.h | 6 +- .../profiler/logging.cpp | 2 +- .../profiler/probes.cpp | 44 ++++++++----- .../profiler/profilerState.cpp | 61 +++++++++++++++---- .../profiler/profilerState.h | 8 ++- 10 files changed, 127 insertions(+), 53 deletions(-) diff --git a/VSharp.CoverageInstrumenter/profiler/ILRewriter.cpp b/VSharp.CoverageInstrumenter/profiler/ILRewriter.cpp index bc5802691..db0accdfb 100644 --- a/VSharp.CoverageInstrumenter/profiler/ILRewriter.cpp +++ b/VSharp.CoverageInstrumenter/profiler/ILRewriter.cpp @@ -1254,7 +1254,8 @@ HRESULT RewriteIL( ModuleID moduleID, mdMethodDef methodDef, int methodId, - bool isMain) + bool isMain, + bool isTestRun) { ILRewriter rewriter(pICorProfilerInfo, pICorProfilerFunctionControl, moduleID, methodDef); auto pilr = &rewriter; @@ -1263,7 +1264,7 @@ HRESULT RewriteIL( vsharp::ProbeCall* enterMethod; vsharp::ProbeCall* leaveMethod; - if (isMain) { + if (isMain || isTestRun) { enterMethod = covProb->EnterMain; leaveMethod = covProb->LeaveMain; } diff --git a/VSharp.CoverageInstrumenter/profiler/ILRewriter.h b/VSharp.CoverageInstrumenter/profiler/ILRewriter.h index a646403f0..b1c226861 100644 --- a/VSharp.CoverageInstrumenter/profiler/ILRewriter.h +++ b/VSharp.CoverageInstrumenter/profiler/ILRewriter.h @@ -115,7 +115,8 @@ HRESULT RewriteIL( ModuleID moduleID, mdMethodDef methodDef, int methodId, - bool isMain); + bool isMain, + bool isTestRun); bool NeedFullInstrumentation(const WCHAR *moduleName, int moduleSize, mdMethodDef method); diff --git a/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp b/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp index 1c492b767..ddadcb3b0 100644 --- a/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp +++ b/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp @@ -56,7 +56,6 @@ HRESULT STDMETHODCALLTYPE CorProfiler::Initialize(IUnknown *pICorProfilerInfoUnk #define IfFailRet(EXPR) do { HRESULT hr = (EXPR); if(FAILED(hr)) { return (hr); } } while (0) IfFailRet(this->corProfilerInfo->SetEventMask(eventMask)); - profilerState = new ProfilerState((ICorProfilerInfo8*)this); LOG(tout << "Initialize finished" << std::endl); @@ -221,7 +220,8 @@ HRESULT STDMETHODCALLTYPE CorProfiler::JITCompilationStarted(FunctionID function UNUSED(fIsSafeToBlock); auto instrument = new Instrumenter(*corProfilerInfo); - HRESULT hr = instrument->instrument(functionId); + auto methodName = GetFunctionName(functionId); + HRESULT hr = instrument->instrument(functionId, methodName); delete instrument; std::atomic_fetch_sub(&shutdownBlockingRequestsCount, 1); diff --git a/VSharp.CoverageInstrumenter/profiler/coverageTracker.h b/VSharp.CoverageInstrumenter/profiler/coverageTracker.h index 8e0a325eb..29d6b2d26 100644 --- a/VSharp.CoverageInstrumenter/profiler/coverageTracker.h +++ b/VSharp.CoverageInstrumenter/profiler/coverageTracker.h @@ -28,6 +28,7 @@ struct MethodInfo { WCHAR *assemblyName; ULONG moduleNameLength; WCHAR *moduleName; + std::string methodName; void serialize(std::vector& buffer) const; }; @@ -57,8 +58,6 @@ class CoverageTracker { private: bool collectMainOnly; - std::mutex collectedMethodsMutex; - std::vector collectedMethods; std::mutex visitedMethodsMutex; std::set visitedMethods; ThreadStorage* trackedCoverage; @@ -67,6 +66,8 @@ class CoverageTracker { std::vector> serializedCoverage; std::vector serializedCoverageThreadIds; public: + std::mutex collectedMethodsMutex; + std::vector collectedMethods; explicit CoverageTracker(ThreadTracker* threadTracker, ThreadInfo* threadInfo, bool collectMainOnly); bool isCollectMainOnly() const; void addCoverage(OFFSET offset, CoverageEvent event, int methodId); diff --git a/VSharp.CoverageInstrumenter/profiler/instrumenter.cpp b/VSharp.CoverageInstrumenter/profiler/instrumenter.cpp index 09fa0f7bc..e023da6e5 100644 --- a/VSharp.CoverageInstrumenter/profiler/instrumenter.cpp +++ b/VSharp.CoverageInstrumenter/profiler/instrumenter.cpp @@ -67,11 +67,21 @@ bool vsharp::IsMain(const WCHAR *moduleName, int moduleSize, mdMethodDef method) return true; } -bool vsharp::InstrumentationIsNeeded(const WCHAR *moduleName, int moduleSize, mdMethodDef method) { - return IsMain(moduleName, moduleSize, method) || !profilerState->collectMainOnly; +bool isApprovedAssembly(const WCHAR *assemblyName, int assemblyNameLength) { + for (auto approved : profilerState->approvedAssemblies) { + if (assemblyNameLength - 1 == approved.length() && std::string(assemblyName, assemblyName + assemblyNameLength - 1) == approved) + return true; + } + return false; +} + +// TODO: simplify the condition by adding two main modes as coverage tool config +bool vsharp::InstrumentationIsNeeded(const WCHAR* assemblyName, int assemblySize, const WCHAR *moduleName, int moduleSize, mdMethodDef method) { + return IsMain(moduleName, moduleSize, method) || (!profilerState->isTestExpected && !profilerState->collectMainOnly) + || (profilerState->isTestExpected && isApprovedAssembly(moduleName, moduleSize)); } -HRESULT Instrumenter::doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength) { +HRESULT Instrumenter::doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength, bool isTestRun) { HRESULT hr; CComPtr metadataImport; CComPtr metadataEmit; @@ -93,13 +103,14 @@ HRESULT Instrumenter::doInstrumentation(ModuleID oldModuleId, size_t methodId, c m_moduleId, m_jittedToken, methodId, - IsMain(moduleName, moduleNameLength, m_jittedToken) + IsMain(moduleName, moduleNameLength, m_jittedToken), + isTestRun ); return S_OK; } -HRESULT Instrumenter::instrument(FunctionID functionId) { +HRESULT Instrumenter::instrument(FunctionID functionId, std::string methodName) { HRESULT hr = S_OK; ModuleID newModuleId; ClassID classId; @@ -119,8 +130,16 @@ HRESULT Instrumenter::instrument(FunctionID functionId) { WCHAR *assemblyName = new WCHAR[assemblyNameLength]; IfFailRet(m_profilerInfo.GetAssemblyInfo(assembly, assemblyNameLength, &assemblyNameLength, assemblyName, &appDomainId, &startModuleId)); + // checking if this method was rewritten before + if (instrumentedMethods.find({ m_jittedToken, newModuleId }) != instrumentedMethods.end()) { + // LOG(tout << "repeated JIT of " << m_jittedToken << "! skipped" << std::endl); + return S_OK; + } + + LOG(tout << "JIT of " << std::string(assemblyName, assemblyName + assemblyNameLength - 1) << '.' << methodName << std::endl); + // skipping non-main methods - if (!InstrumentationIsNeeded(moduleName, moduleNameLength, m_jittedToken)) { + if (!InstrumentationIsNeeded(assemblyName, assemblyNameLength, moduleName, moduleNameLength, m_jittedToken)) { return S_OK; } @@ -128,25 +147,20 @@ HRESULT Instrumenter::instrument(FunctionID functionId) { profilerState->mainFunctionId = functionId; } - // checking if this method was rewritten before - if (instrumentedMethods.find({ m_jittedToken, newModuleId }) != instrumentedMethods.end()) { - // LOG(tout << "repeated JIT of " << m_jittedToken << "! skipped" << std::endl); - return S_OK; - } - mutex.lock(); size_t currentMethodId = profilerState->coverageTracker->collectMethod({ m_jittedToken, assemblyNameLength, assemblyName, moduleNameLength, - moduleName} + moduleName, + methodName} ); instrumentedMethods.insert({m_jittedToken, newModuleId}); mutex.unlock(); ModuleID oldModuleId = m_moduleId; m_moduleId = newModuleId; - hr = doInstrumentation(oldModuleId, currentMethodId, moduleName, moduleNameLength); + hr = doInstrumentation(oldModuleId, currentMethodId, moduleName, moduleNameLength, profilerState->isTestExpected); return hr; } diff --git a/VSharp.CoverageInstrumenter/profiler/instrumenter.h b/VSharp.CoverageInstrumenter/profiler/instrumenter.h index 6d2898d35..645126e26 100644 --- a/VSharp.CoverageInstrumenter/profiler/instrumenter.h +++ b/VSharp.CoverageInstrumenter/profiler/instrumenter.h @@ -19,17 +19,17 @@ class Instrumenter { char *m_signatureTokens; unsigned m_signatureTokensLength; std::mutex mutex; - HRESULT doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength); + HRESULT doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength, bool isTestRun); public: explicit Instrumenter(ICorProfilerInfo8 &profilerInfo); ~Instrumenter(); - HRESULT instrument(FunctionID functionId); + HRESULT instrument(FunctionID functionId, std::string methodName); }; bool IsMain(const WCHAR *moduleName, int moduleSize, mdMethodDef method); -bool InstrumentationIsNeeded(const WCHAR *moduleName, int moduleSize, mdMethodDef method); +bool InstrumentationIsNeeded(const WCHAR* assemblyName, int assemblySize, const WCHAR *moduleName, int moduleSize, mdMethodDef method); } diff --git a/VSharp.CoverageInstrumenter/profiler/logging.cpp b/VSharp.CoverageInstrumenter/profiler/logging.cpp index 92e479523..0ec1dbe52 100644 --- a/VSharp.CoverageInstrumenter/profiler/logging.cpp +++ b/VSharp.CoverageInstrumenter/profiler/logging.cpp @@ -6,7 +6,7 @@ std::ofstream tout; std::recursive_mutex logMutex; void open_log(const char *&logName) { - tout.open(logName, std::ios_base::app); + tout.open(logName, std::ios_base::trunc); } void close_log() { diff --git a/VSharp.CoverageInstrumenter/profiler/probes.cpp b/VSharp.CoverageInstrumenter/profiler/probes.cpp index df9eade85..455db7721 100644 --- a/VSharp.CoverageInstrumenter/profiler/probes.cpp +++ b/VSharp.CoverageInstrumenter/profiler/probes.cpp @@ -35,17 +35,17 @@ CoverageProbes* vsharp::getProbes() { void vsharp::InitializeProbes() { auto covProbes = vsharp::getProbes(); - covProbes->Coverage = new ProbeCall((INT_PTR) &Track_Coverage); - covProbes->Branch = new ProbeCall((INT_PTR) &Branch); - covProbes->Enter = new ProbeCall((INT_PTR) &Track_Enter); - covProbes->EnterMain = new ProbeCall((INT_PTR) &Track_EnterMain); - covProbes->Leave = new ProbeCall((INT_PTR) &Track_Leave); - covProbes->LeaveMain = new ProbeCall((INT_PTR) &Track_LeaveMain); - covProbes->Finalize_Call = new ProbeCall((INT_PTR) &Finalize_Call); - covProbes->Call = new ProbeCall((INT_PTR) &Track_Call); - covProbes->Tailcall = new ProbeCall((INT_PTR) &Track_Tailcall); - covProbes->Stsfld = new ProbeCall((INT_PTR) &Track_Stsfld); - covProbes->Throw = new ProbeCall((INT_PTR) &Track_Throw); + covProbes->Coverage = new ProbeCall((INT_PTR) &Track_Coverage); + covProbes->Branch = new ProbeCall((INT_PTR) &Branch); + covProbes->Enter = new ProbeCall((INT_PTR) &Track_Enter); + covProbes->EnterMain = new ProbeCall((INT_PTR) &Track_EnterMain); + covProbes->Leave = new ProbeCall((INT_PTR) &Track_Leave); + covProbes->LeaveMain = new ProbeCall((INT_PTR) &Track_LeaveMain); + covProbes->Finalize_Call = new ProbeCall((INT_PTR) &Finalize_Call); + covProbes->Call = new ProbeCall((INT_PTR) &Track_Call); + covProbes->Tailcall = new ProbeCall((INT_PTR) &Track_Tailcall); + covProbes->Stsfld = new ProbeCall((INT_PTR) &Track_Stsfld); + covProbes->Throw = new ProbeCall((INT_PTR) &Track_Throw); LOG(tout << "probes initialized" << std::endl); } @@ -84,12 +84,25 @@ void vsharp::Track_Tailcall(OFFSET offset, int methodId) { profilerState->coverageTracker->addCoverage(offset, Tailcall, methodId); } +void printMethod(std::string message, int methodId) { + auto tracker = profilerState->coverageTracker; + LOG( + tracker->collectedMethodsMutex.lock(); + auto method = tracker->collectedMethods[methodId]; + auto wl = method.assemblyNameLength; + auto ws = method.assemblyName; + tout << message << ' ' << std::string(ws, ws + wl - 1) << '.' << method.methodName << std::endl; + tracker->collectedMethodsMutex.unlock(); + ); +} + void vsharp::Track_Enter(OFFSET offset, int methodId, int isSpontaneous) { if (!profilerState->threadTracker->isCurrentThreadTracked()) return; + printMethod("Entered", methodId); if (profilerState->threadTracker->isPossibleStackOverflow()) { LOG(tout << "Possible stack overflow: " << methodId); } - LOG(tout << "Track_Enter: " << methodId); + // LOG(tout << "Track_Enter: " << methodId); if (!profilerState->coverageTracker->isCollectMainOnly()) profilerState->coverageTracker->addCoverage(offset, Enter, methodId); profilerState->threadTracker->stackBalanceUp(); @@ -100,9 +113,11 @@ void vsharp::Track_EnterMain(OFFSET offset, int methodId, int isSpontaneous) { // Recursive enter LOG(tout << "(recursive) Track_EnterMain: " << methodId); profilerState->threadTracker->stackBalanceUp(); + profilerState->coverageTracker->addCoverage(offset, Enter, methodId); return; } - LOG(tout << "Track_EnterMain: " << methodId); + printMethod("Entered Main", methodId); + // LOG(tout << "Track_EnterMain: " << methodId); profilerState->threadTracker->trackCurrentThread(); profilerState->threadTracker->stackBalanceUp(); profilerState->coverageTracker->addCoverage(offset, EnterMain, methodId); @@ -118,8 +133,9 @@ void vsharp::Track_Leave(OFFSET offset, int methodId) { void vsharp::Track_LeaveMain(OFFSET offset, int methodId) { if (!profilerState->threadTracker->isCurrentThreadTracked()) return; + printMethod("Left Main", methodId); profilerState->coverageTracker->addCoverage(offset, LeaveMain, methodId); - LOG(tout << "Track_LeaveMain: " << methodId); + // LOG(tout << "Track_LeaveMain: " << methodId); if (profilerState->threadTracker->stackBalanceDown()) { // first main frame is not yet reached return; diff --git a/VSharp.CoverageInstrumenter/profiler/profilerState.cpp b/VSharp.CoverageInstrumenter/profiler/profilerState.cpp index 9d0b0c8a9..821668acd 100644 --- a/VSharp.CoverageInstrumenter/profiler/profilerState.cpp +++ b/VSharp.CoverageInstrumenter/profiler/profilerState.cpp @@ -13,31 +13,51 @@ void ConvertToWCHAR(const char *str, std::u16string &result) { result = conv16.from_bytes(str); } +bool IsEnvVarPresent(const char *name) { + return std::getenv(name) != nullptr; +} + +char *CheckEnvVarAndGet(const char *name) { + auto str = std::getenv(name); + profiler_assert(str); + return str; +} + bool ProfilerState::isCorrectFunctionId(FunctionID id) { return incorrectFunctionId != id; } ProfilerState::ProfilerState(ICorProfilerInfo8 *corProfilerInfo) { - const char* isPassive = std::getenv("COVERAGE_TOOL_ENABLE_PASSIVE"); + isPassiveRun = IsEnvVarPresent("COVERAGE_TOOL_ENABLE_PASSIVE"); + isTestExpected = IsEnvVarPresent("COVERAGE_TOOL_EXPECT_TEST_SUITE"); + collectMainOnly = IsEnvVarPresent("COVERAGE_TOOL_INSTRUMENT_MAIN_ONLY"); + + mainMethodInfo.moduleName = nullptr; // mainMethod is not yet specified #ifdef _LOGGING - const char* name = isPassive == nullptr ? "lastrun.log" : "lastcoverage.log"; + const char *name = isPassiveRun ? "lastrun.log" : "lastcoverage.log"; open_log(name); #endif InitializeProbes(); - if (isPassive != nullptr) { + + if (isPassiveRun) { LOG(tout << "WORKING IN PASSIVE MODE" << std::endl); + passiveResultPath = CheckEnvVarAndGet("COVERAGE_TOOL_RESULT_NAME"); + } - isPassiveRun = true; - collectMainOnly = true; + if (isTestExpected) { + ReadTestAssemblies(CheckEnvVarAndGet("COVERAGE_TOOL_ASSEMBLY_PATHS_FILE")); + LOG(tout << "RECEIVED ASSEMBLIES TO INSTRUMENT" << std::endl); + } + if (IsEnvVarPresent("COVERAGE_TOOL_SPECIFY_MAIN_METHOD")) { std::u16string assemblyNameU16; std::u16string moduleNameU16; - ConvertToWCHAR(std::getenv("COVERAGE_TOOL_METHOD_ASSEMBLY_NAME"), assemblyNameU16); - ConvertToWCHAR(std::getenv("COVERAGE_TOOL_METHOD_MODULE_NAME"), moduleNameU16); - int mainToken = std::stoi(std::getenv("COVERAGE_TOOL_METHOD_TOKEN")); + ConvertToWCHAR(CheckEnvVarAndGet("COVERAGE_TOOL_METHOD_ASSEMBLY_NAME"), assemblyNameU16); + ConvertToWCHAR(CheckEnvVarAndGet("COVERAGE_TOOL_METHOD_MODULE_NAME"), moduleNameU16); + int mainToken = std::stoi(CheckEnvVarAndGet("COVERAGE_TOOL_METHOD_TOKEN")); size_t mainAssemblyNameLength = assemblyNameU16.size(); auto* mainAssemblyName = new WCHAR[mainAssemblyNameLength]; @@ -54,11 +74,6 @@ ProfilerState::ProfilerState(ICorProfilerInfo8 *corProfilerInfo) { (ULONG) mainModuleNameLength, mainModuleName }; - passiveResultPath = std::getenv("COVERAGE_TOOL_RESULT_NAME"); - } - - if (std::getenv("COVERAGE_TOOL_INSTRUMENT_MAIN_ONLY")) { - collectMainOnly = true; } mainFunctionId = -1; @@ -67,6 +82,26 @@ ProfilerState::ProfilerState(ICorProfilerInfo8 *corProfilerInfo) { coverageTracker = new CoverageTracker(threadTracker, threadInfo, collectMainOnly); } +void vsharp::ProfilerState::ReadTestAssemblies(const char *path) +{ + std::ifstream assembliesFile; + assembliesFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + try { + assembliesFile.open(path, std::ios_base::in); + std::string assembly; + + while (assembliesFile >> assembly) + approvedAssemblies.push_back(assembly); + + assembliesFile.close(); + } + catch (std::ifstream::failure e) { + LOG(tout << "FAILURE DURING THE READ OF ASSEMBLIES FILE! TOTAL RETRIEVED: " << approvedAssemblies.size() + << "\nCONTINUING WITH RETRIEVED DATA"); + } +} + void vsharp::ProfilerState::setEntryMain(char *assemblyName, int assemblyNameLength, char *moduleName, int moduleNameLength, int methodToken) { auto* wcharAssemblyName = new WCHAR[assemblyNameLength]; memcpy(wcharAssemblyName, assemblyName, assemblyNameLength * sizeof(WCHAR)); diff --git a/VSharp.CoverageInstrumenter/profiler/profilerState.h b/VSharp.CoverageInstrumenter/profiler/profilerState.h index 3c7c87ecf..be160641c 100644 --- a/VSharp.CoverageInstrumenter/profiler/profilerState.h +++ b/VSharp.CoverageInstrumenter/profiler/profilerState.h @@ -3,21 +3,27 @@ #include "threadTracker.h" #include "coverageTracker.h" +#include namespace vsharp { class ProfilerState { private: static const FunctionID incorrectFunctionId = 0; + + void ReadTestAssemblies(const char *path); public: ThreadTracker* threadTracker; CoverageTracker* coverageTracker; ThreadInfo* threadInfo; bool isPassiveRun = false; - bool collectMainOnly = true; + bool collectMainOnly = false; bool isFinished = false; + bool isTestExpected = false; char *passiveResultPath = nullptr; + std::vector approvedAssemblies; + MethodInfo mainMethodInfo; FunctionID mainFunctionId; From 4eb6f3bbd3b18da20fb412f4db160515ffb81851 Mon Sep 17 00:00:00 2001 From: DanielELog Date: Mon, 27 May 2024 22:55:49 +0300 Subject: [PATCH 2/6] [feat] fixes CoverageTool memory leaks - adds destructors for classes and respective calls for them - provides a certain background for implementing test project collection in the future --- .../profiler/ILRewriter.cpp | 303 +----------------- .../profiler/ILRewriter.h | 5 +- VSharp.CoverageInstrumenter/profiler/api.cpp | 10 +- VSharp.CoverageInstrumenter/profiler/api.h | 3 + .../profiler/corProfiler.cpp | 32 +- .../profiler/coverageTracker.cpp | 45 ++- .../profiler/coverageTracker.h | 9 +- .../profiler/instrumenter.cpp | 62 ++-- .../profiler/instrumenter.h | 2 +- .../profiler/memory.cpp | 1 + VSharp.CoverageInstrumenter/profiler/memory.h | 1 + .../profiler/probes.cpp | 60 ++-- VSharp.CoverageInstrumenter/profiler/probes.h | 1 + .../profiler/profilerState.cpp | 57 +++- .../profiler/profilerState.h | 12 +- .../profiler/threadStorage.h | 3 + .../profiler/threadTracker.cpp | 16 +- .../profiler/threadTracker.h | 1 + VSharp.CoverageTool/CoverageTool.fs | 115 +++++-- VSharp.Utils/CoverageDeserializer.fs | 11 +- VSharp.Utils/CoverageToolInfo.fs | 30 ++ VSharp.Utils/EnvironmentUtils.fs | 21 +- VSharp.Utils/VSharp.Utils.fsproj | 1 + 23 files changed, 368 insertions(+), 433 deletions(-) create mode 100644 VSharp.Utils/CoverageToolInfo.fs diff --git a/VSharp.CoverageInstrumenter/profiler/ILRewriter.cpp b/VSharp.CoverageInstrumenter/profiler/ILRewriter.cpp index db0accdfb..06562c1a8 100644 --- a/VSharp.CoverageInstrumenter/profiler/ILRewriter.cpp +++ b/VSharp.CoverageInstrumenter/profiler/ILRewriter.cpp @@ -24,305 +24,16 @@ typedef enum #define OPCODEFLAGS_BranchTarget 0x10 #define OPCODEFLAGS_Switch 0x20 +const char* opcodetostr(unsigned int opcode) { #define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) if (opcode == c) return s; +#define OPDEF_REAL_OPCODES_ONLY -const char* opcodetostr(unsigned int opcode) { - OPDEF(CEE_NOP, "nop", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x00, NEXT) - OPDEF(CEE_BREAK, "break", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x01, BREAK) - OPDEF(CEE_LDARG_0, "ldarg.0", Pop0, Push1, InlineNone, IMacro, 1, 0xFF, 0x02, NEXT) - OPDEF(CEE_LDARG_1, "ldarg.1", Pop0, Push1, InlineNone, IMacro, 1, 0xFF, 0x03, NEXT) - OPDEF(CEE_LDARG_2, "ldarg.2", Pop0, Push1, InlineNone, IMacro, 1, 0xFF, 0x04, NEXT) - OPDEF(CEE_LDARG_3, "ldarg.3", Pop0, Push1, InlineNone, IMacro, 1, 0xFF, 0x05, NEXT) - OPDEF(CEE_LDLOC_0, "ldloc.0", Pop0, Push1, InlineNone, IMacro, 1, 0xFF, 0x06, NEXT) - OPDEF(CEE_LDLOC_1, "ldloc.1", Pop0, Push1, InlineNone, IMacro, 1, 0xFF, 0x07, NEXT) - OPDEF(CEE_LDLOC_2, "ldloc.2", Pop0, Push1, InlineNone, IMacro, 1, 0xFF, 0x08, NEXT) - OPDEF(CEE_LDLOC_3, "ldloc.3", Pop0, Push1, InlineNone, IMacro, 1, 0xFF, 0x09, NEXT) - OPDEF(CEE_STLOC_0, "stloc.0", Pop1, Push0, InlineNone, IMacro, 1, 0xFF, 0x0A, NEXT) - OPDEF(CEE_STLOC_1, "stloc.1", Pop1, Push0, InlineNone, IMacro, 1, 0xFF, 0x0B, NEXT) - OPDEF(CEE_STLOC_2, "stloc.2", Pop1, Push0, InlineNone, IMacro, 1, 0xFF, 0x0C, NEXT) - OPDEF(CEE_STLOC_3, "stloc.3", Pop1, Push0, InlineNone, IMacro, 1, 0xFF, 0x0D, NEXT) - OPDEF(CEE_LDARG_S, "ldarg.s", Pop0, Push1, ShortInlineVar, IMacro, 1, 0xFF, 0x0E, NEXT) - OPDEF(CEE_LDARGA_S, "ldarga.s", Pop0, PushI, ShortInlineVar, IMacro, 1, 0xFF, 0x0F, NEXT) - OPDEF(CEE_STARG_S, "starg.s", Pop1, Push0, ShortInlineVar, IMacro, 1, 0xFF, 0x10, NEXT) - OPDEF(CEE_LDLOC_S, "ldloc.s", Pop0, Push1, ShortInlineVar, IMacro, 1, 0xFF, 0x11, NEXT) - OPDEF(CEE_LDLOCA_S, "ldloca.s", Pop0, PushI, ShortInlineVar, IMacro, 1, 0xFF, 0x12, NEXT) - OPDEF(CEE_STLOC_S, "stloc.s", Pop1, Push0, ShortInlineVar, IMacro, 1, 0xFF, 0x13, NEXT) - OPDEF(CEE_LDNULL, "ldnull", Pop0, PushRef, InlineNone, IPrimitive, 1, 0xFF, 0x14, NEXT) - OPDEF(CEE_LDC_I4_M1, "ldc.i4.m1", Pop0, PushI, InlineNone, IMacro, 1, 0xFF, 0x15, NEXT) - OPDEF(CEE_LDC_I4_0, "ldc.i4.0", Pop0, PushI, InlineNone, IMacro, 1, 0xFF, 0x16, NEXT) - OPDEF(CEE_LDC_I4_1, "ldc.i4.1", Pop0, PushI, InlineNone, IMacro, 1, 0xFF, 0x17, NEXT) - OPDEF(CEE_LDC_I4_2, "ldc.i4.2", Pop0, PushI, InlineNone, IMacro, 1, 0xFF, 0x18, NEXT) - OPDEF(CEE_LDC_I4_3, "ldc.i4.3", Pop0, PushI, InlineNone, IMacro, 1, 0xFF, 0x19, NEXT) - OPDEF(CEE_LDC_I4_4, "ldc.i4.4", Pop0, PushI, InlineNone, IMacro, 1, 0xFF, 0x1A, NEXT) - OPDEF(CEE_LDC_I4_5, "ldc.i4.5", Pop0, PushI, InlineNone, IMacro, 1, 0xFF, 0x1B, NEXT) - OPDEF(CEE_LDC_I4_6, "ldc.i4.6", Pop0, PushI, InlineNone, IMacro, 1, 0xFF, 0x1C, NEXT) - OPDEF(CEE_LDC_I4_7, "ldc.i4.7", Pop0, PushI, InlineNone, IMacro, 1, 0xFF, 0x1D, NEXT) - OPDEF(CEE_LDC_I4_8, "ldc.i4.8", Pop0, PushI, InlineNone, IMacro, 1, 0xFF, 0x1E, NEXT) - OPDEF(CEE_LDC_I4_S, "ldc.i4.s", Pop0, PushI, ShortInlineI, IMacro, 1, 0xFF, 0x1F, NEXT) - OPDEF(CEE_LDC_I4, "ldc.i4", Pop0, PushI, InlineI, IPrimitive, 1, 0xFF, 0x20, NEXT) - OPDEF(CEE_LDC_I8, "ldc.i8", Pop0, PushI8, InlineI8, IPrimitive, 1, 0xFF, 0x21, NEXT) - OPDEF(CEE_LDC_R4, "ldc.r4", Pop0, PushR4, ShortInlineR, IPrimitive, 1, 0xFF, 0x22, NEXT) - OPDEF(CEE_LDC_R8, "ldc.r8", Pop0, PushR8, InlineR, IPrimitive, 1, 0xFF, 0x23, NEXT) - OPDEF(CEE_UNUSED49, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x24, NEXT) - OPDEF(CEE_DUP, "dup", Pop1, Push1+Push1, InlineNone, IPrimitive, 1, 0xFF, 0x25, NEXT) - OPDEF(CEE_POP, "pop", Pop1, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x26, NEXT) - OPDEF(CEE_JMP, "jmp", Pop0, Push0, InlineMethod, IPrimitive, 1, 0xFF, 0x27, CALL) - OPDEF(CEE_CALL, "call", VarPop, VarPush, InlineMethod, IPrimitive, 1, 0xFF, 0x28, CALL) - OPDEF(CEE_CALLI, "calli", VarPop, VarPush, InlineSig, IPrimitive, 1, 0xFF, 0x29, CALL) - OPDEF(CEE_RET, "ret", VarPop, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x2A, RETURN) - OPDEF(CEE_BR_S, "br.s", Pop0, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x2B, BRANCH) - OPDEF(CEE_BRFALSE_S, "brfalse.s", PopI, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x2C, COND_BRANCH) - OPDEF(CEE_BRTRUE_S, "brtrue.s", PopI, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x2D, COND_BRANCH) - OPDEF(CEE_BEQ_S, "beq.s", Pop1+Pop1, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x2E, COND_BRANCH) - OPDEF(CEE_BGE_S, "bge.s", Pop1+Pop1, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x2F, COND_BRANCH) - OPDEF(CEE_BGT_S, "bgt.s", Pop1+Pop1, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x30, COND_BRANCH) - OPDEF(CEE_BLE_S, "ble.s", Pop1+Pop1, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x31, COND_BRANCH) - OPDEF(CEE_BLT_S, "blt.s", Pop1+Pop1, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x32, COND_BRANCH) - OPDEF(CEE_BNE_UN_S, "bne.un.s", Pop1+Pop1, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x33, COND_BRANCH) - OPDEF(CEE_BGE_UN_S, "bge.un.s", Pop1+Pop1, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x34, COND_BRANCH) - OPDEF(CEE_BGT_UN_S, "bgt.un.s", Pop1+Pop1, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x35, COND_BRANCH) - OPDEF(CEE_BLE_UN_S, "ble.un.s", Pop1+Pop1, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x36, COND_BRANCH) - OPDEF(CEE_BLT_UN_S, "blt.un.s", Pop1+Pop1, Push0, ShortInlineBrTarget,IMacro, 1, 0xFF, 0x37, COND_BRANCH) - OPDEF(CEE_BR, "br", Pop0, Push0, InlineBrTarget, IPrimitive, 1, 0xFF, 0x38, BRANCH) - OPDEF(CEE_BRFALSE, "brfalse", PopI, Push0, InlineBrTarget, IPrimitive, 1, 0xFF, 0x39, COND_BRANCH) - OPDEF(CEE_BRTRUE, "brtrue", PopI, Push0, InlineBrTarget, IPrimitive, 1, 0xFF, 0x3A, COND_BRANCH) - OPDEF(CEE_BEQ, "beq", Pop1+Pop1, Push0, InlineBrTarget, IMacro, 1, 0xFF, 0x3B, COND_BRANCH) - OPDEF(CEE_BGE, "bge", Pop1+Pop1, Push0, InlineBrTarget, IMacro, 1, 0xFF, 0x3C, COND_BRANCH) - OPDEF(CEE_BGT, "bgt", Pop1+Pop1, Push0, InlineBrTarget, IMacro, 1, 0xFF, 0x3D, COND_BRANCH) - OPDEF(CEE_BLE, "ble", Pop1+Pop1, Push0, InlineBrTarget, IMacro, 1, 0xFF, 0x3E, COND_BRANCH) - OPDEF(CEE_BLT, "blt", Pop1+Pop1, Push0, InlineBrTarget, IMacro, 1, 0xFF, 0x3F, COND_BRANCH) - OPDEF(CEE_BNE_UN, "bne.un", Pop1+Pop1, Push0, InlineBrTarget, IMacro, 1, 0xFF, 0x40, COND_BRANCH) - OPDEF(CEE_BGE_UN, "bge.un", Pop1+Pop1, Push0, InlineBrTarget, IMacro, 1, 0xFF, 0x41, COND_BRANCH) - OPDEF(CEE_BGT_UN, "bgt.un", Pop1+Pop1, Push0, InlineBrTarget, IMacro, 1, 0xFF, 0x42, COND_BRANCH) - OPDEF(CEE_BLE_UN, "ble.un", Pop1+Pop1, Push0, InlineBrTarget, IMacro, 1, 0xFF, 0x43, COND_BRANCH) - OPDEF(CEE_BLT_UN, "blt.un", Pop1+Pop1, Push0, InlineBrTarget, IMacro, 1, 0xFF, 0x44, COND_BRANCH) - OPDEF(CEE_SWITCH, "switch", PopI, Push0, InlineSwitch, IPrimitive, 1, 0xFF, 0x45, COND_BRANCH) - OPDEF(CEE_LDIND_I1, "ldind.i1", PopI, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x46, NEXT) - OPDEF(CEE_LDIND_U1, "ldind.u1", PopI, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x47, NEXT) - OPDEF(CEE_LDIND_I2, "ldind.i2", PopI, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x48, NEXT) - OPDEF(CEE_LDIND_U2, "ldind.u2", PopI, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x49, NEXT) - OPDEF(CEE_LDIND_I4, "ldind.i4", PopI, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x4A, NEXT) - OPDEF(CEE_LDIND_U4, "ldind.u4", PopI, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x4B, NEXT) - OPDEF(CEE_LDIND_I8, "ldind.i8", PopI, PushI8, InlineNone, IPrimitive, 1, 0xFF, 0x4C, NEXT) - OPDEF(CEE_LDIND_I, "ldind.i", PopI, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x4D, NEXT) - OPDEF(CEE_LDIND_R4, "ldind.r4", PopI, PushR4, InlineNone, IPrimitive, 1, 0xFF, 0x4E, NEXT) - OPDEF(CEE_LDIND_R8, "ldind.r8", PopI, PushR8, InlineNone, IPrimitive, 1, 0xFF, 0x4F, NEXT) - OPDEF(CEE_LDIND_REF, "ldind.ref", PopI, PushRef, InlineNone, IPrimitive, 1, 0xFF, 0x50, NEXT) - OPDEF(CEE_STIND_REF, "stind.ref", PopI+PopI, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x51, NEXT) - OPDEF(CEE_STIND_I1, "stind.i1", PopI+PopI, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x52, NEXT) - OPDEF(CEE_STIND_I2, "stind.i2", PopI+PopI, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x53, NEXT) - OPDEF(CEE_STIND_I4, "stind.i4", PopI+PopI, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x54, NEXT) - OPDEF(CEE_STIND_I8, "stind.i8", PopI+PopI8, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x55, NEXT) - OPDEF(CEE_STIND_R4, "stind.r4", PopI+PopR4, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x56, NEXT) - OPDEF(CEE_STIND_R8, "stind.r8", PopI+PopR8, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x57, NEXT) - OPDEF(CEE_ADD, "add", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x58, NEXT) - OPDEF(CEE_SUB, "sub", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x59, NEXT) - OPDEF(CEE_MUL, "mul", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x5A, NEXT) - OPDEF(CEE_DIV, "div", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x5B, NEXT) - OPDEF(CEE_DIV_UN, "div.un", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x5C, NEXT) - OPDEF(CEE_REM, "rem", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x5D, NEXT) - OPDEF(CEE_REM_UN, "rem.un", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x5E, NEXT) - OPDEF(CEE_AND, "and", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x5F, NEXT) - OPDEF(CEE_OR, "or", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x60, NEXT) - OPDEF(CEE_XOR, "xor", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x61, NEXT) - OPDEF(CEE_SHL, "shl", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x62, NEXT) - OPDEF(CEE_SHR, "shr", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x63, NEXT) - OPDEF(CEE_SHR_UN, "shr.un", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x64, NEXT) - OPDEF(CEE_NEG, "neg", Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x65, NEXT) - OPDEF(CEE_NOT, "not", Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0x66, NEXT) - OPDEF(CEE_CONV_I1, "conv.i1", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x67, NEXT) - OPDEF(CEE_CONV_I2, "conv.i2", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x68, NEXT) - OPDEF(CEE_CONV_I4, "conv.i4", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x69, NEXT) - OPDEF(CEE_CONV_I8, "conv.i8", Pop1, PushI8, InlineNone, IPrimitive, 1, 0xFF, 0x6A, NEXT) - OPDEF(CEE_CONV_R4, "conv.r4", Pop1, PushR4, InlineNone, IPrimitive, 1, 0xFF, 0x6B, NEXT) - OPDEF(CEE_CONV_R8, "conv.r8", Pop1, PushR8, InlineNone, IPrimitive, 1, 0xFF, 0x6C, NEXT) - OPDEF(CEE_CONV_U4, "conv.u4", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x6D, NEXT) - OPDEF(CEE_CONV_U8, "conv.u8", Pop1, PushI8, InlineNone, IPrimitive, 1, 0xFF, 0x6E, NEXT) - OPDEF(CEE_CALLVIRT, "callvirt", VarPop, VarPush, InlineMethod, IObjModel, 1, 0xFF, 0x6F, CALL) - OPDEF(CEE_CPOBJ, "cpobj", PopI+PopI, Push0, InlineType, IObjModel, 1, 0xFF, 0x70, NEXT) - OPDEF(CEE_LDOBJ, "ldobj", PopI, Push1, InlineType, IObjModel, 1, 0xFF, 0x71, NEXT) - OPDEF(CEE_LDSTR, "ldstr", Pop0, PushRef, InlineString, IObjModel, 1, 0xFF, 0x72, NEXT) - OPDEF(CEE_NEWOBJ, "newobj", VarPop, PushRef, InlineMethod, IObjModel, 1, 0xFF, 0x73, CALL) - OPDEF(CEE_CASTCLASS, "castclass", PopRef, PushRef, InlineType, IObjModel, 1, 0xFF, 0x74, NEXT) - OPDEF(CEE_ISINST, "isinst", PopRef, PushI, InlineType, IObjModel, 1, 0xFF, 0x75, NEXT) - OPDEF(CEE_CONV_R_UN, "conv.r.un", Pop1, PushR8, InlineNone, IPrimitive, 1, 0xFF, 0x76, NEXT) - OPDEF(CEE_UNUSED58, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x77, NEXT) - OPDEF(CEE_UNUSED1, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0x78, NEXT) - OPDEF(CEE_UNBOX, "unbox", PopRef, PushI, InlineType, IPrimitive, 1, 0xFF, 0x79, NEXT) - OPDEF(CEE_THROW, "throw", PopRef, Push0, InlineNone, IObjModel, 1, 0xFF, 0x7A, THROW) - OPDEF(CEE_LDFLD, "ldfld", PopRef, Push1, InlineField, IObjModel, 1, 0xFF, 0x7B, NEXT) - OPDEF(CEE_LDFLDA, "ldflda", PopRef, PushI, InlineField, IObjModel, 1, 0xFF, 0x7C, NEXT) - OPDEF(CEE_STFLD, "stfld", PopRef+Pop1, Push0, InlineField, IObjModel, 1, 0xFF, 0x7D, NEXT) - OPDEF(CEE_LDSFLD, "ldsfld", Pop0, Push1, InlineField, IObjModel, 1, 0xFF, 0x7E, NEXT) - OPDEF(CEE_LDSFLDA, "ldsflda", Pop0, PushI, InlineField, IObjModel, 1, 0xFF, 0x7F, NEXT) - OPDEF(CEE_STSFLD, "stsfld", Pop1, Push0, InlineField, IObjModel, 1, 0xFF, 0x80, NEXT) - OPDEF(CEE_STOBJ, "stobj", PopI+Pop1, Push0, InlineType, IPrimitive, 1, 0xFF, 0x81, NEXT) - OPDEF(CEE_CONV_OVF_I1_UN, "conv.ovf.i1.un", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x82, NEXT) - OPDEF(CEE_CONV_OVF_I2_UN, "conv.ovf.i2.un", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x83, NEXT) - OPDEF(CEE_CONV_OVF_I4_UN, "conv.ovf.i4.un", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x84, NEXT) - OPDEF(CEE_CONV_OVF_I8_UN, "conv.ovf.i8.un", Pop1, PushI8, InlineNone, IPrimitive, 1, 0xFF, 0x85, NEXT) - OPDEF(CEE_CONV_OVF_U1_UN, "conv.ovf.u1.un", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x86, NEXT) - OPDEF(CEE_CONV_OVF_U2_UN, "conv.ovf.u2.un", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x87, NEXT) - OPDEF(CEE_CONV_OVF_U4_UN, "conv.ovf.u4.un", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x88, NEXT) - OPDEF(CEE_CONV_OVF_U8_UN, "conv.ovf.u8.un", Pop1, PushI8, InlineNone, IPrimitive, 1, 0xFF, 0x89, NEXT) - OPDEF(CEE_CONV_OVF_I_UN, "conv.ovf.i.un", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x8A, NEXT) - OPDEF(CEE_CONV_OVF_U_UN, "conv.ovf.u.un", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0x8B, NEXT) - OPDEF(CEE_BOX, "box", Pop1, PushRef, InlineType, IPrimitive, 1, 0xFF, 0x8C, NEXT) - OPDEF(CEE_NEWARR, "newarr", PopI, PushRef, InlineType, IObjModel, 1, 0xFF, 0x8D, NEXT) - OPDEF(CEE_LDLEN, "ldlen", PopRef, PushI, InlineNone, IObjModel, 1, 0xFF, 0x8E, NEXT) - OPDEF(CEE_LDELEMA, "ldelema", PopRef+PopI, PushI, InlineType, IObjModel, 1, 0xFF, 0x8F, NEXT) - OPDEF(CEE_LDELEM_I1, "ldelem.i1", PopRef+PopI, PushI, InlineNone, IObjModel, 1, 0xFF, 0x90, NEXT) - OPDEF(CEE_LDELEM_U1, "ldelem.u1", PopRef+PopI, PushI, InlineNone, IObjModel, 1, 0xFF, 0x91, NEXT) - OPDEF(CEE_LDELEM_I2, "ldelem.i2", PopRef+PopI, PushI, InlineNone, IObjModel, 1, 0xFF, 0x92, NEXT) - OPDEF(CEE_LDELEM_U2, "ldelem.u2", PopRef+PopI, PushI, InlineNone, IObjModel, 1, 0xFF, 0x93, NEXT) - OPDEF(CEE_LDELEM_I4, "ldelem.i4", PopRef+PopI, PushI, InlineNone, IObjModel, 1, 0xFF, 0x94, NEXT) - OPDEF(CEE_LDELEM_U4, "ldelem.u4", PopRef+PopI, PushI, InlineNone, IObjModel, 1, 0xFF, 0x95, NEXT) - OPDEF(CEE_LDELEM_I8, "ldelem.i8", PopRef+PopI, PushI8, InlineNone, IObjModel, 1, 0xFF, 0x96, NEXT) - OPDEF(CEE_LDELEM_I, "ldelem.i", PopRef+PopI, PushI, InlineNone, IObjModel, 1, 0xFF, 0x97, NEXT) - OPDEF(CEE_LDELEM_R4, "ldelem.r4", PopRef+PopI, PushR4, InlineNone, IObjModel, 1, 0xFF, 0x98, NEXT) - OPDEF(CEE_LDELEM_R8, "ldelem.r8", PopRef+PopI, PushR8, InlineNone, IObjModel, 1, 0xFF, 0x99, NEXT) - OPDEF(CEE_LDELEM_REF, "ldelem.ref", PopRef+PopI, PushRef, InlineNone, IObjModel, 1, 0xFF, 0x9A, NEXT) - OPDEF(CEE_STELEM_I, "stelem.i", PopRef+PopI+PopI, Push0, InlineNone, IObjModel, 1, 0xFF, 0x9B, NEXT) - OPDEF(CEE_STELEM_I1, "stelem.i1", PopRef+PopI+PopI, Push0, InlineNone, IObjModel, 1, 0xFF, 0x9C, NEXT) - OPDEF(CEE_STELEM_I2, "stelem.i2", PopRef+PopI+PopI, Push0, InlineNone, IObjModel, 1, 0xFF, 0x9D, NEXT) - OPDEF(CEE_STELEM_I4, "stelem.i4", PopRef+PopI+PopI, Push0, InlineNone, IObjModel, 1, 0xFF, 0x9E, NEXT) - OPDEF(CEE_STELEM_I8, "stelem.i8", PopRef+PopI+PopI8, Push0, InlineNone, IObjModel, 1, 0xFF, 0x9F, NEXT) - OPDEF(CEE_STELEM_R4, "stelem.r4", PopRef+PopI+PopR4, Push0, InlineNone, IObjModel, 1, 0xFF, 0xA0, NEXT) - OPDEF(CEE_STELEM_R8, "stelem.r8", PopRef+PopI+PopR8, Push0, InlineNone, IObjModel, 1, 0xFF, 0xA1, NEXT) - OPDEF(CEE_STELEM_REF, "stelem.ref", PopRef+PopI+PopRef, Push0, InlineNone, IObjModel, 1, 0xFF, 0xA2, NEXT) - OPDEF(CEE_LDELEM, "ldelem", PopRef+PopI, Push1, InlineType, IObjModel, 1, 0xFF, 0xA3, NEXT) - OPDEF(CEE_STELEM, "stelem", PopRef+PopI+Pop1, Push0, InlineType, IObjModel, 1, 0xFF, 0xA4, NEXT) - OPDEF(CEE_UNBOX_ANY, "unbox.any", PopRef, Push1, InlineType, IObjModel, 1, 0xFF, 0xA5, NEXT) - OPDEF(CEE_UNUSED5, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xA6, NEXT) - OPDEF(CEE_UNUSED6, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xA7, NEXT) - OPDEF(CEE_UNUSED7, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xA8, NEXT) - OPDEF(CEE_UNUSED8, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xA9, NEXT) - OPDEF(CEE_UNUSED9, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xAA, NEXT) - OPDEF(CEE_UNUSED10, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xAB, NEXT) - OPDEF(CEE_UNUSED11, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xAC, NEXT) - OPDEF(CEE_UNUSED12, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xAD, NEXT) - OPDEF(CEE_UNUSED13, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xAE, NEXT) - OPDEF(CEE_UNUSED14, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xAF, NEXT) - OPDEF(CEE_UNUSED15, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xB0, NEXT) - OPDEF(CEE_UNUSED16, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xB1, NEXT) - OPDEF(CEE_UNUSED17, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xB2, NEXT) - OPDEF(CEE_CONV_OVF_I1, "conv.ovf.i1", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0xB3, NEXT) - OPDEF(CEE_CONV_OVF_U1, "conv.ovf.u1", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0xB4, NEXT) - OPDEF(CEE_CONV_OVF_I2, "conv.ovf.i2", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0xB5, NEXT) - OPDEF(CEE_CONV_OVF_U2, "conv.ovf.u2", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0xB6, NEXT) - OPDEF(CEE_CONV_OVF_I4, "conv.ovf.i4", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0xB7, NEXT) - OPDEF(CEE_CONV_OVF_U4, "conv.ovf.u4", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0xB8, NEXT) - OPDEF(CEE_CONV_OVF_I8, "conv.ovf.i8", Pop1, PushI8, InlineNone, IPrimitive, 1, 0xFF, 0xB9, NEXT) - OPDEF(CEE_CONV_OVF_U8, "conv.ovf.u8", Pop1, PushI8, InlineNone, IPrimitive, 1, 0xFF, 0xBA, NEXT) - OPDEF(CEE_UNUSED50, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xBB, NEXT) - OPDEF(CEE_UNUSED18, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xBC, NEXT) - OPDEF(CEE_UNUSED19, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xBD, NEXT) - OPDEF(CEE_UNUSED20, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xBE, NEXT) - OPDEF(CEE_UNUSED21, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xBF, NEXT) - OPDEF(CEE_UNUSED22, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xC0, NEXT) - OPDEF(CEE_UNUSED23, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xC1, NEXT) - OPDEF(CEE_REFANYVAL, "refanyval", Pop1, PushI, InlineType, IPrimitive, 1, 0xFF, 0xC2, NEXT) - OPDEF(CEE_CKFINITE, "ckfinite", Pop1, PushR8, InlineNone, IPrimitive, 1, 0xFF, 0xC3, NEXT) - OPDEF(CEE_UNUSED24, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xC4, NEXT) - OPDEF(CEE_UNUSED25, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xC5, NEXT) - OPDEF(CEE_MKREFANY, "mkrefany", PopI, Push1, InlineType, IPrimitive, 1, 0xFF, 0xC6, NEXT) - OPDEF(CEE_UNUSED59, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xC7, NEXT) - OPDEF(CEE_UNUSED60, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xC8, NEXT) - OPDEF(CEE_UNUSED61, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xC9, NEXT) - OPDEF(CEE_UNUSED62, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xCA, NEXT) - OPDEF(CEE_UNUSED63, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xCB, NEXT) - OPDEF(CEE_UNUSED64, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xCC, NEXT) - OPDEF(CEE_UNUSED65, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xCD, NEXT) - OPDEF(CEE_UNUSED66, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xCE, NEXT) - OPDEF(CEE_UNUSED67, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xCF, NEXT) - OPDEF(CEE_LDTOKEN, "ldtoken", Pop0, PushI, InlineTok, IPrimitive, 1, 0xFF, 0xD0, NEXT) - OPDEF(CEE_CONV_U2, "conv.u2", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0xD1, NEXT) - OPDEF(CEE_CONV_U1, "conv.u1", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0xD2, NEXT) - OPDEF(CEE_CONV_I, "conv.i", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0xD3, NEXT) - OPDEF(CEE_CONV_OVF_I, "conv.ovf.i", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0xD4, NEXT) - OPDEF(CEE_CONV_OVF_U, "conv.ovf.u", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0xD5, NEXT) - OPDEF(CEE_ADD_OVF, "add.ovf", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0xD6, NEXT) - OPDEF(CEE_ADD_OVF_UN, "add.ovf.un", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0xD7, NEXT) - OPDEF(CEE_MUL_OVF, "mul.ovf", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0xD8, NEXT) - OPDEF(CEE_MUL_OVF_UN, "mul.ovf.un", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0xD9, NEXT) - OPDEF(CEE_SUB_OVF, "sub.ovf", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0xDA, NEXT) - OPDEF(CEE_SUB_OVF_UN, "sub.ovf.un", Pop1+Pop1, Push1, InlineNone, IPrimitive, 1, 0xFF, 0xDB, NEXT) - OPDEF(CEE_ENDFINALLY, "endfinally", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xDC, RETURN) - OPDEF(CEE_LEAVE, "leave", Pop0, Push0, InlineBrTarget, IPrimitive, 1, 0xFF, 0xDD, BRANCH) - OPDEF(CEE_LEAVE_S, "leave.s", Pop0, Push0, ShortInlineBrTarget,IPrimitive, 1, 0xFF, 0xDE, BRANCH) - OPDEF(CEE_STIND_I, "stind.i", PopI+PopI, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xDF, NEXT) - OPDEF(CEE_CONV_U, "conv.u", Pop1, PushI, InlineNone, IPrimitive, 1, 0xFF, 0xE0, NEXT) - OPDEF(CEE_UNUSED26, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xE1, NEXT) - OPDEF(CEE_UNUSED27, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xE2, NEXT) - OPDEF(CEE_UNUSED28, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xE3, NEXT) - OPDEF(CEE_UNUSED29, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xE4, NEXT) - OPDEF(CEE_UNUSED30, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xE5, NEXT) - OPDEF(CEE_UNUSED31, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xE6, NEXT) - OPDEF(CEE_UNUSED32, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xE7, NEXT) - OPDEF(CEE_UNUSED33, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xE8, NEXT) - OPDEF(CEE_UNUSED34, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xE9, NEXT) - OPDEF(CEE_UNUSED35, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xEA, NEXT) - OPDEF(CEE_UNUSED36, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xEB, NEXT) - OPDEF(CEE_UNUSED37, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xEC, NEXT) - OPDEF(CEE_UNUSED38, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xED, NEXT) - OPDEF(CEE_UNUSED39, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xEE, NEXT) - OPDEF(CEE_UNUSED40, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xEF, NEXT) - OPDEF(CEE_UNUSED41, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xF0, NEXT) - OPDEF(CEE_UNUSED42, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xF1, NEXT) - OPDEF(CEE_UNUSED43, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xF2, NEXT) - OPDEF(CEE_UNUSED44, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xF3, NEXT) - OPDEF(CEE_UNUSED45, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xF4, NEXT) - OPDEF(CEE_UNUSED46, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xF5, NEXT) - OPDEF(CEE_UNUSED47, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xF6, NEXT) - OPDEF(CEE_UNUSED48, "unused", Pop0, Push0, InlineNone, IPrimitive, 1, 0xFF, 0xF7, NEXT) - OPDEF(CEE_PREFIX7, "prefix7", Pop0, Push0, InlineNone, IInternal, 1, 0xFF, 0xF8, META) - OPDEF(CEE_PREFIX6, "prefix6", Pop0, Push0, InlineNone, IInternal, 1, 0xFF, 0xF9, META) - OPDEF(CEE_PREFIX5, "prefix5", Pop0, Push0, InlineNone, IInternal, 1, 0xFF, 0xFA, META) - OPDEF(CEE_PREFIX4, "prefix4", Pop0, Push0, InlineNone, IInternal, 1, 0xFF, 0xFB, META) - OPDEF(CEE_PREFIX3, "prefix3", Pop0, Push0, InlineNone, IInternal, 1, 0xFF, 0xFC, META) - OPDEF(CEE_PREFIX2, "prefix2", Pop0, Push0, InlineNone, IInternal, 1, 0xFF, 0xFD, META) - OPDEF(CEE_PREFIX1, "prefix1", Pop0, Push0, InlineNone, IInternal, 1, 0xFF, 0xFE, META) - OPDEF(CEE_PREFIXREF, "prefixref", Pop0, Push0, InlineNone, IInternal, 1, 0xFF, 0xFF, META) - - OPDEF(CEE_ARGLIST, "arglist", Pop0, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x00, NEXT) - OPDEF(CEE_CEQ, "ceq", Pop1+Pop1, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x01, NEXT) - OPDEF(CEE_CGT, "cgt", Pop1+Pop1, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x02, NEXT) - OPDEF(CEE_CGT_UN, "cgt.un", Pop1+Pop1, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x03, NEXT) - OPDEF(CEE_CLT, "clt", Pop1+Pop1, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x04, NEXT) - OPDEF(CEE_CLT_UN, "clt.un", Pop1+Pop1, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x05, NEXT) - OPDEF(CEE_LDFTN, "ldftn", Pop0, PushI, InlineMethod, IPrimitive, 2, 0xFE, 0x06, NEXT) - OPDEF(CEE_LDVIRTFTN, "ldvirtftn", PopRef, PushI, InlineMethod, IPrimitive, 2, 0xFE, 0x07, NEXT) - OPDEF(CEE_UNUSED56, "unused", Pop0, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x08, NEXT) - OPDEF(CEE_LDARG, "ldarg", Pop0, Push1, InlineVar, IPrimitive, 2, 0xFE, 0x09, NEXT) - OPDEF(CEE_LDARGA, "ldarga", Pop0, PushI, InlineVar, IPrimitive, 2, 0xFE, 0x0A, NEXT) - OPDEF(CEE_STARG, "starg", Pop1, Push0, InlineVar, IPrimitive, 2, 0xFE, 0x0B, NEXT) - OPDEF(CEE_LDLOC, "ldloc", Pop0, Push1, InlineVar, IPrimitive, 2, 0xFE, 0x0C, NEXT) - OPDEF(CEE_LDLOCA, "ldloca", Pop0, PushI, InlineVar, IPrimitive, 2, 0xFE, 0x0D, NEXT) - OPDEF(CEE_STLOC, "stloc", Pop1, Push0, InlineVar, IPrimitive, 2, 0xFE, 0x0E, NEXT) - OPDEF(CEE_LOCALLOC, "localloc", PopI, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x0F, NEXT) - OPDEF(CEE_UNUSED57, "unused", Pop0, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x10, NEXT) - OPDEF(CEE_ENDFILTER, "endfilter", PopI, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x11, RETURN) - OPDEF(CEE_UNALIGNED, "unaligned.", Pop0, Push0, ShortInlineI, IPrefix, 2, 0xFE, 0x12, META) - OPDEF(CEE_VOLATILE, "volatile.", Pop0, Push0, InlineNone, IPrefix, 2, 0xFE, 0x13, META) - OPDEF(CEE_TAILCALL, "tail.", Pop0, Push0, InlineNone, IPrefix, 2, 0xFE, 0x14, META) - OPDEF(CEE_INITOBJ, "initobj", PopI, Push0, InlineType, IObjModel, 2, 0xFE, 0x15, NEXT) - OPDEF(CEE_CONSTRAINED, "constrained.", Pop0, Push0, InlineType, IPrefix, 2, 0xFE, 0x16, META) - OPDEF(CEE_CPBLK, "cpblk", PopI+PopI+PopI, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x17, NEXT) - OPDEF(CEE_INITBLK, "initblk", PopI+PopI+PopI, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x18, NEXT) - OPDEF(CEE_UNUSED69, "unused", Pop0, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x19, NEXT) - OPDEF(CEE_RETHROW, "rethrow", Pop0, Push0, InlineNone, IObjModel, 2, 0xFE, 0x1A, THROW) - OPDEF(CEE_UNUSED51, "unused", Pop0, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x1B, NEXT) - OPDEF(CEE_SIZEOF, "sizeof", Pop0, PushI, InlineType, IPrimitive, 2, 0xFE, 0x1C, NEXT) - OPDEF(CEE_REFANYTYPE, "refanytype", Pop1, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x1D, NEXT) - OPDEF(CEE_READONLY, "readonly.", Pop0, Push0, InlineNone, IPrefix, 2, 0xFE, 0x1E, META) - OPDEF(CEE_UNUSED53, "unused", Pop0, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x1F, NEXT) - OPDEF(CEE_UNUSED54, "unused", Pop0, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x20, NEXT) - OPDEF(CEE_UNUSED55, "unused", Pop0, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x21, NEXT) - OPDEF(CEE_UNUSED70, "unused", Pop0, Push0, InlineNone, IPrimitive, 2, 0xFE, 0x22, NEXT) +#include "opcode.def" return "unknwn"; -} +#undef OPDEF_REAL_OPCODES_ONLY #undef OPDEF +} static const BYTE s_OpCodeFlags[] = { @@ -948,6 +659,8 @@ LPBYTE ILRewriter::AllocateILMemory(unsigned size) // Else, this is "classic-style" instrumentation on first JIT, and // need to use the CLR's IL allocator + if (m_pICorProfilerInfo == nullptr) return NULL; + if (FAILED(m_pICorProfilerInfo->GetILFunctionBodyAllocator(m_moduleId, &m_pIMethodMalloc))) return NULL; @@ -1395,7 +1108,7 @@ HRESULT RewriteIL( if (pilr->m_pEH != nullptr) { for (int i = 0; i < pilr->m_nEH; i++) { - addPriorityProbe.push_back({ pilr->m_pEH[i].m_pHandlerBegin, nullptr, covProb->Throw, PIBeforeInstr }); + addPriorityProbe.push_back({ pilr->m_pEH[i].m_pHandlerBegin, nullptr, covProb->Coverage, PIBeforeInstr }); } } diff --git a/VSharp.CoverageInstrumenter/profiler/ILRewriter.h b/VSharp.CoverageInstrumenter/profiler/ILRewriter.h index b1c226861..f44b9d1c2 100644 --- a/VSharp.CoverageInstrumenter/profiler/ILRewriter.h +++ b/VSharp.CoverageInstrumenter/profiler/ILRewriter.h @@ -6,12 +6,13 @@ #include "corprof.h" #include #include "probes.h" +#include "memory.h" #undef IfFailRet -#define IfFailRet(EXPR) do { HRESULT hr = (EXPR); if(FAILED(hr)) { return (hr); } } while (0) +#define IfFailRet(EXPR) do { if (std::atomic_load(&vsharp::shutdownInOrder)) { return S_OK; } HRESULT hr = (EXPR); if(FAILED(hr)) { return (hr); } } while (0) #undef IfNullRet -#define IfNullRet(EXPR) do { if ((EXPR) == NULL) return E_OUTOFMEMORY; } while (0) +#define IfNullRet(EXPR) do { if (std::atomic_load(&vsharp::shutdownInOrder)) { return S_OK; } if ((EXPR) == NULL) return E_OUTOFMEMORY; } while (0) struct ILInstr { diff --git a/VSharp.CoverageInstrumenter/profiler/api.cpp b/VSharp.CoverageInstrumenter/profiler/api.cpp index 28ec306ea..eb07183df 100644 --- a/VSharp.CoverageInstrumenter/profiler/api.cpp +++ b/VSharp.CoverageInstrumenter/profiler/api.cpp @@ -8,6 +8,7 @@ #include using namespace vsharp; + extern "C" void SetEntryMain(char* assemblyName, int assemblyNameLength, char* moduleName, int moduleNameLength, int methodToken) { profilerState->setEntryMain(assemblyName, assemblyNameLength, moduleName, moduleNameLength, methodToken); LOG(tout << "received entry main" << std::endl); @@ -18,9 +19,9 @@ extern "C" void GetHistory(UINT_PTR size, UINT_PTR bytes) { std::atomic_fetch_add(&shutdownBlockingRequestsCount, 1); size_t tmpSize; - auto tmpBytes = profilerState->coverageTracker->serializeCoverageReport(&tmpSize); + historyBuffer = profilerState->coverageTracker->serializeCoverageReport(&tmpSize); *(ULONG*)size = tmpSize; - *(char**)bytes = tmpBytes; + *(char**)bytes = historyBuffer; profilerState->coverageTracker->clear(); profilerState->threadTracker->clear(); @@ -29,6 +30,11 @@ extern "C" void GetHistory(UINT_PTR size, UINT_PTR bytes) { LOG(tout << "GetHistory request handled!"); } +extern "C" void ClearHistory() { + delete historyBuffer; + historyBuffer = nullptr; +} + extern "C" void SetCurrentThreadId(int mapId) { LOG(tout << "Map current thread to: " << mapId); vsharp::profilerState->threadTracker->mapCurrentThread(mapId); diff --git a/VSharp.CoverageInstrumenter/profiler/api.h b/VSharp.CoverageInstrumenter/profiler/api.h index def5d6212..5064839c1 100644 --- a/VSharp.CoverageInstrumenter/profiler/api.h +++ b/VSharp.CoverageInstrumenter/profiler/api.h @@ -12,5 +12,8 @@ extern "C" IMAGEHANDLER_API void SetEntryMain(char* assemblyName, int assemblyNameLength, char* moduleName, int moduleNameLength, int methodToken); extern "C" IMAGEHANDLER_API void GetHistory(UINT_PTR size, UINT_PTR bytes); extern "C" IMAGEHANDLER_API void SetCurrentThreadId(int mapId); +extern "C" IMAGEHANDLER_API void ClearHistory(); + +static char* historyBuffer = nullptr; #endif //VSHARP_COVERAGEINSTRUMENTER_API_H diff --git a/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp b/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp index ddadcb3b0..1109f1edb 100644 --- a/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp +++ b/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp @@ -31,6 +31,9 @@ CorProfiler::~CorProfiler() HRESULT STDMETHODCALLTYPE CorProfiler::Initialize(IUnknown *pICorProfilerInfoUnk) { + setbuf(stdout, NULL); + + printf("PROFILER INITIALIZATION\n"); const char* waitDebuggerAttached = std::getenv("COVERAGE_TOOL_WAIT_DEBUGGER_ATTACHED"); volatile int done = waitDebuggerAttached == nullptr ? 1 : 0; while (!done) OS::sleepSeconds(1); @@ -53,7 +56,7 @@ HRESULT STDMETHODCALLTYPE CorProfiler::Initialize(IUnknown *pICorProfilerInfoUnk // TMP Windows fix #undef IfFailRet - #define IfFailRet(EXPR) do { HRESULT hr = (EXPR); if(FAILED(hr)) { return (hr); } } while (0) + #define IfFailRet(EXPR) do { if (std::atomic_load(&shutdownInOrder)) return S_OK; HRESULT hr = (EXPR); if(FAILED(hr)) { return (hr); } } while (0) IfFailRet(this->corProfilerInfo->SetEventMask(eventMask)); profilerState = new ProfilerState((ICorProfilerInfo8*)this); @@ -64,27 +67,36 @@ HRESULT STDMETHODCALLTYPE CorProfiler::Initialize(IUnknown *pICorProfilerInfoUnk HRESULT STDMETHODCALLTYPE CorProfiler::Shutdown() { - profilerState->isFinished = true; + printf("PROFILER SHUTDOWN\n"); + std::atomic_store(&shutdownInOrder, true); // waiting until all current requests are resolved while (std::atomic_load(&shutdownBlockingRequestsCount) > 0) {} LOG(tout << "SHUTDOWN"); if (profilerState->isPassiveRun) { + printf("serializing information\n"); size_t tmpSize; - auto tmpBytes = profilerState->coverageTracker->serializeCoverageReport(&tmpSize);; + auto tmpBytes = profilerState->coverageTracker->serializeCoverageReport(&tmpSize); + + printf("saving to %s\n", profilerState->passiveResultPath); std::ofstream fout; fout.open(profilerState->passiveResultPath, std::ios::out|std::ios::binary); - fout.write(tmpBytes, static_cast(tmpSize)); + if (!fout.write(tmpBytes, static_cast(tmpSize))) + printf("failure while saving the file\n"); fout.close(); + + delete tmpBytes; } #ifdef _LOGGING close_log(); #endif + delete profilerState; + if (this->corProfilerInfo != nullptr) { this->corProfilerInfo->Release(); @@ -214,7 +226,7 @@ HRESULT STDMETHODCALLTYPE CorProfiler::FunctionUnloadStarted(FunctionID function HRESULT STDMETHODCALLTYPE CorProfiler::JITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock) { // the process was finished, ignoring all firther requests - if (profilerState->isFinished) return S_OK; + if (std::atomic_load(&shutdownInOrder)) return S_OK; std::atomic_fetch_add(&shutdownBlockingRequestsCount, 1); @@ -421,6 +433,7 @@ HRESULT STDMETHODCALLTYPE CorProfiler::RootReferences(ULONG cRootRefs, ObjectID HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionThrown(ObjectID thrownObjectId) { + if (std::atomic_load(&shutdownInOrder)) return S_OK; auto exceptionName = GetObjectTypeName(thrownObjectId); LOG( if(profilerState->threadTracker->hasMapping()) { @@ -454,7 +467,7 @@ HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionSearchFunctionLeave() HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionSearchFilterEnter(FunctionID functionId) { - if (profilerState->isFinished || !profilerState->threadTracker->isCurrentThreadTracked()) return S_OK; + if (std::atomic_load(&shutdownInOrder) || !profilerState->threadTracker->isCurrentThreadTracked()) return S_OK; LOG(tout << "EXCEPTION Search filter enter"); profilerState->threadTracker->filterEnter(); UNUSED(functionId); @@ -463,7 +476,7 @@ HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionSearchFilterEnter(FunctionID fun HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionSearchFilterLeave() { - if (profilerState->isFinished || !profilerState->threadTracker->isCurrentThreadTracked()) return S_OK; + if (std::atomic_load(&shutdownInOrder) || !profilerState->threadTracker->isCurrentThreadTracked()) return S_OK; LOG(tout << "EXCEPTION Search filter leave"); profilerState->threadTracker->filterLeave(); return S_OK; @@ -502,7 +515,8 @@ HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionUnwindFunctionLeave() { LOG(tout << "EXCEPTION UNWIND FUNCTION LEAVE"); // the process was finished, ignoring all further requests - if (profilerState->isFinished || !profilerState->threadTracker->isCurrentThreadTracked()) return S_OK; + if (std::atomic_load(&shutdownInOrder) + || !profilerState->threadTracker->isCurrentThreadTracked()) return S_OK; profilerState->threadTracker->unwindFunctionLeave(); return S_OK; } @@ -719,6 +733,7 @@ HRESULT STDMETHODCALLTYPE CorProfiler::DynamicMethodJITCompilationFinished(Funct } std::string CorProfiler::GetObjectTypeName(ObjectID objectId) { + if (std::atomic_load(&shutdownInOrder)) return ""; ClassID classId; corProfilerInfo->GetClassFromObject(objectId, &classId); @@ -739,6 +754,7 @@ std::string CorProfiler::GetObjectTypeName(ObjectID objectId) { } std::string CorProfiler::GetFunctionName(FunctionID functionId) { + if (std::atomic_load(&shutdownInOrder)) return ""; ClassID classId; ModuleID moduleId; mdToken token; diff --git a/VSharp.CoverageInstrumenter/profiler/coverageTracker.cpp b/VSharp.CoverageInstrumenter/profiler/coverageTracker.cpp index e9ae75a26..790dc4f55 100644 --- a/VSharp.CoverageInstrumenter/profiler/coverageTracker.cpp +++ b/VSharp.CoverageInstrumenter/profiler/coverageTracker.cpp @@ -13,6 +13,11 @@ void MethodInfo::serialize(std::vector& buffer) const { serializePrimitive(moduleNameLength, buffer); serializePrimitiveArray(moduleName, moduleNameLength, buffer); } + +void MethodInfo::Dispose() { + delete moduleName; + delete assemblyName; +} //endregion //region CoverageRecord @@ -21,27 +26,31 @@ void CoverageRecord::serialize(std::vector& buffer) const { serializePrimitive(event, buffer); serializePrimitive(methodId, buffer); serializePrimitive(thread, buffer); + serializePrimitive(timestamp, buffer); } //endregion +long long GetMicrosecondTime() { + using namespace std::chrono; + return duration_cast(system_clock::now().time_since_epoch()).count(); +} + //region CoverageHistory CoverageHistory::CoverageHistory(OFFSET offset, int methodId) { auto insertResult = visitedMethods.insert(methodId); - LOG(if (insertResult.second) { - tout << "Visit method: " << methodId; - }); - auto record = new CoverageRecord({offset, EnterMain, profilerState->threadInfo->getCurrentThread(), methodId}); + if (insertResult.second) { + LOG(tout << "Visit method: " << methodId); + } + auto record = new CoverageRecord({offset, EnterMain, profilerState->threadInfo->getCurrentThread(), methodId, GetMicrosecondTime()}); records.push_back(record); } void CoverageHistory::addCoverage(OFFSET offset, CoverageEvent event, int methodId) { auto insertResult = visitedMethods.insert(methodId); - LOG( if (insertResult.second) { - tout << "Visit method: " << methodId; + LOG(tout << "Visit method: " << methodId); } - ); - auto record = new CoverageRecord({offset, event, profilerState->threadInfo->getCurrentThread(), methodId}); + auto record = new CoverageRecord({offset, event, profilerState->threadInfo->getCurrentThread(), methodId, GetMicrosecondTime()}); records.push_back(record); } @@ -54,6 +63,8 @@ void CoverageHistory::serialize(std::vector& buffer) const { } CoverageHistory::~CoverageHistory() { + for (auto r : records) + delete r; records.clear(); } //endregion @@ -99,6 +110,7 @@ void CoverageTracker::invocationFinished() { coverage->serialize(buffer); } + delete coverage; trackedCoverage->remove(); serializedCoverageMutex.lock(); @@ -110,6 +122,7 @@ void CoverageTracker::invocationFinished() { char* CoverageTracker::serializeCoverageReport(size_t* size) { collectedMethodsMutex.lock(); serializedCoverageMutex.lock(); + printf("got locks, converting info\n"); auto buffer = std::vector(); auto methodsToSerialize = std::vector>(); @@ -126,6 +139,7 @@ char* CoverageTracker::serializeCoverageReport(size_t* size) { serializePrimitive(el.first, buffer); el.second.serialize(buffer); } + printf("converted methods: %lld\n", buffer.size()); auto threadMapping = profilerState->threadTracker->getMapping(); for (auto mapping: threadMapping) { @@ -148,6 +162,10 @@ char* CoverageTracker::serializeCoverageReport(size_t* size) { serializePrimitiveArray(&serializedCoverage[i][0], serializedCoverage[i].size(), buffer); } + printf("converted reports: %lld\n", buffer.size()); + + for (auto cov : trackedCoverage->items()) + delete cov.second; trackedCoverage->clear(); serializedCoverage.clear(); methodsToSerialize.clear(); @@ -155,9 +173,14 @@ char* CoverageTracker::serializeCoverageReport(size_t* size) { serializedCoverageMutex.unlock(); collectedMethodsMutex.unlock(); + printf("cleared and unlocked data\n"); + *size = buffer.size(); char* array = new char[*size]; + printf("successfully allocated\n"); std::memcpy(array, &buffer[0], *size); + + printf("total bytes: %lld", *size); return array; } @@ -179,9 +202,15 @@ void CoverageTracker::clear() { CoverageTracker::~CoverageTracker(){ clear(); + for (int i = 0; i < collectedMethods.size(); i++) + collectedMethods[i].Dispose(); + collectedMethods.clear(); + delete trackedCoverage; } void CoverageTracker::invocationAborted() { + auto abortedCov = trackedCoverage->load(); + delete abortedCov; trackedCoverage->update([](CoverageHistory *cov) { return (CoverageHistory*) nullptr; }); diff --git a/VSharp.CoverageInstrumenter/profiler/coverageTracker.h b/VSharp.CoverageInstrumenter/profiler/coverageTracker.h index 29d6b2d26..e1ef99567 100644 --- a/VSharp.CoverageInstrumenter/profiler/coverageTracker.h +++ b/VSharp.CoverageInstrumenter/profiler/coverageTracker.h @@ -19,7 +19,8 @@ enum CoverageEvent { Call, Tailcall, TrackCoverage, - StsfldHit + StsfldHit, + ThrowLeave, }; struct MethodInfo { @@ -31,6 +32,9 @@ struct MethodInfo { std::string methodName; void serialize(std::vector& buffer) const; + + // frees previously allocated resources for it; the object is not supposed to be used afterwards + void Dispose(); }; struct CoverageRecord { @@ -38,6 +42,7 @@ struct CoverageRecord { CoverageEvent event; ThreadID thread; int methodId; + long long timestamp; void serialize(std::vector& buffer) const; }; @@ -63,9 +68,9 @@ class CoverageTracker { ThreadStorage* trackedCoverage; ThreadTracker* threadTracker; std::mutex serializedCoverageMutex; - std::vector> serializedCoverage; std::vector serializedCoverageThreadIds; public: + std::vector> serializedCoverage; std::mutex collectedMethodsMutex; std::vector collectedMethods; explicit CoverageTracker(ThreadTracker* threadTracker, ThreadInfo* threadInfo, bool collectMainOnly); diff --git a/VSharp.CoverageInstrumenter/profiler/instrumenter.cpp b/VSharp.CoverageInstrumenter/profiler/instrumenter.cpp index e023da6e5..71cc947a9 100644 --- a/VSharp.CoverageInstrumenter/profiler/instrumenter.cpp +++ b/VSharp.CoverageInstrumenter/profiler/instrumenter.cpp @@ -27,19 +27,22 @@ HRESULT initTokens(const CComPtr &metadataEmit, std::vectorFinalize_Call->setSig(signatureToken); + covProb->Finalize_Call ->setSig(signatureToken); + SIG_DEF(0x02, ELEMENT_TYPE_VOID, ELEMENT_TYPE_OFFSET, ELEMENT_TYPE_I4) - covProb->Branch->setSig(signatureToken); - covProb->Call->setSig(signatureToken); - covProb->Leave->setSig(signatureToken); - covProb->Throw->setSig(signatureToken); - covProb->Stsfld->setSig(signatureToken); - covProb->Coverage->setSig(signatureToken); - covProb->Tailcall->setSig(signatureToken); - covProb->LeaveMain->setSig(signatureToken); + covProb->Branch ->setSig(signatureToken); + covProb->Call ->setSig(signatureToken); + covProb->Leave ->setSig(signatureToken); + covProb->Throw ->setSig(signatureToken); + covProb->Stsfld ->setSig(signatureToken); + covProb->Coverage ->setSig(signatureToken); + covProb->Tailcall ->setSig(signatureToken); + covProb->LeaveMain ->setSig(signatureToken); + SIG_DEF(0x03, ELEMENT_TYPE_VOID, ELEMENT_TYPE_OFFSET, ELEMENT_TYPE_I4, ELEMENT_TYPE_I4) - covProb->EnterMain->setSig(signatureToken); - covProb->Enter->setSig(signatureToken); + covProb->EnterMain ->setSig(signatureToken); + covProb->Enter ->setSig(signatureToken); + return S_OK; } @@ -67,9 +70,14 @@ bool vsharp::IsMain(const WCHAR *moduleName, int moduleSize, mdMethodDef method) return true; } -bool isApprovedAssembly(const WCHAR *assemblyName, int assemblyNameLength) { - for (auto approved : profilerState->approvedAssemblies) { - if (assemblyNameLength - 1 == approved.length() && std::string(assemblyName, assemblyName + assemblyNameLength - 1) == approved) +bool doesContainAssembly(const WCHAR *assemblyName, int assemblyNameLength, std::vector &assemblyList) { + auto asmName = std::string(assemblyName, assemblyName + assemblyNameLength - 1); + + // safety failcheck as instrumenting System.Private results in an incorrect exit from the profiler + if (asmName.find("System.Private") == 0) return false; + + for (auto approved : assemblyList) { + if (assemblyNameLength - 1 == approved.length() && asmName == approved) return true; } return false; @@ -78,10 +86,10 @@ bool isApprovedAssembly(const WCHAR *assemblyName, int assemblyNameLength) { // TODO: simplify the condition by adding two main modes as coverage tool config bool vsharp::InstrumentationIsNeeded(const WCHAR* assemblyName, int assemblySize, const WCHAR *moduleName, int moduleSize, mdMethodDef method) { return IsMain(moduleName, moduleSize, method) || (!profilerState->isTestExpected && !profilerState->collectMainOnly) - || (profilerState->isTestExpected && isApprovedAssembly(moduleName, moduleSize)); + || (profilerState->isTestExpected && doesContainAssembly(assemblyName, assemblySize, profilerState->approvedAssemblies)); } -HRESULT Instrumenter::doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength, bool isTestRun) { +HRESULT Instrumenter::doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength) { HRESULT hr; CComPtr metadataImport; CComPtr metadataEmit; @@ -104,7 +112,7 @@ HRESULT Instrumenter::doInstrumentation(ModuleID oldModuleId, size_t methodId, c m_jittedToken, methodId, IsMain(moduleName, moduleNameLength, m_jittedToken), - isTestRun + profilerState->isTestExpected ); return S_OK; @@ -130,16 +138,11 @@ HRESULT Instrumenter::instrument(FunctionID functionId, std::string methodName) WCHAR *assemblyName = new WCHAR[assemblyNameLength]; IfFailRet(m_profilerInfo.GetAssemblyInfo(assembly, assemblyNameLength, &assemblyNameLength, assemblyName, &appDomainId, &startModuleId)); - // checking if this method was rewritten before - if (instrumentedMethods.find({ m_jittedToken, newModuleId }) != instrumentedMethods.end()) { - // LOG(tout << "repeated JIT of " << m_jittedToken << "! skipped" << std::endl); - return S_OK; - } - - LOG(tout << "JIT of " << std::string(assemblyName, assemblyName + assemblyNameLength - 1) << '.' << methodName << std::endl); - - // skipping non-main methods - if (!InstrumentationIsNeeded(assemblyName, assemblyNameLength, moduleName, moduleNameLength, m_jittedToken)) { + // checking if this method was rewritten before and if it is in need of rewriting + if (instrumentedMethods.find({ m_jittedToken, newModuleId }) != instrumentedMethods.end() + || !InstrumentationIsNeeded(assemblyName, assemblyNameLength, moduleName, moduleNameLength, m_jittedToken)) { + delete[] moduleName; + delete[] assemblyName; return S_OK; } @@ -158,9 +161,12 @@ HRESULT Instrumenter::instrument(FunctionID functionId, std::string methodName) ); instrumentedMethods.insert({m_jittedToken, newModuleId}); mutex.unlock(); + + profilerState->funcIdToMethodId[functionId] = currentMethodId; + ModuleID oldModuleId = m_moduleId; m_moduleId = newModuleId; - hr = doInstrumentation(oldModuleId, currentMethodId, moduleName, moduleNameLength, profilerState->isTestExpected); + hr = doInstrumentation(oldModuleId, currentMethodId, moduleName, moduleNameLength); return hr; } diff --git a/VSharp.CoverageInstrumenter/profiler/instrumenter.h b/VSharp.CoverageInstrumenter/profiler/instrumenter.h index 645126e26..9d203fc31 100644 --- a/VSharp.CoverageInstrumenter/profiler/instrumenter.h +++ b/VSharp.CoverageInstrumenter/profiler/instrumenter.h @@ -19,7 +19,7 @@ class Instrumenter { char *m_signatureTokens; unsigned m_signatureTokensLength; std::mutex mutex; - HRESULT doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength, bool isTestRun); + HRESULT doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength); public: explicit Instrumenter(ICorProfilerInfo8 &profilerInfo); diff --git a/VSharp.CoverageInstrumenter/profiler/memory.cpp b/VSharp.CoverageInstrumenter/profiler/memory.cpp index c750e8b26..90b1b6f5e 100644 --- a/VSharp.CoverageInstrumenter/profiler/memory.cpp +++ b/VSharp.CoverageInstrumenter/profiler/memory.cpp @@ -3,3 +3,4 @@ using namespace vsharp; std::atomic vsharp::shutdownBlockingRequestsCount {0}; +std::atomic_bool vsharp::shutdownInOrder{ false }; diff --git a/VSharp.CoverageInstrumenter/profiler/memory.h b/VSharp.CoverageInstrumenter/profiler/memory.h index 1a196b598..f1364d71f 100644 --- a/VSharp.CoverageInstrumenter/profiler/memory.h +++ b/VSharp.CoverageInstrumenter/profiler/memory.h @@ -22,6 +22,7 @@ namespace vsharp { #define OFFSET UINT32 extern std::atomic shutdownBlockingRequestsCount; +extern std::atomic_bool shutdownInOrder; } #endif // MEMORY_H_ diff --git a/VSharp.CoverageInstrumenter/profiler/probes.cpp b/VSharp.CoverageInstrumenter/profiler/probes.cpp index 455db7721..62fd7a7f0 100644 --- a/VSharp.CoverageInstrumenter/profiler/probes.cpp +++ b/VSharp.CoverageInstrumenter/profiler/probes.cpp @@ -49,60 +49,63 @@ void vsharp::InitializeProbes() { LOG(tout << "probes initialized" << std::endl); } +void vsharp::DestroyProbes() { + auto covProbes = vsharp::getProbes(); + delete covProbes->Coverage; + delete covProbes->Branch; + delete covProbes->Enter; + delete covProbes->EnterMain; + delete covProbes->Leave; + delete covProbes->LeaveMain; + delete covProbes->Finalize_Call; + delete covProbes->Call; + delete covProbes->Tailcall; + delete covProbes->Stsfld; + delete covProbes->Throw; + LOG(tout << "probes destroyed" << std::endl); +} + CoverageProbes vsharp::coverageProbes; //region Probes declarations void vsharp::Track_Coverage(OFFSET offset, int methodId) { if (!profilerState->threadTracker->isCurrentThreadTracked()) return; - LOG(tout << "Track_Coverage: method = " << methodId << ", offset = " << HEX(offset)); + profilerState->printMethod("Track_Coverage", methodId); profilerState->coverageTracker->addCoverage(offset, TrackCoverage, methodId); } void vsharp::Track_Stsfld(OFFSET offset, int methodId) { if (!profilerState->threadTracker->isCurrentThreadTracked()) return; - LOG(tout << "Track_Stsfld: method = " << methodId << ", offset = " << HEX(offset)); + profilerState->printMethod("Track_Stsfld", methodId); profilerState->coverageTracker->addCoverage(offset, StsfldHit, methodId); } void vsharp::Branch(OFFSET offset, int methodId) { if (!profilerState->threadTracker->isCurrentThreadTracked()) return; - LOG(tout << "Branch: method = " << methodId << ", offset = " << HEX(offset)); + profilerState->printMethod("Branch", methodId); profilerState->coverageTracker->addCoverage(offset, BranchHit, methodId); } void vsharp::Track_Call(OFFSET offset, int methodId) { if (!profilerState->threadTracker->isCurrentThreadTracked()) return; - LOG(tout << "Track_Call: method = " << methodId << ", offset = " << HEX(offset)); + profilerState->printMethod("Track_Call", methodId); profilerState->coverageTracker->addCoverage(offset, Call, methodId); } void vsharp::Track_Tailcall(OFFSET offset, int methodId) { if (!profilerState->threadTracker->isCurrentThreadTracked()) return; - LOG(tout << "Track_Tailcall: method = " << methodId << ", offset = " << HEX(offset)); + profilerState->printMethod("Track_Tailcall", methodId); // popping frame before tailcall execution profilerState->threadTracker->stackBalanceDown(); profilerState->coverageTracker->addCoverage(offset, Tailcall, methodId); } -void printMethod(std::string message, int methodId) { - auto tracker = profilerState->coverageTracker; - LOG( - tracker->collectedMethodsMutex.lock(); - auto method = tracker->collectedMethods[methodId]; - auto wl = method.assemblyNameLength; - auto ws = method.assemblyName; - tout << message << ' ' << std::string(ws, ws + wl - 1) << '.' << method.methodName << std::endl; - tracker->collectedMethodsMutex.unlock(); - ); -} - void vsharp::Track_Enter(OFFSET offset, int methodId, int isSpontaneous) { if (!profilerState->threadTracker->isCurrentThreadTracked()) return; - printMethod("Entered", methodId); + profilerState->printMethod("Entered", methodId); if (profilerState->threadTracker->isPossibleStackOverflow()) { LOG(tout << "Possible stack overflow: " << methodId); } - // LOG(tout << "Track_Enter: " << methodId); if (!profilerState->coverageTracker->isCollectMainOnly()) profilerState->coverageTracker->addCoverage(offset, Enter, methodId); profilerState->threadTracker->stackBalanceUp(); @@ -111,13 +114,12 @@ void vsharp::Track_Enter(OFFSET offset, int methodId, int isSpontaneous) { void vsharp::Track_EnterMain(OFFSET offset, int methodId, int isSpontaneous) { if (profilerState->threadTracker->isCurrentThreadTracked()) { // Recursive enter - LOG(tout << "(recursive) Track_EnterMain: " << methodId); + profilerState->printMethod("Entered main (recursive)", methodId); profilerState->threadTracker->stackBalanceUp(); profilerState->coverageTracker->addCoverage(offset, Enter, methodId); return; } - printMethod("Entered Main", methodId); - // LOG(tout << "Track_EnterMain: " << methodId); + profilerState->printMethod("Entered Main", methodId); profilerState->threadTracker->trackCurrentThread(); profilerState->threadTracker->stackBalanceUp(); profilerState->coverageTracker->addCoverage(offset, EnterMain, methodId); @@ -125,7 +127,7 @@ void vsharp::Track_EnterMain(OFFSET offset, int methodId, int isSpontaneous) { void vsharp::Track_Leave(OFFSET offset, int methodId) { if (!profilerState->threadTracker->isCurrentThreadTracked()) return; - LOG(tout << "Track_Leave: " << methodId); + profilerState->printMethod("Left", methodId); if (!profilerState->coverageTracker->isCollectMainOnly()) profilerState->coverageTracker->addCoverage(offset, Leave, methodId); profilerState->threadTracker->stackBalanceDown(); @@ -133,20 +135,20 @@ void vsharp::Track_Leave(OFFSET offset, int methodId) { void vsharp::Track_LeaveMain(OFFSET offset, int methodId) { if (!profilerState->threadTracker->isCurrentThreadTracked()) return; - printMethod("Left Main", methodId); - profilerState->coverageTracker->addCoverage(offset, LeaveMain, methodId); - // LOG(tout << "Track_LeaveMain: " << methodId); + profilerState->printMethod("Left main", methodId); if (profilerState->threadTracker->stackBalanceDown()) { - // first main frame is not yet reached + // first main frame has not yet been reached + profilerState->coverageTracker->addCoverage(offset, Leave, methodId); return; } + profilerState->coverageTracker->addCoverage(offset, LeaveMain, methodId); profilerState->threadTracker->loseCurrentThread(); } void vsharp::Track_Throw(OFFSET offset, int methodId) { if (!profilerState->threadTracker->isCurrentThreadTracked()) return; - LOG(tout << "Track_Throw: method = " << methodId << ", offset = " << HEX(offset)); - profilerState->coverageTracker->addCoverage(offset, Leave, methodId); + profilerState->printMethod("Throw", methodId); + profilerState->coverageTracker->addCoverage(offset, ThrowLeave, methodId); } void vsharp::Finalize_Call(OFFSET offset) { diff --git a/VSharp.CoverageInstrumenter/profiler/probes.h b/VSharp.CoverageInstrumenter/profiler/probes.h index 0e5979c72..4167a5168 100644 --- a/VSharp.CoverageInstrumenter/profiler/probes.h +++ b/VSharp.CoverageInstrumenter/profiler/probes.h @@ -63,5 +63,6 @@ extern CoverageProbes coverageProbes; CoverageProbes* getProbes(); void InitializeProbes(); +void DestroyProbes(); } #endif // PROBES_H_ diff --git a/VSharp.CoverageInstrumenter/profiler/profilerState.cpp b/VSharp.CoverageInstrumenter/profiler/profilerState.cpp index 821668acd..6bbceaa54 100644 --- a/VSharp.CoverageInstrumenter/profiler/profilerState.cpp +++ b/VSharp.CoverageInstrumenter/profiler/profilerState.cpp @@ -14,7 +14,8 @@ void ConvertToWCHAR(const char *str, std::u16string &result) { } bool IsEnvVarPresent(const char *name) { - return std::getenv(name) != nullptr; + auto value = std::getenv(name); + return value != nullptr && value[0] == '1'; } char *CheckEnvVarAndGet(const char *name) { @@ -32,10 +33,14 @@ ProfilerState::ProfilerState(ICorProfilerInfo8 *corProfilerInfo) { isTestExpected = IsEnvVarPresent("COVERAGE_TOOL_EXPECT_TEST_SUITE"); collectMainOnly = IsEnvVarPresent("COVERAGE_TOOL_INSTRUMENT_MAIN_ONLY"); - mainMethodInfo.moduleName = nullptr; // mainMethod is not yet specified + // mainMethod is not yet specified + mainMethodInfo.moduleName = nullptr; + mainMethodInfo.assemblyName = nullptr; + + std::atomic_store(&shutdownInOrder, false); #ifdef _LOGGING - const char *name = isPassiveRun ? "lastrun.log" : "lastcoverage.log"; + static const char *name = isPassiveRun ? "lastrun.log" : "lastcoverage.log"; open_log(name); #endif @@ -47,9 +52,11 @@ ProfilerState::ProfilerState(ICorProfilerInfo8 *corProfilerInfo) { } if (isTestExpected) { - ReadTestAssemblies(CheckEnvVarAndGet("COVERAGE_TOOL_ASSEMBLY_PATHS_FILE")); + ReadAssembliesFile(CheckEnvVarAndGet("COVERAGE_TOOL_ASSEMBLY_PATHS_FILE"), &approvedAssemblies); + ReadAssembliesFile(CheckEnvVarAndGet("COVERAGE_TOOL_MAIN_ASSEMBLY_PATHS_FILE"), &testAssemblies); LOG(tout << "RECEIVED ASSEMBLIES TO INSTRUMENT" << std::endl); } + LOG(tout << approvedAssemblies.size() << " " << testAssemblies.size() << std::endl); if (IsEnvVarPresent("COVERAGE_TOOL_SPECIFY_MAIN_METHOD")) { std::u16string assemblyNameU16; @@ -82,24 +89,28 @@ ProfilerState::ProfilerState(ICorProfilerInfo8 *corProfilerInfo) { coverageTracker = new CoverageTracker(threadTracker, threadInfo, collectMainOnly); } -void vsharp::ProfilerState::ReadTestAssemblies(const char *path) +ProfilerState::~ProfilerState() { + delete threadInfo; + delete threadTracker; + delete coverageTracker; + DestroyProbes(); +} + +void vsharp::ProfilerState::ReadAssembliesFile(const char *path, std::vector *assemblyList) { std::ifstream assembliesFile; - assembliesFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); - - try { - assembliesFile.open(path, std::ios_base::in); - std::string assembly; - while (assembliesFile >> assembly) - approvedAssemblies.push_back(assembly); + assembliesFile.open(path, std::ios_base::in); + std::string assembly; + profiler_assert(assembliesFile.good()); - assembliesFile.close(); - } - catch (std::ifstream::failure e) { - LOG(tout << "FAILURE DURING THE READ OF ASSEMBLIES FILE! TOTAL RETRIEVED: " << approvedAssemblies.size() - << "\nCONTINUING WITH RETRIEVED DATA"); + while (getline(assembliesFile, assembly)) { + assemblyList->push_back(assembly); + LOG(tout << assembly << std::endl); } + + assembliesFile.close(); + LOG(tout << std::endl); } void vsharp::ProfilerState::setEntryMain(char *assemblyName, int assemblyNameLength, char *moduleName, int moduleNameLength, int methodToken) { @@ -109,6 +120,7 @@ void vsharp::ProfilerState::setEntryMain(char *assemblyName, int assemblyNameLen auto* wcharModuleName = new WCHAR[moduleNameLength]; memcpy(wcharModuleName, moduleName, moduleNameLength * sizeof(WCHAR)); + mainMethodInfo.Dispose(); // deleting previously allocated names mainMethodInfo = { (mdMethodDef) methodToken, (ULONG) assemblyNameLength, @@ -117,3 +129,14 @@ void vsharp::ProfilerState::setEntryMain(char *assemblyName, int assemblyNameLen wcharModuleName }; } + +void vsharp::ProfilerState::printMethod(std::string message, int methodId) { + LOG( + coverageTracker->collectedMethodsMutex.lock(); + auto method = coverageTracker->collectedMethods[methodId]; + auto wl = method.assemblyNameLength; + auto ws = method.assemblyName; + tout << message << ' ' << std::string(ws, ws + wl - 1) << '.' << method.methodName << std::endl; + coverageTracker->collectedMethodsMutex.unlock(); + ); +} diff --git a/VSharp.CoverageInstrumenter/profiler/profilerState.h b/VSharp.CoverageInstrumenter/profiler/profilerState.h index be160641c..888c82a76 100644 --- a/VSharp.CoverageInstrumenter/profiler/profilerState.h +++ b/VSharp.CoverageInstrumenter/profiler/profilerState.h @@ -11,7 +11,7 @@ class ProfilerState { private: static const FunctionID incorrectFunctionId = 0; - void ReadTestAssemblies(const char *path); + void ReadAssembliesFile(const char *path, std::vector *assemblyList); public: ThreadTracker* threadTracker; CoverageTracker* coverageTracker; @@ -19,10 +19,15 @@ class ProfilerState { bool isPassiveRun = false; bool collectMainOnly = false; - bool isFinished = false; bool isTestExpected = false; char *passiveResultPath = nullptr; std::vector approvedAssemblies; + std::vector testAssemblies; + + // saves every FunctionID instrumented, and the corresponding MethodID within the coverage tool + std::map funcIdToMethodId; + + std::mutex finalizator; MethodInfo mainMethodInfo; FunctionID mainFunctionId; @@ -32,6 +37,9 @@ class ProfilerState { void setEntryMain(char* assemblyName, int assemblyNameLength, char* moduleName, int moduleNameLength, int methodToken); explicit ProfilerState(ICorProfilerInfo8 *corProfilerInfo); + ~ProfilerState(); + + void printMethod(std::string message, int methodId); }; extern ProfilerState* profilerState; diff --git a/VSharp.CoverageInstrumenter/profiler/threadStorage.h b/VSharp.CoverageInstrumenter/profiler/threadStorage.h index 7c3f97173..6c2afaa83 100644 --- a/VSharp.CoverageInstrumenter/profiler/threadStorage.h +++ b/VSharp.CoverageInstrumenter/profiler/threadStorage.h @@ -101,6 +101,9 @@ template class ThreadStorage { return result; } + ~ThreadStorage() { + clear(); + } }; #endif //VSHARP_COVERAGEINSTRUMENTER_THREADSTORAGE_H diff --git a/VSharp.CoverageInstrumenter/profiler/threadTracker.cpp b/VSharp.CoverageInstrumenter/profiler/threadTracker.cpp index 04665bf36..2fb7e85ef 100644 --- a/VSharp.CoverageInstrumenter/profiler/threadTracker.cpp +++ b/VSharp.CoverageInstrumenter/profiler/threadTracker.cpp @@ -69,11 +69,13 @@ void ThreadTracker::unwindFunctionEnter(FunctionID functionId) { void ThreadTracker::unwindFunctionLeave() { profiler_assert(isCurrentThreadTracked()); - LOG(tout << "Unwind leave" << std::endl); auto functionId = unwindFunctionIds->load(); unwindFunctionIds->remove(); - if (profilerState->collectMainOnly && profilerState->mainFunctionId != functionId) return; - if ((!isInFilter() || stackBalance() > 1) && !stackBalanceDown()) { + LOG(tout << "Unwind leave" << std::endl); + if ((profilerState->collectMainOnly && profilerState->mainFunctionId != functionId) + || profilerState->funcIdToMethodId.find(functionId) == profilerState->funcIdToMethodId.end()) return; + profilerState->coverageTracker->addCoverage(0, ThrowLeave, profilerState->funcIdToMethodId[functionId]); + if ((!isInFilter() || stackBalance() > 0) && !stackBalanceDown()) { // stack is empty; function left loseCurrentThread(); } @@ -133,3 +135,11 @@ ThreadTracker::ThreadTracker(ThreadInfo *threadInfo) { unwindFunctionIds = new ThreadStorage(threadInfo); inFilterMapping = new ThreadStorage(threadInfo); } + +ThreadTracker::~ThreadTracker() { + clear(); + delete threadIdMapping; + delete stackBalances; + delete unwindFunctionIds; + delete inFilterMapping; +} diff --git a/VSharp.CoverageInstrumenter/profiler/threadTracker.h b/VSharp.CoverageInstrumenter/profiler/threadTracker.h index a7e3030e1..fc9d32dc2 100644 --- a/VSharp.CoverageInstrumenter/profiler/threadTracker.h +++ b/VSharp.CoverageInstrumenter/profiler/threadTracker.h @@ -38,6 +38,7 @@ class ThreadTracker { bool isPossibleStackOverflow(); explicit ThreadTracker(ThreadInfo* threadInfo); + ~ThreadTracker(); }; } diff --git a/VSharp.CoverageTool/CoverageTool.fs b/VSharp.CoverageTool/CoverageTool.fs index b3f4f703d..054fa0692 100644 --- a/VSharp.CoverageTool/CoverageTool.fs +++ b/VSharp.CoverageTool/CoverageTool.fs @@ -5,6 +5,7 @@ open System.Diagnostics open System.IO open System.Reflection open System.Runtime.InteropServices +open FSharpx.Collections open Microsoft.FSharp.NativeInterop open VSharp @@ -15,30 +16,44 @@ open VSharp.CSharpUtils #nowarn "9" module private ExternalCalls = - [] + [] extern void SetEntryMain(byte* assemblyName, int assemblyNameLength, byte* moduleName, int moduleNameLength, int methodToken) - [] + [] extern void GetHistory(nativeint size, nativeint data) - [] + // frees up the memory used to pass history information; a must call after every GetHistory + [] + extern void ClearHistory() + + [] extern void SetCurrentThreadId(int id) module private Configuration = - let (|Windows|MacOs|Linux|) _ = - if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then Windows - elif RuntimeInformation.IsOSPlatform(OSPlatform.Linux) then Linux - elif RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then MacOs - else Prelude.__notImplemented__() - - let libExtension = - match () with - | Windows -> ".dll" - | Linux -> ".so" - | MacOs -> ".dylib" - - let private enabled = "1" + // TODO: a enum to avoid unnecessary code copy? + let private availableCoverageToolOptions = [| + "CORECLR_PROFILER"; + "CORECLR_PROFILER_PATH"; + "CORECLR_ENABLE_PROFILING"; + "COVERAGE_TOOL_INSTRUMENT_MAIN_ONLY"; + "COVERAGE_TOOL_RESULT_NAME"; + "COVERAGE_TOOL_SPECIFY_MAIN_METHOD"; + "COVERAGE_TOOL_METHOD_ASSEMBLY_NAME"; + "COVERAGE_TOOL_METHOD_MODULE_NAME"; + "COVERAGE_TOOL_METHOD_TOKEN"; + "COVERAGE_TOOL_ENABLE_PASSIVE"; + "COVERAGE_TOOL_RESULT_NAME"; + "COVERAGE_TOOL_EXPECT_TEST_SUITE"; + "COVERAGE_TOOL_ASSEMBLY_PATHS_FILE"; + "COVERAGE_TOOL_MAIN_ASSEMBLY_PATHS_FILE"; + |] + + let createUnspecifiedCoverageToolConfiguration() = + let result = EnvironmentCollection() + for envVar in availableCoverageToolOptions do + result[envVar] <- EnvVarUnspecified + result [] type private BaseCoverageToolConfiguration = { @@ -58,6 +73,8 @@ module private Configuration = passiveModeEnable: string [] resultName: string + [] + mainMethodSpecified: string [] assemblyName: string [] @@ -66,34 +83,59 @@ module private Configuration = methodToken: string } + [] + type private UnderDotnetTestConfiguration = { + [] + passiveModeEnable: string + [] + resultNamePath: string + [] + expectTestSuite: string + [] + assemblyPathsFile: string + [] + mainAssemblyPathsFile: string + } + let private withCoverageToolConfiguration mainOnly processInfo = - let currentDirectory = Directory.GetCurrentDirectory() let configuration = { - coreclrProfiler = "{2800fea6-9667-4b42-a2b6-45dc98e77e9e}" - coreclrProfilerPath = $"{currentDirectory}{Path.DirectorySeparatorChar}libvsharpCoverage{libExtension}" - coreclrEnableProfiling = enabled - instrumentMainOnly = if mainOnly then enabled else "" + coreclrProfiler = CoverageToolInfo.ProfilerUid + coreclrProfilerPath = CoverageToolInfo.fullPath + coreclrEnableProfiling = CoverageToolInfo.Enable + instrumentMainOnly = if mainOnly then CoverageToolInfo.Enable else "" } withConfiguration configuration processInfo let withMainOnlyCoverageToolConfiguration = withCoverageToolConfiguration true - let withAllMethodsCoverageToolConfiguration = - withCoverageToolConfiguration false + let withAllMethodsCoverageToolConfiguration (collection: EnvironmentCollection) = + withCoverageToolConfiguration false collection - let withPassiveModeConfiguration (method : MethodBase) resultName processInfo = + let withPassiveModeConfiguration (method: MethodBase) resultName processInfo = let configuration = { - passiveModeEnable = enabled + passiveModeEnable = CoverageToolInfo.Enable resultName = resultName + mainMethodSpecified = CoverageToolInfo.Enable assemblyName = method.Module.Assembly.FullName moduleName = method.Module.FullyQualifiedName methodToken = method.MetadataToken.ToString() } withConfiguration configuration processInfo - + + let withUnderDotnetTestConfiguration allAssembliesPath mainAssembliesPath resultNamePath processInfo = + let configuration = + { + passiveModeEnable = CoverageToolInfo.Enable + resultNamePath = resultNamePath + expectTestSuite = CoverageToolInfo.Enable + assemblyPathsFile = allAssembliesPath + mainAssemblyPathsFile = mainAssembliesPath + } + withConfiguration configuration processInfo + let isCoverageToolAttached () = isConfigured () type InteractionCoverageTool() = @@ -107,7 +149,7 @@ type InteractionCoverageTool() = member this.GetRawHistory () = if not entryMainWasSet then - Prelude.internalfail "Try call GetRawHistory, while entryMain wasn't set" + Prelude.internalfail "Calling GetRawHistory when entryMain isn't set" let sizePtr = NativePtr.stackalloc 1 let dataPtrPtr = NativePtr.stackalloc 1 @@ -118,6 +160,9 @@ type InteractionCoverageTool() = let data = Array.zeroCreate size Marshal.Copy(dataPtr, data, 0, size) + + ExternalCalls.ClearHistory() + data member this.SetEntryMain (assembly : Assembly) (moduleName : string) (methodToken : int) = @@ -138,8 +183,11 @@ type InteractionCoverageTool() = member this.SetCurrentThreadId id = ExternalCalls.SetCurrentThreadId(id) - static member WithCoverageTool (procInfo : ProcessStartInfo) = - Configuration.withMainOnlyCoverageToolConfiguration procInfo + static member WithCoverageTool processInfo = + let configuration = + Configuration.createUnspecifiedCoverageToolConfiguration() + |> Configuration.withMainOnlyCoverageToolConfiguration + setProcInfoEnvironment configuration processInfo type PassiveCoverageTool(workingDirectory: DirectoryInfo, method: MethodBase) = @@ -185,12 +233,16 @@ type PassiveCoverageTool(workingDirectory: DirectoryInfo, method: MethodBase) = (double coveredSize) / (double cfg.MethodSize) * 100. |> int member this.RunWithCoverage (args: string) = + let passiveEnv = + Configuration.createUnspecifiedCoverageToolConfiguration() + |> Configuration.withMainOnlyCoverageToolConfiguration + |> Configuration.withPassiveModeConfiguration method resultName + let procInfo = ProcessStartInfo() procInfo.Arguments <- args procInfo.FileName <- DotnetExecutablePath.ExecutablePath procInfo.WorkingDirectory <- workingDirectory.FullName - Configuration.withMainOnlyCoverageToolConfiguration procInfo - Configuration.withPassiveModeConfiguration method resultName procInfo + setProcInfoEnvironment passiveEnv procInfo let method = Application.getMethod method if not method.HasBody then @@ -212,3 +264,4 @@ type PassiveCoverageTool(workingDirectory: DirectoryInfo, method: MethodBase) = else Logger.error $"Run with coverage failed with exit code: {proc.ExitCode}" -1 + diff --git a/VSharp.Utils/CoverageDeserializer.fs b/VSharp.Utils/CoverageDeserializer.fs index 1ab919e22..a469a4b3a 100644 --- a/VSharp.Utils/CoverageDeserializer.fs +++ b/VSharp.Utils/CoverageDeserializer.fs @@ -19,12 +19,13 @@ type CoverageReport = { #nowarn "9" [] -[] +[] type RawCoverageLocation = { [] offset: uint32 [] event: int32 [] methodId: int32 [] threadId: uint64 + [] timeInMicroseconds: int64 } type RawMethodInfo = { @@ -57,6 +58,11 @@ module CoverageDeserializer = increaseOffset sizeof result + let inline private readInt64 () = + let result = BitConverter.ToInt64(data, dataOffset) + increaseOffset sizeof + result + let inline private readUInt32 () = let result = BitConverter.ToUInt32(data, dataOffset) increaseOffset sizeof @@ -85,7 +91,8 @@ module CoverageDeserializer = let event = readInt32 () let methodId = readInt32 () let threadId = readUInt64 () - { offset = offset; event = event; methodId = methodId; threadId = threadId } + let time = readInt64 () + { offset = offset; event = event; methodId = methodId; threadId = threadId; timeInMicroseconds = time } let inline private deserializeArray elementDeserializer = let arraySize = readInt32 () diff --git a/VSharp.Utils/CoverageToolInfo.fs b/VSharp.Utils/CoverageToolInfo.fs new file mode 100644 index 000000000..3711ec2ba --- /dev/null +++ b/VSharp.Utils/CoverageToolInfo.fs @@ -0,0 +1,30 @@ +namespace VSharp + +open System.IO +open System.Runtime.InteropServices + +module CoverageToolInfo = + + [] + let DllName = "libvsharpCoverage" + + [] + let ProfilerUid = "{2800fea6-9667-4b42-a2b6-45dc98e77e9e}" + + [] + let Enable = "1" + + let (|Windows|MacOs|Linux|) _ = + if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then Windows + elif RuntimeInformation.IsOSPlatform(OSPlatform.Linux) then Linux + elif RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then MacOs + else __notImplemented__() + + let libExtension = + match () with + | Windows -> ".dll" + | Linux -> ".so" + | MacOs -> ".dylib" + + let fullPath = + $"{Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}{DllName}{libExtension}" diff --git a/VSharp.Utils/EnvironmentUtils.fs b/VSharp.Utils/EnvironmentUtils.fs index c18e1d1a5..bb0c636bc 100644 --- a/VSharp.Utils/EnvironmentUtils.fs +++ b/VSharp.Utils/EnvironmentUtils.fs @@ -6,9 +6,11 @@ open System.Diagnostics open System.Reflection open VSharp +let EnvVarUnspecified = "\0" + let optionalFromEnv name = let value = Environment.GetEnvironmentVariable(name) - if value = null then None + if value = null || value = EnvVarUnspecified then None else Some value let fromEnv name = @@ -24,6 +26,11 @@ type EnvironmentVariableAttribute(name : string) = inherit Attribute() member this.Name with get() = name +type EnvironmentCollection = Dictionary + +let isEnvVarSpecified (envCollection: EnvironmentCollection) name = + envCollection.ContainsKey(name) && envCollection[name] <> EnvVarUnspecified + let private getEnvironmentConfiguration<'a> () = let configurationType = typeof<'a> assert Attribute.IsDefined(configurationType, typeof) @@ -39,10 +46,18 @@ let private getEnvironmentConfiguration<'a> () = result -let withConfiguration (configuration: 'a) (procInfo : ProcessStartInfo) = +let withConfiguration (configuration: 'a) (env: EnvironmentCollection) = + for property, envVarName in getEnvironmentConfiguration<'a> () do + assert (isEnvVarSpecified env envVarName |> not) + env[envVarName] <- property.GetValue(configuration) |> string + + env + +let setProcInfoEnvironment (env: EnvironmentCollection) (procInfo: ProcessStartInfo) = + for KeyValue(envVarName, envValue) in env do assert (procInfo.Environment.ContainsKey(envVarName) |> not) - procInfo.Environment[envVarName] <- property.GetValue(configuration) |> string + procInfo.Environment[envVarName] <- envValue let isConfigured<'a> () = let mutable allConfigured = true diff --git a/VSharp.Utils/VSharp.Utils.fsproj b/VSharp.Utils/VSharp.Utils.fsproj index 3bb1a19d6..ce9d85f21 100644 --- a/VSharp.Utils/VSharp.Utils.fsproj +++ b/VSharp.Utils/VSharp.Utils.fsproj @@ -48,6 +48,7 @@ + From 993d716a7162faed0b9d22a5ea4ec0b6dacc47c8 Mon Sep 17 00:00:00 2001 From: DanielELog Date: Mon, 8 Jul 2024 17:58:36 +0300 Subject: [PATCH 3/6] [style] improves readability --- .../profiler/corProfiler.cpp | 10 -------- .../profiler/coverageTracker.cpp | 8 ------- .../profiler/coverageTracker.h | 2 +- .../profiler/instrumenter.cpp | 8 ++++--- .../profiler/profilerState.cpp | 24 +++++++++---------- 5 files changed, 18 insertions(+), 34 deletions(-) diff --git a/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp b/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp index 1109f1edb..9bc98b176 100644 --- a/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp +++ b/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp @@ -31,9 +31,6 @@ CorProfiler::~CorProfiler() HRESULT STDMETHODCALLTYPE CorProfiler::Initialize(IUnknown *pICorProfilerInfoUnk) { - setbuf(stdout, NULL); - - printf("PROFILER INITIALIZATION\n"); const char* waitDebuggerAttached = std::getenv("COVERAGE_TOOL_WAIT_DEBUGGER_ATTACHED"); volatile int done = waitDebuggerAttached == nullptr ? 1 : 0; while (!done) OS::sleepSeconds(1); @@ -67,7 +64,6 @@ HRESULT STDMETHODCALLTYPE CorProfiler::Initialize(IUnknown *pICorProfilerInfoUnk HRESULT STDMETHODCALLTYPE CorProfiler::Shutdown() { - printf("PROFILER SHUTDOWN\n"); std::atomic_store(&shutdownInOrder, true); // waiting until all current requests are resolved @@ -75,17 +71,11 @@ HRESULT STDMETHODCALLTYPE CorProfiler::Shutdown() LOG(tout << "SHUTDOWN"); if (profilerState->isPassiveRun) { - printf("serializing information\n"); - size_t tmpSize; auto tmpBytes = profilerState->coverageTracker->serializeCoverageReport(&tmpSize); - printf("saving to %s\n", profilerState->passiveResultPath); - std::ofstream fout; fout.open(profilerState->passiveResultPath, std::ios::out|std::ios::binary); - if (!fout.write(tmpBytes, static_cast(tmpSize))) - printf("failure while saving the file\n"); fout.close(); delete tmpBytes; diff --git a/VSharp.CoverageInstrumenter/profiler/coverageTracker.cpp b/VSharp.CoverageInstrumenter/profiler/coverageTracker.cpp index 790dc4f55..4b2bc7f98 100644 --- a/VSharp.CoverageInstrumenter/profiler/coverageTracker.cpp +++ b/VSharp.CoverageInstrumenter/profiler/coverageTracker.cpp @@ -122,7 +122,6 @@ void CoverageTracker::invocationFinished() { char* CoverageTracker::serializeCoverageReport(size_t* size) { collectedMethodsMutex.lock(); serializedCoverageMutex.lock(); - printf("got locks, converting info\n"); auto buffer = std::vector(); auto methodsToSerialize = std::vector>(); @@ -139,7 +138,6 @@ char* CoverageTracker::serializeCoverageReport(size_t* size) { serializePrimitive(el.first, buffer); el.second.serialize(buffer); } - printf("converted methods: %lld\n", buffer.size()); auto threadMapping = profilerState->threadTracker->getMapping(); for (auto mapping: threadMapping) { @@ -162,8 +160,6 @@ char* CoverageTracker::serializeCoverageReport(size_t* size) { serializePrimitiveArray(&serializedCoverage[i][0], serializedCoverage[i].size(), buffer); } - printf("converted reports: %lld\n", buffer.size()); - for (auto cov : trackedCoverage->items()) delete cov.second; trackedCoverage->clear(); @@ -173,14 +169,10 @@ char* CoverageTracker::serializeCoverageReport(size_t* size) { serializedCoverageMutex.unlock(); collectedMethodsMutex.unlock(); - printf("cleared and unlocked data\n"); - *size = buffer.size(); char* array = new char[*size]; - printf("successfully allocated\n"); std::memcpy(array, &buffer[0], *size); - printf("total bytes: %lld", *size); return array; } diff --git a/VSharp.CoverageInstrumenter/profiler/coverageTracker.h b/VSharp.CoverageInstrumenter/profiler/coverageTracker.h index e1ef99567..3ecb70dd5 100644 --- a/VSharp.CoverageInstrumenter/profiler/coverageTracker.h +++ b/VSharp.CoverageInstrumenter/profiler/coverageTracker.h @@ -69,10 +69,10 @@ class CoverageTracker { ThreadTracker* threadTracker; std::mutex serializedCoverageMutex; std::vector serializedCoverageThreadIds; -public: std::vector> serializedCoverage; std::mutex collectedMethodsMutex; std::vector collectedMethods; +public: explicit CoverageTracker(ThreadTracker* threadTracker, ThreadInfo* threadInfo, bool collectMainOnly); bool isCollectMainOnly() const; void addCoverage(OFFSET offset, CoverageEvent event, int methodId); diff --git a/VSharp.CoverageInstrumenter/profiler/instrumenter.cpp b/VSharp.CoverageInstrumenter/profiler/instrumenter.cpp index 71cc947a9..2ac204ae5 100644 --- a/VSharp.CoverageInstrumenter/profiler/instrumenter.cpp +++ b/VSharp.CoverageInstrumenter/profiler/instrumenter.cpp @@ -83,10 +83,12 @@ bool doesContainAssembly(const WCHAR *assemblyName, int assemblyNameLength, std: return false; } -// TODO: simplify the condition by adding two main modes as coverage tool config bool vsharp::InstrumentationIsNeeded(const WCHAR* assemblyName, int assemblySize, const WCHAR *moduleName, int moduleSize, mdMethodDef method) { - return IsMain(moduleName, moduleSize, method) || (!profilerState->isTestExpected && !profilerState->collectMainOnly) - || (profilerState->isTestExpected && doesContainAssembly(assemblyName, assemblySize, profilerState->approvedAssemblies)); + bool shouldInstrumentAll = !profilerState->isTestExpected && !profilerState->collectMainOnly; + bool fromTestAssembly = profilerState->isTestExpected && doesContainAssembly(assemblyName, assemblySize, profilerState->approvedAssemblies); + return IsMain(moduleName, moduleSize, method) + || shouldInstrumentAll + || fromTestAssembly; } HRESULT Instrumenter::doInstrumentation(ModuleID oldModuleId, size_t methodId, const WCHAR *moduleName, ULONG moduleNameLength) { diff --git a/VSharp.CoverageInstrumenter/profiler/profilerState.cpp b/VSharp.CoverageInstrumenter/profiler/profilerState.cpp index 6bbceaa54..b398b15cb 100644 --- a/VSharp.CoverageInstrumenter/profiler/profilerState.cpp +++ b/VSharp.CoverageInstrumenter/profiler/profilerState.cpp @@ -13,12 +13,12 @@ void ConvertToWCHAR(const char *str, std::u16string &result) { result = conv16.from_bytes(str); } -bool IsEnvVarPresent(const char *name) { +bool IsEnvVarEnabled(const char *name) { auto value = std::getenv(name); return value != nullptr && value[0] == '1'; } -char *CheckEnvVarAndGet(const char *name) { +char *RetrieveEnvVarOrFail(const char *name) { auto str = std::getenv(name); profiler_assert(str); return str; @@ -29,9 +29,9 @@ bool ProfilerState::isCorrectFunctionId(FunctionID id) { } ProfilerState::ProfilerState(ICorProfilerInfo8 *corProfilerInfo) { - isPassiveRun = IsEnvVarPresent("COVERAGE_TOOL_ENABLE_PASSIVE"); - isTestExpected = IsEnvVarPresent("COVERAGE_TOOL_EXPECT_TEST_SUITE"); - collectMainOnly = IsEnvVarPresent("COVERAGE_TOOL_INSTRUMENT_MAIN_ONLY"); + isPassiveRun = IsEnvVarEnabled("COVERAGE_TOOL_ENABLE_PASSIVE"); + isTestExpected = IsEnvVarEnabled("COVERAGE_TOOL_EXPECT_TEST_SUITE"); + collectMainOnly = IsEnvVarEnabled("COVERAGE_TOOL_INSTRUMENT_MAIN_ONLY"); // mainMethod is not yet specified mainMethodInfo.moduleName = nullptr; @@ -48,23 +48,23 @@ ProfilerState::ProfilerState(ICorProfilerInfo8 *corProfilerInfo) { if (isPassiveRun) { LOG(tout << "WORKING IN PASSIVE MODE" << std::endl); - passiveResultPath = CheckEnvVarAndGet("COVERAGE_TOOL_RESULT_NAME"); + passiveResultPath = RetrieveEnvVarOrFail("COVERAGE_TOOL_RESULT_NAME"); } if (isTestExpected) { - ReadAssembliesFile(CheckEnvVarAndGet("COVERAGE_TOOL_ASSEMBLY_PATHS_FILE"), &approvedAssemblies); - ReadAssembliesFile(CheckEnvVarAndGet("COVERAGE_TOOL_MAIN_ASSEMBLY_PATHS_FILE"), &testAssemblies); + ReadAssembliesFile(RetrieveEnvVarOrFail("COVERAGE_TOOL_ASSEMBLY_PATHS_FILE"), &approvedAssemblies); + ReadAssembliesFile(RetrieveEnvVarOrFail("COVERAGE_TOOL_MAIN_ASSEMBLY_PATHS_FILE"), &testAssemblies); LOG(tout << "RECEIVED ASSEMBLIES TO INSTRUMENT" << std::endl); } LOG(tout << approvedAssemblies.size() << " " << testAssemblies.size() << std::endl); - if (IsEnvVarPresent("COVERAGE_TOOL_SPECIFY_MAIN_METHOD")) { + if (IsEnvVarEnabled("COVERAGE_TOOL_SPECIFY_MAIN_METHOD")) { std::u16string assemblyNameU16; std::u16string moduleNameU16; - ConvertToWCHAR(CheckEnvVarAndGet("COVERAGE_TOOL_METHOD_ASSEMBLY_NAME"), assemblyNameU16); - ConvertToWCHAR(CheckEnvVarAndGet("COVERAGE_TOOL_METHOD_MODULE_NAME"), moduleNameU16); - int mainToken = std::stoi(CheckEnvVarAndGet("COVERAGE_TOOL_METHOD_TOKEN")); + ConvertToWCHAR(RetrieveEnvVarOrFail("COVERAGE_TOOL_METHOD_ASSEMBLY_NAME"), assemblyNameU16); + ConvertToWCHAR(RetrieveEnvVarOrFail("COVERAGE_TOOL_METHOD_MODULE_NAME"), moduleNameU16); + int mainToken = std::stoi(RetrieveEnvVarOrFail("COVERAGE_TOOL_METHOD_TOKEN")); size_t mainAssemblyNameLength = assemblyNameU16.size(); auto* mainAssemblyName = new WCHAR[mainAssemblyNameLength]; From 80de94c27421e56a7616e9f30c03941693f4a930 Mon Sep 17 00:00:00 2001 From: DanielELog Date: Mon, 8 Jul 2024 18:12:19 +0300 Subject: [PATCH 4/6] [fix] use of private fields --- .../profiler/coverageTracker.cpp | 12 ++++++++++++ .../profiler/coverageTracker.h | 1 + .../profiler/profilerState.cpp | 8 ++------ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/VSharp.CoverageInstrumenter/profiler/coverageTracker.cpp b/VSharp.CoverageInstrumenter/profiler/coverageTracker.cpp index 4b2bc7f98..d24941754 100644 --- a/VSharp.CoverageInstrumenter/profiler/coverageTracker.cpp +++ b/VSharp.CoverageInstrumenter/profiler/coverageTracker.cpp @@ -207,4 +207,16 @@ void CoverageTracker::invocationAborted() { return (CoverageHistory*) nullptr; }); } + +void CoverageTracker::printMethodDebug(int methodId) +{ + LOG( + collectedMethodsMutex.lock(); + auto method = collectedMethods[methodId]; + auto wl = method.assemblyNameLength; + auto ws = method.assemblyName; + tout << ' ' << std::string(ws, ws + wl - 1) << '.' << method.methodName << std::endl; + collectedMethodsMutex.unlock(); + ); +} //endregion diff --git a/VSharp.CoverageInstrumenter/profiler/coverageTracker.h b/VSharp.CoverageInstrumenter/profiler/coverageTracker.h index 3ecb70dd5..fbbfdedf5 100644 --- a/VSharp.CoverageInstrumenter/profiler/coverageTracker.h +++ b/VSharp.CoverageInstrumenter/profiler/coverageTracker.h @@ -81,6 +81,7 @@ class CoverageTracker { size_t collectMethod(MethodInfo info); char* serializeCoverageReport(size_t* size); void clear(); + void printMethodDebug(int methodId); ~CoverageTracker(); }; diff --git a/VSharp.CoverageInstrumenter/profiler/profilerState.cpp b/VSharp.CoverageInstrumenter/profiler/profilerState.cpp index b398b15cb..dade44de5 100644 --- a/VSharp.CoverageInstrumenter/profiler/profilerState.cpp +++ b/VSharp.CoverageInstrumenter/profiler/profilerState.cpp @@ -132,11 +132,7 @@ void vsharp::ProfilerState::setEntryMain(char *assemblyName, int assemblyNameLen void vsharp::ProfilerState::printMethod(std::string message, int methodId) { LOG( - coverageTracker->collectedMethodsMutex.lock(); - auto method = coverageTracker->collectedMethods[methodId]; - auto wl = method.assemblyNameLength; - auto ws = method.assemblyName; - tout << message << ' ' << std::string(ws, ws + wl - 1) << '.' << method.methodName << std::endl; - coverageTracker->collectedMethodsMutex.unlock(); + tout << message; + coverageTracker->printMethodDebug(methodId); ); } From 65ecc145e6eb573e8b97a64046b475774e7275d3 Mon Sep 17 00:00:00 2001 From: DanielELog Date: Tue, 9 Jul 2024 19:57:28 +0300 Subject: [PATCH 5/6] [fix] restores coverage output --- VSharp.CoverageInstrumenter/profiler/corProfiler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp b/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp index 9bc98b176..d492ac296 100644 --- a/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp +++ b/VSharp.CoverageInstrumenter/profiler/corProfiler.cpp @@ -76,6 +76,7 @@ HRESULT STDMETHODCALLTYPE CorProfiler::Shutdown() std::ofstream fout; fout.open(profilerState->passiveResultPath, std::ios::out|std::ios::binary); + fout.write(tmpBytes, static_cast(tmpSize)); fout.close(); delete tmpBytes; From 3b560268eb448b6ad7957e0eb878e693a4dc1276 Mon Sep 17 00:00:00 2001 From: DanielELog Date: Wed, 10 Jul 2024 16:57:26 +0300 Subject: [PATCH 6/6] [fix] incorrect thread loss upon filter leave --- VSharp.CoverageInstrumenter/profiler/threadTracker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VSharp.CoverageInstrumenter/profiler/threadTracker.cpp b/VSharp.CoverageInstrumenter/profiler/threadTracker.cpp index 2fb7e85ef..b3d50d097 100644 --- a/VSharp.CoverageInstrumenter/profiler/threadTracker.cpp +++ b/VSharp.CoverageInstrumenter/profiler/threadTracker.cpp @@ -75,7 +75,7 @@ void ThreadTracker::unwindFunctionLeave() { if ((profilerState->collectMainOnly && profilerState->mainFunctionId != functionId) || profilerState->funcIdToMethodId.find(functionId) == profilerState->funcIdToMethodId.end()) return; profilerState->coverageTracker->addCoverage(0, ThrowLeave, profilerState->funcIdToMethodId[functionId]); - if ((!isInFilter() || stackBalance() > 0) && !stackBalanceDown()) { + if (!isInFilter() && !stackBalanceDown()) { // stack is empty; function left loseCurrentThread(); }