Skip to content

Commit

Permalink
Generate NGen symbols in parallel
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Apr 5, 2021
1 parent 6b4771e commit 259be61
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 51 deletions.
91 changes: 43 additions & 48 deletions src/TraceEvent/Symbols/SymbolReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public string FindSymbolFilePathForModule(string dllFilePath, bool ilPDB = false
else
{
m_log.WriteLine("FindSymbolFilePathForModule: Could not find PDB for NGEN image, Trying to generate it.");
ret = GenerateNGenSymbolsForModule(Path.GetFullPath(dllFilePath));
ret = GenerateNGenSymbolsForModule(Path.GetFullPath(dllFilePath), m_log, SymbolPath, SymbolCacheDirectory);
}
}
m_log.WriteLine("FindSymbolFilePathForModule returns {0} for {1} {2} {3} {4}", ret ?? "NULL", pdbName, pdbGuid, pdbAge, fileVersionString ?? "NULL");
Expand Down Expand Up @@ -514,16 +514,11 @@ public SymbolReaderOptions Options
///
/// <returns>The full path name of the PDB generated for the NGEN image.</returns>
/// </summary>
public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outputDirectory = null)
public static string GenerateNGenSymbolsForModule(string ngenImageFullPath, TextWriter log, string symbolPath, string outputDirectory)
{
if (outputDirectory == null)
{
outputDirectory = SymbolCacheDirectory;
}

if (!File.Exists(ngenImageFullPath))
{
m_log.WriteLine("Warning, NGEN image does not exist: {0}", ngenImageFullPath);
log.WriteLine("Warning, NGEN image does not exist: {0}", ngenImageFullPath);
return null;
}

Expand All @@ -534,7 +529,7 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp
{
if (!peFile.GetPdbSignature(out pdbFileName, out pdbGuid, out pdbAge, true))
{
m_log.WriteLine("Could not get PDB signature for {0}", ngenImageFullPath);
log.WriteLine("Could not get PDB signature for {0}", ngenImageFullPath);
return null;
}
}
Expand All @@ -552,22 +547,22 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp
// We only handle cases where we generate NGEN pdbs.
if (!pdbPath.EndsWith(".ni.pdb", StringComparison.OrdinalIgnoreCase))
{
m_log.WriteLine("Pdb does not have .ni.pdb suffix");
log.WriteLine("Pdb does not have .ni.pdb suffix");
return null;
}

string privateRuntimeVerString;
var clrDir = GetClrDirectoryForNGenImage(ngenImageFullPath, m_log, out privateRuntimeVerString);
var clrDir = GetClrDirectoryForNGenImage(ngenImageFullPath, log, out privateRuntimeVerString);
if (clrDir == null)
{
m_log.WriteLine("Could not find CLR directory for NGEN image {0}, Trying .NET Core", ngenImageFullPath);
return HandleNetCorePdbs(ngenImageFullPath, pdbPath);
log.WriteLine("Could not find CLR directory for NGEN image {0}, Trying .NET Core", ngenImageFullPath);
return HandleNetCorePdbs(ngenImageFullPath, pdbPath, log);
}

// See if this is a V4.5 CLR, if so we can do line numbers too.l
var lineNumberArg = "";
var ngenexe = Path.Combine(clrDir, "ngen.exe");
m_log.WriteLine("Checking for V4.5 for NGEN image {0}", ngenexe);
log.WriteLine("Checking for V4.5 for NGEN image {0}", ngenexe);
if (!File.Exists(ngenexe))
{
return null;
Expand All @@ -582,7 +577,7 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp
if (fileVersionInfo != null)
{
var clrFileVersion = fileVersionInfo.FileVersion;
m_log.WriteLine("Got NGEN image file version number: {0}", clrFileVersion);
log.WriteLine("Got NGEN image file version number: {0}", clrFileVersion);

m = Regex.Match(clrFileVersion, @"(\d+).(\d+)((\d|\.)*)");
if (m.Success)
Expand All @@ -592,7 +587,7 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp
var majorMinor = majorVersion * 10 + minorVersion;
if (majorMinor >= 46)
{
m_log.WriteLine("Is a V4.6 or beyond");
log.WriteLine("Is a V4.6 or beyond");
isV4_5Runtime = true;
}
else if (majorMinor == 40)
Expand All @@ -609,8 +604,8 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp
}

var options = new CommandOptions();
options.AddEnvironmentVariable("_NT_SYMBOL_PATH", SymbolPath);
options.AddOutputStream(m_log);
options.AddEnvironmentVariable("_NT_SYMBOL_PATH", symbolPath);
options.AddOutputStream(log);
options.AddNoThrow();

options.AddEnvironmentVariable("COMPLUS_NGenEnableCreatePdb", "1");
Expand All @@ -633,12 +628,12 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp
m = Regex.Match(ngenImageFullPath, @"(.*)\\Microsoft\\CLR_v(\d+)\.\d+(_(\d\d))?\\NativeImages", RegexOptions.IgnoreCase);
if (m.Success)
{
tempDir = Path.Combine(m.Groups[1].Value, @"Temp\NGenPdb");
tempDir = Path.Combine(Path.GetTempPath(), "NGenPdb", Path.GetRandomFileName());
DirectoryUtilities.Clean(tempDir);
Directory.CreateDirectory(tempDir);
ngenOutputDirectory = tempDir;
outputPdbPath = Path.Combine(tempDir, relDirPath, pdbFileName);
m_log.WriteLine("Updating NGEN createPdb output file to {0}", outputPdbPath); // TODO FIX NOW REMOVE (for debugging)
log.WriteLine("Updating NGEN createPdb output file to {0}", outputPdbPath); // TODO FIX NOW REMOVE (for debugging)
}

// TODO: Hack. V4.6.1 has both these characteristics, which leads to the issue
Expand All @@ -647,7 +642,7 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp
// Should be fixed by 12/2015
if (isV4_5Runtime)
{
InsurePathIsInNIC(m_log, ref ngenImageFullPath);
InsurePathIsInNIC(log, ref ngenImageFullPath);
}

try
Expand All @@ -656,8 +651,8 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp
{
if (!string.IsNullOrEmpty(privateRuntimeVerString))
{
m_log.WriteLine("Ngen will run for private runtime ", privateRuntimeVerString);
m_log.WriteLine("set COMPLUS_Version=" + privateRuntimeVerString);
log.WriteLine("Ngen will run for private runtime ", privateRuntimeVerString);
log.WriteLine("set COMPLUS_Version=" + privateRuntimeVerString);
options.AddEnvironmentVariable("COMPLUS_Version", privateRuntimeVerString);
}
// TODO FIX NOW: there is a and ugly problem with persistence of suboptimal PDB files
Expand All @@ -668,17 +663,17 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp
var cmdLine = string.Format(@"{0}\ngen.exe createpdb {1} {2} {3}",
clrDir, Command.Quote(ngenImageFullPath), Command.Quote(ngenOutputDirectory), lineNumberArg);
// TODO FIX NOW REMOVE after V4.5 is out a while
m_log.WriteLine("set COMPLUS_NGenEnableCreatePdb=1");
log.WriteLine("set COMPLUS_NGenEnableCreatePdb=1");
if (!isV4_5Runtime)
{
m_log.WriteLine("set COMPLUS_NGenLocalWorker=1");
log.WriteLine("set COMPLUS_NGenLocalWorker=1");
}

m_log.WriteLine("set PATH=" + newPath);
m_log.WriteLine("set _NT_SYMBOL_PATH={0}", SymbolPath);
m_log.WriteLine("*** NGEN CREATEPDB cmdline: {0}\r\n", cmdLine);
log.WriteLine("set PATH=" + newPath);
log.WriteLine("set _NT_SYMBOL_PATH={0}", symbolPath);
log.WriteLine("*** NGEN CREATEPDB cmdline: {0}\r\n", cmdLine);
var cmd = Command.Run(cmdLine, options);
m_log.WriteLine("*** NGEN CREATEPDB returns: {0}", cmd.ExitCode);
log.WriteLine("*** NGEN CREATEPDB returns: {0}", cmd.ExitCode);

if (cmd.ExitCode != 0)
{
Expand All @@ -691,15 +686,15 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp
// We may have failed because we could not get the PDB.
if (lineNumberArg.Length != 0)
{
m_log.WriteLine("Ngen failed to generate pdb for {0}, trying again without /lines", ngenImageFullPath);
log.WriteLine("Ngen failed to generate pdb for {0}, trying again without /lines", ngenImageFullPath);
lineNumberArg = "";
continue;
}
}

if (cmd.ExitCode != 0 || !File.Exists(outputPdbPath))
{
m_log.WriteLine("ngen failed to generate pdb for {0} at expected location {1}", ngenImageFullPath, outputPdbPath);
log.WriteLine("ngen failed to generate pdb for {0} at expected location {1}", ngenImageFullPath, outputPdbPath);
return null;
}

Expand Down Expand Up @@ -729,23 +724,23 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp
///
/// TODO can be removed when we properly publish the NGEN pdbs as part of build.
/// </summary>
private string HandleNetCorePdbs(string ngenImageFullPath, string pdbPath)
private static string HandleNetCorePdbs(string ngenImageFullPath, string pdbPath, TextWriter log)
{
// We only handle NGEN PDB.
if (!pdbPath.EndsWith(".ni.pdb", StringComparison.OrdinalIgnoreCase))
{
m_log.WriteLine("Not a crossGen PDB {0}", pdbPath);
log.WriteLine("Not a crossGen PDB {0}", pdbPath);
return null;
}

var ngenImageDir = Path.GetDirectoryName(ngenImageFullPath);
var pdbDir = Path.GetDirectoryName(pdbPath);

// We need Crossgen, and there are several options, see what we can do.
string crossGen = GetCrossGenExePath(ngenImageFullPath);
string crossGen = GetCrossGenExePath(ngenImageFullPath, log);
if (crossGen == null)
{
m_log.WriteLine("Could not find Crossgen.exe to generate PDBs, giving up.");
log.WriteLine("Could not find Crossgen.exe to generate PDBs, giving up.");
return null;
}

Expand Down Expand Up @@ -775,17 +770,17 @@ private string HandleNetCorePdbs(string ngenImageFullPath, string pdbPath)
" " + Command.Quote(crossGenInputName);

var options = new CommandOptions();
options.AddOutputStream(m_log);
options.AddOutputStream(log);
options.AddNoThrow();

// Needs diasymreader.dll to be on the path.
var newPath = winDir + @"\Microsoft.NET\Framework\v4.0.30319" + ";" +
winDir + @"\Microsoft.NET\Framework64\v4.0.30319" + ";%PATH%";
options.AddEnvironmentVariable("PATH", newPath);
options.AddCurrentDirectory(ngenImageDir);
m_log.WriteLine("**** Running CrossGen");
m_log.WriteLine("set PATH=" + newPath);
m_log.WriteLine("{0}\r\n", cmdLine);
log.WriteLine("**** Running CrossGen");
log.WriteLine("set PATH=" + newPath);
log.WriteLine("{0}\r\n", cmdLine);
var cmd = Command.Run(cmdLine, options);

// Delete the temporary file if necessary
Expand All @@ -796,7 +791,7 @@ private string HandleNetCorePdbs(string ngenImageFullPath, string pdbPath)

if (cmd.ExitCode != 0 || !File.Exists(pdbPath))
{
m_log.WriteLine("CrossGen failed to generate {0} exit code {0}", pdbPath, cmd.ExitCode);
log.WriteLine("CrossGen failed to generate {0} exit code {0}", pdbPath, cmd.ExitCode);
return null;
}

Expand Down Expand Up @@ -826,12 +821,12 @@ private static string getNugetPackageDir()
return nugetPackageDir;
}

private string GetCrossGenExePath(string ngenImageFullPath)
private static string GetCrossGenExePath(string ngenImageFullPath, TextWriter log)
{
var imageDir = Path.GetDirectoryName(ngenImageFullPath);
string crossGen = Path.Combine(imageDir, "crossGen.exe");

m_log.WriteLine("Checking for CoreCLR case, looking for CrossGen at {0}", crossGen);
log.WriteLine("Checking for CoreCLR case, looking for CrossGen at {0}", crossGen);
if (File.Exists(crossGen))
{
return crossGen;
Expand All @@ -841,11 +836,11 @@ private string GetCrossGenExePath(string ngenImageFullPath)
if (File.Exists(coreclr))
{
DateTime coreClrTimeStamp = File.GetLastWriteTimeUtc(coreclr);
m_log.WriteLine("Found coreclr: at {0}, timestamp {1}", coreclr, coreClrTimeStamp);
log.WriteLine("Found coreclr: at {0}, timestamp {1}", coreclr, coreClrTimeStamp);
string nugetDir = getNugetPackageDir();
if (nugetDir != null)
{
m_log.WriteLine("Found nuget package dir: at {0}", nugetDir);
log.WriteLine("Found nuget package dir: at {0}", nugetDir);
foreach (var runtimeDir in Directory.GetDirectories(nugetDir, "runtime.win*.microsoft.netcore.runtime.coreclr"))
{
foreach (var runtimeVersionDir in Directory.GetDirectories(runtimeDir))
Expand All @@ -854,11 +849,11 @@ private string GetCrossGenExePath(string ngenImageFullPath)
{
string packageCoreCLR = Path.Combine(osarchDir, @"native\coreclr.dll");
DateTime packageCoreClrTimeStamp = File.GetLastWriteTimeUtc(packageCoreCLR);
m_log.WriteLine("Checking timestamp of file {0} = {1}", packageCoreCLR, packageCoreClrTimeStamp);
log.WriteLine("Checking timestamp of file {0} = {1}", packageCoreCLR, packageCoreClrTimeStamp);
if (File.Exists(packageCoreCLR) && packageCoreClrTimeStamp == coreClrTimeStamp)
{
crossGen = Path.Combine(runtimeVersionDir, @"tools\crossgen.exe");
m_log.WriteLine("Found matching CoreCLR, probing for crossgen at {0}", crossGen);
log.WriteLine("Found matching CoreCLR, probing for crossgen at {0}", crossGen);
if (File.Exists(crossGen))
{
return crossGen;
Expand All @@ -881,12 +876,12 @@ private string GetCrossGenExePath(string ngenImageFullPath)
}
}

m_log.WriteLine("Could not find crossgen, giving up");
log.WriteLine("Could not find crossgen, giving up");
return null;
}

// TODO remove after 12/2015
private void InsurePathIsInNIC(TextWriter log, ref string ngenImageFullPath)
private static void InsurePathIsInNIC(TextWriter log, ref string ngenImageFullPath)
{
// We only get called if we are 4.5. or beyond, so we should have AUX files if we are in the nic.
string auxFilePath = ngenImageFullPath + ".aux";
Expand Down
34 changes: 31 additions & 3 deletions src/TraceEvent/ZippedETL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -401,17 +401,45 @@ internal static List<string> GetNGenPdbs(string etlFile, SymbolReader symbolRead
{
// Generate the NGen images for any NGEN image needing symbolic information.
var pdbFileList = new List<string>(100);
var tasks = new List<Task<Tuple<string, string, string, TimeSpan>>>();
foreach (var imageName in ETWTraceEventSource.GetModulesNeedingSymbols(etlFile))
{
var sw = Stopwatch.StartNew();
var pdbName = symbolReader.GenerateNGenSymbolsForModule(imageName);
var symbolPath = symbolReader.SymbolPath;
var symbolCacheDirectory = symbolReader.SymbolCacheDirectory;

tasks.Add(Task.Run(() =>
{
var sw = Stopwatch.StartNew();
using (var log2 = new StringWriter())
{
var pdbName = SymbolReader.GenerateNGenSymbolsForModule(imageName, log2, symbolPath, symbolCacheDirectory);
return Tuple.Create(imageName, pdbName, log2.ToString(), sw.Elapsed);
}
}));
}

Task.WaitAll(tasks.ToArray());
foreach (var task in tasks)
{
var imageName = task.Result.Item1;
var pdbName = task.Result.Item2;
var taskLog = task.Result.Item3;
var elapsed = task.Result.Item4;

if (!string.IsNullOrEmpty(taskLog))
{
symbolReader.Log.Write(taskLog);
}

if (pdbName != null)
{
pdbFileList.Add(pdbName);
log.WriteLine("Found NGEN pdb {0}", pdbName);
}
log.WriteLine("NGEN PDB creation for {0} took {1:n2} Sec", imageName, sw.Elapsed.TotalSeconds);

log.WriteLine("NGEN PDB creation for {0} took {1:n2} Sec", imageName, elapsed.TotalSeconds);
}

return pdbFileList;
}

Expand Down

0 comments on commit 259be61

Please sign in to comment.