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// ReJit.cpp
6//
7
8//
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.
16//
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.
25//
26// Top-level functions in this file of most interest are:
27//
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)
33//
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.
39//
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.
50//
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.
54//
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.)
59//
60//
61// #Invariants:
62//
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.
68//
69// For a given MethodDesc there is at most 1 ReJitInfo in the kJumpToPrestub or kJumpToRejittedCode
70// state.
71//
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).
75//
76// The ReJitManager::m_crstTable lock is never held during callbacks to the profiler
77// such as GetReJITParameters, ReJITStarted, JITComplete, ReportReJITError
78//
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
81//
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
86//
87// 2) Requested -> GettingReJITParameters
88// Inside DoRejitIfNecessary
89// Global Crst NOT held, table Crst held
90//
91// 3) GettingReJITParameters -> Active
92// Inside DoRejitIfNecessary
93// Global Crst NOT held, table Crst held
94//
95// 4) * -> Reverted
96// Inside RequestRejit or RequestRevert
97// Global Crst held, table Crst held
98//
99//
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
105//
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
110//
111// 3) kJumpToPreStub -> kJumpToRejittedCode
112// Inside DoReJitIfNecessary
113// Global Crst NOT held, table Crst held
114// Allowed SharedReJit states: Active
115//
116// 4) * -> kJumpNone
117// Inside RequestRevert, RequestRejit
118// Global Crst held, table crst held
119// Allowed SharedReJit states: Reverted
120//
121//
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.
130//
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.
138//
139//
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)
147//
148// ======================================================================================
149
150#include "common.h"
151#include "rejit.h"
152#include "method.hpp"
153#include "eeconfig.h"
154#include "methoditer.h"
155#include "dbginterface.h"
156#include "threadsuspend.h"
157
158#ifdef FEATURE_REJIT
159#ifdef FEATURE_CODE_VERSIONING
160
161#include "../debug/ee/debugger.h"
162#include "../debug/ee/walker.h"
163#include "../debug/ee/controller.h"
164#include "codeversion.h"
165
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
169
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.
172/* static */
173static ReJITID s_GlobalReJitId = 1;
174
175/* static */
176CrstStatic ReJitManager::s_csGlobalRequest;
177
178
179//---------------------------------------------------------------------------------------
180// Helpers
181
182//static
183CORJIT_FLAGS ReJitManager::JitFlagsFromProfCodegenFlags(DWORD dwCodegenFlags)
184{
185 LIMITED_METHOD_DAC_CONTRACT;
186
187 CORJIT_FLAGS jitFlags;
188 if ((dwCodegenFlags & COR_PRF_CODEGEN_DISABLE_ALL_OPTIMIZATIONS) != 0)
189 {
190 jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE);
191 }
192 if ((dwCodegenFlags & COR_PRF_CODEGEN_DISABLE_INLINING) != 0)
193 {
194 jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_NO_INLINING);
195 }
196
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)
199
200 return jitFlags;
201}
202
203//---------------------------------------------------------------------------------------
204// ProfilerFunctionControl implementation
205
206ProfilerFunctionControl::ProfilerFunctionControl(LoaderHeap * pHeap) :
207 m_refCount(1),
208 m_pHeap(pHeap),
209 m_dwCodegenFlags(0),
210 m_cbIL(0),
211 m_pbIL(NULL),
212 m_cInstrumentedMapEntries(0),
213 m_rgInstrumentedMapEntries(NULL)
214{
215 LIMITED_METHOD_CONTRACT;
216}
217
218ProfilerFunctionControl::~ProfilerFunctionControl()
219{
220 LIMITED_METHOD_CONTRACT;
221
222 // Intentionally not deleting m_pbIL or m_rgInstrumentedMapEntries, as its ownership gets transferred to the
223 // SharedReJitInfo that manages that rejit request.
224}
225
226
227HRESULT ProfilerFunctionControl::QueryInterface(REFIID id, void** pInterface)
228{
229 LIMITED_METHOD_CONTRACT;
230
231 if ((id != IID_IUnknown) &&
232 (id != IID_ICorProfilerFunctionControl))
233 {
234 *pInterface = NULL;
235 return E_NOINTERFACE;
236 }
237
238 *pInterface = this;
239 this->AddRef();
240 return S_OK;
241}
242
243ULONG ProfilerFunctionControl::AddRef()
244{
245 LIMITED_METHOD_CONTRACT;
246
247 return InterlockedIncrement(&m_refCount);
248}
249
250ULONG ProfilerFunctionControl::Release()
251{
252 LIMITED_METHOD_CONTRACT;
253
254 ULONG refCount = InterlockedDecrement(&m_refCount);
255
256 if (0 == refCount)
257 {
258 delete this;
259 }
260
261 return refCount;
262}
263
264//---------------------------------------------------------------------------------------
265//
266// Profiler calls this to specify a set of flags from COR_PRF_CODEGEN_FLAGS
267// to control rejitting a particular methodDef.
268//
269// Arguments:
270// * flags - set of flags from COR_PRF_CODEGEN_FLAGS
271//
272// Return Value:
273// Always S_OK;
274//
275
276HRESULT ProfilerFunctionControl::SetCodegenFlags(DWORD flags)
277{
278 LIMITED_METHOD_CONTRACT;
279
280 m_dwCodegenFlags = flags;
281 return S_OK;
282}
283
284//---------------------------------------------------------------------------------------
285//
286// Profiler calls this to specify the IL to use when rejitting a particular methodDef.
287//
288// Arguments:
289// * cbNewILMethodHeader - Size in bytes of pbNewILMethodHeader
290// * pbNewILMethodHeader - Pointer to beginning of IL header + IL bytes.
291//
292// Return Value:
293// HRESULT indicating success or failure.
294//
295// Notes:
296// Caller owns allocating and freeing pbNewILMethodHeader as expected.
297// SetILFunctionBody copies pbNewILMethodHeader into a separate buffer.
298//
299
300HRESULT ProfilerFunctionControl::SetILFunctionBody(ULONG cbNewILMethodHeader, LPCBYTE pbNewILMethodHeader)
301{
302 CONTRACTL
303 {
304 NOTHROW;
305 GC_NOTRIGGER;
306 MODE_ANY;
307 }
308 CONTRACTL_END;
309
310 if (cbNewILMethodHeader == 0)
311 {
312 return E_INVALIDARG;
313 }
314
315 if (pbNewILMethodHeader == NULL)
316 {
317 return E_INVALIDARG;
318 }
319
320 _ASSERTE(m_cbIL == 0);
321 _ASSERTE(m_pbIL == NULL);
322
323#ifdef DACCESS_COMPILE
324 m_pbIL = new (nothrow) BYTE[cbNewILMethodHeader];
325#else
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));
329#endif
330 if (m_pbIL == NULL)
331 {
332 return E_OUTOFMEMORY;
333 }
334
335 m_cbIL = cbNewILMethodHeader;
336 memcpy(m_pbIL, pbNewILMethodHeader, cbNewILMethodHeader);
337
338 return S_OK;
339}
340
341HRESULT ProfilerFunctionControl::SetILInstrumentedCodeMap(ULONG cILMapEntries, COR_IL_MAP * rgILMapEntries)
342{
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");
347 return E_NOTIMPL;
348#else
349
350 CONTRACTL
351 {
352 NOTHROW;
353 GC_NOTRIGGER;
354 MODE_ANY;
355 }
356 CONTRACTL_END;
357
358 if (cILMapEntries >= (MAXULONG / sizeof(COR_IL_MAP)))
359 {
360 // Too big! The allocation below would overflow when calculating the size.
361 return E_INVALIDARG;
362 }
363
364 if (g_pDebugInterface == NULL)
365 {
366 return CORPROF_E_DEBUGGING_DISABLED;
367 }
368
369
370 // copy the il map and il map entries into the corresponding fields.
371 m_cInstrumentedMapEntries = cILMapEntries;
372
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)));
376
377 if (m_rgInstrumentedMapEntries == NULL)
378 return E_OUTOFMEMORY;
379
380
381 memcpy_s(m_rgInstrumentedMapEntries, sizeof(COR_IL_MAP) * cILMapEntries, rgILMapEntries, sizeof(COR_IL_MAP) * cILMapEntries);
382
383 return S_OK;
384#endif // DACCESS_COMPILE
385}
386
387//---------------------------------------------------------------------------------------
388//
389// ReJitManager may use this to access the codegen flags the profiler had set on this
390// ICorProfilerFunctionControl.
391//
392// Return Value:
393// * codegen flags previously set via SetCodegenFlags; 0 if none were set.
394//
395DWORD ProfilerFunctionControl::GetCodegenFlags()
396{
397 return m_dwCodegenFlags;
398}
399
400//---------------------------------------------------------------------------------------
401//
402// ReJitManager may use this to access the IL header + instructions the
403// profiler had set on this ICorProfilerFunctionControl via SetIL
404//
405// Return Value:
406// * Pointer to ProfilerFunctionControl-allocated buffer containing the
407// IL header and instructions the profiler had provided.
408//
409LPBYTE ProfilerFunctionControl::GetIL()
410{
411 return m_pbIL;
412}
413
414//---------------------------------------------------------------------------------------
415//
416// ReJitManager may use this to access the count of instrumented map entry flags the
417// profiler had set on this ICorProfilerFunctionControl.
418//
419// Return Value:
420// * size of the instrumented map entry array
421//
422ULONG ProfilerFunctionControl::GetInstrumentedMapEntryCount()
423{
424 return m_cInstrumentedMapEntries;
425}
426
427//---------------------------------------------------------------------------------------
428//
429// ReJitManager may use this to access the instrumented map entries the
430// profiler had set on this ICorProfilerFunctionControl.
431//
432// Return Value:
433// * the array of instrumented map entries
434//
435COR_IL_MAP* ProfilerFunctionControl::GetInstrumentedMapEntries()
436{
437 return m_rgInstrumentedMapEntries;
438}
439
440//---------------------------------------------------------------------------------------
441// ReJitManager implementation
442
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.
445
446#ifndef DACCESS_COMPILE
447
448//---------------------------------------------------------------------------------------
449//
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.
453//
454// Arguments:
455// * cFunctions - Element count of rgModuleIDs & rgMethodDefs
456// * rgModuleIDs - Parallel array of ModuleIDs to rejit
457// * rgMethodDefs - Parallel array of methodDefs to rejit
458//
459// Return Value:
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.
464//
465
466// static
467HRESULT ReJitManager::RequestReJIT(
468 ULONG cFunctions,
469 ModuleID rgModuleIDs[],
470 mdMethodDef rgMethodDefs[])
471{
472 return ReJitManager::UpdateActiveILVersions(cFunctions, rgModuleIDs, rgMethodDefs, NULL, FALSE);
473}
474
475
476 // static
477HRESULT ReJitManager::UpdateActiveILVersions(
478 ULONG cFunctions,
479 ModuleID rgModuleIDs[],
480 mdMethodDef rgMethodDefs[],
481 HRESULT rgHrStatuses[],
482 BOOL fIsRevert)
483{
484 CONTRACTL
485 {
486 NOTHROW;
487 GC_TRIGGERS;
488 CAN_TAKE_LOCK;
489 MODE_PREEMPTIVE;
490 }
491 CONTRACTL_END;
492
493 // Serialize all RequestReJIT() and Revert() calls against each other (even across AppDomains)
494 CrstHolder ch(&(s_csGlobalRequest));
495
496 HRESULT hr = S_OK;
497
498 // Request at least 1 method to reJIT!
499 _ASSERTE ((cFunctions != 0) && (rgModuleIDs != NULL) && (rgMethodDefs != NULL));
500
501 // Temporary storage to batch up all the ReJitInfos that will get jump stamped
502 // later when the runtime is suspended.
503 //
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++)
511 {
512 Module * pModule = reinterpret_cast< Module * >(rgModuleIDs[i]);
513 if (pModule == NULL || TypeFromToken(rgMethodDefs[i]) != mdtMethodDef)
514 {
515 ReportReJITError(pModule, rgMethodDefs[i], NULL, E_INVALIDARG);
516 continue;
517 }
518
519 if (pModule->IsBeingUnloaded())
520 {
521 ReportReJITError(pModule, rgMethodDefs[i], NULL, CORPROF_E_DATAINCOMPLETE);
522 continue;
523 }
524
525 if (pModule->IsReflection())
526 {
527 ReportReJITError(pModule, rgMethodDefs[i], NULL, CORPROF_E_MODULE_IS_DYNAMIC);
528 continue;
529 }
530
531 if (!pModule->GetMDImport()->IsValidToken(rgMethodDefs[i]))
532 {
533 ReportReJITError(pModule, rgMethodDefs[i], NULL, E_INVALIDARG);
534 continue;
535 }
536
537 MethodDesc * pMD = pModule->LookupMethodDef(rgMethodDefs[i]);
538
539 if (pMD != NULL)
540 {
541 _ASSERTE(!pMD->IsNoMetadata());
542
543 // Weird, non-user functions can't be rejitted
544 if (!pMD->IsIL())
545 {
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.
553 continue;
554 }
555 }
556
557 CodeVersionManager * pCodeVersionManager = pModule->GetCodeVersionManager();
558 _ASSERTE(pCodeVersionManager != NULL);
559 CodeActivationBatch * pCodeActivationBatch = mgrToCodeActivationBatch.Lookup(pCodeVersionManager);
560 if (pCodeActivationBatch == NULL)
561 {
562 pCodeActivationBatch = new (nothrow)CodeActivationBatch(pCodeVersionManager);
563 if (pCodeActivationBatch == NULL)
564 {
565 return E_OUTOFMEMORY;
566 }
567
568 hr = S_OK;
569 EX_TRY
570 {
571 // This guy throws when out of memory, but remains internally
572 // consistent (without adding the new element)
573 mgrToCodeActivationBatch.Add(pCodeActivationBatch);
574 }
575 EX_CATCH_HRESULT(hr);
576
577 _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY);
578 if (FAILED(hr))
579 {
580 return hr;
581 }
582 }
583
584 {
585 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
586
587 // Bind the il code version
588 ILCodeVersion* pILCodeVersion = pCodeActivationBatch->m_methodsToActivate.Append();
589 if (pILCodeVersion == NULL)
590 {
591 return E_OUTOFMEMORY;
592 }
593 if (fIsRevert)
594 {
595 // activate the original version
596 *pILCodeVersion = ILCodeVersion(pModule, rgMethodDefs[i]);
597 }
598 else
599 {
600 // activate an unused or new IL version
601 hr = ReJitManager::BindILVersion(pCodeVersionManager, pModule, rgMethodDefs[i], pILCodeVersion);
602 if (FAILED(hr))
603 {
604 _ASSERTE(hr == E_OUTOFMEMORY);
605 return hr;
606 }
607 }
608 }
609 } // for (ULONG i = 0; i < cFunctions; i++)
610
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++)
617 {
618 CodeActivationBatch * pCodeActivationBatch = *iter;
619 CodeVersionManager * pCodeVersionManager = pCodeActivationBatch->m_pCodeVersionManager;
620
621 int cMethodsToActivate = pCodeActivationBatch->m_methodsToActivate.Count();
622 if (cMethodsToActivate == 0)
623 {
624 continue;
625 }
626
627 {
628 // SetActiveILCodeVersions takes the SystemDomain crst, which needs to be acquired before the
629 // ThreadStore crsts
630 SystemDomain::LockHolder lh;
631
632 if(!fEESuspended)
633 {
634 // As a potential future optimization we could speculatively try to update the jump stamps without
635 // suspending the runtime. That needs to be plumbed through BatchUpdateJumpStamps though.
636 ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT);
637 fEESuspended = TRUE;
638 }
639
640 _ASSERTE(ThreadStore::HoldingThreadStore());
641 hr = pCodeVersionManager->SetActiveILCodeVersions(pCodeActivationBatch->m_methodsToActivate.Ptr(), pCodeActivationBatch->m_methodsToActivate.Count(), fEESuspended, &errorRecords);
642 if (FAILED(hr))
643 break;
644 }
645 }
646 if (fEESuspended)
647 {
648 ThreadSuspend::RestartEE(FALSE, TRUE);
649 }
650
651 if (FAILED(hr))
652 {
653 _ASSERTE(hr == E_OUTOFMEMORY);
654 return hr;
655 }
656
657 // Report any errors that were batched up
658 for (int i = 0; i < errorRecords.Count(); i++)
659 {
660 if (rgHrStatuses != NULL)
661 {
662 for (DWORD j = 0; j < cFunctions; j++)
663 {
664 if (rgMethodDefs[j] == errorRecords[i].methodDef &&
665 reinterpret_cast<Module*>(rgModuleIDs[j]) == errorRecords[i].pModule)
666 {
667 rgHrStatuses[j] = errorRecords[i].hrStatus;
668 }
669 }
670 }
671 else
672 {
673 ReportReJITError(&(errorRecords[i]));
674 }
675
676 }
677
678 // We got through processing everything, but profiler will need to see the individual ReJITError
679 // callbacks to know what, if anything, failed.
680 return S_OK;
681}
682
683// static
684HRESULT ReJitManager::BindILVersion(
685 CodeVersionManager* pCodeVersionManager,
686 PTR_Module pModule,
687 mdMethodDef methodDef,
688 ILCodeVersion *pILCodeVersion)
689{
690 CONTRACTL
691 {
692 NOTHROW;
693 GC_NOTRIGGER;
694 MODE_PREEMPTIVE;
695 CAN_TAKE_LOCK;
696 PRECONDITION(CheckPointer(pCodeVersionManager));
697 PRECONDITION(CheckPointer(pModule));
698 PRECONDITION(CheckPointer(pILCodeVersion));
699 }
700 CONTRACTL_END;
701
702 _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread());
703 _ASSERTE((pModule != NULL) && (methodDef != mdTokenNil));
704
705 // Check if there was there a previous rejit request for this method that hasn't been exposed back
706 // to the profiler yet
707 ILCodeVersion ilCodeVersion = pCodeVersionManager->GetActiveILCodeVersion(pModule, methodDef);
708
709 if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateRequested)
710 {
711 // We can 'reuse' this instance because the profiler doesn't know about
712 // it yet. (This likely happened because a profiler called RequestReJIT
713 // twice in a row, without us having a chance to jmp-stamp the code yet OR
714 // while iterating through instantiations of a generic, the iterator found
715 // duplicate entries for the same instantiation.)
716 _ASSERTE(ilCodeVersion.HasDefaultIL());
717
718 *pILCodeVersion = ilCodeVersion;
719 return S_FALSE;
720 }
721
722 // Either there was no ILCodeVersion yet for this MethodDesc OR whatever we've found
723 // couldn't be reused (and needed to be reverted). Create a new ILCodeVersion to return
724 // to the caller.
725 return pCodeVersionManager->AddILCodeVersion(pModule, methodDef, InterlockedIncrement(reinterpret_cast<LONG*>(&s_GlobalReJitId)), pILCodeVersion);
726}
727
728//---------------------------------------------------------------------------------------
729//
730// ICorProfilerInfo4::RequestRevert calls into this guy to do most of the
731// work. Takes care of finding the appropriate ReJitManager instances to
732// perform the revert
733//
734// Arguments:
735// * cFunctions - Element count of rgModuleIDs & rgMethodDefs
736// * rgModuleIDs - Parallel array of ModuleIDs to revert
737// * rgMethodDefs - Parallel array of methodDefs to revert
738// * rgHrStatuses - [out] Parallel array of HRESULTs indicating success/failure
739// of reverting each (ModuleID, methodDef).
740//
741// Return Value:
742// HRESULT indicating success or failure of the overall operation. Each
743// individual methodDef (or MethodDesc associated with the methodDef)
744// may encounter its own failure, which is reported by the rgHrStatuses
745// [out] parameter.
746//
747
748// static
749HRESULT ReJitManager::RequestRevert(
750 ULONG cFunctions,
751 ModuleID rgModuleIDs[],
752 mdMethodDef rgMethodDefs[],
753 HRESULT rgHrStatuses[])
754{
755 CONTRACTL
756 {
757 NOTHROW;
758 GC_TRIGGERS;
759 CAN_TAKE_LOCK;
760 MODE_PREEMPTIVE;
761 }
762 CONTRACTL_END;
763
764 return UpdateActiveILVersions(cFunctions, rgModuleIDs, rgMethodDefs, rgHrStatuses, TRUE);
765}
766
767// static
768HRESULT ReJitManager::ConfigureILCodeVersion(ILCodeVersion ilCodeVersion)
769{
770 STANDARD_VM_CONTRACT;
771
772 CodeVersionManager* pCodeVersionManager = ilCodeVersion.GetModule()->GetCodeVersionManager();
773 _ASSERTE(!pCodeVersionManager->LockOwnedByCurrentThread());
774
775
776 HRESULT hr = S_OK;
777 Module* pModule = ilCodeVersion.GetModule();
778 mdMethodDef methodDef = ilCodeVersion.GetMethodDef();
779 BOOL fNeedsParameters = FALSE;
780 BOOL fWaitForParameters = FALSE;
781
782 {
783 // Serialize access to the rejit state
784 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
785 switch (ilCodeVersion.GetRejitState())
786 {
787 case ILCodeVersion::kStateRequested:
788 ilCodeVersion.SetRejitState(ILCodeVersion::kStateGettingReJITParameters);
789 fNeedsParameters = TRUE;
790 break;
791
792 case ILCodeVersion::kStateGettingReJITParameters:
793 fWaitForParameters = TRUE;
794 break;
795
796 default:
797 return S_OK;
798 }
799 }
800
801 if (fNeedsParameters)
802 {
803 // Here's where we give a chance for the rejit requestor to
804 // examine and modify the IL & codegen flags before it gets to
805 // the JIT. This allows one to add probe calls for things like
806 // code coverage, performance, or whatever. These will be
807 // stored in pShared.
808 _ASSERTE(pModule != NULL);
809 _ASSERTE(methodDef != mdTokenNil);
810 ReleaseHolder<ProfilerFunctionControl> pFuncControl =
811 new (nothrow)ProfilerFunctionControl(pModule->GetLoaderAllocator()->GetLowFrequencyHeap());
812 HRESULT hr = S_OK;
813 if (pFuncControl == NULL)
814 {
815 hr = E_OUTOFMEMORY;
816 }
817 else
818 {
819 BEGIN_PIN_PROFILER(CORProfilerPresent());
820 hr = g_profControlBlock.pProfInterface->GetReJITParameters(
821 (ModuleID)pModule,
822 methodDef,
823 pFuncControl);
824 END_PIN_PROFILER();
825 }
826
827 if (FAILED(hr))
828 {
829 {
830 // Historically on failure we would revert to the kRequested state and fall-back
831 // to the initial code gen. The next time the method ran it would try again.
832 //
833 // Preserving that behavior is possible, but a bit awkward now that we have
834 // Precode swapping as well. Instead of doing that I am acting as if GetReJITParameters
835 // had succeeded, using the original IL, no jit flags, and no modified IL mapping.
836 // This is similar to a fallback except the profiler won't get any further attempts
837 // to provide the parameters correctly. If the profiler wants another attempt it would
838 // need to call RequestRejit again.
839 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
840 if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateGettingReJITParameters)
841 {
842 ilCodeVersion.SetRejitState(ILCodeVersion::kStateActive);
843 ilCodeVersion.SetIL(ILCodeVersion(pModule, methodDef).GetIL());
844 }
845 }
846 ReportReJITError(pModule, methodDef, pModule->LookupMethodDef(methodDef), hr);
847 return S_OK;
848 }
849 else
850 {
851 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
852 if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateGettingReJITParameters)
853 {
854 // Inside the above call to ICorProfilerCallback4::GetReJITParameters, the profiler
855 // will have used the specified pFuncControl to provide its IL and codegen flags.
856 // So now we transfer it out to the SharedReJitInfo.
857 ilCodeVersion.SetJitFlags(pFuncControl->GetCodegenFlags());
858 ilCodeVersion.SetIL((COR_ILMETHOD*)pFuncControl->GetIL());
859 // ilCodeVersion is now the owner of the memory for the IL buffer
860 ilCodeVersion.SetInstrumentedILMap(pFuncControl->GetInstrumentedMapEntryCount(),
861 pFuncControl->GetInstrumentedMapEntries());
862 ilCodeVersion.SetRejitState(ILCodeVersion::kStateActive);
863 }
864 }
865 }
866 else if (fWaitForParameters)
867 {
868 // This feels lame, but it doesn't appear like we have the good threading primitves
869 // for this. What I would like is an AutoResetEvent that atomically exits the table
870 // Crst when I wait on it. From what I can tell our AutoResetEvent doesn't have
871 // that atomic transition which means this ordering could occur:
872 // [Thread 1] detect kStateGettingParameters and exit table lock
873 // [Thread 2] enter table lock, transition kStateGettingParameters -> kStateActive
874 // [Thread 2] signal AutoResetEvent
875 // [Thread 2] exit table lock
876 // [Thread 1] wait on AutoResetEvent (which may never be signaled again)
877 //
878 // Another option would be ManualResetEvents, one for each SharedReJitInfo, but
879 // that feels like a lot of memory overhead to handle a case which occurs rarely.
880 // A third option would be dynamically creating ManualResetEvents in a side
881 // dictionary on demand, but that feels like a lot of complexity for an event
882 // that occurs rarely.
883 //
884 // I just ended up with this simple polling loop. Assuming profiler
885 // writers implement GetReJITParameters performantly we will only iterate
886 // this loop once, and even then only in the rare case of threads racing
887 // to JIT the same IL. If this really winds up causing performance issues
888 // We can build something more sophisticated.
889 while (true)
890 {
891 {
892 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
893 if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateActive)
894 {
895 break; // the other thread got the parameters succesfully, go race to rejit
896 }
897 }
898 ClrSleepEx(1, FALSE);
899 }
900 }
901
902 return S_OK;
903}
904
905#endif // DACCESS_COMPILE
906// The rest of the ReJitManager methods are safe to compile for DAC
907
908//---------------------------------------------------------------------------------------
909//
910// Used by profiler to get the ReJITID corrseponding to a (MethodDesc *, PCODE) pair.
911// Can also be used to determine whether (MethodDesc *, PCODE) corresponds to a rejit
912// (vs. a regular JIT) for the purposes of deciding whether to notify the debugger about
913// the rejit (and building the debugger JIT info structure).
914//
915// Arguments:
916// * pMD - MethodDesc * of interestg
917// * pCodeStart - PCODE of the particular interesting JITting of that MethodDesc *
918//
919// Return Value:
920// 0 if no such ReJITID found (e.g., PCODE is from a JIT and not a rejit), else the
921// ReJITID requested.
922//
923// static
924ReJITID ReJitManager::GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart)
925{
926 CONTRACTL
927 {
928 NOTHROW;
929 CAN_TAKE_LOCK;
930 GC_TRIGGERS;
931 PRECONDITION(CheckPointer(pMD));
932 PRECONDITION(pCodeStart != NULL);
933 }
934 CONTRACTL_END;
935
936 // Fast-path: If the rejit map is empty, no need to look up anything. Do this outside
937 // of a lock to impact our caller (the prestub worker) as little as possible. If the
938 // map is nonempty, we'll acquire the lock at that point and do the lookup for real.
939 CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager();
940 if (pCodeVersionManager->GetNonDefaultILVersionCount() == 0)
941 {
942 return 0;
943 }
944
945 CodeVersionManager::TableLockHolder ch(pCodeVersionManager);
946 return ReJitManager::GetReJitIdNoLock(pMD, pCodeStart);
947}
948
949//---------------------------------------------------------------------------------------
950//
951// See comment above code:ReJitManager::GetReJitId for main details of what this does.
952//
953// This function is basically the same as GetReJitId, except caller is expected to take
954// the ReJitManager lock directly (via ReJitManager::TableLockHolder). This exists so
955// that ETW can explicitly take the triggering ReJitManager lock up front, and in the
956// proper order, to avoid lock leveling issues, and triggering issues with other locks it
957// takes that are CRST_UNSAFE_ANYMODE
958//
959
960ReJITID ReJitManager::GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart)
961{
962 CONTRACTL
963 {
964 NOTHROW;
965 CANNOT_TAKE_LOCK;
966 GC_NOTRIGGER;
967 PRECONDITION(CheckPointer(pMD));
968 PRECONDITION(pCodeStart != NULL);
969 }
970 CONTRACTL_END;
971
972 // Caller must ensure this lock is taken!
973 CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager();
974 _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread());
975
976 NativeCodeVersion nativeCodeVersion = pCodeVersionManager->GetNativeCodeVersion(pMD, pCodeStart);
977 if (nativeCodeVersion.IsNull())
978 {
979 return 0;
980 }
981 return nativeCodeVersion.GetILCodeVersion().GetVersionId();
982}
983
984//---------------------------------------------------------------------------------------
985//
986// Called by profiler to retrieve an array of ReJITIDs corresponding to a MethodDesc *
987//
988// Arguments:
989// * pMD - MethodDesc * to look up
990// * cReJitIds - Element count capacity of reJitIds
991// * pcReJitIds - [out] Place total count of ReJITIDs found here; may be more than
992// cReJitIds if profiler passed an array that's too small to hold them all
993// * reJitIds - [out] Place ReJITIDs found here. Count of ReJITIDs returned here is
994// min(cReJitIds, *pcReJitIds)
995//
996// Return Value:
997// * S_OK: ReJITIDs successfully returned, array is big enough
998// * S_FALSE: ReJITIDs successfully found, but array was not big enough. Only
999// cReJitIds were returned and cReJitIds < *pcReJitId (latter being the total
1000// number of ReJITIDs available).
1001//
1002// static
1003HRESULT ReJitManager::GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[])
1004{
1005 CONTRACTL
1006 {
1007 NOTHROW;
1008 CAN_TAKE_LOCK;
1009 GC_NOTRIGGER;
1010 PRECONDITION(CheckPointer(pMD));
1011 PRECONDITION(pcReJitIds != NULL);
1012 PRECONDITION(reJitIds != NULL);
1013 }
1014 CONTRACTL_END;
1015
1016 CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager();
1017 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
1018
1019 ULONG cnt = 0;
1020
1021 ILCodeVersionCollection ilCodeVersions = pCodeVersionManager->GetILCodeVersions(pMD);
1022 for (ILCodeVersionIterator iter = ilCodeVersions.Begin(), end = ilCodeVersions.End();
1023 iter != end;
1024 iter++)
1025 {
1026 ILCodeVersion curILVersion = *iter;
1027
1028 if (curILVersion.GetRejitState() == ILCodeVersion::kStateActive)
1029 {
1030 if (cnt < cReJitIds)
1031 {
1032 reJitIds[cnt] = curILVersion.GetVersionId();
1033 }
1034 ++cnt;
1035
1036 // no overflow
1037 _ASSERTE(cnt != 0);
1038 }
1039 }
1040 *pcReJitIds = cnt;
1041
1042 return (cnt > cReJitIds) ? S_FALSE : S_OK;
1043}
1044
1045#endif // FEATURE_CODE_VERSIONING
1046#else // FEATURE_REJIT
1047
1048// On architectures that don't support rejit, just keep around some do-nothing
1049// stubs so the rest of the VM doesn't have to be littered with #ifdef FEATURE_REJIT
1050
1051// static
1052HRESULT ReJitManager::RequestReJIT(
1053 ULONG cFunctions,
1054 ModuleID rgModuleIDs[],
1055 mdMethodDef rgMethodDefs[])
1056{
1057 return E_NOTIMPL;
1058}
1059
1060// static
1061HRESULT ReJitManager::RequestRevert(
1062 ULONG cFunctions,
1063 ModuleID rgModuleIDs[],
1064 mdMethodDef rgMethodDefs[],
1065 HRESULT rgHrStatuses[])
1066{
1067 return E_NOTIMPL;
1068}
1069
1070ReJITID ReJitManager::GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart)
1071{
1072 return 0;
1073}
1074
1075ReJITID ReJitManager::GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart)
1076{
1077 return 0;
1078}
1079
1080HRESULT ReJitManager::GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[])
1081{
1082 return E_NOTIMPL;
1083}
1084
1085#endif // FEATURE_REJIT
1086