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.
9 // This module implements the tracking and execution of rejit requests. In order to avoid
10 // any overhead on the non-profiled case we don't intrude on any 'normal' data structures
11 // except one member on the AppDomain to hold our main hashtable and crst (the
12 // ReJitManager). See comments in rejit.h to understand relationships between ReJitInfo,
13 // SharedReJitInfo, and ReJitManager, particularly SharedReJitInfo::InternalFlags which
14 // capture the state of a rejit request, and ReJitInfo::InternalFlags which captures the
15 // state of a particular MethodDesc from a rejit request.
17 // A ReJIT request (tracked via SharedReJitInfo) is made at the level of a (Module *,
18 // methodDef) pair, and thus affects all instantiations of a generic. Each MethodDesc
19 // affected by a ReJIT request has its state tracked via a ReJitInfo instance. A
20 // ReJitInfo can represent a rejit request against an already-jitted MethodDesc, or a
21 // rejit request against a not-yet-jitted MethodDesc (called a "pre-rejit" request). A
22 // Pre-ReJIT request happens when a profiler specifies a (Module *, methodDef) pair that
23 // has not yet been JITted, or that represents a generic function which always has the
24 // potential to JIT new instantiations in the future.
26 // Top-level functions in this file of most interest are:
28 // * (static) code:ReJitManager::RequestReJIT:
29 // Profiling API just delegates all rejit requests directly to this function. It is
30 // responsible for recording the request into the appropriate ReJITManagers and for
31 // jump-stamping any already-JITted functions affected by the request (so that future
32 // calls hit the prestub)
34 // * code:ReJitManager::DoReJitIfNecessary:
35 // MethodDesc::DoPrestub calls this to determine whether it's been invoked to do a rejit.
36 // If so, ReJitManager::DoReJitIfNecessary is responsible for (indirectly) gathering the
37 // appropriate IL and codegen flags, calling UnsafeJitFunction(), and redirecting the
38 // jump-stamp from the prestub to the newly-rejitted code.
40 // * code:PublishMethodHolder::PublishMethodHolder
41 // MethodDesc::MakeJitWorker() calls this to determine if there's an outstanding
42 // "pre-rejit" request for a MethodDesc that has just been jitted for the first time. We
43 // also call this from MethodDesc::CheckRestore when restoring generic methods.
44 // The holder applies the jump-stamp to the
45 // top of the originally JITted code, with the jump target being the prestub.
46 // When ReJIT is enabled this holder enters the ReJIT
47 // lock to enforce atomicity of doing the pre-rejit-jmp-stamp & publishing/restoring
48 // the PCODE, which is required to avoid races with a profiler that calls RequestReJIT
49 // just as the method finishes compiling/restoring.
51 // * code:PublishMethodTableHolder::PublishMethodTableHolder
52 // Does the same thing as PublishMethodHolder except iterating over every
53 // method in the MethodTable. This is called from MethodTable::SetIsRestored.
55 // * code:ReJitManager::GetCurrentReJitFlags:
56 // CEEInfo::canInline() calls this as part of its calculation of whether it may inline a
57 // given method. (Profilers may specify on a per-rejit-request basis whether the rejit of
58 // a method may inline callees.)
63 // For a given Module/MethodDef there is at most 1 SharedReJitInfo that is not Reverted,
64 // though there may be many that are in the Reverted state. If a method is rejitted
65 // multiple times, with multiple versions actively in use on the stacks, then all but the
66 // most recent are put into the Reverted state even though they may not yet be physically
67 // reverted and pitched yet.
69 // For a given MethodDesc there is at most 1 ReJitInfo in the kJumpToPrestub or kJumpToRejittedCode
72 // The ReJitManager::m_crstTable lock is held whenever reading or writing to that
73 // ReJitManager instance's table (including state transitions applied to the ReJitInfo &
74 // SharedReJitInfo instances stored in that table).
76 // The ReJitManager::m_crstTable lock is never held during callbacks to the profiler
77 // such as GetReJITParameters, ReJITStarted, JITComplete, ReportReJITError
79 // Any thread holding the ReJitManager::m_crstTable lock can't block during runtime suspension
80 // therefore it can't call any GC_TRIGGERS functions
82 // Transitions between SharedRejitInfo states happen only in the following cicumstances:
83 // 1) New SharedRejitInfo added to table (Requested State)
84 // Inside RequestRejit
85 // Global Crst held, table Crst held
87 // 2) Requested -> GettingReJITParameters
88 // Inside DoRejitIfNecessary
89 // Global Crst NOT held, table Crst held
91 // 3) GettingReJITParameters -> Active
92 // Inside DoRejitIfNecessary
93 // Global Crst NOT held, table Crst held
96 // Inside RequestRejit or RequestRevert
97 // Global Crst held, table Crst held
100 // Transitions between RejitInfo states happen only in the following circumstances:
101 // 1) New RejitInfo added to table (kJumpNone state)
102 // Inside RequestRejit, DoJumpStampIfNecessary
103 // Global Crst MAY/MAY NOT be held, table Crst held
104 // Allowed SharedReJit states: Requested, GettingReJITParameters, Active
106 // 2) kJumpNone -> kJumpToPrestub
107 // Inside RequestRejit, DoJumpStampIfNecessary
108 // Global Crst MAY/MAY NOT be held, table Crst held
109 // Allowed SharedReJit states: Requested, GettingReJITParameters, Active
111 // 3) kJumpToPreStub -> kJumpToRejittedCode
112 // Inside DoReJitIfNecessary
113 // Global Crst NOT held, table Crst held
114 // Allowed SharedReJit states: Active
117 // Inside RequestRevert, RequestRejit
118 // Global Crst held, table crst held
119 // Allowed SharedReJit states: Reverted
122 // #Beware Invariant misconceptions - don't make bad assumptions!
123 // Even if a SharedReJitInfo is in the Reverted state:
124 // a) RejitInfos may still be in the kJumpToPreStub or kJumpToRejittedCode state
125 // Reverted really just means the runtime has started reverting, but it may not
126 // be complete yet on the thread executing Revert or RequestRejit.
127 // b) The code for this version of the method may be executing on any number of
128 // threads. Even after transitioning all rejit infos to kJumpNone state we
129 // have no power to abort or hijack threads already running the rejitted code.
131 // Even if a SharedReJitInfo is in the Active state:
132 // a) The corresponding ReJitInfos may not be jump-stamped yet.
133 // Some thread is still in the progress of getting this thread jump-stamped
134 // OR it is a place-holder ReJitInfo.
135 // b) An older ReJitInfo linked to a reverted SharedReJitInfo could still be
136 // in kJumpToPreStub or kJumpToReJittedCode state. RequestRejit is still in
137 // progress on some thread.
140 // #Known issues with REJIT at this time:
141 // NGEN inlined methods will not be properly rejitted
142 // Exception callstacks through rejitted code do not produce correct StackTraces
143 // Live debugging is not supported when rejit is enabled
144 // Rejit leaks rejitted methods, RejitInfos, and SharedRejitInfos until AppDomain unload
145 // Dump debugging doesn't correctly locate RejitInfos that are keyed by MethodDesc
146 // Metadata update creates large memory increase switching to RW (not specifically a rejit issue)
148 // ======================================================================================
152 #include "method.hpp"
153 #include "eeconfig.h"
154 #include "methoditer.h"
155 #include "dbginterface.h"
156 #include "threadsuspend.h"
159 #ifdef FEATURE_CODE_VERSIONING
161 #include "../debug/ee/debugger.h"
162 #include "../debug/ee/walker.h"
163 #include "../debug/ee/controller.h"
164 #include "codeversion.h"
166 // This HRESULT is only used as a private implementation detail. Corerror.xml has a comment in it
167 // reserving this value for our use but it doesn't appear in the public headers.
168 #define CORPROF_E_RUNTIME_SUSPEND_REQUIRED 0x80131381
170 // This is just used as a unique id. Overflow is OK. If we happen to have more than 4+Billion rejits
171 // and somehow manage to not run out of memory, we'll just have to redefine ReJITID as size_t.
173 static ReJITID s_GlobalReJitId = 1;
176 CrstStatic ReJitManager::s_csGlobalRequest;
179 //---------------------------------------------------------------------------------------
183 CORJIT_FLAGS ReJitManager::JitFlagsFromProfCodegenFlags(DWORD dwCodegenFlags)
185 LIMITED_METHOD_DAC_CONTRACT;
187 CORJIT_FLAGS jitFlags;
188 if ((dwCodegenFlags & COR_PRF_CODEGEN_DISABLE_ALL_OPTIMIZATIONS) != 0)
190 jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE);
192 if ((dwCodegenFlags & COR_PRF_CODEGEN_DISABLE_INLINING) != 0)
194 jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_NO_INLINING);
197 // In the future more flags may be added that need to be converted here (e.g.,
198 // COR_PRF_CODEGEN_ENTERLEAVE / CORJIT_FLAG_PROF_ENTERLEAVE)
203 //---------------------------------------------------------------------------------------
204 // ProfilerFunctionControl implementation
206 ProfilerFunctionControl::ProfilerFunctionControl(LoaderHeap * pHeap) :
212 m_cInstrumentedMapEntries(0),
213 m_rgInstrumentedMapEntries(NULL)
215 LIMITED_METHOD_CONTRACT;
218 ProfilerFunctionControl::~ProfilerFunctionControl()
220 LIMITED_METHOD_CONTRACT;
222 // Intentionally not deleting m_pbIL or m_rgInstrumentedMapEntries, as its ownership gets transferred to the
223 // SharedReJitInfo that manages that rejit request.
227 HRESULT ProfilerFunctionControl::QueryInterface(REFIID id, void** pInterface)
229 LIMITED_METHOD_CONTRACT;
231 if ((id != IID_IUnknown) &&
232 (id != IID_ICorProfilerFunctionControl))
235 return E_NOINTERFACE;
243 ULONG ProfilerFunctionControl::AddRef()
245 LIMITED_METHOD_CONTRACT;
247 return InterlockedIncrement(&m_refCount);
250 ULONG ProfilerFunctionControl::Release()
252 LIMITED_METHOD_CONTRACT;
254 ULONG refCount = InterlockedDecrement(&m_refCount);
264 //---------------------------------------------------------------------------------------
266 // Profiler calls this to specify a set of flags from COR_PRF_CODEGEN_FLAGS
267 // to control rejitting a particular methodDef.
270 // * flags - set of flags from COR_PRF_CODEGEN_FLAGS
276 HRESULT ProfilerFunctionControl::SetCodegenFlags(DWORD flags)
278 LIMITED_METHOD_CONTRACT;
280 m_dwCodegenFlags = flags;
284 //---------------------------------------------------------------------------------------
286 // Profiler calls this to specify the IL to use when rejitting a particular methodDef.
289 // * cbNewILMethodHeader - Size in bytes of pbNewILMethodHeader
290 // * pbNewILMethodHeader - Pointer to beginning of IL header + IL bytes.
293 // HRESULT indicating success or failure.
296 // Caller owns allocating and freeing pbNewILMethodHeader as expected.
297 // SetILFunctionBody copies pbNewILMethodHeader into a separate buffer.
300 HRESULT ProfilerFunctionControl::SetILFunctionBody(ULONG cbNewILMethodHeader, LPCBYTE pbNewILMethodHeader)
310 if (cbNewILMethodHeader == 0)
315 if (pbNewILMethodHeader == NULL)
320 _ASSERTE(m_cbIL == 0);
321 _ASSERTE(m_pbIL == NULL);
323 #ifdef DACCESS_COMPILE
324 m_pbIL = new (nothrow) BYTE[cbNewILMethodHeader];
326 // IL is stored on the appropriate loader heap, and its memory will be owned by the
327 // SharedReJitInfo we copy the pointer to.
328 m_pbIL = (LPBYTE) (void *) m_pHeap->AllocMem_NoThrow(S_SIZE_T(cbNewILMethodHeader));
332 return E_OUTOFMEMORY;
335 m_cbIL = cbNewILMethodHeader;
336 memcpy(m_pbIL, pbNewILMethodHeader, cbNewILMethodHeader);
341 HRESULT ProfilerFunctionControl::SetILInstrumentedCodeMap(ULONG cILMapEntries, COR_IL_MAP * rgILMapEntries)
343 #ifdef DACCESS_COMPILE
344 // I'm not sure why any of these methods would need to be compiled in DAC? Could we remove the
345 // entire class from the DAC'ized code build?
346 _ASSERTE(!"This shouldn't be called in DAC");
358 if (cILMapEntries >= (MAXULONG / sizeof(COR_IL_MAP)))
360 // Too big! The allocation below would overflow when calculating the size.
364 if (g_pDebugInterface == NULL)
366 return CORPROF_E_DEBUGGING_DISABLED;
370 // copy the il map and il map entries into the corresponding fields.
371 m_cInstrumentedMapEntries = cILMapEntries;
373 // IL is stored on the appropriate loader heap, and its memory will be owned by the
374 // SharedReJitInfo we copy the pointer to.
375 m_rgInstrumentedMapEntries = (COR_IL_MAP*) (void *) m_pHeap->AllocMem_NoThrow(S_SIZE_T(cILMapEntries * sizeof(COR_IL_MAP)));
377 if (m_rgInstrumentedMapEntries == NULL)
378 return E_OUTOFMEMORY;
381 memcpy_s(m_rgInstrumentedMapEntries, sizeof(COR_IL_MAP) * cILMapEntries, rgILMapEntries, sizeof(COR_IL_MAP) * cILMapEntries);
384 #endif // DACCESS_COMPILE
387 //---------------------------------------------------------------------------------------
389 // ReJitManager may use this to access the codegen flags the profiler had set on this
390 // ICorProfilerFunctionControl.
393 // * codegen flags previously set via SetCodegenFlags; 0 if none were set.
395 DWORD ProfilerFunctionControl::GetCodegenFlags()
397 return m_dwCodegenFlags;
400 //---------------------------------------------------------------------------------------
402 // ReJitManager may use this to access the IL header + instructions the
403 // profiler had set on this ICorProfilerFunctionControl via SetIL
406 // * Pointer to ProfilerFunctionControl-allocated buffer containing the
407 // IL header and instructions the profiler had provided.
409 LPBYTE ProfilerFunctionControl::GetIL()
414 //---------------------------------------------------------------------------------------
416 // ReJitManager may use this to access the count of instrumented map entry flags the
417 // profiler had set on this ICorProfilerFunctionControl.
420 // * size of the instrumented map entry array
422 ULONG ProfilerFunctionControl::GetInstrumentedMapEntryCount()
424 return m_cInstrumentedMapEntries;
427 //---------------------------------------------------------------------------------------
429 // ReJitManager may use this to access the instrumented map entries the
430 // profiler had set on this ICorProfilerFunctionControl.
433 // * the array of instrumented map entries
435 COR_IL_MAP* ProfilerFunctionControl::GetInstrumentedMapEntries()
437 return m_rgInstrumentedMapEntries;
440 //---------------------------------------------------------------------------------------
441 // ReJitManager implementation
443 // All the state-changey stuff is kept up here in the !DACCESS_COMPILE block.
444 // The more read-only inspection-y stuff follows the block.
446 #ifndef DACCESS_COMPILE
448 //---------------------------------------------------------------------------------------
450 // ICorProfilerInfo4::RequestReJIT calls into this guy to do most of the
451 // work. Takes care of finding the appropriate ReJitManager instances to
452 // record the rejit requests and perform jmp-stamping.
455 // * cFunctions - Element count of rgModuleIDs & rgMethodDefs
456 // * rgModuleIDs - Parallel array of ModuleIDs to rejit
457 // * rgMethodDefs - Parallel array of methodDefs to rejit
460 // HRESULT indicating success or failure of the overall operation. Each
461 // individual methodDef (or MethodDesc associated with the methodDef)
462 // may encounter its own failure, which is reported by the ReJITError()
463 // callback, which is called into the profiler directly.
467 HRESULT ReJitManager::RequestReJIT(
469 ModuleID rgModuleIDs[],
470 mdMethodDef rgMethodDefs[])
472 return ReJitManager::UpdateActiveILVersions(cFunctions, rgModuleIDs, rgMethodDefs, NULL, FALSE);
477 HRESULT ReJitManager::UpdateActiveILVersions(
479 ModuleID rgModuleIDs[],
480 mdMethodDef rgMethodDefs[],
481 HRESULT rgHrStatuses[],
493 // Serialize all RequestReJIT() and Revert() calls against each other (even across AppDomains)
494 CrstHolder ch(&(s_csGlobalRequest));
498 // Request at least 1 method to reJIT!
499 _ASSERTE ((cFunctions != 0) && (rgModuleIDs != NULL) && (rgMethodDefs != NULL));
501 // Temporary storage to batch up all the ReJitInfos that will get jump stamped
502 // later when the runtime is suspended.
504 //DESKTOP WARNING: On CoreCLR we are safe but if this code ever gets ported back
505 //there aren't any protections against domain unload. Any of these moduleIDs
506 //code version managers, or code versions would become invalid if the domain which
507 //contains them was unloaded.
508 SHash<CodeActivationBatchTraits> mgrToCodeActivationBatch;
509 CDynArray<CodeVersionManager::CodePublishError> errorRecords;
510 for (ULONG i = 0; i < cFunctions; i++)
512 Module * pModule = reinterpret_cast< Module * >(rgModuleIDs[i]);
513 if (pModule == NULL || TypeFromToken(rgMethodDefs[i]) != mdtMethodDef)
515 ReportReJITError(pModule, rgMethodDefs[i], NULL, E_INVALIDARG);
519 if (pModule->IsBeingUnloaded())
521 ReportReJITError(pModule, rgMethodDefs[i], NULL, CORPROF_E_DATAINCOMPLETE);
525 if (pModule->IsReflection())
527 ReportReJITError(pModule, rgMethodDefs[i], NULL, CORPROF_E_MODULE_IS_DYNAMIC);
531 if (!pModule->GetMDImport()->IsValidToken(rgMethodDefs[i]))
533 ReportReJITError(pModule, rgMethodDefs[i], NULL, E_INVALIDARG);
537 MethodDesc * pMD = pModule->LookupMethodDef(rgMethodDefs[i]);
541 _ASSERTE(!pMD->IsNoMetadata());
543 // Weird, non-user functions can't be rejitted
546 // Intentionally not reporting an error in this case, to be consistent
547 // with the pre-rejit case, as we have no opportunity to report an error
548 // in a pre-rejit request for a non-IL method, since the rejit manager
549 // never gets a call from the prestub worker for non-IL methods. Thus,
550 // since pre-rejit requests silently ignore rejit requests for non-IL
551 // methods, regular rejit requests will also silently ignore rejit requests for
552 // non-IL methods to be consistent.
557 CodeVersionManager * pCodeVersionManager = pModule->GetCodeVersionManager();
558 _ASSERTE(pCodeVersionManager != NULL);
559 CodeActivationBatch * pCodeActivationBatch = mgrToCodeActivationBatch.Lookup(pCodeVersionManager);
560 if (pCodeActivationBatch == NULL)
562 pCodeActivationBatch = new (nothrow)CodeActivationBatch(pCodeVersionManager);
563 if (pCodeActivationBatch == NULL)
565 return E_OUTOFMEMORY;
571 // This guy throws when out of memory, but remains internally
572 // consistent (without adding the new element)
573 mgrToCodeActivationBatch.Add(pCodeActivationBatch);
575 EX_CATCH_HRESULT(hr);
577 _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY);
585 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
587 // Bind the il code version
588 ILCodeVersion* pILCodeVersion = pCodeActivationBatch->m_methodsToActivate.Append();
589 if (pILCodeVersion == NULL)
591 return E_OUTOFMEMORY;
595 // activate the original version
596 *pILCodeVersion = ILCodeVersion(pModule, rgMethodDefs[i]);
600 // activate an unused or new IL version
601 hr = ReJitManager::BindILVersion(pCodeVersionManager, pModule, rgMethodDefs[i], pILCodeVersion);
604 _ASSERTE(hr == E_OUTOFMEMORY);
609 } // for (ULONG i = 0; i < cFunctions; i++)
611 // For each code versioning mgr, if there's work to do, suspend EE if needed,
612 // enter the code versioning mgr's crst, and do the batched work.
613 BOOL fEESuspended = FALSE;
614 SHash<CodeActivationBatchTraits>::Iterator beginIter = mgrToCodeActivationBatch.Begin();
615 SHash<CodeActivationBatchTraits>::Iterator endIter = mgrToCodeActivationBatch.End();
616 for (SHash<CodeActivationBatchTraits>::Iterator iter = beginIter; iter != endIter; iter++)
618 CodeActivationBatch * pCodeActivationBatch = *iter;
619 CodeVersionManager * pCodeVersionManager = pCodeActivationBatch->m_pCodeVersionManager;
621 int cMethodsToActivate = pCodeActivationBatch->m_methodsToActivate.Count();
622 if (cMethodsToActivate == 0)
628 // As a potential future optimization we could speculatively try to update the jump stamps without
629 // suspending the runtime. That needs to be plumbed through BatchUpdateJumpStamps though.
630 ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT);
634 _ASSERTE(ThreadStore::HoldingThreadStore());
635 hr = pCodeVersionManager->SetActiveILCodeVersions(pCodeActivationBatch->m_methodsToActivate.Ptr(), pCodeActivationBatch->m_methodsToActivate.Count(), fEESuspended, &errorRecords);
641 ThreadSuspend::RestartEE(FALSE, TRUE);
646 _ASSERTE(hr == E_OUTOFMEMORY);
650 // Report any errors that were batched up
651 for (int i = 0; i < errorRecords.Count(); i++)
653 if (rgHrStatuses != NULL)
655 for (DWORD j = 0; j < cFunctions; j++)
657 if (rgMethodDefs[j] == errorRecords[i].methodDef &&
658 reinterpret_cast<Module*>(rgModuleIDs[j]) == errorRecords[i].pModule)
660 rgHrStatuses[j] = errorRecords[i].hrStatus;
666 ReportReJITError(&(errorRecords[i]));
671 // We got through processing everything, but profiler will need to see the individual ReJITError
672 // callbacks to know what, if anything, failed.
677 HRESULT ReJitManager::BindILVersion(
678 CodeVersionManager* pCodeVersionManager,
680 mdMethodDef methodDef,
681 ILCodeVersion *pILCodeVersion)
689 PRECONDITION(CheckPointer(pCodeVersionManager));
690 PRECONDITION(CheckPointer(pModule));
691 PRECONDITION(CheckPointer(pILCodeVersion));
695 _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread());
696 _ASSERTE((pModule != NULL) && (methodDef != mdTokenNil));
698 // Check if there was there a previous rejit request for this method that hasn't been exposed back
699 // to the profiler yet
700 ILCodeVersion ilCodeVersion = pCodeVersionManager->GetActiveILCodeVersion(pModule, methodDef);
702 if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateRequested)
704 // We can 'reuse' this instance because the profiler doesn't know about
705 // it yet. (This likely happened because a profiler called RequestReJIT
706 // twice in a row, without us having a chance to jmp-stamp the code yet OR
707 // while iterating through instantiations of a generic, the iterator found
708 // duplicate entries for the same instantiation.)
709 _ASSERTE(ilCodeVersion.GetILNoThrow() == NULL);
711 *pILCodeVersion = ilCodeVersion;
715 // Either there was no ILCodeVersion yet for this MethodDesc OR whatever we've found
716 // couldn't be reused (and needed to be reverted). Create a new ILCodeVersion to return
718 return pCodeVersionManager->AddILCodeVersion(pModule, methodDef, InterlockedIncrement(reinterpret_cast<LONG*>(&s_GlobalReJitId)), pILCodeVersion);
721 //---------------------------------------------------------------------------------------
723 // ICorProfilerInfo4::RequestRevert calls into this guy to do most of the
724 // work. Takes care of finding the appropriate ReJitManager instances to
725 // perform the revert
728 // * cFunctions - Element count of rgModuleIDs & rgMethodDefs
729 // * rgModuleIDs - Parallel array of ModuleIDs to revert
730 // * rgMethodDefs - Parallel array of methodDefs to revert
731 // * rgHrStatuses - [out] Parallel array of HRESULTs indicating success/failure
732 // of reverting each (ModuleID, methodDef).
735 // HRESULT indicating success or failure of the overall operation. Each
736 // individual methodDef (or MethodDesc associated with the methodDef)
737 // may encounter its own failure, which is reported by the rgHrStatuses
742 HRESULT ReJitManager::RequestRevert(
744 ModuleID rgModuleIDs[],
745 mdMethodDef rgMethodDefs[],
746 HRESULT rgHrStatuses[])
757 return UpdateActiveILVersions(cFunctions, rgModuleIDs, rgMethodDefs, rgHrStatuses, TRUE);
761 HRESULT ReJitManager::ConfigureILCodeVersion(ILCodeVersion ilCodeVersion)
763 STANDARD_VM_CONTRACT;
765 CodeVersionManager* pCodeVersionManager = ilCodeVersion.GetModule()->GetCodeVersionManager();
766 _ASSERTE(!pCodeVersionManager->LockOwnedByCurrentThread());
770 Module* pModule = ilCodeVersion.GetModule();
771 mdMethodDef methodDef = ilCodeVersion.GetMethodDef();
772 BOOL fNeedsParameters = FALSE;
773 BOOL fWaitForParameters = FALSE;
776 // Serialize access to the rejit state
777 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
778 switch (ilCodeVersion.GetRejitState())
780 case ILCodeVersion::kStateRequested:
781 ilCodeVersion.SetRejitState(ILCodeVersion::kStateGettingReJITParameters);
782 fNeedsParameters = TRUE;
785 case ILCodeVersion::kStateGettingReJITParameters:
786 fWaitForParameters = TRUE;
794 if (fNeedsParameters)
796 // Here's where we give a chance for the rejit requestor to
797 // examine and modify the IL & codegen flags before it gets to
798 // the JIT. This allows one to add probe calls for things like
799 // code coverage, performance, or whatever. These will be
800 // stored in pShared.
801 _ASSERTE(pModule != NULL);
802 _ASSERTE(methodDef != mdTokenNil);
803 ReleaseHolder<ProfilerFunctionControl> pFuncControl =
804 new (nothrow)ProfilerFunctionControl(pModule->GetLoaderAllocator()->GetLowFrequencyHeap());
806 if (pFuncControl == NULL)
812 BEGIN_PIN_PROFILER(CORProfilerPresent());
813 hr = g_profControlBlock.pProfInterface->GetReJITParameters(
823 // Historically on failure we would revert to the kRequested state and fall-back
824 // to the initial code gen. The next time the method ran it would try again.
826 // Preserving that behavior is possible, but a bit awkward now that we have
827 // Precode swapping as well. Instead of doing that I am acting as if GetReJITParameters
828 // had succeeded, using the original IL, no jit flags, and no modified IL mapping.
829 // This is similar to a fallback except the profiler won't get any further attempts
830 // to provide the parameters correctly. If the profiler wants another attempt it would
831 // need to call RequestRejit again.
832 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
833 if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateGettingReJITParameters)
835 ilCodeVersion.SetRejitState(ILCodeVersion::kStateActive);
836 ilCodeVersion.SetIL(ILCodeVersion(pModule, methodDef).GetIL());
839 ReportReJITError(pModule, methodDef, pModule->LookupMethodDef(methodDef), hr);
844 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
845 if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateGettingReJITParameters)
847 // Inside the above call to ICorProfilerCallback4::GetReJITParameters, the profiler
848 // will have used the specified pFuncControl to provide its IL and codegen flags.
849 // So now we transfer it out to the SharedReJitInfo.
850 ilCodeVersion.SetJitFlags(pFuncControl->GetCodegenFlags());
851 ilCodeVersion.SetIL((COR_ILMETHOD*)pFuncControl->GetIL());
852 // ilCodeVersion is now the owner of the memory for the IL buffer
853 ilCodeVersion.SetInstrumentedILMap(pFuncControl->GetInstrumentedMapEntryCount(),
854 pFuncControl->GetInstrumentedMapEntries());
855 ilCodeVersion.SetRejitState(ILCodeVersion::kStateActive);
859 else if (fWaitForParameters)
861 // This feels lame, but it doesn't appear like we have the good threading primitves
862 // for this. What I would like is an AutoResetEvent that atomically exits the table
863 // Crst when I wait on it. From what I can tell our AutoResetEvent doesn't have
864 // that atomic transition which means this ordering could occur:
865 // [Thread 1] detect kStateGettingParameters and exit table lock
866 // [Thread 2] enter table lock, transition kStateGettingParameters -> kStateActive
867 // [Thread 2] signal AutoResetEvent
868 // [Thread 2] exit table lock
869 // [Thread 1] wait on AutoResetEvent (which may never be signaled again)
871 // Another option would be ManualResetEvents, one for each SharedReJitInfo, but
872 // that feels like a lot of memory overhead to handle a case which occurs rarely.
873 // A third option would be dynamically creating ManualResetEvents in a side
874 // dictionary on demand, but that feels like a lot of complexity for an event
875 // that occurs rarely.
877 // I just ended up with this simple polling loop. Assuming profiler
878 // writers implement GetReJITParameters performantly we will only iterate
879 // this loop once, and even then only in the rare case of threads racing
880 // to JIT the same IL. If this really winds up causing performance issues
881 // We can build something more sophisticated.
885 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
886 if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateActive)
888 break; // the other thread got the parameters succesfully, go race to rejit
891 ClrSleepEx(1, FALSE);
898 #endif // DACCESS_COMPILE
899 // The rest of the ReJitManager methods are safe to compile for DAC
901 //---------------------------------------------------------------------------------------
903 // Used by profiler to get the ReJITID corrseponding to a (MethodDesc *, PCODE) pair.
904 // Can also be used to determine whether (MethodDesc *, PCODE) corresponds to a rejit
905 // (vs. a regular JIT) for the purposes of deciding whether to notify the debugger about
906 // the rejit (and building the debugger JIT info structure).
909 // * pMD - MethodDesc * of interestg
910 // * pCodeStart - PCODE of the particular interesting JITting of that MethodDesc *
913 // 0 if no such ReJITID found (e.g., PCODE is from a JIT and not a rejit), else the
914 // ReJITID requested.
917 ReJITID ReJitManager::GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart)
924 PRECONDITION(CheckPointer(pMD));
925 PRECONDITION(pCodeStart != NULL);
929 // Fast-path: If the rejit map is empty, no need to look up anything. Do this outside
930 // of a lock to impact our caller (the prestub worker) as little as possible. If the
931 // map is nonempty, we'll acquire the lock at that point and do the lookup for real.
932 CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager();
933 if (pCodeVersionManager->GetNonDefaultILVersionCount() == 0)
938 CodeVersionManager::TableLockHolder ch(pCodeVersionManager);
939 return ReJitManager::GetReJitIdNoLock(pMD, pCodeStart);
942 //---------------------------------------------------------------------------------------
944 // See comment above code:ReJitManager::GetReJitId for main details of what this does.
946 // This function is basically the same as GetReJitId, except caller is expected to take
947 // the ReJitManager lock directly (via ReJitManager::TableLockHolder). This exists so
948 // that ETW can explicitly take the triggering ReJitManager lock up front, and in the
949 // proper order, to avoid lock leveling issues, and triggering issues with other locks it
950 // takes that are CRST_UNSAFE_ANYMODE
953 ReJITID ReJitManager::GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart)
960 PRECONDITION(CheckPointer(pMD));
961 PRECONDITION(pCodeStart != NULL);
965 // Caller must ensure this lock is taken!
966 CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager();
967 _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread());
969 NativeCodeVersion nativeCodeVersion = pCodeVersionManager->GetNativeCodeVersion(pMD, pCodeStart);
970 if (nativeCodeVersion.IsNull())
974 return nativeCodeVersion.GetILCodeVersion().GetVersionId();
977 //---------------------------------------------------------------------------------------
979 // Called by profiler to retrieve an array of ReJITIDs corresponding to a MethodDesc *
982 // * pMD - MethodDesc * to look up
983 // * cReJitIds - Element count capacity of reJitIds
984 // * pcReJitIds - [out] Place total count of ReJITIDs found here; may be more than
985 // cReJitIds if profiler passed an array that's too small to hold them all
986 // * reJitIds - [out] Place ReJITIDs found here. Count of ReJITIDs returned here is
987 // min(cReJitIds, *pcReJitIds)
990 // * S_OK: ReJITIDs successfully returned, array is big enough
991 // * S_FALSE: ReJITIDs successfully found, but array was not big enough. Only
992 // cReJitIds were returned and cReJitIds < *pcReJitId (latter being the total
993 // number of ReJITIDs available).
996 HRESULT ReJitManager::GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[])
1003 PRECONDITION(CheckPointer(pMD));
1004 PRECONDITION(pcReJitIds != NULL);
1005 PRECONDITION(reJitIds != NULL);
1009 CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager();
1010 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
1014 ILCodeVersionCollection ilCodeVersions = pCodeVersionManager->GetILCodeVersions(pMD);
1015 for (ILCodeVersionIterator iter = ilCodeVersions.Begin(), end = ilCodeVersions.End();
1019 ILCodeVersion curILVersion = *iter;
1021 if (curILVersion.GetRejitState() == ILCodeVersion::kStateActive)
1023 if (cnt < cReJitIds)
1025 reJitIds[cnt] = curILVersion.GetVersionId();
1035 return (cnt > cReJitIds) ? S_FALSE : S_OK;
1038 #endif // FEATURE_CODE_VERSIONING
1039 #else // FEATURE_REJIT
1041 // On architectures that don't support rejit, just keep around some do-nothing
1042 // stubs so the rest of the VM doesn't have to be littered with #ifdef FEATURE_REJIT
1045 HRESULT ReJitManager::RequestReJIT(
1047 ModuleID rgModuleIDs[],
1048 mdMethodDef rgMethodDefs[])
1054 HRESULT ReJitManager::RequestRevert(
1056 ModuleID rgModuleIDs[],
1057 mdMethodDef rgMethodDefs[],
1058 HRESULT rgHrStatuses[])
1063 ReJITID ReJitManager::GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart)
1068 ReJITID ReJitManager::GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart)
1073 HRESULT ReJitManager::GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[])
1078 #endif // FEATURE_REJIT