Beispiel #1
0
        /// <summary>
        /// Given a full filename path to an NGEN image, insure that there is an NGEN image for it
        /// in the symbol cache.  If one already exists, this method simply returns that.   If not
        /// it is generated and placed in the symbol cache.  When generating the PDB this routine
        /// attempt to resolve line numbers, which DOES require looking up the PDB for the IL image. 
        /// Thus routine may do network accesses (to download IL PDBs).  
        /// 
        /// Note that FindSymbolFilePathForModule calls this, so normally you don't need to call 
        /// this method directly.  
        /// </summary>
        public string GenerateNGenPdbForModule(string ngenImageFullPath)
        {
            SymbolReader symReader = this;

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

            // When V4.5 shipped, NGEN CreatePdb did not support looking up the IL pdb using symbol servers.  
            // We work around by explicitly fetching the IL PDB and pointing NGEN CreatePdb at that.  
            string ilPdbName = null;
            Guid ilPdbGuid = Guid.Empty;
            int ilPdbAge = 0;

            string pdbName;
            Guid pdbGuid;
            int pdbAge;
            using (var peFile = new PEFile(ngenImageFullPath))
            {
                if (!peFile.GetPdbSignature(out pdbName, out pdbGuid, out pdbAge, true))
                {
                    log.WriteLine("Could not get PDB signature for {0}", ngenImageFullPath);
                    return null;
                }

                // Also get the IL pdb information
                peFile.GetPdbSignature(out ilPdbName, out ilPdbGuid, out ilPdbAge, false);
            }

            // Fast path, the file already exists.
            pdbName = Path.GetFileName(pdbName);
            var relPath = pdbName + "\\" + pdbGuid.ToString("N") + pdbAge.ToString() + "\\" + pdbName;
            var pdbPath = Path.Combine(symReader.SymbolCacheDirectory, relPath);
            if (File.Exists(pdbPath))
                return pdbPath;

            var clrDir = GetClrDirectoryForNGenImage(ngenImageFullPath, log);
            if (clrDir == null)
                return null;

            // 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");
            log.WriteLine("Checking for V4.5 for NGEN image {0}", ngenexe);
            if (!File.Exists(ngenexe))
                return null;
            var isV4_5Runtime = false;

            Match m;
            using (var peFile = new PEFile(ngenexe))
            {
                var fileVersionInfo = peFile.GetFileVersionInfo();
                if (fileVersionInfo != null)
                {
                    var clrFileVersion = fileVersionInfo.FileVersion;
                    m = Regex.Match(clrFileVersion, @"^[\d.]+\.(\d+) ");       // Fetch the build number (last number)
                    if (m.Success)
                    {
                        // Is this a V4.5 runtime?
                        var buildNumber = int.Parse(m.Groups[1].Value);
                        log.WriteLine("Got NGEN.exe Build number: {0}", buildNumber);
                        if (buildNumber > 16000)
                        {
                            if (ilPdbName != null)
                            {
                                var ilPdbPath = symReader.FindSymbolFilePath(ilPdbName, ilPdbGuid, ilPdbAge);
                                if (ilPdbPath != null)
                                    lineNumberArg = "/lines " + Command.Quote(Path.GetDirectoryName(ilPdbPath));
                                else
                                    log.WriteLine("Could not find IL PDB {0} Guid {1} Age {2}.", ilPdbName, ilPdbGuid, ilPdbAge);
                            }
                            else
                                log.WriteLine("NGEN image did not have IL PDB information, giving up on line number info.");
                            isV4_5Runtime = true;
                        }
                    }
                }
            }

            var options = new CommandOptions();
            options.AddEnvironmentVariable("COMPLUS_NGenEnableCreatePdb", "1");

            // NGenLocalWorker is needed for V4.0 runtims but interferes on V4.5 runtimes.  
            if (!isV4_5Runtime)
                options.AddEnvironmentVariable("COMPLUS_NGenLocalWorker", "1");
            options.AddEnvironmentVariable("_NT_SYMBOL_PATH", symReader.SymbolPath.ToString());
            var newPath = "%PATH%;" + clrDir;
            options.AddEnvironmentVariable("PATH", newPath);
            options.AddOutputStream(log);
            options.AddNoThrow();

            // For Metro Auto-NGEN images we need to use a location where the app can write the PDB file
            var outputDirectory = symReader.SymbolCacheDirectory;
            var outputPdbPath = pdbPath;

            // Find the tempDir where we can write.  
            string tempDir = null;
            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");
                DirectoryUtilities.Clean(tempDir);
                Directory.CreateDirectory(tempDir);
                outputDirectory = tempDir;
                outputPdbPath = Path.Combine(tempDir, relPath);
                log.WriteLine("Updating NGEN createPdb output file to {0}", outputPdbPath); // TODO FIX NOW REMOVE (for debugging)
            }
            try
            {

                for (; ; ) // Loop for retrying without /lines 
                {
                    // TODO FIX NOW: there is a and ugly problem with persistance of suboptimial PDB files
                    // This is made pretty bad because the not finding the IL pdbs is enough to make it fail.  

                    // TODO we need to figure out a convention show we know that we have fallen back to no-lines
                    // and we should regenerate it if we ultimately get the PDB information 
                    var cmdLine = string.Format(@"{0}\ngen.exe createpdb {1} {2} {3}",
                        clrDir, Command.Quote(ngenImageFullPath), Command.Quote(outputDirectory), lineNumberArg);
                    // TODO FIX NOW REMOVE after V4.5 is out a while
                    log.WriteLine("set COMPLUS_NGenEnableCreatePdb=1");
                    if (!isV4_5Runtime)
                        log.WriteLine("set COMPLUS_NGenLocalWorker=1");
                    log.WriteLine("set PATH=" + newPath);
                    log.WriteLine("set _NT_SYMBOL_PATH={0}", symReader.SymbolPath);
                    log.WriteLine("*** NGEN  CREATEPDB cmdline: {0}\r\n", cmdLine);
                    options.AddOutputStream(log);
                    var cmd = Command.Run(cmdLine, options);
                    log.WriteLine("*** NGEN CREATEPDB returns: {0}", cmd.ExitCode);

                    if (cmd.ExitCode != 0)
                    {
                        // ngen might make a bad PDB, so if it returns failure delete it.  
                        if (File.Exists(outputPdbPath))
                            File.Delete(outputPdbPath);

                        // We may have failed because we could not get the PDB.  
                        if (lineNumberArg.Length != 0)
                        {
                            log.WriteLine("Ngen failed to generate pdb for {0}, trying again without /lines", ngenImageFullPath);
                            lineNumberArg = "";
                            continue;
                        }
                    }

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

                    // Copy the file to where we want the PDB to live.  
                    if (outputPdbPath != pdbPath)
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(pdbPath));        // Make sure the destination directory exists.
                        File.Copy(outputPdbPath, pdbPath);
                    }
                    return pdbPath;
                }
            }
            finally
            {
                // Insure we have cleaned up any temporary files.  
                if (tempDir != null)
                    DirectoryUtilities.Clean(tempDir);
            }
        }