From: Mike McLaughlin Date: Mon, 7 Jan 2019 05:42:03 +0000 (-0800) Subject: Native module symbol download support. (#106) X-Git-Tag: submit/tizen/20190813.035844~64 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f4982d1077f81d2befe29d15de00fbbaeb26987c;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git Native module symbol download support. (#106) Native module symbol download support. Other fixes and documentation improvements. Fix error formatting. --- diff --git a/README.md b/README.md index 0200c4501..00e9cc79c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ To install the platform's prerequisites: * [Windows Instructions](documentation/building/windows-instructions.md) * [Linux Instructions](documentation/building/linux-instructions.md) - * [macOS Instructions](documentation/building/osx-instructions.md) + * [MacOS Instructions](documentation/building/osx-instructions.md) * [FreeBSD Instructions](documentation/building/freebsd-instructions.md) * [NetBSD Instructions](documentation/building/netbsd-instructions.md) @@ -72,16 +72,28 @@ To test the resulting SOS and plugin: Getting a version of lldb that works for your platform can be a problem sometimes. The version has to be at least 3.9 or greater because of a bug running SOS on a core dump that was fixed. Some Linux distros like Ubuntu it is easy as `sudo apt-get install lldb-3.9 python-lldb-3.9`. On other distros, you will need to build lldb. The directions below should give you some guidance. - * [Linux Instructions](documentation/lldb/linux-instructions.md) - * [macOS Instructions](documentation/lldb/osx-instructions.md) - * [FreeBSD Instructions](documentation/lldb/freebsd-instructions.md) - * [NetBSD Instructions](documentation/lldb/netbsd-instructions.md) +* [Linux Instructions](documentation/lldb/linux-instructions.md) +* [MacOS Instructions](documentation/lldb/osx-instructions.md) +* [FreeBSD Instructions](documentation/lldb/freebsd-instructions.md) +* [NetBSD Instructions](documentation/lldb/netbsd-instructions.md) + +## Using SOS + +* [SOS debugging for Linux/MacOS](documentation/sos-debugging-extension.md) +* [SOS debugging for Windows](documentation/sos-debugging-extension-windows.md) +* [Debugging a core dump](documentation/debugging-coredump.md) + +## New Features + +Symbol server support - The `setsymbolserver` command enables downloading the symbol files (portable PDBs) for managed assemblies during commands like `clrstack`, etc. See `soshelp setsymbolserver` for more details. + + (lldb) setsymbolserver -ms ## Useful Links * [The LLDB Debugger](http://lldb.llvm.org/index.html) - More information about lldb. -* [Debugging CoreCLR](documentation/debugging-instructions.md) - Instructions for debugging .NET Core and the CoreCLR runtime. * [SOS](https://msdn.microsoft.com/en-us/library/bb190764(v=vs.110).aspx) - More information about SOS. +* [Debugging CoreCLR](https://github.com/dotnet/coreclr/blob/master/Documentation/building/debugging-instructions.md) - Instructions for debugging .NET Core and the CoreCLR runtime. * [dotnet/coreclr](https://github.com/dotnet/coreclr) - Source for the .NET Core runtime. [//]: # (Begin current test results) diff --git a/documentation/building/linux-instructions.md b/documentation/building/linux-instructions.md index 791895197..4e122113c 100644 --- a/documentation/building/linux-instructions.md +++ b/documentation/building/linux-instructions.md @@ -150,7 +150,7 @@ This will take some time to complete, but after it is finished all the required ./build.sh ./test.sh -#### Fedora 27, 28 #### +#### Fedora 27, 28, 29 #### sudo dnf install clang cmake compat-openssl10 findutils gdb git libicu libunwind lldb-devel llvm-devel make python python2-lldb tar wget which zip diff --git a/documentation/debugging-coredump.md b/documentation/debugging-coredump.md new file mode 100644 index 000000000..44aeb8877 --- /dev/null +++ b/documentation/debugging-coredump.md @@ -0,0 +1,45 @@ +Debugging Linux or MacOS Core Dump +================================== + +These instructions will lead you through getting symbols, loading and debugging a Linux or MacOS core dump. The best way to generate a core dump on Linux (only) is through the [createdump](https://github.com/dotnet/coreclr/blob/master/Documentation/botr/xplat-minidump-generation.md#configurationpolicy) facility. + +### Getting symbols ### + +First install the dotnet CLI symbol tool. This only needs to be down once. See this [link](https://github.com/dotnet/symstore/tree/master/src/dotnet-symbol#install) for more details. + + ~$ dotnet tool install -g dotnet-symbol + +Copy the core dump to a tmp directory. + + ~$ mkdir /tmp/dump + ~$ cp ~/coredump.32232 /tmp/dump + +Download the modules and symbols for the core dump: + + ~$ dotnet symbol /tmp/dump/coredump.32232 + +Remove (Linux only) the the libcoreclrtraceptprovider to avoid crashing lldb to avoid issue #[20205](https://github.com/dotnet/coreclr/issues/20205). + + ~$ rm /tmp/dump/libcoreclrtraceptprovider.* + +### Install lldb and build the diagnostic repo ### + +See the instructions on the main README.md. + +### Launch lldb under Linux ### + + ~$ lldb + (lldb) plugin load $(HOME)/diagnostics/artifacts/bin/Linux.x64.Debug/libsosplugin.so + (lldb) sethostruntime /usr/share/dotnet/shared/Microsoft.NETCore.App/2.1.0 + (lldb) target create --core /tmp/dump/coredump.32232 + +Even if the core dump was not generated on this machine, the native and managed .NET Core symbols should be avaliable along with all the SOS commands. Note that path passed to the `sethostruntime` needs to be a .NET Core runtime installed on the machine. See `dotnet --info` for information on the installed runtimes and SDKs. + +### Launch lldb under MacOS ### + + ~$ lldb + (lldb) plugin load $(HOME)/diagnostics/artifacts/bin/OSX.x64.Debug/libsosplugin.dylib + (lldb) sethostruntime /usr/share/dotnet/shared/Microsoft.NETCore.App/2.1.0 + (lldb) target create --core /tmp/dump/coredump.32232 + +The MacOS lldb has a bug that prevents SOS clrstack from properly working. Because of this bug SOS can't properly match the lldb native with with the managed thread OSID displayed by `clrthreads`. The `setsostid` command is a work around for this lldb bug. diff --git a/documentation/debugging-instructions.md b/documentation/debugging-instructions.md deleted file mode 100644 index 0ece87556..000000000 --- a/documentation/debugging-instructions.md +++ /dev/null @@ -1,196 +0,0 @@ -Debugging CoreCLR -================= - -These instructions will lead you through debugging CoreCLR on Windows and Linux. They will be expanded to support macOS when we have good instructions for that. - -Debugging CoreCLR on Windows -============================ - -1. Perform a build of the repo. -2. Open solution \\bin\obj\Windows_NT.\.\\CoreCLR.sln in Visual Studio. \ and \ are based - on type of build you did. By default they are 'x64' and 'Debug'. -3. Right click the INSTALL project and choose ‘Set as StartUp Project’ -4. Bring up the properties page for the INSTALL project -5. Select Configuration Properties->Debugging from the left side tree control -6. Set Command=`$(SolutionDir)..\..\product\Windows_NT.$(Platform).$(Configuration)\corerun.exe` - 1. This points to the folder where the built runtime binaries are present. -7. Set Command Arguments=`` (e.g. HelloWorld.exe) -8. Set Working Directory=`$(SolutionDir)..\..\product\Windows_NT.$(Platform).$(Configuration)` - 1. This points to the folder containing CoreCLR binaries. -9. Press F11 to start debugging at wmain in corerun (or set a breakpoint in source and press F5 to run to it) - 1. As an example, set a breakpoint for the EEStartup function in ceemain.cpp to break into CoreCLR startup. - -Steps 1-8 only need to be done once, and then (9) can be repeated whenever you want to start debugging. The above can be done with Visual Studio 2013. - -### Using SOS with windbg or cdb on Windows ### - -If you know the path of the `sos.dll` for the version of your runtime, load it like `.load c:\path\to\sos\sos.dll`. Use can use the `lm` command to find the path of the "coreclr.dll" module. `.loadby sos coreclr` should also work. - -For more information on SOS commands click [here](https://msdn.microsoft.com/en-us/library/bb190764(v=vs.110).aspx). - -Debugging CoreCLR on OS X -========================== - -To use lldb on OS X, you first need to build it and the SOS plugin on the machine you intend to use it. See the instructions in [building lldb](buildinglldb.md). The rest of instructions on how to use lldb for Linux on are the same. - -Debugging CoreCLR on Linux -========================== - -Only lldb is supported by the SOS plugin. gdb can be used to debug the coreclr code but with no SOS support. Visual Studio 2015 RTM remote debugging isn't currently supported. - -1. Perform a build of the coreclr repo. -2. Install the corefx managed assemblies to the binaries directory. -3. cd to build's binaries: `cd ~/coreclr/bin/Product/Linux.x64.Debug` -4. Start lldb (the version the plugin was built with, currently 3.9): `lldb-3.9 corerun HelloWorld.exe linux` -5. Now at the lldb command prompt, load SOS plugin: `plugin load libsosplugin.so` -6. Launch program: `process launch -s` -7. To stop annoying breaks on SIGUSR1/SIGUSR2 signals used by the runtime run: `process handle -s false SIGUSR1 SIGUSR2` -8. Get to a point where coreclr is initialized by setting a breakpoint (i.e. `breakpoint set -n LoadLibraryExW` and then `process continue`) or stepping into the runtime. -9. Run a SOS command like `sos ClrStack` or `sos VerifyHeap`. The command name is case sensitive. - -You can combine steps 4-8 and pass everything on the lldb command line: - -`lldb -o "plugin load libsosplugin.so" -o "process launch -s" -o "process handle -s false SIGUSR1 SIGUSR2" -o "breakpoint set -n LoadLibraryExW" corerun HelloWorld.exe linux` - -The libsosplugin.so built in this repo will work with lldb 3.9 and greater and the plugin and SOS will work with .NET Core versions 1.x, 2.0 and 2.1. - -### SOS commands ### - -This is the full list of commands currently supported by SOS. lldb is case-sensitive unlike windbg. - - Type "soshelp " for detailed info on that function. - - Object Inspection Examining code and stacks - ----------------------------- ----------------------------- - DumpObj (dumpobj) Threads (clrthreads) - DumpArray ThreadState - DumpStackObjects (dso) IP2MD (ip2md) - DumpHeap (dumpheap) u (clru) - DumpVC DumpStack (dumpstack) - GCRoot (gcroot) EEStack (eestack) - PrintException (pe) ClrStack (clrstack) - GCInfo - EHInfo - bpmd (bpmd) - - Examining CLR data structures Diagnostic Utilities - ----------------------------- ----------------------------- - DumpDomain (dumpdomain) VerifyHeap - EEHeap (eeheap) FindAppDomain - Name2EE (name2ee) DumpLog (dumplog) - DumpMT (dumpmt) - DumpClass (dumpclass) - DumpMD (dumpmd) - Token2EE - DumpModule (dumpmodule) - DumpAssembly - DumpRuntimeTypes - DumpIL (dumpil) - DumpSig - DumpSigElem - - Examining the GC history Other - ----------------------------- ----------------------------- - HistInit (histinit) SetHostRuntime (sethostruntime) - HistRoot (histroot) FAQ - HistObj (histobj) Help (soshelp) - HistObjFind (histobjfind) - HistClear (histclear) - -### Aliases ### - -By default you can reach all the SOS commands by using: _sos [command\_name]_ -However the common commands have been aliased so that you don't need the SOS prefix: - - bpmd -- Creates a breakpoint at the specified managed method in the specified module. - clrstack -- Provides a stack trace of managed code only. - clrthreads -- List the managed threads running. - clru -- Displays an annotated disassembly of a managed method. - createdump -- Create a xplat minidump. - dso -- Displays all managed objects found within the bounds of the current stack. - dumpclass -- Displays information about a EE class structure at the specified address. - dumpdomain -- Displays information all the AppDomains and all assemblies within the domains. - dumpheap -- Displays info about the garbage-collected heap and collection statistics about objects. - dumpil -- Displays the Microsoft intermediate language (MSIL) that is associated with a managed method. - dumplog -- Writes the contents of an in-memory stress log to the specified file. - dumpmd -- Displays information about a MethodDesc structure at the specified address. - dumpmodule -- Displays information about a EE module structure at the specified address. - dumpmt -- Displays information about a method table at the specified address. - dumpobj -- Displays info about an object at the specified address. - dumpstack -- Displays a native and managed stack trace. - eeheap -- Displays info about process memory consumed by internal runtime data structures. - eestack -- Runs dumpstack on all threads in the process. - gcroot -- Displays info about references (or roots) to an object at the specified address. - histclear -- Releases any resources used by the family of Hist commands. - histinit -- Initializes the SOS structures from the stress log saved in the debuggee. - histobj -- Examines all stress log relocation records and displays the chain of garbage collection relocations that may have led to the address passed in as an argument. - histobjfind -- Displays all the log entries that reference an object at the specified address. - histroot -- Displays information related to both promotions and relocations of the specified root. - ip2md -- Displays the MethodDesc structure at the specified address in code that has been JIT-compiled. - name2ee -- Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module. - pe -- Displays and formats fields of any object derived from the Exception class at the specified address. - setclrpath -- Set the path to load coreclr dac/dbi files. setclrpath - sethostruntime -- Sets or displays the .NET Core runtime directory to use to run managed code in SOS. - setsostid -- Set the current os tid/thread index instead of using the one lldb provides. setsostid - sos -- Various coreclr debugging commands. See 'soshelp' for more details. sos - soshelp -- Displays all available commands when no parameter is specified, or displays detailed help information about the specified command. soshelp - -It is also possible to debug .NET Core crash dumps using lldb and SOS. In order to do this, you need all of the following: - -- The crash dump file. We have a service called "Dumpling" which collects, uploads, and archives crash dump files during all of our CI jobs and official builds. -- On Linux, there is an utility called `createdump` (see [doc](https://github.com/dotnet/coreclr/blob/master/Documentation/botr/xplat-minidump-generation.md "doc")) that can be setup to generate core dumps when a managed app throws an unhandled exception or faults. -- To get matching runtime and symbol binaries for the core dump use the symbol downloader CLI extension: - - Install the [.NET Core 2.1 SDK](https://www.microsoft.com/net/download/). - - Install the symbol downloader extension: `dotnet tool install -g dotnet-symbol`. - - Run `dotnet symbol coredump` to download the runtime binaries and symbols. - - Check out the coreclr and corefx repositories at the appropriate commit for the appropriate source. - - For more details see: [dotnet-symbol](https://github.com/dotnet/symstore/blob/master/src/dotnet-symbol/README.md). -- lldb version 3.9. The SOS plugin (i.e. libsosplugin.so) provided is now built for lldb 3.9. In order to install lldb 3.9 just run the following commands: -``` -~$ echo "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.9 main" | sudo tee /etc/apt/sources.list.d/llvm.list -~$ wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - -~$ sudo apt-get update -~$ sudo apt-get install lldb-3.9 -``` - -Once you have everything listed above, you are ready to start debugging. You need to specify an extra parameter to lldb in order for it to correctly resolve the symbols for libcoreclr.so. Use a command like this: - -``` -lldb-3.9 -O "settings set target.exec-search-paths " -o "plugin load " --core -``` - -- ``: The path containing libcoreclr.so.dbg, as well as the rest of the runtime and framework assemblies. -- ``: The path to the core dump you are attempting to debug. -- ``: The path to the dotnet or corerun executable, potentially in the `` folder. -- ``: The path to libsosplugin.so, should be in the `` folder. - -lldb should start debugging successfully at this point. You should see stacktraces with resolved symbols for libcoreclr.so. At this point, you can run `plugin load `, and begin using SOS commands, as above. - -##### Example - -``` -lldb-3.9 -O "settings set target.exec-search-paths /home/parallels/Downloads/System.Drawing.Common.Tests/home/helixbot/dotnetbuild/work/2a74cf82-3018-4e08-9e9a-744bb492869e/Payload/shared/Microsoft.NETCore.App/9.9.9/" -o "plugin load /home/parallels/Downloads/System.Drawing.Common.Tests/home/helixbot/dotnetbuild/work/2a74cf82-3018-4e08-9e9a-744bb492869e/Payload/shared/Microsoft.NETCore.App/9.9.9/libsosplugin.so" --core /home/parallels/Downloads/System.Drawing.Common.Tests/home/helixbot/dotnetbuild/work/2a74cf82-3018-4e08-9e9a-744bb492869e/Work/f6414a62-9b41-4144-baed-756321e3e075/Unzip/core /home/parallels/Downloads/System.Drawing.Common.Tests/home/helixbot/dotnetbuild/work/2a74cf82-3018-4e08-9e9a-744bb492869e/Payload/shared/Microsoft.NETCore.App/9.9.9/dotnet -``` -Disabling Managed Attach/Debugging -================================== - -The "COMPlus_EnableDiagnostics" environment variable can be used to disable managed debugging. This prevents the various OS artifacts used for debugging like the named pipes and semaphores on Linux/MacOS and shared memory on Windows from being created. - - export COMPlus_EnableDiagnostics=0 - - -Using Visual Studio Code -======================== - -- Install [Visual Studio Code](https://code.visualstudio.com/) -- Install the [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp) -- Open the folder containing the source you want to debug in VS Code -- Open the debug window: `ctrl-shift-D` or click on the button on the left -- Click the gear button at the top to create a launch configuration, select `.NET Core` from the selection dropdown -- In the `.NET Core Launch (console)` configuration do the following - - delete the `preLaunchTask` property - - set `program` to the full path to corerun in the test directory - - set `cwd` to the test directory - - set `args` to the command line arguments to pass to the test - - something like: `[ "xunit.console.netcore.exe", ".dll", "-notrait", .... ]` -- Set a breakpoint and launch the debugger, inspecting variables and call stacks will now work diff --git a/documentation/lldb/linux-instructions.md b/documentation/lldb/linux-instructions.md index 10f61a6c1..5ee9d5952 100644 --- a/documentation/lldb/linux-instructions.md +++ b/documentation/lldb/linux-instructions.md @@ -153,7 +153,7 @@ To launch lldb: lldb -#### Fedora 27, 28 #### +#### Fedora 27, 28, 29 #### sudo dnf install lldb python2-lldb diff --git a/documentation/sos-debugging-extension-windows.md b/documentation/sos-debugging-extension-windows.md new file mode 100644 index 000000000..57100dff2 --- /dev/null +++ b/documentation/sos-debugging-extension-windows.md @@ -0,0 +1,245 @@ +--- +title: "SOS Debugging Extension" +ms.date: "10/3/2018" +helpviewer_keywords: + - "debugging extensions" + - "SOS debugging extensions" + - "xplat debugging" +ms.assetid: TBD +author: "mikem" +ms.author: "mikem" +--- +# SOS debugging extension for Windows + +## Syntax + +```shell +![command] [options] +``` +### Command Summary + +SOS is a debugger extension DLL designed to aid in the debugging of managed programs. Functions are listed by category, then roughly in order of importance. Shortcut names for popular functions are listed in parenthesis. +Type `!help ` for detailed info on that function. + + Object Inspection Examining code and stacks + ----------------------------- ----------------------------- + DumpObj (do, dumpobj) Threads (clrthreads) + DumpArray (da) ThreadState + DumpAsync IP2MD + DumpStackObjects (dso) U + DumpHeap DumpStack + DumpVC EEStack + GCRoot CLRStack + ObjSize GCInfo + FinalizeQueue EHInfo + PrintException (pe) BPMD + TraverseHeap COMState + + Examining CLR data structures Diagnostic Utilities + ----------------------------- ----------------------------- + DumpDomain VerifyHeap + EEHeap VerifyObj + Name2EE FindRoots + SyncBlk HeapStat + DumpMT GCWhere + DumpClass ListNearObj (lno) + DumpMD GCHandles + Token2EE GCHandleLeaks + EEVersion FinalizeQueue (fq) + DumpModule FindAppDomain + ThreadPool SaveModule + DumpAssembly ProcInfo + DumpSigElem StopOnException (soe) + DumpRuntimeTypes DumpLog + DumpSig VMMap + RCWCleanupList VMStat + DumpIL MinidumpMode + DumpRCW AnalyzeOOM (ao) + DumpCCW + + Examining the GC history Other + ----------------------------- ----------------------------- + HistInit SetHostRuntime (sethostruntime) + HistRoot SetSymbolServer (setsymbolserver) + HistObj FAQ + HistObjFind SOSFlush + HistClear Help (soshelp) + +## Commands + +|Command|Description| +|-------------|-----------------| +|**AnalyzeOOM** (**ao**)|Displays the information for the last out of memory (OOM) that occurred on an allocation request to the garbage collection heap. (In server garbage collection, it displays OOM, if any, on each garbage collection heap.)| +|**BPMD** (**bpmd**) [**-nofuturemodule**] [\<*module name*> \<*method name*>] [**-md** <`MethodDesc`>] **-list** **-clear** \<*pending breakpoint number*> **-clearall**|Creates a breakpoint at the specified method in the specified module.

If the specified module and method have not been loaded, this command waits for a notification that the module was loaded and just-in-time (JIT) compiled before creating a breakpoint.

You can manage the list of pending breakpoints by using the **-list**, **-clear**, and **-clearall** options:

The **-list** option generates a list of all the pending breakpoints. If a pending breakpoint has a non-zero module ID, that breakpoint is specific to a function in that particular loaded module. If the pending breakpoint has a zero module ID, that breakpoint applies to modules that have not yet been loaded.

Use the **-clear** or **-clearall** option to remove pending breakpoints from the list.| +|**CLRStack** [**-a**] [**-l**] [**-p**] [**-n**]|Provides a stack trace of managed code only.

The **-p** option shows arguments to the managed function.

The **-l** option shows information on local variables in a frame. The SOS Debugging Extension cannot retrieve local names, so the output for local names is in the format \<*local address*> **=** \<*value*>.

The **-a**(all) option is a shortcut for **-l** and **-p** combined.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS will look up the symbols for every managed frame and if successful will display the corresponding source file name and line number. The **-n** (No line numbers) parameter can be specified to disable this behavior.

The SOS Debugging Extension does not display transition frames on x64 and IA-64-based platforms.| +|**COMState**|Lists the COM apartment model for each thread and a `Context` pointer, if available.| +|**DumpArray** [**-start** \<*startIndex*>] [**-length** \<*length*>] [**-details**] [**-nofields**] \<*array object address*>

-or-

**DA** [**-start** \<*startIndex*>] [**-length** \<*length*>] [**-detail**] [**-nofields**] *array object address*>|Examines elements of an array object.

The **-start** option specifies the starting index at which to display elements.

The **-length** option specifies how many elements to show.

The **-details** option displays details of the element using the **DumpObj** and **DumpVC** formats.

The **-nofields** option prevents arrays from displaying. This option is available only when the **-detail** option is specified.| +|**DumpAssembly** \<*assembly address*>|Displays information about an assembly.

The **DumpAssembly** command lists multiple modules, if they exist.

You can get an assembly address by using the **DumpDomain** command.| +|**DumpClass** \<*EEClass address*>|Displays information about the `EEClass` structure associated with a type.

The **DumpClass** command displays static field values but does not display nonstatic field values.

Use the **DumpMT**, **DumpObj**, **Name2EE**, or **Token2EE** command to get an `EEClass` structure address.| +|**DumpDomain** [\<*domain address*>]|Enumerates each object that is loaded within the specified object address. When called with no parameters, the **DumpDomain** command lists all objects in a process.| +|**DumpHeap** [**-stat**] [**-strings**] [**-short**] [**-min** \<*size*>] [**-max** \<*size*>] [**-thinlock**] [**-startAtLowerBound**] [**-mt** \<*MethodTable address*>] [**-type** \<*partial type name*>][*start* [*end*]]|Displays information about the garbage-collected heap and collection statistics about objects.

The **DumpHeap** command displays a warning if it detects excessive fragmentation in the garbage collector heap.

The **-stat** option restricts the output to the statistical type summary.

The **-strings** option restricts the output to a statistical string value summary.

The **-short** option limits output to just the address of each object. This lets you easily pipe output from the command to another debugger command for automation.

The **-min** option ignores objects that are less than the `size` parameter, specified in bytes.

The **-max** option ignores objects that are larger than the `size` parameter, specified in bytes.

The **-thinlock** option reports ThinLocks. For more information, see the **SyncBlk** command.

The `-startAtLowerBound` option forces the heap walk to begin at the lower bound of a supplied address range. During the planning phase, the heap is often not walkable because objects are being moved. This option forces **DumpHeap** to begin its walk at the specified lower bound. You must supply the address of a valid object as the lower bound for this option to work. You can display memory at the address of a bad object to manually find the next method table. If the garbage collection is currently in a call to `memcopy`, you may also be able to find the address of the next object by adding the size to the start address, which is supplied as a parameter.

The **-mt** option lists only those objects that correspond to the specified `MethodTable` structure.

The **-type** option lists only those objects whose type name is a substring match of the specified string.

The `start` parameter begins listing from the specified address.

The `end` parameter stops listing at the specified address.| +|**DumpIL** \<*Managed DynamicMethod object*> | \<*DynamicMethodDesc pointer*> | \<*MethodDesc pointer*>|Displays the Microsoft intermediate language (MSIL) that is associated with a managed method.

Note that dynamic MSIL is emitted differently than MSIL that is loaded from an assembly. Dynamic MSIL refers to objects in a managed object array rather than to metadata tokens.| +|**DumpLog** [**-addr** \<*addressOfStressLog*>] [<*Filename*>]|Writes the contents of an in-memory stress log to the specified file. If you do not specify a name, this command creates a file called StressLog.txt in the current directory.

The in-memory stress log helps you diagnose stress failures without using locks or I/O. To enable the stress log, set the following registry keys under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\\.NETFramework:

(DWORD) StressLog = 1

(DWORD) LogFacility = 0xffffffff

(DWORD) StressLogSize = 65536

The optional `-addr` option lets you specify a stress log other than the default log.| +|**DumpMD** \<*MethodDesc address*>|Displays information about a `MethodDesc` structure at the specified address.

You can use the **IP2MD** command to get the `MethodDesc` structure address from a managed function.| +|**DumpMT** [**-MD**] \<*MethodTable address*>|Displays information about a method table at the specified address. Specifying the **-MD** option displays a list of all methods defined with the object.

Each managed object contains a method table pointer.| +|**DumpModule** [**-mt**] \<*Module address*>|Displays information about a module at the specified address. The **-mt** option displays the types defined in a module and the types referenced by the module

You can use the **DumpDomain** or **DumpAssembly** command to retrieve a module's address.| +|**DumpObj** [**-nofields**] \<*object address*>

-or-

**DO** \<*object address*>|Displays information about an object at the specified address. The **DumpObj** command displays the fields, the `EEClass` structure information, the method table, and the size of the object.

You can use the **DumpStackObjects** command to retrieve an object's address.

Note that you can run the **DumpObj** command on fields of type `CLASS` because they are also objects.

The `-`**nofields** option prevents fields of the object being displayed, it is useful for objects like String.| +|**DumpRuntimeTypes**|Displays the runtime type objects in the garbage collector heap and lists their associated type names and method tables.| +|**DumpStack** [**-EE**] [**-n**] [`top` *stack* [`bottom` *stac*`k`]]|Displays a stack trace.

The **-EE** option causes the **DumpStack** command to display only managed functions. Use the `top` and `bottom` parameters to limit the stack frames displayed on x86 platforms.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS will look up the symbols for every managed frame and if successful will display the corresponding source file name and line number. The **-n** (No line numbers) parameter can be specified to disable this behavior.

On x86 and x64 platforms, the **DumpStack** command creates a verbose stack trace.

On IA-64-based platforms, the **DumpStack** command mimics the debugger's **K** command. The `top` and `bottom` parameters are ignored on IA-64-based platforms.| +|**DumpSig** \<*sigaddr*> \<*moduleaddr*>|Displays information about a `Sig` structure at the specified address.| +|**DumpSigElem** \<*sigaddr*> \<*moduleaddr*>|Displays a single element of a signature object. In most cases, you should use **DumpSig** to look at individual signature objects. However, if a signature has been corrupted in some way, you can use **DumpSigElem** to read the valid portions of it.| +|**DumpStackObjects** [**-verify**] [`top` *stack* [`bottom` *stack*]]

-or-

**DSO** [**-verify**] [`top` *stack* [`bottom` *stack*]]|Displays all managed objects found within the bounds of the current stack.

The **-verify** option validates each non-static `CLASS` field of an object field.

Use the **DumpStackObject** command with stack tracing commands such as the **K** command and the **CLRStack** command to determine the values of local variables and parameters.| +|**DumpVC** \<*MethodTable address*> \<*Address*>|Displays information about the fields of a value class at the specified address.

The **MethodTable** parameter allows the **DumpVC** command to correctly interpret fields. Value classes do not have a method table as their first field.| +|**EEHeap** [**-gc**] [**-loader**]|Displays information about process memory consumed by internal runtime data structures.

The **-gc** and **-loader** options limit the output of this command to garbage collector or loader data structures.

The information for the garbage collector lists the ranges of each segment in the managed heap. If the pointer falls within a segment range given by **-gc**, the pointer is an object pointer.| +|**EEStack** [**-short**] [**-EE**]|Runs the **DumpStack** command on all threads in the process.

The **-EE** option is passed directly to the **DumpStack** command. The **-short** parameter limits the output to the following kinds of threads:

Threads that have taken a lock.

Threads that have been stalled in order to allow a garbage collection.

Threads that are currently in managed code.| +|**EEVersion**|Displays the runtime version.| +|**EHInfo** [\<*MethodDesc address*>] [\<*Code address*>]|Displays the exception handling blocks in a specified method. This command displays the code addresses and offsets for the clause block (the `try` block) and the handler block (the `catch` block).| +|**FAQ**|Displays frequently asked questions.| +|**FinalizeQueue** [**-detail**] | [**-allReady**] [**-short**]|Displays all objects registered for finalization.

The **-detail** option displays extra information about any `SyncBlocks` that need to be cleaned up, and any `RuntimeCallableWrappers` (RCWs) that await cleanup. Both of these data structures are cached and cleaned up by the finalizer thread when it runs.

The `-allReady` option displays all objects that are ready for finalization, regardless of whether they are already marked by the garbage collection as such, or will be marked by the next garbage collection. The objects that are in the "ready for finalization" list are finalizable objects that are no longer rooted. This option can be very expensive, because it verifies whether all the objects in the finalizable queues are still rooted.

The `-short` option limits the output to the address of each object. If it is used in conjunction with **-allReady**, it enumerates all objects that have a finalizer that are no longer rooted. If it is used independently, it lists all objects in the finalizable and "ready for finalization" queues.| +|**FindAppDomain** \<*Object address*>|Determines the application domain of an object at the specified address.| +|**FindRoots** **-gen** \<*N*> | **-gen any** |\<*object address*>|Causes the debugger to break in the debuggee on the next collection of the specified generation. The effect is reset as soon as the break occurs. To break on the next collection, you have to reissue the command. The *\* form of this command is used after the break caused by the **-gen** or **-gen any** has occurred. At that time, the debuggee is in the right state for **FindRoots** to identify roots for objects from the current condemned generations.| +|**GCHandles** [**-perdomain**]|Displays statistics about garbage collector handles in the process.

The **-perdomain** option arranges the statistics by application domain.

Use the **GCHandles** command to find memory leaks caused by garbage collector handle leaks. For example, a memory leak occurs when code retains a large array because a strong garbage collector handle still points to it, and the handle is discarded without freeing it.| +|**GCHandleLeaks**|Searches memory for any references to strong and pinned garbage collector handles in the process and displays the results. If a handle is found, the **GCHandleLeaks** command displays the address of the reference. If a handle is not found in memory, this command displays a notification.| +|**GCInfo** \<*MethodDesc address*>\<*Code address*>|Displays data that indicates when registers or stack locations contain managed objects. If a garbage collection occurs, the collector must know the locations of references to objects so it can update them with new object pointer values.| +|**GCRoot** [**-nostacks**] \<*Object address*>|Displays information about references (or roots) to an object at the specified address.

The **GCRoot** command examines the entire managed heap and the handle table for handles within other objects and handles on the stack. Each stack is then searched for pointers to objects, and the finalizer queue is also searched.

This command does not determine whether a stack root is valid or is discarded. Use the **CLRStack** and **U** commands to disassemble the frame that the local or argument value belongs to in order to determine if the stack root is still in use.

The **-nostacks** option restricts the search to garbage collector handles and freachable objects.| +|**GCWhere** *\*|Displays the location and size in the garbage collection heap of the argument passed in. When the argument lies in the managed heap but is not a valid object address, the size is displayed as 0 (zero).| +|**Help** (**soshelp**) [\<*command*>] [`faq`]|Displays all available commands when no parameter is specified, or displays detailed help information about the specified command.

The `faq` parameter displays answers to frequently asked questions.| +|**HeapStat** [**-inclUnrooted** | **-iu**]|Displays the generation sizes for each heap and the total free space in each generation on each heap. If the -**inclUnrooted** option is specified, the report includes information about the managed objects from the garbage collection heap that is no longer rooted.| +|**HistClear**|Releases any resources used by the family of `Hist` commands.

Generally, you do not have to explicitly call `HistClear`, because each `HistInit` cleans up the previous resources.| +|**HistInit**|Initializes the SOS structures from the stress log saved in the debuggee.| +|**HistObj** **|Examines all stress log relocation records and displays the chain of garbage collection relocations that may have led to the address passed in as an argument.| +|**HistObjFind** **|Displays all the log entries that reference an object at the specified address.| +|**HistRoot** *\*|Displays information related to both promotions and relocations of the specified root.

The root value can be used to track the movement of an object through the garbage collections.| +|**IP2MD** \<*Code address*>|Displays the `MethodDesc` structure at the specified address in code that has been JIT-compiled.| +|**ListNearObj** (**lno**) **|Displays the objects preceding and following the specified address. The command looks for the address in the garbage collection heap that looks like a valid beginning of a managed object (based on a valid method table) and the object following the argument address.| +|**MinidumpMode** [**0**] [**1**]|Prevents running unsafe commands when using a minidump.

Pass **0** to disable this feature or **1** to enable this feature. By default, the **MinidumpMode** value is set to **0**.

Minidumps created with the **.dump /m** command or **.dump** command have limited CLR-specific data and allow you to run only a subset of SOS commands correctly. Some commands may fail with unexpected errors because required areas of memory are not mapped or are only partially mapped. This option protects you from running unsafe commands against minidumps.| +|**Name2EE** \<*module name*> \<*type or method name*>

-or-

**Name2EE** \<*module name*>**!**\<*type or method name*>|Displays the `MethodTable` structure and `EEClass` structure for the specified type or method in the specified module.

The specified module must be loaded in the process.

To get the proper type name, browse the module by using the [Ildasm.exe (IL Disassembler)](../../../docs/framework/tools/ildasm-exe-il-disassembler.md). You can also pass `*` as the module name parameter to search all loaded managed modules. The *module name* parameter can also be the debugger's name for a module, such as `mscorlib` or `image00400000`.

This command supports the Windows debugger syntax of <`module`>`!`<`type`>. The type must be fully qualified.| +|**ObjSize** [\<*Object address*>] | [**-aggregate**] [**-stat**]|Displays the size of the specified object. If you do not specify any parameters, the **ObjSize** command displays the size of all objects found on managed threads, displays all garbage collector handles in the process, and totals the size of any objects pointed to by those handles. The **ObjSize** command includes the size of all child objects in addition to the parent.

The **-aggregate** option can be used in conjunction with the **-stat** argument to get a detailed view of the types that are still rooted. By using **!dumpheap -stat** and **!objsize -aggregate -stat**, you can determine which objects are no longer rooted and diagnose various memory issues.| +|**PrintException** [**-nested**] [**-lines**] [\<*Exception object address*>]

-or-

**PE** [**-nested**] [\<*Exception object address*>]|Displays and formats fields of any object derived from the class at the specified address. If you do not specify an address, the **PrintException** command displays the last exception thrown on the current thread.

The **-nested** option displays details about nested exception objects.

The **-lines** option displays source information, if available.

You can use this command to format and view the `_stackTrace` field, which is a binary array.| +|**ProcInfo** [**-env**] [**-time**] [**-mem**]|Displays environment variables for the process, kernel CPU time, and memory usage statistics.| +|**RCWCleanupList** \<*RCWCleanupList address*>|Displays the list of runtime callable wrappers at the specified address that are awaiting cleanup.| +|**SaveModule** \<*Base address*> \<*Filename*>|Writes an image, which is loaded in memory at the specified address, to the specified file.| +|**SOSFlush**|Flushes an internal SOS cache.| +|**StopOnException** [**-derived**] [**-create** | **-create2**] \<*Exception*> \<*Pseudo-register number*>|Causes the debugger to stop when the specified exception is thrown, but to continue running when other exceptions are thrown.

The **-derived** option catches the specified exception and every exception that derives from the specified exception.| +|**SyncBlk** [**-all** | \<*syncblk number*>]|Displays the specified `SyncBlock` structure or all `SyncBlock` structures. If you do not pass any arguments, the **SyncBlk** command displays the `SyncBlock` structure corresponding to objects that are owned by a thread.

A `SyncBlock` structure is a container for extra information that does not need to be created for every object. It can hold COM interop data, hash codes, and locking information for thread-safe operations.| +|**ThreadPool**|Displays information about the managed thread pool, including the number of work requests in the queue, the number of completion port threads, and the number of timers.| +|**Token2EE** \<*module name*> \<*token*>|Turns the specified metadata token in the specified module into a `MethodTable` structure or `MethodDesc` structure.

You can pass `*` for the module name parameter to find what that token maps to in every loaded managed module. You can also pass the debugger's name for a module, such as `mscorlib` or `image00400000`.| +|**Threads** [**-live**] [**-special**]|Displays all managed threads in the process.

The **Threads** command displays the debugger shorthand ID, the CLR thread ID, and the operating system thread ID. Additionally, the **Threads** command displays a Domain column that indicates the application domain in which a thread is executing, an APT column that displays the COM apartment mode, and an Exception column that displays the last exception thrown in the thread.

The **-live** option displays threads associated with a live thread.

The **-special** option displays all special threads created by the CLR. Special threads include garbage collection threads (in concurrent and server garbage collection), debugger helper threads, finalizer threads, unload threads, and thread pool timer threads.| +|**ThreadState \<** *State value field* **>**|Displays the state of the thread. The `value` parameter is the value of the `State` field in the **Threads** report output.| +|**TraverseHeap** [**-xml**] \<*filename*>|Writes heap information to the specified file in a format understood by the CLR profiler. The **-xml** option causes the **TraverseHeap** command to format the file as XML.

You can download the CLR Profiler from the [Microsoft Download Center](https://go.microsoft.com/fwlink/?LinkID=67325).| +|**U** [**-gcinfo**] [**-ehinfo**] [**-n**] \<*MethodDesc address*> | \<*Code address*>|Displays an annotated disassembly of a managed method specified either by a `MethodDesc` structure pointer for the method or by a code address within the method body. The **U** command displays the entire method from start to finish, with annotations that convert metadata tokens to names.

The **-gcinfo** option causes the **U** command to display the `GCInfo` structure for the method.

The **-ehinfo** option displays exception information for the method. You can also obtain this information with the **EHInfo** command.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS looks up the symbols for every managed frame and, if successful, displays the corresponding source file name and line number. You can specify the **-n** option to disable this behavior.| +|**VerifyHeap**|Checks the garbage collector heap for signs of corruption and displays any errors found.

Heap corruptions can be caused by platform invoke calls that are constructed incorrectly.| +|**VerifyObj** \<*object address*>|Checks the object that is passed as an argument for signs of corruption.| +|**VMMap**|Traverses the virtual address space and displays the type of protection applied to each region.| +|**VMStat**|Provides a summary view of the virtual address space, ordered by each type of protection applied to that memory (free, reserved, committed, private, mapped, image). The TOTAL column displays the result of the AVERAGE column multiplied by the BLK COUNT column.| + +## Remarks + +The SOS Debugging Extension lets you view information about code that is running inside the CLR. For example, you can use the SOS Debugging Extension to display information about the managed heap, look for heap corruptions, display internal data types used by the runtime, and view information about all managed code running inside the runtime. + +To use the SOS Debugging Extension in Visual Studio, install the [Windows Driver Kit (WDK)](/windows-hardware/drivers/download-the-wdk). For information about the integrated debugging environment in Visual Studio, see [Debugging Environments](/windows-hardware/drivers/debugger/debuggers-in-the-debugging-tools-for-windows-package). + +You can also use the SOS Debugging Extension by loading it into the [WinDbg.exe debugger](/windows-hardware/drivers/debugger/debugger-download-tools) and executing commands within WinDbg.exe. + +To load the SOS Debugging Extension into the WinDbg.exe debugger, run the following command in the tool: + +``` +.loadby sos coreclr +``` + +WinDbg.exe and Visual Studio use a version of SOS.dll that corresponds to the version of Mscorwks.dll currently in use. By default, you should use the version of SOS.dll that matches the current version of Mscorwks.dll. + +To use a dump file created on another computer, make sure that the Mscorwks.dll file that came with that installation is in your symbol path, and load the corresponding version of SOS.dll. + +To load a specific version of SOS.dll, type the following command into the Windows Debugger: + +``` +.load +``` + +## Examples + +The following command displays the contents of an array at the address `00ad28d0`. The display starts from the second element and continues for five elements. + +``` +!dumparray -start 2 -length 5 -detail 00ad28d0 +``` + + The following command displays the contents of an assembly at the address `1ca248`. + +``` +!dumpassembly 1ca248 +``` + + The following command displays information about the garbage collector heap. + +``` +!dumpheap +``` + + The following command writes the contents of the in-memory stress log to a (default) file called StressLog.txt in the current directory. + +``` +!DumpLog +``` + + The following command displays the `MethodDesc` structure at the address `902f40`. + +``` +!dumpmd 902f40 +``` + + The following command displays information about a module at the address `1caa50`. + +``` +!dumpmodule 1caa50 +``` + + The following command displays information about an object at the address `a79d40`. + +``` +!DumpObj a79d40 +``` + + The following command displays the fields of a value class at the address `00a79d9c` using the method table at the address `0090320c`. + +``` +!DumpVC 0090320c 00a79d9c +``` + + The following command displays the process memory used by the garbage collector. + +``` +!eeheap -gc +``` + + The following command displays all objects scheduled for finalization. + +``` +!finalizequeue +``` + + The following command determines the application domain of an object at the address `00a79d98`. + +``` +!findappdomain 00a79d98 +``` + + The following command displays all garbage collector handles in the current process. + +``` +!gcinfo 5b68dbb8 +``` + + The following command displays the `MethodTable` and `EEClass` structures for the `Main` method in the class `MainClass` in the module `unittest.exe`. + +``` +!name2ee unittest.exe MainClass.Main +``` + + The following command displays information about the metadata token at the address `02000003` in the module `unittest.exe`. + +``` +!token2ee unittest.exe 02000003 +``` + diff --git a/documentation/sos-debugging-extension.md b/documentation/sos-debugging-extension.md new file mode 100644 index 000000000..041da8a69 --- /dev/null +++ b/documentation/sos-debugging-extension.md @@ -0,0 +1,266 @@ +--- +title: "SOS Debugging Extension" +ms.date: "10/3/2018" +helpviewer_keywords: + - "debugging extensions" + - "SOS debugging extensions" + - "xplat debugging" +ms.assetid: TBD +author: "mikem" +ms.author: "mikem" +--- +# SOS debugging extension for xplat + +The SOS Debugging Extension lets you view information about code that is running inside the .NET Core runtime. For example, you can use the SOS Debugging Extension to display information about the managed heap, look for heap corruptions, display internal data types used by the runtime, and view information about all managed code running inside the runtime. + +## Syntax + +```shell +sos [command] [options] +``` + +Many of the commands have aliases or short cuts under lldb: + +``` +clrstack [options] +``` + +### Command Summary + +SOS is a lldb debugger extension designed to aid in the debugging of managed programs. Functions are listed by category, then roughly in order of +importance. Shortcut names for popular functions are listed in parenthesis. Type `soshelp ` for detailed info on that function. + + Object Inspection Examining code and stacks + ----------------------------- ----------------------------- + DumpObj (dumpobj) Threads (clrthreads) + DumpArray ThreadState + DumpAsync (dumpasync) IP2MD (ip2md) + DumpStackObjects (dso) u (clru) + DumpHeap (dumpheap) DumpStack (dumpstack) + DumpVC EEStack (eestack) + GCRoot (gcroot) CLRStack (clrstack) + PrintException (pe) GCInfo + EHInfo + bpmd (bpmd) + + Examining CLR data structures Diagnostic Utilities + ----------------------------- ----------------------------- + DumpDomain (dumpdomain) VerifyHeap + EEHeap (eeheap) FindAppDomain + Name2EE (name2ee) DumpLog (dumplog) + DumpMT (dumpmt) + DumpClass (dumpclass) + DumpMD (dumpmd) + Token2EE + DumpModule (dumpmodule) + DumpAssembly + DumpRuntimeTypes + DumpIL (dumpil) + DumpSig + DumpSigElem + + Examining the GC history Other + ----------------------------- ----------------------------- + HistInit (histinit) SetHostRuntime (sethostruntime) + HistRoot (histroot) SetSymbolServer (setsymbolserver, loadsymbols) + HistObj (histobj) FAQ + HistObjFind (histobjfind) SOSFlush + HistClear (histclear) Help (soshelp) + +## Commands + +|Command|Description| +|-------------|-----------------| +|**bpmd** [**-nofuturemodule**] [\<*module name*> \<*method name*>] [**-md** <`MethodDesc`>] **-list** **-clear** \<*pending breakpoint number*> **-clearall**|Creates a breakpoint at the specified method in the specified module.

If the specified module and method have not been loaded, this command waits for a notification that the module was loaded and just-in-time (JIT) compiled before creating a breakpoint.

You can manage the list of pending breakpoints by using the **-list**, **-clear**, and **-clearall** options:

The **-list** option generates a list of all the pending breakpoints. If a pending breakpoint has a non-zero module ID, that breakpoint is specific to a function in that particular loaded module. If the pending breakpoint has a zero module ID, that breakpoint applies to modules that have not yet been loaded.

Use the **-clear** or **-clearall** option to remove pending breakpoints from the list.| +|**ClrStack** (**clrstack**) [**-a**] [**-l**] [**-p**] [**-n**]|Provides a stack trace of managed code only.

The **-p** option shows arguments to the managed function.

The **-l** option shows information on local variables in a frame. The SOS Debugging Extension cannot retrieve local names, so the output for local names is in the format \<*local address*> **=** \<*value*>.

The **-a**(all) option is a shortcut for **-l** and **-p** combined.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS will look up the symbols for every managed frame and if successful will display the corresponding source file name and line number. The **-n** (No line numbers) parameter can be specified to disable this behavior.

The SOS Debugging Extension does not display transition frames on x64 and IA-64-based platforms.| +|**DumpArray** [**-start** \<*startIndex*>] [**-length** \<*length*>] [**-details**] [**-nofields**] \<*array object address*>

-or-

**DA** [**-start** \<*startIndex*>] [**-length** \<*length*>] [**-detail**] [**-nofields**] *array object address*>|Examines elements of an array object.

The **-start** option specifies the starting index at which to display elements.

The **-length** option specifies how many elements to show.

The **-details** option displays details of the element using the **DumpObj** and **DumpVC** formats.

The **-nofields** option prevents arrays from displaying. This option is available only when the **-detail** option is specified.| +|**DumpAsync** (**dumpasync**) [**-mt** \<*MethodTable address*>] [**-type** \<*partial type name*>] [**-waiting**] [**-roots**]|DumpAsync traverses the garbage collected heap, looking for objects representing async state machines as created when an async method's state is transferred to the heap. This command recognizes async state machines defined as `async void`, `async Task`, `async Task`, `async ValueTask`, and `async ValueTask`.

The output includes a block of details for each async state machine object found. These details include:
- a line for the type of the async state machine object, including its MethodTable address, its object address, its size, and its type name.
- a line for the state machine type name as contained in the object.
- a listing of each field on the state machine.
- a line for a continuation from this state machine object, if one or more has been registered.
- discovered GC roots for this async state machine object.
| +|**DumpAssembly** \<*assembly address*>|Displays information about an assembly.

The **DumpAssembly** command lists multiple modules, if they exist.

You can get an assembly address by using the **DumpDomain** command.| +|**DumpClass** \<*EEClass address*>|Displays information about the `EEClass` structure associated with a type.

The **DumpClass** command displays static field values but does not display nonstatic field values.

Use the **DumpMT**, **DumpObj**, **Name2EE**, or **Token2EE** command to get an `EEClass` structure address.| +|**DumpDomain** [\<*domain address*>]|Enumerates each object that is loaded within the specified object address. When called with no parameters, the **DumpDomain** command lists all objects in a process.| +|**DumpHeap** [**-stat**] [**-strings**] [**-short**] [**-min** \<*size*>] [**-max** \<*size*>] [**-thinlock**] [**-startAtLowerBound**] [**-mt** \<*MethodTable address*>] [**-type** \<*partial type name*>][*start* [*end*]]|Displays information about the garbage-collected heap and collection statistics about objects.

The **DumpHeap** command displays a warning if it detects excessive fragmentation in the garbage collector heap.

The **-stat** option restricts the output to the statistical type summary.

The **-strings** option restricts the output to a statistical string value summary.

The **-short** option limits output to just the address of each object. This lets you easily pipe output from the command to another debugger command for automation.

The **-min** option ignores objects that are less than the `size` parameter, specified in bytes.

The **-max** option ignores objects that are larger than the `size` parameter, specified in bytes.

The **-thinlock** option reports ThinLocks. For more information, see the **SyncBlk** command.

The `-startAtLowerBound` option forces the heap walk to begin at the lower bound of a supplied address range. During the planning phase, the heap is often not walkable because objects are being moved. This option forces **DumpHeap** to begin its walk at the specified lower bound. You must supply the address of a valid object as the lower bound for this option to work. You can display memory at the address of a bad object to manually find the next method table. If the garbage collection is currently in a call to `memcopy`, you may also be able to find the address of the next object by adding the size to the start address, which is supplied as a parameter.

The **-mt** option lists only those objects that correspond to the specified `MethodTable` structure.

The **-type** option lists only those objects whose type name is a substring match of the specified string.

The `start` parameter begins listing from the specified address.

The `end` parameter stops listing at the specified address.| +|**DumpIL** \<*Managed DynamicMethod object*> | \<*DynamicMethodDesc pointer*> | \<*MethodDesc pointer*>|Displays the Microsoft intermediate language (MSIL) that is associated with a managed method.

Note that dynamic MSIL is emitted differently than MSIL that is loaded from an assembly. Dynamic MSIL refers to objects in a managed object array rather than to metadata tokens.| +|**DumpLog** [**-addr** \<*addressOfStressLog*>] [<*Filename*>]|Writes the contents of an in-memory stress log to the specified file. If you do not specify a name, this command creates a file called StressLog.txt in the current directory.

The in-memory stress log helps you diagnose stress failures without using locks or I/O. To enable the stress log, set the following registry keys under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\\.NETFramework:

(DWORD) StressLog = 1

(DWORD) LogFacility = 0xffffffff

(DWORD) StressLogSize = 65536

The optional `-addr` option lets you specify a stress log other than the default log.| +|**DumpMD** \<*MethodDesc address*>|Displays information about a `MethodDesc` structure at the specified address.

You can use the **IP2MD** command to get the `MethodDesc` structure address from a managed function.| +|**DumpMT** [**-MD**] \<*MethodTable address*>|Displays information about a method table at the specified address. Specifying the **-MD** option displays a list of all methods defined with the object.

Each managed object contains a method table pointer.| +|**DumpModule** [**-mt**] \<*Module address*>|Displays information about a module at the specified address. The **-mt** option displays the types defined in a module and the types referenced by the module

You can use the **DumpDomain** or **DumpAssembly** command to retrieve a module's address.| +|**DumpObj** [**-nofields**] \<*object address*>

-or-

**DO** \<*object address*>|Displays information about an object at the specified address. The **DumpObj** command displays the fields, the `EEClass` structure information, the method table, and the size of the object.

You can use the **DumpStackObjects** command to retrieve an object's address.

Note that you can run the **DumpObj** command on fields of type `CLASS` because they are also objects.

The `-`**nofields** option prevents fields of the object being displayed, it is useful for objects like String.| +|**DumpRuntimeTypes**|Displays the runtime type objects in the garbage collector heap and lists their associated type names and method tables.| +|**DumpStack** [**-EE**] [**-n**] [`top` *stack* [`bottom` *stac*`k`]]|Displays a stack trace.

The **-EE** option causes the **DumpStack** command to display only managed functions. Use the `top` and `bottom` parameters to limit the stack frames displayed on x86 platforms.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS will look up the symbols for every managed frame and if successful will display the corresponding source file name and line number. The **-n** (No line numbers) parameter can be specified to disable this behavior.

On x86 and x64 platforms, the **DumpStack** command creates a verbose stack trace.

On IA-64-based platforms, the **DumpStack** command mimics the debugger's **K** command. The `top` and `bottom` parameters are ignored on IA-64-based platforms.| +|**DumpSig** \<*sigaddr*> \<*moduleaddr*>|Displays information about a `Sig` structure at the specified address.| +|**DumpSigElem** \<*sigaddr*> \<*moduleaddr*>|Displays a single element of a signature object. In most cases, you should use **DumpSig** to look at individual signature objects. However, if a signature has been corrupted in some way, you can use **DumpSigElem** to read the valid portions of it.| +|**DumpStackObjects** [**-verify**] [`top` *stack* [`bottom` *stack*]]

-or-

**DSO** [**-verify**] [`top` *stack* [`bottom` *stack*]]|Displays all managed objects found within the bounds of the current stack.

The **-verify** option validates each non-static `CLASS` field of an object field.

Use the **DumpStackObject** command with stack tracing commands such as the **K** command and the **clrstack** command to determine the values of local variables and parameters.| +|**DumpVC** \<*MethodTable address*> \<*Address*>|Displays information about the fields of a value class at the specified address.

The **MethodTable** parameter allows the **DumpVC** command to correctly interpret fields. Value classes do not have a method table as their first field.| +|**EEHeap** [**-gc**] [**-loader**]|Displays information about process memory consumed by internal runtime data structures.

The **-gc** and **-loader** options limit the output of this command to garbage collector or loader data structures.

The information for the garbage collector lists the ranges of each segment in the managed heap. If the pointer falls within a segment range given by **-gc**, the pointer is an object pointer.| +|**EEStack** [**-short**] [**-EE**]|Runs the **DumpStack** command on all threads in the process.

The **-EE** option is passed directly to the **DumpStack** command. The **-short** parameter limits the output to the following kinds of threads:

Threads that have taken a lock.

Threads that have been stalled in order to allow a garbage collection.

Threads that are currently in managed code.| +|**EHInfo** [\<*MethodDesc address*>] [\<*Code address*>]|Displays the exception handling blocks in a specified method. This command displays the code addresses and offsets for the clause block (the `try` block) and the handler block (the `catch` block).| +|**FAQ**|Displays frequently asked questions.| +|**FindAppDomain** \<*Object address*>|Determines the application domain of an object at the specified address.| +|**GCInfo** \<*MethodDesc address*>\<*Code address*>|Displays data that indicates when registers or stack locations contain managed objects. If a garbage collection occurs, the collector must know the locations of references to objects so it can update them with new object pointer values.| +|**GCRoot** [**-nostacks**] \<*Object address*>|Displays information about references (or roots) to an object at the specified address.

The **GCRoot** command examines the entire managed heap and the handle table for handles within other objects and handles on the stack. Each stack is then searched for pointers to objects, and the finalizer queue is also searched.

This command does not determine whether a stack root is valid or is discarded. Use the **clrstack** and **U** commands to disassemble the frame that the local or argument value belongs to in order to determine if the stack root is still in use.

The **-nostacks** option restricts the search to garbage collector handles and freachable objects.| +|**GCWhere** *\*|Displays the location and size in the garbage collection heap of the argument passed in. When the argument lies in the managed heap but is not a valid object address, the size is displayed as 0 (zero).| +|**Help** (**soshelp**) [\<*command*>] [`faq`]|Displays all available commands when no parameter is specified, or displays detailed help information about the specified command.

The `faq` parameter displays answers to frequently asked questions.| +|**HistClear**|Releases any resources used by the family of `Hist` commands.

Generally, you do not have to explicitly call `HistClear`, because each `HistInit` cleans up the previous resources.| +|**HistInit**|Initializes the SOS structures from the stress log saved in the debuggee.| +|**HistObj** **|Examines all stress log relocation records and displays the chain of garbage collection relocations that may have led to the address passed in as an argument.| +|**HistObjFind** **|Displays all the log entries that reference an object at the specified address.| +|**HistRoot** *\*|Displays information related to both promotions and relocations of the specified root.

The root value can be used to track the movement of an object through the garbage collections.| +|**IP2MD** (**ip2md**) \<*Code address*>|Displays the `MethodDesc` structure at the specified address in code that has been JIT-compiled.| +|**Name2EE** (**name2ee**) \<*module name*> \<*type or method name*>

-or-

**Name2EE** \<*module name*>**!**\<*type or method name*>|Displays the `MethodTable` structure and `EEClass` structure for the specified type or method in the specified module.

The specified module must be loaded in the process.

To get the proper type name, browse the module by using the [Ildasm.exe (IL Disassembler)](../../../docs/framework/tools/ildasm-exe-il-disassembler.md). You can also pass `*` as the module name parameter to search all loaded managed modules. The *module name* parameter can also be the debugger's name for a module, such as `mscorlib` or `image00400000`.

This command supports the Windows debugger syntax of <`module`>`!`<`type`>. The type must be fully qualified.| +|**PrintException** [**-nested**] [**-lines**] [\<*Exception object address*>]

-or-

**PE** [**-nested**] [\<*Exception object address*>]|Displays and formats fields of any object derived from the class at the specified address. If you do not specify an address, the **PrintException** command displays the last exception thrown on the current thread.

The **-nested** option displays details about nested exception objects.

The **-lines** option displays source information, if available.

You can use this command to format and view the `_stackTrace` field, which is a binary array.| +|**SyncBlk** [**-all** | \<*syncblk number*>]|Displays the specified `SyncBlock` structure or all `SyncBlock` structures. If you do not pass any arguments, the **SyncBlk** command displays the `SyncBlock` structure corresponding to objects that are owned by a thread.

A `SyncBlock` structure is a container for extra information that does not need to be created for every object. It can hold COM interop data, hash codes, and locking information for thread-safe operations.| +|**SOSFlush**|Flushes an internal SOS cache.| +|**SetSymbolServer** [**-ms**] [**-disable**] [**-log**] [**-cache** \] [**-loadsymbols**] [\]|Enables the symbol server downloading support.

The **-ms** option enables downloading from the public Microsoft symbol server.

The **-disable** option turns on the symbol download support.

The **-cache** \ option specifies a symbol cache directory. The default is $HOME/.sos/symbolcache if not specified.

The **-log** option enables symbol download logging.

The **-loadsymbols** option attempts to download the native .NET Core symbols for the runtime.| +|**Token2EE** \<*module name*> \<*token*>|Turns the specified metadata token in the specified module into a `MethodTable` structure or `MethodDesc` structure.

You can pass `*` for the module name parameter to find what that token maps to in every loaded managed module. You can also pass the debugger's name for a module, such as `mscorlib` or `image00400000`.| +|**Threads** (**clrthreads**) [**-live**] [**-special**]|Displays all managed threads in the process.

The **Threads** command displays the debugger shorthand ID, the CLR thread ID, and the operating system thread ID. Additionally, the **Threads** command displays a Domain column that indicates the application domain in which a thread is executing, an APT column that displays the COM apartment mode, and an Exception column that displays the last exception thrown in the thread.

The **-live** option displays threads associated with a live thread.

The **-special** option displays all special threads created by the CLR. Special threads include garbage collection threads (in concurrent and server garbage collection), debugger helper threads, finalizer threads, unload threads, and thread pool timer threads.| +|**ThreadState \<** *State value field* **>**|Displays the state of the thread. The `value` parameter is the value of the `State` field in the **Threads** report output.| +|**U** [**-gcinfo**] [**-ehinfo**] [**-n**] \<*MethodDesc address*> | \<*Code address*>|Displays an annotated disassembly of a managed method specified either by a `MethodDesc` structure pointer for the method or by a code address within the method body. The **U** command displays the entire method from start to finish, with annotations that convert metadata tokens to names.

The **-gcinfo** option causes the **U** command to display the `GCInfo` structure for the method.

The **-ehinfo** option displays exception information for the method. You can also obtain this information with the **EHInfo** command.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS looks up the symbols for every managed frame and, if successful, displays the corresponding source file name and line number. You can specify the **-n** option to disable this behavior.| +|**VerifyHeap**|Checks the garbage collector heap for signs of corruption and displays any errors found.

Heap corruptions can be caused by platform invoke calls that are constructed incorrectly.| + +### Aliases ### + +By default you can reach all the SOS commands by using: _sos [command\_name]_ +However the common commands have been aliased so that you don't need the SOS prefix: + + bpmd -- Creates a breakpoint at the specified managed method in the specified module. + clrstack -- Provides a stack trace of managed code only. + clrthreads -- List the managed threads running. + clru -- Displays an annotated disassembly of a managed method. + dso -- Displays all managed objects found within the bounds of the current stack. + dumpasync -- Displays info about async state machines on the garbage-collected heap. + dumpclass -- Displays information about a EE class structure at the specified address. + dumpdomain -- Displays information all the AppDomains and all assemblies within the domains. + dumpheap -- Displays info about the garbage-collected heap and collection statistics about objects. + dumpil -- Displays the Microsoft intermediate language (MSIL) that is associated with a managed method. + dumplog -- Writes the contents of an in-memory stress log to the specified file. + dumpmd -- Displays information about a MethodDesc structure at the specified address. + dumpmodule -- Displays information about a EE module structure at the specified address. + dumpmt -- Displays information about a method table at the specified address. + dumpobj -- Displays info about an object at the specified address. + dumpstack -- Displays a native and managed stack trace. + eeheap -- Displays info about process memory consumed by internal runtime data structures. + eestack -- Runs dumpstack on all threads in the process. + gcroot -- Displays info about references (or roots) to an object at the specified address. + histclear -- Releases any resources used by the family of Hist commands. + histinit -- Initializes the SOS structures from the stress log saved in the debuggee. + histobj -- Examines all stress log relocation records and displays the chain of garbage collection relocations that may have led to the address passed in as an argument. + histobjfind -- Displays all the log entries that reference an object at the specified address. + histroot -- Displays information related to both promotions and relocations of the specified root. + ip2md -- Displays the MethodDesc structure at the specified address in code that has been JIT-compiled. + loadsymbols -- Load the .NET Core native module symbols. + name2ee -- Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module. + pe -- Displays and formats fields of any object derived from the Exception class at the specified address. + setclrpath -- Set the path to load coreclr dac/dbi files. setclrpath + sethostruntime -- Sets or displays the .NET Core runtime directory to use to run managed code in SOS. + setsymbolserver -- Enables the symbol server support. + setsostid -- Set the current os tid/thread index instead of using the one lldb provides. setsostid + sos -- Various coreclr debugging commands. See 'soshelp' for more details. sos + soshelp -- Displays all available commands when no parameter is specified, or displays detailed help information about the specified command. soshelp + syncblk -- Displays the SyncBlock holder info. + +## Remarks + +TBD + +## Examples + +The following command displays the contents of an array at the address `00ad28d0`. The display starts from the second element and continues for five elements. + +``` +sos DumpArray -start 2 -length 5 -detail 00ad28d0 +``` + + The following command displays the contents of an assembly at the address `1ca248`. + +``` +sos DumpAssembly 1ca248 +``` + + The following command displays information about the garbage collector heap. + +``` +dumpheap +``` + + The following command writes the contents of the in-memory stress log to a (default) file called StressLog.txt in the current directory. + +``` +dumplog +``` + + The following command displays the `MethodDesc` structure at the address `902f40`. + +``` +dumpmd 902f40 +``` + + The following command displays information about a module at the address `1caa50`. + +``` +dumpmodule 1caa50 +``` + + The following command displays information about an object at the address `a79d40`. + +``` +dumpobj a79d40 +``` + + The following command displays the fields of a value class at the address `00a79d9c` using the method table at the address `0090320c`. + +``` +sos DumpVC 0090320c 00a79d9c +``` + + The following command displays the process memory used by the garbage collector. + +``` +eeheap -gc +``` + + The following command determines the application domain of an object at the address `00a79d98`. + +``` +sos FindAppDomain 00a79d98 +``` + + The following command displays all garbage collector handles in the current process. + +``` +sos GCInfo 5b68dbb8 +``` + + The following command displays the `MethodTable` and `EEClass` structures for the `Main` method in the class `MainClass` in the module `unittest.exe`. + +``` +name2ee unittest.exe MainClass.Main +``` + + The following command displays information about the metadata token at the address `02000003` in the module `unittest.exe`. + +``` +sos Token2EE unittest.exe 02000003 +``` + + The following displays the managed threads and the threadstate for one: + +``` +clrthreads +``` +``` +ThreadCount: 2 +UnstartedThread: 0 +BackgroundThread: 1 +PendingThread: 0 +DeadThread: 0 +Hosted Runtime: no + Lock + ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception + 1 1 12fd 0000000000673A90 20020 Cooperative 00007FFF5801D9A0:00007FFF5801E808 0000000000654CD0 0 Ukn + 7 2 1306 0000000000697E90 21220 Preemptive 0000000000000000:0000000000000000 0000000000654CD0 0 Ukn (Finalizer) +(lldb) sos ThreadState 21220 + Legal to Join + Background + CLR Owns + Fully initialized +``` diff --git a/src/SOS/SOS.NETCore/SymbolReader.cs b/src/SOS/SOS.NETCore/SymbolReader.cs index 377014ffc..d5d1e25b4 100644 --- a/src/SOS/SOS.NETCore/SymbolReader.cs +++ b/src/SOS/SOS.NETCore/SymbolReader.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.FileFormats; +using Microsoft.FileFormats.ELF; +using Microsoft.FileFormats.MachO; using Microsoft.FileFormats.PE; using Microsoft.SymbolStore; using Microsoft.SymbolStore.KeyGenerators; @@ -89,14 +92,11 @@ namespace SOS public override int Read(byte[] buffer, int offset, int count) { - if (Position + count > Length) - { + if (Position + count > Length) { throw new ArgumentOutOfRangeException(); } - unsafe - { - fixed (byte* p = &buffer[offset]) - { + unsafe { + fixed (byte* p = &buffer[offset]) { int read = _readMemory(_address + (ulong)Position, p, count); Position += read; return read; @@ -106,8 +106,7 @@ namespace SOS public override long Seek(long offset, SeekOrigin origin) { - switch (origin) - { + switch (origin) { case SeekOrigin.Begin: Position = offset; break; @@ -154,43 +153,49 @@ namespace SOS /// internal delegate void WriteLine([MarshalAs(UnmanagedType.LPStr)] string message); - static bool s_symbolCacheAdded = false; + /// + /// The LoadNativeSymbols callback + /// + /// module file name + /// symbol file name and path + internal delegate void SymbolFileCallback( + IntPtr parameter, + [MarshalAs(UnmanagedType.LPStr)] string moduleFileName, + [MarshalAs(UnmanagedType.LPStr)] string symbolFileName); + static SymbolStore s_symbolStore = null; + static bool s_symbolCacheAdded = false; + static string s_tempDirectory = null; static ITracer s_tracer = null; /// /// Initializes symbol loading. Adds the symbol server and/or the cache path (if not null) to the list of /// symbol servers. This API can be called more than once to add more servers to search. /// - /// output callback delegate (not used currently) + /// if true, logging diagnostics to console /// if true, use the public microsoft server /// if true, use symweb internal server and protocol (file.ptr) /// symbol server url (optional) /// symbol cache directory path (optional) /// windows symbol path (optional) /// - internal static bool InitializeSymbolStore(WriteLine output, bool msdl, bool symweb, string symbolServerPath, string symbolCachePath, string windowsSymbolPath) + internal static bool InitializeSymbolStore(bool logging, bool msdl, bool symweb, string symbolServerPath, string symbolCachePath, string windowsSymbolPath) { - if (s_tracer == null) - { - s_tracer = new Tracer(enabled: true, enabledVerbose: false, output); + if (s_tracer == null) { + s_tracer = new Tracer(enabled: logging, enabledVerbose: logging, Console.WriteLine); } SymbolStore store = s_symbolStore; // Build the symbol stores - if (windowsSymbolPath != null) - { + if (windowsSymbolPath != null) { // Parse the Windows symbol path syntax (srv*, cache*, etc) - if (!ParseSymbolPath(ref store, windowsSymbolPath)) - { + if (!ParseSymbolPath(ref store, windowsSymbolPath)) { return false; } } - else - { + else { // Build the symbol stores using the other parameters - if (!GetServerSymbolStore(ref store, msdl, symweb, symbolServerPath, symbolCachePath)) - { + if (!GetServerSymbolStore(ref store, msdl, symweb, symbolServerPath, symbolCachePath)) { return false; } } @@ -200,160 +205,91 @@ namespace SOS } /// - /// Parses the Windows debugger symbol path (srv*, cache*, etc.). + /// This function disables any symbol downloading support. /// - /// symbol store to chain - /// Windows symbol path - /// if false, error parsing symbol path - private static bool ParseSymbolPath(ref SymbolStore store, string symbolPath) + internal static void DisableSymbolStore() { - string[] paths = symbolPath.Split(";", StringSplitOptions.RemoveEmptyEntries); - - foreach (string path in paths.Reverse()) - { - string[] parts = path.Split("*", StringSplitOptions.RemoveEmptyEntries); - - // UNC or directory paths are ignored (paths not prefixed with srv* or cache*). - if (parts.Length > 0) - { - string symbolServerPath = null; - string symbolCachePath = null; - bool msdl = false; - - switch (parts[0].ToLowerInvariant()) - { - case "srv": - switch (parts.Length) - { - case 1: - msdl = true; - break; - case 2: - symbolServerPath = parts[1]; - break; - case 3: - symbolCachePath = parts[1]; - symbolServerPath = parts[2]; - break; - default: - return false; - } - break; - - case "cache": - switch (parts.Length) - { - case 1: - symbolCachePath = GetDefaultSymbolCache(); - break; - case 2: - symbolCachePath = parts[1]; - break; - default: - return false; - } - break; - - default: - return false; - } - - // Add the symbol stores to the chain - if (!GetServerSymbolStore(ref store, msdl, false, symbolServerPath, symbolCachePath)) - { - return false; - } - } - } - - return true; + s_tracer = null; + s_symbolStore = null; + s_symbolCacheAdded = false; } - private static bool GetServerSymbolStore(ref SymbolStore store, bool msdl, bool symweb, string symbolServerPath, string symbolCachePath) + /// + /// Load native symbols and modules (i.e. dac, dbi). + /// + /// called back for each symbol file loaded + /// callback parameter + /// module path + /// module file name + /// module base address + /// module size + /// read memory callback delegate + internal static void LoadNativeSymbols(SymbolFileCallback callback, IntPtr parameter, string moduleDirectory, string moduleFileName, + ulong address, int size, ReadMemoryDelegate readMemory) { - bool internalServer = false; - - // Add symbol server URL if exists - if (symbolServerPath == null) + if (s_symbolStore != null) { - if (msdl) + Debug.Assert(s_tracer != null); + string path = Path.Combine(moduleDirectory, moduleFileName); + Stream stream = new TargetStream(address, size, readMemory); + KeyTypeFlags flags = KeyTypeFlags.SymbolKey | KeyTypeFlags.ClrKeys; + KeyGenerator generator = null; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - symbolServerPath = MsdlsymbolServer; + var elfFile = new ELFFile(new StreamAddressSpace(stream), 0, true); + generator = new ELFFileKeyGenerator(s_tracer, elfFile, path); } - else if (symweb) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - symbolServerPath = SymwebSymbolService; - internalServer = true; + var machOFile = new MachOFile(new StreamAddressSpace(stream), 0, true); + generator = new MachOFileKeyGenerator(s_tracer, machOFile, path); } - } - else - { - // Use the internal symbol store for symweb - internalServer = symbolServerPath.Contains("symweb"); - - // Make sure the server Uri ends with "/" - symbolServerPath = symbolServerPath.TrimEnd('/') + '/'; - } - - if (symbolServerPath != null) - { - if (!Uri.TryCreate(symbolServerPath, UriKind.Absolute, out Uri uri) || uri.IsFile) + else { - return false; + return; } - // Create symbol server store - if (internalServer) + try { - store = new SymwebHttpSymbolStore(s_tracer, store, uri); + IEnumerable keys = generator.GetKeys(flags); + foreach (SymbolStoreKey key in keys) + { + string symbolFileName = Path.GetFileName(key.FullPathName); + s_tracer.Verbose("{0} {1}", key.FullPathName, key.Index); + + // Don't download the sos binaries that come with the runtime + if (symbolFileName != "SOS.NETCore.dll" && !symbolFileName.StartsWith("libsos.")) + { + using (SymbolStoreFile file = GetSymbolStoreFile(key)) + { + if (file != null) + { + string downloadFileName = file.FileName; + + // If the downloaded doesn't already exists on disk in the cache, then write it to a temporary location. + if (!File.Exists(downloadFileName)) + { + downloadFileName = Path.Combine(GetTempDirectory(), symbolFileName); + + using (Stream destinationStream = File.OpenWrite(downloadFileName)) + { + file.Stream.CopyTo(destinationStream); + } + s_tracer.WriteLine("Downloaded symbol file {0}", key.FullPathName); + } + s_tracer.Information("{0}: {1}", symbolFileName, downloadFileName); + callback(parameter, symbolFileName, downloadFileName); + } + } + } + } } - else + catch (Exception ex) when (ex is BadInputFormatException || ex is InvalidVirtualAddressException) { - store = new HttpSymbolStore(s_tracer, store, uri); + s_tracer.Error("Exception: {0}/{1}: {2:X16}", moduleDirectory, moduleFileName, address); } } - - if (symbolCachePath != null) - { - store = new CacheSymbolStore(s_tracer, store, symbolCachePath); - - // Don't add the default cache later - s_symbolCacheAdded = true; - } - - return true; - } - - private static string GetDefaultSymbolCache() - { - var sb = new StringBuilder(); - - string environmentVar; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - environmentVar = "ProgramData"; - } - else - { - environmentVar = "HOME"; - } - string userPath = Environment.GetEnvironmentVariable(environmentVar); - sb.Append(userPath); - sb.Append(Path.DirectorySeparatorChar); - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - sb.Append("dbg"); - sb.Append(Path.DirectorySeparatorChar); - sb.Append("sym"); - } - else - { - sb.Append(".sos"); - sb.Append(Path.DirectorySeparatorChar); - sb.Append("symbolcache"); - } - return sb.ToString(); } /// @@ -922,20 +858,7 @@ namespace SOS } Debug.Assert(codeViewEntry.MinorVersion == ImageDebugDirectory.PortablePDBMinorVersion); SymbolStoreKey key = PortablePDBFileKeyGenerator.GetKey(pdbPath, data.Guid); - - // 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; - } - - SymbolStoreFile symbolStoreFile = s_symbolStore.GetFile(key, CancellationToken.None).GetAwaiter().GetResult(); - if (symbolStoreFile == null) - { - return null; - } - pdbStream = symbolStoreFile.Stream; + pdbStream = GetSymbolStoreFile(key)?.Stream; } provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream); @@ -989,6 +912,183 @@ namespace SOS return result; } + /// + /// Attempts to download/retrieve from cache the key. + /// + /// index of the file to retrieve + /// stream or null + private static SymbolStoreFile GetSymbolStoreFile(SymbolStoreKey key) + { + // 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(); + } + + /// + /// Parses the Windows debugger symbol path (srv*, cache*, etc.). + /// + /// symbol store to chain + /// Windows symbol path + /// if false, error parsing symbol path + private static bool ParseSymbolPath(ref SymbolStore store, string symbolPath) + { + string[] paths = symbolPath.Split(";", StringSplitOptions.RemoveEmptyEntries); + + foreach (string path in paths.Reverse()) + { + string[] parts = path.Split("*", StringSplitOptions.RemoveEmptyEntries); + + // UNC or directory paths are ignored (paths not prefixed with srv* or cache*). + if (parts.Length > 0) + { + string symbolServerPath = null; + string symbolCachePath = null; + bool msdl = false; + + switch (parts[0].ToLowerInvariant()) + { + case "srv": + switch (parts.Length) + { + case 1: + msdl = true; + break; + case 2: + symbolServerPath = parts[1]; + break; + case 3: + symbolCachePath = parts[1]; + symbolServerPath = parts[2]; + break; + default: + return false; + } + break; + + case "cache": + switch (parts.Length) + { + case 1: + symbolCachePath = GetDefaultSymbolCache(); + break; + case 2: + symbolCachePath = parts[1]; + break; + default: + return false; + } + break; + + default: + return false; + } + + // Add the symbol stores to the chain + if (!GetServerSymbolStore(ref store, msdl, false, symbolServerPath, symbolCachePath)) + { + return false; + } + } + } + + return true; + } + + private static bool GetServerSymbolStore(ref SymbolStore store, bool msdl, bool symweb, string symbolServerPath, string symbolCachePath) + { + bool internalServer = false; + + // Add symbol server URL if exists + if (symbolServerPath == null) + { + if (msdl) + { + symbolServerPath = MsdlsymbolServer; + } + else if (symweb) + { + symbolServerPath = SymwebSymbolService; + internalServer = true; + } + } + else + { + // Use the internal symbol store for symweb + internalServer = symbolServerPath.Contains("symweb"); + + // Make sure the server Uri ends with "/" + symbolServerPath = symbolServerPath.TrimEnd('/') + '/'; + } + + if (symbolServerPath != null) + { + if (!Uri.TryCreate(symbolServerPath, UriKind.Absolute, out Uri uri) || uri.IsFile) + { + return false; + } + + // Create symbol server store + if (internalServer) + { + store = new SymwebHttpSymbolStore(s_tracer, store, uri); + } + else + { + store = new HttpSymbolStore(s_tracer, store, uri); + } + } + + if (symbolCachePath != null) + { + store = new CacheSymbolStore(s_tracer, store, symbolCachePath); + + // Don't add the default cache later + s_symbolCacheAdded = true; + } + + return true; + } + + private static string GetDefaultSymbolCache() + { + var sb = new StringBuilder(); + + string environmentVar; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + environmentVar = "ProgramData"; + } + else + { + environmentVar = "HOME"; + } + string userPath = Environment.GetEnvironmentVariable(environmentVar); + sb.Append(userPath); + sb.Append(Path.DirectorySeparatorChar); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + sb.Append("dbg"); + sb.Append(Path.DirectorySeparatorChar); + sb.Append("sym"); + } + else + { + sb.Append(".dotnet"); + sb.Append(Path.DirectorySeparatorChar); + sb.Append("symbolcache"); + } + return sb.ToString(); + } + + /// + /// Attempt to open a file stream. + /// + /// file path + /// stream or null if doesn't exist or error private static Stream TryOpenFile(string path) { if (!File.Exists(path)) @@ -1005,6 +1105,19 @@ namespace SOS } } + /// + /// Create/return a temporary directory. + /// + private static string GetTempDirectory() + { + if (s_tempDirectory == null) + { + s_tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(s_tempDirectory); + } + return s_tempDirectory; + } + /// /// Quick fix for Path.GetFileName which incorrectly handles Windows-style paths on Linux /// @@ -1014,7 +1127,9 @@ namespace SOS { int pos = pathName.LastIndexOfAny(new char[] { '/', '\\'}); if (pos < 0) + { return pathName; + } return pathName.Substring(pos + 1); } } diff --git a/src/SOS/SOS.NETCore/Tracer.cs b/src/SOS/SOS.NETCore/Tracer.cs index d856a3fb6..330f954ad 100644 --- a/src/SOS/SOS.NETCore/Tracer.cs +++ b/src/SOS/SOS.NETCore/Tracer.cs @@ -34,7 +34,7 @@ namespace SOS { if (m_enabled) { - m_output?.Invoke(message); + WriteLine(message); } } @@ -64,12 +64,18 @@ namespace SOS public void Error(string message) { - WriteLine("ERROR: " + message); + if (m_enabled) + { + WriteLine("ERROR: " + message); + } } public void Error(string format, params object[] arguments) { - WriteLine("ERROR: " + format, arguments); + if (m_enabled) + { + WriteLine("ERROR: " + format, arguments); + } } public void Verbose(string message) diff --git a/src/SOS/Strike/hostcoreclr.cpp b/src/SOS/Strike/hostcoreclr.cpp index e4f296829..ce91681b6 100644 --- a/src/SOS/Strike/hostcoreclr.cpp +++ b/src/SOS/Strike/hostcoreclr.cpp @@ -25,6 +25,7 @@ #ifdef FEATURE_PAL #include #include +#include #endif // !FEATURE_PAL #include @@ -42,6 +43,8 @@ static bool g_hostingInitialized = false; static bool g_symbolStoreInitialized = false; LPCSTR g_hostRuntimeDirectory = nullptr; +LPCSTR g_dacFilePath = nullptr; +LPCSTR g_dbiFilePath = nullptr; SOSNetCoreCallbacks g_SOSNetCoreCallbacks; #ifdef FEATURE_PAL @@ -279,6 +282,47 @@ HRESULT GetHostRuntime(std::string& coreClrPath, std::string& hostRuntimeDirecto return S_OK; } +LPCSTR GetDacFilePath() +{ + // If the DAC path hasn't been set by the symbol download support, use the one in the runtime directory. + if (g_dacFilePath == nullptr) + { + std::string dacModulePath; + HRESULT hr = GetCoreClrDirectory(dacModulePath); + if (SUCCEEDED(hr)) + { + dacModulePath.append(DIRECTORY_SEPARATOR_STR_A); + dacModulePath.append(MAKEDLLNAME_A("mscordaccore")); +#ifdef FEATURE_PAL + // if DAC file exists + if (access(dacModulePath.c_str(), F_OK) == 0) +#endif + g_dacFilePath = _strdup(dacModulePath.c_str()); + } + } + return g_dacFilePath; +} + +LPCSTR GetDbiFilePath() +{ + if (g_dbiFilePath == nullptr) + { + std::string dbiModulePath; + HRESULT hr = GetCoreClrDirectory(dbiModulePath); + if (SUCCEEDED(hr)) + { + dbiModulePath.append(DIRECTORY_SEPARATOR_STR_A); + dbiModulePath.append(MAKEDLLNAME_A("mscordbi")); +#ifdef FEATURE_PAL + // if DBI file exists + if (access(dbiModulePath.c_str(), F_OK) == 0) +#endif + g_dbiFilePath = _strdup(dbiModulePath.c_str()); + } + } + return g_dbiFilePath; +} + BOOL IsHostingInitialized() { return g_hostingInitialized; @@ -397,11 +441,9 @@ HRESULT InitializeHosting() return Status; } - //SymbolReaderInitialize initialize; - //IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "Initialize", (void **)&initialize)); - //initialize(); - IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "InitializeSymbolStore", (void **)&g_SOSNetCoreCallbacks.InitializeSymbolStoreDelegate)); + IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "DisableSymbolStore", (void **)&g_SOSNetCoreCallbacks.DisableSymbolStoreDelegate)); + IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "LoadNativeSymbols", (void **)&g_SOSNetCoreCallbacks.LoadNativeSymbolsDelegate)); IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "LoadSymbolsForModule", (void **)&g_SOSNetCoreCallbacks.LoadSymbolsForModuleDelegate)); IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "Dispose", (void **)&g_SOSNetCoreCallbacks.DisposeDelegate)); IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "ResolveSequencePoint", (void **)&g_SOSNetCoreCallbacks.ResolveSequencePointDelegate)); @@ -418,36 +460,99 @@ extern "C" void InitializeSymbolReaderCallbacks(SOSNetCoreCallbacks sosNetCoreCa g_hostingInitialized = true; } -static void WriteLineForSymbolStore(const char* message) +// +// Pass to managed helper code to read in-memory PEs/PDBs +// Returns the number of bytes read. +// +static int ReadMemoryForSymbols(ULONG64 address, uint8_t *buffer, int cb) { - ExtOut(message); - ExtOut("\n"); + ULONG read; + if (SafeReadMemory(TO_TADDR(address), (PVOID)buffer, cb, &read)) + { + return read; + } + return 0; } -HRESULT InitializeSymbolStore(BOOL msdl, BOOL symweb, const char* symbolServer, const char* cacheDirectory) +#ifdef FEATURE_PAL + +static void SymbolFileCallback(void* param, const char* moduleFileName, const char* symbolFileName) { - HRESULT Status = S_OK; + if (strcmp(moduleFileName, MAIN_CLR_DLL_NAME_A) == 0) { + return; + } + if (strcmp(moduleFileName, MAKEDLLNAME_A("mscordaccore")) == 0) { + if (g_dacFilePath == nullptr) { + g_dacFilePath = _strdup(symbolFileName); + } + return; + } + if (strcmp(moduleFileName, MAKEDLLNAME_A("mscordbi")) == 0) { + if (g_dbiFilePath == nullptr) { + g_dbiFilePath = _strdup(symbolFileName); + } + return; + } + ToRelease sosHostServices(NULL); + HRESULT Status = g_ExtServices->QueryInterface(__uuidof(ISOSHostServices), (void**)&sosHostServices); + if (SUCCEEDED(Status)) + { + sosHostServices->AddModuleSymbol(param, symbolFileName); + } +} - IfFailRet(InitializeHosting()); +static void LoadNativeSymbolsCallback(void* param, const char* moduleDirectory, const char* moduleFileName, ULONG64 moduleAddress, int moduleSize) +{ + _ASSERTE(g_hostingInitialized); + _ASSERTE(g_SOSNetCoreCallbacks.LoadNativeSymbolsDelegate != nullptr); + g_SOSNetCoreCallbacks.LoadNativeSymbolsDelegate(SymbolFileCallback, param, moduleDirectory, moduleFileName, moduleAddress, moduleSize, ReadMemoryForSymbols); +} +#endif + +HRESULT InitializeSymbolStore(BOOL logging, BOOL msdl, BOOL symweb, const char* symbolServer, const char* cacheDirectory) +{ + HRESULT Status = S_OK; + IfFailRet(InitializeHosting()); _ASSERTE(g_SOSNetCoreCallbacks.InitializeSymbolStoreDelegate != nullptr); - // Only pass the output delegate on Linux (lldb) because on Windows under dbgeng, output from the symbol stores - // can deadlock because it can happen on a thread other than the dbgeng main thread. - OutputDelegate writeLine = nullptr; -#ifdef FEATURE_PAL - writeLine = WriteLineForSymbolStore; -#endif - if (!g_SOSNetCoreCallbacks.InitializeSymbolStoreDelegate(writeLine, msdl, symweb, symbolServer, cacheDirectory, nullptr)) + if (!g_SOSNetCoreCallbacks.InitializeSymbolStoreDelegate(logging, msdl, symweb, symbolServer, cacheDirectory, nullptr)) { ExtErr("Error initializing symbol server support\n"); return E_FAIL; } - g_symbolStoreInitialized = true; return S_OK; } +HRESULT LoadNativeSymbols() +{ + HRESULT Status = S_OK; +#ifdef FEATURE_PAL + if (g_symbolStoreInitialized) + { + ToRelease sosHostServices(NULL); + Status = g_ExtServices->QueryInterface(__uuidof(ISOSHostServices), (void**)&sosHostServices); + if (SUCCEEDED(Status)) + { + Status = sosHostServices->LoadNativeSymbols(LoadNativeSymbolsCallback); + } + } +#endif + return Status; +} + +void DisableSymbolStore() +{ + if (g_symbolStoreInitialized) + { + g_symbolStoreInitialized = false; + + _ASSERTE(g_SOSNetCoreCallbacks.DisableSymbolStoreDelegate != nullptr); + g_SOSNetCoreCallbacks.DisableSymbolStoreDelegate(); + } +} + HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in ICorDebugModule* pModule) { HRESULT Status = S_OK; @@ -606,20 +711,6 @@ HRESULT SymbolReader::LoadSymbolsForWindowsPDB(___in IMetaDataImport* pMD, ___in #endif // FEATURE_PAL -// -// Pass to managed helper code to read in-memory PEs/PDBs -// Returns the number of bytes read. -// -int ReadMemoryForSymbols(ULONG64 address, char *buffer, int cb) -{ - ULONG read; - if (SafeReadMemory(TO_TADDR(address), (PVOID)buffer, cb, &read)) - { - return read; - } - return 0; -} - HRESULT SymbolReader::LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in BOOL isInMemory, ___in BOOL isFileLayout, ___in ULONG64 peAddress, ___in ULONG64 peSize, ___in ULONG64 inMemoryPdbAddress, ___in ULONG64 inMemoryPdbSize) { @@ -640,7 +731,7 @@ HRESULT SymbolReader::LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in { if (strlen(symbolPath) > 0) { - if (!g_SOSNetCoreCallbacks.InitializeSymbolStoreDelegate(nullptr, false, false, nullptr, nullptr, symbolPath)) + if (!g_SOSNetCoreCallbacks.InitializeSymbolStoreDelegate(false, false, false, nullptr, nullptr, symbolPath)) { ExtErr("Windows symbol path parsing FAILED\n"); } diff --git a/src/SOS/Strike/hostcoreclr.h b/src/SOS/Strike/hostcoreclr.h index 967c61c7f..70f2a7e8c 100644 --- a/src/SOS/Strike/hostcoreclr.h +++ b/src/SOS/Strike/hostcoreclr.h @@ -14,10 +14,12 @@ static const char *SymbolReaderDllName = "SOS.NETCore"; static const char *SymbolReaderClassName = "SOS.SymbolReader"; typedef void (*OutputDelegate)(const char*); -typedef int (*ReadMemoryDelegate)(ULONG64, char *, int); +typedef int (*ReadMemoryDelegate)(ULONG64, uint8_t*, int); +typedef void (*SymbolFileCallbackDelegate)(void*, const char* moduleFileName, const char* symbolFileName); -typedef void (*SymbolReaderInitialize)(); -typedef BOOL (*InitializeSymbolStoreDelegate)(OutputDelegate, BOOL, BOOL, const char*, const char*, const char*); +typedef BOOL (*InitializeSymbolStoreDelegate)(BOOL, BOOL, BOOL, const char*, const char*, const char*); +typedef void (*DisableSymbolStoreDelegate)(); +typedef void (*LoadNativeSymbolsDelegate)(SymbolFileCallbackDelegate, void*, const char*, const char*, ULONG64, int, ReadMemoryDelegate); typedef PVOID (*LoadSymbolsForModuleDelegate)(const char*, BOOL, ULONG64, int, ULONG64, int, ReadMemoryDelegate); typedef void (*DisposeDelegate)(PVOID); typedef BOOL (*ResolveSequencePointDelegate)(PVOID, const char*, unsigned int, unsigned int*, unsigned int*); @@ -27,6 +29,8 @@ typedef BOOL (*GetLineByILOffsetDelegate)(PVOID, mdMethodDef, ULONG64, ULONG *, struct SOSNetCoreCallbacks { InitializeSymbolStoreDelegate InitializeSymbolStoreDelegate; + DisableSymbolStoreDelegate DisableSymbolStoreDelegate; + LoadNativeSymbolsDelegate LoadNativeSymbolsDelegate; LoadSymbolsForModuleDelegate LoadSymbolsForModuleDelegate; DisposeDelegate DisposeDelegate; ResolveSequencePointDelegate ResolveSequencePointDelegate; @@ -38,9 +42,13 @@ extern HMODULE g_hInstance; extern LPCSTR g_hostRuntimeDirectory; extern SOSNetCoreCallbacks g_SOSNetCoreCallbacks; +extern LPCSTR GetDacFilePath(); +extern LPCSTR GetDbiFilePath(); extern BOOL IsHostingInitialized(); extern HRESULT InitializeHosting(); -extern HRESULT InitializeSymbolStore(BOOL, BOOL, const char*, const char*); +extern HRESULT InitializeSymbolStore(BOOL logging, BOOL msdl, BOOL symweb, const char* symbolServer, const char* cacheDirectory); +extern HRESULT LoadNativeSymbols(); +extern void DisableSymbolStore(); class SymbolReader { diff --git a/src/SOS/Strike/sosdocs.txt b/src/SOS/Strike/sosdocs.txt index fd3e9573e..0bf4492c5 100644 --- a/src/SOS/Strike/sosdocs.txt +++ b/src/SOS/Strike/sosdocs.txt @@ -61,10 +61,10 @@ DumpCCW Examining the GC history Other ----------------------------- ----------------------------- HistInit SetHostRuntime (sethostruntime) -HistRoot FAQ -HistObj SOSFlush -HistObjFind Help (soshelp) -HistClear +HistRoot SetSymbolServer (setsymbolserver) +HistObj FAQ +HistObjFind SOSFlush +HistClear Help (soshelp) \\ COMMAND: faq. @@ -2608,4 +2608,29 @@ needs to be single-quoted ('). The default is to use the same runtime (coreclr.dll) being debugged. Use this command when the runtime being debugged isn't working to run the SOS code or if the version is less than 2.0.0. + +If you received the following error message when running a SOS command, use +this command to set the path to 2.0.0 or greater .NET Core runtime. + + 0:000> !clrstack + Error: Fail to initialize CoreCLR 80004005 + ClrStack failed + + 0:000> sethostruntime "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.6" + +You can use the "dotnet --info" in a command shell to find the path of an installed +.NET Core runtime. \\ + +COMMAND: setsymbolserver. +SetSymbolServer [-ms] [-mi] [-disable] [-log] [-cache ] [] + +-ms - Use the public Microsoft symbol server. +-mi - Use the internal symweb symbol server. +-disable - Disable symbol download support. +-cache - Specific a symbol cache directory. The default is $HOME/.sos/symbolcache if not specified. +-log - Enable symbol download logging. + - Symbol server URL. + +This commands enables symbol server support for portable PDBs in SOS. If the .sympath is set, this +symbol server support is automatically enabled. \ No newline at end of file diff --git a/src/SOS/Strike/sosdocsunix.txt b/src/SOS/Strike/sosdocsunix.txt index 35289e6b3..46ae004aa 100644 --- a/src/SOS/Strike/sosdocsunix.txt +++ b/src/SOS/Strike/sosdocsunix.txt @@ -54,10 +54,10 @@ DumpSigElem Examining the GC history Other ----------------------------- ----------------------------- HistInit (histinit) SetHostRuntime (sethostruntime) -HistRoot (histroot) FAQ -HistObj (histobj) SOSFlush -HistObjFind (histobjfind) Help (soshelp) -HistClear (histclear) +HistRoot (histroot) SetSymbolServer (setsymbolserver, loadsymbols) +HistObj (histobj) FAQ +HistObjFind (histobjfind) SOSFlush +HistClear (histclear) Help (soshelp) \\ COMMAND: faq. @@ -1822,7 +1822,7 @@ cleanup the previous resources. \\ COMMAND: sethostruntime. -!SetHostRuntime +SetHostRuntime This command sets the path to the .NET Core runtime to use to host the managed code that runs as part of SOS in the debugger (lldb). The runtime needs @@ -1830,6 +1830,59 @@ to be at least version 2.0.0 or greater. If there are spaces in directory, it needs to be single-quoted ('). The default is to use the same runtime (libcoreclr) being debugged. Use this -command when the runtime being debugged isn't working to run the SOS code or -if the version is less than 2.0.0. +command when the runtime being debugged isn't working enough to run the SOS +code or if the version is less than 2.0.0. + +If you received the following error message when running a SOS command, use +this command to set the path to 2.0.0 or greater .NET Core runtime. + + (lldb) clrstack + Error: Fail to initialize CoreCLR 80004005 + ClrStack failed + + (lldb) sethostruntime /usr/share/dotnet/shared/Microsoft.NETCore.App/2.1.6 + +You can use the "dotnet --info" in a command shell to find the path of an installed +.NET Core runtime. \\ + +COMMAND: setsymbolserver. +COMMAND: loadsymbols. +SetSymbolServer [-ms] [-disable] [-log] [-cache ] [-loadsymbols] [] + +-ms - Use the public Microsoft symbol server. +-disable - Disable symbol download support. +-cache - Specific a symbol cache directory. The default is $HOME/.sos/symbolcache if not specified. +-log - Enable symbol download logging. +-loadsymbols - Attempts to download the native .NET Core symbols for the runtime + - Symbol server URL. + +This commands enables symbol server support in SOS. The portable PDBs for managed assemblies +and .NET Core native symbol files are downloaded. + +To enable downloading symbols from the Microsoft symbol server: + + (lldb) setsymbolserver -ms + +This command may take some time without any output while it attempts to download the symbol files. + +To disable download: + + (lldb) setsymbolserver -disable + +To clear the default cache run "rm -r $HOME/.dotnet/symbolcache" in a command shell. + +If you receive an error like the one below on a core dump, you need to set the .NET Core +runtime with the "sethostruntime" command. Type "soshelp sethostruntime" for more details. + + (lldb) setsymbolserver -ms + Error: Fail to initialize CoreCLR 80004005 + SetSymbolServer -ms failed + +The "-loadsymbols" option and the "loadsymbol" command alias attempts to download the native .NET +Core symbol files. It is only useful for live sessions and not core dumps. This command needs to +be run before the lldb "bt" (stack trace) or the "clrstack -f" (dumps both managed and native +stack frames). + + (lldb) loadsymbols + (lldb) bt diff --git a/src/SOS/Strike/strike.cpp b/src/SOS/Strike/strike.cpp index bb9406950..e7d47927c 100644 --- a/src/SOS/Strike/strike.cpp +++ b/src/SOS/Strike/strike.cpp @@ -15404,13 +15404,20 @@ DECLARE_API(SetSymbolServer) INIT_API_EXT(); StringHolder symbolCache; + BOOL disable = FALSE; + BOOL loadNative = FALSE; BOOL msdl = FALSE; BOOL symweb = FALSE; + BOOL logging = FALSE; CMDOption option[] = { // name, vptr, type, hasValue - {"-cache", &symbolCache.data, COSTRING, FALSE}, - {"-ms", &msdl, COBOOL, FALSE}, -#ifndef FEATURE_PAL + {"-disable", &disable, COBOOL, FALSE}, + {"-cache", &symbolCache.data, COSTRING, FALSE}, + {"-ms", &msdl, COBOOL, FALSE}, + {"-log", &logging, COBOOL, FALSE}, +#ifdef FEATURE_PAL + {"-loadsymbols", &loadNative, COBOOL, FALSE}, +#else {"-mi", &symweb, COBOOL, FALSE}, #endif }; @@ -15425,6 +15432,11 @@ DECLARE_API(SetSymbolServer) return E_FAIL; } + if (disable) { + DisableSymbolStore(); + return S_OK; + } + if (msdl && symweb) { ExtErr("Cannot have both -ms and -mi options\n"); @@ -15439,7 +15451,7 @@ DECLARE_API(SetSymbolServer) if (msdl || symweb || symbolServer.data != nullptr || symbolCache.data != nullptr) { - Status = InitializeSymbolStore(msdl, symweb, symbolServer.data, symbolCache.data); + Status = InitializeSymbolStore(logging, msdl, symweb, symbolServer.data, symbolCache.data); if (FAILED(Status)) { return Status; @@ -15461,7 +15473,14 @@ DECLARE_API(SetSymbolServer) ExtOut("Symbol cache path: %s\n", symbolCache.data); } } - + +#ifdef FEATURE_PAL + if (loadNative) + { + Status = LoadNativeSymbols(); + } +#endif + return Status; } diff --git a/src/SOS/Strike/util.cpp b/src/SOS/Strike/util.cpp index aae53ea08..cfe1159c8 100644 --- a/src/SOS/Strike/util.cpp +++ b/src/SOS/Strike/util.cpp @@ -52,9 +52,6 @@ PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) symBuffer; #include #endif -HRESULT InitializeHosting(); -HRESULT GetCoreClrDirectory(std::string& coreClrDirectory); - const char * const CorElementTypeName[ELEMENT_TYPE_MAX]= { #define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) c, @@ -3942,21 +3939,17 @@ void ResetGlobals(void) // HRESULT LoadClrDebugDll(void) { - static IXCLRDataProcess* s_clrDataProcess = NULL; + static IXCLRDataProcess* clrDataProcess = NULL; HRESULT hr = S_OK; - if (s_clrDataProcess == NULL) + if (clrDataProcess == NULL) { - std::string dacModulePath; - hr = GetCoreClrDirectory(dacModulePath); - if (FAILED(hr)) + LPCSTR dacFilePath = GetDacFilePath(); + if (dacFilePath == nullptr) { - return hr; + return E_FAIL; } - dacModulePath.append(DIRECTORY_SEPARATOR_STR_A); - dacModulePath.append(MAKEDLLNAME_A("mscordaccore")); - - HMODULE hdac = LoadLibraryA(dacModulePath.c_str()); + HMODULE hdac = LoadLibraryA(dacFilePath); if (hdac == NULL) { return CORDBG_E_MISSING_DEBUGGER_EXPORTS; @@ -3968,18 +3961,18 @@ HRESULT LoadClrDebugDll(void) return CORDBG_E_MISSING_DEBUGGER_EXPORTS; } ICLRDataTarget *target = new DataTarget(); - hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&s_clrDataProcess); + hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&clrDataProcess); if (FAILED(hr)) { - s_clrDataProcess = NULL; + clrDataProcess = NULL; return hr; } ULONG32 flags = 0; - s_clrDataProcess->GetOtherNotificationFlags(&flags); + clrDataProcess->GetOtherNotificationFlags(&flags); flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD | CLRDATA_NOTIFY_ON_EXCEPTION); - s_clrDataProcess->SetOtherNotificationFlags(flags); + clrDataProcess->SetOtherNotificationFlags(flags); } - g_clrData = s_clrDataProcess; + g_clrData = clrDataProcess; g_clrData->AddRef(); g_clrData->Flush(); @@ -4159,7 +4152,7 @@ public: callbackData.filesize = dwSizeOfImage; // if we are looking for the DAC, just load the one windbg already found - if (_wcsncmp(pwszFileName, W("mscordac"), _wcslen(W("mscordac"))) == 0) + if (_wcsncmp(pwszFileName, MAKEDLLNAME_W(CORECLR_DAC_MODULE_NAME_W), _wcslen(MAKEDLLNAME_W(CORECLR_DAC_MODULE_NAME_W))) == 0) { HMODULE dacModule; if (g_sos == NULL) @@ -4222,7 +4215,7 @@ public: (PVOID)&callbackData)) { hr = HRESULT_FROM_WIN32(GetLastError()); - ExtErr("SymFindFileInPath failed for %S. hr=0x%x.\nPlease ensure that %S is on your symbol path.", pwszFileName, hr, pwszFileName); + ExtErr("SymFindFileInPath failed for %S. hr=0x%x.\nPlease ensure that %S is on your symbol path.\n", pwszFileName, hr, pwszFileName); return hr; } if (ppResolvedModulePath != NULL) @@ -4237,21 +4230,45 @@ public: return S_OK; #else _ASSERTE(phModule == NULL); + const char* filePath = nullptr; - LPCSTR coreclrDirectory = g_ExtServices->GetCoreClrDirectory(); - if (coreclrDirectory == NULL) + if (_wcsncmp(pwszFileName, MAKEDLLNAME_W(CORECLR_DAC_MODULE_NAME_W), _wcslen(MAKEDLLNAME_W(CORECLR_DAC_MODULE_NAME_W))) == 0) { - ExtErr("Runtime module (%s) not loaded yet\n", MAKEDLLNAME_A("coreclr")); - return E_FAIL; + filePath = GetDacFilePath(); } + else if (_wcsncmp(pwszFileName, MAKEDLLNAME_W(MAIN_DBI_MODULE_NAME_W), _wcslen(MAKEDLLNAME_W(MAIN_DBI_MODULE_NAME_W))) == 0) + { + filePath = GetDbiFilePath(); + } + ArrayHolder modulePath = new WCHAR[MAX_LONGPATH + 1]; - int length = MultiByteToWideChar(CP_ACP, 0, coreclrDirectory, -1, modulePath, MAX_LONGPATH); - if (0 >= length) + if (filePath != nullptr) { - ExtErr("MultiByteToWideChar(coreclrDirectory) failed. Last error = 0x%x\n", GetLastError()); - return E_FAIL; + int length = MultiByteToWideChar(CP_ACP, 0, filePath, -1, modulePath, MAX_LONGPATH); + if (0 >= length) + { + ExtErr("MultiByteToWideChar(filePath) failed. Last error = 0x%x\n", GetLastError()); + return E_FAIL; + } + } + else + { + LPCSTR coreclrDirectory = g_ExtServices->GetCoreClrDirectory(); + if (coreclrDirectory == NULL) + { + ExtErr("Runtime module (%s) not loaded yet\n", MAKEDLLNAME_A("coreclr")); + return E_FAIL; + } + int length = MultiByteToWideChar(CP_ACP, 0, coreclrDirectory, -1, modulePath, MAX_LONGPATH); + if (0 >= length) + { + ExtErr("MultiByteToWideChar(coreclrDirectory) failed. Last error = 0x%x\n", GetLastError()); + return E_FAIL; + } + wcscat_s(modulePath, MAX_LONGPATH, pwszFileName); } - wcscat_s(modulePath, MAX_LONGPATH, pwszFileName); + + ExtOut("Loaded %S\n", modulePath.GetPtr()); if (ppResolvedModulePath != NULL) { @@ -4304,10 +4321,7 @@ protected: // Data target for the debugged process. Provided to OpenVirtualProcess in order to // get an ICorDebugProcess back // -class SOSDataTarget : public ICorDebugMutableDataTarget -#ifdef FEATURE_PAL -, public ICorDebugDataTarget4 -#endif +class SOSDataTarget : public ICorDebugMutableDataTarget, public ICorDebugMetaDataLocator, public ICorDebugDataTarget4 { public: SOSDataTarget() : m_ref(0) @@ -4332,12 +4346,14 @@ public: { *pInterface = static_cast(this); } -#ifdef FEATURE_PAL + else if (InterfaceId == IID_ICorDebugMetaDataLocator) + { + *pInterface = static_cast(this); + } else if (InterfaceId == IID_ICorDebugDataTarget4) { *pInterface = static_cast(this); } -#endif else { *pInterface = NULL; @@ -4461,6 +4477,7 @@ public: // // ICorDebugMutableDataTarget. // + virtual HRESULT STDMETHODCALLTYPE WriteVirtual(CORDB_ADDRESS address, const BYTE * pBuffer, ULONG32 bytesRequested) @@ -4485,20 +4502,38 @@ public: return E_NOTIMPL; } -#ifdef FEATURE_PAL + // + // ICorDebugMetaDataLocator. + // + + virtual HRESULT STDMETHODCALLTYPE GetMetaData( + /* [in] */ LPCWSTR wszImagePath, + /* [in] */ DWORD dwImageTimeStamp, + /* [in] */ DWORD dwImageSize, + /* [in] */ ULONG32 cchPathBuffer, + /* [annotation][out] */ + _Out_ ULONG32 *pcchPathBuffer, + /* [annotation][length_is][size_is][out] */ + _Out_writes_to_(cchPathBuffer, *pcchPathBuffer) WCHAR wszPathBuffer[]) + { + return E_NOTIMPL; + } + // // ICorDebugDataTarget4 // virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context) { +#ifdef FEATURE_PAL if (g_ExtServices == NULL) { return E_UNEXPECTED; } return g_ExtServices->VirtualUnwind(threadId, contextSize, context); - +#else + return E_NOTIMPL; +#endif } -#endif // FEATURE_PAL protected: LONG m_ref; diff --git a/src/SOS/lldbplugin/inc/lldbservices.h b/src/SOS/lldbplugin/inc/lldbservices.h index ffb22242d..135f39cb7 100644 --- a/src/SOS/lldbplugin/inc/lldbservices.h +++ b/src/SOS/lldbplugin/inc/lldbservices.h @@ -542,6 +542,24 @@ public: PULONG64 offset) = 0; }; +typedef void (*PFN_MODULE_LOAD_CALLBACK)(void* param, const char* moduleName, const char* moduleDirectory, ULONG64 moduleAddress, int moduleSize); + +MIDL_INTERFACE("012F32F0-33BA-4E8E-BC01-037D382D8A5E") +ISOSHostServices : public IUnknown +{ +public: + //---------------------------------------------------------------------------- + // ISOSHostServices + //---------------------------------------------------------------------------- + + virtual HRESULT LoadNativeSymbols( + PFN_MODULE_LOAD_CALLBACK callback) = 0; + + virtual HRESULT AddModuleSymbol( + void* param, + const char* symbolFileName) = 0; +}; + #ifdef __cplusplus }; #endif diff --git a/src/SOS/lldbplugin/services.cpp b/src/SOS/lldbplugin/services.cpp index 2e853e94e..62b28d42b 100644 --- a/src/SOS/lldbplugin/services.cpp +++ b/src/SOS/lldbplugin/services.cpp @@ -8,13 +8,14 @@ #include #include #include +#include #define CONVERT_FROM_SIGN_EXTENDED(offset) ((ULONG_PTR)(offset)) ULONG g_currentThreadIndex = -1; ULONG g_currentThreadSystemId = -1; -char *g_coreclrDirectory; -char *g_pluginModuleDirectory; +char *g_coreclrDirectory = nullptr; +char *g_pluginModuleDirectory = nullptr; LLDBServices::LLDBServices(lldb::SBDebugger &debugger, lldb::SBCommandReturnObject &returnObject, lldb::SBProcess *process, lldb::SBThread *thread) : m_ref(1), @@ -47,6 +48,12 @@ LLDBServices::QueryInterface( AddRef(); return S_OK; } + else if (InterfaceId == __uuidof(ISOSHostServices)) + { + *Interface = (ISOSHostServices*)this; + AddRef(); + return S_OK; + } else { *Interface = NULL; @@ -1339,6 +1346,13 @@ LLDBServices::GetModuleBase( } } + lldb::SBAddress headerAddress = module.GetObjectFileHeaderAddress(); + lldb::addr_t moduleAddress = headerAddress.GetLoadAddress(target); + if (moduleAddress != 0) + { + return moduleAddress; + } + return UINT64_MAX; } @@ -1702,6 +1716,72 @@ LLDBServices::GetFrameOffset( return S_OK; } +//---------------------------------------------------------------------------- +// ISOSHostServices +//---------------------------------------------------------------------------- + +HRESULT +LLDBServices::LoadNativeSymbols( + PFN_MODULE_LOAD_CALLBACK callback) +{ + uint32_t numTargets = m_debugger.GetNumTargets(); + for (int ti = 0; ti < numTargets; ti++) + { + lldb::SBTarget target = m_debugger.GetTargetAtIndex(ti); + if (target.IsValid()) + { + uint32_t numModules = target.GetNumModules(); + for (int mi = 0; mi < numModules; mi++) + { + lldb::SBModule module = target.GetModuleAtIndex(mi); + if (module.IsValid()) + { + const char* directory = nullptr; + const char* filename = nullptr; + + lldb::SBFileSpec symbolFileSpec = module.GetSymbolFileSpec(); + if (symbolFileSpec.IsValid()) + { + directory = symbolFileSpec.GetDirectory(); + filename = symbolFileSpec.GetFilename(); + } + else { + lldb::SBFileSpec fileSpec = module.GetFileSpec(); + if (fileSpec.IsValid()) + { + directory = fileSpec.GetDirectory(); + filename = fileSpec.GetFilename(); + } + } + + if (directory != nullptr && filename != nullptr) + { + ULONG64 moduleAddress = GetModuleBase(target, module); + int moduleSize = INT32_MAX; + if (moduleAddress != UINT64_MAX) + { + callback(&module, directory, filename, moduleAddress, moduleSize); + } + } + } + } + } + } + return S_OK; +} + +HRESULT +LLDBServices::AddModuleSymbol( + void* param, + const char* symbolFileName) +{ + std::string command; + command.append("target symbols add "); + command.append(symbolFileName); + + return Execute(DEBUG_EXECUTE_NOT_LOGGED, command.c_str(), 0); +} + //---------------------------------------------------------------------------- // Helper functions //---------------------------------------------------------------------------- @@ -1772,21 +1852,20 @@ LLDBServices::GetPluginModuleDirectory() { if (g_pluginModuleDirectory == nullptr) { - Dl_info info; - if (dladdr((void *)&DummyFunction, &info) != 0) - { - std::string path(info.dli_fname); - - // Parse off the module name to get just the path - size_t lastSlash = path.rfind('/'); - if (lastSlash != std::string::npos) - { - path.erase(lastSlash); - path.append("/"); - g_pluginModuleDirectory = strdup(path.c_str()); - } - } + Dl_info info; + if (dladdr((void *)&DummyFunction, &info) != 0) + { + std::string path(info.dli_fname); + + // Parse off the module name to get just the path + size_t lastSlash = path.rfind('/'); + if (lastSlash != std::string::npos) + { + path.erase(lastSlash); + path.append("/"); + g_pluginModuleDirectory = strdup(path.c_str()); + } + } } return g_pluginModuleDirectory; } - diff --git a/src/SOS/lldbplugin/services.h b/src/SOS/lldbplugin/services.h index 6648c091d..9d0060cf3 100644 --- a/src/SOS/lldbplugin/services.h +++ b/src/SOS/lldbplugin/services.h @@ -4,7 +4,7 @@ #include -class LLDBServices : public ILLDBServices +class LLDBServices : public ILLDBServices, public ISOSHostServices { private: LONG m_ref; @@ -265,6 +265,17 @@ public: HRESULT GetFrameOffset( PULONG64 offset); + //---------------------------------------------------------------------------- + // ISOSHostServices + //---------------------------------------------------------------------------- + + HRESULT LoadNativeSymbols( + PFN_MODULE_LOAD_CALLBACK callback); + + HRESULT AddModuleSymbol( + void* param, + const char* symbolFileName); + //---------------------------------------------------------------------------- // LLDBServices (internal) //---------------------------------------------------------------------------- @@ -273,4 +284,6 @@ public: PCSTR name); PCSTR GetPluginModuleDirectory(); + + int StartListenerThread(); }; diff --git a/src/SOS/lldbplugin/setclrpathcommand.cpp b/src/SOS/lldbplugin/setclrpathcommand.cpp index 43387245e..0a964473f 100644 --- a/src/SOS/lldbplugin/setclrpathcommand.cpp +++ b/src/SOS/lldbplugin/setclrpathcommand.cpp @@ -21,12 +21,12 @@ public: char** arguments, lldb::SBCommandReturnObject &result) { - if (arguments[0] == NULL) + if (arguments == nullptr || arguments[0] == nullptr) { - result.Printf("Load path for dac/dbi: '%s'\n", g_coreclrDirectory == NULL ? "" : g_coreclrDirectory); + result.Printf("Load path for dac/dbi: '%s'\n", g_coreclrDirectory == nullptr ? "" : g_coreclrDirectory); } else { - if (g_coreclrDirectory != NULL) + if (g_coreclrDirectory != nullptr) { free(g_coreclrDirectory); } diff --git a/src/SOS/lldbplugin/setsostidcommand.cpp b/src/SOS/lldbplugin/setsostidcommand.cpp index c7d6a1ba2..392682e58 100644 --- a/src/SOS/lldbplugin/setsostidcommand.cpp +++ b/src/SOS/lldbplugin/setsostidcommand.cpp @@ -21,7 +21,7 @@ public: char** arguments, lldb::SBCommandReturnObject &result) { - if (arguments[0] == NULL) + if (arguments == nullptr || arguments[0] == nullptr) { if (g_currentThreadSystemId == -1 || g_currentThreadIndex == -1) { @@ -37,16 +37,16 @@ public: g_currentThreadSystemId = -1; result.Printf("Cleared sos OS tid/index\n"); } - else if (arguments[1] == NULL) + else if (arguments[1] == nullptr) { - result.Printf("Need thread index parameter that maps to the OS tid\n"); + result.Printf("Need thread index parameter that maps to the OS tid. setsostid \n"); } else { - ULONG tid = strtoul(arguments[0], NULL, 16); + ULONG tid = strtoul(arguments[0], nullptr, 16); g_currentThreadSystemId = tid; - ULONG index = strtoul(arguments[1], NULL, 16); + ULONG index = strtoul(arguments[1], nullptr, 16); g_currentThreadIndex = index; result.Printf("Mapped sos OS tid 0x%x to lldb thread index %d\n", tid, index); diff --git a/src/SOS/lldbplugin/soscommand.cpp b/src/SOS/lldbplugin/soscommand.cpp index aec9f2ff1..4ca13f3fb 100644 --- a/src/SOS/lldbplugin/soscommand.cpp +++ b/src/SOS/lldbplugin/soscommand.cpp @@ -7,22 +7,23 @@ #include #include +void *g_sosHandle = nullptr; + +// If true, use the directory that libsosplugin is in to load +// libsos, otherwise (if false) use the libcoreclr module +// directory (legacy behavior). +bool g_usePluginDirectory = true; + class sosCommand : public lldb::SBCommandPluginInterface { const char *m_command; - void *m_sosHandle; - - // If true, use the directory that libsosplugin is in to load - // libsos, otherwise (if false) use the libcoreclr module - // directory (legacy behavior). - bool m_usePluginDirectory; + const char *m_arguments; public: - sosCommand(const char *command) + sosCommand(const char* command, const char* arguments = nullptr) { m_command = command; - m_sosHandle = NULL; - m_usePluginDirectory = true; + m_arguments = arguments; } virtual bool @@ -33,12 +34,12 @@ public: LLDBServices* services = new LLDBServices(debugger, result); LoadSos(services); - if (m_sosHandle != NULL) + if (g_sosHandle != nullptr) { const char* sosCommand = m_command; - if (sosCommand == NULL) + if (sosCommand == nullptr) { - if (arguments == NULL || *arguments == NULL) { + if (arguments == nullptr || *arguments == nullptr) { sosCommand = "Help"; } else @@ -46,11 +47,16 @@ public: sosCommand = *arguments++; } } - CommandFunc commandFunc = (CommandFunc)dlsym(m_sosHandle, sosCommand); + CommandFunc commandFunc = (CommandFunc)dlsym(g_sosHandle, sosCommand); if (commandFunc) { std::string str; - if (arguments != NULL) + if (m_arguments) + { + str.append(m_arguments); + str.append(" "); + } + if (arguments != nullptr) { for (const char* arg = *arguments; arg; arg = *(++arguments)) { @@ -78,26 +84,26 @@ public: void LoadSos(LLDBServices *services) { - if (m_sosHandle == NULL) + if (g_sosHandle == nullptr) { - if (m_usePluginDirectory) + if (g_usePluginDirectory) { const char *loadDirectory = services->GetPluginModuleDirectory(); - if (loadDirectory != NULL) + if (loadDirectory != nullptr) { - m_sosHandle = LoadModule(services, loadDirectory, MAKEDLLNAME_A("sos")); + g_sosHandle = LoadModule(services, loadDirectory, MAKEDLLNAME_A("sos")); } } else { const char *loadDirectory = services->GetCoreClrDirectory(); - if (loadDirectory != NULL) + if (loadDirectory != nullptr) { // Load the DAC module first explicitly because SOS and DBI // have implicit references to the DAC's PAL. LoadModule(services, loadDirectory, MAKEDLLNAME_A("mscordaccore")); - m_sosHandle = LoadModule(services, loadDirectory, MAKEDLLNAME_A("sos")); + g_sosHandle = LoadModule(services, loadDirectory, MAKEDLLNAME_A("sos")); } } } @@ -110,9 +116,9 @@ public: modulePath.append(moduleName); void *moduleHandle = dlopen(modulePath.c_str(), RTLD_NOW); - if (moduleHandle == NULL) + if (moduleHandle == nullptr) { - services->Output(DEBUG_OUTPUT_ERROR, "dlopen(%s) failed %s\n", modulePath.c_str(), dlerror()); + services->Output(DEBUG_OUTPUT_ERROR, "Could not load '%s' - %s\n", modulePath.c_str(), dlerror()); } return moduleHandle; @@ -123,11 +129,10 @@ bool sosCommandInitialize(lldb::SBDebugger debugger) { lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); - interpreter.AddCommand("sos", new sosCommand(NULL), "Various coreclr debugging commands. See 'soshelp' for more details. sos "); + interpreter.AddCommand("sos", new sosCommand(nullptr), "Various coreclr debugging commands. See 'soshelp' for more details. sos "); interpreter.AddCommand("bpmd", new sosCommand("bpmd"), "Creates a breakpoint at the specified managed method in the specified module."); interpreter.AddCommand("clrstack", new sosCommand("ClrStack"), "Provides a stack trace of managed code only."); interpreter.AddCommand("clrthreads", new sosCommand("Threads"), "List the managed threads running."); - interpreter.AddCommand("createdump", new sosCommand("CreateDump"), "Create a xplat minidump."); interpreter.AddCommand("clru", new sosCommand("u"), "Displays an annotated disassembly of a managed method."); interpreter.AddCommand("dumpasync", new sosCommand("DumpAsync"), "Displays info about async state machines on the garbage-collected heap."); interpreter.AddCommand("dumpclass", new sosCommand("DumpClass"), "Displays information about a EE class structure at the specified address."); @@ -146,6 +151,7 @@ sosCommandInitialize(lldb::SBDebugger debugger) interpreter.AddCommand("eestack", new sosCommand("EEStack"), "Runs dumpstack on all threads in the process."); interpreter.AddCommand("gcroot", new sosCommand("GCRoot"), "Displays info about references (or roots) to an object at the specified address."); interpreter.AddCommand("ip2md", new sosCommand("IP2MD"), "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled."); + interpreter.AddCommand("loadsymbols", new sosCommand("SetSymbolServer", "-loadsymbols"), "Load the .NET Core native module symbols."); interpreter.AddCommand("name2ee", new sosCommand("Name2EE"), "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module."); interpreter.AddCommand("pe", new sosCommand("PrintException"), "Displays and formats fields of any object derived from the Exception class at the specified address."); interpreter.AddCommand("syncblk", new sosCommand("SyncBlk"), "Displays the SyncBlock holder info."); diff --git a/src/SOS/lldbplugin/sosplugin.h b/src/SOS/lldbplugin/sosplugin.h index 588ec468e..556a387b8 100644 --- a/src/SOS/lldbplugin/sosplugin.h +++ b/src/SOS/lldbplugin/sosplugin.h @@ -9,7 +9,7 @@ #include #include "services.h" -typedef HRESULT (*CommandFunc)(ILLDBServices* services, const char *args); +typedef HRESULT (*CommandFunc)(ILLDBServices* services, const char* args); extern char *g_coreclrDirectory; extern ULONG g_currentThreadIndex; diff --git a/src/pal/src/loader/module.cpp b/src/pal/src/loader/module.cpp index 9589d2f5f..4755fb9fb 100644 --- a/src/pal/src/loader/module.cpp +++ b/src/pal/src/loader/module.cpp @@ -1276,6 +1276,7 @@ static void *LOADLoadLibraryDirect(LPCSTR libraryNameOrPath) void *dl_handle = dlopen(libraryNameOrPath, RTLD_LAZY); if (dl_handle == nullptr) { + ERROR("dlopen(%s) failed! %s\n", libraryNameOrPath, dlerror()); SetLastError(ERROR_MOD_NOT_FOUND); } else