[Parameter Capturing] Add probe instrumentation APIs to profiler (#4428)
This commit is contained in:
Родитель
9abfff823d
Коммит
3dafe54bb6
|
@ -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"
|
||||
|
|
12
cspell.json
12
cspell.json
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче