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: MultiCoreJIT.cpp
6//
7
8// ===========================================================================
9// This file contains the implementation for MultiCore JIT (player in a seperate file MultiCoreJITPlayer.cpp)
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
25#include "appdomain.hpp"
26#include "qcall.h"
27
28#include "eventtracebase.h"
29#include "multicorejit.h"
30#include "multicorejitimpl.h"
31
32const wchar_t * AppxProfile = W("Application.Profile");
33
34
35
36void MulticoreJitFireEtw(const wchar_t * pAction, const wchar_t * pTarget, int p1, int p2, int p3)
37{
38 LIMITED_METHOD_CONTRACT
39
40 FireEtwMulticoreJit(GetClrInstanceId(), pAction, pTarget, p1, p2, p3);
41}
42
43
44void MulticoreJitFireEtwA(const wchar_t * pAction, const char * pTarget, int p1, int p2, int p3)
45{
46 CONTRACTL {
47 NOTHROW;
48 GC_NOTRIGGER;
49 } CONTRACTL_END;
50
51#ifdef FEATURE_EVENT_TRACE
52 EX_TRY
53 {
54 if (EventEnabledMulticoreJit())
55 {
56 SString wTarget;
57
58 wTarget.SetUTF8(pTarget);
59
60 FireEtwMulticoreJit(GetClrInstanceId(), pAction, wTarget.GetUnicode(), p1, p2, p3);
61 }
62 }
63 EX_CATCH
64 { }
65 EX_END_CATCH(SwallowAllExceptions);
66#endif // FEATURE_EVENT_TRACE
67}
68
69void MulticoreJitFireEtwMethodCodeReturned(MethodDesc * pMethod)
70{
71 CONTRACTL {
72 NOTHROW;
73 GC_NOTRIGGER;
74 } CONTRACTL_END;
75
76 EX_TRY
77 {
78 if(pMethod)
79 {
80 // Get the module id.
81 Module * pModule = pMethod->GetModule_NoLogging();
82 ULONGLONG ullModuleID = (ULONGLONG)(TADDR) pModule;
83
84 // Get the method id.
85 ULONGLONG ullMethodID = (ULONGLONG)pMethod;
86
87 // Fire the event.
88 FireEtwMulticoreJitMethodCodeReturned(GetClrInstanceId(), ullModuleID, ullMethodID);
89 }
90 }
91 EX_CATCH
92 { }
93 EX_END_CATCH(SwallowAllExceptions);
94}
95
96#ifdef MULTICOREJIT_LOGGING
97
98// %s ANSI
99// %S UNICODE
100void _MulticoreJitTrace(const char * format, ...)
101{
102 static unsigned s_startTick = 0;
103
104 WRAPPER_NO_CONTRACT;
105
106 if (s_startTick == 0)
107 {
108 s_startTick = GetTickCount();
109 }
110
111 va_list args;
112 va_start(args, format);
113
114#ifdef LOGGING
115 LogSpew2 (LF2_MULTICOREJIT, LL_INFO100, "Mcj ");
116 LogSpew2Valist(LF2_MULTICOREJIT, LL_INFO100, format, args);
117 LogSpew2 (LF2_MULTICOREJIT, LL_INFO100, ", (time=%d ms)\n", GetTickCount() - s_startTick);
118#else
119
120 // Following LogSpewValist(DWORD facility, DWORD level, const char *fmt, va_list args)
121 char buffer[512];
122
123 int len;
124
125 len = sprintf_s(buffer, _countof(buffer), "Mcj TID %04x: ", GetCurrentThreadId());
126 len += _vsnprintf_s(buffer + len, _countof(buffer) - len, format, args);
127 len += sprintf_s(buffer + len, _countof(buffer) - len, ", (time=%d ms)\r\n", GetTickCount() - s_startTick);
128
129 OutputDebugStringA(buffer);
130#endif
131
132 va_end(args);
133
134}
135
136#endif
137
138
139HRESULT MulticoreJitRecorder::WriteOutput()
140{
141 CONTRACTL
142 {
143 NOTHROW;
144 GC_TRIGGERS;
145 MODE_ANY; // Called from AppDomain::Stop which is MODE_ANY
146 CAN_TAKE_LOCK;
147 }
148 CONTRACTL_END;
149
150 HRESULT hr = E_FAIL;
151
152 // Go into preemptive mode for file operations
153 GCX_PREEMP();
154
155 {
156 CFileStream fileStream;
157
158 if (SUCCEEDED(hr = fileStream.OpenForWrite(m_fullFileName)))
159 {
160 hr = WriteOutput(& fileStream);
161 }
162 }
163
164 return hr;
165}
166
167
168HRESULT WriteData(IStream * pStream, const void * pData, unsigned len)
169{
170 CONTRACTL
171 {
172 NOTHROW;
173 GC_NOTRIGGER;
174 MODE_PREEMPTIVE;
175 }
176 CONTRACTL_END
177
178 ULONG cbWritten;
179
180 HRESULT hr = pStream->Write(pData, len, & cbWritten);
181
182 if (SUCCEEDED(hr) && (cbWritten != len))
183 {
184 hr = E_FAIL;
185 }
186
187 return hr;
188}
189
190// Write string, round to to DWORD alignment
191HRESULT WriteString(const void * pString, unsigned len, IStream * pStream)
192{
193 CONTRACTL
194 {
195 NOTHROW;
196 GC_NOTRIGGER;
197 MODE_PREEMPTIVE;
198 }
199 CONTRACTL_END;
200
201 ULONG cbWritten = 0;
202
203 HRESULT hr;
204
205 hr = pStream->Write(pString, len, & cbWritten);
206
207 if (SUCCEEDED(hr))
208 {
209 len = RoundUp(len) - len;
210
211 if (len != 0)
212 {
213 cbWritten = 0;
214
215 hr = pStream->Write(& cbWritten, len, & cbWritten);
216 }
217 }
218
219 return hr;
220}
221
222
223//static
224FileLoadLevel MulticoreJitManager::GetModuleFileLoadLevel(Module * pModule)
225{
226 CONTRACTL
227 {
228 NOTHROW;
229 GC_NOTRIGGER;
230 }
231 CONTRACTL_END;
232
233 FileLoadLevel level = FILE_LOAD_CREATE; // min level
234
235 if (pModule != NULL)
236 {
237 DomainFile * pDomainFile = pModule->FindDomainFile(GetAppDomain());
238
239 if (pDomainFile != NULL)
240 {
241 level = pDomainFile->GetLoadLevel();
242 }
243 }
244
245 return level;
246}
247
248
249bool ModuleVersion::GetModuleVersion(Module * pModule)
250{
251 STANDARD_VM_CONTRACT;
252
253 HRESULT hr = E_FAIL;
254
255 // GetMVID can throw exception
256 EX_TRY
257 {
258 PEFile * pFile = pModule->GetFile();
259
260 if (pFile != NULL)
261 {
262 PEAssembly * pAsm = pFile->GetAssembly();
263
264 if (pAsm != NULL)
265 {
266 // CorAssemblyFlags, only 16-bit used
267 versionFlags = pAsm->GetFlags();
268
269 _ASSERTE((versionFlags & 0x80000000) == 0);
270
271 if (pFile->HasNativeImage())
272 {
273 hasNativeImage = 1;
274 }
275
276 pAsm->GetVersion(& major, & minor, & build, & revision);
277
278 pAsm->GetMVID(& mvid);
279
280 hr = S_OK;
281 }
282 }
283
284 // If the load context is LOADFROM, store it in the flags.
285 }
286 EX_CATCH
287 {
288 hr = E_FAIL;
289 }
290 EX_END_CATCH(SwallowAllExceptions);
291
292 return SUCCEEDED(hr);
293}
294
295ModuleRecord::ModuleRecord(unsigned lenName, unsigned lenAsmName)
296{
297 LIMITED_METHOD_CONTRACT;
298
299 memset(this, 0, sizeof(ModuleRecord));
300
301 recordID = Pack8_24(MULTICOREJIT_MODULE_RECORD_ID, sizeof(ModuleRecord));
302
303 wLoadLevel = 0;
304 // Extra data
305 lenModuleName = (unsigned short) lenName;
306 lenAssemblyName = (unsigned short) lenAsmName;
307 recordID += RoundUp(lenModuleName) + RoundUp(lenAssemblyName);
308}
309
310
311bool RecorderModuleInfo::SetModule(Module * pMod)
312{
313 STANDARD_VM_CONTRACT;
314
315 pModule = pMod;
316
317 LPCUTF8 pModuleName = pMod->GetSimpleName();
318 unsigned lenModuleName = (unsigned) strlen(pModuleName);
319 simpleName.Set((const BYTE *) pModuleName, lenModuleName); // SBuffer::Set copies over name
320
321 SString sAssemblyName;
322 StackScratchBuffer scratch;
323 pMod->GetAssembly()->GetManifestFile()->GetDisplayName(sAssemblyName);
324
325 LPCUTF8 pAssemblyName = sAssemblyName.GetUTF8(scratch);
326 unsigned lenAssemblyName = sAssemblyName.GetCount();
327 assemblyName.Set((const BYTE *) pAssemblyName, lenAssemblyName);
328
329
330 return moduleVersion.GetModuleVersion(pMod);
331}
332
333
334
335/////////////////////////////////////////////////////
336//
337// class MulticoreJitRecorder
338//
339/////////////////////////////////////////////////////
340
341HRESULT MulticoreJitRecorder::WriteModuleRecord(IStream * pStream, const RecorderModuleInfo & module)
342{
343 CONTRACTL
344 {
345 NOTHROW;
346 GC_NOTRIGGER;
347 MODE_PREEMPTIVE;
348 CAN_TAKE_LOCK;
349 }
350 CONTRACTL_END;
351
352 HRESULT hr;
353
354 const void * pModuleName = module.simpleName;
355 unsigned lenModuleName = module.simpleName.GetSize();
356
357 const void * pAssemblyName = module.assemblyName;
358 unsigned lenAssemblyName = module.assemblyName.GetSize();
359
360 ModuleRecord mod(lenModuleName, lenAssemblyName);
361
362 mod.version = module.moduleVersion;
363 mod.jitMethodCount = module.methodCount;
364 mod.wLoadLevel = (unsigned short) module.loadLevel;
365 mod.flags = module.flags;
366
367 hr = WriteData(pStream, & mod, sizeof(mod));
368
369 if (SUCCEEDED(hr))
370 {
371 hr = WriteString(pModuleName, lenModuleName, pStream);
372
373 if (SUCCEEDED(hr))
374 {
375 hr = WriteString(pAssemblyName, lenAssemblyName, pStream);
376 }
377 }
378
379 return hr;
380}
381
382
383HRESULT MulticoreJitRecorder::WriteOutput(IStream * pStream)
384{
385 CONTRACTL
386 {
387 NOTHROW;
388 GC_NOTRIGGER;
389 MODE_PREEMPTIVE;
390 CAN_TAKE_LOCK;
391 }
392 CONTRACTL_END;
393
394 HRESULT hr = S_OK;
395
396 {
397 HeaderRecord header;
398
399 memset(&header, 0, sizeof(header));
400
401 header.recordID = Pack8_24(MULTICOREJIT_HEADER_RECORD_ID, sizeof(HeaderRecord));
402 header.version = MULTICOREJIT_PROFILE_VERSION;
403 header.moduleCount = m_ModuleCount;
404 header.methodCount = m_JitInfoCount - m_ModuleDepCount;
405 header.moduleDepCount = m_ModuleDepCount;
406
407 MulticoreJitCodeStorage & curStorage = m_pDomain->GetMulticoreJitManager().GetMulticoreJitCodeStorage();
408
409 // Stats about played profile, 14 short, 3 long = 40 bytes
410 header.shortCounters[ 0] = m_stats.m_nTotalMethod;
411 header.shortCounters[ 1] = m_stats.m_nHasNativeCode;
412 header.shortCounters[ 2] = m_stats.m_nTryCompiling;
413 header.shortCounters[ 3] = (unsigned short) curStorage.GetStored();
414 header.shortCounters[ 4] = (unsigned short) curStorage.GetReturned();
415 header.shortCounters[ 5] = m_stats.m_nFilteredMethods;
416 header.shortCounters[ 6] = m_stats.m_nMissingModuleSkip;
417 header.shortCounters[ 7] = m_stats.m_nTotalDelay;
418 header.shortCounters[ 8] = m_stats.m_nDelayCount;
419 header.shortCounters[ 9] = m_stats.m_nWalkBack;
420 header.shortCounters[10] = m_fAppxMode;
421
422 _ASSERTE(HEADER_W_COUNTER >= 14);
423
424 header.longCounters[0] = m_stats.m_hr;
425
426 _ASSERTE(HEADER_D_COUNTER >= 3);
427
428 _ASSERTE((sizeof(header) % sizeof(unsigned)) == 0);
429
430 hr = WriteData(pStream, & header, sizeof(header));
431 }
432
433 DWORD dwData = 0;
434
435 for (unsigned i = 0; SUCCEEDED(hr) && (i < m_ModuleCount); i ++)
436 {
437 hr = WriteModuleRecord(pStream, m_ModuleList[i]);
438 }
439
440 if (SUCCEEDED(hr))
441 {
442 unsigned remain = m_JitInfoCount;
443
444 const unsigned * pInfo = m_JitInfoArray;
445
446 while (SUCCEEDED(hr) && (remain > 0))
447 {
448 unsigned count = remain;
449
450 if (count > MAX_JIT_COUNT)
451 {
452 count = MAX_JIT_COUNT;
453 }
454
455 dwData = Pack8_24(MULTICOREJIT_JITINF_RECORD_ID, count * sizeof(DWORD) + sizeof(DWORD));
456
457 hr = WriteData(pStream, & dwData, sizeof(dwData));
458
459 if (SUCCEEDED(hr))
460 {
461 hr = WriteData(pStream, pInfo, sizeof(unsigned) * count);
462 }
463
464 pInfo += count;
465 remain -= count;
466 }
467 }
468
469 MulticoreJitTrace(("New profile: %d modules, %d methods", m_ModuleCount, m_JitInfoCount));
470
471 _FireEtwMulticoreJit(W("WRITEPROFILE"), m_fullFileName.GetUnicode(), m_ModuleCount, m_JitInfoCount, 0);
472
473 return hr;
474}
475
476
477unsigned MulticoreJitRecorder::FindModule(Module * pModule)
478{
479 LIMITED_METHOD_CONTRACT;
480
481 for (unsigned i = 0 ; i < m_ModuleCount; i ++)
482 {
483 if (m_ModuleList[i].pModule == pModule)
484 {
485 return i;
486 }
487 }
488
489 return UINT_MAX;
490}
491
492
493// Find known module index, or add to module table
494// Return UINT_MAX when table is full, or SetModule fails
495unsigned MulticoreJitRecorder::GetModuleIndex(Module * pModule)
496{
497 STANDARD_VM_CONTRACT;
498
499 unsigned slot = FindModule(pModule);
500
501 if ((slot == UINT_MAX) && (m_ModuleCount < MAX_MODULES))
502 {
503 slot = m_ModuleCount ++;
504
505 if (! m_ModuleList[slot].SetModule(pModule))
506 {
507 return UINT_MAX;
508 }
509 }
510
511 return slot;
512}
513
514
515void MulticoreJitRecorder::RecordJitInfo(unsigned module, unsigned method)
516{
517 LIMITED_METHOD_CONTRACT;
518
519 if (m_JitInfoCount < (LONG) MAX_METHOD_ARRAY)
520 {
521 unsigned info1 = Pack8_24(module, method & 0xFFFFFF);
522
523 // Due to incremental loading, there are quite a few RecordModuleLoad coming with increasing load level, merge
524
525 // Previous record and current record are both MODULE_DEPENDENCY
526 if ((m_JitInfoCount > 0) && (info1 & MODULE_DEPENDENCY))
527 {
528 unsigned info0 = m_JitInfoArray[m_JitInfoCount - 1];
529
530 if ((info0 & 0xFFFF00FF) == (info1 & 0xFFFF00FF)) // to/from modules are the same
531 {
532 if (info1 > info0) // higher level
533 {
534 m_JitInfoArray[m_JitInfoCount - 1] = info1; // replace
535 }
536
537 return; // no new record
538 }
539 }
540
541 if (method & MODULE_DEPENDENCY)
542 {
543 m_ModuleDepCount ++;
544 }
545 else
546 {
547 m_ModuleList[module].methodCount ++;
548 }
549
550 m_JitInfoArray[m_JitInfoCount] = info1;
551 m_JitInfoCount ++;
552 }
553}
554
555class MulticoreJitRecorderModuleEnumerator : public MulticoreJitModuleEnumerator
556{
557 MulticoreJitRecorder * m_pRecorder;
558 bool m_fAppxMode;
559
560 HRESULT OnModule(Module * pModule)
561 {
562 CONTRACTL
563 {
564 THROWS;
565 GC_TRIGGERS;
566 MODE_PREEMPTIVE;
567 CAN_TAKE_LOCK;
568 }
569 CONTRACTL_END;
570
571 if (MulticoreJitManager::IsSupportedModule(pModule, false, m_fAppxMode))
572 {
573 m_pRecorder->AddModuleDependency(pModule, MulticoreJitManager::GetModuleFileLoadLevel(pModule));
574 }
575
576 return S_OK;
577 }
578
579public:
580 MulticoreJitRecorderModuleEnumerator(MulticoreJitRecorder * pRecorder, bool fAppxMode)
581 {
582 m_pRecorder = pRecorder;
583 m_fAppxMode = fAppxMode;
584 }
585};
586
587
588// The whole AppDomain is depending on pModule
589void MulticoreJitRecorder::AddModuleDependency(Module * pModule, FileLoadLevel loadLevel)
590{
591 STANDARD_VM_CONTRACT;
592
593 MulticoreJitTrace(("AddModuleDependency(%s, %d)", pModule->GetSimpleName(), loadLevel));
594
595 _FireEtwMulticoreJitA(W("ADDMODULEDEPENDENCY"), pModule->GetSimpleName(), loadLevel, 0, 0);
596
597 unsigned moduleTo = GetModuleIndex(pModule);
598
599 if (moduleTo != UINT_MAX)
600 {
601 if (m_ModuleList[moduleTo].loadLevel < loadLevel)
602 {
603 m_ModuleList[moduleTo].loadLevel = loadLevel;
604
605 // Update load level
606 RecordJitInfo(0, ((unsigned) loadLevel << 8) | moduleTo | MODULE_DEPENDENCY);
607 }
608 }
609}
610
611
612// Enumerate all modules within an assembly, call OnModule virtual method
613HRESULT MulticoreJitModuleEnumerator::HandleAssembly(DomainAssembly * pAssembly)
614{
615 STANDARD_VM_CONTRACT;
616
617 DomainAssembly::ModuleIterator modIt = pAssembly->IterateModules(kModIterIncludeLoaded);
618
619 HRESULT hr = S_OK;
620
621 while (modIt.Next() && SUCCEEDED(hr))
622 {
623 Module * pModule = modIt.GetModule();
624
625 if (pModule != NULL)
626 {
627 hr = OnModule(pModule);
628 }
629 }
630
631 return hr;
632}
633
634
635// Enum all loaded modules within pDomain, call OnModule virtual method
636HRESULT MulticoreJitModuleEnumerator::EnumerateLoadedModules(AppDomain * pDomain)
637{
638 STANDARD_VM_CONTRACT;
639
640 HRESULT hr = S_OK;
641
642 AppDomain::AssemblyIterator appIt = pDomain->IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution));
643
644 CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
645
646 while (appIt.Next(pDomainAssembly.This()) && SUCCEEDED(hr))
647 {
648 {
649 hr = HandleAssembly(pDomainAssembly);
650 }
651 }
652
653 return hr;
654}
655
656
657
658// static: single instace within a process
659
660#ifndef FEATURE_PAL
661TP_TIMER * MulticoreJitRecorder::s_delayedWriteTimer; // = NULL;
662
663// static
664void CALLBACK
665MulticoreJitRecorder::WriteMulticoreJitProfiler(PTP_CALLBACK_INSTANCE pInstance, PVOID pvContext, PTP_TIMER pTimer)
666{
667 CONTRACTL
668 {
669 THROWS;
670 GC_TRIGGERS;
671 } CONTRACTL_END;
672
673 // Avoid saving after MulticoreJitRecorder is deleted, and saving twice
674 if (! CloseTimer())
675 {
676 return;
677 }
678
679 MulticoreJitRecorder * pRecorder = (MulticoreJitRecorder *) pvContext;
680
681 if (pRecorder != NULL)
682 {
683 {
684 pRecorder->StopProfile(false);
685 }
686 }
687}
688
689#endif // !FEATURE_PAL
690
691void MulticoreJitRecorder::PreRecordFirstMethod()
692{
693 STANDARD_VM_CONTRACT;
694
695 // When first method is added to an AppDomain, add all currently loaded modules as dependent modules
696
697 m_fFirstMethod = false;
698
699 {
700 MulticoreJitRecorderModuleEnumerator enumerator(this, m_fAppxMode);
701
702 enumerator.EnumerateLoadedModules(m_pDomain);
703 }
704
705 // When running under Appx or CoreCLR for K, AppDomain is normally not shut down properly (CLR in hybrid case, or Alt-F4 shutdown),
706 // So we only allow writing out after profileWriteTimeout seconds
707 {
708 // Get the timeout in seconds.
709 int profileWriteTimeout = (int)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MultiCoreJitProfileWriteDelay);
710
711#ifndef FEATURE_PAL
712 // Using the same threadpool timer used by UsageLog to write out profile when running under Appx or CoreCLR.
713 s_delayedWriteTimer = CreateThreadpoolTimer(WriteMulticoreJitProfiler, this, NULL);
714
715 if (s_delayedWriteTimer != NULL)
716 {
717 ULARGE_INTEGER msDelay;
718
719 // SetThreadpoolTimer needs delay to be given in 100 ns unit, negative
720 msDelay.QuadPart = (ULONGLONG) -(profileWriteTimeout * 10 * 1000 * 1000);
721 FILETIME ftDueTime;
722 ftDueTime.dwLowDateTime = msDelay.u.LowPart;
723 ftDueTime.dwHighDateTime = msDelay.u.HighPart;
724
725 // This will either set the timer to happen in profileWriteTimeout seconds, or reset the timer so the same will happen.
726 // This function is safe to call
727 SetThreadpoolTimer(s_delayedWriteTimer, &ftDueTime, 0, 2000 /* large 2000 ms window for executing this timer is acceptable as the timing here is very much not critical */);
728 }
729#endif // !FEATURE_PAL
730 }
731}
732
733
734void MulticoreJitRecorder::RecordMethodJit(MethodDesc * pMethod, bool application)
735{
736 STANDARD_VM_CONTRACT;
737
738 Module * pModule = pMethod->GetModule_NoLogging();
739
740 // Skip methods from non-supported modules
741 if (! MulticoreJitManager::IsSupportedModule(pModule, true, m_fAppxMode))
742 {
743 return;
744 }
745
746 // pModule could be unknown at this point (modules not enumerated, no event received yet)
747 unsigned moduleIndex = GetModuleIndex(pModule);
748
749 if (moduleIndex < UINT_MAX)
750 {
751 if (m_fFirstMethod)
752 {
753 PreRecordFirstMethod();
754 }
755
756 // Make sure level for current module is recorded properly
757 if (m_ModuleList[moduleIndex].loadLevel != FILE_ACTIVE)
758 {
759 FileLoadLevel needLevel = MulticoreJitManager::GetModuleFileLoadLevel(pModule);
760
761 if (m_ModuleList[moduleIndex].loadLevel < needLevel)
762 {
763 m_ModuleList[moduleIndex].loadLevel = needLevel;
764
765 // Update load level
766 RecordJitInfo(0, ((unsigned) needLevel << 8) | moduleIndex | MODULE_DEPENDENCY);
767 }
768 }
769
770 unsigned methodIndex = pMethod->GetMemberDef_NoLogging() & 0xFFFFFF;
771
772 if (methodIndex <= METHODINDEX_MASK)
773 {
774 if (application) // Jitted by application threads, not background thread
775 {
776 methodIndex |= JIT_BY_APP_THREAD;
777 }
778
779 RecordJitInfo(moduleIndex, methodIndex);
780 }
781 }
782}
783
784
785// Called from AppDomain::RaiseAssemblyResolveEvent, make it simple
786
787void MulticoreJitRecorder::AbortProfile()
788{
789 LIMITED_METHOD_CONTRACT;
790
791 // Increment session ID tells background thread to stop
792 m_pDomain->GetMulticoreJitManager().GetProfileSession().Increment();
793
794 m_fAborted = true; // Do not save output when StopProfile is called
795}
796
797
798HRESULT MulticoreJitRecorder::StopProfile(bool appDomainShutdown)
799{
800 CONTRACTL
801 {
802 CAN_TAKE_LOCK;
803 }
804 CONTRACTL_END;
805
806 HRESULT hr = S_OK;
807
808 // Increment session ID tells background thread to stop
809 MulticoreJitManager & manager = m_pDomain->GetMulticoreJitManager();
810
811 manager.GetProfileSession().Increment();
812
813 if (! m_fAborted && ! m_fullFileName.IsEmpty())
814 {
815 hr = WriteOutput();
816 }
817
818 MulticoreJitTrace(("StopProfile: Save new profile to %S, hr=0x%x", m_fullFileName.GetUnicode(), hr));
819
820 return hr;
821}
822
823
824// suffix (>= 0) is used for AutoStartProfile, to support multiple AppDomains. It's set to -1 for normal API call path
825HRESULT MulticoreJitRecorder::StartProfile(const wchar_t * pRoot, const wchar_t * pFile, int suffix, LONG nSession)
826{
827 STANDARD_VM_CONTRACT;
828
829 HRESULT hr = S_FALSE;
830
831 if ((pRoot == NULL) || (pFile == NULL))
832 {
833 return E_INVALIDARG;
834 }
835
836 MulticoreJitTrace(("StartProfile('%S', '%S', %d)", pRoot, pFile, suffix));
837
838 size_t lenFile = wcslen(pFile);
839
840 // Options (only AutoStartProfile using environment variable, for testing)
841 // ([d|D]main-thread-delay)
842 if ((suffix >= 0) && (lenFile >= 3) && (pFile[0]=='('))// AutoStartProfile, using environment variable
843 {
844 pFile ++;
845 lenFile --;
846
847 while ((lenFile > 0) && isalpha(pFile[0]))
848 {
849 switch (pFile[0])
850 {
851 case 'd':
852 case 'D':
853 g_MulticoreJitEnabled = false;
854
855 default:
856 break;
857 }
858
859 pFile ++;
860 lenFile --;
861 }
862
863 if ((lenFile > 0) && isdigit(* pFile))
864 {
865 g_MulticoreJitDelay = 0;
866
867 while ((lenFile > 0) && isdigit(* pFile))
868 {
869 g_MulticoreJitDelay = g_MulticoreJitDelay * 10 + (int) (* pFile - '0');
870
871 pFile ++;
872 lenFile --;
873 }
874 }
875
876 // End of options
877 if ((lenFile > 0) && (* pFile == ')'))
878 {
879 pFile ++;
880 lenFile --;
881 }
882 }
883
884 MulticoreJitTrace(("g_MulticoreJitEnabled = %d, disable/enable Mcj feature", g_MulticoreJitEnabled));
885
886 if (g_MulticoreJitEnabled && (lenFile > 0))
887 {
888 m_fullFileName = pRoot;
889
890 // Append seperator if root does not end with one
891 unsigned len = m_fullFileName.GetCount();
892
893 if ((len != 0) && (m_fullFileName[len - 1] != '\\'))
894 {
895 m_fullFileName.Append('\\');
896 }
897
898 m_fullFileName.Append(pFile);
899
900 // Suffix for AutoStartProfile, used for multiple appdomain
901 if (suffix >= 0)
902 {
903 m_fullFileName.AppendPrintf(W("_%s_%s_%d.prof"),
904 SystemDomain::System()->DefaultDomain()->GetFriendlyName(),
905 m_pDomain->GetFriendlyName(),
906 suffix);
907 }
908
909 NewHolder<MulticoreJitProfilePlayer> player(new (nothrow) MulticoreJitProfilePlayer(
910 m_pDomain,
911 m_pBinderContext,
912 nSession,
913 m_fAppxMode));
914
915 if (player == NULL)
916 {
917 hr = E_OUTOFMEMORY;
918 }
919 else
920 {
921 HRESULT hr1 = S_OK;
922
923 EX_TRY
924 {
925 hr1 = player->ProcessProfile(m_fullFileName);
926 }
927 EX_CATCH_HRESULT(hr1);
928
929 // If ProcessProfile succeeds, the background thread is responsible for deleting it when it finishes; otherwise, delete now
930 if (SUCCEEDED(hr1))
931 {
932 if (g_MulticoreJitDelay > 0)
933 {
934 MulticoreJitTrace(("Delay main thread %d ms", g_MulticoreJitDelay));
935
936 ClrSleepEx(g_MulticoreJitDelay, FALSE);
937 }
938
939 player.SuppressRelease();
940 }
941
942 MulticoreJitTrace(("ProcessProfile('%S') returns %x", m_fullFileName.GetUnicode(), hr1));
943
944 // Ignore error, even when we can't play back the file, we can still record new one
945
946 // If file exists, but profile header can't be read, pass error to caller (ignored by caller for non Appx)
947 if (hr1 == COR_E_BADIMAGEFORMAT)
948 {
949 hr = hr1;
950 }
951 }
952 }
953
954 MulticoreJitTrace(("StartProfile('%S', '%S', %d) returns %x", pRoot, pFile, suffix, hr));
955
956 _FireEtwMulticoreJit(W("STARTPROFILE"), m_fullFileName.GetUnicode(), hr, 0, 0);
957
958 return hr;
959}
960
961
962// Module load call back, record new module information, update play-back module list
963void MulticoreJitRecorder::RecordModuleLoad(Module * pModule, FileLoadLevel loadLevel)
964{
965 STANDARD_VM_CONTRACT;
966
967 if (pModule != NULL)
968 {
969 if (! m_fFirstMethod) // If m_fFirstMethod flag is still on, defer calling AddModuleDependency until first method JIT
970 {
971 AddModuleDependency(pModule, loadLevel);
972 }
973 }
974}
975
976
977// Call back from MethodDesc::MakeJitWorker for
978PCODE MulticoreJitRecorder::RequestMethodCode(MethodDesc * pMethod, MulticoreJitManager * pManager)
979{
980 STANDARD_VM_CONTRACT;
981
982 // Disable it when profiler is running
983
984#ifdef PROFILING_SUPPORTED
985
986 _ASSERTE(! CORProfilerTrackJITInfo());
987
988#endif
989
990 _ASSERTE(! pMethod->IsDynamicMethod());
991
992 PCODE pCode = NULL;
993
994 pCode = pManager->GetMulticoreJitCodeStorage().QueryMethodCode(pMethod, TRUE);
995
996 if ((pCode != NULL) && pManager->IsRecorderActive()) // recorder may be off when player is on (e.g. for Appx)
997 {
998 RecordMethodJit(pMethod, false); // JITTed by background thread, returned to application
999 }
1000
1001 return pCode;
1002}
1003
1004
1005//////////////////////////////////////////////////////////
1006//
1007// class MulticoreJitManager: attachment to AppDomain
1008//
1009//
1010//////////////////////////////////////////////////////////
1011
1012
1013// API Function: SettProfileRoot, store information with MulticoreJitManager class
1014// Threading: protected by InterlockedExchange(m_fMulticoreJITEnabled)
1015
1016void MulticoreJitManager::SetProfileRoot(AppDomain * pDomain, const wchar_t * pProfilePath)
1017{
1018 STANDARD_VM_CONTRACT;
1019
1020#ifdef PROFILING_SUPPORTED
1021
1022 if (CORProfilerTrackJITInfo())
1023 {
1024 return;
1025 }
1026
1027#endif
1028
1029 if (g_SystemInfo.dwNumberOfProcessors >= 2)
1030 {
1031 if (InterlockedCompareExchange(& m_fSetProfileRootCalled, SETPROFILEROOTCALLED, 0) == 0) // Only allow the first call per appdomain
1032 {
1033 m_profileRoot = pProfilePath;
1034 }
1035 }
1036}
1037
1038
1039// API Function: StartProfile
1040// Threading: protected by m_playerLock
1041void MulticoreJitManager::StartProfile(AppDomain * pDomain, ICLRPrivBinder *pBinderContext, const wchar_t * pProfile, int suffix)
1042{
1043 CONTRACTL
1044 {
1045 THROWS;
1046 MODE_PREEMPTIVE;
1047 INJECT_FAULT(COMPlusThrowOM(););
1048 CAN_TAKE_LOCK;
1049 }
1050 CONTRACTL_END;
1051
1052 if (m_fSetProfileRootCalled != SETPROFILEROOTCALLED)
1053 {
1054 MulticoreJitTrace(("StartProfile fail: SetProfileRoot not called/failed"));
1055 _FireEtwMulticoreJit(W("STARTPROFILE"), W("No SetProfileRoot"), 0, 0, 0);
1056 return;
1057 }
1058
1059 // Need extra processor for multicore JIT feature
1060 _ASSERTE(g_SystemInfo.dwNumberOfProcessors >= 2);
1061
1062#ifdef PROFILING_SUPPORTED
1063
1064 if (CORProfilerTrackJITInfo())
1065 {
1066 MulticoreJitTrace(("StartProfile fail: CORProfilerTrackJITInfo on"));
1067 _FireEtwMulticoreJit(W("STARTPROFILE"), W("Profiling On"), 0, 0, 0);
1068 return;
1069 }
1070
1071#endif
1072 CrstHolder hold(& m_playerLock);
1073
1074 // Stop current profiling first, delete current m_pMulticoreJitRecorder if any
1075 StopProfile(false);
1076
1077 if ((pProfile != NULL) && (pProfile[0] != 0)) // Ignore empty file name, just same as StopProfile
1078 {
1079 MulticoreJitRecorder * pRecorder = new (nothrow) MulticoreJitRecorder(
1080 pDomain,
1081 pBinderContext,
1082 m_fAppxMode);
1083
1084 if (pRecorder != NULL)
1085 {
1086 m_pMulticoreJitRecorder = pRecorder;
1087
1088 LONG sessionID = m_ProfileSession.Increment();
1089
1090 HRESULT hr = m_pMulticoreJitRecorder->StartProfile(m_profileRoot, pProfile, suffix, sessionID);
1091
1092 MulticoreJitTrace(("MulticoreJitRecorder session %d created: %x", sessionID, hr));
1093
1094 if (m_fAppxMode) // In Appx mode, recorder is only enabled when file exists, but header is bad (e.g. zero-length)
1095 {
1096 if (hr == COR_E_BADIMAGEFORMAT)
1097 {
1098 m_fRecorderActive = true;
1099 }
1100 }
1101 else if ((hr == COR_E_BADIMAGEFORMAT) || SUCCEEDED(hr)) // Otherwise, ignore COR_E_BADIMAGEFORMAT, alway record new profile
1102 {
1103 m_fRecorderActive = true;
1104 }
1105
1106 _FireEtwMulticoreJit(W("STARTPROFILE"), W("Recorder"), m_fRecorderActive, hr, 0);
1107 }
1108 }
1109}
1110
1111
1112// Threading: protected by m_playerLock
1113void MulticoreJitManager::AbortProfile()
1114{
1115 CONTRACTL
1116 {
1117 NOTHROW;
1118 CAN_TAKE_LOCK;
1119 }
1120 CONTRACTL_END;
1121
1122 if (m_fSetProfileRootCalled != SETPROFILEROOTCALLED)
1123 {
1124 return;
1125 }
1126
1127 CrstHolder hold(& m_playerLock);
1128
1129 if (m_pMulticoreJitRecorder != NULL)
1130 {
1131 MulticoreJitTrace(("AbortProfile"));
1132
1133 _FireEtwMulticoreJit(W("ABORTPROFILE"), W(""), 0, 0, 0);
1134
1135 m_fRecorderActive = false;
1136
1137 m_pMulticoreJitRecorder->AbortProfile();
1138 }
1139
1140 // Disable the feature within the AppDomain
1141 m_fSetProfileRootCalled = -1;
1142}
1143
1144
1145// Stop current profiling, could be called automatically from AppDomain shut down
1146// Threading: protected by m_playerLock
1147void MulticoreJitManager::StopProfile(bool appDomainShutdown)
1148{
1149 CONTRACTL
1150 {
1151 NOTHROW;
1152 CAN_TAKE_LOCK;
1153 }
1154 CONTRACTL_END;
1155
1156 if (m_fSetProfileRootCalled != SETPROFILEROOTCALLED)
1157 {
1158 return;
1159 }
1160
1161 MulticoreJitRecorder * pRecorder;
1162
1163 if (appDomainShutdown)
1164 {
1165 // In the app domain shut down code path, need to hold m_playerLock critical section to wait for other thread to finish using recorder
1166 CrstHolder hold(& m_playerLock);
1167
1168 pRecorder = InterlockedExchangeT(& m_pMulticoreJitRecorder, NULL);
1169 }
1170 else
1171 {
1172 // When called from StartProfile, should not take critical section because it's already entered
1173
1174 pRecorder = InterlockedExchangeT(& m_pMulticoreJitRecorder, NULL);
1175 }
1176
1177 if (pRecorder != NULL)
1178 {
1179 m_fRecorderActive = false;
1180
1181 EX_TRY
1182 {
1183 pRecorder->StopProfile(appDomainShutdown);
1184 }
1185 EX_CATCH
1186 {
1187 MulticoreJitTrace(("StopProfile(%d) throws exception", appDomainShutdown));
1188 }
1189 EX_END_CATCH(SwallowAllExceptions);
1190
1191 delete pRecorder;
1192 }
1193
1194 MulticoreJitTrace(("StopProfile(%d) returns", appDomainShutdown));
1195}
1196
1197
1198LONG g_nMulticoreAutoStart = 0;
1199
1200// Threading: calls into StartProfile
1201void MulticoreJitManager::AutoStartProfile(AppDomain * pDomain)
1202{
1203 CONTRACTL
1204 {
1205 THROWS;
1206 GC_TRIGGERS;
1207 MODE_PREEMPTIVE;
1208 INJECT_FAULT(COMPlusThrowOM(););
1209 }
1210 CONTRACTL_END;
1211
1212 CLRConfigStringHolder wszProfile(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MultiCoreJitProfile));
1213
1214 if ((wszProfile != NULL) && (wszProfile[0] != 0))
1215 {
1216 int suffix = (int) InterlockedIncrement(& g_nMulticoreAutoStart);
1217
1218 SetProfileRoot(pDomain, W("")); // Fake a SetProfileRoot call
1219
1220 StartProfile(
1221 pDomain,
1222 NULL,
1223 wszProfile,
1224 suffix);
1225 }
1226}
1227
1228
1229// Constructor
1230
1231MulticoreJitManager::MulticoreJitManager()
1232{
1233 CONTRACTL
1234 {
1235 THROWS;
1236 }
1237 CONTRACTL_END;
1238
1239 m_pMulticoreJitRecorder = NULL;
1240 m_fSetProfileRootCalled = 0;
1241 m_fAutoStartCalled = 0;
1242 m_fRecorderActive = false;
1243 m_fAppxMode = false;
1244
1245 m_playerLock.Init(CrstMulticoreJitManager, (CrstFlags)(CRST_TAKEN_DURING_SHUTDOWN));
1246 m_MulticoreJitCodeStorage.Init();
1247}
1248
1249
1250// Threading: uses Release to free object
1251MulticoreJitManager::~MulticoreJitManager()
1252{
1253 LIMITED_METHOD_CONTRACT;
1254
1255 if (m_pMulticoreJitRecorder != NULL)
1256 {
1257 delete m_pMulticoreJitRecorder;
1258
1259 m_pMulticoreJitRecorder = NULL;
1260 }
1261
1262 m_playerLock.Destroy();
1263}
1264
1265
1266// Threading: proected by m_playerLock
1267
1268void MulticoreJitManager::RecordModuleLoad(Module * pModule, FileLoadLevel loadLevel)
1269{
1270 STANDARD_VM_CONTRACT;
1271
1272
1273
1274 if (m_fRecorderActive)
1275 {
1276 if(IsSupportedModule(pModule, false, m_fAppxMode)) // Filter out unsupported module
1277 {
1278 CrstHolder hold(& m_playerLock);
1279
1280 if (m_pMulticoreJitRecorder != NULL)
1281 {
1282 m_pMulticoreJitRecorder->RecordModuleLoad(pModule, loadLevel);
1283 }
1284 }
1285 else
1286 {
1287 _FireEtwMulticoreJitA(W("UNSUPPORTEDMODULE"), pModule->GetSimpleName(), 0, 0, 0);
1288 }
1289 }
1290}
1291
1292
1293// Call back from MethodDesc::MakeJitWorker for
1294// Threading: proected by m_playerLock
1295
1296PCODE MulticoreJitManager::RequestMethodCode(MethodDesc * pMethod)
1297{
1298 STANDARD_VM_CONTRACT;
1299
1300 CrstHolder hold(& m_playerLock);
1301
1302 if (m_pMulticoreJitRecorder != NULL)
1303 {
1304 PCODE requestedCode = m_pMulticoreJitRecorder->RequestMethodCode(pMethod, this);
1305 if(requestedCode)
1306 {
1307 _FireEtwMulticoreJitMethodCodeReturned(pMethod);
1308 }
1309
1310 return requestedCode;
1311 }
1312
1313 return NULL;
1314}
1315
1316
1317// Call back from MethodDesc::MakeJitWorker for
1318// Threading: proected by m_playerLock
1319
1320void MulticoreJitManager::RecordMethodJit(MethodDesc * pMethod)
1321{
1322 STANDARD_VM_CONTRACT;
1323
1324 CrstHolder hold(& m_playerLock);
1325
1326 if (m_pMulticoreJitRecorder != NULL)
1327 {
1328 m_pMulticoreJitRecorder->RecordMethodJit(pMethod, true);
1329
1330 if (m_pMulticoreJitRecorder->IsAtFullCapacity())
1331 {
1332 m_fRecorderActive = false;
1333 }
1334 }
1335}
1336
1337
1338// static
1339bool MulticoreJitManager::IsMethodSupported(MethodDesc * pMethod)
1340{
1341 CONTRACTL
1342 {
1343 NOTHROW;
1344 GC_NOTRIGGER;
1345 MODE_ANY;
1346 }
1347 CONTRACTL_END;
1348
1349 return pMethod->HasILHeader() &&
1350 pMethod->IsTypicalSharedInstantiation() &&
1351 ! pMethod->IsDynamicMethod();
1352}
1353
1354
1355// static
1356// Stop all multicore Jitting profile, called from EEShutDown
1357void MulticoreJitManager::StopProfileAll()
1358{
1359 CONTRACTL
1360 {
1361 NOTHROW;
1362 CAN_TAKE_LOCK;
1363 }
1364 CONTRACTL_END;
1365
1366 _ASSERTE(!AppX::IsAppXProcess());
1367
1368 AppDomainIterator domain(TRUE);
1369
1370 while (domain.Next())
1371 {
1372 AppDomain * pDomain = domain.GetDomain();
1373
1374 if (pDomain != NULL)
1375 {
1376 pDomain->GetMulticoreJitManager().StopProfile(true);
1377 }
1378 }
1379}
1380
1381// static
1382// Stop all multicore Jitting in the current process, called from ProfilingAPIUtility::LoadProfiler
1383void MulticoreJitManager::DisableMulticoreJit()
1384{
1385 CONTRACTL
1386 {
1387 NOTHROW;
1388 CAN_TAKE_LOCK;
1389 }
1390 CONTRACTL_END;
1391
1392#ifdef PROFILING_SUPPORTED
1393
1394 AppDomainIterator domain(TRUE);
1395
1396 while (domain.Next())
1397 {
1398 AppDomain * pDomain = domain.GetDomain();
1399
1400 if (pDomain != NULL)
1401 {
1402 pDomain->GetMulticoreJitManager().AbortProfile();
1403 }
1404 }
1405
1406#endif
1407}
1408
1409
1410//---------------------------------------------------------------------------------------
1411//
1412// MultiCore JIT
1413//
1414// Arguments:
1415// wszProfile - profile name
1416// ptrNativeAssemblyLoadContext - the binding context
1417//
1418void QCALLTYPE MultiCoreJITNative::InternalStartProfile(__in_z LPCWSTR wszProfile, INT_PTR ptrNativeAssemblyLoadContext)
1419{
1420 QCALL_CONTRACT;
1421
1422 BEGIN_QCALL;
1423
1424 AppDomain * pDomain = GetAppDomain();
1425
1426 ICLRPrivBinder *pBinderContext = reinterpret_cast<ICLRPrivBinder *>(ptrNativeAssemblyLoadContext);
1427
1428 pDomain->GetMulticoreJitManager().StartProfile(
1429 pDomain,
1430 pBinderContext,
1431 wszProfile);
1432
1433 END_QCALL;
1434}
1435
1436
1437void QCALLTYPE MultiCoreJITNative::InternalSetProfileRoot(__in_z LPCWSTR wszProfilePath)
1438{
1439 QCALL_CONTRACT;
1440
1441 BEGIN_QCALL;
1442
1443 AppDomain * pDomain = GetAppDomain();
1444
1445 pDomain->GetMulticoreJitManager().SetProfileRoot(pDomain, wszProfilePath);
1446
1447 END_QCALL;
1448}
1449