1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5// File: rsfunction.cpp
6//
7
8//
9//*****************************************************************************
10#include "stdafx.h"
11
12// We have an assert in ceemain.cpp that validates this assumption
13#define FIELD_OFFSET_NEW_ENC_DB 0x07FFFFFB
14
15#include "winbase.h"
16#include "corpriv.h"
17
18/* ------------------------------------------------------------------------- *
19 * Function class
20 * ------------------------------------------------------------------------- */
21
22//-----------------------------------------------------------------------------
23// Constructor for CordbFunction class.
24// This represents an IL Function in the debuggee.
25// CordbFunction is 1:1 with IL method bodies.
26//
27// Parameters:
28// m - module containing this function. All functions live in a single module.
29// funcMetadataToken - the metadata token for this function (scoped to the module).
30// enCVersion - Enc Version number of this function (in sync with the module's
31// EnC version). Each edit to a function means a whole new IL method body,
32// and since CordbFunction is 1:1 with IL, that means a new CordbFunction instance.
33//-----------------------------------------------------------------------------
34CordbFunction::CordbFunction(CordbModule * m,
35 mdMethodDef funcMetadataToken,
36 SIZE_T enCVersion)
37 : CordbBase(m->GetProcess(), funcMetadataToken, enumCordbFunction), m_pModule(m), m_pClass(NULL),
38 m_pILCode(NULL),
39 m_nativeCode(NULL),
40 m_MDToken(funcMetadataToken),
41 m_dwEnCVersionNumber(enCVersion),
42 m_pPrevVersion(NULL),
43 m_fIsNativeImpl(kUnknownImpl),
44 m_fCachedMethodValuesValid(FALSE),
45 m_argCountCached(0),
46 m_fIsStaticCached(FALSE),
47 m_reJitILCodes(1)
48{
49 m_methodSigParserCached = SigParser(NULL, 0);
50
51 _ASSERTE(enCVersion >= CorDB_DEFAULT_ENC_FUNCTION_VERSION);
52}
53
54
55
56/*
57 A list of which resources owned by this object are accounted for.
58
59 UNKNOWN:
60 ICorDebugInfo::NativeVarInfo *m_nativeInfo;
61
62 HANDLED:
63 CordbModule *m_module; // Assigned w/o AddRef()
64 CordbClass *m_class; // Assigned w/o AddRef()
65*/
66
67//-----------------------------------------------------------------------------
68// CordbFunction destructor
69// All external resources, including references counts, should have been
70// released in Neuter(), so this should literally just delete memory or
71// or check that the object is already dead.
72//-----------------------------------------------------------------------------
73CordbFunction::~CordbFunction()
74{
75 // We should have been explicitly neutered before our internal ref went to 0.
76 _ASSERTE(IsNeutered());
77
78 // Since we've been neutered, we shouldn't have any References to release and
79 // our hash of JitInfos should be empty.
80 _ASSERTE(m_pILCode == NULL);
81 _ASSERTE(m_pPrevVersion == NULL);
82}
83
84//-----------------------------------------------------------------------------
85// CordbFunction::Neuter
86// Neuter releases all of the resources this object holds. CordbFunction
87// lives in a CordbModule, so Module neuter will neuter this.
88// See CordbBase::Neuter for further semantics.
89//
90//-----------------------------------------------------------------------------
91void CordbFunction::Neuter()
92{
93 // Neuter any/all CordbNativeCode & CordbILCode objects
94 if (m_pILCode != NULL)
95 {
96 m_pILCode->Neuter();
97 m_pILCode.Clear(); // this will internal release.
98 }
99
100 // Neuter & Release the Prev-Function list.
101 if (m_pPrevVersion != NULL)
102 {
103 m_pPrevVersion->Neuter();
104 m_pPrevVersion.Clear(); // this will internal release.
105 }
106
107 m_pModule = NULL;
108 m_pClass = NULL;
109
110 m_nativeCode.Clear();
111 m_reJitILCodes.NeuterAndClear(GetProcess()->GetProcessLock());
112
113 CordbBase::Neuter();
114}
115
116//-----------------------------------------------------------------------------
117// CordbFunction::QueryInterface
118// Public method to implement IUnknown::QueryInterface.
119// Has standard QI semantics.
120//-----------------------------------------------------------------------------
121HRESULT CordbFunction::QueryInterface(REFIID id, void **pInterface)
122{
123 if (id == IID_ICorDebugFunction)
124 {
125 *pInterface = static_cast<ICorDebugFunction*>(this);
126 }
127 else if (id == IID_ICorDebugFunction2)
128 {
129 *pInterface = static_cast<ICorDebugFunction2*>(this);
130 }
131 else if (id == IID_ICorDebugFunction3)
132 {
133 *pInterface = static_cast<ICorDebugFunction3*>(this);
134 }
135 else if (id == IID_ICorDebugFunction4)
136 {
137 *pInterface = static_cast<ICorDebugFunction4*>(this);
138 }
139 else if (id == IID_IUnknown)
140 {
141 *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugFunction*>(this));
142 }
143 else
144 {
145 *pInterface = NULL;
146 return E_NOINTERFACE;
147 }
148
149 ExternalAddRef();
150 return S_OK;
151}
152
153//-----------------------------------------------------------------------------
154// CordbFunction::GetModule
155// Public method (implements ICorDebugFunction::GetModule).
156// Get the ICorDebugModule (external representation of a module) that this
157// Function is contained in. All functions live in exactly 1 module.
158// This is related to the 'CordbModule* GetModule()' method which returns the
159// internal module representation for the containing module.
160//
161// Parameters:
162// ppModule - out parameter to hold module.
163//
164// Return values:
165// S_OK iff *ppModule is set.
166//-----------------------------------------------------------------------------
167HRESULT CordbFunction::GetModule(ICorDebugModule **ppModule)
168{
169 PUBLIC_API_ENTRY(this);
170 FAIL_IF_NEUTERED(this);
171 VALIDATE_POINTER_TO_OBJECT(ppModule, ICorDebugModule **);
172
173 HRESULT hr = S_OK;
174
175 // Module is set on creation, so just return it.
176 *ppModule = static_cast<ICorDebugModule*> (m_pModule);
177 m_pModule->ExternalAddRef();
178
179 return hr;
180}
181
182//-----------------------------------------------------------------------------
183// CordbFunction::GetClass
184// Public function to get ICorDebugClass that this function is in.
185//
186// Parameters:
187// ppClass - out parameter holding which class this function lives in.
188//
189// Return value:
190// S_OK iff *ppClass is set.
191//-----------------------------------------------------------------------------
192HRESULT CordbFunction::GetClass(ICorDebugClass **ppClass)
193{
194 PUBLIC_API_ENTRY(this);
195 FAIL_IF_NEUTERED(this);
196 VALIDATE_POINTER_TO_OBJECT(ppClass, ICorDebugClass **);
197 ATT_ALLOW_LIVE_DO_STOPGO(GetProcess());
198 *ppClass = NULL;
199
200 HRESULT hr = S_OK;
201
202 if (m_pClass == NULL)
203 {
204 // We're not looking for any particular version, just
205 // the class info. This seems like the best version to request
206 hr = InitParentClassOfFunction();
207
208 if (FAILED(hr))
209 goto LExit;
210 }
211
212 *ppClass = static_cast<ICorDebugClass*> (m_pClass);
213
214LExit:
215 if (FAILED(hr))
216 return hr;
217
218 if (*ppClass)
219 {
220 m_pClass->ExternalAddRef();
221 return S_OK;
222 }
223 else
224 return S_FALSE;
225}
226
227//-----------------------------------------------------------------------------
228// CordbFunction::GetToken
229// Public function to get the metadata token for this function.
230// This is a MethodDef, which is scoped to a module.
231//
232// Parameters:
233// pMemberDef - out parameter to hold token.
234//
235// Return values:
236// S_OK if pMemberDef is set.
237//-----------------------------------------------------------------------------
238HRESULT CordbFunction::GetToken(mdMethodDef *pMemberDef)
239{
240 PUBLIC_API_ENTRY(this);
241 FAIL_IF_NEUTERED(this);
242 VALIDATE_POINTER_TO_OBJECT(pMemberDef, mdMethodDef *);
243
244
245 // Token is set on creation, so no updating needed.
246 CONSISTENCY_CHECK_MSGF((TypeFromToken(m_MDToken) == mdtMethodDef),
247 ("CordbFunction token (%08x) is not a mdtMethodDef. This=%p", m_MDToken, this));
248
249 *pMemberDef = m_MDToken;
250 return S_OK;
251}
252
253//-----------------------------------------------------------------------------
254// CordbFunction::GetILCode
255// Public function to get an ICorDebugCode object for the IL code in
256// this function.
257// If we EnC, we get a new ICorDebugFunction, so the IL code & function
258// should be 1:1.
259//
260// Parameters:
261// ppCode - out parameter to hold the code object.
262//
263// Return value:
264// S_OK iff *ppCode != NULL. Else error.
265//-----------------------------------------------------------------------------
266HRESULT CordbFunction::GetILCode(ICorDebugCode ** ppCode)
267{
268 PUBLIC_REENTRANT_API_ENTRY(this);
269 FAIL_IF_NEUTERED(this);
270 VALIDATE_POINTER_TO_OBJECT(ppCode, ICorDebugCode **);
271 ATT_ALLOW_LIVE_DO_STOPGO(GetProcess());
272
273 *ppCode = NULL;
274 HRESULT hr = S_OK;
275
276 // Get the code object.
277 CordbILCode * pCode = NULL;
278 hr = GetILCode(&pCode);
279 _ASSERTE((pCode == NULL) == FAILED(hr));
280
281 if (FAILED(hr))
282 return hr;
283
284 *ppCode = (ICorDebugCode *)pCode;
285
286 return hr;
287}
288
289//-----------------------------------------------------------------------------
290// CordbFunction::GetNativeCode
291// Public API (ICorDebugFunction::GetNativeCode) to get the native code for
292// this function.
293// Note that this gets a pretty much random version of the native code when the
294// function is a generic method that gets JITted more than once, e.g. for generics.
295// Use EnumerateNativeCode instead in that case.
296//
297// Parameters:
298// ppCode - out parameter yeilding the native code object.
299//
300// Returns:
301// S_OK iff *ppCode is set.
302// CORDBG_E_CODE_NOT_AVAILABLE if there is no native code. This is common
303// if the function is not yet jitted.
304//-----------------------------------------------------------------------------
305HRESULT CordbFunction::GetNativeCode(ICorDebugCode **ppCode)
306{
307 PUBLIC_API_ENTRY(this);
308 FAIL_IF_NEUTERED(this);
309 VALIDATE_POINTER_TO_OBJECT(ppCode, ICorDebugCode **);
310 ATT_ALLOW_LIVE_DO_STOPGO(GetProcess());
311
312 HRESULT hr = S_OK;
313
314 // Make sure native code is updated before we go searching it.
315 hr = InitNativeCodeInfo();
316 if (FAILED(hr))
317 return hr;
318
319 // Generic methods may be jitted multiple times for different native instantiations,
320 // and so have 1:n relationship between IL:Native. CordbFunction is 1:1 with IL,
321 // CordbNativeCode is 1:1 with native.
322 // The interface here only lets us return 1 CordbNativeCode object, so we are
323 // returning an arbitrary one
324 RSLockHolder lockHolder(GetProcess()->GetProcessLock());
325 _ASSERTE(m_nativeCode == NULL || m_nativeCode->GetVersion() == m_dwEnCVersionNumber);
326
327 if (m_nativeCode == NULL)
328 {
329 hr = CORDBG_E_CODE_NOT_AVAILABLE; // This is the case for an unjitted function,
330 // and so it will be very common.
331 }
332 else
333 {
334 m_nativeCode->ExternalAddRef();
335 *ppCode = m_nativeCode;
336 hr = S_OK;
337 }
338
339 return hr;
340}
341
342
343//-----------------------------------------------------------------------------
344// CordbFunction::GetCode
345// Internal method to get the IL code for this function. Each CordbFunction is
346// 1:1 with IL, so there is a unique IL Code object to hand out.
347//
348// Parameters:
349// ppCode - out parameter, the IL code object for this function. This should
350// be set to NULL on entry.
351// Return value:
352// S_OK iff *ppCode is set. Else error.
353//-----------------------------------------------------------------------------
354HRESULT CordbFunction::GetILCode(CordbILCode ** ppCode)
355{
356 FAIL_IF_NEUTERED(this);
357 INTERNAL_SYNC_API_ENTRY(GetProcess()); //
358 VALIDATE_POINTER_TO_OBJECT(ppCode, ICorDebugCode **);
359
360 _ASSERTE(*ppCode == NULL && "Common source of errors is getting addref'd copy here and never Release()ing it");
361 *ppCode = NULL;
362
363 // Its okay to do this if the process is not sync'd.
364 CORDBRequireProcessStateOK(GetProcess());
365
366 // Fetch all information about this function.
367 HRESULT hr = S_OK;
368 CordbILCode * pCode = NULL;
369
370 hr = GetILCodeAndSigToken();
371 if (FAILED(hr))
372 {
373 return hr;
374 }
375
376 // It's possible that m_ILCode will still be NULL.
377 pCode = m_pILCode;
378
379 if (pCode != NULL)
380 {
381 pCode->ExternalAddRef();
382 *ppCode = pCode;
383
384 return hr;
385 }
386 else
387 {
388 return CORDBG_E_CODE_NOT_AVAILABLE;
389 }
390} // CordbFunction::GetCode
391
392//-----------------------------------------------------------------------------
393// CordbFunction::CreateBreakpoint
394// Implements ICorDebugFunction::CreateBreakpoint
395// Creates a breakpoint at IL offset 0 (which is after the prolog) of the function.
396// The function does not need to be jitted yet.
397//
398// Parameters:
399// ppBreakpoint - out parameter for newly created breakpoint object.
400//
401// Return:
402// S_OK - on success. Else error.
403//----------------------------------------------------------------------------
404HRESULT CordbFunction::CreateBreakpoint(ICorDebugFunctionBreakpoint **ppBreakpoint)
405{
406 HRESULT hr = S_OK;
407
408 PUBLIC_API_ENTRY(this);
409 FAIL_IF_NEUTERED(this);
410 VALIDATE_POINTER_TO_OBJECT(ppBreakpoint, ICorDebugFunctionBreakpoint **);
411 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
412
413 RSExtSmartPtr<ICorDebugCode> pCode;
414
415 // Use the IL code so that we stop after the prolog
416 hr = GetILCode(&pCode);
417
418 if (SUCCEEDED(hr))
419 {
420 hr = pCode->CreateBreakpoint(0, ppBreakpoint);
421 }
422
423 return hr;
424}
425
426#ifdef EnC_SUPPORTED
427//-----------------------------------------------------------------------------
428// CordbFunction::MakeOld
429// Internal method to do any cleanup necessary when a Function is no longer
430// the most current.
431//-----------------------------------------------------------------------------
432void CordbFunction::MakeOld()
433{
434 if (m_pILCode != NULL)
435 {
436 m_pILCode->MakeOld();
437 }
438}
439#endif
440
441//-----------------------------------------------------------------------------
442// CordbFunction::GetLocalVarSigToken
443// Public function (implements ICorDebugFunction::GetLocalVarSigToken) to
444// get signature token.
445//
446// Parameters:
447// pmdSig - out parameter to hold signature token, which is scoped to the
448// function's module.
449//
450// Return value:
451// S_OK if pmdSig is set.
452//-----------------------------------------------------------------------------
453HRESULT CordbFunction::GetLocalVarSigToken(mdSignature *pmdSig)
454{
455 PUBLIC_API_ENTRY(this);
456 FAIL_IF_NEUTERED(this);
457 VALIDATE_POINTER_TO_OBJECT(pmdSig, mdSignature *);
458
459 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
460
461 // This will initialize the token.
462 HRESULT hr = GetILCodeAndSigToken();
463 if (FAILED(hr))
464 return hr;
465
466 *pmdSig = GetILCode()->GetLocalVarSigToken();
467
468 return S_OK;
469}
470
471//-----------------------------------------------------------------------------
472// CordbFunction::GetCurrentVersionNumber
473// Public method for ICorDebugFunction::GetCurrentVersionNumber.
474// Gets the most recent (highest) EnC version number of this Function.
475// See CordbModule for EnC version number semantics.
476//
477// Parameters
478// pnCurrentVersion - out parameter to hold the version number.
479//
480// Returns:
481// S_OK on success.
482//-----------------------------------------------------------------------------
483HRESULT CordbFunction::GetCurrentVersionNumber(ULONG32 *pnCurrentVersion)
484{
485 PUBLIC_API_ENTRY(this);
486 FAIL_IF_NEUTERED(this);
487 VALIDATE_POINTER_TO_OBJECT(pnCurrentVersion, ULONG32 *);
488
489 HRESULT hr = S_OK;
490 RSLockHolder lockHolder(GetProcess()->GetProcessLock());
491
492 // the most current version will always be the one found.
493 CordbFunction* curFunc = m_pModule->LookupFunctionLatestVersion(m_MDToken);
494
495 // will always find at least ourself
496 PREFIX_ASSUME(curFunc != NULL);
497
498 *pnCurrentVersion = (ULONG32)(curFunc->m_dwEnCVersionNumber);
499
500#ifdef EnC_SUPPORTED
501 _ASSERTE( *pnCurrentVersion >= this->m_dwEnCVersionNumber );
502#else
503 _ASSERTE(*pnCurrentVersion == CorDB_DEFAULT_ENC_FUNCTION_VERSION);
504#endif
505
506 return hr;
507}
508
509//-----------------------------------------------------------------------------
510// CordbFunction::GetVersionNumber
511// Public method for ICorDebugFunction2::GetVersionNumber.
512// Gets the EnC version number of this specific Function instance.
513// See CordbModule for EnC version number semantics.
514//
515// Parameters
516// pnVersion - out parameter to hold the version number.
517//
518// Returns:
519// S_OK on success.
520//-----------------------------------------------------------------------------
521HRESULT CordbFunction::GetVersionNumber(ULONG32 *pnVersion)
522{
523 PUBLIC_API_ENTRY(this);
524 FAIL_IF_NEUTERED(this);
525 VALIDATE_POINTER_TO_OBJECT(pnVersion, ULONG32 *);
526
527 // This API existed in V1.0 but wasn't implemented. It needs V2 support to work.
528 if (! this->GetProcess()->SupportsVersion(ver_ICorDebugFunction2))
529 {
530 return E_NOTIMPL;
531 }
532
533 *pnVersion = (ULONG32)m_dwEnCVersionNumber;
534
535#ifdef EnC_SUPPORTED
536 _ASSERTE(*pnVersion >= CorDB_DEFAULT_ENC_FUNCTION_VERSION);
537#else
538 _ASSERTE(*pnVersion == CorDB_DEFAULT_ENC_FUNCTION_VERSION);
539#endif
540
541 return S_OK;
542}
543
544//-----------------------------------------------------------------------------
545// CordbFunction::GetVersionNumber
546// Public method for ICorDebugFunction2::GetVersionNumber.
547// Gets the EnC version number of this specific Function instance.
548// See CordbModule for EnC version number semantics.
549//
550// Parameters
551// pnVersion - out parameter to hold the version number.
552//
553// Returns:
554// S_OK on success.
555//-----------------------------------------------------------------------------
556HRESULT CordbFunction::GetActiveReJitRequestILCode(ICorDebugILCode **ppReJitedILCode)
557{
558 HRESULT hr = S_OK;
559 VALIDATE_POINTER_TO_OBJECT(ppReJitedILCode, ICorDebugILCode **);
560 PUBLIC_API_BEGIN(this);
561 {
562 *ppReJitedILCode = NULL;
563
564 VMPTR_ILCodeVersionNode vmILCodeVersionNode = VMPTR_ILCodeVersionNode::NullPtr();
565 GetProcess()->GetDAC()->GetActiveRejitILCodeVersionNode(GetModule()->m_vmModule, m_MDToken, &vmILCodeVersionNode);
566 if (!vmILCodeVersionNode.IsNull())
567 {
568 RSSmartPtr<CordbReJitILCode> pILCode;
569 IfFailThrow(LookupOrCreateReJitILCode(vmILCodeVersionNode, &pILCode));
570 IfFailThrow(pILCode->QueryInterface(IID_ICorDebugILCode, (void**)ppReJitedILCode));
571 }
572 }
573 PUBLIC_API_END(hr);
574 return hr;
575}
576
577//-----------------------------------------------------------------------------
578// CordbFunction::CreateNativeBreakpoint
579// Public method for ICorDebugFunction4::CreateNativeBreakpoint.
580// Sets a breakpoint at native offset 0 for all native code versions of a method.
581//
582// Parameters
583// pnVersion - out parameter to hold the version number.
584//
585// Returns:
586// S_OK on success.
587//-----------------------------------------------------------------------------
588HRESULT CordbFunction::CreateNativeBreakpoint(ICorDebugFunctionBreakpoint **ppBreakpoint)
589{
590 PUBLIC_API_ENTRY(this);
591 FAIL_IF_NEUTERED(this);
592 VALIDATE_POINTER_TO_OBJECT(ppBreakpoint, ICorDebugFunctionBreakpoint **);
593 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
594
595 HRESULT hr = S_OK;
596
597 RSExtSmartPtr<CordbILCode> pCode;
598
599 hr = GetILCode(&pCode);
600
601 if (SUCCEEDED(hr))
602 {
603 hr = pCode->CreateNativeBreakpoint(ppBreakpoint);
604 }
605
606 return hr;
607}
608
609// determine whether we have a native-only implementation
610// Arguments:
611// Input: none (we use information in various data members of this instance of CordbFunction: m_isNativeImpl,
612// m_pIMImport, m_EnCCount)
613// Output none, although we will set m_isNativeImpl to true iff the function has a native-only implementation
614
615void CordbFunction::InitNativeImpl()
616{
617 INTERNAL_SYNC_API_ENTRY(GetProcess());
618
619 // Bail now if we've already discovered that this function is implemented natively as part of the Runtime.
620 if (m_fIsNativeImpl != kUnknownImpl)
621 {
622 return;
623 }
624
625 // If we don't have a methodToken then we can't figure out what kind of function this is. This includes functions
626 // such as LCG and ILStubs. In the past we created codepaths that avoided ever calling in here in the common case
627 // and there would have been asserts and exceptions in the uncommon cases. Now I have just officially let the
628 // function handle staying as an 'unknown' impl. In such a state it provides no IL, no sigtoken, no native code, and
629 // no parent class.
630 if (m_MDToken == mdMethodDefNil)
631 {
632 return;
633 }
634
635 // Figure out if this function is implemented as a native part of the Runtime. If it is, then this ICorDebugFunction
636 // is just a container for certain Right Side bits of info, i.e., module, class, token, etc.
637 DWORD attrs;
638 DWORD implAttrs;
639 ULONG ulRVA;
640 BOOL isDynamic;
641
642 IfFailThrow(GetModule()->GetMetaDataImporter()->GetMethodProps(m_MDToken, NULL, NULL, 0, NULL,
643 &attrs, NULL, NULL, &ulRVA, &implAttrs));
644 isDynamic = GetModule()->IsDynamic();
645
646 // A method has associated IL if its RVA is non-zero, unless it is a dynamic module
647 // @todo : if RVA is 0 and function has been EnC'd then it isn't native. Remove isEnC
648 // condition when the compilers stop generating 0 for an RVA.
649 BOOL isEnC = (GetModule()->m_EnCCount != 0);
650 if (IsMiNative(implAttrs) || ((isDynamic == FALSE) && (isEnC == FALSE) && (ulRVA == 0)))
651 {
652
653 m_fIsNativeImpl = kNativeOnly;
654 }
655 else
656 {
657 m_fIsNativeImpl = kHasIL;
658 }
659
660} // CordbFunction::GetProcessAndCheckForNativeImpl
661
662// Returns the function's ILCode and SigToken
663// Arguments:
664// Input: none (required info comes from various data members of this instance of CordbFunction
665// Output (required):
666// none explicit, but this will:
667// construct a new instance of CordbILCode and assign it to m_pILCode
668
669HRESULT CordbFunction::GetILCodeAndSigToken()
670{
671 INTERNAL_SYNC_API_ENTRY(GetProcess());
672
673 CordbProcess * pProcess = m_pModule->GetProcess();
674 HRESULT hr = S_OK;
675
676 EX_TRY
677 {
678
679 // ensure that we're not trying to get information about a native-only function
680 InitNativeImpl();
681 if (m_fIsNativeImpl == kNativeOnly || m_fIsNativeImpl == kUnknownImpl)
682 {
683 ThrowHR(CORDBG_E_FUNCTION_NOT_IL);
684 }
685
686 if (m_pILCode == NULL)
687 {
688 // we haven't gotten the information previously
689
690 _ASSERTE(pProcess != NULL);
691
692 // This target buffer and mdSignature might never have their values changed from the
693 // initial ones if the dump target is missing memory. TargetBuffer has a default
694 // constructor to zero its data and localVarSigToken is explicitly inited.
695 TargetBuffer codeInfo;
696 mdSignature localVarSigToken = mdSignatureNil;
697 SIZE_T currentEnCVersion;
698
699 {
700 RSLockHolder lockHolder(GetProcess()->GetProcessLock());
701
702 // In the dump case we may not have the backing memory for this. In such a case
703 // we construct an empty ILCode object and leave the signatureToken as mdSignatureNil.
704 // It may also be the case that the memory we read from the dump be inconsistent (huge method size)
705 // and we also fallback on creating an empty ILCode object.
706 // See issue DD 273199 for cases where IL and NGEN metadata mismatch (different RVAs).
707 ALLOW_DATATARGET_MISSING_OR_INCONSISTENT_MEMORY(
708 pProcess->GetDAC()->GetILCodeAndSig(m_pModule->GetRuntimeDomainFile(),
709 m_MDToken,
710 &codeInfo,
711 &localVarSigToken);
712 );
713
714 currentEnCVersion = m_pModule->LookupFunctionLatestVersion(m_MDToken)->m_dwEnCVersionNumber;
715 }
716
717 LOG((LF_CORDB,LL_INFO10000,"R:CF::GICAST: looking for IL code, version 0x%x\n", currentEnCVersion));
718
719 if (m_pILCode == NULL)
720 {
721 LOG((LF_CORDB,LL_INFO10000,"R:CF::GICAST: not found, creating...\n"));
722 if(codeInfo.pAddress == 0)
723 {
724 LOG((LF_CORDB,LL_INFO10000,"R:CF::GICAST: memory was missing - empty ILCode being created\n"));
725 }
726
727 // If everything succeeded, we set the IL code object (it's an outparam here).
728 _ASSERTE(m_pILCode == NULL);
729 m_pILCode.Assign(new(nothrow)CordbILCode(this,
730 codeInfo,
731 currentEnCVersion,
732 localVarSigToken));
733
734 if (m_pILCode == NULL)
735 {
736 ThrowHR(E_OUTOFMEMORY);
737 }
738 }
739 }
740 }
741 EX_CATCH_HRESULT(hr);
742 return hr;
743} // CordbFunction::GetILCodeAndSigToken
744
745
746// Get the metadata token for the class to which a function belongs.
747// Arguments:
748// Input:
749// funcMetadataToken - the metadata token for the method
750// Output (required):
751// classMetadataToken - the metadata token for the class to which the method belongs
752mdTypeDef CordbFunction::InitParentClassOfFunctionHelper(mdToken funcMetadataToken)
753{
754 // Get the class this method is in.
755 mdToken tkParent = mdTypeDefNil;
756 IfFailThrow(GetModule()->GetInternalMD()->GetParentToken(funcMetadataToken, &tkParent));
757 _ASSERTE(TypeFromToken(tkParent) == mdtTypeDef);
758
759 return tkParent;
760} // CordbFunction::InitParentClassOfFunctionHelper
761
762// Get the class to which a given function belongs
763// Arguments:
764// Input: none (required information comes from data members of this instance of CordbFunction)
765// Output (required): none, but sets m_pClass
766HRESULT CordbFunction::InitParentClassOfFunction()
767{
768 INTERNAL_SYNC_API_ENTRY(GetProcess());
769
770 CordbProcess * pProcess = m_pModule->GetProcess();
771 (void)pProcess; //prevent "unused variable" error from GCC
772 HRESULT hr = S_OK;
773
774 EX_TRY
775 {
776
777 // ensure that we're not trying to get information about a native-only function
778 InitNativeImpl();
779 if (m_fIsNativeImpl == kNativeOnly || m_fIsNativeImpl == kUnknownImpl)
780 {
781 ThrowHR(CORDBG_E_FUNCTION_NOT_IL);
782 }
783
784 mdTypeDef classMetadataToken;
785 VMPTR_DomainFile vmDomainFile = m_pModule->GetRuntimeDomainFile();
786
787 classMetadataToken = InitParentClassOfFunctionHelper(m_MDToken);
788
789 if ((m_pClass == NULL) && (classMetadataToken != mdTypeDefNil))
790 {
791 // we haven't gotten the information previously but we have it now
792
793 _ASSERTE(pProcess != NULL);
794
795 CordbAssembly *pAssembly = m_pModule->GetCordbAssembly();
796 PREFIX_ASSUME(pAssembly != NULL);
797
798 CordbModule* pClassModule = pAssembly->GetAppDomain()->LookupOrCreateModule(vmDomainFile);
799 PREFIX_ASSUME(pClassModule != NULL);
800
801 CordbClass *pClass;
802 hr = pClassModule->LookupOrCreateClass(classMetadataToken, &pClass);
803
804 IfFailThrow(hr);
805
806 _ASSERTE(pClass != NULL);
807 m_pClass = pClass;
808 }
809 }
810 EX_CATCH_HRESULT(hr);
811 return hr;
812
813} // CordbFunction::InitParentClassOfFunction
814
815// Get information about the native code blob for a function and add it to m_nativeCodeTable
816// Arguments:
817// Input: none, but we use some data members of this instance of CordbFunction
818// Output: standard HRESULT value
819// Notes: Apart from the HRESULT, this function will build a new instance of CordbNativeCode and
820// add it to the hash table of CordbNativeCodes for this function, unless we have done that
821// previously
822
823HRESULT CordbFunction::InitNativeCodeInfo()
824{
825 INTERNAL_SYNC_API_ENTRY(GetProcess());
826
827 CordbProcess * pProcess = m_pModule->GetProcess();
828 HRESULT hr = S_OK;
829
830 EX_TRY
831 {
832
833 // ensure that we're not trying to get information about a native-only function
834 InitNativeImpl();
835 if (m_fIsNativeImpl == kNativeOnly || m_fIsNativeImpl == kUnknownImpl)
836 {
837 ThrowHR(CORDBG_E_FUNCTION_NOT_IL);
838 }
839
840 _ASSERTE(pProcess != NULL);
841
842 // storage for information retrieved from the DAC. This is cleared in the constructor, so it
843 // won't contain garbage if we don't use the DAC to retrieve information we already got before.
844 NativeCodeFunctionData codeInfo;
845
846 if (m_nativeCode == NULL)
847 {
848 // Get the native code information from the DAC
849 // PERF: this call is potentially more costly than it needs to be
850 // All we actually need is the start address and method desc which are cheap to get relative
851 // to some of the other members. So far this doesn't appear to be a perf hotspot, but if it
852 // shows up in some scenario it wouldn't be too hard to improve it
853 pProcess->GetDAC()->GetNativeCodeInfo(m_pModule->GetRuntimeDomainFile(), m_MDToken, &codeInfo);
854 }
855
856 // populate the m_nativeCode pointer with the code info we found
857 if (codeInfo.IsValid())
858 {
859 m_nativeCode.Assign(m_pModule->LookupOrCreateNativeCode(m_MDToken, codeInfo.vmNativeCodeMethodDescToken,
860 codeInfo.m_rgCodeRegions[kHot].pAddress));
861 }
862
863 }
864 EX_CATCH_HRESULT(hr);
865 return hr;
866} // CordbFunction::InitNativeCodeInfo
867
868//-----------------------------------------------------------------------------
869// CordbFunction::SetJMCStatus
870// Public method (implements ICorDebugFunction2::SetJMCStatus).
871// Set the JMC (eg, "User code" vs. "Non-user code") status of this function.
872//
873// Parameters:
874// fIsUserCode - true to set this Function to JMC, else False.
875//
876// Returns:
877// S_OK if successfully updated JMC status.
878//-----------------------------------------------------------------------------
879HRESULT CordbFunction::SetJMCStatus(BOOL fIsUserCode)
880{
881 PUBLIC_REENTRANT_API_ENTRY(this);
882 HRESULT hr = S_OK;
883
884 LOG((LF_CORDB,LL_INFO10000,"CordbFunction::SetJMCStatus to %d, (token=0x%08x, module=%p)\n",
885 fIsUserCode, m_MDToken, m_pModule));
886
887 // Make sure the Left-Side is in a good state.
888 FAIL_IF_NEUTERED(this);
889 CordbProcess* pProcess = m_pModule->GetProcess();
890 ATT_REQUIRE_STOPPED_MAY_FAIL(pProcess);
891
892
893
894 // Send an event to the LS to keep it updated.
895
896 // Validation - JMC Steppers don't have defined behavior if
897 // JMC method status gets toggled underneath them. However, we don't have
898 // a good way of verifying which methods are of interest to a JMC stepper.
899 // Having outstanding JMC steppers is dangerous here, but still can be
900 // done safely.
901 // Furthermore, debuggers may want to lazily set JMC status (such as when
902 // code is loaded), which may happen while we have outstanding steppers.
903
904
905 DebuggerIPCEvent event;
906 pProcess->InitIPCEvent(&event, DB_IPCE_SET_METHOD_JMC_STATUS, true, m_pModule->GetAppDomain()->GetADToken());
907 event.SetJMCFunctionStatus.vmDomainFile = m_pModule->GetRuntimeDomainFile();
908 event.SetJMCFunctionStatus.funcMetadataToken = m_MDToken;
909 event.SetJMCFunctionStatus.dwStatus = fIsUserCode;
910
911
912 // Note: two-way event here...
913 hr = pProcess->m_cordb->SendIPCEvent(pProcess, &event, sizeof(DebuggerIPCEvent));
914
915 // Stop now if we can't even send the event.
916 if (!SUCCEEDED(hr))
917 return hr;
918
919 _ASSERTE(event.type == DB_IPCE_SET_METHOD_JMC_STATUS_RESULT);
920
921 return event.hr;
922}
923
924//-----------------------------------------------------------------------------
925// CordbFunction::GetJMCStatus
926// Public function (implements ICorDebugFunction2::GetJMCStatus)
927// Get the JMC status of this function.
928//
929// Parameters:
930// pfIsUserCode - out parameter describing whether this method is user code.
931// true iff this function is user code, else false.
932//
933// Return:
934// returns S_OK if *pfIsUserCode is set.
935//-----------------------------------------------------------------------------
936HRESULT CordbFunction::GetJMCStatus(BOOL * pfIsUserCode)
937{
938 PUBLIC_REENTRANT_API_ENTRY(this);
939 FAIL_IF_NEUTERED(this);
940 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
941 VALIDATE_POINTER_TO_OBJECT(pfIsUserCode, BOOL*);
942
943
944 _ASSERTE(pfIsUserCode != NULL);
945 if (pfIsUserCode == NULL)
946 return E_INVALIDARG;
947
948 // <TODO> @perf - If we know that we haven't updated the JMC status on anything
949 // in this module (we could keep a dirty flag), then we can just cache
950 // the jmc status and not send an event to query each time. </TODO>
951
952 // Make sure the process is in a sane state.
953 CordbProcess* pProcess = m_pModule->GetProcess();
954 _ASSERTE(pProcess != NULL);
955
956 // Ask the left-side if a method is user code or not.
957 DebuggerIPCEvent event;
958 pProcess->InitIPCEvent(&event, DB_IPCE_GET_METHOD_JMC_STATUS, true, m_pModule->GetAppDomain()->GetADToken());
959 event.SetJMCFunctionStatus.vmDomainFile = m_pModule->GetRuntimeDomainFile();
960 event.SetJMCFunctionStatus.funcMetadataToken = m_MDToken;
961
962
963 // Note: two-way event here...
964 HRESULT hr = pProcess->m_cordb->SendIPCEvent(pProcess, &event, sizeof(DebuggerIPCEvent));
965
966 // Stop now if we can't even send the event.
967 if (!SUCCEEDED(hr))
968 return hr;
969
970 _ASSERTE(event.type == DB_IPCE_GET_METHOD_JMC_STATUS_RESULT);
971
972 // update our internal copy of the status.
973 BOOL fIsUserCode = event.SetJMCFunctionStatus.dwStatus;
974
975 *pfIsUserCode = fIsUserCode;
976
977 return event.hr;
978}
979
980
981/*
982 * CordbFunction::GetSig
983 *
984 * Get the method's full metadata signature. This may be cached, but for dynamic modules we'll always read it from
985 * the metadata. This function also returns the argument count and whether or not the method is static.
986 *
987 * Parameters:
988 * pMethodSigParser - OUT: the signature parser class to use for all signature parsing.
989 * pFunctionArgCount - OUT: the number of arguments the method takes.
990 * pFunctionIsStatic - OUT: TRUE if the method is static, FALSE if it is not..
991 *
992 * Returns:
993 * HRESULT for success or failure.
994 *
995 */
996HRESULT CordbFunction::GetSig(SigParser *pMethodSigParser,
997 ULONG *pFunctionArgCount,
998 BOOL *pFunctionIsStatic)
999{
1000 INTERNAL_API_ENTRY(this);
1001 FAIL_IF_NEUTERED(this);
1002
1003 HRESULT hr = S_OK;
1004
1005 // If the module is dynamic, there had better not be a cached locals signature.
1006 _ASSERTE(!GetModule()->IsDynamic() || !m_fCachedMethodValuesValid);
1007
1008 // If the method signature cache is null, then go read the signature from the
1009 // matadata. For dynamic methods we never cache the parser because the method
1010 // may change and the cached value will not match.
1011 if (!m_fCachedMethodValuesValid)
1012 {
1013 PCCOR_SIGNATURE functionSignature;
1014 ULONG size;
1015 DWORD methodAttr = 0;
1016 ULONG argCount;
1017
1018 EX_TRY // @dbgtotod - push this up
1019 {
1020 hr = GetModule()->GetMetaDataImporter()->GetMethodProps(m_MDToken, NULL, NULL, 0, NULL,
1021 &methodAttr, &functionSignature, &size, NULL, NULL);
1022 }
1023 EX_CATCH_HRESULT(hr);
1024 IfFailRet(hr);
1025
1026 SigParser sigParser = SigParser(functionSignature, size);
1027
1028 IfFailRet(sigParser.SkipMethodHeaderSignature(&argCount));
1029
1030 // If this function is not static, then we've got one extra arg.
1031 BOOL isStatic = (methodAttr & mdStatic) != 0;
1032
1033 if (!isStatic)
1034 {
1035 argCount++;
1036 }
1037
1038 // Cache the value for non-dynamic modules, so this is faster later.
1039 if (!GetModule()->IsDynamic())
1040 {
1041 m_methodSigParserCached = sigParser;
1042 m_argCountCached = argCount;
1043 m_fIsStaticCached = isStatic;
1044 m_fCachedMethodValuesValid = TRUE;
1045 }
1046 else
1047 {
1048 // This is the Dynamic method case, so we can't cache. Just leave fields blank
1049 // and set out-parameters based off locals.
1050 if (pMethodSigParser != NULL)
1051 {
1052 *pMethodSigParser = sigParser;
1053 }
1054
1055 if (pFunctionArgCount != NULL)
1056 {
1057 *pFunctionArgCount = argCount;
1058 }
1059
1060 if (pFunctionIsStatic != NULL)
1061 {
1062 *pFunctionIsStatic = isStatic;
1063 }
1064 }
1065 }
1066
1067 if (m_fCachedMethodValuesValid)
1068 {
1069 //
1070 // Retrieve values from cache
1071 //
1072
1073 if (pMethodSigParser != NULL)
1074 {
1075 //
1076 // Give them a new instance of the cached value
1077 //
1078 *pMethodSigParser = m_methodSigParserCached;
1079 }
1080
1081 if (pFunctionArgCount != NULL)
1082 {
1083 *pFunctionArgCount = m_argCountCached;
1084 }
1085
1086 if (pFunctionIsStatic != NULL)
1087 {
1088 *pFunctionIsStatic = m_fIsStaticCached;
1089 }
1090
1091 }
1092
1093 //
1094 // We should never have a cached value for in a dynamic module.
1095 //
1096 CONSISTENCY_CHECK_MSGF(((GetModule()->IsDynamic() && !m_fCachedMethodValuesValid) ||
1097 (!GetModule()->IsDynamic() && m_fCachedMethodValuesValid)),
1098 ("No dynamic modules should be cached! Module=%p This=%p", GetModule(), this));
1099
1100 return hr;
1101}
1102
1103
1104//-----------------------------------------------------------------------------
1105// CordbFunction::GetArgumentType
1106// Internal method. Given an 0-based IL argument number, return its type.
1107// This can't access hidden parameters.
1108//
1109// Parameters:
1110// dwIndex - 0-based index for IL argument number. For instance types,
1111// 'this' argument is #0. For static types, first argument is #0.
1112// pInst - instantiation information if this is a generic function. Eg,
1113// if function is List<T>, inst describes T.
1114// ppResultType - out parameter, yields to CordbType of the argument.
1115//
1116// Return:
1117// S_OK on success.
1118//
1119HRESULT CordbFunction::GetArgumentType(DWORD dwIndex,
1120 const Instantiation * pInst,
1121 CordbType ** ppResultType)
1122{
1123 FAIL_IF_NEUTERED(this);
1124 INTERNAL_SYNC_API_ENTRY(GetProcess());
1125
1126 HRESULT hr = S_OK;
1127
1128 // Get the method's signature, which contains the types for all the arguments.
1129 SigParser sigParser;
1130 ULONG cMethodArgs;
1131 BOOL fMethodIsStatic;
1132
1133 IfFailRet(GetSig(&sigParser, &cMethodArgs, &fMethodIsStatic));
1134
1135 // Check the index
1136 if (dwIndex >= cMethodArgs)
1137 {
1138 return E_INVALIDARG;
1139 }
1140
1141 if (!fMethodIsStatic)
1142 {
1143 if (dwIndex == 0)
1144 {
1145 // Return the signature for the 'this' pointer for the
1146 // class this method is in.
1147 return m_pClass->GetThisType(pInst, ppResultType);
1148 }
1149 else
1150 {
1151 dwIndex--;
1152 }
1153 }
1154
1155 // Run the signature and find the required argument.
1156 for (unsigned int i = 0; i < dwIndex; i++)
1157 {
1158 IfFailRet(sigParser.SkipExactlyOne());
1159 }
1160
1161 hr = CordbType::SigToType(m_pModule, &sigParser, pInst, ppResultType);
1162
1163 return hr;
1164}
1165
1166//-----------------------------------------------------------------------------
1167// CordbFunction::NotifyCodeCreated
1168// Internal method. Allows CordbFunctions to get access to a canonical native code entry
1169// that they will return when asked for native code. The 1:1 mapping between
1170// function and code was invalidated by generics but debuggers continue to use
1171// the old API. When they do we need to have some code to hand them back even
1172// though it is an arbitrary instantiation. Note that that the cannonical code
1173// here is merely the first one that a user inspects... it is not guaranteed to
1174// be the same in each debugging session but once set it will never change. It is
1175// also definately NOT guaranteed to be the instantation over the runtime type
1176// __Canon.
1177//
1178// Parameters:
1179// nativeCode - the code which corresponds to this function
1180//
1181VOID CordbFunction::NotifyCodeCreated(CordbNativeCode* nativeCode)
1182{
1183 INTERNAL_SYNC_API_ENTRY(GetProcess());
1184 CONTRACTL
1185 {
1186 NOTHROW;
1187 }
1188 CONTRACTL_END;
1189
1190 // Grab this native code as the canonical one if we don't already
1191 // have a canonical entry
1192 if(m_nativeCode == NULL)
1193 m_nativeCode.Assign(nativeCode);
1194}
1195
1196
1197//-----------------------------------------------------------------------------
1198// LookupOrCreateReJitILCode finds an existing version of CordbReJitILCode in the given function.
1199// If the CordbReJitILCode doesn't exist, it creates it.
1200//
1201//
1202HRESULT CordbFunction::LookupOrCreateReJitILCode(VMPTR_ILCodeVersionNode vmILCodeVersionNode, CordbReJitILCode** ppILCode)
1203{
1204 INTERNAL_API_ENTRY(this);
1205
1206 HRESULT hr = S_OK;
1207 _ASSERTE(GetProcess()->ThreadHoldsProcessLock());
1208
1209 CordbReJitILCode * pILCode = m_reJitILCodes.GetBase(VmPtrToCookie(vmILCodeVersionNode));
1210
1211 // special case non-existance as need to add to the hash table too
1212 if (pILCode == NULL)
1213 {
1214 // we don't yet support ENC and ReJIT together, so the version should be 1
1215 _ASSERTE(m_dwEnCVersionNumber == 1);
1216 RSInitHolder<CordbReJitILCode> pILCodeHolder(new CordbReJitILCode(this, 1, vmILCodeVersionNode));
1217 IfFailRet(m_reJitILCodes.AddBase(pILCodeHolder));
1218 pILCode = pILCodeHolder;
1219 pILCodeHolder.ClearAndMarkDontNeuter();
1220 }
1221
1222 pILCode->InternalAddRef();
1223 *ppILCode = pILCode;
1224 return S_OK;
1225}
1226