diff options
Diffstat (limited to 'src/ToolBox/superpmi/superpmi-shared/callutils.cpp')
-rw-r--r-- | src/ToolBox/superpmi/superpmi-shared/callutils.cpp | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/src/ToolBox/superpmi/superpmi-shared/callutils.cpp b/src/ToolBox/superpmi/superpmi-shared/callutils.cpp new file mode 100644 index 0000000000..027929bec0 --- /dev/null +++ b/src/ToolBox/superpmi/superpmi-shared/callutils.cpp @@ -0,0 +1,416 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +//---------------------------------------------------------- +// CallUtils.cpp - Utility code for analyzing and working with managed calls +//---------------------------------------------------------- + +#include "standardpch.h" +#include "callutils.h" +#include "typeutils.h" +#include "errorhandling.h" +#include "logging.h" + +// String representations of the JIT helper functions +const char *kHelperName[CORINFO_HELP_COUNT] = +{ +#define JITHELPER(code, pfnHelper, sig) #code, +#define DYNAMICJITHELPER(code, pfnHelper,sig) #code, +#include "jithelpers.h" +}; + +//------------------------------------------------------------------------------------------------- + +// +// Provides information about the target of an outgoing call, based on where it was emitted in the +// generated code stream. +// +// Primarily, this returns what the destination of the call is (e.g. a method, a helper function), but this +// can also provide: +// +// - A symbolic name for the call target (i.e. the function name). +// - For non-helper methods, the method signature of the call target. +// +// Arguments: +// mc - The method context of the method containing this call site. +// cr - The compile result for the method containing this call site. +// callInstrOffset - The native offset of the call site in the generated code stream. +// outSigInfo - [out] The signature of the outgoing call. Optional (pass nullptr if unwanted). +// outCallTargetSymbol - [out] A string representation of the outgoing call. Optional (pass nullptr if +// unwanted). +// +// Return Value: +// What type of call the outgoing call is. +// +// Notes: +// - This depends on the JIT having registered the call site with the EE through recordCallSite. If the +// JIT didn't do this, GetDirectCallSite can obtain most of the same information for direct calls. +// - If the call site is for a helper method, then outSigInfo will not be changed, since helper calls +// have no signature information. +// - If you pass in a valid pointer for outCallTargetSymbol, this function will allocate memory for it +// if it is able to understand that call (i.e. if it does not return CallType_Unknown). You, the caller, +// are responsible for freeing the memory (with delete[]). +// +CallType CallUtils::GetRecordedCallSiteInfo(MethodContext *mc, + CompileResult *cr, + unsigned int callInstrOffset, + /*out*/ CORINFO_SIG_INFO *outSigInfo, + /*out*/ char **outCallTargetSymbol) +{ + AssertCodeMsg(mc != nullptr, EXCEPTIONCODE_CALLUTILS, + "Null method context passed into GetCallTargetInfo for call at offset %x.", + callInstrOffset); + AssertCodeMsg(cr != nullptr, EXCEPTIONCODE_CALLUTILS, + "Null compile result passed into GetCallTargetInfo for call at offset %x.", + callInstrOffset); + + CallType targetType = CallType_Unknown; + + CORINFO_SIG_INFO callSig; + bool recordedCallSig = cr->fndRecordCallSiteSigInfo(callInstrOffset, &callSig); + + CORINFO_METHOD_HANDLE methodHandle = nullptr; + bool recordedMethodHandle = cr->fndRecordCallSiteMethodHandle(callInstrOffset, &methodHandle); + + if (recordedCallSig) + { + if (outSigInfo != nullptr) + *outSigInfo = callSig; + + if (outCallTargetSymbol != nullptr) + *outCallTargetSymbol = (char *)GetMethodFullName(mc, methodHandle, callSig); + + targetType = CallType_UserFunction; + } + else if (recordedMethodHandle) + { + CorInfoHelpFunc helperNum = CallUtils::GetHelperNum(methodHandle); + AssertCodeMsg(helperNum != CORINFO_HELP_UNDEF, EXCEPTIONCODE_CALLUTILS, + "Unknown call at offset %x with method handle %016llX.", + callInstrOffset, methodHandle); + + size_t length = strlen(kHelperName[helperNum]) + 1; + *outCallTargetSymbol = new char[length]; + strcpy_s(*outCallTargetSymbol, length, kHelperName[helperNum]); + + targetType = CallType_Helper; + } + else + { + LogWarning("Call site at offset %x was not recorded via recordCallSite.", callInstrOffset); + } + + return targetType; +} + +// +// Provides information about the target of an outgoing call, based on the outgoing call's target address. +// +// Primarily, this returns what the destination of the call is (e.g. a method, a helper function), but this +// can also provide: +// +// - A symbolic name for the call target (i.e. the function name). +// - For certain types of managed methods, the method signature of the call target. +// +// Arguments: +// mc - The method context of the method containing this outgoing call. +// callTarget - The target address of the outgoing call. +// outSigInfo - [out] The signature of the outgoing call. Optional (pass nullptr if unwanted). +// outCallTargetSymbol - [out] A string representation of the outgoing call. Optional (pass nullptr if +// unwanted). +// +// Return Value: +// What type of call the outgoing call is. +// +// Assumptions: +// The given method address does not point to a jump stub. +// +// Notes: +// - This only works for direct calls that have a static target address. +// - If you pass in a valid pointer for outCallTargetSymbol, this function will allocate memory for it +// if it is able to understand that call (i.e. if it does not return CallType_Unknown). You, the caller, +// are responsible for freeing the memory (with delete[]). +// + +CallType CallUtils::GetDirectCallSiteInfo(MethodContext *mc, + void *callTarget, + /*out*/ CORINFO_SIG_INFO *outSigInfo, + /*out*/ char **outCallTargetSymbol) +{ + AssertCodeMsg(mc != nullptr, EXCEPTIONCODE_CALLUTILS, + "Null method context passed into GetCallTargetInfo for call to target %016llX.", callTarget); + + CallType targetType = CallType_Unknown; + MethodContext::DLD functionEntryPoint; + CORINFO_METHOD_HANDLE methodHandle; + + // Try to first obtain a method handle associated with this call target + functionEntryPoint.A = (DWORDLONG)callTarget; + functionEntryPoint.B = 0; // TODO-Cleanup: we should be more conscious of this... + + if (mc->fndGetFunctionEntryPoint(functionEntryPoint, &methodHandle)) + { + // Now try to obtain the call info associated with this method handle + + struct Param { + MethodContext* mc; + CORINFO_SIG_INFO* outSigInfo; + char** outCallTargetSymbol; + CallType* pTargetType; + CORINFO_METHOD_HANDLE* pMethodHandle; + } param; + param.mc = mc; + param.outSigInfo = outSigInfo; + param.outCallTargetSymbol = outCallTargetSymbol; + param.pTargetType = &targetType; + param.pMethodHandle = &methodHandle; + + PAL_TRY(Param*, pParam, ¶m) + { + CORINFO_CALL_INFO callInfo; + + pParam->mc->repGetCallInfoFromMethodHandle(*pParam->pMethodHandle, &callInfo); + + if (pParam->outSigInfo != nullptr) + *pParam->outSigInfo = callInfo.sig; + + if (pParam->outCallTargetSymbol != nullptr) + *pParam->outCallTargetSymbol = (char *)GetMethodFullName(pParam->mc, *pParam->pMethodHandle, callInfo.sig); + + *pParam->pTargetType = CallType_UserFunction; + } + PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CatchMC) + { + LogWarning("Didn't find call info for method handle %016llX (call target: %016llX)", + methodHandle, callTarget); + } + PAL_ENDTRY + } + else + { + // No method handle associated with this target, so check if it's a helper + CorInfoHelpFunc helperNum; + + if (mc->fndGetHelperFtn(callTarget, &helperNum)) + { + if (outCallTargetSymbol != nullptr) + { + size_t length = strlen(kHelperName[helperNum]) + 1; + *outCallTargetSymbol = new char[length]; + strcpy_s(*outCallTargetSymbol, length, kHelperName[helperNum]); + } + + targetType = CallType_Helper; + } + else + { + LogWarning("Call to target %016llX has no method handle and is not a helper call.", + callTarget); + } + } + + return targetType; +} + +//------------------------------------------------------------------------------------------------- +// Utilty code that was stolen from various sections of the JIT codebase and tweaked to go through +// SuperPMI's method context replaying instead of directly making calls into the JIT/EE interface. +//------------------------------------------------------------------------------------------------- + +// Stolen from Compiler::impMethodInfo_hasRetBuffArg (in the importer) +bool CallUtils::HasRetBuffArg(MethodContext *mc, CORINFO_SIG_INFO args) +{ + if (args.retType != CORINFO_TYPE_VALUECLASS && + args.retType != CORINFO_TYPE_REFANY) + { + return false; + } + +#if defined(_TARGET_AMD64_) + // We don't need a return buffer if: + // i) TYP_STRUCT argument that can fit into a single register and + // ii) Power of two sized TYP_STRUCT on AMD64. + unsigned size = mc->repGetClassSize(args.retTypeClass); + return (size > sizeof(void*)) || ((size & (size-1)) != 0); +#else + return true; +#endif +} + +// Originally from src/jit/ee_il_dll.cpp +const char *CallUtils::GetMethodName(MethodContext *mc, + CORINFO_METHOD_HANDLE method, + const char **classNamePtr) +{ + if (GetHelperNum(method)) + { + if (classNamePtr != nullptr) + *classNamePtr = "HELPER"; + + // The JIT version uses the getHelperName JIT/EE interface call, but this is easier for us + return kHelperName[GetHelperNum(method)]; + } + + if (IsNativeMethod(method)) + { + if (classNamePtr != nullptr) + *classNamePtr = "NATIVE"; + method = GetMethodHandleForNative(method); + } + + return(mc->repGetMethodName(method, classNamePtr)); +} + +// Originally from src/jit/eeinterface.cpp +const char *CallUtils::GetMethodFullName(MethodContext *mc, + CORINFO_METHOD_HANDLE hnd, + CORINFO_SIG_INFO sig) +{ + const char* returnType = NULL; + + const char* className; + const char* methodName = GetMethodName(mc, hnd, &className); + if ((GetHelperNum(hnd) != CORINFO_HELP_UNDEF) || IsNativeMethod(hnd)) + { + return methodName; + } + + size_t length = 0; + unsigned i; + + /* Generating the full signature is a two-pass process. First we have to walk + the components in order to assess the total size, then we allocate the buffer + and copy the elements into it. + */ + + /* Right now there is a race-condition in the EE, className can be NULL */ + + /* initialize length with length of className and '.' */ + + if (className != nullptr) + length = strlen(className)+1; + else + { + // Tweaked to avoid using CRT assertions + Assert(strlen("<NULL>.") == 7); + length = 7; + } + + /* add length of methodName and opening bracket */ + length += strlen(methodName) + 1; + + CORINFO_ARG_LIST_HANDLE argList = sig.args; + + for (i = 0; i < sig.numArgs; i++) + { + // Tweaked to use EE types instead of JIT-specific types + CORINFO_CLASS_HANDLE typeHandle; + DWORD exception; + CorInfoType type = strip(mc->repGetArgType(&sig, argList, &typeHandle, &exception)); + + length += strlen(TypeUtils::GetCorInfoTypeName(type)); + argList = mc->repGetArgNext(argList); + } + + /* add ',' if there is more than one argument */ + + if (sig.numArgs > 1) + length += (sig.numArgs - 1); + + // Tweaked to use EE types instead of JIT-specific types + if (sig.retType != CORINFO_TYPE_VOID) + { + returnType = TypeUtils::GetCorInfoTypeName(sig.retType); + length += strlen(returnType) + 1; // don't forget the delimiter ':' + } + + // Does it have a 'this' pointer? Don't count explicit this, which has the this pointer type as the first element of the arg type list + if (sig.hasThis() && !sig.hasExplicitThis()) + { + // Tweaked to avoid using CRT assertions + Assert(strlen(":this") == 5); + length += 5; + } + + /* add closing bracket and null terminator */ + + length += 2; + + char *retName = new char[length]; // Tweaked to use "new" instead of compGetMem + + /* Now generate the full signature string in the allocated buffer */ + + if (className) + { + strcpy_s(retName, length, className); + strcat_s(retName, length, ":"); + } + else + { + strcpy_s(retName, length, "<NULL>."); + } + + strcat_s(retName, length, methodName); + + // append the signature + strcat_s(retName, length, "("); + + argList = sig.args; + + for (i = 0; i < sig.numArgs; i++) + { + // Tweaked to use EE types instead of JIT-specific types + CORINFO_CLASS_HANDLE typeHandle; + DWORD exception; + CorInfoType type = strip(mc->repGetArgType(&sig, argList, &typeHandle, &exception)); + strcat_s(retName, length, TypeUtils::GetCorInfoTypeName(type)); + + argList = mc->repGetArgNext(argList); + if (i + 1 < sig.numArgs) + strcat_s(retName, length, ","); + } + + strcat_s(retName, length, ")"); + + if (returnType) + { + strcat_s(retName, length, ":"); + strcat_s(retName, length, returnType); + } + + // Does it have a 'this' pointer? Don't count explicit this, which has the this pointer type as the first element of the arg type list + if (sig.hasThis() && !sig.hasExplicitThis()) + { + strcat_s(retName, length, ":this"); + } + + // Tweaked to avoid using CRT assertions + Assert(strlen(retName) == (length-1)); + + return(retName); +} + +// Originally from jit/compiler.hpp +inline CorInfoHelpFunc CallUtils::GetHelperNum(CORINFO_METHOD_HANDLE method) +{ + // Helpers are marked by the fact that they are odd numbers + if (!(((size_t) method) & 1)) + return(CORINFO_HELP_UNDEF); + return((CorInfoHelpFunc) (((size_t) method) >> 2)); +} + +// Originally from jit/compiler.hpp +inline bool CallUtils::IsNativeMethod(CORINFO_METHOD_HANDLE method) +{ + return ((((size_t)method) & 0x2) == 0x2); +} + +// Originally from jit/compiler.hpp +inline CORINFO_METHOD_HANDLE CallUtils::GetMethodHandleForNative(CORINFO_METHOD_HANDLE method) +{ + // Tweaked to avoid using CRT assertions + Assert((((size_t) method) & 0x3) == 0x2); + return (CORINFO_METHOD_HANDLE)(((size_t)method)& ~0x3); +} |