opentelemetry-dotnet-instru.../src/OpenTelemetry.AutoInstrumen.../clr_helpers.h

568 lines
16 KiB
C++

/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef OTEL_CLR_PROFILER_CLR_HELPERS_H_
#define OTEL_CLR_PROFILER_CLR_HELPERS_H_
#include <corhlpr.h>
#include <corprof.h>
#include <functional>
#include <utility>
#include "com_ptr.h"
#include "integration.h"
#include "util.h"
#include <set>
namespace trace
{
class ModuleMetadata;
const size_t kNameMaxSize = 1024;
const ULONG kEnumeratorMax = 256;
const auto SystemBoolean = WStr("System.Boolean");
const auto SystemChar = WStr("System.Char");
const auto SystemByte = WStr("System.Byte");
const auto SystemSByte = WStr("System.SByte");
const auto SystemUInt16 = WStr("System.UInt16");
const auto SystemInt16 = WStr("System.Int16");
const auto SystemInt32 = WStr("System.Int32");
const auto SystemUInt32 = WStr("System.UInt32");
const auto SystemInt64 = WStr("System.Int64");
const auto SystemUInt64 = WStr("System.UInt64");
const auto SystemSingle = WStr("System.Single");
const auto SystemDouble = WStr("System.Double");
const auto SystemIntPtr = WStr("System.IntPtr");
const auto SystemUIntPtr = WStr("System.UIntPtr");
const auto SystemString = WStr("System.String");
const auto SystemObject = WStr("System.Object");
const auto SystemException = WStr("System.Exception");
const auto SystemTypeName = WStr("System.Type");
const auto GetTypeFromHandleMethodName = WStr("GetTypeFromHandle");
const auto RuntimeTypeHandleTypeName = WStr("System.RuntimeTypeHandle");
const auto RuntimeMethodHandleTypeName = WStr("System.RuntimeMethodHandle");
template <typename T>
class EnumeratorIterator;
template <typename T>
class Enumerator
{
private:
const std::function<HRESULT(HCORENUM*, T[], ULONG, ULONG*)> callback_;
const std::function<void(HCORENUM)> close_;
mutable HCORENUM ptr_;
public:
Enumerator(std::function<HRESULT(HCORENUM*, T[], ULONG, ULONG*)> callback, std::function<void(HCORENUM)> close) :
callback_(callback), close_(close), ptr_(nullptr)
{
}
Enumerator(const Enumerator& other) = default;
Enumerator& operator=(const Enumerator& other) = default;
~Enumerator()
{
close_(ptr_);
}
EnumeratorIterator<T> begin() const
{
return EnumeratorIterator<T>(this, S_OK);
}
EnumeratorIterator<T> end() const
{
return EnumeratorIterator<T>(this, S_FALSE);
}
HRESULT Next(T arr[], ULONG max, ULONG* cnt) const
{
return callback_(&ptr_, arr, max, cnt);
}
};
template <typename T>
class EnumeratorIterator
{
private:
const Enumerator<T>* enumerator_;
HRESULT status_ = S_FALSE;
T arr_[kEnumeratorMax]{};
ULONG idx_ = 0;
ULONG sz_ = 0;
public:
EnumeratorIterator(const Enumerator<T>* enumerator, HRESULT status) : enumerator_(enumerator)
{
if (status == S_OK)
{
status_ = enumerator_->Next(arr_, kEnumeratorMax, &sz_);
if (status_ == S_OK && sz_ == 0)
{
status_ = S_FALSE;
}
}
else
{
status_ = status;
}
}
bool operator!=(EnumeratorIterator const& other) const
{
return enumerator_ != other.enumerator_ || (status_ == S_OK) != (other.status_ == S_OK);
}
T const& operator*() const
{
return arr_[idx_];
}
EnumeratorIterator<T>& operator++()
{
if (idx_ < sz_ - 1)
{
idx_++;
}
else
{
idx_ = 0;
status_ = enumerator_->Next(arr_, kEnumeratorMax, &sz_);
if (status_ == S_OK && sz_ == 0)
{
status_ = S_FALSE;
}
}
return *this;
}
};
static Enumerator<mdTypeDef> EnumTypeDefs(const ComPtr<IMetaDataImport2>& metadata_import)
{
return Enumerator<mdTypeDef>(
[metadata_import](HCORENUM* ptr, mdTypeDef arr[], ULONG max, ULONG* cnt) -> HRESULT {
return metadata_import->EnumTypeDefs(ptr, arr, max, cnt);
},
[metadata_import](HCORENUM ptr) -> void { metadata_import->CloseEnum(ptr); });
}
static Enumerator<mdTypeRef> EnumTypeRefs(const ComPtr<IMetaDataImport2>& metadata_import)
{
return Enumerator<mdTypeRef>(
[metadata_import](HCORENUM* ptr, mdTypeRef arr[], ULONG max, ULONG* cnt) -> HRESULT {
return metadata_import->EnumTypeRefs(ptr, arr, max, cnt);
},
[metadata_import](HCORENUM ptr) -> void { metadata_import->CloseEnum(ptr); });
}
static Enumerator<mdModuleRef> EnumModuleRefs(const ComPtr<IMetaDataImport2>& metadata_import)
{
return Enumerator<mdModuleRef>(
[metadata_import](HCORENUM* ptr, mdModuleRef arr[], ULONG max, ULONG* cnt) -> HRESULT {
return metadata_import->EnumModuleRefs(ptr, arr, max, cnt);
},
[metadata_import](HCORENUM ptr) -> void { metadata_import->CloseEnum(ptr); });
}
static Enumerator<mdAssemblyRef> EnumAssemblyRefs(const ComPtr<IMetaDataAssemblyImport>& assembly_import)
{
return Enumerator<mdAssemblyRef>(
[assembly_import](HCORENUM* ptr, mdAssemblyRef arr[], ULONG max, ULONG* cnt) -> HRESULT {
return assembly_import->EnumAssemblyRefs(ptr, arr, max, cnt);
},
[assembly_import](HCORENUM ptr) -> void { assembly_import->CloseEnum(ptr); });
}
struct RuntimeInformation
{
COR_PRF_RUNTIME_TYPE runtime_type;
USHORT major_version;
USHORT minor_version;
USHORT build_version;
USHORT qfe_version;
RuntimeInformation() :
runtime_type((COR_PRF_RUNTIME_TYPE) 0x0), major_version(0), minor_version(0), build_version(0), qfe_version(0)
{
}
RuntimeInformation(COR_PRF_RUNTIME_TYPE runtime_type, USHORT major_version, USHORT minor_version,
USHORT build_version, USHORT qfe_version) :
runtime_type(runtime_type),
major_version(major_version),
minor_version(minor_version),
build_version(build_version),
qfe_version(qfe_version)
{
}
RuntimeInformation& operator=(const RuntimeInformation& other)
{
runtime_type = other.runtime_type;
major_version = other.major_version;
minor_version = other.minor_version;
build_version = other.build_version;
qfe_version = other.qfe_version;
return *this;
}
bool is_desktop() const
{
return runtime_type == COR_PRF_DESKTOP_CLR;
}
bool is_core() const
{
return runtime_type == COR_PRF_CORE_CLR;
}
};
struct AssemblyInfo
{
const AssemblyID id;
const WSTRING name;
const ModuleID manifest_module_id;
const AppDomainID app_domain_id;
const WSTRING app_domain_name;
AssemblyInfo() : id(0), name(EmptyWStr), manifest_module_id(0), app_domain_id(0), app_domain_name(EmptyWStr)
{
}
AssemblyInfo(AssemblyID id, WSTRING name, ModuleID manifest_module_id, AppDomainID app_domain_id,
WSTRING app_domain_name) :
id(id),
name(name),
manifest_module_id(manifest_module_id),
app_domain_id(app_domain_id),
app_domain_name(app_domain_name)
{
}
bool IsValid() const
{
return id != 0;
}
};
struct AssemblyMetadata
{
const ModuleID module_id;
const WSTRING name;
const mdAssembly assembly_token;
const Version version;
AssemblyMetadata() : module_id(0), name(EmptyWStr), assembly_token(mdTokenNil)
{
}
AssemblyMetadata(ModuleID module_id, WSTRING name, mdAssembly assembly_token, USHORT major, USHORT minor,
USHORT build, USHORT revision) :
module_id(module_id),
name(name),
assembly_token(assembly_token),
version(Version(major, minor, build, revision))
{
}
bool IsValid() const
{
return module_id != 0;
}
};
struct AssemblyProperty
{
const void* ppbPublicKey;
ULONG pcbPublicKey;
ULONG pulHashAlgId;
ASSEMBLYMETADATA pMetaData{};
WSTRING szName;
DWORD assemblyFlags = 0;
AssemblyProperty() : ppbPublicKey(nullptr), pcbPublicKey(0), pulHashAlgId(0), szName(EmptyWStr)
{
}
};
struct ModuleInfo
{
const ModuleID id;
const WSTRING path;
const AssemblyInfo assembly;
const DWORD flags;
ModuleInfo() : id(0), path(EmptyWStr), assembly({}), flags(0)
{
}
ModuleInfo(ModuleID id, WSTRING path, AssemblyInfo assembly, DWORD flags) :
id(id), path(path), assembly(assembly), flags(flags)
{
}
bool IsValid() const
{
return id != 0;
}
bool IsWindowsRuntime() const
{
return ((flags & COR_PRF_MODULE_WINDOWS_RUNTIME) != 0);
}
bool IsNGEN() const
{
return ((flags & COR_PRF_MODULE_NGEN) != 0);
}
bool IsDynamic() const
{
return ((flags & COR_PRF_MODULE_DYNAMIC) != 0);
}
bool IsResource() const
{
return ((flags & COR_PRF_MODULE_RESOURCE) != 0);
}
};
struct TypeInfo
{
const mdToken id;
const WSTRING name;
const mdTypeSpec type_spec;
const ULONG32 token_type;
std::shared_ptr<TypeInfo> extend_from;
const bool valueType;
const bool isGeneric;
std::shared_ptr<TypeInfo> parent_type;
TypeInfo() :
id(0),
name(EmptyWStr),
type_spec(0),
token_type(0),
extend_from(nullptr),
valueType(false),
isGeneric(false),
parent_type(nullptr)
{
}
TypeInfo(mdToken id, WSTRING name, mdTypeSpec type_spec, ULONG32 token_type, std::shared_ptr<TypeInfo> extend_from,
bool valueType, bool isGeneric, std::shared_ptr<TypeInfo> parent_type) :
id(id),
name(name),
type_spec(type_spec),
token_type(token_type),
extend_from(extend_from),
valueType(valueType),
isGeneric(isGeneric),
parent_type(parent_type)
{
}
bool IsValid() const
{
return id != 0;
}
};
enum MethodArgumentTypeFlag
{
TypeFlagByRef = 0x01,
TypeFlagVoid = 0x02,
TypeFlagBoxedType = 0x04
};
struct FunctionMethodArgument
{
ULONG offset;
ULONG length;
PCCOR_SIGNATURE pbBase;
mdToken GetTypeTok(ComPtr<IMetaDataEmit2>& pEmit, mdAssemblyRef corLibRef) const;
WSTRING GetTypeTokName(ComPtr<IMetaDataImport2>& pImport) const;
int GetTypeFlags(unsigned& elementType) const;
ULONG GetSignature(PCCOR_SIGNATURE& data) const;
};
struct FunctionMethodSignature
{
private:
PCCOR_SIGNATURE pbBase;
unsigned len;
ULONG numberOfTypeArguments = 0;
ULONG numberOfArguments = 0;
FunctionMethodArgument ret{};
std::vector<FunctionMethodArgument> params;
public:
FunctionMethodSignature() : pbBase(nullptr), len(0)
{
}
FunctionMethodSignature(PCCOR_SIGNATURE pb, unsigned cbBuffer)
{
pbBase = pb;
len = cbBuffer;
};
ULONG NumberOfTypeArguments() const
{
return numberOfTypeArguments;
}
ULONG NumberOfArguments() const
{
return numberOfArguments;
}
WSTRING str() const
{
return HexStr(pbBase, len);
}
FunctionMethodArgument GetRet() const
{
return ret;
}
std::vector<FunctionMethodArgument> GetMethodArguments() const
{
return params;
}
HRESULT TryParse();
bool operator==(const FunctionMethodSignature& other) const
{
return memcmp(pbBase, other.pbBase, len);
}
CorCallingConvention CallingConvention() const
{
return CorCallingConvention(len == 0 ? 0 : pbBase[0]);
}
bool IsEmpty() const
{
return len == 0;
}
};
struct FunctionInfo
{
const mdToken id;
const WSTRING name;
const TypeInfo type;
const BOOL is_generic;
const MethodSignature signature;
const MethodSignature function_spec_signature;
const mdToken method_def_id;
FunctionMethodSignature method_signature;
FunctionInfo() : id(0), name(EmptyWStr), type({}), is_generic(false), method_def_id(0), method_signature({})
{
}
FunctionInfo(mdToken id, WSTRING name, TypeInfo type, MethodSignature signature,
MethodSignature function_spec_signature, mdToken method_def_id,
FunctionMethodSignature method_signature) :
id(id),
name(name),
type(type),
is_generic(true),
signature(signature),
function_spec_signature(function_spec_signature),
method_def_id(method_def_id),
method_signature(method_signature)
{
}
FunctionInfo(mdToken id, WSTRING name, TypeInfo type, MethodSignature signature,
FunctionMethodSignature method_signature) :
id(id),
name(name),
type(type),
is_generic(false),
signature(signature),
method_def_id(0),
method_signature(method_signature)
{
}
bool IsValid() const
{
return id != 0;
}
};
struct AssemblyVersionRedirection
{
// This struct is used to check and track version against ASSEMBLYMETADATA structs
// so it uses the same naming conventions, keeping fields with the same names in both
// structs.
USHORT usMajorVersion; // Major Version.
USHORT usMinorVersion; // Minor Version.
USHORT usBuildNumber; // Build Number.
USHORT usRevisionNumber; // Revision Number.
// Redirection related fields
ULONG ulRedirectionCount; // Tracks the number of times that the redirection was applied.
AssemblyVersionRedirection() :
usMajorVersion(0), usMinorVersion(0), usBuildNumber(0), usRevisionNumber(0), ulRedirectionCount(0)
{
}
AssemblyVersionRedirection(USHORT major, USHORT minor, USHORT build, USHORT revision) : ulRedirectionCount(0)
{
usMajorVersion = major;
usMinorVersion = minor;
usBuildNumber = build;
usRevisionNumber = revision;
}
int CompareToAssemblyVersion(const ASSEMBLYMETADATA& assembly)
{
return
usMajorVersion != assembly.usMajorVersion
? (usMajorVersion > assembly.usMajorVersion ? 1 : -1) :
usMinorVersion != assembly.usMinorVersion
? (usMinorVersion > assembly.usMinorVersion ? 1 : -1) :
usBuildNumber != assembly.usBuildNumber
? (usBuildNumber > assembly.usBuildNumber ? 1 : -1) :
usRevisionNumber != assembly.usRevisionNumber
? (usRevisionNumber > assembly.usRevisionNumber ? 1 : -1) :
0;
}
WSTRING VersionStr()
{
return trace::VersionStr(usMajorVersion, usMinorVersion, usBuildNumber, usRevisionNumber);
}
};
RuntimeInformation GetRuntimeInformation(ICorProfilerInfo7* info);
AssemblyInfo GetAssemblyInfo(ICorProfilerInfo7* info, const AssemblyID& assembly_id);
AssemblyMetadata GetAssemblyImportMetadata(const ComPtr<IMetaDataAssemblyImport>& assembly_import);
AssemblyMetadata GetReferencedAssemblyMetadata(const ComPtr<IMetaDataAssemblyImport>& assembly_import,
const mdAssemblyRef& assembly_ref);
FunctionInfo GetFunctionInfo(const ComPtr<IMetaDataImport2>& metadata_import, const mdToken& token);
ModuleInfo GetModuleInfo(ICorProfilerInfo7* info, const ModuleID& module_id);
TypeInfo GetTypeInfo(const ComPtr<IMetaDataImport2>& metadata_import, const mdToken& token);
mdAssemblyRef FindAssemblyRef(const ComPtr<IMetaDataAssemblyImport>& assembly_import, const WSTRING& assembly_name);
bool DisableOptimizations();
bool EnableInlining();
HRESULT GetCorLibAssemblyRef(const ComPtr<IMetaDataAssemblyEmit>& assembly_emit, AssemblyProperty& corAssemblyProperty,
mdAssemblyRef* corlib_ref);
bool FindTypeDefByName(const trace::WSTRING instrumentationTargetMethodTypeName, const trace::WSTRING assemblyName,
const ComPtr<IMetaDataImport2>& metadata_import, mdTypeDef& typeDef);
} // namespace trace
#endif // OTEL_CLR_PROFILER_CLR_HELPERS_H_