diff --git a/src/TraceEvent/Symbols/SymbolReader.cs b/src/TraceEvent/Symbols/SymbolReader.cs index 351c6589d..a01e34f51 100644 --- a/src/TraceEvent/Symbols/SymbolReader.cs +++ b/src/TraceEvent/Symbols/SymbolReader.cs @@ -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"); @@ -514,16 +514,11 @@ public SymbolReaderOptions Options /// /// The full path name of the PDB generated for the NGEN image. /// - 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; } @@ -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; } } @@ -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; @@ -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) @@ -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) @@ -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"); @@ -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 @@ -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 @@ -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 @@ -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) { @@ -691,7 +686,7 @@ 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; } @@ -699,7 +694,7 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp 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; } @@ -729,12 +724,12 @@ public string GenerateNGenSymbolsForModule(string ngenImageFullPath, string outp /// /// TODO can be removed when we properly publish the NGEN pdbs as part of build. /// - 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; } @@ -742,10 +737,10 @@ private string HandleNetCorePdbs(string ngenImageFullPath, string pdbPath) 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; } @@ -775,7 +770,7 @@ 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. @@ -783,9 +778,9 @@ private string HandleNetCorePdbs(string ngenImageFullPath, string pdbPath) 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 @@ -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; } @@ -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; @@ -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)) @@ -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; @@ -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"; diff --git a/src/TraceEvent/ZippedETL.cs b/src/TraceEvent/ZippedETL.cs index bb0421ebf..c3dfc181a 100644 --- a/src/TraceEvent/ZippedETL.cs +++ b/src/TraceEvent/ZippedETL.cs @@ -401,17 +401,45 @@ internal static List GetNGenPdbs(string etlFile, SymbolReader symbolRead { // Generate the NGen images for any NGEN image needing symbolic information. var pdbFileList = new List(100); + var tasks = new List>>(); 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 log = new StringWriter()) + { + var pdbName = SymbolReader.GenerateNGenSymbolsForModule(imageName, log, symbolPath, symbolCacheDirectory); + return Tuple.Create(imageName, pdbName, log.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; }