[Tizen] Unify dnetmemoryenumlib terms to match the codebase (#291)
[platform/upstream/coreclr.git] / src / vm / multicorejitplayer.cpp
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
33 unsigned g_MulticoreJitDelay      = 0;          // Delay in StartProfile
34
35 bool     g_MulticoreJitEnabled    = true;       // Enable/Disable feature
36
37 ///////////////////////////////////////////////////////////////////////////////////
38 //
39 //            class MulticoreJitCodeStorage
40 //
41 ///////////////////////////////////////////////////////////////////////////////////
42
43
44 void 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
60 MulticoreJitCodeStorage::~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
69 void 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
104 PCODE 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
142 class PlayerModuleInfo
143 {
144 public:
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
241 bool 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
270 void 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
318 const unsigned EmptyToken = 0xFFFFFFFF;
319
320 bool 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
368 MulticoreJitProfilePlayer::MulticoreJitProfilePlayer(ICLRPrivBinder * pBinderContext, LONG nSession, bool fAppxMode)
369     : m_stats(::GetAppDomain()->GetMulticoreJitManager().GetStats()), m_appdomainSession(::GetAppDomain()->GetMulticoreJitManager().GetProfileSession())
370 {
371     LIMITED_METHOD_CONTRACT;
372
373     m_pBinderContext     = pBinderContext;
374     m_nMySession         = nSession;
375     m_moduleCount        = 0;
376     m_headerModuleCount  = 0;
377     m_pModules           = NULL;
378     m_nBlockingCount     = 0;
379     m_nMissingModule     = 0;
380     m_nLoadedModuleCount = 0;
381     m_shouldAbort        = false;
382     m_fAppxMode          = fAppxMode;
383
384     m_pThread            = NULL;
385     m_pFileBuffer        = NULL;
386     m_nFileSize          = 0;
387     
388     m_busyWith           = EmptyToken;
389
390     m_nStartTime         = GetTickCount();
391 }
392
393
394 MulticoreJitProfilePlayer::~MulticoreJitProfilePlayer()
395 {
396     LIMITED_METHOD_CONTRACT;
397     
398     if (m_pModules != NULL)
399     {
400         delete [] m_pModules;
401         m_pModules = NULL;
402     }
403
404     if (m_pFileBuffer != NULL)
405     {
406         delete [] m_pFileBuffer;
407     }
408 }
409
410
411 // static
412 bool MulticoreJitManager::ModuleHasNoCode(Module * pModule)
413 {
414     LIMITED_METHOD_CONTRACT;
415     
416     if (pModule->IsResource())
417     {
418         return true;
419     }
420
421     IMDInternalImport * pImport = pModule->GetMDImport();
422
423     if (pImport != NULL)
424     {
425         if ((pImport->GetCountWithTokenKind(mdtTypeDef)   == 0) &&
426             (pImport->GetCountWithTokenKind(mdtMethodDef) == 0) &&
427             (pImport->GetCountWithTokenKind(mdtFieldDef)  == 0)
428             )
429         {
430             return true;
431         }
432     }
433
434     return false;
435 }
436
437
438 // We only support default load context, non dynamic module, non domain neutral (needed for dependency)
439 bool MulticoreJitManager::IsSupportedModule(Module * pModule, bool fMethodJit, bool fAppx)
440 {
441     CONTRACTL
442     {
443         NOTHROW;
444         GC_NOTRIGGER;
445         MODE_ANY;
446     }
447     CONTRACTL_END;
448     
449     if (pModule == NULL)
450     {
451         return false;
452     }
453     
454     PEFile * pFile = pModule->GetFile();
455
456     // dynamic module.
457     if (pFile->IsDynamic()) // Ignore dynamic modules
458     {
459         return false;
460     }
461
462     if (pFile->GetPath().IsEmpty()) // Ignore in-memory modules
463     {
464         return false;
465     }
466
467
468     if (! fMethodJit)
469     {
470         if (ModuleHasNoCode(pModule))
471         {
472             return false;
473         }
474     }
475     
476     Assembly * pAssembly = pModule->GetAssembly();
477     
478
479     return true;
480
481 }
482
483
484 // ModuleRecord handling: add to m_ModuleList
485
486 HRESULT MulticoreJitProfilePlayer::HandleModuleRecord(const ModuleRecord * pMod)
487 {
488     STANDARD_VM_CONTRACT;
489     
490     HRESULT hr = S_OK;
491
492     PlayerModuleInfo & info = m_pModules[m_moduleCount];
493
494     info.m_pModule = NULL;
495     info.m_pRecord = pMod;
496
497 #ifdef MULTICOREJIT_LOGGING
498     info.Dump(W("ModuleRecord"), m_moduleCount);
499 #endif
500
501     m_moduleCount ++;
502
503     return hr;
504 }
505
506
507 #ifndef DACCESS_COMPILE
508 class MulticoreJitPrepareCodeConfig : public PrepareCodeConfig
509 {
510 public:
511     MulticoreJitPrepareCodeConfig(MethodDesc* pMethod) :
512         PrepareCodeConfig(NativeCodeVersion(pMethod), FALSE, FALSE)
513     {}
514     
515     virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse)
516     {
517         MulticoreJitManager & mcJitManager = GetAppDomain()->GetMulticoreJitManager();
518         mcJitManager.GetMulticoreJitCodeStorage().StoreMethodCode(GetMethodDesc(), pCode);
519         return TRUE;
520     }
521 };
522 #endif
523
524 // Call JIT to compile a method
525
526 bool MulticoreJitProfilePlayer::CompileMethodDesc(Module * pModule, MethodDesc * pMD)
527 {
528     STANDARD_VM_CONTRACT;
529     
530     COR_ILMETHOD_DECODER::DecoderStatus status;
531         
532     COR_ILMETHOD_DECODER header(pMD->GetILHeader(), pModule->GetMDImport(), & status);
533         
534     if (status == COR_ILMETHOD_DECODER::SUCCESS)
535     {
536         if (m_stats.m_nTryCompiling == 0)
537         {
538             MulticoreJitTrace(("First call to MakeJitWorker"));
539         }
540
541         m_stats.m_nTryCompiling ++;
542
543         // Reset the flag to allow managed code to be called in multicore JIT background thread from this routine
544         ThreadStateNCStackHolder holder(-1, Thread::TSNC_CallingManagedCodeDisabled);
545
546         // PrepareCode calls back to MulticoreJitCodeStorage::StoreMethodCode under MethodDesc lock
547         MulticoreJitPrepareCodeConfig config(pMD);
548         pMD->PrepareCode(&config);
549
550         return true;
551     }
552
553     return false;
554 }
555
556
557 // Conditional JIT of a method
558 void MulticoreJitProfilePlayer::JITMethod(Module * pModule, unsigned methodIndex)
559 {
560     STANDARD_VM_CONTRACT;
561     
562         // Ensure non-null module
563         if (pModule == NULL)
564         {
565                 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context, TRACE_LEVEL_VERBOSE, CLR_PRIVATEMULTICOREJIT_KEYWORD))
566                 {
567                         _FireEtwMulticoreJitA(W("NULLMODULEPOINTER"), NULL, methodIndex, 0, 0);
568                 }
569                 return;
570         }
571
572     methodIndex &= METHODINDEX_MASK; // 20-bit
573
574     unsigned token = TokenFromRid(methodIndex, mdtMethodDef);
575
576     // Similar to Module::FindMethod + Module::FindMethodThrowing, 
577     // except it calls GetMethodDescFromMemberDefOrRefOrSpec with strictMetadataChecks=FALSE to allow generic instantiation
578     MethodDesc * pMethod = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec(pModule, token, NULL, FALSE, FALSE);
579
580     if ((pMethod != NULL) && ! pMethod->IsDynamicMethod() && pMethod->HasILHeader())
581     {
582         // MethodDesc::FindOrCreateTypicalSharedInstantiation is expensive, avoid calling it unless the method or class has generic arguments
583         if (pMethod->HasClassOrMethodInstantiation())
584         {
585              pMethod = pMethod->FindOrCreateTypicalSharedInstantiation();
586
587             if (pMethod == NULL)
588             {
589                 goto BadMethod;
590             }
591
592             pModule = pMethod->GetModule_NoLogging();
593         }
594
595         if (pMethod->GetNativeCode() != NULL) // last check before
596         {
597             m_stats.m_nHasNativeCode ++;
598
599             return;
600         }
601         else
602         {                    
603             m_busyWith = methodIndex;
604
605             bool rslt = CompileMethodDesc(pModule, pMethod);
606
607             m_busyWith = EmptyToken;
608
609             if (rslt)
610             {
611                 return;
612             }
613         }
614     }
615     
616 BadMethod:
617
618     m_stats.m_nFilteredMethods ++;
619         
620     MulticoreJitTrace(("Filtered out methods: pModule:[%s] token:[%x]", pModule->GetSimpleName(), token));
621
622     if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context, TRACE_LEVEL_VERBOSE, CLR_PRIVATEMULTICOREJIT_KEYWORD)) 
623     {
624         _FireEtwMulticoreJitA(W("FILTERMETHOD-GENERIC"), pModule->GetSimpleName(), token, 0, 0);
625     }
626 }
627
628
629 class MulticoreJitPlayerModuleEnumerator : public MulticoreJitModuleEnumerator
630 {
631     MulticoreJitProfilePlayer * m_pPlayer;
632
633     // Implementation of MulticoreJitModuleEnumerator::OnModule
634     HRESULT OnModule(Module * pModule)
635     {
636         CONTRACTL
637         {
638             THROWS;
639             GC_TRIGGERS;
640             MODE_PREEMPTIVE;
641             CAN_TAKE_LOCK;
642         }
643         CONTRACTL_END;
644
645         return m_pPlayer->OnModule(pModule);
646     }
647
648 public:
649     
650     MulticoreJitPlayerModuleEnumerator(MulticoreJitProfilePlayer * pPlayer)
651     {
652         m_pPlayer = pPlayer;
653     }
654 };
655
656
657 HRESULT MulticoreJitProfilePlayer::OnModule(Module * pModule)
658 {
659     STANDARD_VM_CONTRACT;
660
661     HRESULT hr = S_OK;
662
663     // Check if already matched
664     for (unsigned i = 0; i < m_moduleCount; i ++)
665     {
666         if (m_pModules[i].m_pModule == pModule)
667         {
668             return hr;
669         }
670     }
671
672     ModuleVersion version; // GetModuleVersion is called on-demand when simple names matches
673
674     bool gotVersion = false;
675
676     // Match with simple name, and then version/flag/guid
677     for (unsigned i = 0; i < m_moduleCount; i ++)
678     {
679         if (m_pModules[i].MatchWith(version, gotVersion, pModule, m_shouldAbort, m_fAppxMode))
680         {
681             m_nLoadedModuleCount ++;
682             return hr;
683         }
684     }
685
686     return hr;
687 }
688
689
690 HRESULT MulticoreJitProfilePlayer::UpdateModuleInfo()
691 {
692     STANDARD_VM_CONTRACT;
693
694     HRESULT hr = S_OK;
695
696     MulticoreJitTrace(("UpdateModuleInfo"));
697
698     // Enumerate module if there is a module needed, but not loaded yet
699     for (unsigned i = 0; i < m_moduleCount; i ++)
700     {
701         PlayerModuleInfo & info = m_pModules[i];
702
703         if (! info.LoadOkay() && info.IsDependency() && ! info.IsModuleLoaded())
704         {
705             MulticoreJitTrace(("  Enumerate modules for player"));
706
707             MulticoreJitPlayerModuleEnumerator enumerator(this);
708
709             enumerator.EnumerateLoadedModules(GetAppDomain()); // Enumerate modules, hope to find new matches
710
711             break;
712         }
713     }
714
715     // Update load level, re-calculate blocking count
716     m_nBlockingCount = 0;
717     m_nMissingModule = 0;
718
719     if (m_shouldAbort)
720     {
721         hr = E_ABORT;
722     }
723     else
724     {
725         // Check for blocking level
726         for (unsigned i = 0; i < m_moduleCount; i ++)
727         {
728             PlayerModuleInfo & info = m_pModules[i];
729
730             if (! info.LoadOkay() && info.IsLowerLevel())
731             {
732                 if (info.IsModuleLoaded())
733                 {
734                     info.UpdateCurrentLevel();
735                 }
736                 else
737                 {
738                     m_nMissingModule ++;
739                 }
740
741                 if (info.IsLowerLevel())
742                 {
743     #ifdef MULTICOREJIT_LOGGING
744                     info.Dump(W("    BlockingModule"), i);
745     #endif
746
747                     if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context, TRACE_LEVEL_VERBOSE, CLR_PRIVATEMULTICOREJIT_KEYWORD)) 
748                     {
749                         _FireEtwMulticoreJitA(W("BLOCKINGMODULE"), info.m_pRecord->GetModuleName(), i, info.m_curLevel, info.m_needLevel);
750                     }
751
752                     m_nBlockingCount ++;
753                 }
754             }
755         }
756     }
757
758     MulticoreJitTrace(("Blocking count: %d, missing module: %d, hr=%x", m_nBlockingCount, m_nMissingModule, hr));
759
760     return hr;
761 }
762
763
764 bool MulticoreJitProfilePlayer::ShouldAbort(bool fast) const
765 {
766     LIMITED_METHOD_CONTRACT;
767     
768     if (m_nMySession != m_appdomainSession.GetValue())
769     {
770         MulticoreJitTrace(("MulticoreJitProfilePlayer::ShouldAbort session over"));
771         _FireEtwMulticoreJit(W("ABORTPLAYER"), W("Session over"), 0, 0, 0);
772         return true;
773     }
774
775     if (fast)
776     {
777         return false;
778     }
779
780     if (GetTickCount() - m_nStartTime > MULTICOREJITLIFE)
781     {
782         MulticoreJitTrace(("MulticoreJitProfilePlayer::ShouldAbort time over"));
783         
784         _FireEtwMulticoreJit(W("ABORTPLAYER"), W("Time out"), 0, 0, 0);
785
786         return true;
787     }
788
789     return false;
790 }
791
792
793 // Basic delay unit
794 const int DelayUnit          = 1;     //  1 ms delay
795 const int MissingModuleDelay = 10;    // 10 ms for each missing module
796
797
798 // Wait for all the module loading and level requests to be fullfilled
799 // This allows for longer delay based on number of mismatches, to reduce CPU usage
800
801 // Return true blocking count is 0, false if aborted
802 bool MulticoreJitProfilePlayer::GroupWaitForModuleLoad(int pos)
803 {
804     STANDARD_VM_CONTRACT;
805     
806     MulticoreJitTrace(("Enter GroupWaitForModuleLoad(pos=%4d): %d modules loaded, blocking count=%d", pos, m_nLoadedModuleCount, m_nBlockingCount));
807
808     _FireEtwMulticoreJit(W("GROUPWAIT"), W("Enter"), m_nLoadedModuleCount, m_nBlockingCount, pos);
809
810     bool rslt = false;
811
812     // Ensure that we don't block in this particular case for longer than the block limit.
813     // This limit is smaller than the overall MULTICOREJITLIFE and ensures that we don't sit for the
814     // full player lifetime waiting for a module when the app behavior has changed.
815     DWORD currentModuleBlockStart = GetTickCount();
816
817     // Only allow module blocking to occur a certain number of times.
818
819     while (! ShouldAbort(false))
820     {
821         if (FAILED(UpdateModuleInfo()))
822         {
823             break;
824         }
825
826         if (m_nBlockingCount == 0)
827         {
828             rslt = true;
829             break;
830         }
831
832         if(GetTickCount() - currentModuleBlockStart > MULTICOREJITBLOCKLIMIT)
833         {
834             MulticoreJitTrace(("MulticoreJitProfilePlayer::GroupWaitForModuleLoad timeout exceeded."));
835             _FireEtwMulticoreJit(W("ABORTPLAYER"), W("GroupWaitForModuleLoad timeout exceeded."), 0, 0, 0);
836
837             break;
838         }
839
840         // Heuristic for reducing CPU usage: delay longer when there are more blocking modules
841         unsigned delay = min((m_nMissingModule * MissingModuleDelay + m_nBlockingCount) * DelayUnit, 50);
842
843         MulticoreJitTrace(("Delay: %d ms", delay));
844
845         if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context, TRACE_LEVEL_VERBOSE, CLR_PRIVATEMULTICOREJIT_KEYWORD)) 
846         {
847             _FireEtwMulticoreJit(W("GROUPWAIT"), W("Delay"), delay, 0, 0);
848         }
849
850         ClrSleepEx(delay, FALSE);
851
852         m_stats.m_nTotalDelay += (unsigned short) delay;
853         m_stats.m_nDelayCount ++;
854     }
855
856     MulticoreJitTrace(("Leave GroupWaitForModuleLoad(pos=%4d): blocking count=%d (rslt=%d)", pos, m_nBlockingCount, rslt));
857
858     _FireEtwMulticoreJit(W("GROUPWAIT"), W("Leave"), m_nLoadedModuleCount, m_nBlockingCount, rslt);
859
860     return rslt;
861 }
862
863
864 bool MulticoreJitProfilePlayer::HandleModuleDependency(unsigned jitInfo)
865 {
866     STANDARD_VM_CONTRACT;
867     
868     // depends on moduleTo, which may not loaded yet
869
870     unsigned moduleTo = jitInfo & MODULE_MASK;
871                         
872     if (moduleTo < m_moduleCount)
873     {
874         unsigned level = (jitInfo >> LEVEL_SHIFT) & LEVEL_MASK;
875
876         PlayerModuleInfo & mod = m_pModules[moduleTo];
877
878         // Load the module if necessary.
879         if (!mod.m_pModule)
880         {
881             // Update loaded module status.
882             AppDomain * pAppDomain = GetAppDomain();
883             _ASSERTE(pAppDomain != NULL);
884
885             MulticoreJitPlayerModuleEnumerator moduleEnumerator(this);
886             moduleEnumerator.EnumerateLoadedModules(pAppDomain);
887
888             if (!mod.m_pModule)
889             {
890                 // Get the assembly name.
891                 SString assemblyName;
892                 assemblyName.SetASCII(mod.m_pRecord->GetAssemblyName(), mod.m_pRecord->AssemblyNameLen());
893                 
894                 // Load the assembly.
895                 DomainAssembly * pDomainAssembly = LoadAssembly(assemblyName);
896                 
897                 if (pDomainAssembly)
898                 {
899                     // If we successfully loaded the assembly, enumerate the modules in the assembly
900                     // and update all modules status.
901                     moduleEnumerator.HandleAssembly(pDomainAssembly);
902
903                     if (mod.m_pModule == NULL)
904                     {
905                         // Unable to load the assembly, so abort.
906                         return false;
907                     }
908                 }
909                 else
910                 {
911                     // Unable to load the assembly, so abort.
912                     return false;
913                 }
914             }
915         }
916
917         if (mod.UpdateNeedLevel((FileLoadLevel) level))
918         {
919             if (! mod.LoadOkay()) // allow first part WinMD to load in background thread
920             {
921                 m_nBlockingCount ++;
922             }
923         }
924     }
925
926     return true;
927 }
928
929 DomainAssembly * MulticoreJitProfilePlayer::LoadAssembly(SString & assemblyName)
930 {
931     STANDARD_VM_CONTRACT;
932
933     // Get the assembly name.
934     StackScratchBuffer scratch;
935     const ANSI* pAnsiAssemblyName = assemblyName.GetANSI(scratch);
936
937     AssemblySpec spec;
938
939     // Initialize the assembly spec.
940     HRESULT hr = spec.Init(pAnsiAssemblyName);
941     if (FAILED(hr))
942     {
943         return NULL;
944     }
945
946     // Set the binding context to the assembly load context.
947     if (m_pBinderContext != NULL)
948     {
949         spec.SetBindingContext(m_pBinderContext);
950     }
951
952     // Bind and load the assembly.
953     return spec.LoadDomainAssembly(
954         FILE_LOADED,
955         FALSE); // Don't throw on FileNotFound.
956 }
957
958
959 inline bool MethodJifInfo(unsigned inst)
960 {
961     LIMITED_METHOD_CONTRACT;
962
963     return ((inst & MODULE_DEPENDENCY) == 0);
964 }
965
966
967 // Process a block of methodDef, call JIT if not blocked
968 HRESULT MulticoreJitProfilePlayer::HandleMethodRecord(unsigned * buffer, int count)
969 {
970     STANDARD_VM_CONTRACT;
971     
972     HRESULT hr = E_ABORT;
973
974     MulticoreJitTrace(("MethodRecord(%d) start %d methods, %d mod loaded", m_stats.m_nTotalMethod, count, m_nLoadedModuleCount));
975
976     MulticoreJitManager & manager = GetAppDomain()->GetMulticoreJitManager();
977     
978 #ifdef MULTICOREJIT_LOGGING
979
980     MulticoreJitCodeStorage & curStorage = manager.GetMulticoreJitCodeStorage();
981     
982     int lastCompiled = curStorage.GetStored();
983
984 #endif
985
986     int pos = 0;
987
988     while (! ShouldAbort(true) && (pos < count))
989     {
990         unsigned jitInfo = buffer[pos]; // moduleIndex + methodIndex
991
992         unsigned moduleIndex = jitInfo >> 24;
993                 
994         if (moduleIndex < m_moduleCount)
995         {
996             if (jitInfo & MODULE_DEPENDENCY) // Module depedency information
997             {
998                 if (! HandleModuleDependency(jitInfo))
999                 {
1000                     goto Abort;
1001                 }
1002             }
1003             else
1004             {
1005                 PlayerModuleInfo & info = m_pModules[moduleIndex];
1006
1007                 m_stats.m_nTotalMethod ++;
1008
1009                 // If module is disabled for Jitting, just skip method without even waiting
1010                 if (! info.m_enableJit)
1011                 {
1012                     m_stats.m_nFilteredMethods ++;
1013                 }
1014                 else
1015                 {
1016
1017                     //  To reduce contention with foreground thread, walk backward within the group of methods Jittable methods, not broken apart by dependency
1018                     {
1019                         int run = 1; // size of the group
1020
1021                         while (((pos + run) < count) && MethodJifInfo(buffer[pos + run]))
1022                         {
1023                             run ++;
1024
1025                             // If walk-back run is too long, lots of methods in the front will be missed by background thread
1026                             if (run > MAX_WALKBACK)
1027                             {
1028                                 break;
1029                             }
1030                         }
1031
1032                         if (run > 1)
1033                         {
1034                             MulticoreJitTrace(("Jit backwards %d methods",  run));
1035                         }
1036
1037                         // Walk backwards within the same group, may be from different modules
1038                         for (int p = pos + run - 1; p >= pos; p --)
1039                         {
1040                             unsigned inst = buffer[p];
1041
1042                             _ASSERTE(MethodJifInfo(inst));
1043
1044                             PlayerModuleInfo & mod = m_pModules[inst >> 24];
1045
1046                             _ASSERTE(mod.IsModuleLoaded());
1047
1048                             if (mod.m_enableJit)
1049                             {
1050                                 JITMethod(mod.m_pModule, inst);
1051                             }
1052                             else
1053                             {
1054                                 m_stats.m_nFilteredMethods ++;
1055                             }
1056                         }
1057
1058                         m_stats.m_nWalkBack    += (short) (run - 1);
1059                         m_stats.m_nTotalMethod += (short) (run - 1);
1060
1061                         pos += run - 1; // Skip the group
1062                     }
1063                 }
1064             }
1065         }
1066         else
1067         {
1068             hr = COR_E_BADIMAGEFORMAT;
1069             goto Abort;
1070         }
1071
1072         pos ++;
1073     }
1074
1075     // Mark success
1076     hr = S_OK;
1077
1078 Abort:
1079     
1080     m_stats.m_nMissingModuleSkip += (short) (count - pos);
1081
1082     MulticoreJitTrace(("MethodRecord(%d) end %d compiled, %d aborted / %d methods, hr=%x", 
1083         m_stats.m_nTotalMethod,
1084         curStorage.GetStored() - lastCompiled, 
1085         count - pos, count, hr));
1086
1087     TraceSummary();
1088
1089     return hr;
1090 }
1091
1092
1093 void MulticoreJitProfilePlayer::TraceSummary()
1094 {
1095     LIMITED_METHOD_CONTRACT;
1096
1097     MulticoreJitCodeStorage & curStorage =  GetAppDomain()->GetMulticoreJitManager().GetMulticoreJitCodeStorage();
1098     
1099     unsigned returned = curStorage.GetReturned();
1100
1101 #ifdef MULTICOREJIT_LOGGING
1102
1103     unsigned compiled =   curStorage.GetStored();
1104
1105     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)", 
1106         m_stats.m_nTotalMethod,
1107         m_stats.m_nMissingModuleSkip,
1108         m_stats.m_nFilteredMethods,
1109         m_stats.m_nHasNativeCode,
1110         m_stats.m_nTotalMethod - m_stats.m_nMissingModuleSkip - m_stats.m_nFilteredMethods - m_stats.m_nHasNativeCode - m_stats.m_nTryCompiling,
1111         m_stats.m_nTryCompiling, 
1112         compiled,
1113         returned,
1114         (m_stats.m_nTotalMethod == 0) ? 100 : returned * 100 / m_stats.m_nTotalMethod,
1115         m_nLoadedModuleCount,
1116         m_stats.m_nTotalDelay,
1117         m_stats.m_nDelayCount
1118         ));
1119
1120 #endif
1121
1122     _FireEtwMulticoreJit(W("PLAYERSUMMARY"), W(""), m_stats.m_nTryCompiling, m_stats.m_nHasNativeCode, returned);
1123 }
1124
1125
1126 HRESULT MulticoreJitProfilePlayer::ReadCheckFile(const wchar_t * pFileName)
1127 {
1128     CONTRACTL
1129     {
1130         THROWS;
1131         MODE_PREEMPTIVE;
1132     }
1133     CONTRACTL_END;
1134
1135     HRESULT hr = S_OK;
1136
1137     {
1138         HANDLE hFile = WszCreateFile(pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1139     
1140         if (hFile == INVALID_HANDLE_VALUE) 
1141         {
1142             return COR_E_FILENOTFOUND;
1143         }
1144
1145         HeaderRecord header;
1146
1147         DWORD cbRead = 0;
1148
1149         if (! ::ReadFile(hFile, & header, sizeof(header), &cbRead, NULL)) 
1150         {
1151             hr = COR_E_BADIMAGEFORMAT;
1152         }
1153         else if (cbRead != sizeof(header))
1154         {
1155             hr = COR_E_BADIMAGEFORMAT;
1156         }
1157         else
1158         {
1159             m_headerModuleCount = header.moduleCount;
1160
1161             MulticoreJitTrace(("HeaderRecord(version=%d, module=%d, method=%d)", header.version, m_headerModuleCount, header.methodCount));
1162
1163             if ((header.version != MULTICOREJIT_PROFILE_VERSION) || (header.moduleCount > MAX_MODULES) || (header.methodCount > MAX_METHOD_ARRAY) ||
1164                 (header.recordID != Pack8_24(MULTICOREJIT_HEADER_RECORD_ID, sizeof(HeaderRecord))))
1165             {
1166                 hr = COR_E_BADIMAGEFORMAT;
1167             }
1168             else
1169             {
1170                 m_pModules = new (nothrow) PlayerModuleInfo[m_headerModuleCount];
1171
1172                 if (m_pModules == NULL)
1173                 {
1174                     hr = E_OUTOFMEMORY;
1175                 }
1176             }
1177         }
1178
1179         if (SUCCEEDED(hr))
1180         {
1181             m_nFileSize = SafeGetFileSize(hFile, 0);
1182             
1183             if (m_nFileSize > sizeof(header))
1184             {
1185                 m_nFileSize -= sizeof(header);
1186              
1187                 m_pFileBuffer = new (nothrow) BYTE[m_nFileSize];
1188
1189                 if (m_pFileBuffer == NULL)
1190                 {
1191                     hr = E_OUTOFMEMORY;
1192                 }
1193                 else if (::ReadFile(hFile, m_pFileBuffer, m_nFileSize, & cbRead, NULL))
1194                 {
1195                     if (cbRead != m_nFileSize)
1196                     {
1197                         hr = COR_E_BADIMAGEFORMAT;
1198                     }
1199                 }
1200                 else
1201                 {
1202                     hr = CLDB_E_FILE_BADREAD;
1203                 }
1204             }
1205             else
1206             {
1207                 hr = COR_E_BADIMAGEFORMAT;
1208             }
1209         }
1210
1211         CloseHandle(hFile);
1212
1213         _FireEtwMulticoreJit(W("PLAYER"), W("Header"), hr, m_headerModuleCount, header.methodCount);
1214     }
1215
1216
1217     return hr;
1218 }
1219
1220
1221 HRESULT MulticoreJitProfilePlayer::PlayProfile()
1222 {
1223     STANDARD_VM_CONTRACT;
1224     
1225     HRESULT hr = S_OK;
1226
1227     DWORD start = GetTickCount();
1228
1229     Thread * pThread = GetThread();
1230
1231     {
1232         // 1 marks background thread
1233         FireEtwThreadCreated((ULONGLONG) pThread, (ULONGLONG) GetAppDomain(), 1, pThread->GetThreadId(), pThread->GetOSThreadId(), GetClrInstanceId());
1234     }
1235
1236     const BYTE * pBuffer = m_pFileBuffer;
1237
1238     unsigned nSize = m_nFileSize;
1239
1240     MulticoreJitTrace(("PlayProfile %d bytes in (%s)", 
1241         nSize,
1242         GetAppDomain()->GetFriendlyNameForLogging()));
1243
1244     while ((SUCCEEDED(hr)) && (nSize > sizeof(unsigned)))
1245     {
1246         unsigned data   = * (const unsigned *) pBuffer;
1247         unsigned rcdLen = data & 0xFFFFFF;
1248         unsigned rcdTyp = data >> 24;
1249
1250         if ((rcdLen > nSize) || (rcdLen & 3)) // Better DWORD align
1251         {
1252             hr = COR_E_BADIMAGEFORMAT;
1253         }
1254         else
1255         {
1256             if (rcdTyp == MULTICOREJIT_MODULE_RECORD_ID)
1257             {
1258                 const ModuleRecord * pRec = (const ModuleRecord * ) pBuffer;
1259
1260                 if (((unsigned)(pRec->lenModuleName
1261                     + pRec->lenAssemblyName
1262                     ) > (rcdLen - sizeof(ModuleRecord))) || 
1263                     (m_moduleCount >= m_headerModuleCount))
1264                 {
1265                     hr = COR_E_BADIMAGEFORMAT;
1266                 }
1267                 else
1268                 {
1269                     hr = HandleModuleRecord(pRec);
1270                 }
1271             }
1272             else if (rcdTyp == MULTICOREJIT_JITINF_RECORD_ID)
1273             {
1274                 int mCount = (rcdLen - sizeof(unsigned)) / sizeof(unsigned);
1275
1276                 hr = HandleMethodRecord((unsigned *) (pBuffer + sizeof(unsigned)), mCount);
1277             }
1278             else
1279             {
1280                 hr = COR_E_BADIMAGEFORMAT;
1281             }
1282
1283             pBuffer += rcdLen;
1284             nSize -= rcdLen;
1285         }
1286
1287         if (SUCCEEDED(hr) && ShouldAbort(false))
1288         {
1289             hr = E_ABORT;
1290         }
1291     }
1292
1293     start = GetTickCount() - start;
1294
1295     {
1296         FireEtwThreadTerminated((ULONGLONG) pThread, (ULONGLONG) GetAppDomain(), GetClrInstanceId());
1297     }
1298
1299     MulticoreJitTrace(("Background thread running for %d ms, %d methods, hr=%x", start, m_stats.m_nTotalMethod, hr));
1300
1301     TraceSummary();
1302
1303     return hr;
1304 }
1305
1306
1307 HRESULT MulticoreJitProfilePlayer::JITThreadProc(Thread * pThread)
1308 {
1309     CONTRACTL
1310     {
1311         NOTHROW;
1312         GC_TRIGGERS;
1313         MODE_COOPERATIVE;
1314         INJECT_FAULT(COMPlusThrowOM(););
1315     }
1316     CONTRACTL_END;
1317
1318     m_stats.m_hr = S_OK;
1319
1320     EX_TRY
1321     {
1322         {
1323             // Go into preemptive mode
1324             GCX_PREEMP();
1325
1326             m_stats.m_hr = PlayProfile();
1327         }
1328     }
1329     EX_CATCH
1330     {
1331         if (SUCCEEDED(m_stats.m_hr))
1332         {
1333             m_stats.m_hr = COR_E_EXCEPTION;
1334         }
1335     }
1336     EX_END_CATCH(SwallowAllExceptions);
1337
1338     return (DWORD) m_stats.m_hr;
1339 }
1340
1341
1342 DWORD WINAPI MulticoreJitProfilePlayer::StaticJITThreadProc(void *args)
1343 {
1344     CONTRACTL
1345     {
1346         NOTHROW;
1347         GC_TRIGGERS;
1348         MODE_ANY;
1349         ENTRY_POINT;
1350         INJECT_FAULT(COMPlusThrowOM(););
1351     }
1352     CONTRACTL_END;
1353
1354     HRESULT hr = S_OK;
1355     
1356     BEGIN_ENTRYPOINT_NOTHROW;
1357
1358     MulticoreJitTrace(("StaticJITThreadProc starting"));
1359
1360     // Mark the background thread via an ETW event for diagnostics.
1361     _FireEtwMulticoreJit(W("JITTHREAD"), W(""), 0, 0, 0);
1362
1363     MulticoreJitProfilePlayer * pPlayer =  (MulticoreJitProfilePlayer *) args;
1364     
1365     if (pPlayer != NULL)
1366     {
1367         Thread * pThread = pPlayer->m_pThread;
1368
1369         if ((pThread != NULL) && pThread->HasStarted())
1370         {
1371             // Disable calling managed code in background thread
1372             ThreadStateNCStackHolder holder(TRUE, Thread::TSNC_CallingManagedCodeDisabled);
1373             
1374             // Run as background thread, so ThreadStore::WaitForOtherThreads will not wait for it
1375             pThread->SetBackground(TRUE);
1376
1377             hr = pPlayer->JITThreadProc(pThread);
1378         }
1379
1380         // It needs to be deleted after GCX_PREEMP ends
1381         if (pThread != NULL)
1382         {
1383             DestroyThread(pThread);
1384         }
1385
1386         // The background thread is reponsible for deleting the MulticoreJitProfilePlayer object once it's started
1387         // Actually after Thread::StartThread succeeds
1388         delete pPlayer;
1389     }
1390     
1391     MulticoreJitTrace(("StaticJITThreadProc endding(%x)", hr));
1392
1393     END_ENTRYPOINT_NOTHROW;
1394
1395     return (DWORD) hr;
1396 }
1397
1398
1399 HRESULT MulticoreJitProfilePlayer::ProcessProfile(const wchar_t * pFileName)
1400 {
1401     STANDARD_VM_CONTRACT;
1402
1403     HRESULT hr = ReadCheckFile(pFileName);
1404
1405     if (SUCCEEDED(hr))
1406     {
1407         _ASSERTE(m_pThread == NULL);
1408
1409         m_pThread = SetupUnstartedThread();
1410
1411         _ASSERTE(m_pThread != NULL);
1412
1413         if (m_pThread->CreateNewThread(0, StaticJITThreadProc, this))
1414         {
1415             int t = (int) m_pThread->StartThread();
1416
1417             if (t > 0)
1418             {
1419                 hr = S_OK;
1420             }
1421         }
1422     }
1423
1424     return hr;
1425 }
1426
1427