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: MultiCoreJITPlayer.cpp
6//
7
8// ===========================================================================
9// This file contains the implementation for MultiCore JIT profile playing back
10// ===========================================================================
11//
12
13#include "common.h"
14#include "vars.hpp"
15#include "eeconfig.h"
16#include "dllimport.h"
17#include "comdelegate.h"
18#include "dbginterface.h"
19#include "stubgen.h"
20#include "eventtrace.h"
21#include "array.h"
22#include "fstream.h"
23#include "hash.h"
24#include "clrex.h"
25
26#include "appdomain.hpp"
27
28#include "multicorejit.h"
29#include "multicorejitimpl.h"
30
31// Options for controlling multicore JIT
32
33unsigned g_MulticoreJitDelay = 0; // Delay in StartProfile
34
35bool g_MulticoreJitEnabled = true; // Enable/Disable feature
36
37///////////////////////////////////////////////////////////////////////////////////
38//
39// class MulticoreJitCodeStorage
40//
41///////////////////////////////////////////////////////////////////////////////////
42
43
44void MulticoreJitCodeStorage::Init()
45{
46 CONTRACTL
47 {
48 THROWS;
49 MODE_ANY; // called from BaseDomain::Init which is MODE_ANY
50 }
51 CONTRACTL_END;
52
53 m_nStored = 0;
54 m_nReturned = 0;
55 m_crstCodeMap.Init(CrstMulticoreJitHash);
56}
57
58
59// Destructor
60MulticoreJitCodeStorage::~MulticoreJitCodeStorage()
61{
62 LIMITED_METHOD_CONTRACT;
63
64 m_crstCodeMap.Destroy();
65}
66
67
68// Callback from MakeJitWorker to store compiled code, under MethodDesc lock
69void MulticoreJitCodeStorage::StoreMethodCode(MethodDesc * pMD, PCODE pCode)
70{
71 STANDARD_VM_CONTRACT;
72
73#ifdef PROFILING_SUPPORTED
74 if (CORProfilerTrackJITInfo())
75 {
76 return;
77 }
78#endif
79
80 if (pCode != NULL)
81 {
82 CrstHolder holder(& m_crstCodeMap);
83
84#ifdef MULTICOREJIT_LOGGING
85 if (Logging2On(LF2_MULTICOREJIT, LL_INFO1000))
86 {
87 MulticoreJitTrace(("%p %p StoredMethodCode", pMD, pCode));
88 }
89#endif
90
91 PCODE code = NULL;
92
93 if (! m_nativeCodeMap.Lookup(pMD, & code))
94 {
95 m_nativeCodeMap.Add(pMD, pCode);
96
97 m_nStored ++;
98 }
99 }
100}
101
102
103// Query from MakeJitWorker: Lookup stored JITted methods
104PCODE MulticoreJitCodeStorage::QueryMethodCode(MethodDesc * pMethod, BOOL shouldRemoveCode)
105{
106 STANDARD_VM_CONTRACT;
107
108 PCODE code = NULL;
109
110 if (m_nStored > m_nReturned) // Quick check before taking lock
111 {
112 CrstHolder holder(& m_crstCodeMap);
113
114 if (m_nativeCodeMap.Lookup(pMethod, & code) && shouldRemoveCode)
115 {
116 m_nReturned ++;
117
118 // Remove it to keep storage small (hopefully flat)
119 m_nativeCodeMap.Remove(pMethod);
120 }
121 }
122
123#ifdef MULTICOREJIT_LOGGING
124 if (Logging2On(LF2_MULTICOREJIT, LL_INFO1000))
125 {
126 MulticoreJitTrace(("%p %p QueryMethodCode", pMethod, code));
127 }
128#endif
129
130 return code;
131}
132
133
134///////////////////////////////////////////////////////////////////////////////////
135//
136// class PlayerModuleInfo
137//
138///////////////////////////////////////////////////////////////////////////////////
139
140// Per module information kept for mapping to Module object
141
142class PlayerModuleInfo
143{
144public:
145
146 const ModuleRecord * m_pRecord;
147 Module * m_pModule;
148 int m_needLevel;
149 int m_curLevel;
150 bool m_enableJit;
151
152 PlayerModuleInfo()
153 {
154 LIMITED_METHOD_CONTRACT;
155
156 m_pRecord = NULL;
157 m_pModule = NULL;
158 m_needLevel = -1;
159 m_curLevel = -1;
160 m_enableJit = true;
161 }
162
163 bool MeetLevel(FileLoadLevel level) const
164 {
165 LIMITED_METHOD_CONTRACT;
166
167 return (m_pModule != NULL) && (m_curLevel >= (int) level);
168 }
169
170 bool IsModuleLoaded() const
171 {
172 LIMITED_METHOD_CONTRACT;
173
174 return m_pModule != NULL;
175 }
176
177 bool LoadOkay() const
178 {
179 LIMITED_METHOD_CONTRACT;
180
181 return (m_pRecord->flags & FLAG_LOADOKAY) != 0;
182 }
183
184 // UpdateNeedLevel called
185 bool IsDependency() const
186 {
187 LIMITED_METHOD_CONTRACT;
188
189 return m_needLevel > -1;
190 }
191
192 bool IsLowerLevel() const
193 {
194 LIMITED_METHOD_CONTRACT;
195
196 return m_curLevel < m_needLevel;
197 }
198
199 // If module is loaded, lower then needed level, update its level
200 void UpdateCurrentLevel()
201 {
202 CONTRACTL
203 {
204 NOTHROW;
205 GC_NOTRIGGER;
206 }
207 CONTRACTL_END;
208
209 if (m_pModule != NULL)
210 {
211 if (m_curLevel < m_needLevel)
212 {
213 m_curLevel = (int) MulticoreJitManager::GetModuleFileLoadLevel(m_pModule);
214 }
215 }
216 }
217
218 bool UpdateNeedLevel(FileLoadLevel level)
219 {
220 LIMITED_METHOD_CONTRACT;
221
222 if (m_needLevel < (int) level)
223 {
224 m_needLevel = (int) level;
225
226 return true;
227 }
228
229 return false;
230 }
231
232 bool MatchWith(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shortAbort, bool fAppx);
233
234#ifdef MULTICOREJIT_LOGGING
235 void Dump(const wchar_t * prefix, int index);
236#endif
237
238};
239
240
241bool PlayerModuleInfo::MatchWith(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shortAbort, bool fAppx)
242{
243 STANDARD_VM_CONTRACT;
244
245 if ((m_pModule == NULL) && m_pRecord->MatchWithModule(version, gotVersion, pModule, shortAbort, fAppx))
246 {
247 m_pModule = pModule;
248 m_curLevel = (int) MulticoreJitManager::GetModuleFileLoadLevel(pModule);
249
250 if (m_pRecord->jitMethodCount == 0)
251 {
252 m_enableJit = false; // No method to JIT for this module, not really needed; just to be correct
253 }
254 else if (CORDebuggerEnCMode(pModule->GetDebuggerInfoBits()))
255 {
256 m_enableJit = false;
257 MulticoreJitTrace(("Jit disable for module due to EnC"));
258 _FireEtwMulticoreJit(W("FILTERMETHOD-EnC"), W(""), 0, 0, 0);
259 }
260
261 return true;
262 }
263
264 return false;
265}
266
267
268#ifdef MULTICOREJIT_LOGGING
269
270void PlayerModuleInfo::Dump(const wchar_t * prefix, int index)
271{
272 WRAPPER_NO_CONTRACT;
273
274#ifdef LOGGING
275 if (!Logging2On(LF2_MULTICOREJIT, LL_INFO100))
276 return;
277
278 DEBUG_ONLY_FUNCTION;
279#endif
280
281 StackSString ssBuff;
282
283 ssBuff.Append(prefix);
284 ssBuff.AppendPrintf(W("[%2d]: "), index);
285
286 const ModuleVersion & ver = m_pRecord->version;
287
288 ssBuff.AppendPrintf(W(" %d.%d.%05d.%04d.%d level %2d, need %2d"), ver.major, ver.minor, ver.build, ver.revision, ver.versionFlags, m_curLevel, m_needLevel);
289
290 ssBuff.AppendPrintf(W(" pModule: %p "), m_pModule);
291
292 unsigned i;
293
294 for (i = 0; i < m_pRecord->ModuleNameLen(); i ++)
295 {
296 ssBuff.Append((wchar_t) m_pRecord->GetModuleName()[i]);
297 }
298
299 while (i < 32)
300 {
301 ssBuff.Append(' ');
302 i ++;
303 }
304
305 MulticoreJitTrace(("%S", ssBuff.GetUnicode()));
306}
307
308#endif
309
310
311
312///////////////////////////////////////////////////////////////////////////////////
313//
314// MulticoreJitProfilePlayer
315//
316///////////////////////////////////////////////////////////////////////////////////
317
318const unsigned EmptyToken = 0xFFFFFFFF;
319
320bool ModuleRecord::MatchWithModule(ModuleVersion & modVersion, bool & gotVersion, Module * pModule, bool & shouldAbort, bool fAppx) const
321{
322 STANDARD_VM_CONTRACT;
323
324 LPCUTF8 pModuleName = pModule->GetSimpleName();
325 const char * pName = GetModuleName();
326
327 size_t len = strlen(pModuleName);
328
329 if ((len == lenModuleName) && (memcmp(pModuleName, pName, lenModuleName) == 0))
330 {
331 // Ignore version check on play back when running under Appx (also GetModuleVersion is expensive)
332
333 // For Appx, multicore JIT profile is pre-generated by application vendor, installed together with the package.
334 // So it may not have exact match with its own assemblies, and assemblies installed on the system.
335 if (fAppx)
336 {
337 return true;
338 }
339
340 if (! gotVersion) // Calling expensive GetModuleVersion only when simple name matches
341 {
342 gotVersion = true;
343
344 if (! modVersion.GetModuleVersion(pModule))
345 {
346 return false;
347 }
348 }
349
350 if (version.MatchWith(modVersion))
351 {
352 // If matching image with different native image flag is detected, mark and abort playing profile back
353 if (version.NativeImageFlagDiff(modVersion))
354 {
355 MulticoreJitTrace((" Module with different native image flag: %s", pName));
356
357 shouldAbort = true;
358 }
359
360 return true;
361 }
362 }
363
364 return false;
365}
366
367
368MulticoreJitProfilePlayer::MulticoreJitProfilePlayer(AppDomain * pDomain, ICLRPrivBinder * pBinderContext, LONG nSession, bool fAppxMode)
369 : m_stats(pDomain->GetMulticoreJitManager().GetStats()), m_appdomainSession(pDomain->GetMulticoreJitManager().GetProfileSession())
370{
371 LIMITED_METHOD_CONTRACT;
372
373 m_DomainID = pDomain->GetId();
374 m_pBinderContext = pBinderContext;
375 m_nMySession = nSession;
376 m_moduleCount = 0;
377 m_headerModuleCount = 0;
378 m_pModules = NULL;
379 m_nBlockingCount = 0;
380 m_nMissingModule = 0;
381 m_nLoadedModuleCount = 0;
382 m_shouldAbort = false;
383 m_fAppxMode = fAppxMode;
384
385 m_pThread = NULL;
386 m_pFileBuffer = NULL;
387 m_nFileSize = 0;
388
389 m_busyWith = EmptyToken;
390
391 m_nStartTime = GetTickCount();
392}
393
394
395MulticoreJitProfilePlayer::~MulticoreJitProfilePlayer()
396{
397 LIMITED_METHOD_CONTRACT;
398
399 if (m_pModules != NULL)
400 {
401 delete [] m_pModules;
402 m_pModules = NULL;
403 }
404
405 if (m_pFileBuffer != NULL)
406 {
407 delete [] m_pFileBuffer;
408 }
409}
410
411
412// static
413bool MulticoreJitManager::ModuleHasNoCode(Module * pModule)
414{
415 LIMITED_METHOD_CONTRACT;
416
417 if (pModule->IsResource())
418 {
419 return true;
420 }
421
422 IMDInternalImport * pImport = pModule->GetMDImport();
423
424 if (pImport != NULL)
425 {
426 if ((pImport->GetCountWithTokenKind(mdtTypeDef) == 0) &&
427 (pImport->GetCountWithTokenKind(mdtMethodDef) == 0) &&
428 (pImport->GetCountWithTokenKind(mdtFieldDef) == 0)
429 )
430 {
431 return true;
432 }
433 }
434
435 return false;
436}
437
438
439// We only support default load context, non dynamic module, non domain neutral (needed for dependency)
440bool MulticoreJitManager::IsSupportedModule(Module * pModule, bool fMethodJit, bool fAppx)
441{
442 CONTRACTL
443 {
444 NOTHROW;
445 GC_NOTRIGGER;
446 MODE_ANY;
447 }
448 CONTRACTL_END;
449
450 if (pModule == NULL)
451 {
452 return false;
453 }
454
455 PEFile * pFile = pModule->GetFile();
456
457 // dynamic module.
458 if (pFile->IsDynamic()) // Ignore dynamic modules
459 {
460 return false;
461 }
462
463 if (pFile->GetPath().IsEmpty()) // Ignore in-memory modules
464 {
465 return false;
466 }
467
468
469 if (! fMethodJit)
470 {
471 if (ModuleHasNoCode(pModule))
472 {
473 return false;
474 }
475 }
476
477 Assembly * pAssembly = pModule->GetAssembly();
478
479
480 return true;
481
482}
483
484
485// ModuleRecord handling: add to m_ModuleList
486
487HRESULT MulticoreJitProfilePlayer::HandleModuleRecord(const ModuleRecord * pMod)
488{
489 STANDARD_VM_CONTRACT;
490
491 HRESULT hr = S_OK;
492
493 PlayerModuleInfo & info = m_pModules[m_moduleCount];
494
495 info.m_pModule = NULL;
496 info.m_pRecord = pMod;
497
498#ifdef MULTICOREJIT_LOGGING
499 info.Dump(W("ModuleRecord"), m_moduleCount);
500#endif
501
502 m_moduleCount ++;
503
504 return hr;
505}
506
507
508#ifndef DACCESS_COMPILE
509class MulticoreJitPrepareCodeConfig : public PrepareCodeConfig
510{
511public:
512 MulticoreJitPrepareCodeConfig(MethodDesc* pMethod) :
513 PrepareCodeConfig(NativeCodeVersion(pMethod), FALSE, FALSE)
514 {}
515
516 virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse)
517 {
518 MulticoreJitManager & mcJitManager = GetAppDomain()->GetMulticoreJitManager();
519 mcJitManager.GetMulticoreJitCodeStorage().StoreMethodCode(GetMethodDesc(), pCode);
520 return TRUE;
521 }
522};
523#endif
524
525// Call JIT to compile a method
526
527bool MulticoreJitProfilePlayer::CompileMethodDesc(Module * pModule, MethodDesc * pMD)
528{
529 STANDARD_VM_CONTRACT;
530
531 COR_ILMETHOD_DECODER::DecoderStatus status;
532
533 COR_ILMETHOD_DECODER header(pMD->GetILHeader(), pModule->GetMDImport(), & status);
534
535 if (status == COR_ILMETHOD_DECODER::SUCCESS)
536 {
537 if (m_stats.m_nTryCompiling == 0)
538 {
539 MulticoreJitTrace(("First call to MakeJitWorker"));
540 }
541
542 m_stats.m_nTryCompiling ++;
543
544 // Reset the flag to allow managed code to be called in multicore JIT background thread from this routine
545 ThreadStateNCStackHolder holder(-1, Thread::TSNC_CallingManagedCodeDisabled);
546
547 // PrepareCode calls back to MulticoreJitCodeStorage::StoreMethodCode under MethodDesc lock
548 MulticoreJitPrepareCodeConfig config(pMD);
549 pMD->PrepareCode(&config);
550
551 return true;
552 }
553
554 return false;
555}
556
557
558// Conditional JIT of a method
559void MulticoreJitProfilePlayer::JITMethod(Module * pModule, unsigned methodIndex)
560{
561 STANDARD_VM_CONTRACT;
562
563 // Ensure non-null module
564 if (pModule == NULL)
565 {
566 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, TRACE_LEVEL_VERBOSE, CLR_PRIVATEMULTICOREJIT_KEYWORD))
567 {
568 _FireEtwMulticoreJitA(W("NULLMODULEPOINTER"), NULL, methodIndex, 0, 0);
569 }
570 return;
571 }
572
573 methodIndex &= METHODINDEX_MASK; // 20-bit
574
575 unsigned token = TokenFromRid(methodIndex, mdtMethodDef);
576
577 // Similar to Module::FindMethod + Module::FindMethodThrowing,
578 // except it calls GetMethodDescFromMemberDefOrRefOrSpec with strictMetadataChecks=FALSE to allow generic instantiation
579 MethodDesc * pMethod = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec(pModule, token, NULL, FALSE, FALSE);
580
581 if ((pMethod != NULL) && ! pMethod->IsDynamicMethod() && pMethod->HasILHeader())
582 {
583 // MethodDesc::FindOrCreateTypicalSharedInstantiation is expensive, avoid calling it unless the method or class has generic arguments
584 if (pMethod->HasClassOrMethodInstantiation())
585 {
586 pMethod = pMethod->FindOrCreateTypicalSharedInstantiation();
587
588 if (pMethod == NULL)
589 {
590 goto BadMethod;
591 }
592
593 pModule = pMethod->GetModule_NoLogging();
594 }
595
596 if (pMethod->GetNativeCode() != NULL) // last check before
597 {
598 m_stats.m_nHasNativeCode ++;
599
600 return;
601 }
602 else
603 {
604 m_busyWith = methodIndex;
605
606 bool rslt = CompileMethodDesc(pModule, pMethod);
607
608 m_busyWith = EmptyToken;
609
610 if (rslt)
611 {
612 return;
613 }
614 }
615 }
616
617BadMethod:
618
619 m_stats.m_nFilteredMethods ++;
620
621 MulticoreJitTrace(("Filtered out methods: pModule:[%s] token:[%x]", pModule->GetSimpleName(), token));
622
623 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, TRACE_LEVEL_VERBOSE, CLR_PRIVATEMULTICOREJIT_KEYWORD))
624 {
625 _FireEtwMulticoreJitA(W("FILTERMETHOD-GENERIC"), pModule->GetSimpleName(), token, 0, 0);
626 }
627}
628
629
630class MulticoreJitPlayerModuleEnumerator : public MulticoreJitModuleEnumerator
631{
632 MulticoreJitProfilePlayer * m_pPlayer;
633
634 // Implementation of MulticoreJitModuleEnumerator::OnModule
635 HRESULT OnModule(Module * pModule)
636 {
637 CONTRACTL
638 {
639 THROWS;
640 GC_TRIGGERS;
641 MODE_PREEMPTIVE;
642 CAN_TAKE_LOCK;
643 }
644 CONTRACTL_END;
645
646 return m_pPlayer->OnModule(pModule);
647 }
648
649public:
650
651 MulticoreJitPlayerModuleEnumerator(MulticoreJitProfilePlayer * pPlayer)
652 {
653 m_pPlayer = pPlayer;
654 }
655};
656
657
658HRESULT MulticoreJitProfilePlayer::OnModule(Module * pModule)
659{
660 STANDARD_VM_CONTRACT;
661
662 HRESULT hr = S_OK;
663
664 // Check if already matched
665 for (unsigned i = 0; i < m_moduleCount; i ++)
666 {
667 if (m_pModules[i].m_pModule == pModule)
668 {
669 return hr;
670 }
671 }
672
673 ModuleVersion version; // GetModuleVersion is called on-demand when simple names matches
674
675 bool gotVersion = false;
676
677 // Match with simple name, and then version/flag/guid
678 for (unsigned i = 0; i < m_moduleCount; i ++)
679 {
680 if (m_pModules[i].MatchWith(version, gotVersion, pModule, m_shouldAbort, m_fAppxMode))
681 {
682 m_nLoadedModuleCount ++;
683 return hr;
684 }
685 }
686
687 return hr;
688}
689
690
691HRESULT MulticoreJitProfilePlayer::UpdateModuleInfo()
692{
693 STANDARD_VM_CONTRACT;
694
695 HRESULT hr = S_OK;
696
697 MulticoreJitTrace(("UpdateModuleInfo"));
698
699 // Enumerate module if there is a module needed, but not loaded yet
700 for (unsigned i = 0; i < m_moduleCount; i ++)
701 {
702 PlayerModuleInfo & info = m_pModules[i];
703
704 if (! info.LoadOkay() && info.IsDependency() && ! info.IsModuleLoaded())
705 {
706 MulticoreJitTrace((" Enumerate modules for player"));
707
708 MulticoreJitPlayerModuleEnumerator enumerator(this);
709
710 enumerator.EnumerateLoadedModules(GetAppDomain()); // Enumerate modules, hope to find new matches
711
712 break;
713 }
714 }
715
716 // Update load level, re-calculate blocking count
717 m_nBlockingCount = 0;
718 m_nMissingModule = 0;
719
720 if (m_shouldAbort)
721 {
722 hr = E_ABORT;
723 }
724 else
725 {
726 // Check for blocking level
727 for (unsigned i = 0; i < m_moduleCount; i ++)
728 {
729 PlayerModuleInfo & info = m_pModules[i];
730
731 if (! info.LoadOkay() && info.IsLowerLevel())
732 {
733 if (info.IsModuleLoaded())
734 {
735 info.UpdateCurrentLevel();
736 }
737 else
738 {
739 m_nMissingModule ++;
740 }
741
742 if (info.IsLowerLevel())
743 {
744 #ifdef MULTICOREJIT_LOGGING
745 info.Dump(W(" BlockingModule"), i);
746 #endif
747
748 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, TRACE_LEVEL_VERBOSE, CLR_PRIVATEMULTICOREJIT_KEYWORD))
749 {
750 _FireEtwMulticoreJitA(W("BLOCKINGMODULE"), info.m_pRecord->GetModuleName(), i, info.m_curLevel, info.m_needLevel);
751 }
752
753 m_nBlockingCount ++;
754 }
755 }
756 }
757 }
758
759 MulticoreJitTrace(("Blocking count: %d, missing module: %d, hr=%x", m_nBlockingCount, m_nMissingModule, hr));
760
761 return hr;
762}
763
764
765bool MulticoreJitProfilePlayer::ShouldAbort(bool fast) const
766{
767 LIMITED_METHOD_CONTRACT;
768
769 if (m_nMySession != m_appdomainSession.GetValue())
770 {
771 MulticoreJitTrace(("MulticoreJitProfilePlayer::ShouldAbort session over"));
772 _FireEtwMulticoreJit(W("ABORTPLAYER"), W("Session over"), 0, 0, 0);
773 return true;
774 }
775
776 if (fast)
777 {
778 return false;
779 }
780
781 if (GetTickCount() - m_nStartTime > MULTICOREJITLIFE)
782 {
783 MulticoreJitTrace(("MulticoreJitProfilePlayer::ShouldAbort time over"));
784
785 _FireEtwMulticoreJit(W("ABORTPLAYER"), W("Time out"), 0, 0, 0);
786
787 return true;
788 }
789
790 return false;
791}
792
793
794// Basic delay unit
795const int DelayUnit = 1; // 1 ms delay
796const int MissingModuleDelay = 10; // 10 ms for each missing module
797
798
799// Wait for all the module loading and level requests to be fullfilled
800// This allows for longer delay based on number of mismatches, to reduce CPU usage
801
802// Return true blocking count is 0, false if aborted
803bool MulticoreJitProfilePlayer::GroupWaitForModuleLoad(int pos)
804{
805 STANDARD_VM_CONTRACT;
806
807 MulticoreJitTrace(("Enter GroupWaitForModuleLoad(pos=%4d): %d modules loaded, blocking count=%d", pos, m_nLoadedModuleCount, m_nBlockingCount));
808
809 _FireEtwMulticoreJit(W("GROUPWAIT"), W("Enter"), m_nLoadedModuleCount, m_nBlockingCount, pos);
810
811 bool rslt = false;
812
813 // Ensure that we don't block in this particular case for longer than the block limit.
814 // This limit is smaller than the overall MULTICOREJITLIFE and ensures that we don't sit for the
815 // full player lifetime waiting for a module when the app behavior has changed.
816 DWORD currentModuleBlockStart = GetTickCount();
817
818 // Only allow module blocking to occur a certain number of times.
819
820 while (! ShouldAbort(false))
821 {
822 if (FAILED(UpdateModuleInfo()))
823 {
824 break;
825 }
826
827 if (m_nBlockingCount == 0)
828 {
829 rslt = true;
830 break;
831 }
832
833 if(GetTickCount() - currentModuleBlockStart > MULTICOREJITBLOCKLIMIT)
834 {
835 MulticoreJitTrace(("MulticoreJitProfilePlayer::GroupWaitForModuleLoad timeout exceeded."));
836 _FireEtwMulticoreJit(W("ABORTPLAYER"), W("GroupWaitForModuleLoad timeout exceeded."), 0, 0, 0);
837
838 break;
839 }
840
841 // Heuristic for reducing CPU usage: delay longer when there are more blocking modules
842 unsigned delay = min((m_nMissingModule * MissingModuleDelay + m_nBlockingCount) * DelayUnit, 50);
843
844 MulticoreJitTrace(("Delay: %d ms", delay));
845
846 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, TRACE_LEVEL_VERBOSE, CLR_PRIVATEMULTICOREJIT_KEYWORD))
847 {
848 _FireEtwMulticoreJit(W("GROUPWAIT"), W("Delay"), delay, 0, 0);
849 }
850
851 ClrSleepEx(delay, FALSE);
852
853 m_stats.m_nTotalDelay += (unsigned short) delay;
854 m_stats.m_nDelayCount ++;
855 }
856
857 MulticoreJitTrace(("Leave GroupWaitForModuleLoad(pos=%4d): blocking count=%d (rslt=%d)", pos, m_nBlockingCount, rslt));
858
859 _FireEtwMulticoreJit(W("GROUPWAIT"), W("Leave"), m_nLoadedModuleCount, m_nBlockingCount, rslt);
860
861 return rslt;
862}
863
864
865bool MulticoreJitProfilePlayer::HandleModuleDependency(unsigned jitInfo)
866{
867 STANDARD_VM_CONTRACT;
868
869 // depends on moduleTo, which may not loaded yet
870
871 unsigned moduleTo = jitInfo & MODULE_MASK;
872
873 if (moduleTo < m_moduleCount)
874 {
875 unsigned level = (jitInfo >> LEVEL_SHIFT) & LEVEL_MASK;
876
877 PlayerModuleInfo & mod = m_pModules[moduleTo];
878
879 // Load the module if necessary.
880 if (!mod.m_pModule)
881 {
882 // Update loaded module status.
883 AppDomain * pAppDomain = GetAppDomain();
884 _ASSERTE(pAppDomain != NULL);
885
886 MulticoreJitPlayerModuleEnumerator moduleEnumerator(this);
887 moduleEnumerator.EnumerateLoadedModules(pAppDomain);
888
889 if (!mod.m_pModule)
890 {
891 // Get the assembly name.
892 SString assemblyName;
893 assemblyName.SetASCII(mod.m_pRecord->GetAssemblyName(), mod.m_pRecord->AssemblyNameLen());
894
895 // Load the assembly.
896 DomainAssembly * pDomainAssembly = LoadAssembly(assemblyName);
897
898 if (pDomainAssembly)
899 {
900 // If we successfully loaded the assembly, enumerate the modules in the assembly
901 // and update all modules status.
902 moduleEnumerator.HandleAssembly(pDomainAssembly);
903
904 if (mod.m_pModule == NULL)
905 {
906 // Unable to load the assembly, so abort.
907 return false;
908 }
909 }
910 else
911 {
912 // Unable to load the assembly, so abort.
913 return false;
914 }
915 }
916 }
917
918 if (mod.UpdateNeedLevel((FileLoadLevel) level))
919 {
920 if (! mod.LoadOkay()) // allow first part WinMD to load in background thread
921 {
922 m_nBlockingCount ++;
923 }
924 }
925 }
926
927 return true;
928}
929
930DomainAssembly * MulticoreJitProfilePlayer::LoadAssembly(SString & assemblyName)
931{
932 STANDARD_VM_CONTRACT;
933
934 // Get the assembly name.
935 StackScratchBuffer scratch;
936 const ANSI* pAnsiAssemblyName = assemblyName.GetANSI(scratch);
937
938 AssemblySpec spec;
939
940 // Initialize the assembly spec.
941 HRESULT hr = spec.Init(pAnsiAssemblyName);
942 if (FAILED(hr))
943 {
944 return NULL;
945 }
946
947 // Set the binding context to the assembly load context.
948 if (m_pBinderContext != NULL)
949 {
950 spec.SetBindingContext(m_pBinderContext);
951 }
952
953 // Bind and load the assembly.
954 return spec.LoadDomainAssembly(
955 FILE_LOADED,
956 FALSE); // Don't throw on FileNotFound.
957}
958
959
960inline bool MethodJifInfo(unsigned inst)
961{
962 LIMITED_METHOD_CONTRACT;
963
964 return ((inst & MODULE_DEPENDENCY) == 0);
965}
966
967
968// Process a block of methodDef, call JIT if not blocked
969HRESULT MulticoreJitProfilePlayer::HandleMethodRecord(unsigned * buffer, int count)
970{
971 STANDARD_VM_CONTRACT;
972
973 HRESULT hr = E_ABORT;
974
975 MulticoreJitTrace(("MethodRecord(%d) start %d methods, %d mod loaded", m_stats.m_nTotalMethod, count, m_nLoadedModuleCount));
976
977 MulticoreJitManager & manager = GetAppDomain()->GetMulticoreJitManager();
978
979#ifdef MULTICOREJIT_LOGGING
980
981 MulticoreJitCodeStorage & curStorage = manager.GetMulticoreJitCodeStorage();
982
983 int lastCompiled = curStorage.GetStored();
984
985#endif
986
987 int pos = 0;
988
989 while (! ShouldAbort(true) && (pos < count))
990 {
991 unsigned jitInfo = buffer[pos]; // moduleIndex + methodIndex
992
993 unsigned moduleIndex = jitInfo >> 24;
994
995 if (moduleIndex < m_moduleCount)
996 {
997 if (jitInfo & MODULE_DEPENDENCY) // Module depedency information
998 {
999 if (! HandleModuleDependency(jitInfo))
1000 {
1001 goto Abort;
1002 }
1003 }
1004 else
1005 {
1006 PlayerModuleInfo & info = m_pModules[moduleIndex];
1007
1008 m_stats.m_nTotalMethod ++;
1009
1010 // If module is disabled for Jitting, just skip method without even waiting
1011 if (! info.m_enableJit)
1012 {
1013 m_stats.m_nFilteredMethods ++;
1014 }
1015 else
1016 {
1017
1018 // To reduce contention with foreground thread, walk backward within the group of methods Jittable methods, not broken apart by dependency
1019 {
1020 int run = 1; // size of the group
1021
1022 while (((pos + run) < count) && MethodJifInfo(buffer[pos + run]))
1023 {
1024 run ++;
1025
1026 // If walk-back run is too long, lots of methods in the front will be missed by background thread
1027 if (run > MAX_WALKBACK)
1028 {
1029 break;
1030 }
1031 }
1032
1033 if (run > 1)
1034 {
1035 MulticoreJitTrace(("Jit backwards %d methods", run));
1036 }
1037
1038 // Walk backwards within the same group, may be from different modules
1039 for (int p = pos + run - 1; p >= pos; p --)
1040 {
1041 unsigned inst = buffer[p];
1042
1043 _ASSERTE(MethodJifInfo(inst));
1044
1045 PlayerModuleInfo & mod = m_pModules[inst >> 24];
1046
1047 _ASSERTE(mod.IsModuleLoaded());
1048
1049 if (mod.m_enableJit)
1050 {
1051 JITMethod(mod.m_pModule, inst);
1052 }
1053 else
1054 {
1055 m_stats.m_nFilteredMethods ++;
1056 }
1057 }
1058
1059 m_stats.m_nWalkBack += (short) (run - 1);
1060 m_stats.m_nTotalMethod += (short) (run - 1);
1061
1062 pos += run - 1; // Skip the group
1063 }
1064 }
1065 }
1066 }
1067 else
1068 {
1069 hr = COR_E_BADIMAGEFORMAT;
1070 goto Abort;
1071 }
1072
1073 pos ++;
1074 }
1075
1076 // Mark success
1077 hr = S_OK;
1078
1079Abort:
1080
1081 m_stats.m_nMissingModuleSkip += (short) (count - pos);
1082
1083 MulticoreJitTrace(("MethodRecord(%d) end %d compiled, %d aborted / %d methods, hr=%x",
1084 m_stats.m_nTotalMethod,
1085 curStorage.GetStored() - lastCompiled,
1086 count - pos, count, hr));
1087
1088 TraceSummary();
1089
1090 return hr;
1091}
1092
1093
1094void MulticoreJitProfilePlayer::TraceSummary()
1095{
1096 LIMITED_METHOD_CONTRACT;
1097
1098 MulticoreJitCodeStorage & curStorage = GetAppDomain()->GetMulticoreJitManager().GetMulticoreJitCodeStorage();
1099
1100 unsigned returned = curStorage.GetReturned();
1101
1102#ifdef MULTICOREJIT_LOGGING
1103
1104 unsigned compiled = curStorage.GetStored();
1105
1106 MulticoreJitTrace(("PlayerSummary: %d total: %d no mod, %d filtered out, %d had code, %d other, %d tried, %d compiled, %d returned, %d%% efficiency, %d mod loaded, %d ms delay(%d)",
1107 m_stats.m_nTotalMethod,
1108 m_stats.m_nMissingModuleSkip,
1109 m_stats.m_nFilteredMethods,
1110 m_stats.m_nHasNativeCode,
1111 m_stats.m_nTotalMethod - m_stats.m_nMissingModuleSkip - m_stats.m_nFilteredMethods - m_stats.m_nHasNativeCode - m_stats.m_nTryCompiling,
1112 m_stats.m_nTryCompiling,
1113 compiled,
1114 returned,
1115 (m_stats.m_nTotalMethod == 0) ? 100 : returned * 100 / m_stats.m_nTotalMethod,
1116 m_nLoadedModuleCount,
1117 m_stats.m_nTotalDelay,
1118 m_stats.m_nDelayCount
1119 ));
1120
1121#endif
1122
1123 _FireEtwMulticoreJit(W("PLAYERSUMMARY"), W(""), m_stats.m_nTryCompiling, m_stats.m_nHasNativeCode, returned);
1124}
1125
1126
1127HRESULT MulticoreJitProfilePlayer::ReadCheckFile(const wchar_t * pFileName)
1128{
1129 CONTRACTL
1130 {
1131 THROWS;
1132 MODE_PREEMPTIVE;
1133 }
1134 CONTRACTL_END;
1135
1136 HRESULT hr = S_OK;
1137
1138 {
1139 HANDLE hFile = WszCreateFile(pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1140
1141 if (hFile == INVALID_HANDLE_VALUE)
1142 {
1143 return COR_E_FILENOTFOUND;
1144 }
1145
1146 HeaderRecord header;
1147
1148 DWORD cbRead = 0;
1149
1150 if (! ::ReadFile(hFile, & header, sizeof(header), &cbRead, NULL))
1151 {
1152 hr = COR_E_BADIMAGEFORMAT;
1153 }
1154 else if (cbRead != sizeof(header))
1155 {
1156 hr = COR_E_BADIMAGEFORMAT;
1157 }
1158 else
1159 {
1160 m_headerModuleCount = header.moduleCount;
1161
1162 MulticoreJitTrace(("HeaderRecord(version=%d, module=%d, method=%d)", header.version, m_headerModuleCount, header.methodCount));
1163
1164 if ((header.version != MULTICOREJIT_PROFILE_VERSION) || (header.moduleCount > MAX_MODULES) || (header.methodCount > MAX_METHOD_ARRAY) ||
1165 (header.recordID != Pack8_24(MULTICOREJIT_HEADER_RECORD_ID, sizeof(HeaderRecord))))
1166 {
1167 hr = COR_E_BADIMAGEFORMAT;
1168 }
1169 else
1170 {
1171 m_pModules = new (nothrow) PlayerModuleInfo[m_headerModuleCount];
1172
1173 if (m_pModules == NULL)
1174 {
1175 hr = E_OUTOFMEMORY;
1176 }
1177 }
1178 }
1179
1180 if (SUCCEEDED(hr))
1181 {
1182 m_nFileSize = SafeGetFileSize(hFile, 0);
1183
1184 if (m_nFileSize > sizeof(header))
1185 {
1186 m_nFileSize -= sizeof(header);
1187
1188 m_pFileBuffer = new (nothrow) BYTE[m_nFileSize];
1189
1190 if (m_pFileBuffer == NULL)
1191 {
1192 hr = E_OUTOFMEMORY;
1193 }
1194 else if (::ReadFile(hFile, m_pFileBuffer, m_nFileSize, & cbRead, NULL))
1195 {
1196 if (cbRead != m_nFileSize)
1197 {
1198 hr = COR_E_BADIMAGEFORMAT;
1199 }
1200 }
1201 else
1202 {
1203 hr = CLDB_E_FILE_BADREAD;
1204 }
1205 }
1206 else
1207 {
1208 hr = COR_E_BADIMAGEFORMAT;
1209 }
1210 }
1211
1212 CloseHandle(hFile);
1213
1214 _FireEtwMulticoreJit(W("PLAYER"), W("Header"), hr, m_headerModuleCount, header.methodCount);
1215 }
1216
1217
1218 return hr;
1219}
1220
1221
1222HRESULT MulticoreJitProfilePlayer::PlayProfile()
1223{
1224 STANDARD_VM_CONTRACT;
1225
1226 HRESULT hr = S_OK;
1227
1228 DWORD start = GetTickCount();
1229
1230 Thread * pThread = GetThread();
1231
1232 {
1233 // 1 marks background thread
1234 FireEtwThreadCreated((ULONGLONG) pThread, (ULONGLONG) GetAppDomain(), 1, pThread->GetThreadId(), pThread->GetOSThreadId(), GetClrInstanceId());
1235 }
1236
1237 const BYTE * pBuffer = m_pFileBuffer;
1238
1239 unsigned nSize = m_nFileSize;
1240
1241 MulticoreJitTrace(("PlayProfile %d bytes in (%d, %s)",
1242 nSize,
1243 GetAppDomain()->GetId().m_dwId,
1244 GetAppDomain()->GetFriendlyNameForLogging()));
1245
1246 while ((SUCCEEDED(hr)) && (nSize > sizeof(unsigned)))
1247 {
1248 unsigned data = * (const unsigned *) pBuffer;
1249 unsigned rcdLen = data & 0xFFFFFF;
1250 unsigned rcdTyp = data >> 24;
1251
1252 if ((rcdLen > nSize) || (rcdLen & 3)) // Better DWORD align
1253 {
1254 hr = COR_E_BADIMAGEFORMAT;
1255 }
1256 else
1257 {
1258 if (rcdTyp == MULTICOREJIT_MODULE_RECORD_ID)
1259 {
1260 const ModuleRecord * pRec = (const ModuleRecord * ) pBuffer;
1261
1262 if (((unsigned)(pRec->lenModuleName
1263 + pRec->lenAssemblyName
1264 ) > (rcdLen - sizeof(ModuleRecord))) ||
1265 (m_moduleCount >= m_headerModuleCount))
1266 {
1267 hr = COR_E_BADIMAGEFORMAT;
1268 }
1269 else
1270 {
1271 hr = HandleModuleRecord(pRec);
1272 }
1273 }
1274 else if (rcdTyp == MULTICOREJIT_JITINF_RECORD_ID)
1275 {
1276 int mCount = (rcdLen - sizeof(unsigned)) / sizeof(unsigned);
1277
1278 hr = HandleMethodRecord((unsigned *) (pBuffer + sizeof(unsigned)), mCount);
1279 }
1280 else
1281 {
1282 hr = COR_E_BADIMAGEFORMAT;
1283 }
1284
1285 pBuffer += rcdLen;
1286 nSize -= rcdLen;
1287 }
1288
1289 if (SUCCEEDED(hr) && ShouldAbort(false))
1290 {
1291 hr = E_ABORT;
1292 }
1293 }
1294
1295 start = GetTickCount() - start;
1296
1297 {
1298 FireEtwThreadTerminated((ULONGLONG) pThread, (ULONGLONG) GetAppDomain(), GetClrInstanceId());
1299 }
1300
1301 MulticoreJitTrace(("Background thread running for %d ms, %d methods, hr=%x", start, m_stats.m_nTotalMethod, hr));
1302
1303 TraceSummary();
1304
1305 return hr;
1306}
1307
1308
1309HRESULT MulticoreJitProfilePlayer::JITThreadProc(Thread * pThread)
1310{
1311 CONTRACTL
1312 {
1313 NOTHROW;
1314 GC_TRIGGERS;
1315 MODE_COOPERATIVE;
1316 INJECT_FAULT(COMPlusThrowOM(););
1317 }
1318 CONTRACTL_END;
1319
1320 m_stats.m_hr = S_OK;
1321
1322 EX_TRY
1323 {
1324 ENTER_DOMAIN_ID(m_DomainID);
1325 {
1326 // Go into preemptive mode
1327 GCX_PREEMP();
1328
1329 m_stats.m_hr = PlayProfile();
1330 }
1331 END_DOMAIN_TRANSITION;
1332 }
1333 EX_CATCH
1334 {
1335 if (SUCCEEDED(m_stats.m_hr))
1336 {
1337 m_stats.m_hr = COR_E_EXCEPTION;
1338 }
1339 }
1340 EX_END_CATCH(SwallowAllExceptions);
1341
1342 return (DWORD) m_stats.m_hr;
1343}
1344
1345
1346DWORD WINAPI MulticoreJitProfilePlayer::StaticJITThreadProc(void *args)
1347{
1348 CONTRACTL
1349 {
1350 NOTHROW;
1351 GC_TRIGGERS;
1352 MODE_ANY;
1353 ENTRY_POINT;
1354 INJECT_FAULT(COMPlusThrowOM(););
1355 }
1356 CONTRACTL_END;
1357
1358 HRESULT hr = S_OK;
1359
1360 BEGIN_ENTRYPOINT_NOTHROW;
1361
1362 MulticoreJitTrace(("StaticJITThreadProc starting"));
1363
1364 // Mark the background thread via an ETW event for diagnostics.
1365 _FireEtwMulticoreJit(W("JITTHREAD"), W(""), 0, 0, 0);
1366
1367 MulticoreJitProfilePlayer * pPlayer = (MulticoreJitProfilePlayer *) args;
1368
1369 if (pPlayer != NULL)
1370 {
1371 Thread * pThread = pPlayer->m_pThread;
1372
1373 if ((pThread != NULL) && pThread->HasStarted())
1374 {
1375 // Disable calling managed code in background thread
1376 ThreadStateNCStackHolder holder(TRUE, Thread::TSNC_CallingManagedCodeDisabled);
1377
1378 // Run as background thread, so ThreadStore::WaitForOtherThreads will not wait for it
1379 pThread->SetBackground(TRUE);
1380
1381 hr = pPlayer->JITThreadProc(pThread);
1382 }
1383
1384 // It needs to be deleted after GCX_PREEMP ends
1385 if (pThread != NULL)
1386 {
1387 DestroyThread(pThread);
1388 }
1389
1390 // The background thread is reponsible for deleting the MulticoreJitProfilePlayer object once it's started
1391 // Actually after Thread::StartThread succeeds
1392 delete pPlayer;
1393 }
1394
1395 MulticoreJitTrace(("StaticJITThreadProc endding(%x)", hr));
1396
1397 END_ENTRYPOINT_NOTHROW;
1398
1399 return (DWORD) hr;
1400}
1401
1402
1403HRESULT MulticoreJitProfilePlayer::ProcessProfile(const wchar_t * pFileName)
1404{
1405 STANDARD_VM_CONTRACT;
1406
1407 HRESULT hr = ReadCheckFile(pFileName);
1408
1409 if (SUCCEEDED(hr))
1410 {
1411 _ASSERTE(m_pThread == NULL);
1412
1413 m_pThread = SetupUnstartedThread();
1414
1415 _ASSERTE(m_pThread != NULL);
1416
1417 if (m_pThread->CreateNewThread(0, StaticJITThreadProc, this))
1418 {
1419 int t = (int) m_pThread->StartThread();
1420
1421 if (t > 0)
1422 {
1423 hr = S_OK;
1424 }
1425 }
1426 }
1427
1428 return hr;
1429}
1430
1431
1432