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 // ===========================================================================
10 #if defined(FEATURE_PERFMAP) && !defined(DACCESS_COMPILE)
15 // The code addresses are actually native image offsets during crossgen. Print
16 // them as 32-bit numbers for consistent output when cross-targeting and to
17 // make the output more compact.
19 #ifdef CROSSGEN_COMPILE
20 #define FMT_CODE_ADDR "%08x"
22 #define FMT_CODE_ADDR "%p"
25 PerfMap * PerfMap::s_Current = nullptr;
26 bool PerfMap::s_ShowOptimizationTiers = false;
28 // Initialize the map for the process - called from EEStartupHelper.
29 void PerfMap::Initialize()
31 LIMITED_METHOD_CONTRACT;
33 // Only enable the map if requested.
34 if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapEnabled))
36 // Get the current process id.
37 int currentPid = GetCurrentProcessId();
40 s_Current = new PerfMap(currentPid);
42 int signalNum = (int) CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapIgnoreSignal);
46 PAL_IgnoreProfileSignal(signalNum);
49 if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapShowOptimizationTiers) != 0)
51 s_ShowOptimizationTiers = true;
56 // Destroy the map for the process - called from EEShutdownHelper.
57 void PerfMap::Destroy()
59 LIMITED_METHOD_CONTRACT;
61 if (s_Current != nullptr)
68 // Construct a new map for the process.
69 PerfMap::PerfMap(int pid)
71 LIMITED_METHOD_CONTRACT;
73 // Initialize with no failures.
74 m_ErrorEncountered = false;
78 // Build the path to the map file on disk.
79 WCHAR tempPath[MAX_LONGPATH+1];
80 if(!GetTempPathW(MAX_LONGPATH, tempPath))
86 path.Printf("%Sperf-%d.map", &tempPath, pid);
88 // Open the map file for writing.
91 m_PerfInfo = new PerfInfo(pid);
94 // Construct a new map without a specified file name.
95 // Used for offline creation of NGEN map files.
97 : m_FileStream(nullptr)
100 LIMITED_METHOD_CONTRACT;
102 // Initialize with no failures.
103 m_ErrorEncountered = false;
108 // Clean-up resources.
111 LIMITED_METHOD_CONTRACT;
114 m_FileStream = nullptr;
117 m_PerfInfo = nullptr;
120 // Open the specified destination map file.
121 void PerfMap::OpenFile(SString& path)
123 STANDARD_VM_CONTRACT;
125 // Open the file stream.
126 m_FileStream = new (nothrow) CFileStream();
127 if(m_FileStream != nullptr)
129 HRESULT hr = m_FileStream->OpenForWrite(path.GetUnicode());
133 m_FileStream = nullptr;
138 // Write a line to the map file.
139 void PerfMap::WriteLine(SString& line)
141 STANDARD_VM_CONTRACT;
146 // The PAL already takes a lock when writing, so we don't need to do so here.
147 StackScratchBuffer scratch;
148 const char * strLine = line.GetANSI(scratch);
149 ULONG inCount = line.GetCount();
151 m_FileStream->Write(strLine, inCount, &outCount);
153 if (inCount != outCount)
155 // This will cause us to stop writing to the file.
156 // The file will still remain open until shutdown so that we don't have to take a lock at this level when we touch the file stream.
157 m_ErrorEncountered = true;
161 EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
164 // Log a method to the map.
165 void PerfMap::LogMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize, const char *optimizationTier)
171 PRECONDITION(pMethod != nullptr);
172 PRECONDITION(pCode != nullptr);
173 PRECONDITION(codeSize > 0);
176 if (m_FileStream == nullptr || m_ErrorEncountered)
178 // A failure occurred, do not log.
182 // Logging failures should not cause any exceptions to flow upstream.
185 // Get the full method signature.
186 SString fullMethodSignature;
187 pMethod->GetFullMethodInfo(fullMethodSignature);
189 // Build the map file line.
190 StackScratchBuffer scratch;
192 line.Printf(FMT_CODE_ADDR " %x %s", pCode, codeSize, fullMethodSignature.GetANSI(scratch));
193 if (optimizationTier != nullptr && s_ShowOptimizationTiers)
195 line.AppendPrintf("[%s]\n", optimizationTier);
199 line.Append(W('\n'));
205 EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
209 void PerfMap::LogImageLoad(PEFile * pFile)
211 if (s_Current != nullptr)
213 s_Current->LogImage(pFile);
217 // Log an image load to the map.
218 void PerfMap::LogImage(PEFile * pFile)
224 PRECONDITION(pFile != nullptr);
228 if (m_FileStream == nullptr || m_ErrorEncountered)
230 // A failure occurred, do not log.
236 WCHAR wszSignature[39];
237 GetNativeImageSignature(pFile, wszSignature, lengthof(wszSignature));
239 m_PerfInfo->LogImage(pFile, wszSignature);
241 EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
245 // Log a method to the map.
246 void PerfMap::LogJITCompiledMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize, PrepareCodeConfig *pConfig)
248 LIMITED_METHOD_CONTRACT;
250 if (s_Current == nullptr)
255 const char *optimizationTier = nullptr;
256 #ifndef CROSSGEN_COMPILE
257 if (s_ShowOptimizationTiers)
259 optimizationTier = PrepareCodeConfig::GetJitOptimizationTierStr(pConfig, pMethod);
261 #endif // CROSSGEN_COMPILE
263 s_Current->LogMethod(pMethod, pCode, codeSize, optimizationTier);
266 // Log a set of stub to the map.
267 void PerfMap::LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, size_t codeSize)
269 LIMITED_METHOD_CONTRACT;
271 if (s_Current == nullptr || s_Current->m_FileStream == nullptr)
276 // Logging failures should not cause any exceptions to flow upstream.
288 // Build the map file line.
290 line.Printf(FMT_CODE_ADDR " %x stub<%d> %s<%s>\n", pCode, codeSize, ++(s_Current->m_StubsMapped), stubType, stubOwner);
293 s_Current->WriteLine(line);
295 EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
298 void PerfMap::GetNativeImageSignature(PEFile * pFile, WCHAR * pwszSig, unsigned int nSigSize)
301 PRECONDITION(pFile != nullptr);
302 PRECONDITION(pwszSig != nullptr);
303 PRECONDITION(nSigSize >= 39);
306 // We use the MVID as the signature, since ready to run images
307 // don't have a native image signature.
309 pFile->GetMVID(&mvid);
310 if(!StringFromGUID2(mvid, pwszSig, nSigSize))
316 // Create a new native image perf map.
317 NativeImagePerfMap::NativeImagePerfMap(Assembly * pAssembly, BSTR pDestPath)
320 STANDARD_VM_CONTRACT;
322 // Generate perfmap path.
324 // Get the assembly simple name.
325 LPCUTF8 lpcSimpleName = pAssembly->GetSimpleName();
327 // Get the native image signature (GUID).
328 // Used to ensure that we match symbols to the correct NGEN image.
329 WCHAR wszSignature[39];
330 GetNativeImageSignature(pAssembly->GetManifestFile(), wszSignature, lengthof(wszSignature));
332 // Build the path to the perfmap file, which consists of <inputpath><imagesimplename>.ni.<signature>.map.
333 // Example: /tmp/mscorlib.ni.{GUID}.map
334 SString sDestPerfMapPath;
335 sDestPerfMapPath.Printf("%S%s.ni.%S.map", pDestPath, lpcSimpleName, wszSignature);
337 // Open the perf map file.
338 OpenFile(sDestPerfMapPath);
340 // Determine whether to emit RVAs or file offsets based on the specified configuration.
342 CLRConfigStringHolder wszFormat(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_NativeImagePerfMapFormat));
343 if(wszFormat != NULL && (wcsncmp(wszFormat, strOFFSET, wcslen(strOFFSET)) == 0))
349 // Log data to the perfmap for the specified module.
350 void NativeImagePerfMap::LogDataForModule(Module * pModule)
352 STANDARD_VM_CONTRACT;
354 PEImageLayout * pLoadedLayout = pModule->GetFile()->GetLoaded();
355 _ASSERTE(pLoadedLayout != nullptr);
357 #ifdef FEATURE_PREJIT
358 if (!pLoadedLayout->HasReadyToRunHeader())
360 MethodIterator mi((PTR_Module)pModule);
363 MethodDesc *hotDesc = mi.GetMethodDesc();
364 hotDesc->CheckRestore();
366 LogPreCompiledMethod(hotDesc, mi.GetMethodStartAddress(), pLoadedLayout, nullptr);
372 ReadyToRunInfo::MethodIterator mi(pModule->GetReadyToRunInfo());
375 MethodDesc* hotDesc = mi.GetMethodDesc();
377 LogPreCompiledMethod(hotDesc, mi.GetMethodStartAddress(), pLoadedLayout, "ReadyToRun");
381 // Log a pre-compiled method to the perfmap.
382 void NativeImagePerfMap::LogPreCompiledMethod(MethodDesc * pMethod, PCODE pCode, PEImageLayout *pLoadedLayout, const char *optimizationTier)
384 STANDARD_VM_CONTRACT;
386 _ASSERTE(pLoadedLayout != nullptr);
387 SIZE_T baseAddr = (SIZE_T)pLoadedLayout->GetBase();
389 // Get information about the NGEN'd method code.
390 EECodeInfo codeInfo(pCode);
391 _ASSERTE(codeInfo.IsValid());
393 IJitManager::MethodRegionInfo methodRegionInfo;
394 codeInfo.GetMethodRegionInfo(&methodRegionInfo);
396 // NGEN can split code between hot and cold sections which are separate in memory.
397 // Emit an entry for each section if it is used.
399 if (methodRegionInfo.hotSize > 0)
401 addr = (PCODE)methodRegionInfo.hotStartAddress - baseAddr;
404 addr = pLoadedLayout->RvaToOffset(addr);
406 LogMethod(pMethod, addr, methodRegionInfo.hotSize, optimizationTier);
409 if (methodRegionInfo.coldSize > 0)
411 addr = (PCODE)methodRegionInfo.coldStartAddress - baseAddr;
414 addr = pLoadedLayout->RvaToOffset(addr);
416 LogMethod(pMethod, addr, methodRegionInfo.coldSize, optimizationTier);
420 #endif // FEATURE_PERFMAP && !DACCESS_COMPILE