// Copyright (c) Microsoft Corporation. // Licensed under the MIT License - see LICENSE file in this repo. namespace Microsoft.SqlServer.Utils.Misc.SQLCallStackResolver { using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Text; public static class SymSrvHelpers { static readonly int processId = Process.GetCurrentProcess().Id; /// Wrapper around the symsrv.dll functionality to initialize the symbol load handler for this process. private static bool InitSymSrv(string symPath) { return SafeNativeMethods.SymInitialize((IntPtr)processId, symPath, false); } /// Un-initialize the symbol load handler for this process. private static bool CleanupSymSrv() { return SafeNativeMethods.SymCleanup((IntPtr)processId); } /// Private method to locate the local path for a matching PDB. Implicitly handles symbol download if needed. private static string GetLocalSymbolFolderForModule(string pdbFilename, string pdbGuid, int pdbAge) { const int MAX_PATH = 4096; StringBuilder outPath = new StringBuilder(MAX_PATH); var guid = Guid.Parse(pdbGuid); int rawsize = Marshal.SizeOf(guid); IntPtr buffer = Marshal.AllocHGlobal(rawsize); Marshal.StructureToPtr(guid, buffer, false); bool success = SafeNativeMethods.SymFindFileInPath((IntPtr)processId, null, pdbFilename, buffer, pdbAge, 0, 8, outPath, IntPtr.Zero, IntPtr.Zero); if (!success) { return String.Empty; } return outPath.ToString(); } /// /// Public method to return local PDB file paths for specified symbols. /// public static List GetFolderPathsForPDBs(StackResolver parent, string symPath, List syms) { var retval = new List(); Contract.Requires(null != syms); Contract.Requires(null != parent); if (!InitSymSrv(symPath)) { return retval; } int progress = 0; foreach (var sym in syms) { parent.StatusMessage = string.Format(CultureInfo.CurrentCulture, $"Finding local PDB path for {sym.PDBName}"); var path = GetLocalSymbolFolderForModule(sym.PDBName, sym.PDBGuid, sym.PDBAge); if (!string.IsNullOrEmpty(path)) { retval.Add(Path.GetDirectoryName(path)); parent.StatusMessage = string.Format(CultureInfo.CurrentCulture, $"Successfully found local PDB at {path}"); } else { parent.StatusMessage = string.Format(CultureInfo.CurrentCulture, $"Could not find local PDB for {sym.PDBName}"); } progress++; parent.PercentComplete = (int)((double)progress / syms.Count * 100.0); } CleanupSymSrv(); return retval; } } }