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: CodeVersion.cpp |
6 | // |
7 | // =========================================================================== |
8 | |
9 | #include "common.h" |
10 | #include "codeversion.h" |
11 | |
12 | #ifdef FEATURE_CODE_VERSIONING |
13 | #include "threadsuspend.h" |
14 | #include "methoditer.h" |
15 | #include "../debug/ee/debugger.h" |
16 | #include "../debug/ee/walker.h" |
17 | #include "../debug/ee/controller.h" |
18 | #endif // FEATURE_CODE_VERSIONING |
19 | |
20 | #ifndef FEATURE_CODE_VERSIONING |
21 | |
22 | // |
23 | // When not using code versioning we've got a minimal implementation of |
24 | // NativeCodeVersion that simply wraps a MethodDesc* with no additional |
25 | // versioning information |
26 | // |
27 | |
28 | NativeCodeVersion::NativeCodeVersion(const NativeCodeVersion & rhs) : m_pMethod(rhs.m_pMethod) {} |
29 | NativeCodeVersion::NativeCodeVersion(PTR_MethodDesc pMethod) : m_pMethod(pMethod) {} |
30 | BOOL NativeCodeVersion::IsNull() const { return m_pMethod == NULL; } |
31 | PTR_MethodDesc NativeCodeVersion::GetMethodDesc() const { return m_pMethod; } |
32 | PCODE NativeCodeVersion::GetNativeCode() const { return m_pMethod->GetNativeCode(); } |
33 | NativeCodeVersionId NativeCodeVersion::GetVersionId() const { return 0; } |
34 | ReJITID NativeCodeVersion::GetILCodeVersionId() const; { return 0; } |
35 | ILCodeVersion NativeCodeVersion::GetILCodeVersion() const { return ILCodeVersion(m_pMethod); } |
36 | #ifndef DACCESS_COMPILE |
37 | BOOL NativeCodeVersion::SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected) { return m_pMethod->SetNativeCodeInterlocked(pCode, pExpected); } |
38 | #endif |
39 | bool NativeCodeVersion::operator==(const NativeCodeVersion & rhs) const { return m_pMethod == rhs.m_pMethod; } |
40 | bool NativeCodeVersion::operator!=(const NativeCodeVersion & rhs) const { return !operator==(rhs); } |
41 | |
42 | |
43 | #else // FEATURE_CODE_VERSIONING |
44 | |
45 | |
46 | // This HRESULT is only used as a private implementation detail. If it escapes through public APIS |
47 | // it is a bug. Corerror.xml has a comment in it reserving this value for our use but it doesn't |
48 | // appear in the public headers. |
49 | |
50 | #define CORPROF_E_RUNTIME_SUSPEND_REQUIRED 0x80131381 |
51 | |
52 | #ifndef DACCESS_COMPILE |
53 | NativeCodeVersionNode::NativeCodeVersionNode( |
54 | NativeCodeVersionId id, |
55 | MethodDesc* pMethodDesc, |
56 | ReJITID parentId, |
57 | NativeCodeVersion::OptimizationTier optimizationTier) |
58 | : |
59 | m_pNativeCode(NULL), |
60 | m_pMethodDesc(pMethodDesc), |
61 | m_parentId(parentId), |
62 | m_pNextMethodDescSibling(NULL), |
63 | m_id(id), |
64 | #ifdef FEATURE_TIERED_COMPILATION |
65 | m_optTier(optimizationTier), |
66 | #endif |
67 | m_flags(0) |
68 | {} |
69 | #endif |
70 | |
71 | #ifdef DEBUG |
72 | BOOL NativeCodeVersionNode::LockOwnedByCurrentThread() const |
73 | { |
74 | LIMITED_METHOD_DAC_CONTRACT; |
75 | return GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread(); |
76 | } |
77 | #endif //DEBUG |
78 | |
79 | PTR_MethodDesc NativeCodeVersionNode::GetMethodDesc() const |
80 | { |
81 | LIMITED_METHOD_DAC_CONTRACT; |
82 | return m_pMethodDesc; |
83 | } |
84 | |
85 | PCODE NativeCodeVersionNode::GetNativeCode() const |
86 | { |
87 | LIMITED_METHOD_DAC_CONTRACT; |
88 | return m_pNativeCode; |
89 | } |
90 | |
91 | ReJITID NativeCodeVersionNode::GetILVersionId() const |
92 | { |
93 | LIMITED_METHOD_DAC_CONTRACT; |
94 | return m_parentId; |
95 | } |
96 | |
97 | ILCodeVersion NativeCodeVersionNode::GetILCodeVersion() const |
98 | { |
99 | LIMITED_METHOD_DAC_CONTRACT; |
100 | #ifdef DEBUG |
101 | if (GetILVersionId() != 0) |
102 | { |
103 | _ASSERTE(LockOwnedByCurrentThread()); |
104 | } |
105 | #endif |
106 | PTR_MethodDesc pMD = GetMethodDesc(); |
107 | return pMD->GetCodeVersionManager()->GetILCodeVersion(pMD, GetILVersionId()); |
108 | } |
109 | |
110 | NativeCodeVersionId NativeCodeVersionNode::GetVersionId() const |
111 | { |
112 | LIMITED_METHOD_DAC_CONTRACT; |
113 | return m_id; |
114 | } |
115 | |
116 | #ifndef DACCESS_COMPILE |
117 | BOOL NativeCodeVersionNode::SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected) |
118 | { |
119 | LIMITED_METHOD_CONTRACT; |
120 | return FastInterlockCompareExchangePointer(&m_pNativeCode, |
121 | (TADDR&)pCode, (TADDR&)pExpected) == (TADDR&)pExpected; |
122 | } |
123 | #endif |
124 | |
125 | BOOL NativeCodeVersionNode::IsActiveChildVersion() const |
126 | { |
127 | LIMITED_METHOD_DAC_CONTRACT; |
128 | _ASSERTE(LockOwnedByCurrentThread()); |
129 | return (m_flags & IsActiveChildFlag) != 0; |
130 | } |
131 | |
132 | #ifndef DACCESS_COMPILE |
133 | void NativeCodeVersionNode::SetActiveChildFlag(BOOL isActive) |
134 | { |
135 | LIMITED_METHOD_CONTRACT; |
136 | _ASSERTE(LockOwnedByCurrentThread()); |
137 | if (isActive) |
138 | { |
139 | m_flags |= IsActiveChildFlag; |
140 | } |
141 | else |
142 | { |
143 | m_flags &= ~IsActiveChildFlag; |
144 | } |
145 | } |
146 | #endif |
147 | |
148 | |
149 | #ifdef FEATURE_TIERED_COMPILATION |
150 | NativeCodeVersion::OptimizationTier NativeCodeVersionNode::GetOptimizationTier() const |
151 | { |
152 | LIMITED_METHOD_DAC_CONTRACT; |
153 | return m_optTier; |
154 | } |
155 | #endif // FEATURE_TIERED_COMPILATION |
156 | |
157 | NativeCodeVersion::NativeCodeVersion() : |
158 | m_storageKind(StorageKind::Unknown) |
159 | {} |
160 | |
161 | NativeCodeVersion::NativeCodeVersion(const NativeCodeVersion & rhs) : |
162 | m_storageKind(rhs.m_storageKind) |
163 | { |
164 | if(m_storageKind == StorageKind::Explicit) |
165 | { |
166 | m_pVersionNode = rhs.m_pVersionNode; |
167 | } |
168 | else if(m_storageKind == StorageKind::Synthetic) |
169 | { |
170 | m_synthetic = rhs.m_synthetic; |
171 | } |
172 | } |
173 | |
174 | NativeCodeVersion::NativeCodeVersion(PTR_NativeCodeVersionNode pVersionNode) : |
175 | m_storageKind(pVersionNode != NULL ? StorageKind::Explicit : StorageKind::Unknown), |
176 | m_pVersionNode(pVersionNode) |
177 | {} |
178 | |
179 | NativeCodeVersion::NativeCodeVersion(PTR_MethodDesc pMethod) : |
180 | m_storageKind(pMethod != NULL ? StorageKind::Synthetic : StorageKind::Unknown) |
181 | { |
182 | LIMITED_METHOD_DAC_CONTRACT; |
183 | m_synthetic.m_pMethodDesc = pMethod; |
184 | } |
185 | |
186 | BOOL NativeCodeVersion::IsNull() const |
187 | { |
188 | LIMITED_METHOD_DAC_CONTRACT; |
189 | return m_storageKind == StorageKind::Unknown; |
190 | } |
191 | |
192 | BOOL NativeCodeVersion::IsDefaultVersion() const |
193 | { |
194 | LIMITED_METHOD_DAC_CONTRACT; |
195 | return m_storageKind == StorageKind::Synthetic; |
196 | } |
197 | |
198 | PTR_MethodDesc NativeCodeVersion::GetMethodDesc() const |
199 | { |
200 | LIMITED_METHOD_DAC_CONTRACT; |
201 | if (m_storageKind == StorageKind::Explicit) |
202 | { |
203 | return AsNode()->GetMethodDesc(); |
204 | } |
205 | else |
206 | { |
207 | return m_synthetic.m_pMethodDesc; |
208 | } |
209 | } |
210 | |
211 | PCODE NativeCodeVersion::GetNativeCode() const |
212 | { |
213 | LIMITED_METHOD_DAC_CONTRACT; |
214 | if (m_storageKind == StorageKind::Explicit) |
215 | { |
216 | return AsNode()->GetNativeCode(); |
217 | } |
218 | else |
219 | { |
220 | return GetMethodDesc()->GetNativeCode(); |
221 | } |
222 | } |
223 | |
224 | ReJITID NativeCodeVersion::GetILCodeVersionId() const |
225 | { |
226 | LIMITED_METHOD_DAC_CONTRACT; |
227 | if (m_storageKind == StorageKind::Explicit) |
228 | { |
229 | return AsNode()->GetILVersionId(); |
230 | } |
231 | else |
232 | { |
233 | return 0; |
234 | } |
235 | } |
236 | |
237 | ILCodeVersion NativeCodeVersion::GetILCodeVersion() const |
238 | { |
239 | LIMITED_METHOD_DAC_CONTRACT; |
240 | if (m_storageKind == StorageKind::Explicit) |
241 | { |
242 | return AsNode()->GetILCodeVersion(); |
243 | } |
244 | else |
245 | { |
246 | PTR_MethodDesc pMethod = GetMethodDesc(); |
247 | return ILCodeVersion(dac_cast<PTR_Module>(pMethod->GetModule()), pMethod->GetMemberDef()); |
248 | } |
249 | } |
250 | |
251 | NativeCodeVersionId NativeCodeVersion::GetVersionId() const |
252 | { |
253 | LIMITED_METHOD_DAC_CONTRACT; |
254 | if (m_storageKind == StorageKind::Explicit) |
255 | { |
256 | return AsNode()->GetVersionId(); |
257 | } |
258 | else |
259 | { |
260 | return 0; |
261 | } |
262 | } |
263 | |
264 | #ifndef DACCESS_COMPILE |
265 | BOOL NativeCodeVersion::SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected) |
266 | { |
267 | LIMITED_METHOD_CONTRACT; |
268 | if (m_storageKind == StorageKind::Explicit) |
269 | { |
270 | return AsNode()->SetNativeCodeInterlocked(pCode, pExpected); |
271 | } |
272 | else |
273 | { |
274 | return GetMethodDesc()->SetNativeCodeInterlocked(pCode, pExpected); |
275 | } |
276 | } |
277 | #endif |
278 | |
279 | BOOL NativeCodeVersion::IsActiveChildVersion() const |
280 | { |
281 | LIMITED_METHOD_DAC_CONTRACT; |
282 | if (m_storageKind == StorageKind::Explicit) |
283 | { |
284 | return AsNode()->IsActiveChildVersion(); |
285 | } |
286 | else |
287 | { |
288 | MethodDescVersioningState* pMethodVersioningState = GetMethodDescVersioningState(); |
289 | if (pMethodVersioningState == NULL) |
290 | { |
291 | return TRUE; |
292 | } |
293 | return pMethodVersioningState->IsDefaultVersionActiveChild(); |
294 | } |
295 | } |
296 | |
297 | PTR_MethodDescVersioningState NativeCodeVersion::GetMethodDescVersioningState() const |
298 | { |
299 | LIMITED_METHOD_DAC_CONTRACT; |
300 | PTR_MethodDesc pMethodDesc = GetMethodDesc(); |
301 | CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager(); |
302 | return pCodeVersionManager->GetMethodDescVersioningState(pMethodDesc); |
303 | } |
304 | |
305 | #ifndef DACCESS_COMPILE |
306 | void NativeCodeVersion::SetActiveChildFlag(BOOL isActive) |
307 | { |
308 | LIMITED_METHOD_DAC_CONTRACT; |
309 | if (m_storageKind == StorageKind::Explicit) |
310 | { |
311 | AsNode()->SetActiveChildFlag(isActive); |
312 | } |
313 | else |
314 | { |
315 | MethodDescVersioningState* pMethodVersioningState = GetMethodDescVersioningState(); |
316 | pMethodVersioningState->SetDefaultVersionActiveChildFlag(isActive); |
317 | } |
318 | } |
319 | |
320 | MethodDescVersioningState* NativeCodeVersion::GetMethodDescVersioningState() |
321 | { |
322 | LIMITED_METHOD_DAC_CONTRACT; |
323 | MethodDesc* pMethodDesc = GetMethodDesc(); |
324 | CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager(); |
325 | return pCodeVersionManager->GetMethodDescVersioningState(pMethodDesc); |
326 | } |
327 | #endif |
328 | |
329 | #ifdef FEATURE_TIERED_COMPILATION |
330 | NativeCodeVersion::OptimizationTier NativeCodeVersion::GetOptimizationTier() const |
331 | { |
332 | LIMITED_METHOD_DAC_CONTRACT; |
333 | if (m_storageKind == StorageKind::Explicit) |
334 | { |
335 | return AsNode()->GetOptimizationTier(); |
336 | } |
337 | else |
338 | { |
339 | return TieredCompilationManager::GetInitialOptimizationTier(GetMethodDesc()); |
340 | } |
341 | } |
342 | #endif |
343 | |
344 | PTR_NativeCodeVersionNode NativeCodeVersion::AsNode() const |
345 | { |
346 | LIMITED_METHOD_DAC_CONTRACT; |
347 | if (m_storageKind == StorageKind::Explicit) |
348 | { |
349 | return m_pVersionNode; |
350 | } |
351 | else |
352 | { |
353 | return NULL; |
354 | } |
355 | } |
356 | |
357 | #ifndef DACCESS_COMPILE |
358 | PTR_NativeCodeVersionNode NativeCodeVersion::AsNode() |
359 | { |
360 | LIMITED_METHOD_CONTRACT; |
361 | if (m_storageKind == StorageKind::Explicit) |
362 | { |
363 | return m_pVersionNode; |
364 | } |
365 | else |
366 | { |
367 | return NULL; |
368 | } |
369 | } |
370 | #endif |
371 | |
372 | bool NativeCodeVersion::operator==(const NativeCodeVersion & rhs) const |
373 | { |
374 | LIMITED_METHOD_DAC_CONTRACT; |
375 | if (m_storageKind == StorageKind::Explicit) |
376 | { |
377 | return (rhs.m_storageKind == StorageKind::Explicit) && |
378 | (rhs.AsNode() == AsNode()); |
379 | } |
380 | else if (m_storageKind == StorageKind::Synthetic) |
381 | { |
382 | return (rhs.m_storageKind == StorageKind::Synthetic) && |
383 | (m_synthetic.m_pMethodDesc == rhs.m_synthetic.m_pMethodDesc); |
384 | } |
385 | else |
386 | { |
387 | return rhs.m_storageKind == StorageKind::Unknown; |
388 | } |
389 | } |
390 | bool NativeCodeVersion::operator!=(const NativeCodeVersion & rhs) const |
391 | { |
392 | LIMITED_METHOD_DAC_CONTRACT; |
393 | return !operator==(rhs); |
394 | } |
395 | |
396 | NativeCodeVersionCollection::NativeCodeVersionCollection(PTR_MethodDesc pMethodDescFilter, ILCodeVersion ilCodeFilter) : |
397 | m_pMethodDescFilter(pMethodDescFilter), |
398 | m_ilCodeFilter(ilCodeFilter) |
399 | { |
400 | } |
401 | |
402 | NativeCodeVersionIterator NativeCodeVersionCollection::Begin() |
403 | { |
404 | LIMITED_METHOD_DAC_CONTRACT; |
405 | return NativeCodeVersionIterator(this); |
406 | } |
407 | NativeCodeVersionIterator NativeCodeVersionCollection::End() |
408 | { |
409 | LIMITED_METHOD_DAC_CONTRACT; |
410 | return NativeCodeVersionIterator(NULL); |
411 | } |
412 | |
413 | NativeCodeVersionIterator::NativeCodeVersionIterator(NativeCodeVersionCollection* pNativeCodeVersionCollection) : |
414 | m_stage(IterationStage::Initial), |
415 | m_pCollection(pNativeCodeVersionCollection), |
416 | m_pLinkedListCur(dac_cast<PTR_NativeCodeVersionNode>(nullptr)) |
417 | { |
418 | LIMITED_METHOD_DAC_CONTRACT; |
419 | First(); |
420 | } |
421 | void NativeCodeVersionIterator::First() |
422 | { |
423 | LIMITED_METHOD_DAC_CONTRACT; |
424 | if (m_pCollection == NULL) |
425 | { |
426 | m_stage = IterationStage::End; |
427 | } |
428 | Next(); |
429 | } |
430 | void NativeCodeVersionIterator::Next() |
431 | { |
432 | LIMITED_METHOD_DAC_CONTRACT; |
433 | if (m_stage == IterationStage::Initial) |
434 | { |
435 | ILCodeVersion ilCodeFilter = m_pCollection->m_ilCodeFilter; |
436 | m_stage = IterationStage::ImplicitCodeVersion; |
437 | if (ilCodeFilter.IsNull() || ilCodeFilter.IsDefaultVersion()) |
438 | { |
439 | m_cur = NativeCodeVersion(m_pCollection->m_pMethodDescFilter); |
440 | return; |
441 | } |
442 | } |
443 | if (m_stage == IterationStage::ImplicitCodeVersion) |
444 | { |
445 | m_stage = IterationStage::LinkedList; |
446 | CodeVersionManager* pCodeVersionManager = m_pCollection->m_pMethodDescFilter->GetCodeVersionManager(); |
447 | MethodDescVersioningState* pMethodDescVersioningState = pCodeVersionManager->GetMethodDescVersioningState(m_pCollection->m_pMethodDescFilter); |
448 | if (pMethodDescVersioningState == NULL) |
449 | { |
450 | m_pLinkedListCur = NULL; |
451 | } |
452 | else |
453 | { |
454 | ILCodeVersion ilCodeFilter = m_pCollection->m_ilCodeFilter; |
455 | m_pLinkedListCur = pMethodDescVersioningState->GetFirstVersionNode(); |
456 | while (m_pLinkedListCur != NULL && !ilCodeFilter.IsNull() && ilCodeFilter.GetVersionId() != m_pLinkedListCur->GetILVersionId()) |
457 | { |
458 | m_pLinkedListCur = m_pLinkedListCur->m_pNextMethodDescSibling; |
459 | } |
460 | } |
461 | if (m_pLinkedListCur != NULL) |
462 | { |
463 | m_cur = NativeCodeVersion(m_pLinkedListCur); |
464 | return; |
465 | } |
466 | } |
467 | if (m_stage == IterationStage::LinkedList) |
468 | { |
469 | if (m_pLinkedListCur != NULL) |
470 | { |
471 | ILCodeVersion ilCodeFilter = m_pCollection->m_ilCodeFilter; |
472 | do |
473 | { |
474 | m_pLinkedListCur = m_pLinkedListCur->m_pNextMethodDescSibling; |
475 | } while (m_pLinkedListCur != NULL && !ilCodeFilter.IsNull() && ilCodeFilter.GetVersionId() != m_pLinkedListCur->GetILVersionId()); |
476 | } |
477 | if (m_pLinkedListCur != NULL) |
478 | { |
479 | m_cur = NativeCodeVersion(m_pLinkedListCur); |
480 | return; |
481 | } |
482 | else |
483 | { |
484 | m_stage = IterationStage::End; |
485 | m_cur = NativeCodeVersion(); |
486 | } |
487 | } |
488 | } |
489 | const NativeCodeVersion & NativeCodeVersionIterator::Get() const |
490 | { |
491 | LIMITED_METHOD_DAC_CONTRACT; |
492 | return m_cur; |
493 | } |
494 | bool NativeCodeVersionIterator::Equal(const NativeCodeVersionIterator &i) const |
495 | { |
496 | LIMITED_METHOD_DAC_CONTRACT; |
497 | return m_cur == i.m_cur; |
498 | } |
499 | |
500 | ILCodeVersionNode::ILCodeVersionNode() : |
501 | m_pModule(dac_cast<PTR_Module>(nullptr)), |
502 | m_methodDef(0), |
503 | m_rejitId(0), |
504 | m_pNextILVersionNode(dac_cast<PTR_ILCodeVersionNode>(nullptr)), |
505 | m_rejitState(ILCodeVersion::kStateRequested), |
506 | m_pIL(), |
507 | m_jitFlags(0) |
508 | { |
509 | m_pIL.Store(dac_cast<PTR_COR_ILMETHOD>(nullptr)); |
510 | } |
511 | |
512 | #ifndef DACCESS_COMPILE |
513 | ILCodeVersionNode::ILCodeVersionNode(Module* pModule, mdMethodDef methodDef, ReJITID id) : |
514 | m_pModule(pModule), |
515 | m_methodDef(methodDef), |
516 | m_rejitId(id), |
517 | m_pNextILVersionNode(dac_cast<PTR_ILCodeVersionNode>(nullptr)), |
518 | m_rejitState(ILCodeVersion::kStateRequested), |
519 | m_pIL(nullptr), |
520 | m_jitFlags(0) |
521 | {} |
522 | #endif |
523 | |
524 | #ifdef DEBUG |
525 | BOOL ILCodeVersionNode::LockOwnedByCurrentThread() const |
526 | { |
527 | LIMITED_METHOD_DAC_CONTRACT; |
528 | return GetModule()->GetCodeVersionManager()->LockOwnedByCurrentThread(); |
529 | } |
530 | #endif //DEBUG |
531 | |
532 | PTR_Module ILCodeVersionNode::GetModule() const |
533 | { |
534 | LIMITED_METHOD_DAC_CONTRACT; |
535 | return m_pModule; |
536 | } |
537 | |
538 | mdMethodDef ILCodeVersionNode::GetMethodDef() const |
539 | { |
540 | LIMITED_METHOD_DAC_CONTRACT; |
541 | return m_methodDef; |
542 | } |
543 | |
544 | ReJITID ILCodeVersionNode::GetVersionId() const |
545 | { |
546 | LIMITED_METHOD_DAC_CONTRACT; |
547 | return m_rejitId; |
548 | } |
549 | |
550 | ILCodeVersion::RejitFlags ILCodeVersionNode::GetRejitState() const |
551 | { |
552 | LIMITED_METHOD_DAC_CONTRACT; |
553 | return m_rejitState.Load(); |
554 | } |
555 | |
556 | PTR_COR_ILMETHOD ILCodeVersionNode::GetIL() const |
557 | { |
558 | LIMITED_METHOD_DAC_CONTRACT; |
559 | return dac_cast<PTR_COR_ILMETHOD>(m_pIL.Load()); |
560 | } |
561 | |
562 | DWORD ILCodeVersionNode::GetJitFlags() const |
563 | { |
564 | LIMITED_METHOD_DAC_CONTRACT; |
565 | return m_jitFlags.Load(); |
566 | } |
567 | |
568 | const InstrumentedILOffsetMapping* ILCodeVersionNode::GetInstrumentedILMap() const |
569 | { |
570 | LIMITED_METHOD_DAC_CONTRACT; |
571 | _ASSERTE(LockOwnedByCurrentThread()); |
572 | return &m_instrumentedILMap; |
573 | } |
574 | |
575 | PTR_ILCodeVersionNode ILCodeVersionNode::GetNextILVersionNode() const |
576 | { |
577 | LIMITED_METHOD_DAC_CONTRACT; |
578 | _ASSERTE(LockOwnedByCurrentThread()); |
579 | return m_pNextILVersionNode; |
580 | } |
581 | |
582 | #ifndef DACCESS_COMPILE |
583 | void ILCodeVersionNode::SetRejitState(ILCodeVersion::RejitFlags newState) |
584 | { |
585 | LIMITED_METHOD_CONTRACT; |
586 | m_rejitState.Store(newState); |
587 | } |
588 | |
589 | void ILCodeVersionNode::SetIL(COR_ILMETHOD* pIL) |
590 | { |
591 | LIMITED_METHOD_CONTRACT; |
592 | m_pIL.Store(pIL); |
593 | } |
594 | |
595 | void ILCodeVersionNode::SetJitFlags(DWORD flags) |
596 | { |
597 | LIMITED_METHOD_CONTRACT; |
598 | m_jitFlags.Store(flags); |
599 | } |
600 | |
601 | void ILCodeVersionNode::SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap) |
602 | { |
603 | LIMITED_METHOD_CONTRACT; |
604 | _ASSERTE(LockOwnedByCurrentThread()); |
605 | m_instrumentedILMap.SetMappingInfo(cMap, rgMap); |
606 | } |
607 | |
608 | void ILCodeVersionNode::SetNextILVersionNode(ILCodeVersionNode* pNextILVersionNode) |
609 | { |
610 | LIMITED_METHOD_CONTRACT; |
611 | _ASSERTE(LockOwnedByCurrentThread()); |
612 | m_pNextILVersionNode = pNextILVersionNode; |
613 | } |
614 | #endif |
615 | |
616 | ILCodeVersion::ILCodeVersion() : |
617 | m_storageKind(StorageKind::Unknown) |
618 | {} |
619 | |
620 | ILCodeVersion::ILCodeVersion(const ILCodeVersion & ilCodeVersion) : |
621 | m_storageKind(ilCodeVersion.m_storageKind) |
622 | { |
623 | if(m_storageKind == StorageKind::Explicit) |
624 | { |
625 | m_pVersionNode = ilCodeVersion.m_pVersionNode; |
626 | } |
627 | else if(m_storageKind == StorageKind::Synthetic) |
628 | { |
629 | m_synthetic = ilCodeVersion.m_synthetic; |
630 | } |
631 | } |
632 | |
633 | ILCodeVersion::ILCodeVersion(PTR_ILCodeVersionNode pILCodeVersionNode) : |
634 | m_storageKind(pILCodeVersionNode != NULL ? StorageKind::Explicit : StorageKind::Unknown), |
635 | m_pVersionNode(pILCodeVersionNode) |
636 | {} |
637 | |
638 | ILCodeVersion::ILCodeVersion(PTR_Module pModule, mdMethodDef methodDef) : |
639 | m_storageKind(pModule != NULL ? StorageKind::Synthetic : StorageKind::Unknown) |
640 | { |
641 | LIMITED_METHOD_DAC_CONTRACT; |
642 | m_synthetic.m_pModule = pModule; |
643 | m_synthetic.m_methodDef = methodDef; |
644 | } |
645 | |
646 | bool ILCodeVersion::operator==(const ILCodeVersion & rhs) const |
647 | { |
648 | LIMITED_METHOD_DAC_CONTRACT; |
649 | if (m_storageKind == StorageKind::Explicit) |
650 | { |
651 | return (rhs.m_storageKind == StorageKind::Explicit) && |
652 | (AsNode() == rhs.AsNode()); |
653 | } |
654 | else if (m_storageKind == StorageKind::Synthetic) |
655 | { |
656 | return (rhs.m_storageKind == StorageKind::Synthetic) && |
657 | (m_synthetic.m_pModule == rhs.m_synthetic.m_pModule) && |
658 | (m_synthetic.m_methodDef == rhs.m_synthetic.m_methodDef); |
659 | } |
660 | else |
661 | { |
662 | return rhs.m_storageKind == StorageKind::Unknown; |
663 | } |
664 | } |
665 | |
666 | BOOL ILCodeVersion::HasDefaultIL() const |
667 | { |
668 | LIMITED_METHOD_CONTRACT; |
669 | |
670 | return (m_storageKind == StorageKind::Synthetic) || (AsNode()->GetIL() == NULL); |
671 | } |
672 | |
673 | BOOL ILCodeVersion::IsNull() const |
674 | { |
675 | LIMITED_METHOD_DAC_CONTRACT; |
676 | return m_storageKind == StorageKind::Unknown; |
677 | } |
678 | |
679 | BOOL ILCodeVersion::IsDefaultVersion() const |
680 | { |
681 | LIMITED_METHOD_DAC_CONTRACT; |
682 | return m_storageKind == StorageKind::Synthetic; |
683 | } |
684 | |
685 | PTR_Module ILCodeVersion::GetModule() const |
686 | { |
687 | LIMITED_METHOD_DAC_CONTRACT; |
688 | if (m_storageKind == StorageKind::Explicit) |
689 | { |
690 | return AsNode()->GetModule(); |
691 | } |
692 | else |
693 | { |
694 | return m_synthetic.m_pModule; |
695 | } |
696 | } |
697 | |
698 | mdMethodDef ILCodeVersion::GetMethodDef() const |
699 | { |
700 | LIMITED_METHOD_DAC_CONTRACT; |
701 | if (m_storageKind == StorageKind::Explicit) |
702 | { |
703 | return AsNode()->GetMethodDef(); |
704 | } |
705 | else |
706 | { |
707 | return m_synthetic.m_methodDef; |
708 | } |
709 | } |
710 | |
711 | ReJITID ILCodeVersion::GetVersionId() const |
712 | { |
713 | LIMITED_METHOD_DAC_CONTRACT; |
714 | if (m_storageKind == StorageKind::Explicit) |
715 | { |
716 | return AsNode()->GetVersionId(); |
717 | } |
718 | else |
719 | { |
720 | return 0; |
721 | } |
722 | } |
723 | |
724 | NativeCodeVersionCollection ILCodeVersion::GetNativeCodeVersions(PTR_MethodDesc pClosedMethodDesc) const |
725 | { |
726 | LIMITED_METHOD_DAC_CONTRACT; |
727 | return NativeCodeVersionCollection(pClosedMethodDesc, *this); |
728 | } |
729 | |
730 | NativeCodeVersion ILCodeVersion::GetActiveNativeCodeVersion(PTR_MethodDesc pClosedMethodDesc) const |
731 | { |
732 | LIMITED_METHOD_DAC_CONTRACT; |
733 | NativeCodeVersionCollection versions = GetNativeCodeVersions(pClosedMethodDesc); |
734 | for (NativeCodeVersionIterator cur = versions.Begin(), end = versions.End(); cur != end; cur++) |
735 | { |
736 | if (cur->IsActiveChildVersion()) |
737 | { |
738 | return *cur; |
739 | } |
740 | } |
741 | return NativeCodeVersion(); |
742 | } |
743 | |
744 | ILCodeVersion::RejitFlags ILCodeVersion::GetRejitState() const |
745 | { |
746 | LIMITED_METHOD_DAC_CONTRACT; |
747 | if (m_storageKind == StorageKind::Explicit) |
748 | { |
749 | return AsNode()->GetRejitState(); |
750 | } |
751 | else |
752 | { |
753 | return ILCodeVersion::kStateActive; |
754 | } |
755 | } |
756 | |
757 | PTR_COR_ILMETHOD ILCodeVersion::GetIL() const |
758 | { |
759 | CONTRACTL |
760 | { |
761 | THROWS; //GetILHeader throws |
762 | GC_NOTRIGGER; |
763 | FORBID_FAULT; |
764 | MODE_ANY; |
765 | } |
766 | CONTRACTL_END |
767 | |
768 | PTR_COR_ILMETHOD pIL = NULL; |
769 | if (m_storageKind == StorageKind::Explicit) |
770 | { |
771 | pIL = AsNode()->GetIL(); |
772 | } |
773 | |
774 | // For the default code version we always fetch the globally stored default IL for a method |
775 | // |
776 | // In the non-default code version we assume NULL is the equivalent of explicitly requesting to |
777 | // re-use the default IL. Ideally there would be no reason to create a new version that re-uses |
778 | // the default IL (just use the default code version for that) but we do it here for compat. We've |
779 | // got some profilers that use ReJIT to create a new code version and then instead of calling |
780 | // ICorProfilerFunctionControl::SetILFunctionBody they call ICorProfilerInfo::SetILFunctionBody. |
781 | // This mutates the default IL so that it is now correct for their new code version. Of course this |
782 | // also overwrote the previous default IL so now the default code version GetIL() is out of sync |
783 | // with the jitted code. In the majority of cases we never re-read the IL after the initial |
784 | // jitting so this issue goes unnoticed. |
785 | // |
786 | // If changing the default IL after it is in use becomes more problematic in the future we would |
787 | // need to add enforcement that prevents profilers from using ICorProfilerInfo::SetILFunctionBody |
788 | // that way + coordinate with them because it is a breaking change for any profiler currently doing it. |
789 | if(pIL == NULL) |
790 | { |
791 | PTR_Module pModule = GetModule(); |
792 | PTR_MethodDesc pMethodDesc = dac_cast<PTR_MethodDesc>(pModule->LookupMethodDef(GetMethodDef())); |
793 | if (pMethodDesc != NULL) |
794 | { |
795 | pIL = dac_cast<PTR_COR_ILMETHOD>(pMethodDesc->GetILHeader(TRUE)); |
796 | } |
797 | } |
798 | |
799 | return pIL; |
800 | } |
801 | |
802 | PTR_COR_ILMETHOD ILCodeVersion::GetILNoThrow() const |
803 | { |
804 | LIMITED_METHOD_DAC_CONTRACT; |
805 | PTR_COR_ILMETHOD ret; |
806 | EX_TRY |
807 | { |
808 | ret = GetIL(); |
809 | } |
810 | EX_CATCH |
811 | { |
812 | ret = NULL; |
813 | } |
814 | EX_END_CATCH(RethrowTerminalExceptions); |
815 | return ret; |
816 | } |
817 | |
818 | DWORD ILCodeVersion::GetJitFlags() const |
819 | { |
820 | LIMITED_METHOD_DAC_CONTRACT; |
821 | if (m_storageKind == StorageKind::Explicit) |
822 | { |
823 | return AsNode()->GetJitFlags(); |
824 | } |
825 | else |
826 | { |
827 | return 0; |
828 | } |
829 | } |
830 | |
831 | const InstrumentedILOffsetMapping* ILCodeVersion::GetInstrumentedILMap() const |
832 | { |
833 | LIMITED_METHOD_DAC_CONTRACT; |
834 | if (m_storageKind == StorageKind::Explicit) |
835 | { |
836 | return AsNode()->GetInstrumentedILMap(); |
837 | } |
838 | else |
839 | { |
840 | return NULL; |
841 | } |
842 | } |
843 | |
844 | #ifndef DACCESS_COMPILE |
845 | void ILCodeVersion::SetRejitState(RejitFlags newState) |
846 | { |
847 | LIMITED_METHOD_CONTRACT; |
848 | AsNode()->SetRejitState(newState); |
849 | } |
850 | |
851 | void ILCodeVersion::SetIL(COR_ILMETHOD* pIL) |
852 | { |
853 | LIMITED_METHOD_CONTRACT; |
854 | AsNode()->SetIL(pIL); |
855 | } |
856 | |
857 | void ILCodeVersion::SetJitFlags(DWORD flags) |
858 | { |
859 | LIMITED_METHOD_CONTRACT; |
860 | AsNode()->SetJitFlags(flags); |
861 | } |
862 | |
863 | void ILCodeVersion::SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap) |
864 | { |
865 | LIMITED_METHOD_CONTRACT; |
866 | AsNode()->SetInstrumentedILMap(cMap, rgMap); |
867 | } |
868 | |
869 | HRESULT ILCodeVersion::AddNativeCodeVersion( |
870 | MethodDesc* pClosedMethodDesc, |
871 | NativeCodeVersion::OptimizationTier optimizationTier, |
872 | NativeCodeVersion* pNativeCodeVersion) |
873 | { |
874 | LIMITED_METHOD_CONTRACT; |
875 | CodeVersionManager* pManager = GetModule()->GetCodeVersionManager(); |
876 | HRESULT hr = pManager->AddNativeCodeVersion(*this, pClosedMethodDesc, optimizationTier, pNativeCodeVersion); |
877 | if (FAILED(hr)) |
878 | { |
879 | _ASSERTE(hr == E_OUTOFMEMORY); |
880 | return hr; |
881 | } |
882 | return S_OK; |
883 | } |
884 | |
885 | HRESULT ILCodeVersion::GetOrCreateActiveNativeCodeVersion(MethodDesc* pClosedMethodDesc, NativeCodeVersion* pActiveNativeCodeVersion) |
886 | { |
887 | LIMITED_METHOD_CONTRACT; |
888 | HRESULT hr = S_OK; |
889 | NativeCodeVersion activeNativeChild = GetActiveNativeCodeVersion(pClosedMethodDesc); |
890 | if (activeNativeChild.IsNull()) |
891 | { |
892 | NativeCodeVersion::OptimizationTier optimizationTier = |
893 | TieredCompilationManager::GetInitialOptimizationTier(pClosedMethodDesc); |
894 | if (FAILED(hr = AddNativeCodeVersion(pClosedMethodDesc, optimizationTier, &activeNativeChild))) |
895 | { |
896 | _ASSERTE(hr == E_OUTOFMEMORY); |
897 | return hr; |
898 | } |
899 | } |
900 | // The first added child should automatically become active |
901 | _ASSERTE(GetActiveNativeCodeVersion(pClosedMethodDesc) == activeNativeChild); |
902 | *pActiveNativeCodeVersion = activeNativeChild; |
903 | return S_OK; |
904 | } |
905 | |
906 | HRESULT ILCodeVersion::SetActiveNativeCodeVersion(NativeCodeVersion activeNativeCodeVersion, BOOL fEESuspended) |
907 | { |
908 | LIMITED_METHOD_CONTRACT; |
909 | HRESULT hr = S_OK; |
910 | MethodDesc* pMethodDesc = activeNativeCodeVersion.GetMethodDesc(); |
911 | NativeCodeVersion prevActiveVersion = GetActiveNativeCodeVersion(pMethodDesc); |
912 | if (prevActiveVersion == activeNativeCodeVersion) |
913 | { |
914 | //nothing to do, this version is already active |
915 | return S_OK; |
916 | } |
917 | |
918 | if (!prevActiveVersion.IsNull()) |
919 | { |
920 | prevActiveVersion.SetActiveChildFlag(FALSE); |
921 | } |
922 | activeNativeCodeVersion.SetActiveChildFlag(TRUE); |
923 | |
924 | // If needed update the published code body for this method |
925 | CodeVersionManager* pCodeVersionManager = GetModule()->GetCodeVersionManager(); |
926 | if (pCodeVersionManager->GetActiveILCodeVersion(GetModule(), GetMethodDef()) == *this) |
927 | { |
928 | if (FAILED(hr = pCodeVersionManager->PublishNativeCodeVersion(pMethodDesc, activeNativeCodeVersion, fEESuspended))) |
929 | { |
930 | return hr; |
931 | } |
932 | } |
933 | |
934 | return S_OK; |
935 | } |
936 | |
937 | ILCodeVersionNode* ILCodeVersion::AsNode() |
938 | { |
939 | LIMITED_METHOD_CONTRACT; |
940 | //This is dangerous - NativeCodeVersion coerces non-explicit versions to NULL but ILCodeVersion assumes the caller |
941 | //will never invoke AsNode() on a non-explicit node. Asserting for now as a minimal fix, but we should revisit this. |
942 | _ASSERTE(m_storageKind == StorageKind::Explicit); |
943 | return m_pVersionNode; |
944 | } |
945 | #endif //DACCESS_COMPILE |
946 | |
947 | PTR_ILCodeVersionNode ILCodeVersion::AsNode() const |
948 | { |
949 | LIMITED_METHOD_DAC_CONTRACT; |
950 | //This is dangerous - NativeCodeVersion coerces non-explicit versions to NULL but ILCodeVersion assumes the caller |
951 | //will never invoke AsNode() on a non-explicit node. Asserting for now as a minimal fix, but we should revisit this. |
952 | _ASSERTE(m_storageKind == StorageKind::Explicit); |
953 | return m_pVersionNode; |
954 | } |
955 | |
956 | ILCodeVersionCollection::ILCodeVersionCollection(PTR_Module pModule, mdMethodDef methodDef) : |
957 | m_pModule(pModule), |
958 | m_methodDef(methodDef) |
959 | {} |
960 | |
961 | ILCodeVersionIterator ILCodeVersionCollection::Begin() |
962 | { |
963 | LIMITED_METHOD_DAC_CONTRACT; |
964 | return ILCodeVersionIterator(this); |
965 | } |
966 | |
967 | ILCodeVersionIterator ILCodeVersionCollection::End() |
968 | { |
969 | LIMITED_METHOD_DAC_CONTRACT; |
970 | return ILCodeVersionIterator(NULL); |
971 | } |
972 | |
973 | ILCodeVersionIterator::ILCodeVersionIterator(const ILCodeVersionIterator & iter) : |
974 | m_stage(iter.m_stage), |
975 | m_cur(iter.m_cur), |
976 | m_pLinkedListCur(iter.m_pLinkedListCur), |
977 | m_pCollection(iter.m_pCollection) |
978 | {} |
979 | |
980 | ILCodeVersionIterator::ILCodeVersionIterator(ILCodeVersionCollection* pCollection) : |
981 | m_stage(pCollection != NULL ? IterationStage::Initial : IterationStage::End), |
982 | m_pLinkedListCur(dac_cast<PTR_ILCodeVersionNode>(nullptr)), |
983 | m_pCollection(pCollection) |
984 | { |
985 | LIMITED_METHOD_DAC_CONTRACT; |
986 | First(); |
987 | } |
988 | |
989 | const ILCodeVersion & ILCodeVersionIterator::Get() const |
990 | { |
991 | LIMITED_METHOD_DAC_CONTRACT; |
992 | return m_cur; |
993 | } |
994 | |
995 | void ILCodeVersionIterator::First() |
996 | { |
997 | LIMITED_METHOD_DAC_CONTRACT; |
998 | Next(); |
999 | } |
1000 | |
1001 | void ILCodeVersionIterator::Next() |
1002 | { |
1003 | LIMITED_METHOD_DAC_CONTRACT; |
1004 | if (m_stage == IterationStage::Initial) |
1005 | { |
1006 | m_stage = IterationStage::ImplicitCodeVersion; |
1007 | m_cur = ILCodeVersion(m_pCollection->m_pModule, m_pCollection->m_methodDef); |
1008 | return; |
1009 | } |
1010 | if (m_stage == IterationStage::ImplicitCodeVersion) |
1011 | { |
1012 | CodeVersionManager* pCodeVersionManager = m_pCollection->m_pModule->GetCodeVersionManager(); |
1013 | _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread()); |
1014 | PTR_ILCodeVersioningState pILCodeVersioningState = pCodeVersionManager->GetILCodeVersioningState(m_pCollection->m_pModule, m_pCollection->m_methodDef); |
1015 | if (pILCodeVersioningState != NULL) |
1016 | { |
1017 | m_pLinkedListCur = pILCodeVersioningState->GetFirstVersionNode(); |
1018 | } |
1019 | m_stage = IterationStage::LinkedList; |
1020 | if (m_pLinkedListCur != NULL) |
1021 | { |
1022 | m_cur = ILCodeVersion(m_pLinkedListCur); |
1023 | return; |
1024 | } |
1025 | } |
1026 | if (m_stage == IterationStage::LinkedList) |
1027 | { |
1028 | if (m_pLinkedListCur != NULL) |
1029 | { |
1030 | m_pLinkedListCur = m_pLinkedListCur->GetNextILVersionNode(); |
1031 | } |
1032 | if (m_pLinkedListCur != NULL) |
1033 | { |
1034 | m_cur = ILCodeVersion(m_pLinkedListCur); |
1035 | return; |
1036 | } |
1037 | else |
1038 | { |
1039 | m_stage = IterationStage::End; |
1040 | m_cur = ILCodeVersion(); |
1041 | return; |
1042 | } |
1043 | } |
1044 | } |
1045 | |
1046 | bool ILCodeVersionIterator::Equal(const ILCodeVersionIterator &i) const |
1047 | { |
1048 | LIMITED_METHOD_DAC_CONTRACT; |
1049 | return m_cur == i.m_cur; |
1050 | } |
1051 | |
1052 | MethodDescVersioningState::MethodDescVersioningState(PTR_MethodDesc pMethodDesc) : |
1053 | m_pMethodDesc(pMethodDesc), |
1054 | m_flags(IsDefaultVersionActiveChildFlag), |
1055 | m_nextId(1), |
1056 | m_pFirstVersionNode(dac_cast<PTR_NativeCodeVersionNode>(nullptr)) |
1057 | { |
1058 | LIMITED_METHOD_DAC_CONTRACT; |
1059 | #ifdef FEATURE_JUMPSTAMP |
1060 | ZeroMemory(m_rgSavedCode, JumpStubSize); |
1061 | #endif |
1062 | } |
1063 | |
1064 | PTR_MethodDesc MethodDescVersioningState::GetMethodDesc() const |
1065 | { |
1066 | LIMITED_METHOD_DAC_CONTRACT; |
1067 | return m_pMethodDesc; |
1068 | } |
1069 | |
1070 | #ifndef DACCESS_COMPILE |
1071 | NativeCodeVersionId MethodDescVersioningState::AllocateVersionId() |
1072 | { |
1073 | LIMITED_METHOD_CONTRACT; |
1074 | return m_nextId++; |
1075 | } |
1076 | #endif |
1077 | |
1078 | PTR_NativeCodeVersionNode MethodDescVersioningState::GetFirstVersionNode() const |
1079 | { |
1080 | LIMITED_METHOD_DAC_CONTRACT; |
1081 | return m_pFirstVersionNode; |
1082 | } |
1083 | |
1084 | #ifdef FEATURE_JUMPSTAMP |
1085 | MethodDescVersioningState::JumpStampFlags MethodDescVersioningState::GetJumpStampState() |
1086 | { |
1087 | LIMITED_METHOD_DAC_CONTRACT; |
1088 | return (JumpStampFlags)(m_flags & JumpStampMask); |
1089 | } |
1090 | |
1091 | #ifndef DACCESS_COMPILE |
1092 | void MethodDescVersioningState::SetJumpStampState(JumpStampFlags newState) |
1093 | { |
1094 | LIMITED_METHOD_CONTRACT; |
1095 | m_flags = (m_flags & ~JumpStampMask) | (BYTE)newState; |
1096 | } |
1097 | #endif // DACCESS_COMPILE |
1098 | |
1099 | #ifndef DACCESS_COMPILE |
1100 | HRESULT MethodDescVersioningState::SyncJumpStamp(NativeCodeVersion nativeCodeVersion, BOOL fEESuspended) |
1101 | { |
1102 | LIMITED_METHOD_CONTRACT; |
1103 | HRESULT hr = S_OK; |
1104 | PCODE pCode = nativeCodeVersion.IsNull() ? NULL : nativeCodeVersion.GetNativeCode(); |
1105 | MethodDesc* pMethod = GetMethodDesc(); |
1106 | _ASSERTE(pMethod->IsVersionable() && pMethod->IsVersionableWithJumpStamp()); |
1107 | |
1108 | if (!pMethod->HasNativeCode()) |
1109 | { |
1110 | //we'll set up the jump-stamp when the default native code is created |
1111 | return S_OK; |
1112 | } |
1113 | |
1114 | if (!nativeCodeVersion.IsNull() && nativeCodeVersion.IsDefaultVersion()) |
1115 | { |
1116 | return UndoJumpStampNativeCode(fEESuspended); |
1117 | } |
1118 | else |
1119 | { |
1120 | // We don't have new code ready yet, jumpstamp back to the prestub to let us generate it the next time |
1121 | // the method is called |
1122 | if (pCode == NULL) |
1123 | { |
1124 | if (!fEESuspended) |
1125 | { |
1126 | return CORPROF_E_RUNTIME_SUSPEND_REQUIRED; |
1127 | } |
1128 | return JumpStampNativeCode(); |
1129 | } |
1130 | // We do know the new code body, install the jump stamp now |
1131 | else |
1132 | { |
1133 | return UpdateJumpTarget(fEESuspended, pCode); |
1134 | } |
1135 | } |
1136 | } |
1137 | #endif // DACCESS_COMPILE |
1138 | |
1139 | //--------------------------------------------------------------------------------------- |
1140 | // |
1141 | // Simple, thin abstraction of debugger breakpoint patching. Given an address and a |
1142 | // previously procured DebuggerControllerPatch governing the code address, this decides |
1143 | // whether the code address is patched. If so, it returns a pointer to the debugger's |
1144 | // buffer (of what's "underneath" the int 3 patch); otherwise, it returns the code |
1145 | // address itself. |
1146 | // |
1147 | // Arguments: |
1148 | // * pbCode - Code address to return if unpatched |
1149 | // * dbgpatch - DebuggerControllerPatch to test |
1150 | // |
1151 | // Return Value: |
1152 | // Either pbCode or the debugger's patch buffer, as per description above. |
1153 | // |
1154 | // Assumptions: |
1155 | // Caller must manually grab (and hold) the ControllerLockHolder and get the |
1156 | // DebuggerControllerPatch before calling this helper. |
1157 | // |
1158 | // Notes: |
1159 | // pbCode need not equal the code address governed by dbgpatch, but is always |
1160 | // "related" (and sometimes really is equal). For example, this helper may be used |
1161 | // when writing a code byte to an internal rejit buffer (e.g., in preparation for an |
1162 | // eventual 64-bit interlocked write into the code stream), and thus pbCode would |
1163 | // point into the internal rejit buffer whereas dbgpatch governs the corresponding |
1164 | // code byte in the live code stream. This function would then be used to determine |
1165 | // whether a byte should be written into the internal rejit buffer OR into the |
1166 | // debugger controller's breakpoint buffer. |
1167 | // |
1168 | |
1169 | LPBYTE FirstCodeByteAddr(LPBYTE pbCode, DebuggerControllerPatch * dbgpatch) |
1170 | { |
1171 | LIMITED_METHOD_CONTRACT; |
1172 | |
1173 | if (dbgpatch != NULL && dbgpatch->IsActivated()) |
1174 | { |
1175 | // Debugger has patched the code, so return the address of the buffer |
1176 | return LPBYTE(&(dbgpatch->opcode)); |
1177 | } |
1178 | |
1179 | // no active patch, just return the direct code address |
1180 | return pbCode; |
1181 | } |
1182 | |
1183 | |
1184 | #ifdef _DEBUG |
1185 | #ifndef DACCESS_COMPILE |
1186 | BOOL MethodDescVersioningState::CodeIsSaved() |
1187 | { |
1188 | LIMITED_METHOD_CONTRACT; |
1189 | |
1190 | for (size_t i = 0; i < sizeof(m_rgSavedCode); i++) |
1191 | { |
1192 | if (m_rgSavedCode[i] != 0) |
1193 | return TRUE; |
1194 | } |
1195 | return FALSE; |
1196 | } |
1197 | #endif //DACCESS_COMPILE |
1198 | #endif //_DEBUG |
1199 | |
1200 | //--------------------------------------------------------------------------------------- |
1201 | // |
1202 | // Do the actual work of stamping the top of originally-jitted-code with a jmp that goes |
1203 | // to the prestub. This can be called in one of three ways: |
1204 | // * Case 1: By RequestReJIT against an already-jitted function, in which case the |
1205 | // PCODE may be inferred by the MethodDesc, and our caller will have suspended |
1206 | // the EE for us, OR |
1207 | // * Case 2: By the prestub worker after jitting the original code of a function |
1208 | // (i.e., the "pre-rejit" scenario). In this case, the EE is not suspended. But |
1209 | // that's ok, because the PCODE has not yet been published to the MethodDesc, and |
1210 | // no thread can be executing inside the originally JITted function yet. |
1211 | // * Case 3: At type/method restore time for an NGEN'ed assembly. This is also the pre-rejit |
1212 | // scenario because we are guaranteed to do this before the code in the module |
1213 | // is executable. EE suspend is not required. |
1214 | // |
1215 | // Arguments: |
1216 | // * pCode - Case 1 (above): will be NULL, and we can infer the PCODE from the |
1217 | // MethodDesc; Case 2+3 (above, pre-rejit): will be non-NULL, and we'll need to use |
1218 | // this to find the code to stamp on top of. |
1219 | // |
1220 | // Return Value: |
1221 | // * S_OK: Either we successfully did the jmp-stamp, or a racing thread took care of |
1222 | // it for us. |
1223 | // * Else, HRESULT indicating failure. |
1224 | // |
1225 | // Assumptions: |
1226 | // The caller will have suspended the EE if necessary (case 1), before this is |
1227 | // called. |
1228 | // |
1229 | #ifndef DACCESS_COMPILE |
1230 | HRESULT MethodDescVersioningState::JumpStampNativeCode(PCODE pCode /* = NULL */) |
1231 | { |
1232 | CONTRACTL |
1233 | { |
1234 | NOTHROW; |
1235 | GC_NOTRIGGER; |
1236 | // It may seem dangerous to be stamping jumps over code while a GC is going on, |
1237 | // but we're actually safe. As we assert below, either we're holding the thread |
1238 | // store lock (and thus preventing a GC) OR we're stamping code that has not yet |
1239 | // been published (and will thus not be executed by managed therads or examined |
1240 | // by the GC). |
1241 | MODE_ANY; |
1242 | } |
1243 | CONTRACTL_END; |
1244 | |
1245 | PCODE pCodePublished = GetMethodDesc()->GetNativeCode(); |
1246 | |
1247 | _ASSERTE((pCode != NULL) || (pCodePublished != NULL)); |
1248 | _ASSERTE(GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread()); |
1249 | |
1250 | HRESULT hr = S_OK; |
1251 | |
1252 | // We'll jump-stamp over pCode, or if pCode is NULL, jump-stamp over the published |
1253 | // code for this's MethodDesc. |
1254 | LPBYTE pbCode = (LPBYTE)pCode; |
1255 | if (pbCode == NULL) |
1256 | { |
1257 | // If caller didn't specify a pCode, just use the one that was published after |
1258 | // the original JIT. (A specific pCode would be passed in the pre-rejit case, |
1259 | // to jump-stamp the original code BEFORE the PCODE gets published.) |
1260 | pbCode = (LPBYTE)pCodePublished; |
1261 | } |
1262 | _ASSERTE(pbCode != NULL); |
1263 | |
1264 | // The debugging API may also try to write to the very top of this function (though |
1265 | // with an int 3 for breakpoint purposes). Coordinate with the debugger so we know |
1266 | // whether we can safely patch the actual code, or instead write to the debugger's |
1267 | // buffer. |
1268 | DebuggerController::ControllerLockHolder lockController; |
1269 | |
1270 | if (GetJumpStampState() == JumpStampToPrestub) |
1271 | { |
1272 | // The method has already been jump stamped so nothing left to do |
1273 | _ASSERTE(CodeIsSaved()); |
1274 | return S_OK; |
1275 | } |
1276 | |
1277 | // Remember what we're stamping our jump on top of, so we can replace it during a |
1278 | // revert. |
1279 | if (GetJumpStampState() == JumpStampNone) |
1280 | { |
1281 | for (int i = 0; i < sizeof(m_rgSavedCode); i++) |
1282 | { |
1283 | m_rgSavedCode[i] = *FirstCodeByteAddr(pbCode + i, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)(pbCode + i))); |
1284 | } |
1285 | } |
1286 | |
1287 | EX_TRY |
1288 | { |
1289 | AllocMemTracker amt; |
1290 | |
1291 | // This guy might throw on out-of-memory, so rely on the tracker to clean-up |
1292 | Precode * pPrecode = Precode::Allocate(PRECODE_STUB, GetMethodDesc(), GetMethodDesc()->GetLoaderAllocator(), &amt); |
1293 | PCODE target = pPrecode->GetEntryPoint(); |
1294 | |
1295 | #if defined(_X86_) || defined(_AMD64_) |
1296 | |
1297 | // Normal unpatched code never starts with a jump |
1298 | _ASSERTE(GetJumpStampState() == JumpStampToActiveVersion || |
1299 | *FirstCodeByteAddr(pbCode, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)pbCode)) != X86_INSTR_JMP_REL32); |
1300 | |
1301 | INT64 i64OldCode = *(INT64*)pbCode; |
1302 | INT64 i64NewCode = i64OldCode; |
1303 | LPBYTE pbNewValue = (LPBYTE)&i64NewCode; |
1304 | *pbNewValue = X86_INSTR_JMP_REL32; |
1305 | INT32 UNALIGNED * pOffset = reinterpret_cast<INT32 UNALIGNED *>(&pbNewValue[1]); |
1306 | // This will throw for out-of-memory, so don't write anything until |
1307 | // after he succeeds |
1308 | // This guy will leak/cache/reuse the jumpstub |
1309 | *pOffset = rel32UsingJumpStub(reinterpret_cast<INT32 UNALIGNED *>(pbCode + 1), target, GetMethodDesc(), GetMethodDesc()->GetLoaderAllocator()); |
1310 | |
1311 | // If we have the EE suspended or the code is unpublished there won't be contention on this code |
1312 | hr = UpdateJumpStampHelper(pbCode, i64OldCode, i64NewCode, FALSE); |
1313 | if (FAILED(hr)) |
1314 | { |
1315 | ThrowHR(hr); |
1316 | } |
1317 | |
1318 | // |
1319 | // No failure point after this! |
1320 | // |
1321 | amt.SuppressRelease(); |
1322 | |
1323 | #else // _X86_ || _AMD64_ |
1324 | #error "Need to define a way to jump-stamp the prolog in a safe way for this platform" |
1325 | #endif // _X86_ || _AMD64_ |
1326 | |
1327 | SetJumpStampState(JumpStampToPrestub); |
1328 | } |
1329 | EX_CATCH_HRESULT(hr); |
1330 | _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY); |
1331 | |
1332 | if (SUCCEEDED(hr)) |
1333 | { |
1334 | _ASSERTE(GetJumpStampState() == JumpStampToPrestub); |
1335 | _ASSERTE(m_rgSavedCode[0] != 0); // saved code should not start with 0 |
1336 | } |
1337 | |
1338 | return hr; |
1339 | } |
1340 | |
1341 | |
1342 | //--------------------------------------------------------------------------------------- |
1343 | // |
1344 | // After code has been rejitted, this is called to update the jump-stamp to go from |
1345 | // pointing to the prestub, to pointing to the newly rejitted code. |
1346 | // |
1347 | // Arguments: |
1348 | // fEESuspended - TRUE if the caller keeps the EE suspended during this call |
1349 | // pRejittedCode - jitted code for the updated IL this method should execute |
1350 | // |
1351 | // Assumptions: |
1352 | // This rejit manager's table crst should be held by the caller |
1353 | // |
1354 | // Returns - S_OK if the jump target is updated |
1355 | // CORPROF_E_RUNTIME_SUSPEND_REQUIRED if the ee isn't suspended and it |
1356 | // will need to be in order to do the update safely |
1357 | HRESULT MethodDescVersioningState::UpdateJumpTarget(BOOL fEESuspended, PCODE pRejittedCode) |
1358 | { |
1359 | CONTRACTL |
1360 | { |
1361 | NOTHROW; |
1362 | GC_NOTRIGGER; |
1363 | MODE_PREEMPTIVE; |
1364 | } |
1365 | CONTRACTL_END; |
1366 | |
1367 | MethodDesc * pMD = GetMethodDesc(); |
1368 | _ASSERTE(pMD->GetCodeVersionManager()->LockOwnedByCurrentThread()); |
1369 | |
1370 | // It isn't safe to overwrite the original method prolog with a jmp because threads might |
1371 | // be at an IP in the middle of the jump stamp already. However converting between different |
1372 | // jump stamps is OK (when done atomically) because this only changes the jmp target, not |
1373 | // instruction boundaries. |
1374 | if (GetJumpStampState() == JumpStampNone && !fEESuspended) |
1375 | { |
1376 | return CORPROF_E_RUNTIME_SUSPEND_REQUIRED; |
1377 | } |
1378 | |
1379 | // Beginning of originally JITted code containing the jmp that we will redirect. |
1380 | BYTE * pbCode = (BYTE*)pMD->GetNativeCode(); |
1381 | |
1382 | // Remember what we're stamping our jump on top of, so we can replace it during a |
1383 | // revert. |
1384 | if (GetJumpStampState() == JumpStampNone) |
1385 | { |
1386 | for (int i = 0; i < sizeof(m_rgSavedCode); i++) |
1387 | { |
1388 | m_rgSavedCode[i] = *FirstCodeByteAddr(pbCode + i, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)(pbCode + i))); |
1389 | } |
1390 | } |
1391 | |
1392 | #if defined(_X86_) || defined(_AMD64_) |
1393 | |
1394 | HRESULT hr = S_OK; |
1395 | { |
1396 | DebuggerController::ControllerLockHolder lockController; |
1397 | |
1398 | // This will throw for out-of-memory, so don't write anything until |
1399 | // after he succeeds |
1400 | // This guy will leak/cache/reuse the jumpstub |
1401 | INT32 offset = 0; |
1402 | EX_TRY |
1403 | { |
1404 | offset = rel32UsingJumpStub( |
1405 | reinterpret_cast<INT32 UNALIGNED *>(&pbCode[1]), // base of offset |
1406 | pRejittedCode, // target of jump |
1407 | pMD, |
1408 | pMD->GetLoaderAllocator()); |
1409 | } |
1410 | EX_CATCH_HRESULT(hr); |
1411 | _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY); |
1412 | if (FAILED(hr)) |
1413 | { |
1414 | return hr; |
1415 | } |
1416 | // For validation later, remember what pbCode is right now |
1417 | INT64 i64OldValue = *(INT64 *)pbCode; |
1418 | |
1419 | // Assemble the INT64 of the new code bytes to write. Start with what's there now |
1420 | INT64 i64NewValue = i64OldValue; |
1421 | LPBYTE pbNewValue = (LPBYTE)&i64NewValue; |
1422 | |
1423 | // First byte becomes a rel32 jmp instruction (if it wasn't already) |
1424 | *pbNewValue = X86_INSTR_JMP_REL32; |
1425 | // Next 4 bytes are the jmp target (offset to jmp stub) |
1426 | INT32 UNALIGNED * pnOffset = reinterpret_cast<INT32 UNALIGNED *>(&pbNewValue[1]); |
1427 | *pnOffset = offset; |
1428 | |
1429 | hr = UpdateJumpStampHelper(pbCode, i64OldValue, i64NewValue, !fEESuspended); |
1430 | _ASSERTE(hr == S_OK || (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED && !fEESuspended)); |
1431 | } |
1432 | if (FAILED(hr)) |
1433 | { |
1434 | return hr; |
1435 | } |
1436 | |
1437 | #else // _X86_ || _AMD64_ |
1438 | #error "Need to define a way to jump-stamp the prolog in a safe way for this platform" |
1439 | #endif // _X86_ || _AMD64_ |
1440 | |
1441 | // State transition |
1442 | SetJumpStampState(JumpStampToActiveVersion); |
1443 | return S_OK; |
1444 | } |
1445 | |
1446 | |
1447 | //--------------------------------------------------------------------------------------- |
1448 | // |
1449 | // Poke the JITted code to satsify a revert request (or to perform an implicit revert as |
1450 | // part of a second, third, etc. rejit request). Reinstates the originally JITted code |
1451 | // that had been jump-stamped over to perform a prior rejit. |
1452 | // |
1453 | // Arguments |
1454 | // fEESuspended - TRUE if the caller keeps the EE suspended during this call |
1455 | // |
1456 | // |
1457 | // Return Value: |
1458 | // S_OK to indicate the revert succeeded, |
1459 | // CORPROF_E_RUNTIME_SUSPEND_REQUIRED to indicate the jumpstamp hasn't been reverted |
1460 | // and EE suspension will be needed for success |
1461 | // other failure HRESULT indicating what went wrong. |
1462 | // |
1463 | // Assumptions: |
1464 | // Caller must be holding the owning ReJitManager's table crst. |
1465 | // |
1466 | HRESULT MethodDescVersioningState::UndoJumpStampNativeCode(BOOL fEESuspended) |
1467 | { |
1468 | CONTRACTL |
1469 | { |
1470 | NOTHROW; |
1471 | GC_NOTRIGGER; |
1472 | MODE_ANY; |
1473 | } |
1474 | CONTRACTL_END; |
1475 | |
1476 | _ASSERTE(GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread()); |
1477 | if (GetJumpStampState() == JumpStampNone) |
1478 | { |
1479 | return S_OK; |
1480 | } |
1481 | |
1482 | _ASSERTE(m_rgSavedCode[0] != 0); // saved code should not start with 0 |
1483 | |
1484 | BYTE * pbCode = (BYTE*)GetMethodDesc()->GetNativeCode(); |
1485 | DebuggerController::ControllerLockHolder lockController; |
1486 | |
1487 | #if defined(_X86_) || defined(_AMD64_) |
1488 | _ASSERTE(m_rgSavedCode[0] != X86_INSTR_JMP_REL32); |
1489 | _ASSERTE(*FirstCodeByteAddr(pbCode, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)pbCode)) == X86_INSTR_JMP_REL32); |
1490 | #else |
1491 | #error "Need to define a way to jump-stamp the prolog in a safe way for this platform" |
1492 | #endif // _X86_ || _AMD64_ |
1493 | |
1494 | // For the interlocked compare, remember what pbCode is right now |
1495 | INT64 i64OldValue = *(INT64 *)pbCode; |
1496 | // Assemble the INT64 of the new code bytes to write. Start with what's there now |
1497 | INT64 i64NewValue = i64OldValue; |
1498 | memcpy(LPBYTE(&i64NewValue), m_rgSavedCode, sizeof(m_rgSavedCode)); |
1499 | HRESULT hr = UpdateJumpStampHelper(pbCode, i64OldValue, i64NewValue, !fEESuspended); |
1500 | _ASSERTE(hr == S_OK || (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED && !fEESuspended)); |
1501 | if (hr != S_OK) |
1502 | return hr; |
1503 | |
1504 | // Transition state of this ReJitInfo to indicate the MD no longer has any jump stamp |
1505 | SetJumpStampState(JumpStampNone); |
1506 | return S_OK; |
1507 | } |
1508 | #endif |
1509 | |
1510 | //--------------------------------------------------------------------------------------- |
1511 | // |
1512 | // This is called to modify the jump-stamp area, the first ReJitInfo::JumpStubSize bytes |
1513 | // in the method's code. |
1514 | // |
1515 | // Notes: |
1516 | // Callers use this method in a variety of circumstances: |
1517 | // a) when the code is unpublished (fContentionPossible == FALSE) |
1518 | // b) when the caller has taken the ThreadStoreLock and suspended the EE |
1519 | // (fContentionPossible == FALSE) |
1520 | // c) when the code is published, the EE isn't suspended, and the jumpstamp |
1521 | // area consists of a single 5 byte long jump instruction |
1522 | // (fContentionPossible == TRUE) |
1523 | // This method will attempt to alter the jump-stamp even if the caller has not prevented |
1524 | // contention, but there is no guarantee it will be succesful. When the caller has prevented |
1525 | // contention, then success is assured. Callers may opportunistically try without |
1526 | // EE suspension, and then upgrade to EE suspension if the first attempt fails. |
1527 | // |
1528 | // Assumptions: |
1529 | // This rejit manager's table crst should be held by the caller or fContentionPossible==FALSE |
1530 | // The debugger patch table lock should be held by the caller |
1531 | // |
1532 | // Arguments: |
1533 | // pbCode - pointer to the code where the jump stamp is placed |
1534 | // i64OldValue - the bytes which should currently be at the start of the method code |
1535 | // i64NewValue - the new bytes which should be written at the start of the method code |
1536 | // fContentionPossible - See the Notes section above. |
1537 | // |
1538 | // Returns: |
1539 | // S_OK => the jumpstamp has been succesfully updated. |
1540 | // CORPROF_E_RUNTIME_SUSPEND_REQUIRED => the jumpstamp remains unchanged (preventing contention will be necessary) |
1541 | // other failing HR => VirtualProtect failed, the jumpstamp remains unchanged |
1542 | // |
1543 | #ifndef DACCESS_COMPILE |
1544 | HRESULT MethodDescVersioningState::UpdateJumpStampHelper(BYTE* pbCode, INT64 i64OldValue, INT64 i64NewValue, BOOL fContentionPossible) |
1545 | { |
1546 | CONTRACTL |
1547 | { |
1548 | NOTHROW; |
1549 | GC_NOTRIGGER; |
1550 | MODE_ANY; |
1551 | } |
1552 | CONTRACTL_END; |
1553 | |
1554 | MethodDesc * pMD = GetMethodDesc(); |
1555 | _ASSERTE(pMD->GetCodeVersionManager()->LockOwnedByCurrentThread() || !fContentionPossible); |
1556 | |
1557 | // When ReJIT is enabled, method entrypoints are always at least 8-byte aligned (see |
1558 | // code:EEJitManager::allocCode), so we can do a single 64-bit interlocked operation |
1559 | // to update the jump target. However, some code may have gotten compiled before |
1560 | // the profiler had a chance to enable ReJIT (e.g., NGENd code, or code JITted |
1561 | // before a profiler attaches). In such cases, we cannot rely on a simple |
1562 | // interlocked operation, and instead must suspend the runtime to ensure we can |
1563 | // safely update the jmp instruction. |
1564 | // |
1565 | // This method doesn't verify that the method is actually safe to rejit, we expect |
1566 | // callers to do that. At the moment NGEN'ed code is safe to rejit even if |
1567 | // it is unaligned, but code generated before the profiler attaches is not. |
1568 | if (fContentionPossible && !(IS_ALIGNED(pbCode, sizeof(INT64)))) |
1569 | { |
1570 | return CORPROF_E_RUNTIME_SUSPEND_REQUIRED; |
1571 | } |
1572 | |
1573 | // The debugging API may also try to write to this function (though |
1574 | // with an int 3 for breakpoint purposes). Coordinate with the debugger so we know |
1575 | // whether we can safely patch the actual code, or instead write to the debugger's |
1576 | // buffer. |
1577 | if (fContentionPossible) |
1578 | { |
1579 | for (CORDB_ADDRESS_TYPE* pbProbeAddr = pbCode; pbProbeAddr < pbCode + MethodDescVersioningState::JumpStubSize; pbProbeAddr++) |
1580 | { |
1581 | if (NULL != DebuggerController::GetPatchTable()->GetPatch(pbProbeAddr)) |
1582 | { |
1583 | return CORPROF_E_RUNTIME_SUSPEND_REQUIRED; |
1584 | } |
1585 | } |
1586 | } |
1587 | |
1588 | #if defined(_X86_) || defined(_AMD64_) |
1589 | |
1590 | DWORD oldProt; |
1591 | if (!ClrVirtualProtect((LPVOID)pbCode, 8, PAGE_EXECUTE_READWRITE, &oldProt)) |
1592 | { |
1593 | return HRESULT_FROM_WIN32(GetLastError()); |
1594 | } |
1595 | |
1596 | if (fContentionPossible) |
1597 | { |
1598 | INT64 i64InterlockReportedOldValue = FastInterlockCompareExchangeLong((INT64 *)pbCode, i64NewValue, i64OldValue); |
1599 | // Since changes to these bytes are protected by this rejitmgr's m_crstTable, we |
1600 | // shouldn't have two writers conflicting. |
1601 | _ASSERTE(i64InterlockReportedOldValue == i64OldValue); |
1602 | } |
1603 | else |
1604 | { |
1605 | // In this path the caller ensures: |
1606 | // a) no thread will execute through the prologue area we are modifying |
1607 | // b) no thread is stopped in a prologue such that it resumes in the middle of code we are modifying |
1608 | // c) no thread is doing a debugger patch skip operation in which an unmodified copy of the method's |
1609 | // code could be executed from a patch skip buffer. |
1610 | |
1611 | // PERF: we might still want a faster path through here if we aren't debugging that doesn't do |
1612 | // all the patch checks |
1613 | for (int i = 0; i < MethodDescVersioningState::JumpStubSize; i++) |
1614 | { |
1615 | *FirstCodeByteAddr(pbCode + i, DebuggerController::GetPatchTable()->GetPatch(pbCode + i)) = ((BYTE*)&i64NewValue)[i]; |
1616 | } |
1617 | } |
1618 | |
1619 | if (oldProt != PAGE_EXECUTE_READWRITE) |
1620 | { |
1621 | // The CLR codebase in many locations simply ignores failures to restore the page protections |
1622 | // Its true that it isn't a problem functionally, but it seems a bit sketchy? |
1623 | // I am following the convention for now. |
1624 | ClrVirtualProtect((LPVOID)pbCode, 8, oldProt, &oldProt); |
1625 | } |
1626 | |
1627 | FlushInstructionCache(GetCurrentProcess(), pbCode, MethodDescVersioningState::JumpStubSize); |
1628 | return S_OK; |
1629 | |
1630 | #else // _X86_ || _AMD64_ |
1631 | #error "Need to define a way to jump-stamp the prolog in a safe way for this platform" |
1632 | #endif // _X86_ || _AMD64_ |
1633 | } |
1634 | #endif |
1635 | #endif // FEATURE_JUMPSTAMP |
1636 | |
1637 | BOOL MethodDescVersioningState::IsDefaultVersionActiveChild() const |
1638 | { |
1639 | LIMITED_METHOD_DAC_CONTRACT; |
1640 | return (m_flags & IsDefaultVersionActiveChildFlag) != 0; |
1641 | } |
1642 | #ifndef DACCESS_COMPILE |
1643 | void MethodDescVersioningState::SetDefaultVersionActiveChildFlag(BOOL isActive) |
1644 | { |
1645 | LIMITED_METHOD_CONTRACT; |
1646 | if (isActive) |
1647 | { |
1648 | m_flags |= IsDefaultVersionActiveChildFlag; |
1649 | } |
1650 | else |
1651 | { |
1652 | m_flags &= ~IsDefaultVersionActiveChildFlag; |
1653 | } |
1654 | } |
1655 | |
1656 | void MethodDescVersioningState::LinkNativeCodeVersionNode(NativeCodeVersionNode* pNativeCodeVersionNode) |
1657 | { |
1658 | LIMITED_METHOD_CONTRACT; |
1659 | pNativeCodeVersionNode->m_pNextMethodDescSibling = m_pFirstVersionNode; |
1660 | m_pFirstVersionNode = pNativeCodeVersionNode; |
1661 | } |
1662 | #endif |
1663 | |
1664 | ILCodeVersioningState::ILCodeVersioningState(PTR_Module pModule, mdMethodDef methodDef) : |
1665 | m_activeVersion(ILCodeVersion(pModule,methodDef)), |
1666 | m_pFirstVersionNode(dac_cast<PTR_ILCodeVersionNode>(nullptr)), |
1667 | m_pModule(pModule), |
1668 | m_methodDef(methodDef) |
1669 | {} |
1670 | |
1671 | |
1672 | ILCodeVersioningState::Key::Key() : |
1673 | m_pModule(dac_cast<PTR_Module>(nullptr)), |
1674 | m_methodDef(0) |
1675 | {} |
1676 | |
1677 | ILCodeVersioningState::Key::Key(PTR_Module pModule, mdMethodDef methodDef) : |
1678 | m_pModule(pModule), |
1679 | m_methodDef(methodDef) |
1680 | {} |
1681 | |
1682 | size_t ILCodeVersioningState::Key::Hash() const |
1683 | { |
1684 | LIMITED_METHOD_DAC_CONTRACT; |
1685 | return (size_t)(dac_cast<TADDR>(m_pModule) ^ m_methodDef); |
1686 | } |
1687 | |
1688 | bool ILCodeVersioningState::Key::operator==(const Key & rhs) const |
1689 | { |
1690 | LIMITED_METHOD_DAC_CONTRACT; |
1691 | return (m_pModule == rhs.m_pModule) && (m_methodDef == rhs.m_methodDef); |
1692 | } |
1693 | |
1694 | ILCodeVersioningState::Key ILCodeVersioningState::GetKey() const |
1695 | { |
1696 | LIMITED_METHOD_DAC_CONTRACT; |
1697 | return Key(m_pModule, m_methodDef); |
1698 | } |
1699 | |
1700 | ILCodeVersion ILCodeVersioningState::GetActiveVersion() const |
1701 | { |
1702 | LIMITED_METHOD_DAC_CONTRACT; |
1703 | return m_activeVersion; |
1704 | } |
1705 | |
1706 | PTR_ILCodeVersionNode ILCodeVersioningState::GetFirstVersionNode() const |
1707 | { |
1708 | LIMITED_METHOD_DAC_CONTRACT; |
1709 | return m_pFirstVersionNode; |
1710 | } |
1711 | |
1712 | #ifndef DACCESS_COMPILE |
1713 | void ILCodeVersioningState::SetActiveVersion(ILCodeVersion ilActiveCodeVersion) |
1714 | { |
1715 | LIMITED_METHOD_CONTRACT; |
1716 | m_activeVersion = ilActiveCodeVersion; |
1717 | } |
1718 | |
1719 | void ILCodeVersioningState::LinkILCodeVersionNode(ILCodeVersionNode* pILCodeVersionNode) |
1720 | { |
1721 | LIMITED_METHOD_CONTRACT; |
1722 | pILCodeVersionNode->SetNextILVersionNode(m_pFirstVersionNode); |
1723 | m_pFirstVersionNode = pILCodeVersionNode; |
1724 | } |
1725 | #endif |
1726 | |
1727 | CodeVersionManager::CodeVersionManager() |
1728 | {} |
1729 | |
1730 | //--------------------------------------------------------------------------------------- |
1731 | // |
1732 | // Called from BaseDomain::BaseDomain to do any constructor-time initialization. |
1733 | // Presently, this takes care of initializing the Crst. |
1734 | // |
1735 | |
1736 | void CodeVersionManager::PreInit() |
1737 | { |
1738 | CONTRACTL |
1739 | { |
1740 | THROWS; |
1741 | GC_TRIGGERS; |
1742 | CAN_TAKE_LOCK; |
1743 | MODE_ANY; |
1744 | } |
1745 | CONTRACTL_END; |
1746 | |
1747 | #ifndef DACCESS_COMPILE |
1748 | m_crstTable.Init( |
1749 | CrstReJITDomainTable, |
1750 | CrstFlags(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN)); |
1751 | #endif // DACCESS_COMPILE |
1752 | } |
1753 | |
1754 | CodeVersionManager::TableLockHolder::TableLockHolder(CodeVersionManager* pCodeVersionManager) : |
1755 | CrstHolder(&pCodeVersionManager->m_crstTable) |
1756 | { |
1757 | } |
1758 | #ifndef DACCESS_COMPILE |
1759 | void CodeVersionManager::EnterLock() |
1760 | { |
1761 | m_crstTable.Enter(); |
1762 | } |
1763 | void CodeVersionManager::LeaveLock() |
1764 | { |
1765 | m_crstTable.Leave(); |
1766 | } |
1767 | #endif |
1768 | |
1769 | #ifdef DEBUG |
1770 | BOOL CodeVersionManager::LockOwnedByCurrentThread() const |
1771 | { |
1772 | LIMITED_METHOD_DAC_CONTRACT; |
1773 | #ifdef DACCESS_COMPILE |
1774 | return TRUE; |
1775 | #else |
1776 | return const_cast<CrstExplicitInit &>(m_crstTable).OwnedByCurrentThread(); |
1777 | #endif |
1778 | } |
1779 | #endif |
1780 | |
1781 | PTR_ILCodeVersioningState CodeVersionManager::GetILCodeVersioningState(PTR_Module pModule, mdMethodDef methodDef) const |
1782 | { |
1783 | LIMITED_METHOD_DAC_CONTRACT; |
1784 | ILCodeVersioningState::Key key = ILCodeVersioningState::Key(pModule, methodDef); |
1785 | return m_ilCodeVersioningStateMap.Lookup(key); |
1786 | } |
1787 | |
1788 | PTR_MethodDescVersioningState CodeVersionManager::GetMethodDescVersioningState(PTR_MethodDesc pClosedMethodDesc) const |
1789 | { |
1790 | LIMITED_METHOD_DAC_CONTRACT; |
1791 | return m_methodDescVersioningStateMap.Lookup(pClosedMethodDesc); |
1792 | } |
1793 | |
1794 | #ifndef DACCESS_COMPILE |
1795 | HRESULT CodeVersionManager::GetOrCreateILCodeVersioningState(Module* pModule, mdMethodDef methodDef, ILCodeVersioningState** ppILCodeVersioningState) |
1796 | { |
1797 | LIMITED_METHOD_CONTRACT; |
1798 | HRESULT hr = S_OK; |
1799 | ILCodeVersioningState* pILCodeVersioningState = GetILCodeVersioningState(pModule, methodDef); |
1800 | if (pILCodeVersioningState == NULL) |
1801 | { |
1802 | pILCodeVersioningState = new (nothrow) ILCodeVersioningState(pModule, methodDef); |
1803 | if (pILCodeVersioningState == NULL) |
1804 | { |
1805 | return E_OUTOFMEMORY; |
1806 | } |
1807 | EX_TRY |
1808 | { |
1809 | // This throws when out of memory, but remains internally |
1810 | // consistent (without adding the new element) |
1811 | m_ilCodeVersioningStateMap.Add(pILCodeVersioningState); |
1812 | } |
1813 | EX_CATCH_HRESULT(hr); |
1814 | if (FAILED(hr)) |
1815 | { |
1816 | delete pILCodeVersioningState; |
1817 | return hr; |
1818 | } |
1819 | } |
1820 | *ppILCodeVersioningState = pILCodeVersioningState; |
1821 | return S_OK; |
1822 | } |
1823 | |
1824 | HRESULT CodeVersionManager::GetOrCreateMethodDescVersioningState(MethodDesc* pMethod, MethodDescVersioningState** ppMethodVersioningState) |
1825 | { |
1826 | LIMITED_METHOD_CONTRACT; |
1827 | HRESULT hr = S_OK; |
1828 | MethodDescVersioningState* pMethodVersioningState = m_methodDescVersioningStateMap.Lookup(pMethod); |
1829 | if (pMethodVersioningState == NULL) |
1830 | { |
1831 | pMethodVersioningState = new (nothrow) MethodDescVersioningState(pMethod); |
1832 | if (pMethodVersioningState == NULL) |
1833 | { |
1834 | return E_OUTOFMEMORY; |
1835 | } |
1836 | EX_TRY |
1837 | { |
1838 | // This throws when out of memory, but remains internally |
1839 | // consistent (without adding the new element) |
1840 | m_methodDescVersioningStateMap.Add(pMethodVersioningState); |
1841 | } |
1842 | EX_CATCH_HRESULT(hr); |
1843 | if (FAILED(hr)) |
1844 | { |
1845 | delete pMethodVersioningState; |
1846 | return hr; |
1847 | } |
1848 | } |
1849 | *ppMethodVersioningState = pMethodVersioningState; |
1850 | return S_OK; |
1851 | } |
1852 | #endif // DACCESS_COMPILE |
1853 | |
1854 | DWORD CodeVersionManager::GetNonDefaultILVersionCount() |
1855 | { |
1856 | LIMITED_METHOD_DAC_CONTRACT; |
1857 | |
1858 | //This function is legal to call WITHOUT taking the lock |
1859 | //It is used to do a quick check if work might be needed without paying the overhead |
1860 | //of acquiring the lock and doing dictionary lookups |
1861 | return m_ilCodeVersioningStateMap.GetCount(); |
1862 | } |
1863 | |
1864 | ILCodeVersionCollection CodeVersionManager::GetILCodeVersions(PTR_MethodDesc pMethod) |
1865 | { |
1866 | LIMITED_METHOD_DAC_CONTRACT; |
1867 | _ASSERTE(LockOwnedByCurrentThread()); |
1868 | return GetILCodeVersions(dac_cast<PTR_Module>(pMethod->GetModule()), pMethod->GetMemberDef()); |
1869 | } |
1870 | |
1871 | ILCodeVersionCollection CodeVersionManager::GetILCodeVersions(PTR_Module pModule, mdMethodDef methodDef) |
1872 | { |
1873 | LIMITED_METHOD_DAC_CONTRACT; |
1874 | _ASSERTE(LockOwnedByCurrentThread()); |
1875 | return ILCodeVersionCollection(pModule, methodDef); |
1876 | } |
1877 | |
1878 | ILCodeVersion CodeVersionManager::GetActiveILCodeVersion(PTR_MethodDesc pMethod) |
1879 | { |
1880 | LIMITED_METHOD_DAC_CONTRACT; |
1881 | _ASSERTE(LockOwnedByCurrentThread()); |
1882 | return GetActiveILCodeVersion(dac_cast<PTR_Module>(pMethod->GetModule()), pMethod->GetMemberDef()); |
1883 | } |
1884 | |
1885 | ILCodeVersion CodeVersionManager::GetActiveILCodeVersion(PTR_Module pModule, mdMethodDef methodDef) |
1886 | { |
1887 | LIMITED_METHOD_DAC_CONTRACT; |
1888 | _ASSERTE(LockOwnedByCurrentThread()); |
1889 | ILCodeVersioningState* pILCodeVersioningState = GetILCodeVersioningState(pModule, methodDef); |
1890 | if (pILCodeVersioningState == NULL) |
1891 | { |
1892 | return ILCodeVersion(pModule, methodDef); |
1893 | } |
1894 | else |
1895 | { |
1896 | return pILCodeVersioningState->GetActiveVersion(); |
1897 | } |
1898 | } |
1899 | |
1900 | ILCodeVersion CodeVersionManager::GetILCodeVersion(PTR_MethodDesc pMethod, ReJITID rejitId) |
1901 | { |
1902 | LIMITED_METHOD_DAC_CONTRACT; |
1903 | _ASSERTE(LockOwnedByCurrentThread()); |
1904 | |
1905 | #ifdef FEATURE_REJIT |
1906 | ILCodeVersionCollection collection = GetILCodeVersions(pMethod); |
1907 | for (ILCodeVersionIterator cur = collection.Begin(), end = collection.End(); cur != end; cur++) |
1908 | { |
1909 | if (cur->GetVersionId() == rejitId) |
1910 | { |
1911 | return *cur; |
1912 | } |
1913 | } |
1914 | return ILCodeVersion(); |
1915 | #else // FEATURE_REJIT |
1916 | _ASSERTE(rejitId == 0); |
1917 | return ILCodeVersion(dac_cast<PTR_Module>(pMethod->GetModule()), pMethod->GetMemberDef()); |
1918 | #endif // FEATURE_REJIT |
1919 | } |
1920 | |
1921 | NativeCodeVersionCollection CodeVersionManager::GetNativeCodeVersions(PTR_MethodDesc pMethod) const |
1922 | { |
1923 | LIMITED_METHOD_DAC_CONTRACT; |
1924 | _ASSERTE(LockOwnedByCurrentThread()); |
1925 | return NativeCodeVersionCollection(pMethod, ILCodeVersion()); |
1926 | } |
1927 | |
1928 | NativeCodeVersion CodeVersionManager::GetNativeCodeVersion(PTR_MethodDesc pMethod, PCODE codeStartAddress) const |
1929 | { |
1930 | LIMITED_METHOD_DAC_CONTRACT; |
1931 | _ASSERTE(LockOwnedByCurrentThread()); |
1932 | |
1933 | NativeCodeVersionCollection nativeCodeVersions = GetNativeCodeVersions(pMethod); |
1934 | for (NativeCodeVersionIterator cur = nativeCodeVersions.Begin(), end = nativeCodeVersions.End(); cur != end; cur++) |
1935 | { |
1936 | if (cur->GetNativeCode() == codeStartAddress) |
1937 | { |
1938 | return *cur; |
1939 | } |
1940 | } |
1941 | return NativeCodeVersion(); |
1942 | } |
1943 | |
1944 | #ifndef DACCESS_COMPILE |
1945 | HRESULT CodeVersionManager::AddILCodeVersion(Module* pModule, mdMethodDef methodDef, ReJITID rejitId, ILCodeVersion* pILCodeVersion) |
1946 | { |
1947 | LIMITED_METHOD_CONTRACT; |
1948 | _ASSERTE(LockOwnedByCurrentThread()); |
1949 | |
1950 | ILCodeVersioningState* pILCodeVersioningState; |
1951 | HRESULT hr = GetOrCreateILCodeVersioningState(pModule, methodDef, &pILCodeVersioningState); |
1952 | if (FAILED(hr)) |
1953 | { |
1954 | _ASSERTE(hr == E_OUTOFMEMORY); |
1955 | return hr; |
1956 | } |
1957 | |
1958 | ILCodeVersionNode* pILCodeVersionNode = new (nothrow) ILCodeVersionNode(pModule, methodDef, rejitId); |
1959 | if (pILCodeVersionNode == NULL) |
1960 | { |
1961 | return E_OUTOFMEMORY; |
1962 | } |
1963 | pILCodeVersioningState->LinkILCodeVersionNode(pILCodeVersionNode); |
1964 | *pILCodeVersion = ILCodeVersion(pILCodeVersionNode); |
1965 | return S_OK; |
1966 | } |
1967 | |
1968 | HRESULT CodeVersionManager::SetActiveILCodeVersions(ILCodeVersion* pActiveVersions, DWORD cActiveVersions, BOOL fEESuspended, CDynArray<CodePublishError> * pErrors) |
1969 | { |
1970 | // If the IL version is in the shared domain we need to iterate all domains |
1971 | // looking for instantiations. The domain iterator lock is bigger than |
1972 | // the code version manager lock so we can't do this atomically. In one atomic |
1973 | // update the bookkeeping for IL versioning will happen and then in a second |
1974 | // update the active native code versions will change/code jumpstamps+precodes |
1975 | // will update. |
1976 | // |
1977 | // Note: For all domains other than the shared AppDomain we could do this |
1978 | // atomically, but for now we use the lowest common denominator for all |
1979 | // domains. |
1980 | CONTRACTL |
1981 | { |
1982 | NOTHROW; |
1983 | GC_TRIGGERS; |
1984 | MODE_PREEMPTIVE; |
1985 | CAN_TAKE_LOCK; |
1986 | PRECONDITION(CheckPointer(pActiveVersions)); |
1987 | PRECONDITION(CheckPointer(pErrors, NULL_OK)); |
1988 | } |
1989 | CONTRACTL_END; |
1990 | _ASSERTE(!LockOwnedByCurrentThread()); |
1991 | HRESULT hr = S_OK; |
1992 | |
1993 | #if DEBUG |
1994 | for (DWORD i = 0; i < cActiveVersions; i++) |
1995 | { |
1996 | ILCodeVersion activeVersion = pActiveVersions[i]; |
1997 | if (activeVersion.IsNull()) |
1998 | { |
1999 | _ASSERTE(!"The active IL version can't be NULL" ); |
2000 | } |
2001 | } |
2002 | #endif |
2003 | |
2004 | // step 1 - mark the IL versions as being active, this ensures that |
2005 | // any new method instantiations added after this point will bind to |
2006 | // the correct version |
2007 | { |
2008 | TableLockHolder(this); |
2009 | for (DWORD i = 0; i < cActiveVersions; i++) |
2010 | { |
2011 | ILCodeVersion activeVersion = pActiveVersions[i]; |
2012 | ILCodeVersioningState* pILCodeVersioningState = NULL; |
2013 | if (FAILED(hr = GetOrCreateILCodeVersioningState(activeVersion.GetModule(), activeVersion.GetMethodDef(), &pILCodeVersioningState))) |
2014 | { |
2015 | _ASSERTE(hr == E_OUTOFMEMORY); |
2016 | return hr; |
2017 | } |
2018 | pILCodeVersioningState->SetActiveVersion(activeVersion); |
2019 | } |
2020 | } |
2021 | |
2022 | // step 2 - determine the set of pre-existing method instantiations |
2023 | |
2024 | // a parallel array to activeVersions |
2025 | // for each ILCodeVersion in activeVersions, this lists the set |
2026 | // MethodDescs that will need to be updated |
2027 | CDynArray<CDynArray<MethodDesc*>> methodDescsToUpdate; |
2028 | CDynArray<CodePublishError> errorRecords; |
2029 | for (DWORD i = 0; i < cActiveVersions; i++) |
2030 | { |
2031 | CDynArray<MethodDesc*>* pMethodDescs = methodDescsToUpdate.Append(); |
2032 | if (pMethodDescs == NULL) |
2033 | { |
2034 | return E_OUTOFMEMORY; |
2035 | } |
2036 | *pMethodDescs = CDynArray<MethodDesc*>(); |
2037 | |
2038 | MethodDesc* pLoadedMethodDesc = pActiveVersions[i].GetModule()->LookupMethodDef(pActiveVersions[i].GetMethodDef()); |
2039 | if (FAILED(hr = CodeVersionManager::EnumerateClosedMethodDescs(pLoadedMethodDesc, pMethodDescs, &errorRecords))) |
2040 | { |
2041 | _ASSERTE(hr == E_OUTOFMEMORY); |
2042 | return hr; |
2043 | } |
2044 | } |
2045 | |
2046 | // step 3 - update each pre-existing method instantiation |
2047 | { |
2048 | TableLockHolder lock(this); |
2049 | for (DWORD i = 0; i < cActiveVersions; i++) |
2050 | { |
2051 | // Its possible the active IL version has changed if |
2052 | // another caller made an update while this method wasn't |
2053 | // holding the lock. We will ensure that we synchronize |
2054 | // publishing to whatever version is currently active, even |
2055 | // if that isn't the IL version we set above. |
2056 | // |
2057 | // Note: Although we attempt to handle this case gracefully |
2058 | // it isn't recommended for callers to do this. Racing two calls |
2059 | // that set the IL version to different results means it will be |
2060 | // completely arbitrary which version wins. |
2061 | ILCodeVersion requestedActiveILVersion = pActiveVersions[i]; |
2062 | ILCodeVersion activeILVersion = GetActiveILCodeVersion(requestedActiveILVersion.GetModule(), requestedActiveILVersion.GetMethodDef()); |
2063 | |
2064 | CDynArray<MethodDesc*> methodDescs = methodDescsToUpdate[i]; |
2065 | for (int j = 0; j < methodDescs.Count(); j++) |
2066 | { |
2067 | // Get an the active child code version for this method instantiation (it might be NULL, that is OK) |
2068 | NativeCodeVersion activeNativeChild = activeILVersion.GetActiveNativeCodeVersion(methodDescs[j]); |
2069 | |
2070 | // Publish that child version, because it is the active native child of the active IL version |
2071 | // Failing to publish is non-fatal, but we do record it so the caller is aware |
2072 | if (FAILED(hr = PublishNativeCodeVersion(methodDescs[j], activeNativeChild, fEESuspended))) |
2073 | { |
2074 | if (FAILED(hr = AddCodePublishError(activeILVersion.GetModule(), activeILVersion.GetMethodDef(), methodDescs[j], hr, &errorRecords))) |
2075 | { |
2076 | _ASSERTE(hr == E_OUTOFMEMORY); |
2077 | return hr; |
2078 | } |
2079 | } |
2080 | } |
2081 | } |
2082 | } |
2083 | |
2084 | return S_OK; |
2085 | } |
2086 | |
2087 | HRESULT CodeVersionManager::AddNativeCodeVersion( |
2088 | ILCodeVersion ilCodeVersion, |
2089 | MethodDesc* pClosedMethodDesc, |
2090 | NativeCodeVersion::OptimizationTier optimizationTier, |
2091 | NativeCodeVersion* pNativeCodeVersion) |
2092 | { |
2093 | LIMITED_METHOD_CONTRACT; |
2094 | _ASSERTE(LockOwnedByCurrentThread()); |
2095 | |
2096 | MethodDescVersioningState* pMethodVersioningState; |
2097 | HRESULT hr = GetOrCreateMethodDescVersioningState(pClosedMethodDesc, &pMethodVersioningState); |
2098 | if (FAILED(hr)) |
2099 | { |
2100 | _ASSERTE(hr == E_OUTOFMEMORY); |
2101 | return hr; |
2102 | } |
2103 | |
2104 | NativeCodeVersionId newId = pMethodVersioningState->AllocateVersionId(); |
2105 | NativeCodeVersionNode* pNativeCodeVersionNode = new (nothrow) NativeCodeVersionNode(newId, pClosedMethodDesc, ilCodeVersion.GetVersionId(), optimizationTier); |
2106 | if (pNativeCodeVersionNode == NULL) |
2107 | { |
2108 | return E_OUTOFMEMORY; |
2109 | } |
2110 | |
2111 | pMethodVersioningState->LinkNativeCodeVersionNode(pNativeCodeVersionNode); |
2112 | |
2113 | // the first child added is automatically considered the active one. |
2114 | if (ilCodeVersion.GetActiveNativeCodeVersion(pClosedMethodDesc).IsNull()) |
2115 | { |
2116 | pNativeCodeVersionNode->SetActiveChildFlag(TRUE); |
2117 | _ASSERTE(!ilCodeVersion.GetActiveNativeCodeVersion(pClosedMethodDesc).IsNull()); |
2118 | |
2119 | // the new child shouldn't have any native code. If it did we might need to |
2120 | // publish that code as part of adding the node which would require callers |
2121 | // to pay attention to GC suspension and we'd need to report publishing errors |
2122 | // back to them. |
2123 | _ASSERTE(pNativeCodeVersionNode->GetNativeCode() == NULL); |
2124 | } |
2125 | *pNativeCodeVersion = NativeCodeVersion(pNativeCodeVersionNode); |
2126 | return S_OK; |
2127 | } |
2128 | |
2129 | PCODE CodeVersionManager::PublishVersionableCodeIfNecessary(MethodDesc* pMethodDesc, BOOL fCanBackpatchPrestub) |
2130 | { |
2131 | STANDARD_VM_CONTRACT; |
2132 | _ASSERTE(!LockOwnedByCurrentThread()); |
2133 | _ASSERTE(pMethodDesc->IsVersionable()); |
2134 | _ASSERTE(!pMethodDesc->IsPointingToPrestub() || !pMethodDesc->IsVersionableWithJumpStamp()); |
2135 | |
2136 | HRESULT hr = S_OK; |
2137 | PCODE pCode = NULL; |
2138 | BOOL fIsJumpStampMethod = pMethodDesc->IsVersionableWithJumpStamp(); |
2139 | |
2140 | NativeCodeVersion activeVersion; |
2141 | { |
2142 | TableLockHolder lock(this); |
2143 | if (FAILED(hr = GetActiveILCodeVersion(pMethodDesc).GetOrCreateActiveNativeCodeVersion(pMethodDesc, &activeVersion))) |
2144 | { |
2145 | _ASSERTE(hr == E_OUTOFMEMORY); |
2146 | ReportCodePublishError(pMethodDesc->GetModule(), pMethodDesc->GetMemberDef(), pMethodDesc, hr); |
2147 | return NULL; |
2148 | } |
2149 | } |
2150 | |
2151 | BOOL fEESuspend = FALSE; |
2152 | while (true) |
2153 | { |
2154 | // compile the code if needed |
2155 | pCode = activeVersion.GetNativeCode(); |
2156 | if (pCode == NULL) |
2157 | { |
2158 | pCode = pMethodDesc->PrepareCode(activeVersion); |
2159 | } |
2160 | |
2161 | // suspend in preparation for publishing if needed |
2162 | if (fEESuspend) |
2163 | { |
2164 | ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT); |
2165 | } |
2166 | |
2167 | { |
2168 | TableLockHolder lock(this); |
2169 | // The common case is that newActiveCode == activeCode, however we did leave the lock so there is |
2170 | // possibility that the active version has changed. If it has we need to restart the compilation |
2171 | // and publishing process with the new active version instead. |
2172 | // |
2173 | // In theory it should be legitimate to break out of this loop and run the less recent active version, |
2174 | // because ultimately this is a race between one thread that is updating the version and another thread |
2175 | // trying to run the current version. However for back-compat with ReJIT we need to guarantee that |
2176 | // a versioning update at least as late as the profiler JitCompilationFinished callback wins the race. |
2177 | NativeCodeVersion newActiveVersion; |
2178 | if (FAILED(hr = GetActiveILCodeVersion(pMethodDesc).GetOrCreateActiveNativeCodeVersion(pMethodDesc, &newActiveVersion))) |
2179 | { |
2180 | _ASSERTE(hr == E_OUTOFMEMORY); |
2181 | ReportCodePublishError(pMethodDesc->GetModule(), pMethodDesc->GetMemberDef(), pMethodDesc, hr); |
2182 | pCode = NULL; |
2183 | break; |
2184 | } |
2185 | if (newActiveVersion != activeVersion) |
2186 | { |
2187 | activeVersion = newActiveVersion; |
2188 | } |
2189 | else |
2190 | { |
2191 | // if we aren't allowed to backpatch we are done |
2192 | if (!fCanBackpatchPrestub) |
2193 | { |
2194 | break; |
2195 | } |
2196 | |
2197 | // attempt to publish the active version still under the lock |
2198 | if (FAILED(hr = PublishNativeCodeVersion(pMethodDesc, activeVersion, fEESuspend))) |
2199 | { |
2200 | // If we need an EESuspend to publish then start over. We have to leave the lock in order to suspend, |
2201 | // and when we leave the lock the active version might change again. However now we know that suspend is |
2202 | // necessary. |
2203 | if (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED) |
2204 | { |
2205 | _ASSERTE(!fEESuspend); |
2206 | fEESuspend = true; |
2207 | continue; // skip RestartEE() below since SuspendEE() has not been called yet |
2208 | } |
2209 | else |
2210 | { |
2211 | ReportCodePublishError(pMethodDesc->GetModule(), pMethodDesc->GetMemberDef(), pMethodDesc, hr); |
2212 | pCode = NULL; |
2213 | break; |
2214 | } |
2215 | } |
2216 | else |
2217 | { |
2218 | //success |
2219 | break; |
2220 | } |
2221 | } |
2222 | } // exit lock |
2223 | |
2224 | if (fEESuspend) |
2225 | { |
2226 | ThreadSuspend::RestartEE(FALSE, TRUE); |
2227 | } |
2228 | } |
2229 | |
2230 | // if the EE is still suspended from breaking in the middle of the loop, resume it |
2231 | if (fEESuspend) |
2232 | { |
2233 | ThreadSuspend::RestartEE(FALSE, TRUE); |
2234 | } |
2235 | return pCode; |
2236 | } |
2237 | |
2238 | HRESULT CodeVersionManager::PublishNativeCodeVersion(MethodDesc* pMethod, NativeCodeVersion nativeCodeVersion, BOOL fEESuspended) |
2239 | { |
2240 | // TODO: This function needs to make sure it does not change the precode's target if call counting is in progress. Track |
2241 | // whether call counting is currently being done for the method, and use a lock to ensure the expected precode target. |
2242 | LIMITED_METHOD_CONTRACT; |
2243 | _ASSERTE(LockOwnedByCurrentThread()); |
2244 | _ASSERTE(pMethod->IsVersionable()); |
2245 | HRESULT hr = S_OK; |
2246 | PCODE pCode = nativeCodeVersion.IsNull() ? NULL : nativeCodeVersion.GetNativeCode(); |
2247 | if (pMethod->IsVersionableWithPrecode()) |
2248 | { |
2249 | Precode* pPrecode = pMethod->GetOrCreatePrecode(); |
2250 | if (pCode == NULL) |
2251 | { |
2252 | EX_TRY |
2253 | { |
2254 | pPrecode->Reset(); |
2255 | } |
2256 | EX_CATCH_HRESULT(hr); |
2257 | return hr; |
2258 | } |
2259 | else |
2260 | { |
2261 | EX_TRY |
2262 | { |
2263 | pPrecode->SetTargetInterlocked(pCode, FALSE); |
2264 | |
2265 | // SetTargetInterlocked() would return false if it lost the race with another thread. That is fine, this thread |
2266 | // can continue assuming it was successful, similarly to it successfully updating the target and another thread |
2267 | // updating the target again shortly afterwards. |
2268 | hr = S_OK; |
2269 | } |
2270 | EX_CATCH_HRESULT(hr); |
2271 | return hr; |
2272 | } |
2273 | } |
2274 | else |
2275 | { |
2276 | #ifndef FEATURE_JUMPSTAMP |
2277 | _ASSERTE(!"This platform doesn't support JumpStamp but this method doesn't version with Precode," |
2278 | " this method can't be updated" ); |
2279 | return E_FAIL; |
2280 | #else |
2281 | MethodDescVersioningState* pVersioningState; |
2282 | if (FAILED(hr = GetOrCreateMethodDescVersioningState(pMethod, &pVersioningState))) |
2283 | { |
2284 | _ASSERTE(hr == E_OUTOFMEMORY); |
2285 | return hr; |
2286 | } |
2287 | return pVersioningState->SyncJumpStamp(nativeCodeVersion, fEESuspended); |
2288 | #endif |
2289 | } |
2290 | } |
2291 | |
2292 | // static |
2293 | HRESULT CodeVersionManager::EnumerateClosedMethodDescs( |
2294 | MethodDesc* pMD, |
2295 | CDynArray<MethodDesc*> * pClosedMethodDescs, |
2296 | CDynArray<CodePublishError> * pUnsupportedMethodErrors) |
2297 | { |
2298 | CONTRACTL |
2299 | { |
2300 | NOTHROW; |
2301 | GC_TRIGGERS; |
2302 | MODE_PREEMPTIVE; |
2303 | CAN_TAKE_LOCK; |
2304 | PRECONDITION(CheckPointer(pMD, NULL_OK)); |
2305 | PRECONDITION(CheckPointer(pClosedMethodDescs)); |
2306 | PRECONDITION(CheckPointer(pUnsupportedMethodErrors)); |
2307 | } |
2308 | CONTRACTL_END; |
2309 | HRESULT hr = S_OK; |
2310 | if (pMD == NULL) |
2311 | { |
2312 | // nothing is loaded yet so we're done for this method. |
2313 | return S_OK; |
2314 | } |
2315 | |
2316 | if (!pMD->HasClassOrMethodInstantiation()) |
2317 | { |
2318 | // We have a JITted non-generic. |
2319 | MethodDesc ** ppMD = pClosedMethodDescs->Append(); |
2320 | if (ppMD == NULL) |
2321 | { |
2322 | return E_OUTOFMEMORY; |
2323 | } |
2324 | *ppMD = pMD; |
2325 | } |
2326 | |
2327 | if (!pMD->HasClassOrMethodInstantiation()) |
2328 | { |
2329 | // not generic, we're done for this method |
2330 | return S_OK; |
2331 | } |
2332 | |
2333 | // Ok, now the case of a generic function (or function on generic class), which |
2334 | // is loaded, and may thus have compiled instantiations. |
2335 | // It's impossible to get to any other kind of domain from the profiling API |
2336 | Module* pModule = pMD->GetModule(); |
2337 | mdMethodDef methodDef = pMD->GetMemberDef(); |
2338 | BaseDomain * pBaseDomainFromModule = pModule->GetDomain(); |
2339 | _ASSERTE(pBaseDomainFromModule->IsAppDomain() || |
2340 | pBaseDomainFromModule->IsSharedDomain()); |
2341 | |
2342 | if (pBaseDomainFromModule->IsSharedDomain()) |
2343 | { |
2344 | // Iterate through all modules loaded into the shared domain, to |
2345 | // find all instantiations living in the shared domain. This will |
2346 | // include orphaned code (i.e., shared code used by ADs that have |
2347 | // all unloaded), which is good, because orphaned code could get |
2348 | // re-adopted if a new AD is created that can use that shared code |
2349 | hr = EnumerateDomainClosedMethodDescs( |
2350 | NULL, // NULL means to search SharedDomain instead of an AD |
2351 | pModule, |
2352 | methodDef, |
2353 | pClosedMethodDescs, |
2354 | pUnsupportedMethodErrors); |
2355 | } |
2356 | else |
2357 | { |
2358 | // Module is unshared, so just use the module's domain to find instantiations. |
2359 | hr = EnumerateDomainClosedMethodDescs( |
2360 | pBaseDomainFromModule->AsAppDomain(), |
2361 | pModule, |
2362 | methodDef, |
2363 | pClosedMethodDescs, |
2364 | pUnsupportedMethodErrors); |
2365 | } |
2366 | if (FAILED(hr)) |
2367 | { |
2368 | _ASSERTE(hr == E_OUTOFMEMORY); |
2369 | return hr; |
2370 | } |
2371 | |
2372 | // We want to iterate through all compilations of existing instantiations to |
2373 | // ensure they get marked for rejit. Note: There may be zero instantiations, |
2374 | // but we won't know until we try. |
2375 | if (pBaseDomainFromModule->IsSharedDomain()) |
2376 | { |
2377 | // Iterate through all real domains, to find shared instantiations. |
2378 | AppDomainIterator appDomainIterator(TRUE); |
2379 | while (appDomainIterator.Next()) |
2380 | { |
2381 | AppDomain * pAppDomain = appDomainIterator.GetDomain(); |
2382 | hr = EnumerateDomainClosedMethodDescs( |
2383 | pAppDomain, |
2384 | pModule, |
2385 | methodDef, |
2386 | pClosedMethodDescs, |
2387 | pUnsupportedMethodErrors); |
2388 | if (FAILED(hr)) |
2389 | { |
2390 | _ASSERTE(hr == E_OUTOFMEMORY); |
2391 | return hr; |
2392 | } |
2393 | } |
2394 | } |
2395 | return S_OK; |
2396 | } |
2397 | |
2398 | // static |
2399 | HRESULT CodeVersionManager::EnumerateDomainClosedMethodDescs( |
2400 | AppDomain * pAppDomainToSearch, |
2401 | Module* pModuleContainingMethodDef, |
2402 | mdMethodDef methodDef, |
2403 | CDynArray<MethodDesc*> * pClosedMethodDescs, |
2404 | CDynArray<CodePublishError> * pUnsupportedMethodErrors) |
2405 | { |
2406 | CONTRACTL |
2407 | { |
2408 | NOTHROW; |
2409 | GC_NOTRIGGER; |
2410 | MODE_PREEMPTIVE; |
2411 | CAN_TAKE_LOCK; |
2412 | PRECONDITION(CheckPointer(pAppDomainToSearch, NULL_OK)); |
2413 | PRECONDITION(CheckPointer(pModuleContainingMethodDef)); |
2414 | PRECONDITION(CheckPointer(pClosedMethodDescs)); |
2415 | PRECONDITION(CheckPointer(pUnsupportedMethodErrors)); |
2416 | } |
2417 | CONTRACTL_END; |
2418 | |
2419 | _ASSERTE(methodDef != mdTokenNil); |
2420 | |
2421 | HRESULT hr; |
2422 | |
2423 | BaseDomain * pDomainContainingGenericDefinition = pModuleContainingMethodDef->GetDomain(); |
2424 | |
2425 | #ifdef _DEBUG |
2426 | // If the generic definition is not loaded domain-neutral, then all its |
2427 | // instantiations will also be non-domain-neutral and loaded into the same |
2428 | // domain as the generic definition. So the caller may only pass the |
2429 | // domain containing the generic definition as pAppDomainToSearch |
2430 | if (!pDomainContainingGenericDefinition->IsSharedDomain()) |
2431 | { |
2432 | _ASSERTE(pDomainContainingGenericDefinition == pAppDomainToSearch); |
2433 | } |
2434 | #endif //_DEBUG |
2435 | |
2436 | // these are the default flags which won't actually be used in shared mode other than |
2437 | // asserting they were specified with their default values |
2438 | AssemblyIterationFlags assemFlags = (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution); |
2439 | ModuleIterationOption moduleFlags = (ModuleIterationOption)kModIterIncludeLoaded; |
2440 | if (pAppDomainToSearch != NULL) |
2441 | { |
2442 | assemFlags = (AssemblyIterationFlags)(kIncludeAvailableToProfilers | kIncludeExecution); |
2443 | moduleFlags = (ModuleIterationOption)kModIterIncludeAvailableToProfilers; |
2444 | } |
2445 | LoadedMethodDescIterator it( |
2446 | pAppDomainToSearch, |
2447 | pModuleContainingMethodDef, |
2448 | methodDef, |
2449 | assemFlags, |
2450 | moduleFlags); |
2451 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
2452 | while (it.Next(pDomainAssembly.This())) |
2453 | { |
2454 | MethodDesc * pLoadedMD = it.Current(); |
2455 | |
2456 | if (!pLoadedMD->IsVersionable()) |
2457 | { |
2458 | // For compatibility with the rejit APIs we ensure certain errors are detected and reported using their |
2459 | // original HRESULTS |
2460 | HRESULT errorHR = GetNonVersionableError(pLoadedMD); |
2461 | if (FAILED(errorHR)) |
2462 | { |
2463 | if (FAILED(hr = CodeVersionManager::AddCodePublishError(pModuleContainingMethodDef, methodDef, pLoadedMD, CORPROF_E_FUNCTION_IS_COLLECTIBLE, pUnsupportedMethodErrors))) |
2464 | { |
2465 | _ASSERTE(hr == E_OUTOFMEMORY); |
2466 | return hr; |
2467 | } |
2468 | } |
2469 | continue; |
2470 | } |
2471 | |
2472 | #ifdef _DEBUG |
2473 | if (!pDomainContainingGenericDefinition->IsSharedDomain()) |
2474 | { |
2475 | // Method is defined outside of the shared domain, so its instantiation must |
2476 | // be defined in the AD we're iterating over (pAppDomainToSearch, which, as |
2477 | // asserted above, must be the same domain as the generic's definition) |
2478 | _ASSERTE(pLoadedMD->GetDomain() == pAppDomainToSearch); |
2479 | } |
2480 | #endif // _DEBUG |
2481 | |
2482 | MethodDesc ** ppMD = pClosedMethodDescs->Append(); |
2483 | if (ppMD == NULL) |
2484 | { |
2485 | return E_OUTOFMEMORY; |
2486 | } |
2487 | *ppMD = pLoadedMD; |
2488 | } |
2489 | return S_OK; |
2490 | } |
2491 | #endif // DACCESS_COMPILE |
2492 | |
2493 | |
2494 | //--------------------------------------------------------------------------------------- |
2495 | // |
2496 | // Given the default version code for a MethodDesc that is about to published, add |
2497 | // a jumpstamp pointing back to the prestub if the currently active version isn't |
2498 | // the default one. This called from the PublishMethodHolder. |
2499 | // |
2500 | // Arguments: |
2501 | // * pMD - MethodDesc to jmp-stamp |
2502 | // * pCode - Top of the code that was just jitted (using original IL). |
2503 | // |
2504 | // |
2505 | // Return value: |
2506 | // * S_OK: Either we successfully did the jmp-stamp, or we didn't have to |
2507 | // * Else, HRESULT indicating failure. |
2508 | |
2509 | // Assumptions: |
2510 | // The caller has not yet published pCode to the MethodDesc, so no threads can be |
2511 | // executing inside pMD's code yet. Thus, we don't need to suspend the runtime while |
2512 | // applying the jump-stamp like we usually do for rejit requests that are made after |
2513 | // a function has been JITted. |
2514 | // |
2515 | #ifndef DACCESS_COMPILE |
2516 | HRESULT CodeVersionManager::DoJumpStampIfNecessary(MethodDesc* pMD, PCODE pCode) |
2517 | { |
2518 | CONTRACTL |
2519 | { |
2520 | NOTHROW; |
2521 | GC_NOTRIGGER; |
2522 | MODE_ANY; |
2523 | CAN_TAKE_LOCK; |
2524 | PRECONDITION(CheckPointer(pMD)); |
2525 | PRECONDITION(pCode != NULL); |
2526 | } |
2527 | CONTRACTL_END; |
2528 | |
2529 | _ASSERTE(LockOwnedByCurrentThread()); |
2530 | |
2531 | NativeCodeVersion activeCodeVersion = GetActiveILCodeVersion(pMD).GetActiveNativeCodeVersion(pMD); |
2532 | if (activeCodeVersion.IsDefaultVersion()) |
2533 | { |
2534 | //Method not requested to be rejitted, nothing to do |
2535 | return S_OK; |
2536 | } |
2537 | |
2538 | if (!(pMD->IsVersionable() && pMD->IsVersionableWithJumpStamp())) |
2539 | { |
2540 | return GetNonVersionableError(pMD); |
2541 | } |
2542 | |
2543 | #ifndef FEATURE_JUMPSTAMP |
2544 | _ASSERTE(!"How did we get here? IsVersionableWithJumpStamp() should have been FALSE above" ); |
2545 | return S_OK; |
2546 | #else |
2547 | HRESULT hr; |
2548 | MethodDescVersioningState* pVersioningState; |
2549 | if (FAILED(hr = GetOrCreateMethodDescVersioningState(pMD, &pVersioningState))) |
2550 | { |
2551 | _ASSERTE(hr == E_OUTOFMEMORY); |
2552 | return hr; |
2553 | } |
2554 | if (pVersioningState->GetJumpStampState() != MethodDescVersioningState::JumpStampNone) |
2555 | { |
2556 | //JumpStamp already in place |
2557 | return S_OK; |
2558 | } |
2559 | return pVersioningState->JumpStampNativeCode(pCode); |
2560 | #endif // FEATURE_JUMPSTAMP |
2561 | |
2562 | } |
2563 | #endif // DACCESS_COMPILE |
2564 | |
2565 | #ifndef DACCESS_COMPILE |
2566 | //static |
2567 | void CodeVersionManager::OnAppDomainExit(AppDomain * pAppDomain) |
2568 | { |
2569 | LIMITED_METHOD_CONTRACT; |
2570 | // This would clean up all the allocations we have done and synchronize with any threads that might |
2571 | // still be using the data |
2572 | _ASSERTE(!".Net Core shouldn't be doing app domain shutdown - if we start doing so this needs to be implemented" ); |
2573 | } |
2574 | #endif |
2575 | |
2576 | //--------------------------------------------------------------------------------------- |
2577 | // |
2578 | // Small helper to determine whether a given (possibly instantiated generic) MethodDesc |
2579 | // is safe to rejit. |
2580 | // |
2581 | // Arguments: |
2582 | // pMD - MethodDesc to test |
2583 | // Return Value: |
2584 | // S_OK iff pMD is safe to rejit |
2585 | // CORPROF_E_FUNCTION_IS_COLLECTIBLE - function can't be rejitted because it is collectible |
2586 | // |
2587 | |
2588 | // static |
2589 | #ifndef DACCESS_COMPILE |
2590 | HRESULT CodeVersionManager::GetNonVersionableError(MethodDesc* pMD) |
2591 | { |
2592 | CONTRACTL |
2593 | { |
2594 | NOTHROW; |
2595 | GC_NOTRIGGER; |
2596 | CAN_TAKE_LOCK; |
2597 | MODE_ANY; |
2598 | } |
2599 | CONTRACTL_END; |
2600 | |
2601 | _ASSERTE(pMD != NULL); |
2602 | |
2603 | // Weird, non-user functions were already weeded out in RequestReJIT(), and will |
2604 | // also never be passed to us by the prestub worker (for the pre-rejit case). |
2605 | _ASSERTE(pMD->IsIL()); |
2606 | |
2607 | // Any MethodDescs that could be collected are not currently supported. Although we |
2608 | // rule out all Ref.Emit modules in RequestReJIT(), there can still exist types defined |
2609 | // in a non-reflection module and instantiated into a collectible assembly |
2610 | // (e.g., List<MyCollectibleStruct>). In the future we may lift this |
2611 | // restriction by updating the ReJitManager when the collectible assemblies |
2612 | // owning the instantiations get collected. |
2613 | if (pMD->GetLoaderAllocator()->IsCollectible()) |
2614 | { |
2615 | return CORPROF_E_FUNCTION_IS_COLLECTIBLE; |
2616 | } |
2617 | |
2618 | return S_OK; |
2619 | } |
2620 | #endif |
2621 | |
2622 | //--------------------------------------------------------------------------------------- |
2623 | // |
2624 | // Helper that inits a new CodePublishError and adds it to the pErrors array |
2625 | // |
2626 | // Arguments: |
2627 | // * pModule - The module in the module/MethodDef identifier pair for the method which |
2628 | // had an error during rejit |
2629 | // * methodDef - The MethodDef in the module/MethodDef identifier pair for the method which |
2630 | // had an error during rejit |
2631 | // * pMD - If available, the specific method instance which had an error during rejit |
2632 | // * hrStatus - HRESULT for the rejit error that occurred |
2633 | // * pErrors - the list of error records that this method will append to |
2634 | // |
2635 | // Return Value: |
2636 | // * S_OK: error was appended |
2637 | // * E_OUTOFMEMORY: Not enough memory to create the new error item. The array is unchanged. |
2638 | // |
2639 | |
2640 | //static |
2641 | #ifndef DACCESS_COMPILE |
2642 | HRESULT CodeVersionManager::AddCodePublishError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus, CDynArray<CodePublishError> * pErrors) |
2643 | { |
2644 | CONTRACTL |
2645 | { |
2646 | NOTHROW; |
2647 | GC_NOTRIGGER; |
2648 | MODE_ANY; |
2649 | } |
2650 | CONTRACTL_END; |
2651 | |
2652 | if (pErrors == NULL) |
2653 | { |
2654 | return S_OK; |
2655 | } |
2656 | |
2657 | CodePublishError* pError = pErrors->Append(); |
2658 | if (pError == NULL) |
2659 | { |
2660 | return E_OUTOFMEMORY; |
2661 | } |
2662 | pError->pModule = pModule; |
2663 | pError->methodDef = methodDef; |
2664 | pError->pMethodDesc = pMD; |
2665 | pError->hrStatus = hrStatus; |
2666 | return S_OK; |
2667 | } |
2668 | #endif |
2669 | |
2670 | #ifndef DACCESS_COMPILE |
2671 | void CodeVersionManager::ReportCodePublishError(CodePublishError* pErrorRecord) |
2672 | { |
2673 | CONTRACTL |
2674 | { |
2675 | NOTHROW; |
2676 | GC_TRIGGERS; |
2677 | CAN_TAKE_LOCK; |
2678 | MODE_ANY; |
2679 | } |
2680 | CONTRACTL_END; |
2681 | |
2682 | ReportCodePublishError(pErrorRecord->pModule, pErrorRecord->methodDef, pErrorRecord->pMethodDesc, pErrorRecord->hrStatus); |
2683 | } |
2684 | |
2685 | void CodeVersionManager::ReportCodePublishError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus) |
2686 | { |
2687 | CONTRACTL |
2688 | { |
2689 | NOTHROW; |
2690 | GC_TRIGGERS; |
2691 | CAN_TAKE_LOCK; |
2692 | MODE_ANY; |
2693 | } |
2694 | CONTRACTL_END; |
2695 | |
2696 | #ifdef FEATURE_REJIT |
2697 | BOOL isRejitted = FALSE; |
2698 | { |
2699 | TableLockHolder(this); |
2700 | isRejitted = !GetActiveILCodeVersion(pModule, methodDef).IsDefaultVersion(); |
2701 | } |
2702 | |
2703 | // this isn't perfect, we might be activating a tiered jitting variation of a rejitted |
2704 | // method for example. If it proves to be an issue we can revisit. |
2705 | if (isRejitted) |
2706 | { |
2707 | ReJitManager::ReportReJITError(pModule, methodDef, pMD, hrStatus); |
2708 | } |
2709 | #endif |
2710 | } |
2711 | #endif // DACCESS_COMPILE |
2712 | |
2713 | //--------------------------------------------------------------------------------------- |
2714 | // |
2715 | // PrepareCodeConfig::SetNativeCode() calls this to determine if there's a non-default code |
2716 | // version requested for a MethodDesc that has just been jitted for the first time. |
2717 | // This is also called when methods are being restored in NGEN images. The sequence looks like: |
2718 | // *Enter holder |
2719 | // Enter code version manager lock |
2720 | // DoJumpStampIfNecessary |
2721 | // *Runtime code publishes/restores method |
2722 | // *Exit holder |
2723 | // Leave code version manager lock |
2724 | // Send rejit error callbacks if needed |
2725 | // |
2726 | // |
2727 | // #PublishCode: |
2728 | // Note that the runtime needs to publish/restore the PCODE while this holder is |
2729 | // on the stack, so it can happen under the code version manager's lock. |
2730 | // This prevents a race with a profiler that calls |
2731 | // RequestReJIT just as the method finishes compiling. In particular, the locking ensures |
2732 | // atomicity between this set of steps (performed in DoJumpStampIfNecessary): |
2733 | // * (1) Checking whether there is a non-default version for this MD |
2734 | // * (2) If not, skip doing the jmp-stamp |
2735 | // * (3) Publishing the PCODE |
2736 | // |
2737 | // with respect to these steps performed in RequestReJIT: |
2738 | // * (a) Is PCODE published yet? |
2739 | // * (b) Create non-default ILCodeVersion which the prestub will |
2740 | // consult when it JITs the original IL |
2741 | // |
2742 | // Without this atomicity, we could get the ordering (1), (2), (a), (b), (3), resulting |
2743 | // in the rejit request getting completely ignored (i.e., we file away the new ILCodeVersion |
2744 | // AFTER the prestub checks for it). |
2745 | // |
2746 | // A similar race is possible for code being restored. In that case the restoring thread |
2747 | // does: |
2748 | // * (1) Check if there is a non-default ILCodeVersion for this MD |
2749 | // * (2) If not, no need to jmp-stamp |
2750 | // * (3) Restore the MD |
2751 | |
2752 | // And RequestRejit does: |
2753 | // * (a) [In LoadedMethodDescIterator] Is a potential MD restored yet? |
2754 | // * (b) [In EnumerateDomainClosedMethodDescs] If not, don't queue it for jump-stamping |
2755 | // |
2756 | // Same ordering (1), (2), (a), (b), (3) results in missing both opportunities to jump |
2757 | // stamp. |
2758 | |
2759 | #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) |
2760 | PublishMethodHolder::PublishMethodHolder(MethodDesc* pMethodDesc, PCODE pCode) : |
2761 | m_pMD(NULL), m_hr(S_OK) |
2762 | { |
2763 | // This method can't have a contract because entering the table lock |
2764 | // below increments GCNoTrigger count. Contracts always revert these changes |
2765 | // at the end of the method but we need the incremented count to flow out of the |
2766 | // method. The balancing decrement occurs in the destructor. |
2767 | STATIC_CONTRACT_NOTHROW; |
2768 | STATIC_CONTRACT_GC_NOTRIGGER; |
2769 | STATIC_CONTRACT_CAN_TAKE_LOCK; |
2770 | STATIC_CONTRACT_MODE_ANY; |
2771 | |
2772 | // We come here from the PreStub and from MethodDesc::CheckRestore |
2773 | // The method should be effectively restored, but we haven't yet |
2774 | // cleared the unrestored bit so we can't assert pMethodDesc->IsRestored() |
2775 | // We can assert: |
2776 | _ASSERTE(pMethodDesc->GetMethodTable()->IsRestored()); |
2777 | |
2778 | if (pCode != NULL) |
2779 | { |
2780 | m_pMD = pMethodDesc; |
2781 | CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager(); |
2782 | pCodeVersionManager->EnterLock(); |
2783 | m_hr = pCodeVersionManager->DoJumpStampIfNecessary(pMethodDesc, pCode); |
2784 | } |
2785 | } |
2786 | |
2787 | |
2788 | PublishMethodHolder::~PublishMethodHolder() |
2789 | { |
2790 | // This method can't have a contract because leaving the table lock |
2791 | // below decrements GCNoTrigger count. Contracts always revert these changes |
2792 | // at the end of the method but we need the decremented count to flow out of the |
2793 | // method. The balancing increment occurred in the constructor. |
2794 | STATIC_CONTRACT_NOTHROW; |
2795 | STATIC_CONTRACT_GC_TRIGGERS; // NOTRIGGER until we leave the lock |
2796 | STATIC_CONTRACT_CAN_TAKE_LOCK; |
2797 | STATIC_CONTRACT_MODE_ANY; |
2798 | |
2799 | if (m_pMD) |
2800 | { |
2801 | CodeVersionManager* pCodeVersionManager = m_pMD->GetCodeVersionManager(); |
2802 | pCodeVersionManager->LeaveLock(); |
2803 | if (FAILED(m_hr)) |
2804 | { |
2805 | pCodeVersionManager->ReportCodePublishError(m_pMD->GetModule(), m_pMD->GetMemberDef(), m_pMD, m_hr); |
2806 | } |
2807 | } |
2808 | } |
2809 | |
2810 | PublishMethodTableHolder::PublishMethodTableHolder(MethodTable* pMethodTable) : |
2811 | m_pMethodTable(NULL) |
2812 | { |
2813 | // This method can't have a contract because entering the table lock |
2814 | // below increments GCNoTrigger count. Contracts always revert these changes |
2815 | // at the end of the method but we need the incremented count to flow out of the |
2816 | // method. The balancing decrement occurs in the destructor. |
2817 | STATIC_CONTRACT_NOTHROW; |
2818 | STATIC_CONTRACT_GC_NOTRIGGER; |
2819 | STATIC_CONTRACT_CAN_TAKE_LOCK; |
2820 | STATIC_CONTRACT_MODE_ANY; |
2821 | |
2822 | // We come here from MethodTable::SetIsRestored |
2823 | // The method table should be effectively restored, but we haven't yet |
2824 | // cleared the unrestored bit so we can't assert pMethodTable->IsRestored() |
2825 | |
2826 | m_pMethodTable = pMethodTable; |
2827 | CodeVersionManager* pCodeVersionManager = pMethodTable->GetModule()->GetCodeVersionManager(); |
2828 | pCodeVersionManager->EnterLock(); |
2829 | MethodTable::IntroducedMethodIterator itMethods(pMethodTable, FALSE); |
2830 | for (; itMethods.IsValid(); itMethods.Next()) |
2831 | { |
2832 | // Although the MethodTable is restored, the methods might not be. |
2833 | // We need to be careful to only query portions of the MethodDesc |
2834 | // that work in a partially restored state. The only methods that need |
2835 | // further restoration are IL stubs (which aren't rejittable) and |
2836 | // generic methods. The only generic methods directly accessible from |
2837 | // the MethodTable are definitions. GetNativeCode() on generic defs |
2838 | // will run succesfully and return NULL which short circuits the |
2839 | // rest of the logic. |
2840 | MethodDesc * pMD = itMethods.GetMethodDesc(); |
2841 | PCODE pCode = pMD->GetNativeCode(); |
2842 | if (pCode != NULL) |
2843 | { |
2844 | HRESULT hr = pCodeVersionManager->DoJumpStampIfNecessary(pMD, pCode); |
2845 | if (FAILED(hr)) |
2846 | { |
2847 | CodeVersionManager::AddCodePublishError(pMD->GetModule(), pMD->GetMemberDef(), pMD, hr, &m_errors); |
2848 | } |
2849 | } |
2850 | } |
2851 | } |
2852 | |
2853 | |
2854 | PublishMethodTableHolder::~PublishMethodTableHolder() |
2855 | { |
2856 | // This method can't have a contract because leaving the table lock |
2857 | // below decrements GCNoTrigger count. Contracts always revert these changes |
2858 | // at the end of the method but we need the decremented count to flow out of the |
2859 | // method. The balancing increment occurred in the constructor. |
2860 | STATIC_CONTRACT_NOTHROW; |
2861 | STATIC_CONTRACT_GC_TRIGGERS; // NOTRIGGER until we leave the lock |
2862 | STATIC_CONTRACT_CAN_TAKE_LOCK; |
2863 | STATIC_CONTRACT_MODE_ANY; |
2864 | |
2865 | if (m_pMethodTable) |
2866 | { |
2867 | CodeVersionManager* pCodeVersionManager = m_pMethodTable->GetModule()->GetCodeVersionManager(); |
2868 | pCodeVersionManager->LeaveLock(); |
2869 | for (int i = 0; i < m_errors.Count(); i++) |
2870 | { |
2871 | pCodeVersionManager->ReportCodePublishError(&(m_errors[i])); |
2872 | } |
2873 | } |
2874 | } |
2875 | #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) |
2876 | |
2877 | #endif // FEATURE_CODE_VERSIONING |
2878 | |
2879 | |