in src/InstrumentationEngine/Instruction.h [13:221]
class __declspec(uuid("BEA964F2-5527-4F7C-B606-D8A1BD8CFB39"))
CInstruction : public IInstruction, public CDataContainer
{
friend CInstructionGraph::~CInstructionGraph();
// Static table of information about each opcode
struct ILOpcodeInfo
{
public:
LPCTSTR m_name; // Display name of the opcode
DWORD m_opcodeLength; // Length of the opcode (1 or 2)
DWORD m_operandLength; // Length of the parameter length
ILOperandType m_type; // The type of the operand
ILOrdinalOpcode m_alternate; // The index of the alternate short/long form of the instruction
ILOpcodeFlags m_flags;
DWORD m_popSlots; // Number of slots this instruction pops off the instruction graph
DWORD m_pushSlots; // Number of slots this instruction pushes on the instruction graph
};
protected:
static ILOpcodeInfo s_ilOpcodeInfo[];
// CONSIDER: Store the original length as well? The graph immediately changes short branches etc...
ILOrdinalOpcode m_opcode;
DWORD m_offset;
DWORD m_origOffset;
InstructionGeneration m_instructionGeneration;
BOOL m_bIsRemoved;
// The graph that owns this instruction. Not a CComPtr
// so that we don't have circular references.
CInstructionGraph* m_pGraph;
CComPtr<CInstruction> m_pNextInstruction;
CComPtr<CInstruction> m_pPreviousInstruction;
CComPtr<CInstruction> m_pOriginalNextInstruction;
CComPtr<CInstruction> m_pOriginalPreviousInstruction;
//Private helper to break up circular dependencies between instructions.
virtual HRESULT Disconnect();
public:
CInstruction(
_In_ ILOrdinalOpcode opcode,
_In_ bool isNew
);
virtual HRESULT InitializeFromBytes(
_In_reads_to_ptr_(pEndOfCode) LPCBYTE pCode,
_In_ LPCBYTE pEndOfCode
);
public:
DEFINE_DELEGATED_REFCOUNT_ADDREF(CInstruction);
DEFINE_DELEGATED_REFCOUNT_RELEASE(CInstruction);
STDMETHOD(QueryInterface)(_In_ REFIID riid, _Out_ void **ppvObject) override
{
if (FAILED(QueryBaseType<CInstruction>(riid, ppvObject)))
{
return ImplQueryInterface(
static_cast<IInstruction*>(this),
static_cast<IDataContainer*>(this),
riid,
ppvObject
);
}
return S_OK;
}
public:
HRESULT SetIsRemoved();
// Return the instruction's impact on the execution stack.
// This can be positive for a push and negative for a pop.
// For variable stack impact (call, etc...) it calculates the stack impact using metadata.
HRESULT GetStackImpact(_In_ IMethodInfo* pMethodInfo, _In_ DWORD currStackDepth, _Out_ int* pStackImpact);
HRESULT SetGraph(_In_ CInstructionGraph* pGraph);
constexpr CInstructionGraph* GetGraph() { return m_pGraph; }
public:
virtual HRESULT __stdcall GetOffset(_Out_ DWORD* pdwOffset) override;
virtual HRESULT __stdcall GetOriginalOffset(_Out_ DWORD* pdwOffset) override;
virtual HRESULT __stdcall GetOpCodeName(_Out_ BSTR* pbstrName) override;
virtual HRESULT __stdcall GetOpCode(_Out_ ILOrdinalOpcode* pOpCode) override;
// Only single byte opcodes have short and long forms
virtual HRESULT __stdcall GetAlternateOrdinalOpcode(_Out_ ILOrdinalOpcode* pdwAlternative) override;
virtual HRESULT __stdcall GetInstructionLength(_Out_ DWORD* pdwLength) override;
virtual HRESULT __stdcall GetOpcodeFlags(_Out_ ILOpcodeFlags* pFlags) override;
virtual HRESULT __stdcall GetOpcodeLength(_Out_ DWORD* pdwLength) override;
virtual HRESULT __stdcall GetOperandType(_Out_ ILOperandType* pType) override;
virtual HRESULT __stdcall GetOperandLength(_Out_ DWORD* pdwLength) override;
// set to true if this instruction did not exist in the original instruction stream.
virtual HRESULT __stdcall GetIsNew(_Out_ BOOL* pbValue) override;
// set to true of this instruction has been removed or replaced from in the instruction stream.
virtual HRESULT __stdcall GetIsRemoved(_Out_ BOOL* pbValue) override;
virtual HRESULT __stdcall GetIsBranch(_Out_ BOOL* pbValue) override;
virtual HRESULT __stdcall GetIsSwitch(_Out_ BOOL* pbValue) override;
virtual HRESULT __stdcall GetIsCallInstruction(_Out_ BOOL* pbValue) override;
virtual HRESULT __stdcall GetTerminationType(_Out_ InstructionTerminationType* pTerminationType) override;
virtual HRESULT __stdcall GetIsFallThrough(_Out_ BOOL* pbIsFallThrough) override;
// Get the current next and previous instructions reflecting changes that instrumentation methods have made
virtual HRESULT __stdcall GetNextInstruction(_Out_ IInstruction** ppNextInstruction) override;
virtual HRESULT __stdcall GetPreviousInstruction(_Out_ IInstruction** ppPrevInstruction) override;
// Get the original instructions from the graph before any instrumentation method made chanages to it.
virtual HRESULT __stdcall GetOriginalNextInstruction(_Out_ IInstruction** ppNextInstruction) override;
virtual HRESULT __stdcall GetOriginalPreviousInstruction(_Out_ IInstruction** ppPrevInstruction) override;
virtual HRESULT __stdcall GetInstructionGeneration(_Out_ InstructionGeneration* pInstructionGeneration) override;
public:
// NOTE: these do not fixup the rest of hte graph. Call the versions on instruction graph if you want
// previous, and next to be udpated correctly
virtual DWORD GetInstructionSize();
HRESULT SetNextInstruction(_In_opt_ CInstruction* pInstruction, _In_ bool setOrig);
HRESULT SetPreviousInstruction(_In_opt_ CInstruction* pInstruction, _In_ bool setOrig);
HRESULT SetOriginalOffset(_In_ ULONG offset);
HRESULT SetOffset(_In_ ULONG offset);
HRESULT SetInstructionGeneration(_In_ InstructionGeneration instructionGeneration);
static HRESULT InstructionFromBytes(_In_ LPCBYTE pCode, _In_ LPCBYTE pEndOfCode, _Out_ CInstruction** ppInstruction);
static HRESULT OrdinalOpcodeFromBytes(_In_reads_to_ptr_(pEndOfCode) LPCBYTE pCode, _In_ LPCBYTE pEndOfCode, _Out_ ILOrdinalOpcode* pOpcode);
HRESULT EmitIL(_In_reads_bytes_(dwcbILBuffer) BYTE* pILBuffer, _In_ DWORD dwcbILBuffer);
HRESULT LogInstruction(bool ignoreTest);
constexpr CInstruction* NextInstructionInternal() { return m_pNextInstruction.p; }
constexpr CInstruction* PreviousInstructionInternal() { return m_pPreviousInstruction.p; }
constexpr CInstruction* OriginalNextInstructionInternal() { return m_pOriginalNextInstruction.p; }
constexpr CInstruction* OriginalPreviousInstructionInternal() { return m_pOriginalPreviousInstruction.p; }
constexpr bool GetIsSwitchInternal() const { return m_opcode == Cee_Switch; }
bool GetIsBranchInternal() const { return IsFlagSet(s_ilOpcodeInfo[m_opcode].m_flags, ILOpcodeFlag_Branch); }
// Attempt to cast the given IInstruction to a more specific TCast type. pInstruction may
// be null, in which case, the result will also be null.
template<typename TCast>
static HRESULT CastTo(_In_opt_ IInstruction* pInstruction, _Outptr_result_maybenull_ TCast** pResult)
{
IfNullRet(pResult);
if (pInstruction == nullptr)
{
*pResult = nullptr;
}
return pInstruction->QueryInterface(pResult);
}
protected:
template<typename TBase>
HRESULT QueryBaseType(_In_ REFIID riid, _Out_ void** ppvObject)
{
// Workaround for the diamond inheritence problem.
// Both CDataContainer and IInstruction inherit from IUnknown and
// ImplQueryInterface() doesn't know which one to pick when casting CInstruction* to IUnknown*.
// A more comprehensive fix will be covered by https://github.com/microsoft/CLRInstrumentationEngine/issues/311
IfNullRet(ppvObject);
if (__uuidof(TBase) == riid)
{
*ppvObject = static_cast<TBase*>(this);
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
private:
// If the callee token is a MethodSpec (generic method), the parameter count and signature returned
// will represent the parent method declaration/reference, not the generic parameters or generic signature.
HRESULT GetSignatureInfoFromCallToken(
_In_ IMethodInfo* pMethodInfo,
_Out_ PCCOR_SIGNATURE* ppSig,
_Out_ DWORD* pSigLength,
_Out_ CorCallingConvention* pCallConv,
_Out_ DWORD* pParameterCount,
_Out_ CorElementType* pRetTypeElementType
);
HRESULT EnsureGraphUpdated()
{
if (m_pGraph != nullptr)
{
return m_pGraph->RefreshInstructions();
}
return S_OK;
}
};