Add directory symbol search
authorMike McLaughlin <mikem@microsoft.com>
Wed, 21 Aug 2019 21:49:56 +0000 (14:49 -0700)
committerMike McLaughlin <mikem@microsoft.com>
Thu, 22 Aug 2019 22:00:07 +0000 (15:00 -0700)
Adds a setsymbolserver option (-directory) to add directories to the symbol search path.

Cleaned up how the symbol code added the "default" symbol cache. It is done when the server URI is added if a cache path wasn't specified.

Ignore adding duplicate server URI's and search directories. Ignore adding the same cache directory twice in a row.

Add -sympath option to setsymbolserver and lldb "sympath" command alias for devs that want to use the Window symbol path syntax.

Change to Preview9 prerelease label.

Issues:

https://github.com/dotnet/diagnostics/issues/422
https://github.com/dotnet/diagnostics/issues/420

eng/Versions.props
src/SOS/SOS.Hosting/SOSHost.cs
src/SOS/SOS.NETCore/SymbolReader.cs
src/SOS/Strike/hostcoreclr.cpp
src/SOS/Strike/hostcoreclr.h
src/SOS/Strike/strike.cpp
src/SOS/lldbplugin/soscommand.cpp
src/Tools/dotnet-dump/Analyzer.cs
src/Tools/dotnet-dump/Commands/HelpCommand.cs

index e4e3c41c456f2f3e665afe4e906bac715e525293..0efd7a0b4c5975059ab4b97440282bbd145b111d 100644 (file)
@@ -2,7 +2,7 @@
 <Project>
   <PropertyGroup>
     <RepositoryUrl>https://github.com/dotnet/diagnostics</RepositoryUrl>
-    <PreReleaseVersionLabel>preview8</PreReleaseVersionLabel>
+    <PreReleaseVersionLabel>preview9</PreReleaseVersionLabel>
     <VersionPrefix>3.0.0</VersionPrefix>
     <DotNetUseShippingVersions>true</DotNetUseShippingVersions>
     <AutoGenerateAssemblyVersion>true</AutoGenerateAssemblyVersion>
@@ -22,7 +22,7 @@
     <MicrosoftWin32PrimitivesVersion>4.3.0</MicrosoftWin32PrimitivesVersion>
 
     <!-- Other libs -->
-    <MicrosoftSymbolStoreVersion>1.0.0-dev-64131-02</MicrosoftSymbolStoreVersion>
+    <MicrosoftSymbolStoreVersion>1.0.41901</MicrosoftSymbolStoreVersion>
     <MicrosoftDiagnosticsRuntimeVersion>1.1.37504</MicrosoftDiagnosticsRuntimeVersion>
     <MicrosoftDiaSymReaderNativePackageVersion>1.7.0</MicrosoftDiaSymReaderNativePackageVersion>
     <MicrosoftDiagnosticsTracingTraceEventVersion>2.0.44</MicrosoftDiagnosticsTracingTraceEventVersion>
@@ -40,7 +40,6 @@
     <RestoreSources>
       $(RestoreSources);
       https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json;
-      https://dotnet.myget.org/F/symstore/api/v3/index.json;
       https://dotnet.myget.org/F/dotnet-core/api/v3/index.json;
       https://dotnet.myget.org/F/dotnet-buildtools/api/v3/index.json;
       https://dotnet.myget.org/F/system-commandline/api/v3/index.json
index e4f3f2816d0193efd4d34550e6d08279bbd8d955..e19e4e69bec0cb6ff86017dd95452ee64b2b06c1 100644 (file)
@@ -62,6 +62,7 @@ namespace SOS
             string tempDirectory,
             string symbolServerPath,
             string symbolCachePath,
+            string symbolDirectoryPath,
             string windowsSymbolPath);
 
         private delegate void DisplaySymbolStoreDelegate(
index ed42d77762f8bf33c1f67325465b725508b99d35..392c42ca3411a0ce1d02c2c12cf275f149421df2 100644 (file)
@@ -137,8 +137,8 @@ namespace SOS
         /// <summary>
         /// Symbol server URLs
         /// </summary>
-        const string MsdlsymbolServer = "http://msdl.microsoft.com/download/symbols/";
-        const string SymwebSymbolService = "http://symweb.corp.microsoft.com/";
+        const string MsdlSymbolServer = "http://msdl.microsoft.com/download/symbols/";
+        const string SymwebSymbolServer = "http://symweb.corp.microsoft.com/";
 
         /// <summary>
         /// Read memory callback
@@ -166,7 +166,6 @@ namespace SOS
 
         static readonly ITracer s_tracer = new Tracer();
         static SymbolStore s_symbolStore = null;
-        static bool s_symbolCacheAdded = false;
 
         /// <summary>
         /// Initializes symbol loading. Adds the symbol server and/or the cache path (if not null) to the list of
@@ -178,9 +177,10 @@ namespace SOS
         /// <param name="tempDirectory">temp directory unique to this instance of SOS</param>
         /// <param name="symbolServerPath">symbol server url (optional)</param>
         /// <param name="symbolCachePath">symbol cache directory path (optional)</param>
+        /// <param name="symbolDirectoryPath">symbol directory path to search (optional)</param>
         /// <param name="windowsSymbolPath">windows symbol path (optional)</param>
         /// <returns>if false, failure</returns>
-        public static bool InitializeSymbolStore(bool logging, bool msdl, bool symweb, string tempDirectory, string symbolServerPath, string symbolCachePath, string windowsSymbolPath)
+        public static bool InitializeSymbolStore(bool logging, bool msdl, bool symweb, string tempDirectory, string symbolServerPath, string symbolCachePath, string symbolDirectoryPath, string windowsSymbolPath)
         {
             if (logging) {
                 // Uses the standard console to do the logging instead of sending it to the hosting debugger console
@@ -201,8 +201,15 @@ namespace SOS
                 }
             }
             else {
+                // Add the default symbol cache if no cache specified and adding server
+                if (symbolCachePath == null)
+                {
+                    if (msdl || symweb || symbolServerPath != null) {
+                        symbolCachePath = GetDefaultSymbolCache();
+                    }
+                }
                 // Build the symbol stores using the other parameters
-                if (!GetServerSymbolStore(ref store, msdl, symweb, symbolServerPath, symbolCachePath)) {
+                if (!GetServerSymbolStore(ref store, msdl, symweb, symbolServerPath, symbolCachePath, symbolDirectoryPath)) {
                     return false;
                 }
             }
@@ -222,9 +229,12 @@ namespace SOS
                 if (symbolStore is CacheSymbolStore cache) {
                     writeLine($"Cache: {cache.CacheDirectory}");
                 }
-                else if (symbolStore is HttpSymbolStore http)  {
+                else if (symbolStore is HttpSymbolStore http) {
                     writeLine($"Server: {http.Uri}");
                 }
+                else if (symbolStore is DirectorySymbolStore directory) {
+                    writeLine($"Directory: {directory.Directory}");
+                }
                 else {
                     writeLine("Unknown symbol store");
                 }
@@ -238,7 +248,6 @@ namespace SOS
         public static void DisableSymbolStore()
         {
             s_symbolStore = null;
-            s_symbolCacheAdded = false;
         }
 
         /// <summary>
@@ -992,11 +1001,6 @@ namespace SOS
         {
             try
             {
-                // Add the default symbol cache if it hasn't already been added
-                if (!s_symbolCacheAdded) {
-                    s_symbolStore = new CacheSymbolStore(s_tracer, s_symbolStore, GetDefaultSymbolCache());
-                    s_symbolCacheAdded = true;
-                }
                 return s_symbolStore.GetFile(key, CancellationToken.None).GetAwaiter().GetResult();
             }
             catch (Exception ex) when (ex is UnauthorizedAccessException || ex is BadImageFormatException || ex is IOException)
@@ -1025,6 +1029,7 @@ namespace SOS
                 {
                     string symbolServerPath = null;
                     string symbolCachePath = null;
+                    string symbolDirectoryPath = null;
                     bool msdl = false;
 
                     switch (parts[0].ToLowerInvariant())
@@ -1034,6 +1039,7 @@ namespace SOS
                             { 
                                 case 1:
                                     msdl = true;
+                                    symbolCachePath = GetDefaultSymbolCache();
                                     break;
                                 case 2:
                                     symbolServerPath = parts[1];
@@ -1062,12 +1068,20 @@ namespace SOS
                             break;
 
                         default:
-                            // Directory path search (currently ignored)
+                            // Directory path search
+                            switch (parts.Length)
+                            {
+                                case 1:
+                                    symbolDirectoryPath = parts[0];
+                                    break;
+                                default:
+                                    return false;
+                            }
                             break;
                     }
 
                     // Add the symbol stores to the chain
-                    if (!GetServerSymbolStore(ref store, msdl, false, symbolServerPath, symbolCachePath))
+                    if (!GetServerSymbolStore(ref store, msdl, false, symbolServerPath, symbolCachePath, symbolDirectoryPath))
                     {
                         return false;
                     }
@@ -1077,7 +1091,7 @@ namespace SOS
             return true;
         }
 
-        private static bool GetServerSymbolStore(ref SymbolStore store, bool msdl, bool symweb, string symbolServerPath, string symbolCachePath)
+        private static bool GetServerSymbolStore(ref SymbolStore store, bool msdl, bool symweb, string symbolServerPath, string symbolCachePath, string symbolDirectoryPath)
         {
             bool internalServer = false;
 
@@ -1086,11 +1100,11 @@ namespace SOS
             {
                 if (msdl)
                 {
-                    symbolServerPath = MsdlsymbolServer;
+                    symbolServerPath = MsdlSymbolServer;
                 }
                 else if (symweb)
                 {
-                    symbolServerPath = SymwebSymbolService;
+                    symbolServerPath = SymwebSymbolServer;
                     internalServer = true;
                 }
             }
@@ -1105,33 +1119,69 @@ namespace SOS
 
             if (symbolServerPath != null)
             {
+                // Validate symbol server path
                 if (!Uri.TryCreate(symbolServerPath, UriKind.Absolute, out Uri uri) || uri.IsFile)
                 {
                     return false;
                 }
 
-                // Create symbol server store
-                if (internalServer)
+                if (!IsDuplicateSymbolStore<HttpSymbolStore >(store, (httpSymbolStore) => uri.Equals(httpSymbolStore.Uri)))
                 {
-                    store = new SymwebHttpSymbolStore(s_tracer, store, uri);
+                    // Create symbol server store
+                    if (internalServer)
+                    {
+                        store = new SymwebHttpSymbolStore(s_tracer, store, uri);
+                    }
+                    else
+                    {
+                        store = new HttpSymbolStore(s_tracer, store, uri);
+                    }
                 }
-                else
+            }
+
+            if (symbolCachePath != null)
+            {
+                symbolCachePath = Path.GetFullPath(symbolCachePath);
+
+                // Check only the first symbol store for duplication. The same cache directory can be
+                // added more than once but just not more than once in a row.
+                if (!(store is CacheSymbolStore cacheSymbolStore && IsPathEqual(symbolCachePath, cacheSymbolStore.CacheDirectory)))
                 {
-                    store = new HttpSymbolStore(s_tracer, store, uri);
+                    store = new CacheSymbolStore(s_tracer, store, symbolCachePath);
                 }
             }
 
-            if (symbolCachePath != null)
+            if (symbolDirectoryPath != null)
             {
-                store = new CacheSymbolStore(s_tracer, store, symbolCachePath);
+                symbolDirectoryPath = Path.GetFullPath(symbolDirectoryPath);
 
-                // Don't add the default cache later
-                s_symbolCacheAdded = true;
+                if (!IsDuplicateSymbolStore<DirectorySymbolStore>(store, (directorySymbolStore) => IsPathEqual(symbolDirectoryPath, directorySymbolStore.Directory)))
+                {
+                    store = new DirectorySymbolStore(s_tracer, store, symbolDirectoryPath);
+                }
             }
 
             return true;
         }
 
+        private static bool IsDuplicateSymbolStore<T>(SymbolStore symbolStore, Func<T, bool> match) 
+            where T : SymbolStore
+        {
+            while (symbolStore != null)
+            {
+                if (symbolStore is T store)
+                {
+                    // TODO: replace this by adding an Equal override to the symbol stores
+                    if (match(store))
+                    {
+                        return true;
+                    }
+                }
+                symbolStore = symbolStore.BackingStore;
+            }
+            return false;
+        }
+
         private static string GetDefaultSymbolCache()
         {
             if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@@ -1151,17 +1201,31 @@ namespace SOS
         /// <returns>stream or null if doesn't exist or error</returns>
         internal static Stream TryOpenFile(string path)
         {
-            if (!File.Exists(path))
+            if (File.Exists(path))
             {
-                return null;
+                try
+                {
+                    return File.OpenRead(path);
+                }
+                catch (Exception ex) when (ex is UnauthorizedAccessException || ex is NotSupportedException || ex is IOException)
+                {
+                }
             }
-            try
+            return null;
+        }
+
+        /// <summary>
+        /// Compares two file paths using OS specific casing.
+        /// </summary>
+        private static bool IsPathEqual(string path1, string path2)
+        {
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 
             {
-                return File.OpenRead(path);
+                return StringComparer.OrdinalIgnoreCase.Equals(path1, path2);
             }
-            catch
+            else 
             {
-                return null;
+                return string.Equals(path1, path2);
             }
         }
 
index 670617dca06620621c3f4df2a065486fc8ad1be5..41dfe1b78d4eb01db13cc0141d99916afb8bb93d 100644 (file)
@@ -42,6 +42,7 @@
 
 static bool g_hostingInitialized = false;
 static bool g_symbolStoreInitialized = false;
+static bool g_windowsSymbolPathInitialized = false;
 LPCSTR g_hostRuntimeDirectory = nullptr;
 LPCSTR g_dacFilePath = nullptr;
 LPCSTR g_dbiFilePath = nullptr;
@@ -777,13 +778,13 @@ static int ReadMemoryForSymbols(ULONG64 address, uint8_t *buffer, int cb)
 /**********************************************************************\
  * Setup and initialize the symbol server support.
 \**********************************************************************/
-HRESULT InitializeSymbolStore(BOOL logging, BOOL msdl, BOOL symweb, const char* symbolServer, const char* cacheDirectory)
+HRESULT InitializeSymbolStore(BOOL logging, BOOL msdl, BOOL symweb, const char* symbolServer, const char* cacheDirectory, const char* searchDirectory, const char* windowsSymbolPath)
 {
     HRESULT Status = S_OK;
     IfFailRet(InitializeHosting());
     _ASSERTE(g_SOSNetCoreCallbacks.InitializeSymbolStoreDelegate != nullptr);
 
-    if (!g_SOSNetCoreCallbacks.InitializeSymbolStoreDelegate(logging, msdl, symweb, GetTempDirectory(), symbolServer, cacheDirectory, nullptr))
+    if (!g_SOSNetCoreCallbacks.InitializeSymbolStoreDelegate(logging, msdl, symweb, GetTempDirectory(), symbolServer, cacheDirectory, searchDirectory, windowsSymbolPath))
     {
         ExtErr("Error initializing symbol server support\n");
         return E_FAIL;
@@ -792,6 +793,7 @@ HRESULT InitializeSymbolStore(BOOL logging, BOOL msdl, BOOL symweb, const char*
     return S_OK;
 }
 
+
 /**********************************************************************\
  * Setup and initialize the symbol server support using the .sympath
 \**********************************************************************/
@@ -800,19 +802,20 @@ void InitializeSymbolStore()
     _ASSERTE(g_SOSNetCoreCallbacks.InitializeSymbolStoreDelegate != nullptr);
 
 #ifndef FEATURE_PAL
-    if (!g_symbolStoreInitialized)
+    if (!g_windowsSymbolPathInitialized)
     {
-        g_symbolStoreInitialized = true;
-
         ArrayHolder<char> symbolPath = new char[MAX_LONGPATH];
         if (SUCCEEDED(g_ExtSymbols->GetSymbolPath(symbolPath, MAX_LONGPATH, nullptr)))
         {
             if (strlen(symbolPath) > 0)
             {
-                if (!g_SOSNetCoreCallbacks.InitializeSymbolStoreDelegate(false, false, false, GetTempDirectory(), nullptr, nullptr, symbolPath))
+                if (!g_SOSNetCoreCallbacks.InitializeSymbolStoreDelegate(false, false, false, GetTempDirectory(), nullptr, nullptr, nullptr, symbolPath))
                 {
                     ExtErr("Windows symbol path parsing FAILED\n");
+                    return;
                 }
+                g_windowsSymbolPathInitialized = true;
+                g_symbolStoreInitialized = true;
             }
         }
     }
@@ -926,6 +929,7 @@ void DisableSymbolStore()
     if (g_symbolStoreInitialized)
     {
         g_symbolStoreInitialized = false;
+        g_windowsSymbolPathInitialized = false;
 
         _ASSERTE(g_SOSNetCoreCallbacks.DisableSymbolStoreDelegate != nullptr);
         g_SOSNetCoreCallbacks.DisableSymbolStoreDelegate();
@@ -1528,4 +1532,4 @@ HRESULT SymbolReader::ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG3
 #endif // FEATURE_PAL
 
     return E_FAIL;
-}
\ No newline at end of file
+}
index a5fe423cb88bcd0e0ed8bf34e348f64a5cd65e86..ef260a399471d418d09cc6e4bffd1204b56150ef 100644 (file)
@@ -16,7 +16,7 @@ typedef void (*WriteLineDelegate)(const char*);
 typedef  int (*ReadMemoryDelegate)(ULONG64, uint8_t*, int);
 typedef void (*SymbolFileCallbackDelegate)(void*, const char* moduleFileName, const char* symbolFilePath);
 
-typedef  BOOL (*InitializeSymbolStoreDelegate)(BOOL, BOOL, BOOL, const char*, const char*, const char*, const char*);
+typedef  BOOL (*InitializeSymbolStoreDelegate)(BOOL, BOOL, BOOL, const char*, const char*, const char*, const char*, const char*);
 typedef  void (*DisplaySymbolStoreDelegate)(WriteLineDelegate);
 typedef  void (*DisableSymbolStoreDelegate)();
 typedef  void (*LoadNativeSymbolsDelegate)(SymbolFileCallbackDelegate, void*, const char*, ULONG64, int, ReadMemoryDelegate);
@@ -67,7 +67,7 @@ extern LPCSTR GetDacFilePath();
 extern LPCSTR GetDbiFilePath();
 extern BOOL IsHostingInitialized();
 extern HRESULT InitializeHosting();
-extern HRESULT InitializeSymbolStore(BOOL logging, BOOL msdl, BOOL symweb, const char* symbolServer, const char* cacheDirectory);
+extern HRESULT InitializeSymbolStore(BOOL logging, BOOL msdl, BOOL symweb, const char* symbolServer, const char* cacheDirectory, const char* searchDirectory, const char* windowsSymbolPath);
 extern void InitializeSymbolStore();
 extern HRESULT LoadNativeSymbols(bool runtimeOnly = false);
 extern void DisplaySymbolStore();
index 77577adb3967d921da6aaf02c29864d5811fa0ec..afb2c21a686b30da1bae24a23c96cb950683311d 100644 (file)
@@ -15527,6 +15527,8 @@ DECLARE_API(SetSymbolServer)
     INIT_API_EXT();
 
     StringHolder symbolCache;
+    StringHolder searchDirectory;
+    StringHolder windowsSymbolPath;
     BOOL disable = FALSE;
     BOOL loadNative = FALSE;
     BOOL msdl = FALSE;
@@ -15535,11 +15537,13 @@ DECLARE_API(SetSymbolServer)
     CMDOption option[] =
     {   // name, vptr, type, hasValue
         {"-disable", &disable, COBOOL, FALSE},
-        {"-cache", &symbolCache.data, COSTRING, FALSE},
+        {"-cache", &symbolCache.data, COSTRING, TRUE},
+        {"-directory", &searchDirectory.data, COSTRING, TRUE},
         {"-ms", &msdl, COBOOL, FALSE},
         {"-log", &logging, COBOOL, FALSE},
 #ifdef FEATURE_PAL
         {"-loadsymbols", &loadNative, COBOOL, FALSE},
+        {"-sympath", &windowsSymbolPath.data, COSTRING, TRUE},
 #else
         {"-mi", &symweb, COBOOL, FALSE},
 #endif
@@ -15555,11 +15559,6 @@ DECLARE_API(SetSymbolServer)
         return E_FAIL;
     }
 
-    if (disable) {
-        DisableSymbolStore();
-        return S_OK;
-    }
-
     if (msdl && symweb)
     {
         ExtErr("Cannot have both -ms and -mi options\n");
@@ -15572,9 +15571,13 @@ DECLARE_API(SetSymbolServer)
         return E_FAIL;
     }
 
-    if (msdl || symweb || symbolServer.data != nullptr || symbolCache.data != nullptr)
+    if (disable) {
+        DisableSymbolStore();
+    }
+
+    if (msdl || symweb || symbolServer.data != nullptr || symbolCache.data != nullptr || searchDirectory.data != nullptr || windowsSymbolPath.data != nullptr)
     {
-        Status = InitializeSymbolStore(logging, msdl, symweb, symbolServer.data, symbolCache.data);
+        Status = InitializeSymbolStore(logging, msdl, symweb, symbolServer.data, symbolCache.data, searchDirectory.data, windowsSymbolPath.data);
         if (FAILED(Status))
         {
             return Status;
@@ -15593,7 +15596,15 @@ DECLARE_API(SetSymbolServer)
         }
         if (symbolCache.data != nullptr)
         {
-            ExtOut("Symbol cache path: %s\n", symbolCache.data);
+            ExtOut("Added symbol cache path: %s\n", symbolCache.data);
+        }
+        if (searchDirectory.data != nullptr)
+        {
+            ExtOut("Added symbol directory path: %s\n", searchDirectory.data);
+        }
+        if (windowsSymbolPath.data != nullptr)
+        {
+            ExtOut("Added Windows symbol path: %s\n", windowsSymbolPath.data);
         }
     }
     else if (loadNative)
index da21b02fa5df892aea86240543c00e13b73add33..b6a4edc8f5c570bf4193a8422b530bc5a54fdf3d 100644 (file)
@@ -165,6 +165,7 @@ sosCommandInitialize(lldb::SBDebugger debugger)
     interpreter.AddCommand("histroot", new sosCommand("HistRoot"), "Displays information related to both promotions and relocations of the specified root.");
     interpreter.AddCommand("sethostruntime", new sosCommand("SetHostRuntime"), "Sets or displays the .NET Core runtime directory to use to run managed code in SOS.");
     interpreter.AddCommand("setsymbolserver", new sosCommand("SetSymbolServer"), "Enables the symbol server support ");
+    interpreter.AddCommand("sympath", new sosCommand("SetSymbolServer", "-sympath"), "Add server, cache and directory paths in the Windows symbol path format.");
     interpreter.AddCommand("soshelp", new sosCommand("Help"), "Displays all available commands when no parameter is specified, or displays detailed help information about the specified command. soshelp <command>");
     return true;
 }
index d462f0269cd4ad17999d83aea07e84ce4b931320..81c1a342bb3ff7c5802180c20849dad1ce064868 100644 (file)
@@ -71,7 +71,7 @@ namespace Microsoft.Diagnostics.Tools.Dump
                     AddServices(target);
 
                     // Automatically enable symbol server support
-                    SymbolReader.InitializeSymbolStore(logging: false, msdl: true, symweb: false, tempDirectory: null, symbolServerPath: null, symbolCachePath: null, windowsSymbolPath: null);
+                    SymbolReader.InitializeSymbolStore(logging: false, msdl: true, symweb: false, tempDirectory: null, symbolServerPath: null, symbolCachePath: null, symbolDirectoryPath: null, windowsSymbolPath: null);
 
                     // Run the commands from the dotnet-dump command line
                     if (command != null)
index d2f3ce8507de673f3e62d69c314947af5b46dffb..687ae127894b2c0c269a02cdb67abc2395c72bd0 100644 (file)
@@ -8,6 +8,7 @@ using System.CommandLine;
 namespace Microsoft.Diagnostics.Tools.Dump
 {
     [Command(Name = "help", Help = "Display help for a command.")]
+    [CommandAlias(Name = "soshelp")]
     public class HelpCommand : CommandBase
     {
         [Argument(Help = "Command to find help.")]