From: Mike McLaughlin Date: Tue, 30 Jul 2019 17:03:16 +0000 (-0700) Subject: Add arm/arm64 support to dotnet-dump (#411) X-Git-Tag: submit/tizen/20191015.063341~14^2^2~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2e6f16b0c9662fa0d3caff9a68242b72fb80407f;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git Add arm/arm64 support to dotnet-dump (#411) Add arm/arm64 support Using new CLRMD version 1.1.37504 Add Microsoft.Diagnostics.DebugServices project containing the new RegisterService and what's left of the AnalyzeContext. Add ServiceProvider and refactor adding/creating the services. Made OutputVaList global so we don't allocate 8K every line of output Fixed that the "mask" (determines if error/normal/warning) is pasted on to the debugger in OutputVaList. Removed the formatting/buffer allocating in the OutputVaList in lldb services Always use C++ runtime to format output now. SOS_PTR keeps the architecture pointer size (size_t). On x86, dotnet-dump had problems with %p formatting because the C++ runtime was used and the pointer was converted to 64 bit by the SOS_PTR macro. Add arm64 registers to lldb service GetContextFromFrame Enable tests on arm. Added IConsoleService and removed the dependencies on System.CommandLine from SOS.Hosting and MS.D.DebugServices. IConsole is no longer exposed as a service. --- diff --git a/diagnostics.sln b/diagnostics.sln index 257e4c17a..65c8d5402 100644 --- a/diagnostics.sln +++ b/diagnostics.sln @@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Diagnostics.Tools EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SOS.Package", "src\SOS\SOS.Package\SOS.Package.csproj", "{234416E9-EA5F-4018-AC34-67682C5D3E04}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Diagnostics.DebugServices", "src\Microsoft.Diagnostics.DebugServices\Microsoft.Diagnostics.DebugServices.csproj", "{A1CE682A-12C4-4FF9-B864-A9A15A8726D2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Checked|Any CPU = Checked|Any CPU @@ -719,6 +721,46 @@ Global {234416E9-EA5F-4018-AC34-67682C5D3E04}.RelWithDebInfo|x64.Build.0 = Release|Any CPU {234416E9-EA5F-4018-AC34-67682C5D3E04}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU {234416E9-EA5F-4018-AC34-67682C5D3E04}.RelWithDebInfo|x86.Build.0 = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Checked|Any CPU.Build.0 = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Checked|ARM.ActiveCfg = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Checked|ARM.Build.0 = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Checked|ARM64.ActiveCfg = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Checked|ARM64.Build.0 = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Checked|x64.ActiveCfg = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Checked|x64.Build.0 = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Checked|x86.ActiveCfg = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Checked|x86.Build.0 = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Debug|ARM.ActiveCfg = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Debug|ARM.Build.0 = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Debug|ARM64.Build.0 = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Debug|x64.ActiveCfg = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Debug|x64.Build.0 = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Debug|x86.ActiveCfg = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Debug|x86.Build.0 = Debug|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Release|ARM.ActiveCfg = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Release|ARM.Build.0 = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Release|ARM64.ActiveCfg = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Release|ARM64.Build.0 = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Release|x64.ActiveCfg = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Release|x64.Build.0 = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Release|x86.ActiveCfg = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.Release|x86.Build.0 = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2}.RelWithDebInfo|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -744,6 +786,7 @@ Global {2A9B5988-982F-4E26-9E44-D38AC5978C30} = {B62728C8-1267-4043-B46F-5537BBAEC692} {54C240C5-7932-4421-A5FB-75205DE0B824} = {19FAB78C-3351-4911-8F0C-8C6056401740} {234416E9-EA5F-4018-AC34-67682C5D3E04} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7} + {A1CE682A-12C4-4FF9-B864-A9A15A8726D2} = {19FAB78C-3351-4911-8F0C-8C6056401740} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {46465737-C938-44FC-BE1A-4CE139EBB5E0} diff --git a/eng/Build-Native.cmd b/eng/Build-Native.cmd index 378c486e1..690ac7633 100644 --- a/eng/Build-Native.cmd +++ b/eng/Build-Native.cmd @@ -332,8 +332,7 @@ REM ============================================================================ echo %__MsgPrefix%Repo successfully built. Finished at %TIME% echo %__MsgPrefix%Product binaries are available at !__BinDir! -if /i "%__BuildArch%" == "arm" goto Done -if /i "%__BuildArch%" == "arm64" goto Done +if /i %__BuildCrossArch% EQU 1 goto Done :: Test components if %__Test% EQU 1 ( diff --git a/eng/Versions.props b/eng/Versions.props index 4c5748f14..47309088f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -2,7 +2,7 @@ https://github.com/dotnet/diagnostics - preview7 + preview8 3.0.0 true true @@ -23,7 +23,7 @@ 1.0.0-dev-64131-02 - 1.1.35902 + 1.1.37504 1.7.0 2.0.43 0.2.0-alpha.19254.1 diff --git a/eng/build-native.sh b/eng/build-native.sh index 81e8926ea..b4932f8a5 100755 --- a/eng/build-native.sh +++ b/eng/build-native.sh @@ -364,7 +364,7 @@ initTargetDistroRid() local passedRootfsDir="" # Only pass ROOTFS_DIR if cross is specified. - if [ "$__CrossBuild" == true ]; then + if [ $__CrossBuild == true ]; then passedRootfsDir=${ROOTFS_DIR} fi @@ -438,7 +438,7 @@ fi # Run SOS/lldbplugin tests if [ $__Test == true ]; then - if [[ "$__BuildArch" != "arm" && "$__BuildArch" != "armel" && "$__BuildArch" != "arm64" ]]; then + if [ $__CrossBuild != true ]; then # Install the other versions of .NET Core runtime we are going to test on "$__ProjectRoot/eng/install-test-runtimes.sh" --dotnet-directory "$__ProjectRoot/.dotnet" --runtime-version-21 "$__DotNetRuntimeVersion" --temp-directory "$__IntermediatesDir" --architecture "$__BuildArch" $__DailyTest diff --git a/src/Microsoft.Diagnostics.DebugServices/AnalyzeContext.cs b/src/Microsoft.Diagnostics.DebugServices/AnalyzeContext.cs new file mode 100644 index 000000000..e1f4e27b8 --- /dev/null +++ b/src/Microsoft.Diagnostics.DebugServices/AnalyzeContext.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; + +namespace Microsoft.Diagnostics.DebugServices +{ + /// + /// Common context for commands + /// + public class AnalyzeContext + { + public AnalyzeContext() + { + } + + /// + /// Current OS thread Id + /// + public int CurrentThreadId { get; set; } + + /// + /// Cancellation token for current command + /// + public CancellationToken CancellationToken { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.Diagnostics.DebugServices/IConsoleService.cs b/src/Microsoft.Diagnostics.DebugServices/IConsoleService.cs new file mode 100644 index 000000000..2990d37f1 --- /dev/null +++ b/src/Microsoft.Diagnostics.DebugServices/IConsoleService.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Diagnostics.DebugServices +{ + /// + /// Console output service + /// + public interface IConsoleService + { + /// + /// Write text to console's standard out + /// + /// text + void Write(string value); + + /// + /// Write text to console's standard error + /// + /// + void WriteError(string value); + + /// + /// Exit the interactive console + /// + void Exit(); + } +} \ No newline at end of file diff --git a/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj b/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj new file mode 100644 index 000000000..098bc4552 --- /dev/null +++ b/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + true + ;1591;1701 + Diagnostics debug services + $(Description) + true + + + + + + diff --git a/src/Microsoft.Diagnostics.DebugServices/RegisterService.cs b/src/Microsoft.Diagnostics.DebugServices/RegisterService.cs new file mode 100644 index 000000000..3bd96d6f3 --- /dev/null +++ b/src/Microsoft.Diagnostics.DebugServices/RegisterService.cs @@ -0,0 +1,216 @@ +// Licensed to the .NET Foundation under one or more agreements. +// 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.Diagnostics.Runtime; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using Architecture = Microsoft.Diagnostics.Runtime.Architecture; + +namespace Microsoft.Diagnostics.DebugServices +{ + /// + /// Provides register info and values + /// + public class RegisterService + { + public struct RegisterInfo + { + public readonly int RegisterIndex; + public readonly int RegisterOffset; + public readonly int RegisterSize; + public readonly string RegisterName; + + internal RegisterInfo(int registerIndex, int registerOffset, int registerSize, string registerName) + { + RegisterIndex = registerIndex; + RegisterOffset = registerOffset; + RegisterSize = registerSize; + RegisterName = registerName; + } + } + + private readonly DataTarget _target; + private readonly int _contextSize; + private readonly uint _contextFlags; + private readonly Dictionary _lookupByName; + private readonly Dictionary _lookupByIndex; + private readonly Dictionary _threadContextCache = new Dictionary(); + + public IEnumerable Registers { get; } + + public int InstructionPointerIndex { get; } + + public int FramePointerIndex { get; } + + public int StackPointerIndex { get; } + + public RegisterService(DataTarget target) + { + _target = target; + + Type contextType; + switch (target.Architecture) + { + case Architecture.Amd64: + _contextSize = AMD64Context.Size; + _contextFlags = AMD64Context.ContextControl | AMD64Context.ContextInteger | AMD64Context.ContextSegments; + contextType = typeof(AMD64Context); + break; + + case Architecture.X86: + _contextSize = X86Context.Size; + _contextFlags = X86Context.ContextControl | X86Context.ContextInteger | X86Context.ContextSegments; + contextType = typeof(X86Context); + break; + + case Architecture.Arm64: + _contextSize = Arm64Context.Size; + _contextFlags = Arm64Context.ContextControl | Arm64Context.ContextInteger; + contextType = typeof(Arm64Context); + break; + + case Architecture.Arm: + _contextSize = ArmContext.Size; + _contextFlags = ArmContext.ContextControl | ArmContext.ContextInteger; + contextType = typeof(ArmContext); + break; + + default: + throw new PlatformNotSupportedException($"Unsupported architecture: {target.Architecture}"); + } + + var registers = new List(); + int index = 0; + + FieldInfo[] fields = contextType.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic); + foreach (FieldInfo field in fields) { + RegisterAttribute registerAttribute = field.GetCustomAttributes(inherit: false).SingleOrDefault(); + if (registerAttribute == null) { + continue; + } + RegisterType registerType = registerAttribute.RegisterType & RegisterType.TypeMask; + switch (registerType) + { + case RegisterType.Control: + case RegisterType.General: + case RegisterType.Segments: + break; + default: + continue; + } + if ((registerAttribute.RegisterType & RegisterType.ProgramCounter) != 0) { + InstructionPointerIndex = index; + } + if ((registerAttribute.RegisterType & RegisterType.StackPointer) != 0) { + StackPointerIndex = index; + } + if ((registerAttribute.RegisterType & RegisterType.FramePointer) != 0) { + FramePointerIndex = index; + } + FieldOffsetAttribute offsetAttribute = field.GetCustomAttributes(inherit: false).Single(); + var registerInfo = new RegisterInfo(index, offsetAttribute.Value, Marshal.SizeOf(field.FieldType), registerAttribute.Name ?? field.Name.ToLower()); + registers.Add(registerInfo); + index++; + } + + _lookupByName = registers.ToDictionary((info) => info.RegisterName); + _lookupByIndex = registers.ToDictionary((info) => info.RegisterIndex); + + Registers = registers; + } + + /// + /// Return the register index for the register name + /// + /// register name + /// returns register index or -1 + /// true if name found + public bool GetRegisterIndexByName(string name, out int index) + { + if (_lookupByName.TryGetValue(name, out RegisterInfo info)) + { + index = info.RegisterIndex; + return true; + } + index = int.MaxValue; + return false; + } + + /// + /// Returns the register info (name, offset, size, etc). + /// + /// register index + /// RegisterInfo + /// true if index found + public bool GetRegisterInfo(int index, out RegisterInfo info) + { + return _lookupByIndex.TryGetValue(index, out info); + } + + /// + /// Returns the register value for the thread and register index. This function + /// can only return register values that are 64 bits or less and currently the + /// clrmd data targets don't return any floating point or larger registers. + /// + /// thread id + /// register index + /// value returned + /// true if value found + public unsafe bool GetRegisterValue(uint threadId, int index, out ulong value) + { + value = 0; + + if (_lookupByIndex.TryGetValue(index, out RegisterInfo info)) + { + byte[] threadContext = GetThreadContext(threadId); + if (threadContext != null) + { + fixed (byte* ptr = threadContext) + { + switch (info.RegisterSize) + { + case 1: + value = *((byte*)(ptr + info.RegisterOffset)); + return true; + case 2: + value = *((ushort*)(ptr + info.RegisterOffset)); + return true; + case 4: + value = *((uint*)(ptr + info.RegisterOffset)); + return true; + case 8: + value = *((ulong*)(ptr + info.RegisterOffset)); + return true; + } + } + } + } + return false; + } + + private unsafe byte[] GetThreadContext(uint threadId) + { + if (_threadContextCache.TryGetValue(threadId, out byte[] threadContext)) + { + return threadContext; + } + else + { + threadContext = new byte[_contextSize]; + fixed (byte* ptr = threadContext) + { + if (_target.DataReader.GetThreadContext(threadId, _contextFlags, (uint)_contextSize, new IntPtr(ptr))) + { + _threadContextCache.Add(threadId, threadContext); + return threadContext; + } + } + } + return null; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Diagnostics.DebugServices/ServiceProvider.cs b/src/Microsoft.Diagnostics.DebugServices/ServiceProvider.cs new file mode 100644 index 000000000..c45562969 --- /dev/null +++ b/src/Microsoft.Diagnostics.DebugServices/ServiceProvider.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Diagnostics.DebugServices +{ + public class ServiceProvider : IServiceProvider + { + readonly Dictionary _services = new Dictionary(); + readonly Dictionary> _factories = new Dictionary>(); + + /// + /// Create a service provider instance + /// + public ServiceProvider() + { + } + + /// + /// Add service factory + /// + /// service type + /// function to create service instance + public void AddServiceFactory(Type type, Func factory) => _factories.Add(type, factory); + + /// + /// Adds a service or context to inject into an command. + /// + /// type of service + /// service instance + public void AddService(T instance) => AddService(typeof(T), instance); + + /// + /// Add a service instance. + /// + /// service type + /// instance + public void AddService(Type type, object service) => _services.Add(type, service); + + /// + /// Returns the instance of the service or returns null if service doesn't exist + /// + /// service type + /// service instance or null + public object GetService(Type type) + { + if (!_services.TryGetValue(type, out object service)) + { + if (_factories.TryGetValue(type, out Func factory)) + { + service = factory(); + _services.Add(type, service); + } + } + return service; + } + } + + public static class ServiceProviderExtensions + { + /// + /// Returns the instance of the service or returns null if service doesn't exist + /// + /// service type + /// service instance or null + public static T GetService(this IServiceProvider serviceProvider) + { + return (T)serviceProvider.GetService(typeof(T)); + } + } +} diff --git a/src/Microsoft.Diagnostics.Repl/Command/CommandBase.cs b/src/Microsoft.Diagnostics.Repl/Command/CommandBase.cs index cf6238cec..5537cd031 100644 --- a/src/Microsoft.Diagnostics.Repl/Command/CommandBase.cs +++ b/src/Microsoft.Diagnostics.Repl/Command/CommandBase.cs @@ -2,6 +2,7 @@ // 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.Diagnostics.DebugServices; using System; using System.CommandLine; using System.CommandLine.Invocation; @@ -21,9 +22,9 @@ namespace Microsoft.Diagnostics.Repl public InvocationContext InvocationContext { get; set; } /// - /// Console instance + /// Console service /// - public IConsole Console { get; set; } + public IConsoleService Console { get; set; } /// /// The AliasExpansion value from the CommandAttribute or null if none. @@ -42,7 +43,7 @@ namespace Microsoft.Diagnostics.Repl /// text message protected void WriteLine(string message) { - Console.Out.WriteLine(message); + Console.Write(message + Environment.NewLine); } /// @@ -52,7 +53,17 @@ namespace Microsoft.Diagnostics.Repl /// arguments protected void WriteLine(string format, params object[] args) { - Console.Out.WriteLine(string.Format(format, args)); + Console.Write(string.Format(format, args) + Environment.NewLine); + } + + /// + /// Display formatted error text + /// + /// format string + /// arguments + protected void WriteLineError(string format, params object[] args) + { + Console.WriteError(string.Format(format, args) + Environment.NewLine); } } } \ No newline at end of file diff --git a/src/Microsoft.Diagnostics.Repl/Command/CommandProcessor.cs b/src/Microsoft.Diagnostics.Repl/Command/CommandProcessor.cs index 4efec289e..745bca2a5 100644 --- a/src/Microsoft.Diagnostics.Repl/Command/CommandProcessor.cs +++ b/src/Microsoft.Diagnostics.Repl/Command/CommandProcessor.cs @@ -3,10 +3,8 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections; using System.Collections.Generic; using System.CommandLine; -using System.CommandLine.Binding; using System.CommandLine.Builder; using System.CommandLine.Invocation; using System.Diagnostics; @@ -20,22 +18,24 @@ namespace Microsoft.Diagnostics.Repl { private readonly Parser _parser; private readonly Command _rootCommand; - private readonly Dictionary _services = new Dictionary(); + private readonly IServiceProvider _serviceProvider; + private readonly IConsole _console; private readonly Dictionary _commandHandlers = new Dictionary(); /// /// Create an instance of the command processor; /// - /// console instance to use for commands + /// service provider interface + /// console instance /// Optional list of assemblies to look for commands /// Optional list of types to look for commands - public CommandProcessor(IConsole console, IEnumerable assemblies = null, IEnumerable types = null) + public CommandProcessor(IServiceProvider serviceProvider, IConsole console, IEnumerable assemblies = null, IEnumerable types = null) { - Debug.Assert(console != null); + Debug.Assert(serviceProvider != null); Debug.Assert(assemblies != null); - _services.Add(typeof(CommandProcessor), this); - _services.Add(typeof(IConsole), console); - _services.Add(typeof(IHelpBuilder), new LocalHelpBuilder(this)); + _serviceProvider = serviceProvider; + _console = console; + var rootBuilder = new CommandLineBuilder(new Command(">")); rootBuilder.UseHelp() .UseHelpBuilder((bindingContext) => GetService()) @@ -43,6 +43,7 @@ namespace Microsoft.Diagnostics.Repl .UseSuggestDirective() .UseParseErrorReporting() .UseExceptionHandler(); + if (assemblies != null) { BuildCommands(rootBuilder, assemblies); } @@ -54,23 +55,11 @@ namespace Microsoft.Diagnostics.Repl } /// - /// Adds a service or context to inject into an command. - /// - /// type of service - /// service instance - public void AddService(T instance) - { - AddService(typeof(T), instance); - } - - /// - /// Adds a service or context to inject into an command. + /// Creates a new instance of the command help builder /// - /// service type - /// service instance - public void AddService(Type type, object instance) + public IHelpBuilder CreateHelpBuilder() { - _services.Add(type, instance); + return new LocalHelpBuilder(this); } /// @@ -81,7 +70,7 @@ namespace Microsoft.Diagnostics.Repl public Task Parse(string commandLine) { ParseResult result = _parser.Parse(commandLine); - return _parser.InvokeAsync(result, GetService()); + return _parser.InvokeAsync(result, _console); } /// @@ -188,11 +177,16 @@ namespace Microsoft.Diagnostics.Repl } } + private object GetService(Type serviceType) + { + return _serviceProvider.GetService(serviceType); + } + private T GetService() { - _services.TryGetValue(typeof(T), out object service); + T service = (T)_serviceProvider.GetService(typeof(T)); Debug.Assert(service != null); - return (T)service; + return service; } private static string BuildAlias(string parameterName) @@ -293,7 +287,8 @@ namespace Microsoft.Diagnostics.Repl else { Type propertyType = property.Property.PropertyType; - if (TryGetService(propertyType, context, out object service)) { + object service = GetService(propertyType, context); + if (service != null) { value = service; } else if (context != null && property.Option != null) @@ -330,23 +325,23 @@ namespace Microsoft.Diagnostics.Repl object[] arguments = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { Type parameterType = parameters[i].ParameterType; - // Ignoring false: the parameter will passed as null to allow for "optional" - // services. The invoked method needs to check for possible null parameters. - TryGetService(parameterType, context, out arguments[i]); + // The parameter will passed as null to allow for "optional" services. The invoked + // method needs to check for possible null parameters. + arguments[i] = GetService(parameterType, context); } return arguments; } - private bool TryGetService(Type type, InvocationContext context, out object service) + private object GetService(Type type, InvocationContext context) { + object service; if (type == typeof(InvocationContext)) { service = context; } - else if (!_commandProcessor._services.TryGetValue(type, out service)) { - service = null; - return false; + else { + service = _commandProcessor.GetService(type); } - return true; + return service; } } @@ -367,7 +362,7 @@ namespace Microsoft.Diagnostics.Repl return; } } - var helpBuilder = new HelpBuilder(_commandProcessor.GetService(), maxWidth: Console.WindowWidth); + var helpBuilder = new HelpBuilder(_commandProcessor._console, maxWidth: Console.WindowWidth); helpBuilder.Write(command); } } diff --git a/src/Microsoft.Diagnostics.Repl/Console/ConsoleProvider.cs b/src/Microsoft.Diagnostics.Repl/Console/ConsoleProvider.cs index 71958b917..c5399a1fa 100644 --- a/src/Microsoft.Diagnostics.Repl/Console/ConsoleProvider.cs +++ b/src/Microsoft.Diagnostics.Repl/Console/ConsoleProvider.cs @@ -2,6 +2,7 @@ // 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.Diagnostics.DebugServices; using System; using System.Collections.Generic; using System.CommandLine; @@ -12,7 +13,7 @@ using System.Threading.Tasks; namespace Microsoft.Diagnostics.Repl { - public sealed class ConsoleProvider : IConsole + public sealed class ConsoleProvider : IConsole, IConsoleService { private readonly List m_history; @@ -120,12 +121,20 @@ namespace Microsoft.Diagnostics.Repl RefreshLine(); } + /// + /// Writes a message with a new line to console. + /// + public void WriteLine(string format, params object[] parameters) + { + WriteLine(OutputType.Normal, format, parameters); + } + /// /// Writes a message with a new line to console. /// public void WriteLine(OutputType type, string format, params object[] parameters) { - WriteOutput(type, string.Format(format + Environment.NewLine, parameters)); + WriteOutput(type, string.Format(format, parameters) + Environment.NewLine); } /// @@ -506,5 +515,15 @@ namespace Microsoft.Diagnostics.Repl } #endregion + + #region IConsoleService + + void IConsoleService.Write(string text) => WriteOutput(OutputType.Normal, text); + + void IConsoleService.WriteError(string text) => WriteOutput(OutputType.Error, text); + + void IConsoleService.Exit() => Stop(); + + #endregion } } diff --git a/src/Microsoft.Diagnostics.Repl/Microsoft.Diagnostics.Repl.csproj b/src/Microsoft.Diagnostics.Repl/Microsoft.Diagnostics.Repl.csproj index caeb36fa6..a209fe5d3 100644 --- a/src/Microsoft.Diagnostics.Repl/Microsoft.Diagnostics.Repl.csproj +++ b/src/Microsoft.Diagnostics.Repl/Microsoft.Diagnostics.Repl.csproj @@ -13,4 +13,8 @@ + + + + diff --git a/src/SOS/SOS.Hosting/Amd64Context.cs b/src/SOS/SOS.Hosting/Amd64Context.cs deleted file mode 100644 index 9eb3b2612..000000000 --- a/src/SOS/SOS.Hosting/Amd64Context.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime.InteropServices; - -namespace SOS -{ - [StructLayout(LayoutKind.Explicit)] - internal struct AMD64Context - { - [FieldOffset(0x0)] - public ulong P1Home; - [FieldOffset(0x8)] - public ulong P2Home; - [FieldOffset(0x10)] - public ulong P3Home; - [FieldOffset(0x18)] - public ulong P4Home; - [FieldOffset(0x20)] - public ulong P5Home; - [FieldOffset(0x28)] - public ulong P6Home; - - [FieldOffset(0x30)] - public int ContextFlags; - - [FieldOffset(0x34)] - public int MxCsr; - - [FieldOffset(0x38)] - public short Cs; - [FieldOffset(0x3a)] - public short Ds; - [FieldOffset(0x3c)] - public short Es; - [FieldOffset(0x3e)] - public short Fs; - [FieldOffset(0x40)] - public short Gs; - [FieldOffset(0x42)] - public short Ss; - [FieldOffset(0x44)] - public int EFlags; - - [FieldOffset(0x48)] - public ulong Dr0; - [FieldOffset(0x50)] - public ulong Dr1; - [FieldOffset(0x58)] - public ulong Dr2; - [FieldOffset(0x60)] - public ulong Dr3; - [FieldOffset(0x68)] - public ulong Dr6; - [FieldOffset(0x70)] - public ulong Dr7; - - [FieldOffset(0x78)] - public ulong Rax; - [FieldOffset(0x80)] - public ulong Rcx; - [FieldOffset(0x88)] - public ulong Rdx; - [FieldOffset(0x90)] - public ulong Rbx; - [FieldOffset(0x98)] - public ulong Rsp; - [FieldOffset(0xa0)] - public ulong Rbp; - [FieldOffset(0xa8)] - public ulong Rsi; - [FieldOffset(0xb0)] - public ulong Rdi; - [FieldOffset(0xb8)] - public ulong R8; - [FieldOffset(0xc0)] - public ulong R9; - [FieldOffset(0xc8)] - public ulong R10; - [FieldOffset(0xd0)] - public ulong R11; - [FieldOffset(0xd8)] - public ulong R12; - [FieldOffset(0xe0)] - public ulong R13; - [FieldOffset(0xe8)] - public ulong R14; - [FieldOffset(0xf0)] - public ulong R15; - - [FieldOffset(0xf8)] - public ulong Rip; - - //[FieldOffset(0x100)] - //public XmmSaveArea FltSave; - - //[FieldOffset(0x300)] - //public VectorRegisterArea VectorRegisters; - - [FieldOffset(0x4a8)] - public ulong DebugControl; - [FieldOffset(0x4b0)] - public ulong LastBranchToRip; - [FieldOffset(0x4b8)] - public ulong LastBranchFromRip; - [FieldOffset(0x4c0)] - public ulong LastExceptionToRip; - [FieldOffset(0x4c8)] - public ulong LastExceptionFromRip; - - public static int Size => Marshal.SizeOf(typeof(AMD64Context)); - } -} \ No newline at end of file diff --git a/src/SOS/SOS.Hosting/ISOSHostContext.cs b/src/SOS/SOS.Hosting/ISOSHostContext.cs deleted file mode 100644 index 3157a3036..000000000 --- a/src/SOS/SOS.Hosting/ISOSHostContext.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading; - -namespace SOS -{ - /// - /// Context/services provided to the SOS host. - /// - public interface ISOSHostContext - { - /// - /// Display text on the console - /// - /// message - void Write(string text); - - /// - /// Get/set the current native thread id - /// - int CurrentThreadId { get; set; } - - /// - /// Cancellation token for current operation - /// - CancellationToken CancellationToken { get; } - } -} \ No newline at end of file diff --git a/src/SOS/SOS.Hosting/LLDBServices.cs b/src/SOS/SOS.Hosting/LLDBServices.cs index 2423e3f9f..8f5425bdf 100644 --- a/src/SOS/SOS.Hosting/LLDBServices.cs +++ b/src/SOS/SOS.Hosting/LLDBServices.cs @@ -152,9 +152,11 @@ namespace SOS int GetValueByName( IntPtr self, string name, - out ulong value) + out UIntPtr value) { - return _soshost.GetRegister(name, out value); + int hr = _soshost.GetRegister(name, out ulong register); + value = new UIntPtr(register); + return hr; } #endregion @@ -202,7 +204,7 @@ namespace SOS IntPtr self); [UnmanagedFunctionPointer(CallingConvention.StdCall)] - private delegate ulong GetExpressionDelegate( + private delegate UIntPtr GetExpressionDelegate( IntPtr self, [In][MarshalAs(UnmanagedType.LPStr)] string text); @@ -434,7 +436,7 @@ namespace SOS private delegate int GetValueByNameDelegate( IntPtr self, [In, MarshalAs(UnmanagedType.LPStr)] string name, - out ulong value); + out UIntPtr value); [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate int GetInstructionOffsetDelegate( diff --git a/src/SOS/SOS.Hosting/SOS.Hosting.csproj b/src/SOS/SOS.Hosting/SOS.Hosting.csproj index a3d158746..8cb3f77ef 100644 --- a/src/SOS/SOS.Hosting/SOS.Hosting.csproj +++ b/src/SOS/SOS.Hosting/SOS.Hosting.csproj @@ -15,5 +15,6 @@ + diff --git a/src/SOS/SOS.Hosting/SOSHost.cs b/src/SOS/SOS.Hosting/SOSHost.cs index 6491c83e3..6425ec1db 100644 --- a/src/SOS/SOS.Hosting/SOSHost.cs +++ b/src/SOS/SOS.Hosting/SOSHost.cs @@ -2,11 +2,11 @@ // 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.Diagnostics.DebugServices; using Microsoft.Diagnostics.Runtime; using Microsoft.Diagnostics.Runtime.Interop; using Microsoft.Diagnostics.Runtime.Utilities; using System; -using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; @@ -43,7 +43,7 @@ namespace SOS bool symbolStoreEnabled); [UnmanagedFunctionPointer(CallingConvention.StdCall)] - private delegate ulong GetExpressionDelegate( + private delegate UIntPtr GetExpressionDelegate( [In, MarshalAs(UnmanagedType.LPStr)] string expression); private const string SOSInitialize = "SOSInitializeByHost"; @@ -149,12 +149,12 @@ namespace SOS }; internal readonly IDataReader DataReader; - internal readonly ISOSHostContext SOSHostContext; private static readonly string s_coreclrModuleName; - private static readonly Dictionary s_registersByName; - private static readonly int[] s_registerOffsets; + private readonly AnalyzeContext _analyzeContext; + private readonly RegisterService _registerService; + private readonly IConsoleService _console; private readonly COMCallableIUnknown _ccw; private readonly IntPtr _interface; private IntPtr _sosLibrary = IntPtr.Zero; @@ -176,23 +176,6 @@ namespace SOS else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { s_coreclrModuleName = "libcoreclr.dylib"; } - - // TODO: Support other architectures - Type contextType = typeof(AMD64Context); - var registerNames = new Dictionary(); - var offsets = new List(); - int index = 0; - - FieldInfo[] fields = contextType.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic); - foreach (FieldInfo field in fields) { - registerNames.Add(field.Name.ToLower(), index); - - FieldOffsetAttribute attribute = field.GetCustomAttributes(inherit: false).Single(); - offsets.Insert(index, attribute.Value); - index++; - } - s_registersByName = registerNames; - s_registerOffsets = offsets.ToArray(); } /// @@ -203,10 +186,14 @@ namespace SOS /// /// Create an instance of the hosting class /// - public SOSHost(IDataReader dataReader, ISOSHostContext context) + public SOSHost(IServiceProvider serviceProvider) { - DataReader = dataReader; - SOSHostContext = context; + DataTarget dataTarget = serviceProvider.GetService(); + DataReader = dataTarget.DataReader; + _console = serviceProvider.GetService(); + _analyzeContext = serviceProvider.GetService(); + _registerService = serviceProvider.GetService(); + string rid = InstallHelper.GetRid(); SOSPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), rid); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -325,23 +312,23 @@ namespace SOS #region Reverse PInvoke Implementations - internal static ulong GetExpression( + internal static UIntPtr GetExpression( string expression) { if (expression != null) { if (ulong.TryParse(expression.Replace("0x", ""), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong result)) { - return result; + return new UIntPtr(result); } } - return 0; + return UIntPtr.Zero; } internal int GetInterrupt( IntPtr self) { - return SOSHostContext.CancellationToken.IsCancellationRequested ? S_OK : E_FAIL; + return _analyzeContext.CancellationToken.IsCancellationRequested ? S_OK : E_FAIL; } internal int OutputVaList( @@ -353,7 +340,7 @@ namespace SOS try { // The text has already been formated by sos - SOSHostContext.Write(format); + _console.Write(format); } catch (OperationCanceledException) { @@ -401,7 +388,10 @@ namespace SOS *type = IMAGE_FILE_MACHINE.I386; break; case Microsoft.Diagnostics.Runtime.Architecture.Arm: - *type = IMAGE_FILE_MACHINE.ARM; + *type = IMAGE_FILE_MACHINE.THUMB2; + break; + case Microsoft.Diagnostics.Runtime.Architecture.Arm64: + *type = IMAGE_FILE_MACHINE.ARM64; break; default: *type = IMAGE_FILE_MACHINE.UNKNOWN; @@ -706,7 +696,7 @@ namespace SOS IntPtr context, uint contextSize) { - uint threadId = (uint)SOSHostContext.CurrentThreadId; + uint threadId = (uint)_analyzeContext.CurrentThreadId; if (!DataReader.GetThreadContext(threadId, uint.MaxValue, contextSize, context)) { return E_FAIL; } @@ -754,7 +744,7 @@ namespace SOS IntPtr self, out uint id) { - return GetThreadIdBySystemId(self, (uint)SOSHostContext.CurrentThreadId, out id); + return GetThreadIdBySystemId(self, (uint)_analyzeContext.CurrentThreadId, out id); } internal int SetCurrentThreadId( @@ -764,7 +754,7 @@ namespace SOS try { unchecked { - SOSHostContext.CurrentThreadId = (int)DataReader.EnumerateAllThreads().ElementAt((int)id); + _analyzeContext.CurrentThreadId = (int)DataReader.EnumerateAllThreads().ElementAt((int)id); } } catch (ArgumentOutOfRangeException) @@ -778,7 +768,7 @@ namespace SOS IntPtr self, out uint sysId) { - sysId = (uint)SOSHostContext.CurrentThreadId; + sysId = (uint)_analyzeContext.CurrentThreadId; return S_OK; } @@ -834,7 +824,7 @@ namespace SOS IntPtr self, ulong* offset) { - uint threadId = (uint)SOSHostContext.CurrentThreadId; + uint threadId = (uint)_analyzeContext.CurrentThreadId; ulong teb = DataReader.GetThreadTeb(threadId); Write(offset, teb); return S_OK; @@ -844,24 +834,21 @@ namespace SOS IntPtr self, out ulong offset) { - // TODO: Support other architectures - return GetRegister("rip", out offset); + return GetRegister(_registerService.InstructionPointerIndex, out offset); } internal int GetStackOffset( IntPtr self, out ulong offset) { - // TODO: Support other architectures - return GetRegister("rsp", out offset); + return GetRegister(_registerService.StackPointerIndex, out offset); } internal int GetFrameOffset( IntPtr self, out ulong offset) { - // TODO: Support other architectures - return GetRegister("rbp", out offset); + return GetRegister(_registerService.FramePointerIndex, out offset); } internal int GetIndexByName( @@ -869,7 +856,7 @@ namespace SOS string name, out uint index) { - if (!s_registersByName.TryGetValue(name, out int value)) { + if (_registerService.GetRegisterIndexByName(name, out int value)) { index = 0; return E_INVALIDARG; } @@ -882,46 +869,52 @@ namespace SOS uint register, out DEBUG_VALUE value) { - return GetRegister((int)register, out value); + int hr = GetRegister((int)register, out ulong offset); + + // SOS expects the DEBUG_VALUE field to be set based on the + // processor architecture instead of the register size. + switch (DataReader.GetPointerSize()) + { + case 8: + value = new DEBUG_VALUE { + Type = DEBUG_VALUE_TYPE.INT64, + I64 = offset + }; + break; + + case 4: + value = new DEBUG_VALUE { + Type = DEBUG_VALUE_TYPE.INT32, + I32 = (uint)offset + }; + break; + + default: + value = new DEBUG_VALUE(); + hr = E_FAIL; + break; + } + return hr; } - internal unsafe int GetRegister( + internal int GetRegister( string register, out ulong value) { value = 0; - if (!s_registersByName.TryGetValue(register, out int index)) { + if (!_registerService.GetRegisterIndexByName(register, out int index)) { return E_INVALIDARG; } - int hr = GetRegister(index, out DEBUG_VALUE debugValue); - if (hr != S_OK) { - return hr; - } - // TODO: Support other architectures - value = debugValue.I64; - return S_OK; + return GetRegister(index, out value); } - internal unsafe int GetRegister( + internal int GetRegister( int index, - out DEBUG_VALUE value) + out ulong value) { - value = new DEBUG_VALUE(); - - if (index >= s_registerOffsets.Length) { - return E_INVALIDARG; - } - uint threadId = (uint)SOSHostContext.CurrentThreadId; - - // TODO: Support other architectures - byte[] buffer = new byte[AMD64Context.Size]; - fixed (byte* ptr = buffer) - { - if (!DataReader.GetThreadContext(threadId, uint.MaxValue, (uint)AMD64Context.Size, new IntPtr(ptr))) { - return E_FAIL; - } - int offset = s_registerOffsets[index]; - value.I64 = *((ulong*)(ptr + offset)); + uint threadId = (uint)_analyzeContext.CurrentThreadId; + if (!_registerService.GetRegisterValue(threadId, index, out value)) { + return E_FAIL; } return S_OK; } diff --git a/src/SOS/SOS.InstallHelper/InstallHelper.cs b/src/SOS/SOS.InstallHelper/InstallHelper.cs index c0eb3e897..731e27429 100644 --- a/src/SOS/SOS.InstallHelper/InstallHelper.cs +++ b/src/SOS/SOS.InstallHelper/InstallHelper.cs @@ -330,7 +330,7 @@ namespace SOS { throw new SOSInstallerException($"Unsupported operating system {RuntimeInformation.OSDescription}"); } - string architecture = RuntimeInformation.OSArchitecture.ToString().ToLowerInvariant(); + string architecture = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); return $"{os}-{architecture}"; } diff --git a/src/SOS/SOS.UnitTests/SOS.cs b/src/SOS/SOS.UnitTests/SOS.cs index 8db30a6c6..4dcb14146 100644 --- a/src/SOS/SOS.UnitTests/SOS.cs +++ b/src/SOS/SOS.UnitTests/SOS.cs @@ -22,7 +22,6 @@ public class SOS ITestOutputHelper Output { get; set; } public static IEnumerable Configurations => TestRunConfiguration.Instance.Configurations.Select(c => new[] { c }); - private void SkipIfArm(TestConfiguration config) { if (config.TargetArchitecture == "arm" || config.TargetArchitecture == "arm64") @@ -48,8 +47,6 @@ public class SOS private async Task RunTest(TestConfiguration config, string testName, string debuggeeName, string scriptName, string debuggeeArguments = null, bool useCreateDump = true) { - SkipIfArm(config); - if (!SOSRunner.IsAlpine()) { // Live @@ -101,8 +98,9 @@ public class SOS [SkippableTheory, MemberData(nameof(Configurations))] public async Task GCTests(TestConfiguration config) { - // Live only SkipIfArm(config); + + // Live only if (SOSRunner.IsAlpine()) { throw new SkipTestException("lldb tests not supported on Alpine"); @@ -153,7 +151,6 @@ public class SOS [SkippableTheory, MemberData(nameof(Configurations))] public async Task StackAndOtherTests(TestConfiguration config) { - SkipIfArm(config); if (config.RuntimeFrameworkVersionMajor == 1) { throw new SkipTestException("The debuggee (SymbolTestApp) doesn't work on .NET Core 1.1 because of a AssemblyLoadContext problem"); diff --git a/src/SOS/SOS.UnitTests/Scripts/StackAndOtherTests.script b/src/SOS/SOS.UnitTests/Scripts/StackAndOtherTests.script index ac2ec47fe..51d3cdc82 100644 --- a/src/SOS/SOS.UnitTests/Scripts/StackAndOtherTests.script +++ b/src/SOS/SOS.UnitTests/Scripts/StackAndOtherTests.script @@ -54,12 +54,37 @@ IFDEF:PROJECTK SOSCOMMAND:ClrStack -r VERIFY:.*OS Thread Id:\s+0x\s+.* VERIFY:\s+Child\s+SP\s+IP\s+Call Site\s+ + VERIFY:.*\s+\s+\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 54\]\s* -VERIFY:\s+[r|e]sp=\s+[r|e]bp=\s+[r|e]ip=\s+ -VERIFY:\s+|r|e]ax=\s+[r|e]bx=\s+[r|e]cx=\s+ +IFDEF:arm +VERIFY:\s+r0=\s+r1=\s+r2=\s+ +ENDIF:arm +IFDEF:arm64 +VERIFY:\s+x0=\s+x1=\s+x2=\s+ +ENDIF:arm64 +VERIFY:\s+([r|e]sp|sp)=\s+([r|e]bp|lr)=\s+([r|e]ip|pc)=\s+ +IFDEF:x64 +VERIFY:\s+rax=\s+rbx=\s+rcx=\s+ +ENDIF:x64 +IFDEF:x86 +VERIFY:\s+eax=\s+ebx=\s+ecx=\s+ +ENDIF:x86 + VERIFY:.*\s+\s+\s+SymbolTestApp\.Program\.Foo2\(.*\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 29\]\s* -VERIFY:\s+[r|e]sp=\s+[r|e]bp=\s+[r|e]ip=\s+ -VERIFY:\s+[r|e]ax=\s+[r|e]bx=\s+[r|e]cx=\s+ +IFDEF:arm +VERIFY:\s+r0=\s+r1=\s+r2=\s+ +ENDIF:arm +IFDEF:arm64 +VERIFY:\s+x0=\s+x1=\s+x2=\s+ +ENDIF:arm64 +VERIFY:\s+([r|e]sp|sp)=\s+([r|e]bp|lr)=\s+([r|e]ip|pc)=\s+ +IFDEF:x64 +VERIFY:\s+rax=\s+rbx=\s+rcx=\s+ +ENDIF:x64 +IFDEF:x86 +VERIFY:\s+eax=\s+ebx=\s+ecx=\s+ +ENDIF:x86 + VERIFY:.*\s+\s+\s+SymbolTestApp\.Program\.Foo1\(.*\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 24\]\s* VERIFY:.*\s+\s+\s+SymbolTestApp\.Program\.Main\(.*\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 19\]\s* ENDIF:PROJECTK @@ -100,7 +125,7 @@ ENDIF:PROJECTK IFDEF:PROJECTK SOSCOMMAND:DumpStackObjects VERIFY:.*OS Thread Id:\s+0x\s+.* -VERIFY:\s+[R|E]SP/REG\s+Object\s+Name\s+ +VERIFY:\s+([R|E])*SP/REG\s+Object\s+Name\s+ VERIFY:.*\s+\s+\s+System\.String.* VERIFY:.*\s+\s+\s+System\.String\[\].* ENDIF:PROJECTK @@ -109,7 +134,7 @@ ENDIF:PROJECTK IFDEF:PROJECTK SOSCOMMAND:DumpStackObjects -verify VERIFY:.*OS Thread Id:\s+0x\s+.* -VERIFY:\s+[R|E]SP/REG\s+Object\s+Name\s+ +VERIFY:\s+([R|E])*SP/REG\s+Object\s+Name\s+ VERIFY:.*\s+\s+\s+System\.String.* VERIFY:.*\s+\s+\s+System\.String\[\].* ENDIF:PROJECTK @@ -117,26 +142,28 @@ ENDIF:PROJECTK ENDIF:ALPINE !IFDEF:DOTNETDUMP +!IFDEF:arm IFDEF:PROJECTK # Verify DumpStack works SOSCOMMAND:DumpStack VERIFY:.*OS Thread Id:\s+0x\s+.* -VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+ +VERIFY:.*Child(-SP|EBP|FP)\s+RetAddr\s+Caller, Callee\s+ VERIFY:(.*\s+\s+\s+\(MethodDesc\s+\s+(\+\s*0x\s+)?SymbolTestApp\.Program\.Foo4\(System\.String\)\),\s+calling.*\s+)|(.*\s+\s+\s+\(MethodDesc\s+\s+(\+\s*0x\s+)?SymbolTestApp\.Program\.Foo2\(Int32, System\.String\)\),\s+calling.*\s+) # Verify DumpStack -EE works SOSCOMMAND:DumpStack -EE VERIFY:.*OS Thread Id:\s+0x\s+.* -VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+ +VERIFY:.*Child(-SP|EBP|FP)\s+RetAddr\s+Caller, Callee\s+ VERIFY:(.*\s+\s+\s+\(MethodDesc\s+\s+(\+\s*0x\s+)?SymbolTestApp\.Program\.Foo4\(System\.String\)\)\s+)|(.*\s+\s+\s+\(MethodDesc\s+\s+(\+\s*0x\s+)?SymbolTestApp\.Program\.Foo2\(Int32, System\.String\)\)\s+) # Verify EEStack works SOSCOMMAND:EEStack -VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+ +VERIFY:.*Child(-SP|EBP|FP)\s+RetAddr\s+Caller, Callee\s+ VERIFY:(.*\s+\s+\s+\(MethodDesc\s+\s+(\+\s*0x\s+)?SymbolTestApp\.Program\.Foo4\(System\.String\)\),\s+calling.*\s+)|(.*\s+\s+\s+\(MethodDesc\s+\s+(\+\s*0x\s+)?SymbolTestApp\.Program\.Foo2\(Int32, System\.String\)\),\s+calling.*\s+) ENDIF:PROJECTK +ENDIF:arm ENDIF:DOTNETDUMP # Verify that IP2MD works (uses IP from ClrStack) diff --git a/src/SOS/SOS.UnitTests/Scripts/StackTests.script b/src/SOS/SOS.UnitTests/Scripts/StackTests.script index 115509c26..c4535a5be 100644 --- a/src/SOS/SOS.UnitTests/Scripts/StackTests.script +++ b/src/SOS/SOS.UnitTests/Scripts/StackTests.script @@ -56,13 +56,37 @@ IFDEF:PROJECTK SOSCOMMAND:ClrStack -r VERIFY:.*OS Thread Id:\s+0x\s+.* VERIFY:\s+Child\s+SP\s+IP\s+Call Site\s+ + VERIFY:\s+\s+\s+NestedExceptionTest\.Program\.Main\(.*\)\s+\[(?i:.*[\\|/]NestedExceptionTest\.cs) @ 8\s*\]\s+ -VERIFY:\s+[r|e]sp=\s+[r|e]bp=\s+[r|e]ip=\s+ -VERIFY:\s+[r|e]ax=\s+[r|e]bx=\s+[r|e]cx=\s+ +IFDEF:arm +VERIFY:\s+r0=\s+r1=\s+r2=\s+ +ENDIF:arm +IFDEF:arm64 +VERIFY:\s+x0=\s+x1=\s+x2=\s+ +ENDIF:arm64 +VERIFY:\s+([r|e]sp|sp)=\s+([r|e]bp|lr)=\s+([r|e]ip|pc)=\s+ +IFDEF:x64 +VERIFY:\s+rax=\s+rbx=\s+rcx=\s+ +ENDIF:x64 +IFDEF:x86 +VERIFY:\s+eax=\s+ebx=\s+ecx=\s+ +ENDIF:x86 + IFDEF:64BIT VERIFY:.*\s+\s+\s+NestedExceptionTest\.Program\.Main\(.*\)\s+\[(?i:.*[\\|/]NestedExceptionTest\.cs) @ 13\s*\]\s+ -VERIFY:\s+[r|e]sp=\s+[r|e]bp=\s+[r|e]ip=\s+ -VERIFY:\s+[r|e]ax=\s+[r|e]bx=\s+[r|e]cx=\s+ +IFDEF:arm +VERIFY:\s+r0=\s+r1=\s+r2=\s+ +ENDIF:arm +IFDEF:arm64 +VERIFY:\s+x0=\s+x1=\s+x2=\s+ +ENDIF:arm64 +VERIFY:\s+([r|e]sp|sp)=\s+([r|e]bp|lr)=\s+([r|e]ip|pc)=\s+ +IFDEF:x64 +VERIFY:\s+rax=\s+rbx=\s+rcx=\s+ +ENDIF:x64 +IFDEF:x86 +VERIFY:\s+eax=\s+ebx=\s+ecx=\s+ +ENDIF:x86 ENDIF:64BIT ENDIF:PROJECTK @@ -96,7 +120,7 @@ ENDIF:ALPINE IFDEF:PROJECTK SOSCOMMAND:DumpStackObjects VERIFY:.*OS Thread Id:\s+0x\s+.* -VERIFY:\s+[R|E]SP/REG\s+Object\s+Name\s+ +VERIFY:\s+([R|E])*SP/REG\s+Object\s+Name\s+ VERIFY:.*\s+\s+\s+System\.FormatException\s+ VERIFY:.*\s+\s+\s+System\.InvalidOperationException\s+ VERIFY:.*\s+\s+\s+System\.String.* @@ -106,29 +130,31 @@ ENDIF:PROJECTK IFDEF:PROJECTK SOSCOMMAND:DumpStackObjects -verify VERIFY:.*OS Thread Id:\s+0x\s+.* -VERIFY:\s+[R|E]SP/REG\s+Object\s+Name\s+ +VERIFY:\s+([R|E])*SP/REG\s+Object\s+Name\s+ VERIFY:.*\s+\s+\s+System\.FormatException\s+ VERIFY:.*\s+\s+\s+System\.InvalidOperationException\s+ VERIFY:.*\s+\s+\s+System\.String.* ENDIF:PROJECTK !IFDEF:DOTNETDUMP +!IFDEF:arm # 9) Verify DumpStack works SOSCOMMAND:DumpStack VERIFY:.*OS Thread Id:\s+0x\s+.* -VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+ +VERIFY:.*Child(-SP|EBP|FP)\s+RetAddr\s+Caller, Callee\s+ VERIFY:.*\s+\s+\s+\(MethodDesc\s+\s+\+\s*0x\s+NestedExceptionTest\.Program\.Main\(System\.String\[\]\)\),\s+calling.* # 10) Verify DumpStack -EE works SOSCOMMAND:DumpStack -EE VERIFY:.*OS Thread Id:\s+0x\s+.* -VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+ +VERIFY:.*Child(-SP|EBP|FP)\s+RetAddr\s+Caller, Callee\s+ VERIFY:.*\s+\s+\s+\(MethodDesc\s+\s+\+\s*0x\s+NestedExceptionTest\.Program\.Main\(System\.String\[\]\)\)\s+ # 11) Verify EEStack works SOSCOMMAND:EEStack -VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+ +VERIFY:.*Child(-SP|EBP|FP)\s+RetAddr\s+Caller, Callee\s+ VERIFY:.*\s+\s+\s+\(MethodDesc\s+\s+\+\s*0x\s+NestedExceptionTest\.Program\.Main\(System\.String\[\]\)\),\s+calling.* +ENDIF:arm ENDIF:DOTNETDUMP diff --git a/src/SOS/Strike/exts.cpp b/src/SOS/Strike/exts.cpp index ed40770d0..ef84f7469 100644 --- a/src/SOS/Strike/exts.cpp +++ b/src/SOS/Strike/exts.cpp @@ -125,9 +125,13 @@ ArchQuery(void) } #endif // SOS_TARGET_X86 #ifdef SOS_TARGET_ARM - if (targetArchitecture == IMAGE_FILE_MACHINE_ARMNT) + switch (targetArchitecture) { - targetMachine = ARMMachine::GetInstance(); + case IMAGE_FILE_MACHINE_ARM: + case IMAGE_FILE_MACHINE_THUMB: + case IMAGE_FILE_MACHINE_ARMNT: + targetMachine = ARMMachine::GetInstance(); + break; } #endif // SOS_TARGET_ARM #ifdef SOS_TARGET_ARM64 @@ -140,7 +144,7 @@ ArchQuery(void) if (targetMachine == NULL) { g_targetMachine = NULL; - ExtErr("SOS does not support the current target architecture 0x%llx.\n", targetArchitecture); + ExtErr("SOS does not support the current target architecture 0x%08x\n", targetArchitecture); return E_FAIL; } diff --git a/src/SOS/Strike/strike.h b/src/SOS/Strike/strike.h index dfc0dcfea..13e62f39a 100644 --- a/src/SOS/Strike/strike.h +++ b/src/SOS/Strike/strike.h @@ -100,11 +100,7 @@ #define INITGUID #include "guiddef.h" -#ifdef FEATURE_PAL #define SOS_PTR(x) (size_t)(x) -#else // FEATURE_PAL -#define SOS_PTR(x) (unsigned __int64)(x) -#endif // FEATURE_PAL else #include "exts.h" diff --git a/src/SOS/Strike/util.cpp b/src/SOS/Strike/util.cpp index 651950af0..049f5e81e 100644 --- a/src/SOS/Strike/util.cpp +++ b/src/SOS/Strike/util.cpp @@ -34,12 +34,7 @@ #define STRESS_LOG_READONLY #include "stresslog.h" -#ifndef FEATURE_PAL -#define MAX_SYMBOL_LEN 4096 -#define SYM_BUFFER_SIZE (sizeof(IMAGEHLP_SYMBOL) + MAX_SYMBOL_LEN) -char symBuffer[SYM_BUFFER_SIZE]; -PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) symBuffer; -#else +#ifdef FEATURE_PAL #include #include #endif // !FEATURE_PAL @@ -4805,28 +4800,44 @@ size_t CountHexCharacters(CLRDATA_ADDRESS val) return ret; } +// SOS is single threaded so a global buffer doesn't need any locking +char g_printBuffer[8192]; + +//--------------------------------------------------------------------- +// Because debuggers and hosts SOS runs on now output formatting always +// happens with the C++ runtime functions and not dbgeng. This means +// the special dbgeng formatting charaters are not supported: %N, %I, +// %ma, %mu, %msa, %msu, %y, %ly and %p takes an architecture size +// pointer (size_t) instead of always a 64bit one. +//--------------------------------------------------------------------- + HRESULT OutputVaList( ULONG mask, PCSTR format, va_list args) { -#ifndef FEATURE_PAL - if (IsInitializedByDbgEng()) + int length = _vsnprintf_s((char* const)&g_printBuffer, sizeof(g_printBuffer), _TRUNCATE, format, args); + if (length > 0) { - return g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, format, args); + return g_ExtControl->OutputVaList(mask, (char* const)&g_printBuffer, args); } - else -#endif + return E_FAIL; +} + +HRESULT +ControlledOutputVaList( + ULONG outputControl, + ULONG mask, + PCSTR format, + va_list args) +{ + int length = _vsnprintf_s((char* const)&g_printBuffer, sizeof(g_printBuffer), _TRUNCATE, format, args); + if (length > 0) { - ArrayHolder str = new char[8192]; - int length = _vsnprintf_s(str, 8192, _TRUNCATE, format, args); - if (length > 0) - { - return g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, str, args); - } - return E_FAIL; + return g_ExtControl->ControlledOutputVaList(outputControl, mask, (char* const)&g_printBuffer, args); } + return E_FAIL; } HRESULT @@ -4870,13 +4881,11 @@ void DMLOut(PCSTR format, ...) va_start(args, format); ExtOutIndent(); -#ifndef FEATURE_PAL if (IsDMLEnabled() && !Output::IsDMLExposed()) { - g_ExtControl->ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, format, args); + ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, format, args); } else -#endif { OutputVaList(DEBUG_OUTPUT_NORMAL, format, args); } @@ -4886,7 +4895,6 @@ void DMLOut(PCSTR format, ...) void IfDMLOut(PCSTR format, ...) { -#ifndef FEATURE_PAL if (Output::IsOutputSuppressed() || !IsDMLEnabled()) return; @@ -4896,7 +4904,6 @@ void IfDMLOut(PCSTR format, ...) ExtOutIndent(); g_ExtControl->ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, format, args); va_end(args); -#endif } void ExtOut(PCSTR Format, ...) diff --git a/src/SOS/Strike/xplat/dbgeng.h b/src/SOS/Strike/xplat/dbgeng.h index 4379aabb2..13f37988e 100644 --- a/src/SOS/Strike/xplat/dbgeng.h +++ b/src/SOS/Strike/xplat/dbgeng.h @@ -60,24 +60,6 @@ public: return m_lldbservices->GetInterrupt(); } - // Sends output through clients - // output callbacks if the mask is allowed - // by the current output control mask and - // according to the output distribution - // settings. - HRESULT - Output( - ULONG mask, - PCSTR format, - ...) - { - va_list args; - va_start (args, format); - HRESULT result = OutputVaList(mask, format, args); - va_end (args); - return result; - } - HRESULT OutputVaList( ULONG mask, @@ -87,26 +69,6 @@ public: return m_lldbservices->OutputVaList(mask, format, args); } - // The following methods allow direct control - // over the distribution of the given output - // for situations where something other than - // the default is desired. These methods require - // extra work in the engine so they should - // only be used when necessary. - HRESULT - ControlledOutput( - ULONG outputControl, - ULONG mask, - PCSTR format, - ...) - { - va_list args; - va_start (args, format); - HRESULT result = ControlledOutputVaList(outputControl, mask, format, args); - va_end (args); - return result; - } - HRESULT ControlledOutputVaList( ULONG outputControl, diff --git a/src/SOS/lldbplugin/inc/lldbservices.h b/src/SOS/lldbplugin/inc/lldbservices.h index 69c78e62f..881e4387e 100644 --- a/src/SOS/lldbplugin/inc/lldbservices.h +++ b/src/SOS/lldbplugin/inc/lldbservices.h @@ -41,6 +41,9 @@ extern "C" { // Symbol messages, such as for !sym noisy. #define DEBUG_OUTPUT_SYMBOLS 0x00000200 +#define DEBUG_OUTCTL_DML 0x00000020 +#define DEBUG_OUTCTL_AMBIENT_DML 0xfffffffe + // Execute and ExecuteCommandFile flags. // These flags only apply to the command // text itself; output from the executed diff --git a/src/SOS/lldbplugin/services.cpp b/src/SOS/lldbplugin/services.cpp index 22eb1cda5..76e2e792a 100644 --- a/src/SOS/lldbplugin/services.cpp +++ b/src/SOS/lldbplugin/services.cpp @@ -336,23 +336,10 @@ LLDBServices::Output( { va_list args; va_start (args, format); - HRESULT result = OutputVaList(mask, format, args); - va_end (args); - return result; -} -HRESULT -LLDBServices::OutputVaList( - ULONG mask, - PCSTR format, - va_list args) -{ HRESULT result = S_OK; char str[1024]; - va_list args_copy; - va_copy (args_copy, args); - // Try and format our string into a fixed buffer first and see if it fits size_t length = ::vsnprintf(str, sizeof(str), format, args); if (length < sizeof(str)) @@ -364,7 +351,7 @@ LLDBServices::OutputVaList( // Our stack buffer wasn't big enough to contain the entire formatted // string, so lets let vasprintf create the string for us! char *str_ptr = nullptr; - length = ::vasprintf(&str_ptr, format, args_copy); + length = ::vasprintf(&str_ptr, format, args); if (str_ptr) { OutputString(mask, str_ptr); @@ -376,11 +363,21 @@ LLDBServices::OutputVaList( } } - va_end (args_copy); - + va_end (args); return result; } +HRESULT +LLDBServices::OutputVaList( + ULONG mask, + PCSTR format, + va_list args) +{ + // Just output the string; ignore args. It is always formatted by SOS. + OutputString(mask, format); + return S_OK; +} + // The following methods allow direct control // over the distribution of the given output // for situations where something other than @@ -1603,6 +1600,42 @@ LLDBServices::GetContextFromFrame( dtcontext->R10 = GetRegister(frame, "r10"); dtcontext->R11 = GetRegister(frame, "r11"); dtcontext->R12 = GetRegister(frame, "r12"); +#elif DBG_TARGET_ARM64 + dtcontext->Pc = frame.GetPC(); + dtcontext->Sp = frame.GetSP(); + dtcontext->Lr = GetRegister(frame, "x30"); + dtcontext->Fp = GetRegister(frame, "x29"); + dtcontext->Cpsr = GetRegister(frame, "cpsr"); + + dtcontext->X0 = GetRegister(frame, "x0"); + dtcontext->X1 = GetRegister(frame, "x1"); + dtcontext->X2 = GetRegister(frame, "x2"); + dtcontext->X3 = GetRegister(frame, "x3"); + dtcontext->X4 = GetRegister(frame, "x4"); + dtcontext->X5 = GetRegister(frame, "x5"); + dtcontext->X6 = GetRegister(frame, "x6"); + dtcontext->X7 = GetRegister(frame, "x7"); + dtcontext->X8 = GetRegister(frame, "x8"); + dtcontext->X9 = GetRegister(frame, "x9"); + dtcontext->X10 = GetRegister(frame, "x10"); + dtcontext->X11 = GetRegister(frame, "x11"); + dtcontext->X12 = GetRegister(frame, "x12"); + dtcontext->X13 = GetRegister(frame, "x13"); + dtcontext->X14 = GetRegister(frame, "x14"); + dtcontext->X15 = GetRegister(frame, "x15"); + dtcontext->X16 = GetRegister(frame, "x16"); + dtcontext->X17 = GetRegister(frame, "x17"); + dtcontext->X18 = GetRegister(frame, "x18"); + dtcontext->X19 = GetRegister(frame, "x19"); + dtcontext->X20 = GetRegister(frame, "x20"); + dtcontext->X21 = GetRegister(frame, "x21"); + dtcontext->X22 = GetRegister(frame, "x22"); + dtcontext->X23 = GetRegister(frame, "x23"); + dtcontext->X24 = GetRegister(frame, "x24"); + dtcontext->X25 = GetRegister(frame, "x25"); + dtcontext->X26 = GetRegister(frame, "x26"); + dtcontext->X27 = GetRegister(frame, "x27"); + dtcontext->X28 = GetRegister(frame, "x28"); #elif DBG_TARGET_X86 dtcontext->Eip = frame.GetPC(); dtcontext->Esp = frame.GetSP(); diff --git a/src/Tools/dotnet-dump/AnalyzeContext.cs b/src/Tools/dotnet-dump/AnalyzeContext.cs deleted file mode 100644 index fae01c05b..000000000 --- a/src/Tools/dotnet-dump/AnalyzeContext.cs +++ /dev/null @@ -1,164 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// 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.Diagnostics.Runtime; -using Microsoft.SymbolStore; -using Microsoft.SymbolStore.KeyGenerators; -using SOS; -using System; -using System.Collections.Generic; -using System.CommandLine; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Threading; - -namespace Microsoft.Diagnostics.Tools.Dump -{ - /// - /// The the common context for analyze commands - /// - public class AnalyzeContext: ISOSHostContext - { - private readonly IConsole _console; - private ClrRuntime _runtime; - - private static SOSHost s_sosHost; - private static string s_tempDirectory; - private static string s_dacFilePath; - - public AnalyzeContext(IConsole console, DataTarget target) - { - _console = console; - Target = target; - } - - /// - /// ClrMD data target - /// - public DataTarget Target { get; } - - /// - /// ClrMD runtime info - /// - public ClrRuntime Runtime - { - get - { - if (_runtime == null) - { - if (Target.ClrVersions.Count != 1) { - throw new InvalidOperationException("More or less than 1 CLR version is present"); - } - ClrInfo clrInfo = Target.ClrVersions[0]; - string dacFilePath = GetDacFile(clrInfo); - try - { - _runtime = clrInfo.CreateRuntime(dacFilePath); - } - catch (DllNotFoundException ex) - { - // This is a workaround for the Microsoft SDK docker images. Can fail when clrmd uses libdl.so - // to create a symlink to and load the DAC module. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - throw new DllNotFoundException("Problem initializing CLRMD. Try installing libc6-dev (apt-get install libc6-dev) to work around this problem.", ex); - } - else - { - throw; - } - } - } - return _runtime; - } - } - - private string GetDacFile(ClrInfo clrInfo) - { - if (s_dacFilePath == null) - { - string dac = clrInfo.LocalMatchingDac; - if (dac != null && File.Exists(dac)) - { - s_dacFilePath = dac; - } - else if (SymbolReader.IsSymbolStoreEnabled()) - { - string dacFileName = Path.GetFileName(dac ?? clrInfo.DacInfo.FileName); - if (dacFileName != null) - { - SymbolStoreKey key = null; - - if (clrInfo.ModuleInfo.BuildId != null) - { - IEnumerable keys = ELFFileKeyGenerator.GetKeys( - KeyTypeFlags.ClrKeys, clrInfo.ModuleInfo.FileName, clrInfo.ModuleInfo.BuildId, symbolFile: false, symbolFileName: null); - - key = keys.SingleOrDefault((k) => Path.GetFileName(k.FullPathName) == dacFileName); - } - else - { - // Use the coreclr.dll's id (timestamp/filesize) to download the the dac module. - key = PEFileKeyGenerator.GetKey(dacFileName, clrInfo.ModuleInfo.TimeStamp, clrInfo.ModuleInfo.FileSize); - } - - if (key != null) - { - if (s_tempDirectory == null) - { - int processId = Process.GetCurrentProcess().Id; - s_tempDirectory = Path.Combine(Path.GetTempPath(), "analyze" + processId.ToString()); - } - // Now download the DAC module from the symbol server - s_dacFilePath = SymbolReader.GetSymbolFile(key, s_tempDirectory); - } - } - } - - if (s_dacFilePath == null) - { - throw new FileNotFoundException("Could not find matching DAC for this runtime: {0}", clrInfo.ModuleInfo.FileName); - } - } - - return s_dacFilePath; - } - - /// - /// Returns the SOS host instance - /// - public SOSHost SOSHost - { - get - { - if (s_sosHost == null) { - s_sosHost = new SOSHost(Target.DataReader, this); - s_sosHost.InitializeSOSHost(s_tempDirectory, s_dacFilePath, dbiFilePath: null); - } - return s_sosHost; - } - } - - /// - /// Current OS thread Id - /// - public int CurrentThreadId { get; set; } - - /// - /// Cancellation token for current command - /// - public CancellationToken CancellationToken { get; set; } - - /// - /// Console write function - /// - /// - void ISOSHostContext.Write(string text) - { - _console.Out.Write(text); - } - } -} \ No newline at end of file diff --git a/src/Tools/dotnet-dump/Analyzer.cs b/src/Tools/dotnet-dump/Analyzer.cs index f92d79edd..839f734cc 100644 --- a/src/Tools/dotnet-dump/Analyzer.cs +++ b/src/Tools/dotnet-dump/Analyzer.cs @@ -2,15 +2,19 @@ // 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.Diagnostics.DebugServices; using Microsoft.Diagnostics.Repl; using Microsoft.Diagnostics.Runtime; +using Microsoft.SymbolStore; +using Microsoft.SymbolStore.KeyGenerators; using SOS; using System; +using System.Collections.Generic; using System.CommandLine; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; -using System.Reflection.Metadata; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -19,8 +23,12 @@ namespace Microsoft.Diagnostics.Tools.Dump { public class Analyzer { + private readonly ServiceProvider _serviceProvider; private readonly ConsoleProvider _consoleProvider; private readonly CommandProcessor _commandProcessor; + private string _dacFilePath; + + private static string s_tempDirectory; /// /// Enable the assembly resolver to get the right SOS.NETCore version (the one @@ -33,15 +41,15 @@ namespace Microsoft.Diagnostics.Tools.Dump public Analyzer() { + _serviceProvider = new ServiceProvider(); _consoleProvider = new ConsoleProvider(); Type type = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? typeof(SOSCommandForWindows) : typeof(SOSCommand); - _commandProcessor = new CommandProcessor(_consoleProvider, new Assembly[] { typeof(Analyzer).Assembly }, new Type[] { type }); - _commandProcessor.AddService(_consoleProvider); + _commandProcessor = new CommandProcessor(_serviceProvider, _consoleProvider, new Assembly[] { typeof(Analyzer).Assembly }, new Type[] { type }); } public async Task Analyze(FileInfo dump_path, string[] command) { - _consoleProvider.Out.WriteLine($"Loading core dump: {dump_path} ..."); + _consoleProvider.WriteLine($"Loading core dump: {dump_path} ..."); try { @@ -58,14 +66,11 @@ namespace Microsoft.Diagnostics.Tools.Dump using (target) { - _consoleProvider.Out.WriteLine("Ready to process analysis commands. Type 'help' to list available commands or 'help [command]' to get detailed help on a command."); - _consoleProvider.Out.WriteLine("Type 'quit' or 'exit' to exit the session."); + _consoleProvider.WriteLine("Ready to process analysis commands. Type 'help' to list available commands or 'help [command]' to get detailed help on a command."); + _consoleProvider.WriteLine("Type 'quit' or 'exit' to exit the session."); - // Create common analyze context for commands - var analyzeContext = new AnalyzeContext(_consoleProvider, target) { - CurrentThreadId = unchecked((int)target.DataReader.EnumerateAllThreads().FirstOrDefault()) - }; - _commandProcessor.AddService(analyzeContext); + // Add all the services needed by commands and other services + AddServices(target); // Automatically enable symbol server support SymbolReader.InitializeSymbolStore(logging: false, msdl: true, symweb: false, symbolServerPath: null, symbolCachePath: null, windowsSymbolPath: null); @@ -79,6 +84,7 @@ namespace Microsoft.Diagnostics.Tools.Dump } // Start interactive command line processing + var analyzeContext = _serviceProvider.GetService(); await _consoleProvider.Start(async (string commandLine, CancellationToken cancellation) => { analyzeContext.CancellationToken = cancellation; await _commandProcessor.Parse(commandLine); @@ -95,11 +101,121 @@ namespace Microsoft.Diagnostics.Tools.Dump ex is InvalidOperationException || ex is NotSupportedException) { - _consoleProvider.Error.WriteLine($"{ex.Message}"); + _consoleProvider.WriteLine(OutputType.Error, $"{ex.Message}"); return 1; } return 0; } + + /// + /// Add all the services needed by commands + /// + private void AddServices(DataTarget target) + { + _serviceProvider.AddService(target); + _serviceProvider.AddService(_consoleProvider); + _serviceProvider.AddService(_commandProcessor); + _serviceProvider.AddServiceFactory(typeof(IHelpBuilder), _commandProcessor.CreateHelpBuilder); + + // Create common analyze context for commands + var analyzeContext = new AnalyzeContext() { + CurrentThreadId = unchecked((int)target.DataReader.EnumerateAllThreads().FirstOrDefault()) + }; + _serviceProvider.AddService(analyzeContext); + + // Add the register, SOSHost and ClrRuntime services + var registerService = new RegisterService(target); + _serviceProvider.AddService(registerService); + + _serviceProvider.AddServiceFactory(typeof(ClrRuntime), () => CreateRuntime(target)); + + _serviceProvider.AddServiceFactory(typeof(SOSHost), () => { + var sosHost = new SOSHost(_serviceProvider); + sosHost.InitializeSOSHost(s_tempDirectory, _dacFilePath, dbiFilePath: null); + return sosHost; + }); + } + + /// + /// ClrRuntime service factory + /// + private ClrRuntime CreateRuntime(DataTarget target) + { + ClrRuntime runtime; + if (target.ClrVersions.Count != 1) { + throw new InvalidOperationException("More or less than 1 CLR version is present"); + } + ClrInfo clrInfo = target.ClrVersions[0]; + string dacFilePath = GetDacFile(clrInfo); + try + { + runtime = clrInfo.CreateRuntime(dacFilePath); + } + catch (DllNotFoundException ex) + { + // This is a workaround for the Microsoft SDK docker images. Can fail when clrmd uses libdl.so + // to create a symlink to and load the DAC module. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + throw new DllNotFoundException("Problem initializing CLRMD. Try installing libc6-dev (apt-get install libc6-dev) to work around this problem.", ex); + } + else + { + throw; + } + } + return runtime; + } + + private string GetDacFile(ClrInfo clrInfo) + { + if (_dacFilePath == null) + { + string dac = clrInfo.LocalMatchingDac; + if (dac != null && File.Exists(dac)) + { + _dacFilePath = dac; + } + else if (SymbolReader.IsSymbolStoreEnabled()) + { + string dacFileName = Path.GetFileName(dac ?? clrInfo.DacInfo.FileName); + if (dacFileName != null) + { + SymbolStoreKey key = null; + + if (clrInfo.ModuleInfo.BuildId != null) + { + IEnumerable keys = ELFFileKeyGenerator.GetKeys( + KeyTypeFlags.ClrKeys, clrInfo.ModuleInfo.FileName, clrInfo.ModuleInfo.BuildId, symbolFile: false, symbolFileName: null); + + key = keys.SingleOrDefault((k) => Path.GetFileName(k.FullPathName) == dacFileName); + } + else + { + // Use the coreclr.dll's id (timestamp/filesize) to download the the dac module. + key = PEFileKeyGenerator.GetKey(dacFileName, clrInfo.ModuleInfo.TimeStamp, clrInfo.ModuleInfo.FileSize); + } + + if (key != null) + { + if (s_tempDirectory == null) + { + int processId = Process.GetCurrentProcess().Id; + s_tempDirectory = Path.Combine(Path.GetTempPath(), "analyze" + processId.ToString()); + } + // Now download the DAC module from the symbol server + _dacFilePath = SymbolReader.GetSymbolFile(key, s_tempDirectory); + } + } + } + + if (_dacFilePath == null) + { + throw new FileNotFoundException("Could not find matching DAC for this runtime: {0}", clrInfo.ModuleInfo.FileName); + } + } + return _dacFilePath; + } } } diff --git a/src/Tools/dotnet-dump/Commands/ClrModulesCommand.cs b/src/Tools/dotnet-dump/Commands/ClrModulesCommand.cs index 9c2a32287..fe44d534d 100644 --- a/src/Tools/dotnet-dump/Commands/ClrModulesCommand.cs +++ b/src/Tools/dotnet-dump/Commands/ClrModulesCommand.cs @@ -12,11 +12,11 @@ namespace Microsoft.Diagnostics.Tools.Dump [Command(Name = "clrmodules", Help = "Lists the managed modules in the process.")] public class ClrModulesCommand : CommandBase { - public AnalyzeContext AnalyzeContext { get; set; } + public ClrRuntime Runtime { get; set; } public override void Invoke() { - foreach (ClrModule module in AnalyzeContext.Runtime.Modules) + foreach (ClrModule module in Runtime.Modules) { WriteLine("{0:X16} {1}", module.Address, module.FileName); } diff --git a/src/Tools/dotnet-dump/Commands/ExitCommand.cs b/src/Tools/dotnet-dump/Commands/ExitCommand.cs index a7dcaa592..1c249c424 100644 --- a/src/Tools/dotnet-dump/Commands/ExitCommand.cs +++ b/src/Tools/dotnet-dump/Commands/ExitCommand.cs @@ -12,11 +12,9 @@ namespace Microsoft.Diagnostics.Tools.Dump [CommandAlias(Name = "quit")] public class ExitCommand : CommandBase { - public ConsoleProvider ConsoleProvider { get; set; } - public override void Invoke() { - ConsoleProvider.Stop(); + Console.Exit(); } } } diff --git a/src/Tools/dotnet-dump/Commands/HelpCommand.cs b/src/Tools/dotnet-dump/Commands/HelpCommand.cs index 2a1289090..d2f3ce850 100644 --- a/src/Tools/dotnet-dump/Commands/HelpCommand.cs +++ b/src/Tools/dotnet-dump/Commands/HelpCommand.cs @@ -24,7 +24,7 @@ namespace Microsoft.Diagnostics.Tools.Dump HelpBuilder.Write(command); } else { - Console.Error.WriteLine($"Help for {Command} not found."); + WriteLineError($"Help for {Command} not found."); } } } diff --git a/src/Tools/dotnet-dump/Commands/ModulesCommand.cs b/src/Tools/dotnet-dump/Commands/ModulesCommand.cs index 6d0b0a9d1..ecf97ff19 100644 --- a/src/Tools/dotnet-dump/Commands/ModulesCommand.cs +++ b/src/Tools/dotnet-dump/Commands/ModulesCommand.cs @@ -18,11 +18,11 @@ namespace Microsoft.Diagnostics.Tools.Dump [OptionAlias(Name = "-v")] public bool Verbose { get; set; } - public AnalyzeContext AnalyzeContext { get; set; } + public DataTarget DataTarget { get; set; } public override void Invoke() { - foreach (ModuleInfo module in AnalyzeContext.Target.DataReader.EnumerateModules()) + foreach (ModuleInfo module in DataTarget.DataReader.EnumerateModules()) { if (Verbose) { diff --git a/src/Tools/dotnet-dump/Commands/RegistersCommand.cs b/src/Tools/dotnet-dump/Commands/RegistersCommand.cs new file mode 100644 index 000000000..441613ba4 --- /dev/null +++ b/src/Tools/dotnet-dump/Commands/RegistersCommand.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// 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.Diagnostics.DebugServices; +using Microsoft.Diagnostics.Repl; +using Microsoft.Diagnostics.Runtime; +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.Linq; + +namespace Microsoft.Diagnostics.Tools.Dump +{ + [Command(Name = "registers", Help = "Displays the thread's registers.")] + public class RegistersCommand : CommandBase + { + [Argument(Help = "The thread index to display, otherwise use the current thread.")] + public int? ThreadIndex { get; set; } = null; + + public DataTarget DataTarget { get; set; } + + public AnalyzeContext AnalyzeContext { get; set; } + + public RegisterService RegisterService { get; set; } + + public override void Invoke() + { + IEnumerable threads = DataTarget.DataReader.EnumerateAllThreads(); + uint threadId; + + if (ThreadIndex.HasValue) + { + if (ThreadIndex.Value >= threads.Count()) { + throw new InvalidOperationException($"Invalid thread index {ThreadIndex.Value}"); + } + threadId = threads.ElementAt(ThreadIndex.Value); + } + else + { + threadId = (uint)AnalyzeContext.CurrentThreadId; + } + + foreach (RegisterService.RegisterInfo register in RegisterService.Registers) + { + if (RegisterService.GetRegisterValue(threadId, register.RegisterIndex, out ulong value)) + { + switch (register.RegisterSize) + { + case 1: + WriteLine("{0} = {1:X1}", register.RegisterName, value); + break; + case 2: + WriteLine("{0} = {1:X4}", register.RegisterName, value); + break; + case 4: + WriteLine("{0} = {1:X8}", register.RegisterName, value); + break; + case 8: + WriteLine("{0} = {1:X16}", register.RegisterName, value); + break; + } + } + else + { + WriteLine("{0} = ", register.RegisterName); + } + } + } + } +} diff --git a/src/Tools/dotnet-dump/Commands/SOSCommand.cs b/src/Tools/dotnet-dump/Commands/SOSCommand.cs index 40cf8b45c..2e622abb7 100644 --- a/src/Tools/dotnet-dump/Commands/SOSCommand.cs +++ b/src/Tools/dotnet-dump/Commands/SOSCommand.cs @@ -2,7 +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.Diagnostics.DebugServices; using Microsoft.Diagnostics.Repl; +using SOS; using System; using System.CommandLine; using System.IO; @@ -46,7 +48,7 @@ namespace Microsoft.Diagnostics.Tools.Dump [Argument(Name = "arguments", Help = "Arguments to SOS command.")] public string[] Arguments { get; set; } - public AnalyzeContext AnalyzeContext { get; set; } + public SOSHost SOSHost { get; set; } public override void Invoke() { @@ -55,17 +57,17 @@ namespace Microsoft.Diagnostics.Tools.Dump if (Arguments.Length > 0) { arguments = string.Concat(Arguments.Select((arg) => arg + " ")); } - AnalyzeContext.SOSHost.ExecuteCommand(AliasExpansion, arguments); + SOSHost.ExecuteCommand(AliasExpansion, arguments); } catch (Exception ex) when (ex is FileNotFoundException || ex is EntryPointNotFoundException || ex is InvalidOperationException) { - Console.Error.WriteLine(ex.Message); + WriteLineError(ex.Message); } } [HelpInvoke] public void InvokeHelp() { - AnalyzeContext.SOSHost.ExecuteCommand("Help", AliasExpansion); + SOSHost.ExecuteCommand("Help", AliasExpansion); } } } diff --git a/src/Tools/dotnet-dump/Commands/SetThreadCommand.cs b/src/Tools/dotnet-dump/Commands/SetThreadCommand.cs index c08dd307c..c13ac1cb6 100644 --- a/src/Tools/dotnet-dump/Commands/SetThreadCommand.cs +++ b/src/Tools/dotnet-dump/Commands/SetThreadCommand.cs @@ -2,7 +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.Diagnostics.DebugServices; using Microsoft.Diagnostics.Repl; +using Microsoft.Diagnostics.Runtime; using System; using System.Collections.Generic; using System.CommandLine; @@ -17,13 +19,15 @@ namespace Microsoft.Diagnostics.Tools.Dump [Argument(Help = "The thread index to set, otherwise displays the list of threads.")] public int? ThreadIndex { get; set; } = null; + public DataTarget DataTarget { get; set; } + public AnalyzeContext AnalyzeContext { get; set; } public override void Invoke() { if (ThreadIndex.HasValue) { - IEnumerable threads = AnalyzeContext.Target.DataReader.EnumerateAllThreads(); + IEnumerable threads = DataTarget.DataReader.EnumerateAllThreads(); if (ThreadIndex.Value >= threads.Count()) { throw new InvalidOperationException($"Invalid thread index {ThreadIndex.Value}"); } @@ -32,7 +36,7 @@ namespace Microsoft.Diagnostics.Tools.Dump else { int index = 0; - foreach (uint threadId in AnalyzeContext.Target.DataReader.EnumerateAllThreads()) + foreach (uint threadId in DataTarget.DataReader.EnumerateAllThreads()) { WriteLine("{0}{1} 0x{2:X4} ({2})", threadId == AnalyzeContext.CurrentThreadId ? "*" : " ", index, threadId); index++; diff --git a/src/Tools/dotnet-dump/dotnet-dump.csproj b/src/Tools/dotnet-dump/dotnet-dump.csproj index 22008e224..f1aa20e8e 100644 --- a/src/Tools/dotnet-dump/dotnet-dump.csproj +++ b/src/Tools/dotnet-dump/dotnet-dump.csproj @@ -2,6 +2,7 @@ netcoreapp2.1 + true 2.1.0 dotnet-dump Microsoft.Diagnostic.Tools.Dump @@ -21,6 +22,7 @@ +