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: MultiCoreJITImpl.h
6//
7
8//
9// Multicore JIT internal implementation header file
10//
11// ======================================================================================
12
13#ifdef _DEBUG
14
15#define MULTICOREJIT_LOGGING
16
17#else
18// Enable direct logging through OutputDebugMessage in ret build for perf investigation, to be disabled when checkin
19
20// #define MULTICOREJIT_LOGGING
21
22#endif
23
24// Make sure a record can fit within 2048 bytes, 511 methods now
25
26const int MAX_RECORD_SIZE = 2048;
27const unsigned MAX_JIT_COUNT = (MAX_RECORD_SIZE - sizeof(unsigned)) / sizeof(unsigned);
28
29const int HEADER_W_COUNTER = 14; // Extra 16-bit counters in header for statistics: 28
30const int HEADER_D_COUNTER = 3; // Extra 32-bit counters in header for statistics: 12
31const unsigned MAX_MODULES = 512; // Maximum number of modules
32
33const unsigned MAX_METHOD_ARRAY = 16384; // Maximum number of methods
34
35const int MULTICOREJITLIFE = 60 * 1000; // 60 seconds
36
37const int MULTICOREJITBLOCKLIMIT = 10 * 1000; // 10 seconds
38
39 // 8-bit module index
40
41 // Method JIT information: 8-bit module 4-bit flag 20-bit method index
42const unsigned MODULE_DEPENDENCY = 0x800000; // 1-bit module dependency mask
43const unsigned JIT_BY_APP_THREAD = 0x400000; // 1-bit application thread
44
45const unsigned METHODINDEX_MASK = 0x0FFFFF; // 20-bit method index
46
47 // Dependendy information: 8-bit module 4-bit flag 4-bit unused 8-bit level 8-bit module
48const unsigned LEVEL_SHIFT = 8;
49const unsigned LEVEL_MASK = 0xFF; // 8-bit file load level
50const unsigned MODULE_MASK = 0xFF; // 8-bit dependent module index
51
52const int MAX_WALKBACK = 128;
53
54enum
55{
56 MULTICOREJIT_PROFILE_VERSION = 101,
57
58 MULTICOREJIT_HEADER_RECORD_ID = 1,
59 MULTICOREJIT_MODULE_RECORD_ID = 2,
60 MULTICOREJIT_JITINF_RECORD_ID = 3
61};
62
63
64inline unsigned Pack8_24(unsigned up, unsigned low)
65{
66 LIMITED_METHOD_CONTRACT;
67
68 return (up << 24) + low;
69}
70
71// Multicore JIT profile format
72
73// <profile>::= <HeaderRecord> { <ModuleRecord> | <JitInfRecord> }
74//
75// 1. Each record is DWORD aligned
76// 2. Each record starts with a DWORD <recordID> with Pack8_24(record type, record size)
77// 3. Counter are just statistical information gathed (mainly during play back), good for quick diagnosis, not used to guide playback
78// 4 Maximum number of modules supported is 256
79// 5 Simple module name stored
80// 6 Maximum method index: 20-bit, could extend to 22 bits
81// 7 JIT_BY_APP_THREAD is for diagnosis only
82
83// <HeaderRecord>::= <recordID> <version> <timeStamp> <moduleCount> <methodCount> <DependencyCount> <unsigned short counter>*14 <unsigned counter>*3
84// <ModuleRecord>::= <recordID> <ModuleVersion> <JitMethodCount> <loadLevel> <lenModuleName> char*lenModuleName <padding>
85// <JifInfRecord>::= <recordID> { <moduleDependency> | <methodJitInfo> }
86
87// <moduleDependency>::
88// 8-bit source module index, current always 0 until we track per module dependency
89// 8-bit flag MODULE_DEPENDENCY is 1
90// 8-bit load level
91// 8-bit target module index
92
93// <methodJitInfo>::
94// 8-bit module index, current always 0 until we track per module dependency
95// 4-bit flag MODULE_DEPENDENCY is 0, JIT_BY_APP_THREAD could be 1
96// 20-bit method index
97
98
99struct HeaderRecord
100{
101 unsigned recordID;
102 unsigned version;
103 unsigned timeStamp;
104 unsigned moduleCount;
105 unsigned methodCount;
106 unsigned moduleDepCount;
107 unsigned short shortCounters[HEADER_W_COUNTER];
108 unsigned longCounters [HEADER_D_COUNTER];
109};
110
111
112class ModuleVersion
113{
114public:
115 unsigned short major;
116 unsigned short minor;
117 unsigned short build;
118 unsigned short revision;
119
120 unsigned versionFlags :31;
121 unsigned hasNativeImage:1;
122
123 GUID mvid;
124
125 bool GetModuleVersion(Module * pModule);
126
127 ModuleVersion()
128 {
129 LIMITED_METHOD_CONTRACT;
130
131 memset(this, 0, sizeof(ModuleVersion));
132 }
133
134 bool MatchWith(const ModuleVersion & other) const
135 {
136 LIMITED_METHOD_CONTRACT;
137
138 if ((major == other.major) &&
139 (minor == other.minor) &&
140 (build == other.build) &&
141 (revision == other.revision) &&
142 (versionFlags == other.versionFlags))
143 {
144 return memcmp(& mvid, & other.mvid, sizeof(mvid)) == 0;
145 }
146
147 return false;
148 }
149
150 bool NativeImageFlagDiff(const ModuleVersion & other) const
151 {
152 LIMITED_METHOD_CONTRACT;
153
154 return hasNativeImage != other.hasNativeImage;
155 }
156};
157
158inline unsigned RoundUp(unsigned val)
159{
160 LIMITED_METHOD_CONTRACT;
161
162 return (val + 3) / 4 * 4;
163}
164
165const unsigned short FLAG_LOADOKAY = 1; // Okay to load the module in background thread (e.g. for Appx first party WinMD)
166
167// Used to mark a module that was loaded in the LOADFROM context.
168// First 16 bits are reserved for CorAssemblyFlags. Use the last bit (bit 31) to allow for expansion of CorAssemblyFlags.
169const unsigned int VERSIONFLAG_LOADCTX_LOADFROM = 0x40000000;
170
171// Module record stored in the profile without the name
172
173class ModuleRecord
174{
175public:
176 unsigned recordID;
177 ModuleVersion version;
178 unsigned short jitMethodCount;
179 unsigned short flags;
180 unsigned short wLoadLevel;
181 unsigned short lenModuleName;
182 unsigned short lenAssemblyName;
183
184 ModuleRecord(unsigned lenName = 0, unsigned lenAssemblyName = 0);
185
186 bool MatchWithModule(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shouldAbort, bool fAppx) const;
187
188 unsigned ModuleNameLen() const
189 {
190 LIMITED_METHOD_CONTRACT;
191
192 return lenModuleName;
193 }
194
195 const char * GetModuleName() const
196 {
197 LIMITED_METHOD_CONTRACT;
198
199 return (const char *) (this + 1); // after this record
200 }
201
202 unsigned AssemblyNameLen() const
203 {
204 LIMITED_METHOD_CONTRACT;
205
206 return lenAssemblyName;
207 }
208
209 const char * GetAssemblyName() const
210 {
211 return GetModuleName() + RoundUp(lenModuleName); // after the module name
212 }
213
214 void SetLoadLevel(FileLoadLevel loadLevel)
215 {
216 LIMITED_METHOD_CONTRACT;
217
218 wLoadLevel = (unsigned short) loadLevel;
219 }
220};
221
222
223class Module;
224class AppDomain;
225
226class PlayerModuleInfo;
227
228// Module enumerator
229class MulticoreJitModuleEnumerator
230{
231 virtual HRESULT OnModule(Module * pModule) = 0;
232
233public:
234 HRESULT EnumerateLoadedModules(AppDomain * pDomain);
235 HRESULT HandleAssembly(DomainAssembly * pAssembly);
236};
237
238
239class PlayerModuleInfo;
240
241// MulticoreJitProfilePlayer manages background thread, playing back profile, storing result into code stoage, and gather statistics information
242
243class MulticoreJitProfilePlayer
244{
245friend class MulticoreJitRecorder;
246
247private:
248 ADID m_DomainID;
249 ICLRPrivBinder * m_pBinderContext;
250 LONG m_nMySession;
251 unsigned m_nStartTime;
252 BYTE * m_pFileBuffer;
253 unsigned m_nFileSize;
254 MulticoreJitPlayerStat & m_stats;
255 MulticoreJitCounter & m_appdomainSession;
256 bool m_shouldAbort;
257 bool m_fAppxMode;
258
259 Thread * m_pThread;
260
261 unsigned m_nBlockingCount;
262 unsigned m_nMissingModule;
263
264 int m_nLoadedModuleCount;
265
266 unsigned m_busyWith;
267
268 unsigned m_headerModuleCount;
269 unsigned m_moduleCount;
270 PlayerModuleInfo * m_pModules;
271
272 void JITMethod(Module * pModule, unsigned methodIndex);
273
274 HRESULT HandleModuleRecord(const ModuleRecord * pModule);
275 HRESULT HandleMethodRecord(unsigned * buffer, int count);
276
277 bool CompileMethodDesc(Module * pModule, MethodDesc * pMD);
278
279 HRESULT PlayProfile();
280
281 bool GroupWaitForModuleLoad(int pos);
282
283 bool ShouldAbort(bool fast) const;
284
285 HRESULT JITThreadProc(Thread * pThread);
286
287 static DWORD WINAPI StaticJITThreadProc(void *args);
288
289 void TraceSummary();
290
291 HRESULT UpdateModuleInfo();
292
293 bool HandleModuleDependency(unsigned jitInfo);
294
295 HRESULT ReadCheckFile(const wchar_t * pFileName);
296
297 DomainAssembly * LoadAssembly(SString & assemblyName);
298
299public:
300
301 MulticoreJitProfilePlayer(AppDomain * pDomain, ICLRPrivBinder * pBinderContext, LONG nSession, bool fAppxMode);
302
303 ~MulticoreJitProfilePlayer();
304
305 HRESULT ProcessProfile(const wchar_t * pFileName);
306
307 HRESULT OnModule(Module * pModule);
308};
309
310
311struct RecorderModuleInfo
312{
313 Module * pModule;
314 unsigned short methodCount;
315 unsigned short flags;
316 ModuleVersion moduleVersion;
317 SBuffer simpleName;
318 SBuffer assemblyName;
319 FileLoadLevel loadLevel;
320
321 RecorderModuleInfo()
322 {
323 LIMITED_METHOD_CONTRACT;
324
325 pModule = NULL;
326 methodCount = 0;
327 flags = 0;
328 loadLevel = FILE_LOAD_CREATE;
329 }
330
331 bool SetModule(Module * pModule);
332};
333
334
335class MulticoreJitRecorder
336{
337private:
338 AppDomain * m_pDomain; // AutoStartProfile could be called from SystemDomain
339 ICLRPrivBinder * m_pBinderContext;
340 SString m_fullFileName;
341 MulticoreJitPlayerStat & m_stats;
342
343 RecorderModuleInfo m_ModuleList[MAX_MODULES];
344 unsigned m_ModuleCount;
345 unsigned m_ModuleDepCount;
346
347 unsigned m_JitInfoArray[MAX_METHOD_ARRAY];
348 LONG m_JitInfoCount;
349
350 bool m_fFirstMethod;
351 bool m_fAborted;
352 bool m_fAppxMode;
353
354#ifndef FEATURE_PAL
355 static TP_TIMER * s_delayedWriteTimer;
356#endif // !FEATURE_PAL
357
358
359 unsigned FindModule(Module * pModule);
360 unsigned GetModuleIndex(Module * pModule);
361
362 HRESULT WriteModuleRecord(IStream * pStream, const RecorderModuleInfo & module);
363
364 void RecordJitInfo(unsigned module, unsigned method);
365
366 void AddAllModulesInAsm(DomainAssembly * pAssembly);
367
368 HRESULT WriteOutput(IStream * pStream);
369
370 HRESULT WriteOutput();
371
372 void PreRecordFirstMethod();
373
374#ifndef FEATURE_PAL
375 static void CALLBACK WriteMulticoreJitProfiler(PTP_CALLBACK_INSTANCE pInstance, PVOID pvContext, PTP_TIMER pTimer);
376#endif // !FEATURE_PAL
377
378public:
379
380 MulticoreJitRecorder(AppDomain * pDomain, ICLRPrivBinder * pBinderContext, bool fAppxMode)
381 : m_stats(pDomain->GetMulticoreJitManager().GetStats())
382 {
383 LIMITED_METHOD_CONTRACT;
384
385 m_pDomain = pDomain;
386 m_pBinderContext = pBinderContext;
387 m_JitInfoCount = 0;
388 m_ModuleCount = 0;
389 m_ModuleDepCount = 0;
390
391 m_fFirstMethod = true;
392 m_fAborted = false;
393 m_fAppxMode = fAppxMode;
394
395
396 m_stats.Clear();
397 }
398
399#ifndef FEATURE_PAL
400 static bool CloseTimer()
401 {
402 LIMITED_METHOD_CONTRACT;
403
404 TP_TIMER * pTimer = InterlockedExchangeT(& s_delayedWriteTimer, NULL);
405
406 if (pTimer == NULL)
407 {
408 return false;
409 }
410
411 CloseThreadpoolTimer(pTimer);
412
413 return true;
414 }
415
416 ~MulticoreJitRecorder()
417 {
418 LIMITED_METHOD_CONTRACT;
419
420 CloseTimer();
421 }
422#endif // !FEATURE_PAL
423
424 bool IsAtFullCapacity() const
425 {
426 LIMITED_METHOD_CONTRACT;
427
428 return (m_JitInfoCount >= (LONG) MAX_METHOD_ARRAY) ||
429 (m_ModuleCount >= MAX_MODULES);
430 }
431
432 void RecordMethodJit(MethodDesc * pMethod, bool application);
433
434 PCODE RequestMethodCode(MethodDesc * pMethod, MulticoreJitManager * pManager);
435
436 HRESULT StartProfile(const wchar_t * pRoot, const wchar_t * pFileName, int suffix, LONG nSession);
437
438 HRESULT StopProfile(bool appDomainShutdown);
439
440 void AbortProfile();
441
442 void RecordModuleLoad(Module * pModule, FileLoadLevel loadLevel);
443
444 void AddModuleDependency(Module * pModule, FileLoadLevel loadLevel);
445};
446
447#ifdef MULTICOREJIT_LOGGING
448
449void _MulticoreJitTrace(const char * format, ...);
450
451#define MulticoreJitTrace(x) do { _MulticoreJitTrace x; } while (0)
452
453#else
454
455#define MulticoreJitTrace(x)
456
457#endif
458
459extern unsigned g_MulticoreJitDelay; // Delay in StartProfile
460extern bool g_MulticoreJitEnabled; // Enable/Disable feature
461
462
463inline bool PrivateEtwEnabled()
464{
465#ifdef FEATURE_EVENT_TRACE
466 return ETW_PROVIDER_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER) != 0;
467#else // FEATURE_EVENT_TRACE
468 return FALSE;
469#endif // FEATURE_EVENT_TRACE
470}
471
472void MulticoreJitFireEtw(const wchar_t * pAction, const wchar_t * pTarget, int p1, int p2, int p3);
473
474void MulticoreJitFireEtwA(const wchar_t * pAction, const char * pTarget, int p1, int p2, int p3);
475
476void MulticoreJitFireEtwMethodCodeReturned(MethodDesc * pMethod);
477
478#define _FireEtwMulticoreJit(String1, String2, Int1, Int2, Int3) if (PrivateEtwEnabled()) MulticoreJitFireEtw (String1, String2, Int1, Int2, Int3)
479#define _FireEtwMulticoreJitA(String1, String2, Int1, Int2, Int3) if (PrivateEtwEnabled()) MulticoreJitFireEtwA(String1, String2, Int1, Int2, Int3)
480#define _FireEtwMulticoreJitMethodCodeReturned(pMethod) if(PrivateEtwEnabled()) MulticoreJitFireEtwMethodCodeReturned(pMethod)
481
482