[Parameter Capturing] Add probe instrumentation APIs to profiler (#4428)

This commit is contained in:
Joe Schmitt 2023-05-31 13:11:03 -07:00 коммит произвёл GitHub
Родитель 9abfff823d
Коммит 3dafe54bb6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
20 изменённых файлов: 1370 добавлений и 13 удалений

1
.vscode/settings.json поставляемый
Просмотреть файл

@ -43,6 +43,7 @@
// Parts of our documentation include large text blobs, such as examples of public keys.
"cSpell.blockCheckingWhenTextChunkSizeGreaterThan": 1000,
"cSpell.enableFiletypes": [
"cpp",
"xml",
"msbuild",
"javascript"

Просмотреть файл

@ -41,6 +41,7 @@
"gcdumps",
"globbing",
"globstar",
"HCORENUM",
"hmac",
"HMACSHA",
"hresult",
@ -63,7 +64,9 @@
"NTLM",
"nupkg",
"openapi",
"PCCOR",
"Pids",
"PINVOKE",
"PKCS",
"quantile",
"rclsid",
@ -85,6 +88,8 @@
"trce",
"tstr",
"ukwn",
"Uninstallation",
"uniquifier",
"Unlocalized",
"Unredacted",
"upvoting",
@ -115,7 +120,11 @@
},
{
"name": "error_codes",
"pattern": "/(_| )E_\\w+/gm"
"pattern": "/[A-Z_]+(E|S)_\\w+/gm"
},
{
"name": "il_opcodes",
"pattern": "/CEE_[A-Z_]+/gm"
},
{
"name": "xml_comment_block",
@ -150,6 +159,7 @@
"allowCompoundWords": true,
"ignoreRegExpList": [
"preprocessor_conditional",
"il_opcodes",
"error_codes"
]
},

Просмотреть файл

@ -28,6 +28,9 @@ set(SOURCES
MainProfiler/ThreadDataManager.cpp
Stacks/StacksEventProvider.cpp
Stacks/StackSampler.cpp
ProbeInstrumentation/AssemblyProbePrep.cpp
ProbeInstrumentation/ProbeInstrumentation.cpp
ProbeInstrumentation/ProbeInjector.cpp
Utilities/ILRewriter.cpp
Utilities/NameCache.cpp
Utilities/ThreadNameCache.cpp

Просмотреть файл

@ -9,6 +9,7 @@
#include "../Logging/StdErrLogger.h"
#include "../Stacks/StacksEventProvider.h"
#include "../Stacks/StackSampler.h"
#include "../ProbeInstrumentation/ProbeInstrumentation.h"
#include "../Utilities/ThreadUtilities.h"
#include "corhlpr.h"
#include "macros.h"
@ -18,6 +19,8 @@ using namespace std;
#define IfFailLogRet(EXPR) IfFailLogRet_(m_pLogger, EXPR)
shared_ptr<MainProfiler> MainProfiler::s_profiler;
GUID MainProfiler::GetClsid()
{
// {6A494330-5848-4A23-9D87-0E57BBF6DE79}
@ -33,6 +36,8 @@ STDMETHODIMP MainProfiler::Initialize(IUnknown *pICorProfilerInfoUnk)
// These should always be initialized first
IfFailRet(ProfilerBase::Initialize(pICorProfilerInfoUnk));
MainProfiler::s_profiler = shared_ptr<MainProfiler>(this);
IfFailRet(InitializeCommon());
return S_OK;
@ -42,6 +47,10 @@ STDMETHODIMP MainProfiler::Shutdown()
{
m_pLogger.reset();
m_pEnvironment.reset();
m_pProbeInstrumentation->ShutdownBackgroundService();
m_pProbeInstrumentation.reset();
_commandServer->Shutdown();
_commandServer.reset();
@ -140,7 +149,8 @@ STDMETHODIMP MainProfiler::LoadAsNotificationOnly(BOOL *pbNotificationOnly)
{
ExpectedPtr(pbNotificationOnly);
*pbNotificationOnly = TRUE;
// JSFIX: Split this into two profilers, one notification-only and one capable of mutating the target app.
*pbNotificationOnly = FALSE;
return S_OK;
}
@ -163,6 +173,9 @@ HRESULT MainProfiler::InitializeCommon()
IfNullRet(_exceptionTracker);
#endif // DOTNETMONITOR_FEATURE_EXCEPTIONS
m_pProbeInstrumentation.reset(new (nothrow) ProbeInstrumentation(m_pLogger, m_pCorProfilerInfo));
IfNullRet(m_pProbeInstrumentation);
// Set product version environment variable to allow discovery of if the profiler
// as been applied to a target process. Diagnostic tools must use the diagnostic
// communication channel's GetProcessEnvironment command to get this value.
@ -174,6 +187,8 @@ HRESULT MainProfiler::InitializeCommon()
_exceptionTracker->AddProfilerEventMask(eventsLow);
#endif // DOTNETMONITOR_FEATURE_EXCEPTIONS
StackSampler::AddProfilerEventMask(eventsLow);
m_pProbeInstrumentation->AddProfilerEventMask(eventsLow);
_threadNameCache = make_shared<ThreadNameCache>();
@ -181,6 +196,8 @@ HRESULT MainProfiler::InitializeCommon()
eventsLow,
COR_PRF_HIGH_MONITOR::COR_PRF_HIGH_MONITOR_NONE));
IfFailLogRet(m_pProbeInstrumentation->InitBackgroundService());
//Initialize this last. The CommandServer creates secondary threads, which will be difficult to cleanup if profiler initialization fails.
IfFailLogRet(InitializeCommandServer());
@ -314,3 +331,42 @@ HRESULT MainProfiler::ProcessCallstackMessage()
return S_OK;
}
HRESULT STDMETHODCALLTYPE MainProfiler::GetReJITParameters(ModuleID moduleId, mdMethodDef methodId, ICorProfilerFunctionControl* pFunctionControl)
{
return m_pProbeInstrumentation->GetReJITParameters(moduleId, methodId, pFunctionControl);
}
HRESULT STDMETHODCALLTYPE MainProfiler::RequestFunctionProbeUninstallation()
{
return m_pProbeInstrumentation->RequestFunctionProbeUninstallation();
}
HRESULT STDMETHODCALLTYPE MainProfiler::RegisterFunctionProbe(FunctionID enterProbeId)
{
return m_pProbeInstrumentation->RegisterFunctionProbe(enterProbeId);
}
HRESULT STDMETHODCALLTYPE MainProfiler::RequestFunctionProbeInstallation(ULONG64 functionIds[], ULONG32 count, ULONG32 argumentBoxingTypes[], ULONG32 argumentCounts[])
{
return m_pProbeInstrumentation->RequestFunctionProbeInstallation(functionIds, count, argumentBoxingTypes, argumentCounts);
}
#ifndef DLLEXPORT
#define DLLEXPORT
#endif
STDAPI DLLEXPORT RegisterFunctionProbe(ULONG64 enterProbeId)
{
return MainProfiler::s_profiler->RegisterFunctionProbe((FunctionID)enterProbeId);
}
STDAPI DLLEXPORT RequestFunctionProbeInstallation(ULONG64 functionIds[], ULONG32 count, ULONG32 argumentBoxingTypes[], ULONG32 argumentCounts[])
{
return MainProfiler::s_profiler->RequestFunctionProbeInstallation(functionIds, count, argumentBoxingTypes, argumentCounts);
}
STDAPI DLLEXPORT RequestFunctionProbeUninstallation()
{
return MainProfiler::s_profiler->RequestFunctionProbeUninstallation();
}

Просмотреть файл

@ -8,6 +8,7 @@
#include "../Environment/EnvironmentHelper.h"
#include "../Logging/Logger.h"
#include "../Communication/CommandServer.h"
#include "../ProbeInstrumentation/ProbeInstrumentation.h"
#include "../Utilities/ThreadNameCache.h"
#include <memory>
@ -19,6 +20,9 @@
class MainProfiler final :
public ProfilerBase
{
public:
static std::shared_ptr<MainProfiler> s_profiler;
private:
std::shared_ptr<IEnvironment> m_pEnvironment;
std::shared_ptr<EnvironmentHelper> _environmentHelper;
@ -28,6 +32,8 @@ private:
std::shared_ptr<ThreadDataManager> _threadDataManager;
std::unique_ptr<ExceptionTracker> _exceptionTracker;
#endif // DOTNETMONITOR_FEATURE_EXCEPTIONS
std::unique_ptr<ProbeInstrumentation> m_pProbeInstrumentation;
public:
static GUID GetClsid();
@ -42,6 +48,12 @@ public:
STDMETHOD(ExceptionUnwindFunctionEnter)(FunctionID functionId) override;
STDMETHOD(InitializeForAttach)(IUnknown* pCorProfilerInfoUnk, void* pvClientData, UINT cbClientData) override;
STDMETHOD(LoadAsNotificationOnly)(BOOL *pbNotificationOnly) override;
STDMETHOD(GetReJITParameters)(ModuleID moduleId, mdMethodDef methodId, ICorProfilerFunctionControl* pFunctionControl) override;
public:
STDMETHOD(RegisterFunctionProbe)(FunctionID enterProbeId);
STDMETHOD(RequestFunctionProbeInstallation)(ULONG64 functionIds[], ULONG32 count, ULONG32 argumentBoxingTypes[], ULONG32 argumentCounts[]);
STDMETHOD(RequestFunctionProbeUninstallation)();
private:
HRESULT InitializeCommon();

Просмотреть файл

@ -1,7 +1,10 @@
LIBRARY MonitorProfiler
EXPORTS
DllCanUnloadNow PRIVATE
DllGetClassObject PRIVATE
DllMain PRIVATE
TestHook PRIVATE
DllCanUnloadNow PRIVATE
DllGetClassObject PRIVATE
DllMain PRIVATE
TestHook PRIVATE
RegisterFunctionProbe PRIVATE
RequestFunctionProbeInstallation PRIVATE
RequestFunctionProbeUninstallation PRIVATE

Просмотреть файл

@ -0,0 +1,457 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "corhlpr.h"
#include "AssemblyProbePrep.h"
#include "../Utilities/TypeNameUtilities.h"
#include "../Utilities/MetadataEnumCloser.h"
#include "../Utilities/StringUtilities.h"
using namespace std;
#define ENUM_BUFFER_SIZE 10
#define STRING_BUFFER_LEN 256
AssemblyProbePrep::AssemblyProbePrep(ICorProfilerInfo12* profilerInfo, FunctionID probeFunctionId) :
m_pCorProfilerInfo(profilerInfo),
m_resolvedCorLibId(0),
m_probeFunctionId(probeFunctionId),
m_didHydrateProbeCache(false)
{
}
bool AssemblyProbePrep::TryGetAssemblyPrepData(ModuleID moduleId, shared_ptr<AssemblyProbePrepData>& data)
{
auto const& it = m_assemblyProbeCache.find(moduleId);
if (it != m_assemblyProbeCache.end())
{
data = it->second;
return true;
}
return false;
}
HRESULT AssemblyProbePrep::PrepareAssemblyForProbes(ModuleID moduleId)
{
HRESULT hr;
auto const& it = m_assemblyProbeCache.find(moduleId);
if (it != m_assemblyProbeCache.end())
{
return S_OK;
}
COR_LIB_TYPE_TOKENS corLibTypeTokens = {};
IfFailRet(EmitNecessaryCorLibTypeTokens(moduleId, corLibTypeTokens));
mdMemberRef probeMemberRef;
IfFailRet(EmitProbeReference(moduleId, probeMemberRef));
shared_ptr<AssemblyProbePrepData> data(new (nothrow) AssemblyProbePrepData(probeMemberRef, corLibTypeTokens));
IfNullRet(data);
m_assemblyProbeCache.insert({moduleId, data});
return S_OK;
}
HRESULT AssemblyProbePrep::EmitProbeReference(
ModuleID moduleId,
mdMemberRef& probeMemberRef)
{
HRESULT hr;
probeMemberRef = mdMemberRefNil;
IfFailRet(HydrateProbeMetadata());
std::shared_ptr<FunctionData> probeFunctionData;
std::shared_ptr<ModuleData> probeModuleData;
if (!m_nameCache.TryGetFunctionData(m_probeFunctionId, probeFunctionData) ||
!m_nameCache.TryGetModuleData(probeFunctionData->GetModuleId(), probeModuleData))
{
return E_UNEXPECTED;
}
ComPtr<IMetaDataEmit> pMetadataEmit;
IfFailRet(m_pCorProfilerInfo->GetModuleMetaData(
moduleId,
ofRead | ofWrite,
IID_IMetaDataEmit,
reinterpret_cast<IUnknown **>(&pMetadataEmit)));
ComPtr<IMetaDataAssemblyEmit> pMetadataAssemblyEmit;
mdAssemblyRef probeAssemblyRefToken = mdAssemblyRefNil;
IfFailRet(pMetadataEmit->QueryInterface(IID_IMetaDataAssemblyEmit, reinterpret_cast<void **>(&pMetadataAssemblyEmit)));
IfFailRet(pMetadataAssemblyEmit->DefineAssemblyRef(
reinterpret_cast<const void *>(m_probeCache.publicKey.data()),
static_cast<ULONG>(m_probeCache.publicKey.size()),
m_probeCache.assemblyName.c_str(),
&m_probeCache.assemblyMetadata,
nullptr,
0,
m_probeCache.assemblyFlags,
&probeAssemblyRefToken));
tstring className;
IfFailRet(m_nameCache.GetFullyQualifiedClassName(probeFunctionData->GetClass(), className));
mdTypeRef classTypeRef;
IfFailRet(pMetadataEmit->DefineTypeRefByName(
probeAssemblyRefToken,
className.c_str(),
&classTypeRef));
mdMemberRef memberRef;
IfFailRet(pMetadataEmit->DefineMemberRef(
classTypeRef,
probeFunctionData->GetName().c_str(),
m_probeCache.signature.data(),
static_cast<ULONG>(m_probeCache.signature.size()),
&memberRef));
probeMemberRef = memberRef;
return S_OK;
}
HRESULT AssemblyProbePrep::EmitNecessaryCorLibTypeTokens(
ModuleID moduleId,
COR_LIB_TYPE_TOKENS& corLibTypeTokens)
{
HRESULT hr;
ComPtr<IMetaDataImport> pMetadataImport;
IfFailRet(m_pCorProfilerInfo->GetModuleMetaData(
moduleId,
ofRead,
IID_IMetaDataImport,
reinterpret_cast<IUnknown **>(&pMetadataImport)));
ComPtr<IMetaDataEmit> pMetadataEmit;
IfFailRet(m_pCorProfilerInfo->GetModuleMetaData(
moduleId,
ofRead | ofWrite,
IID_IMetaDataEmit,
reinterpret_cast<IUnknown **>(&pMetadataEmit)));
mdAssemblyRef corlibAssemblyRef;
IfFailRet(GetOrEmitTokenForCorLibAssemblyRef(
pMetadataImport,
pMetadataEmit,
corlibAssemblyRef));
#define GET_OR_DEFINE_TYPE_TOKEN(name, token) \
IfFailRet(GetTokenForType( \
pMetadataEmit, \
corlibAssemblyRef, \
name, \
token))
GET_OR_DEFINE_TYPE_TOKEN(_T("System.Boolean"), corLibTypeTokens.systemBooleanType);
GET_OR_DEFINE_TYPE_TOKEN(_T("System.Byte"), corLibTypeTokens.systemByteType);
GET_OR_DEFINE_TYPE_TOKEN(_T("System.Char"), corLibTypeTokens.systemCharType);
GET_OR_DEFINE_TYPE_TOKEN(_T("System.Double"), corLibTypeTokens.systemDoubleType);
GET_OR_DEFINE_TYPE_TOKEN(_T("System.Int16"), corLibTypeTokens.systemInt16Type);
GET_OR_DEFINE_TYPE_TOKEN(_T("System.Int32"), corLibTypeTokens.systemInt32Type);
GET_OR_DEFINE_TYPE_TOKEN(_T("System.Int64"), corLibTypeTokens.systemInt64Type);
GET_OR_DEFINE_TYPE_TOKEN(_T("System.Object"), corLibTypeTokens.systemObjectType);
GET_OR_DEFINE_TYPE_TOKEN(_T("System.SByte"), corLibTypeTokens.systemSByteType);
GET_OR_DEFINE_TYPE_TOKEN(_T("System.Single"), corLibTypeTokens.systemSingleType);
GET_OR_DEFINE_TYPE_TOKEN(_T("System.UInt16"), corLibTypeTokens.systemUInt16Type);
GET_OR_DEFINE_TYPE_TOKEN(_T("System.UInt32"), corLibTypeTokens.systemUInt32Type);
GET_OR_DEFINE_TYPE_TOKEN(_T("System.UInt64"), corLibTypeTokens.systemUInt64Type);
return S_OK;
}
HRESULT AssemblyProbePrep::GetTokenForType(
IMetaDataEmit* pMetadataEmit,
mdToken resolutionScope,
tstring name,
mdToken& typeToken)
{
IfNullRet(pMetadataEmit);
HRESULT hr;
typeToken = mdTokenNil;
mdTypeRef typeRefToken;
// DefineTypeRefByName will return an existing token if available.
IfFailRet(pMetadataEmit->DefineTypeRefByName(
resolutionScope,
name.c_str(),
&typeRefToken));
typeToken = typeRefToken;
return S_OK;
}
HRESULT AssemblyProbePrep::GetOrEmitTokenForCorLibAssemblyRef(
IMetaDataImport* pMetadataImport,
IMetaDataEmit* pMetadataEmit,
mdAssemblyRef& corlibAssemblyRef)
{
IfNullRet(pMetadataImport);
IfNullRet(pMetadataEmit);
HRESULT hr;
corlibAssemblyRef = mdAssemblyRefNil;
IfFailRet(HydrateResolvedCorLib());
ComPtr<IMetaDataAssemblyImport> pMetadataAssemblyImport;
IfFailRet(pMetadataImport->QueryInterface(IID_IMetaDataAssemblyImport, reinterpret_cast<void **>(&pMetadataAssemblyImport)));
mdAssemblyRef mdRefs[ENUM_BUFFER_SIZE];
const ULONG expectedLength = (ULONG)m_resolvedCorLibName.length();
unique_ptr<WCHAR[]> assemblyName(new (nothrow) WCHAR[expectedLength]);
IfNullRet(assemblyName);
MetadataEnumCloser<IMetaDataAssemblyImport> enumCloser(pMetadataAssemblyImport, NULL);
ULONG count = 0;
while ((hr = pMetadataAssemblyImport->EnumAssemblyRefs(enumCloser.GetEnumPtr(), mdRefs, ENUM_BUFFER_SIZE, &count)) == S_OK)
{
for (ULONG i = 0; i < count; i++)
{
mdAssemblyRef curRef = mdRefs[i];
ULONG nameLength = 0;
hr = pMetadataAssemblyImport->GetAssemblyRefProps(
curRef,
nullptr,
nullptr,
assemblyName.get(),
expectedLength,
&nameLength,
nullptr,
nullptr,
nullptr,
nullptr);
if (hr == CLDB_S_TRUNCATION)
{
// Current assembly's name is longer than corlib's
continue;
}
else if (hr != S_OK)
{
return hr;
}
if (nameLength != expectedLength)
{
continue;
}
tstring assemblyNameStr = tstring(assemblyName.get());
if (assemblyNameStr == m_resolvedCorLibName)
{
corlibAssemblyRef = curRef;
return S_OK;
}
}
}
IfFailRet(EmitCorLibAssemblyRef(pMetadataEmit, corlibAssemblyRef));
return S_OK;
}
HRESULT AssemblyProbePrep::EmitCorLibAssemblyRef(IMetaDataEmit* pMetadataEmit, mdAssemblyRef& corlibAssemblyRef)
{
IfNullRet(pMetadataEmit);
HRESULT hr;
corlibAssemblyRef = mdAssemblyRefNil;
ComPtr<IMetaDataAssemblyEmit> pMetadataAssemblyEmit;
IfFailRet(pMetadataEmit->QueryInterface(IID_IMetaDataAssemblyEmit, reinterpret_cast<void **>(&pMetadataAssemblyEmit)));
BYTE publicKeyToken[] = { 0x7c, 0xec, 0x85, 0xd7, 0xbe, 0xa7, 0x79, 0x8e };
ASSEMBLYMETADATA corLibMetadata = {};
corLibMetadata.usMajorVersion = 4;
mdAssemblyRef newAssemblyRef;
IfFailRet(pMetadataAssemblyEmit->DefineAssemblyRef(
publicKeyToken,
sizeof(publicKeyToken),
m_resolvedCorLibName.c_str(),
&corLibMetadata,
nullptr,
0,
afContentType_Default,
&newAssemblyRef));
corlibAssemblyRef = newAssemblyRef;
return S_OK;
}
HRESULT AssemblyProbePrep::HydrateResolvedCorLib()
{
if (m_resolvedCorLibId != 0)
{
return S_OK;
}
HRESULT hr;
ModuleID corLibId = 0;
ComPtr<ICorProfilerModuleEnum> pEnum;
ModuleID curModuleId;
mdTypeDef sysObjectTypeDef = mdTypeDefNil;
IfFailRet(m_pCorProfilerInfo->EnumModules(&pEnum));
while (pEnum->Next(1, &curModuleId, NULL) == S_OK)
{
//
// Determine the identity of the System assembly by querying if the Assembly defines the
// well known type "System.Object" as that type must be defined by the System assembly
//
mdTypeDef objectTypeDef = mdTypeDefNil;
ComPtr<IMetaDataImport> pMetadataImport;
hr = m_pCorProfilerInfo->GetModuleMetaData(
curModuleId,
ofRead,
IID_IMetaDataImport,
reinterpret_cast<IUnknown **>(&pMetadataImport));
if (hr != S_OK)
{
continue;
}
if (pMetadataImport->FindTypeDefByName(_T("System.Object"), mdTokenNil, &objectTypeDef) != S_OK)
{
continue;
}
DWORD classAttributes = 0;
mdToken extendsToken = mdTokenNil;
if (pMetadataImport->GetTypeDefProps(
objectTypeDef,
nullptr,
0,
nullptr,
&classAttributes,
&extendsToken) != S_OK)
{
continue;
}
//
// Also check the type properties to make sure it is a class and not a value-type definition
// and that this type definition isn't extending another type.
//
bool doesExtend = pMetadataImport->IsValidToken(extendsToken);
bool isClass = ((classAttributes & tdClassSemanticsMask) == tdClass);
if (isClass && !doesExtend)
{
corLibId = curModuleId;
sysObjectTypeDef = objectTypeDef;
break;
}
}
if (corLibId == 0 || sysObjectTypeDef == mdTypeDefNil)
{
return E_FAIL;
}
tstring corLibName;
TypeNameUtilities nameUtilities(m_pCorProfilerInfo);
nameUtilities.CacheModuleNames(m_nameCache, corLibId);
std::shared_ptr<ModuleData> moduleData;
if (!m_nameCache.TryGetModuleData(corLibId, moduleData))
{
return E_UNEXPECTED;
}
corLibName = moduleData->GetName();
// Trim the .dll file extension
const tstring dllExtension = _T(".dll");
if (StringUtilities::EndsWithCaseInsensitive(corLibName, dllExtension))
{
corLibName.erase(corLibName.length() - dllExtension.length());
}
m_resolvedCorLibName = corLibName;
m_resolvedCorLibId = corLibId;
return S_OK;
}
HRESULT AssemblyProbePrep::HydrateProbeMetadata()
{
if (m_didHydrateProbeCache)
{
return S_OK;
}
HRESULT hr;
TypeNameUtilities typeNameUtilities(m_pCorProfilerInfo);
IfFailRet(typeNameUtilities.CacheNames(m_nameCache, m_probeFunctionId, NULL));
std::shared_ptr<FunctionData> probeFunctionData;
std::shared_ptr<ModuleData> probeModuleData;
if (!m_nameCache.TryGetFunctionData(m_probeFunctionId, probeFunctionData) ||
!m_nameCache.TryGetModuleData(probeFunctionData->GetModuleId(), probeModuleData))
{
return E_UNEXPECTED;
}
ComPtr<IMetaDataImport> pProbeMetadataImport;
IfFailRet(m_pCorProfilerInfo->GetModuleMetaData(
probeFunctionData->GetModuleId(),
ofRead,
IID_IMetaDataImport,
reinterpret_cast<IUnknown **>(&pProbeMetadataImport)));
ComPtr<IMetaDataAssemblyImport> pProbeAssemblyImport;
IfFailRet(pProbeMetadataImport->QueryInterface(IID_IMetaDataAssemblyImport, reinterpret_cast<void **>(&pProbeAssemblyImport)));
mdAssembly probeAssemblyToken;
IfFailRet(pProbeAssemblyImport->GetAssemblyFromScope(&probeAssemblyToken));
const BYTE *pPublicKey;
ULONG publicKeyLength;
ASSEMBLYMETADATA metadata = {};
DWORD assemblyFlags;
WCHAR assemblyName[STRING_BUFFER_LEN];
IfFailRet(pProbeAssemblyImport->GetAssemblyProps(
probeAssemblyToken,
(const void **)&pPublicKey,
&publicKeyLength,
nullptr,
assemblyName,
STRING_BUFFER_LEN,
nullptr,
&metadata,
&assemblyFlags));
m_probeCache.assemblyFlags = assemblyFlags;
m_probeCache.assemblyMetadata = metadata;
m_probeCache.assemblyName = tstring(assemblyName);
m_probeCache.publicKey = vector<BYTE>(pPublicKey, pPublicKey + publicKeyLength);
PCCOR_SIGNATURE pProbeSignature;
ULONG signatureLength;
IfFailRet(pProbeMetadataImport->GetMethodProps(
probeFunctionData->GetMethodToken(),
nullptr,
nullptr,
NULL,
nullptr,
nullptr,
&pProbeSignature,
&signatureLength,
nullptr,
nullptr));
m_probeCache.signature = vector<BYTE>(pProbeSignature, pProbeSignature + signatureLength);
m_didHydrateProbeCache = true;
return S_OK;
}

Просмотреть файл

@ -0,0 +1,113 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#pragma once
#include "cor.h"
#include "corprof.h"
#include "tstring.h"
#include "../Logging/Logger.h"
#include "../Utilities/NameCache.h"
#include <unordered_map>
#include <vector>
#include <memory>
typedef struct _COR_LIB_TYPE_TOKENS
{
mdToken
systemBooleanType,
systemByteType,
systemCharType,
systemDoubleType,
systemInt16Type,
systemInt32Type,
systemInt64Type,
systemObjectType,
systemSByteType,
systemSingleType,
systemUInt16Type,
systemUInt32Type,
systemUInt64Type;
} COR_LIB_TYPE_TOKENS;
class AssemblyProbePrepData
{
public:
AssemblyProbePrepData(mdMemberRef probeMemberRef, COR_LIB_TYPE_TOKENS corLibTypeTokens) :
m_probeMemberRef(probeMemberRef), m_corLibTypeTokens(corLibTypeTokens)
{
}
const mdMemberRef GetProbeMemberRef() const { return m_probeMemberRef; }
const COR_LIB_TYPE_TOKENS& GetCorLibTypeTokens() const { return m_corLibTypeTokens; }
private:
mdMemberRef m_probeMemberRef;
COR_LIB_TYPE_TOKENS m_corLibTypeTokens;
};
typedef struct _PROBE_INFO_CACHE
{
tstring assemblyName;
std::vector<BYTE> signature;
std::vector<BYTE> publicKey;
ASSEMBLYMETADATA assemblyMetadata;
DWORD assemblyFlags;
} PROBE_INFO_CACHE;
class AssemblyProbePrep
{
private:
ICorProfilerInfo12* m_pCorProfilerInfo;
NameCache m_nameCache;
ModuleID m_resolvedCorLibId;
tstring m_resolvedCorLibName;
FunctionID m_probeFunctionId;
bool m_didHydrateProbeCache;
PROBE_INFO_CACHE m_probeCache;
std::unordered_map<ModuleID, std::shared_ptr<AssemblyProbePrepData>> m_assemblyProbeCache;
public:
AssemblyProbePrep(
ICorProfilerInfo12* profilerInfo,
FunctionID probeFunctionId);
HRESULT PrepareAssemblyForProbes(
ModuleID moduleId);
bool TryGetAssemblyPrepData(
ModuleID moduleId,
std::shared_ptr<AssemblyProbePrepData>& data);
private:
HRESULT HydrateResolvedCorLib();
HRESULT HydrateProbeMetadata();
HRESULT GetTokenForType(
IMetaDataEmit* pMetadataEmit,
mdToken resolutionScope,
tstring name,
mdToken& typeToken);
HRESULT EmitProbeReference(
ModuleID moduleId,
mdMemberRef& probeMemberRef);
HRESULT EmitNecessaryCorLibTypeTokens(
ModuleID moduleId,
COR_LIB_TYPE_TOKENS& pCorLibTypeTokens);
HRESULT GetOrEmitTokenForCorLibAssemblyRef(
IMetaDataImport* pMetadataImport,
IMetaDataEmit* pMetadataEmit,
mdAssemblyRef& corlibAssemblyRef);
HRESULT EmitCorLibAssemblyRef(
IMetaDataEmit* pMetadataEmit,
mdAssemblyRef& corlibAssemblyRef);
};

Просмотреть файл

@ -0,0 +1,213 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "cor.h"
#include "corprof.h"
#include "ProbeInjector.h"
#include "../Utilities/ILRewriter.h"
#include <vector>
//
// SpecialCaseBoxingTypes shares the same format as other mdTokens.
// Instrumentation requests provide special boxing instructions by using SpecialCaseBoxingTypeFlag
// as the "token type" and the SpecialCaseBoxingTypes enum as the RID.
//
constexpr ULONG32 SpecialCaseBoxingTypeFlag = 0x7f000000;
enum class SpecialCaseBoxingTypes : ULONG32
{
TYPE_UNKNOWN = 0,
TYPE_OBJECT,
TYPE_BOOLEAN,
TYPE_CHAR,
TYPE_SBYTE,
TYPE_BYTE,
TYPE_INT16,
TYPE_UINT16,
TYPE_INT32,
TYPE_UINT32,
TYPE_INT64,
TYPE_UINT64,
TYPE_SINGLE,
TYPE_DOUBLE
};
HRESULT ProbeInjector::InstallProbe(
ICorProfilerInfo* pICorProfilerInfo,
ICorProfilerFunctionControl* pICorProfilerFunctionControl,
const INSTRUMENTATION_REQUEST& request)
{
IfNullRet(pICorProfilerInfo);
IfNullRet(pICorProfilerFunctionControl);
if (request.boxingTypes.size() > UINT32_MAX)
{
return E_INVALIDARG;
}
HRESULT hr;
ILRewriter rewriter(pICorProfilerInfo, pICorProfilerFunctionControl, request.moduleId, request.methodDef);
IfFailRet(rewriter.Import());
const COR_LIB_TYPE_TOKENS& corLibTypeTokens = request.pAssemblyData->GetCorLibTypeTokens();
//
// JSFIX: Wrap the probe in a try/catch.
// Consider: In the catch, try/catch a PINVOKE into the profiler,
// notifying that a probe exception occurred and the probes need to be uninstalled.
//
ILInstr* pInsertProbeBeforeThisInstr = rewriter.GetILList()->m_pNext;
ILInstr* pNewInstr = nullptr;
UINT32 numArgs = static_cast<UINT32>(request.boxingTypes.size());
//
// The below IL is equivalent to: ProbeFunction(uniquifier, new object[] { arg1, arg2, ... })
// When an argument isn't supported, pass null in its place.
//
/* uniquifier */
pNewInstr = rewriter.NewILInstr();
pNewInstr->m_opcode = CEE_LDC_I8;
pNewInstr->m_Arg64 = request.uniquifier;
rewriter.InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
/* Args */
// Size of array
pNewInstr = rewriter.NewILInstr();
pNewInstr->m_opcode = CEE_LDC_I4;
pNewInstr->m_Arg32 = numArgs;
rewriter.InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
// Create the array
pNewInstr = rewriter.NewILInstr();
pNewInstr->m_opcode = CEE_NEWARR;
pNewInstr->m_Arg32 = corLibTypeTokens.systemObjectType;
rewriter.InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
for (UINT32 i = 0; i < numArgs; i++)
{
// New entry on the evaluation stack
pNewInstr = rewriter.NewILInstr();
pNewInstr->m_opcode = CEE_DUP;
rewriter.InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
// Index to set
pNewInstr = rewriter.NewILInstr();
pNewInstr->m_opcode = CEE_LDC_I4;
pNewInstr->m_Arg32 = i;
rewriter.InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
// Load arg
ULONG32 typeInfo = request.boxingTypes.at(i);
if (typeInfo == (SpecialCaseBoxingTypeFlag | static_cast<ULONG32>(SpecialCaseBoxingTypes::TYPE_UNKNOWN)))
{
pNewInstr = rewriter.NewILInstr();
pNewInstr->m_opcode = CEE_LDNULL;
rewriter.InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
}
else
{
pNewInstr = rewriter.NewILInstr();
pNewInstr->m_opcode = CEE_LDARG_S;
pNewInstr->m_Arg32 = i;
rewriter.InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
// Resolve the boxing token.
mdToken boxedTypeToken;
IfFailRet(GetBoxingToken(typeInfo, corLibTypeTokens, boxedTypeToken));
if (boxedTypeToken != mdTokenNil)
{
pNewInstr = rewriter.NewILInstr();
pNewInstr->m_opcode = CEE_BOX;
pNewInstr->m_Arg32 = boxedTypeToken;
rewriter.InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
}
}
// Replace the i'th element in our new array with what we just pushed on the stack
pNewInstr = rewriter.NewILInstr();
pNewInstr->m_opcode = CEE_STELEM_REF;
rewriter.InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
}
pNewInstr = rewriter.NewILInstr();
pNewInstr->m_opcode = CEE_CALL;
pNewInstr->m_Arg32 = request.pAssemblyData->GetProbeMemberRef();
rewriter.InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
IfFailRet(rewriter.Export());
return S_OK;
}
HRESULT ProbeInjector::GetBoxingToken(
UINT32 typeInfo,
const COR_LIB_TYPE_TOKENS& corLibTypeTokens,
mdToken& boxedType)
{
boxedType = mdTokenNil;
//
// typeInfo is either:
// 1. A real metadata token that is what should be used for boxing.
// 2. A special case (one of the SpecialCaseBoxingTypes enum values)
//
if (TypeFromToken(typeInfo) != SpecialCaseBoxingTypeFlag)
{
boxedType = static_cast<mdToken>(typeInfo);
return S_OK;
}
switch(static_cast<SpecialCaseBoxingTypes>(RidFromToken(typeInfo)))
{
case SpecialCaseBoxingTypes::TYPE_BOOLEAN:
boxedType = corLibTypeTokens.systemBooleanType;
break;
case SpecialCaseBoxingTypes::TYPE_BYTE:
boxedType = corLibTypeTokens.systemByteType;
break;
case SpecialCaseBoxingTypes::TYPE_CHAR:
boxedType = corLibTypeTokens.systemCharType;
break;
case SpecialCaseBoxingTypes::TYPE_DOUBLE:
boxedType = corLibTypeTokens.systemDoubleType;
break;
case SpecialCaseBoxingTypes::TYPE_INT16:
boxedType = corLibTypeTokens.systemInt16Type;
break;
case SpecialCaseBoxingTypes::TYPE_INT32:
boxedType = corLibTypeTokens.systemInt32Type;
break;
case SpecialCaseBoxingTypes::TYPE_INT64:
boxedType = corLibTypeTokens.systemInt64Type;
break;
case SpecialCaseBoxingTypes::TYPE_SBYTE:
boxedType = corLibTypeTokens.systemSByteType;
break;
case SpecialCaseBoxingTypes::TYPE_SINGLE:
boxedType = corLibTypeTokens.systemSingleType;
break;
case SpecialCaseBoxingTypes::TYPE_UINT16:
boxedType = corLibTypeTokens.systemUInt16Type;
break;
case SpecialCaseBoxingTypes::TYPE_UINT32:
boxedType = corLibTypeTokens.systemUInt32Type;
break;
case SpecialCaseBoxingTypes::TYPE_UINT64:
boxedType = corLibTypeTokens.systemUInt64Type;
break;
case SpecialCaseBoxingTypes::TYPE_OBJECT:
// No boxing needed.
break;
case SpecialCaseBoxingTypes::TYPE_UNKNOWN:
default:
return E_FAIL;
}
return S_OK;
}

Просмотреть файл

@ -0,0 +1,37 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#pragma once
#include "corprof.h"
#include "corhdr.h"
#include "AssemblyProbePrep.h"
#include <vector>
#include <memory>
typedef struct _INSTRUMENTATION_REQUEST
{
ULONG64 uniquifier;
std::vector<ULONG32> boxingTypes;
ModuleID moduleId;
mdMethodDef methodDef;
std::shared_ptr<AssemblyProbePrepData> pAssemblyData;
} INSTRUMENTATION_REQUEST;
class ProbeInjector
{
public:
static HRESULT InstallProbe(
ICorProfilerInfo* pICorProfilerInfo,
ICorProfilerFunctionControl* pICorProfilerFunctionControl,
const INSTRUMENTATION_REQUEST& request);
private:
static HRESULT GetBoxingToken(
ULONG32 typeInfo,
const COR_LIB_TYPE_TOKENS& corLibTypeTokens,
mdToken& boxedType);
};

Просмотреть файл

@ -0,0 +1,313 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#include "corhlpr.h"
#include "ProbeInstrumentation.h"
using namespace std;
#define IfFailLogRet(EXPR) IfFailLogRet_(m_pLogger, EXPR)
ProbeInstrumentation::ProbeInstrumentation(const shared_ptr<ILogger>& logger, ICorProfilerInfo12* profilerInfo) :
m_pCorProfilerInfo(profilerInfo),
m_pLogger(logger),
m_probeFunctionId(0),
m_pAssemblyProbePrep(nullptr)
{
}
HRESULT ProbeInstrumentation::RegisterFunctionProbe(FunctionID enterProbeId)
{
lock_guard<mutex> lock(m_probePinningMutex);
if (HasRegisteredProbe())
{
m_pLogger->Log(LogLevel::Debug, _LS("Received probes but they have already been registered"));
return E_FAIL;
}
m_pLogger->Log(LogLevel::Debug, _LS("Received probes"));
m_pAssemblyProbePrep.reset(new (nothrow) AssemblyProbePrep(m_pCorProfilerInfo, enterProbeId));
IfNullRet(m_pAssemblyProbePrep);
// JSFIX: Validate the probe's signature before pinning it.
m_probeFunctionId = enterProbeId;
return S_OK;
}
HRESULT ProbeInstrumentation::InitBackgroundService()
{
m_probeManagementThread = thread(&ProbeInstrumentation::WorkerThread, this);
return S_OK;
}
void ProbeInstrumentation::WorkerThread()
{
HRESULT hr = m_pCorProfilerInfo->InitializeCurrentThread();
if (FAILED(hr))
{
m_pLogger->Log(LogLevel::Error, _LS("Unable to initialize thread: 0x%08x"), hr);
return;
}
while (true)
{
PROBE_WORKER_PAYLOAD payload;
hr = m_probeManagementQueue.BlockingDequeue(payload);
if (hr != S_OK)
{
break;
}
switch (payload.instruction)
{
case ProbeWorkerInstruction::INSTALL_PROBES:
hr = InstallProbes(payload.requests);
if (hr != S_OK)
{
m_pLogger->Log(LogLevel::Error, _LS("Failed to install probes: 0x%08x"), hr);
}
break;
case ProbeWorkerInstruction::UNINSTALL_PROBES:
hr = UninstallProbes();
if (hr != S_OK)
{
m_pLogger->Log(LogLevel::Error, _LS("Failed to uninstall probes: 0x%08x"), hr);
}
break;
default:
m_pLogger->Log(LogLevel::Error, _LS("Unknown message"));
break;
}
}
}
void ProbeInstrumentation::ShutdownBackgroundService()
{
m_probeManagementQueue.Complete();
m_probeManagementThread.join();
}
HRESULT ProbeInstrumentation::RequestFunctionProbeInstallation(
ULONG64 functionIds[],
ULONG32 count,
ULONG32 argumentBoxingTypes[],
ULONG32 argumentCounts[])
{
m_pLogger->Log(LogLevel::Debug, _LS("Probe installation requested"));
if (!HasRegisteredProbe())
{
return S_FALSE;
}
//
// This method receives N (where n is "count") function IDs that probes should be installed into.
//
// Along with this, boxing types are provided for every argument in all of the functions, and the number of
// arguments for each function can be found using argumentCounts.
//
// The boxing types are passed in as a flattened multidimensional array (argumentBoxingTypes).
//
//
//
// This method un-flattens the passed in data, reconstructing it into an easier-to-understand format
// before passing off the request to the worker thread.
//
vector<UNPROCESSED_INSTRUMENTATION_REQUEST> requests;
requests.reserve(count);
ULONG32 offset = 0;
for (ULONG32 i = 0; i < count; i++)
{
if (UINT32_MAX - offset < argumentCounts[i])
{
return E_INVALIDARG;
}
vector<ULONG32> tokens;
tokens.reserve(argumentCounts[i]);
for (ULONG32 j = 0; j < argumentCounts[i]; j++)
{
tokens.push_back(argumentBoxingTypes[offset+j]);
}
offset += argumentCounts[i];
UNPROCESSED_INSTRUMENTATION_REQUEST request;
request.functionId = static_cast<FunctionID>(functionIds[i]);
request.boxingTypes = tokens;
requests.push_back(request);
}
m_probeManagementQueue.Enqueue({ProbeWorkerInstruction::INSTALL_PROBES, requests});
return S_OK;
}
HRESULT ProbeInstrumentation::RequestFunctionProbeUninstallation()
{
m_pLogger->Log(LogLevel::Debug, _LS("Probe uninstallation requested"));
if (!HasRegisteredProbe())
{
return S_FALSE;
}
PROBE_WORKER_PAYLOAD payload = {};
payload.instruction = ProbeWorkerInstruction::UNINSTALL_PROBES;
m_probeManagementQueue.Enqueue(payload);
return S_OK;
}
bool ProbeInstrumentation::HasRegisteredProbe()
{
return m_probeFunctionId != 0;
}
HRESULT ProbeInstrumentation::InstallProbes(vector<UNPROCESSED_INSTRUMENTATION_REQUEST>& requests)
{
HRESULT hr;
lock_guard<mutex> lock(m_instrumentationProcessingMutex);
if (!HasRegisteredProbe() ||
AreProbesInstalled())
{
return E_FAIL;
}
unordered_map<pair<ModuleID, mdMethodDef>, INSTRUMENTATION_REQUEST, PairHash<ModuleID, mdMethodDef>> newRequests;
vector<ModuleID> requestedModuleIds;
vector<mdMethodDef> requestedMethodDefs;
// JSFIX: Handle OOM scenarios.
requestedModuleIds.reserve(requests.size());
requestedMethodDefs.reserve(requests.size());
for (auto const& req : requests)
{
INSTRUMENTATION_REQUEST processedRequest;
// For now just use the function id as the uniquifier.
// Consider allowing the caller to specify one.
processedRequest.uniquifier = static_cast<ULONG64>(req.functionId);
processedRequest.boxingTypes = req.boxingTypes;
IfFailLogRet(m_pCorProfilerInfo->GetFunctionInfo2(
req.functionId,
NULL,
nullptr,
&processedRequest.moduleId,
&processedRequest.methodDef,
0,
nullptr,
nullptr));
IfFailLogRet(m_pAssemblyProbePrep->PrepareAssemblyForProbes(processedRequest.moduleId));
requestedModuleIds.push_back(processedRequest.moduleId);
requestedMethodDefs.push_back(processedRequest.methodDef);
if (!m_pAssemblyProbePrep->TryGetAssemblyPrepData(processedRequest.moduleId, processedRequest.pAssemblyData))
{
return E_UNEXPECTED;
}
newRequests.insert({{processedRequest.moduleId, processedRequest.methodDef}, processedRequest});
}
IfFailLogRet(m_pCorProfilerInfo->RequestReJITWithInliners(
COR_PRF_REJIT_BLOCK_INLINING | COR_PRF_REJIT_INLINING_CALLBACKS,
static_cast<ULONG>(requestedModuleIds.size()),
requestedModuleIds.data(),
requestedMethodDefs.data()));
m_activeInstrumentationRequests = newRequests;
return S_OK;
}
HRESULT ProbeInstrumentation::UninstallProbes()
{
HRESULT hr;
lock_guard<mutex> lock(m_instrumentationProcessingMutex);
if (!HasRegisteredProbe() ||
!AreProbesInstalled())
{
return S_FALSE;
}
vector<ModuleID> moduleIds;
vector<mdMethodDef> methodDefs;
moduleIds.reserve(m_activeInstrumentationRequests.size());
methodDefs.reserve(m_activeInstrumentationRequests.size());
for (auto const& requestData: m_activeInstrumentationRequests)
{
auto const& methodInfo = requestData.first;
moduleIds.push_back(methodInfo.first);
methodDefs.push_back(methodInfo.second);
}
IfFailLogRet(m_pCorProfilerInfo->RequestRevert(
static_cast<ULONG>(moduleIds.size()),
moduleIds.data(),
methodDefs.data(),
nullptr));
m_activeInstrumentationRequests.clear();
return S_OK;
}
bool ProbeInstrumentation::AreProbesInstalled()
{
return !m_activeInstrumentationRequests.empty();
}
void ProbeInstrumentation::AddProfilerEventMask(DWORD& eventsLow)
{
eventsLow |= COR_PRF_MONITOR::COR_PRF_ENABLE_REJIT;
}
HRESULT STDMETHODCALLTYPE ProbeInstrumentation::GetReJITParameters(ModuleID moduleId, mdMethodDef methodDef, ICorProfilerFunctionControl* pFunctionControl)
{
HRESULT hr;
INSTRUMENTATION_REQUEST request;
{
lock_guard<mutex> lock(m_instrumentationProcessingMutex);
auto const& it = m_activeInstrumentationRequests.find({moduleId, methodDef});
if (it == m_activeInstrumentationRequests.end())
{
return E_FAIL;
}
request = it->second;
}
hr = ProbeInjector::InstallProbe(
m_pCorProfilerInfo,
pFunctionControl,
request);
if (FAILED(hr))
{
m_pLogger->Log(LogLevel::Error, _LS("Failed to install probes, reverting: 0x%08x"), hr);
RequestFunctionProbeUninstallation();
return hr;
}
return S_OK;
}

Просмотреть файл

@ -0,0 +1,78 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#pragma once
#include "cor.h"
#include "corprof.h"
#include "com.h"
#include "AssemblyProbePrep.h"
#include "ProbeInjector.h"
#include "../Logging/Logger.h"
#include "../Utilities/PairHash.h"
#include "../Utilities/BlockingQueue.h"
#include <unordered_map>
#include <vector>
#include <memory>
#include <mutex>
#include <thread>
typedef struct _UNPROCESSED_INSTRUMENTATION_REQUEST
{
FunctionID functionId;
std::vector<ULONG32> boxingTypes;
} UNPROCESSED_INSTRUMENTATION_REQUEST;
enum class ProbeWorkerInstruction
{
INSTALL_PROBES,
UNINSTALL_PROBES
};
typedef struct _PROBE_WORKER_PAYLOAD
{
ProbeWorkerInstruction instruction;
std::vector<UNPROCESSED_INSTRUMENTATION_REQUEST> requests;
} PROBE_WORKER_PAYLOAD;
class ProbeInstrumentation
{
private:
ICorProfilerInfo12* m_pCorProfilerInfo;
std::shared_ptr<ILogger> m_pLogger;
FunctionID m_probeFunctionId;
std::unique_ptr<AssemblyProbePrep> m_pAssemblyProbePrep;
/* Probe management */
std::thread m_probeManagementThread;
BlockingQueue<PROBE_WORKER_PAYLOAD> m_probeManagementQueue;
std::unordered_map<std::pair<ModuleID, mdMethodDef>, INSTRUMENTATION_REQUEST, PairHash<ModuleID, mdMethodDef>> m_activeInstrumentationRequests;
std::mutex m_instrumentationProcessingMutex;
std::mutex m_probePinningMutex;
private:
void WorkerThread();
HRESULT InstallProbes(std::vector<UNPROCESSED_INSTRUMENTATION_REQUEST>& requests);
HRESULT UninstallProbes();
bool HasRegisteredProbe();
public:
ProbeInstrumentation(
const std::shared_ptr<ILogger>& logger,
ICorProfilerInfo12* profilerInfo);
HRESULT InitBackgroundService();
void ShutdownBackgroundService();
bool AreProbesInstalled();
HRESULT RegisterFunctionProbe(FunctionID enterProbeId);
HRESULT RequestFunctionProbeUninstallation();
HRESULT RequestFunctionProbeInstallation(ULONG64 functionIds[], ULONG32 count, ULONG32 argumentBoxingTypes[], ULONG32 argumentCounts[]);
void AddProfilerEventMask(DWORD& eventsLow);
HRESULT STDMETHODCALLTYPE GetReJITParameters(ModuleID moduleId, mdMethodDef methodId, ICorProfilerFunctionControl* pFunctionControl);
};

Просмотреть файл

@ -1,6 +1,8 @@
#// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#pragma once
#include <queue>
#include <mutex>
#include <condition_variable>

Просмотреть файл

@ -69,14 +69,15 @@ private:
class FunctionData
{
public:
FunctionData(ModuleID moduleId, ClassID containingClass, tstring&& name, mdTypeDef classToken) :
_moduleId(moduleId), _class(containingClass), _functionName(name), _classToken(classToken)
FunctionData(ModuleID moduleId, ClassID containingClass, tstring&& name, mdToken methodToken, mdTypeDef classToken) :
_moduleId(moduleId), _class(containingClass), _functionName(name), _methodToken(methodToken), _classToken(classToken)
{
}
const ModuleID GetModuleId() const { return _moduleId; }
const tstring& GetName() const { return _functionName; }
const ClassID GetClass() const { return _class; }
const mdToken GetMethodToken() const { return _methodToken; }
const mdTypeDef GetClassToken() const { return _classToken; }
const std::vector<UINT64>& GetTypeArgs() const { return _typeArgs; }
void AddTypeArg(ClassID classID) { _typeArgs.push_back(static_cast<UINT64>(classID)); }
@ -85,6 +86,7 @@ private:
ModuleID _moduleId;
ClassID _class;
tstring _functionName;
mdToken _methodToken;
mdTypeDef _classToken;
std::vector<UINT64> _typeArgs;
};

Просмотреть файл

@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#pragma once
#include "cor.h"
#include "corprof.h"
template<class T> class MetadataEnumCloser final
{
private:
ComPtr<T> m_pEnumOwner;
HCORENUM m_hEnum;
public:
MetadataEnumCloser(T* pImport, HCORENUM hEnum) : m_pEnumOwner(pImport), m_hEnum(hEnum) {}
~MetadataEnumCloser()
{
m_pEnumOwner->CloseEnum(m_hEnum);
}
HCORENUM* GetEnumPtr() { return &m_hEnum; }
};

Просмотреть файл

@ -192,9 +192,9 @@ const std::unordered_map<std::pair<ModuleID, mdTypeDef>, std::shared_ptr<TokenDa
return _names;
}
void NameCache::AddFunctionData(ModuleID moduleId, FunctionID id, tstring&& name, ClassID parent, mdTypeDef parentToken, ClassID* typeArgs, int typeArgsCount)
void NameCache::AddFunctionData(ModuleID moduleId, FunctionID id, tstring&& name, ClassID parent, mdToken methodToken, mdTypeDef parentToken, ClassID* typeArgs, int typeArgsCount)
{
std::shared_ptr<FunctionData> functionData = std::make_shared<FunctionData>(moduleId, parent, std::move(name), parentToken);
std::shared_ptr<FunctionData> functionData = std::make_shared<FunctionData>(moduleId, parent, std::move(name), methodToken, parentToken);
for (int i = 0; i < typeArgsCount; i++)
{
functionData->AddTypeArg(typeArgs[i]);

Просмотреть файл

@ -25,7 +25,7 @@ public:
bool TryGetTokenData(ModuleID modId, mdTypeDef token, std::shared_ptr<TokenData>& data);
void AddModuleData(ModuleID moduleId, tstring&& name);
void AddFunctionData(ModuleID moduleId, FunctionID id, tstring&& name, ClassID parent, mdTypeDef parentToken, ClassID* typeArgs, int typeArgsCount);
void AddFunctionData(ModuleID moduleId, FunctionID id, tstring&& name, ClassID parent, mdToken methodToken, mdTypeDef parentToken, ClassID* typeArgs, int typeArgsCount);
void AddClassData(ModuleID moduleId, ClassID id, mdTypeDef typeDef, ClassFlags flags, ClassID* typeArgs, int typeArgsCount);
void AddTokenData(ModuleID moduleId, mdTypeDef typeDef, mdTypeDef outerToken, tstring&& name);

Просмотреть файл

@ -3,6 +3,8 @@
#pragma once
#include <cwctype>
#include <tstring.h>
#include <string.h>
#if !defined(_IMPLEMENT_STRNCPY_S)
@ -20,6 +22,26 @@
class StringUtilities
{
public:
static bool EndsWithCaseInsensitive(const tstring& str, const tstring& postfix)
{
if (str.length() < postfix.length())
{
return false;
}
size_t offset = str.length() - postfix.length();
for (auto& postfixChar : postfix)
{
if (std::towlower(str[offset]) != std::towlower(postfixChar))
{
return false;
}
offset++;
}
return true;
}
template<size_t DestinationSize>
static HRESULT Copy(char (&destination)[DestinationSize], const char* source)
{

Просмотреть файл

@ -8,6 +8,17 @@ TypeNameUtilities::TypeNameUtilities(ICorProfilerInfo12* profilerInfo) : _profil
{
}
HRESULT TypeNameUtilities::CacheModuleNames(NameCache& nameCache, ModuleID moduleId)
{
std::shared_ptr<ModuleData> moduleData;
if (!nameCache.TryGetModuleData(moduleId, moduleData))
{
return GetModuleInfo(nameCache, moduleId);
}
return S_OK;
}
HRESULT TypeNameUtilities::CacheNames(NameCache& nameCache, ClassID classId)
{
std::shared_ptr<ClassData> classData;
@ -75,7 +86,7 @@ HRESULT TypeNameUtilities::GetFunctionInfo(NameCache& nameCache, FunctionID id,
IfFailRet(GetModuleInfo(nameCache, moduleId));
nameCache.AddFunctionData(moduleId, id, tstring(funcName), classId, classToken, typeArgs, typeArgsCount);
nameCache.AddFunctionData(moduleId, id, tstring(funcName), classId, token, classToken, typeArgs, typeArgsCount);
// If the ClassID returned from GetFunctionInfo is 0, then the function is a shared generic function.
if (classId != 0)

Просмотреть файл

@ -18,6 +18,7 @@ class TypeNameUtilities
TypeNameUtilities(ICorProfilerInfo12* profilerInfo);
HRESULT CacheNames(NameCache& nameCache, ClassID classId);
HRESULT CacheNames(NameCache& nameCache, FunctionID functionId, COR_PRF_FRAME_INFO frameInfo);
HRESULT CacheModuleNames(NameCache& nameCache, ModuleID moduleId);
private:
HRESULT GetFunctionInfo(NameCache& nameCache, FunctionID id, COR_PRF_FRAME_INFO frameInfo);
HRESULT GetClassInfo(NameCache& nameCache, ClassID classId);