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 | |
32 | const wchar_t * AppxProfile = W("Application.Profile" ); |
33 | |
34 | |
35 | |
36 | void 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 | |
44 | void 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 | |
69 | void 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 |
100 | void _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 | |
139 | HRESULT 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 | |
168 | HRESULT 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 |
191 | HRESULT 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 |
224 | FileLoadLevel 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 | |
249 | bool 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 | |
295 | ModuleRecord::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 | |
311 | bool 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 | |
341 | HRESULT 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 | |
383 | HRESULT 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 ; |
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 | |
477 | unsigned 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 |
495 | unsigned 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 | |
515 | void 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 | |
555 | class 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 | |
579 | public: |
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 |
589 | void 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 |
613 | HRESULT 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 |
636 | HRESULT 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 |
661 | TP_TIMER * MulticoreJitRecorder::s_delayedWriteTimer; // = NULL; |
662 | |
663 | // static |
664 | void CALLBACK |
665 | MulticoreJitRecorder::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 | |
691 | void 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 | |
734 | void 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 | |
787 | void 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 | |
798 | HRESULT 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 |
825 | HRESULT 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 |
963 | void 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 |
978 | PCODE 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 | |
1016 | void 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 |
1041 | void 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 |
1113 | void 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 |
1147 | void 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 | |
1198 | LONG g_nMulticoreAutoStart = 0; |
1199 | |
1200 | // Threading: calls into StartProfile |
1201 | void 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 | |
1231 | MulticoreJitManager::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 |
1251 | MulticoreJitManager::~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 | |
1268 | void 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 | |
1296 | PCODE 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 | |
1320 | void 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 |
1339 | bool 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 |
1357 | void 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 |
1383 | void 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 | // |
1418 | void 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 | |
1437 | void 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 | |