Add "dotnet-dump analyze" tests using the existing SOS tests and SOS runner.
Removed "dumpstack" and "eestack" from dotnet-dump. They don't work well at all in
the hosted environment. Added "gcwhere".
Changed the SOSHost interop code from using "out" parameters in most cases to
using IntPtr/Marshal.Write* so the pointer can be checked for null.
# Init the target distro name
initTargetDistroRid
+echo "RID: $__DistroRid"
+
if [ "$__HostOS" == "OSX" ]; then
export LLDB_H=$__ProjectRoot/src/SOS/lldbplugin/swift-4.0
export LLDB_LIB=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/LLDB
python --version
fi
+
# Build native components
if [ $__Build == true ]; then
if [[ $__CI == true ]]; then
fi
build_native "$__BuildArch" "$__IntermediatesDir" "$__ExtraCmakeArgs"
+fi
+if [[ $__Build == true || $__Test == true ]]; then
# Copy the native SOS binaries to where these tools expect for testing
__dotnet_sos=$__RootBinDir/bin/dotnet-sos/$__BuildType/netcoreapp2.1/publish/$__DistroRid
__dotnet_dump=$__RootBinDir/bin/dotnet-dump/$__BuildType/netcoreapp2.1/publish/$__DistroRid
+
mkdir -p "$__dotnet_sos"
mkdir -p "$__dotnet_dump"
+
cp "$__BinDir"/* "$__dotnet_sos"
+ echo "Copied SOS to $__dotnet_sos"
+
cp "$__BinDir"/* "$__dotnet_dump"
+ echo "Copied SOS to $__dotnet_dump"
fi
# Run SOS/lldbplugin tests
using System;
using System.Collections.Generic;
using System.CommandLine;
+using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
private CancellationTokenSource m_interruptExecutingCommand;
private string m_clearLine;
+ private bool m_interactiveConsole;
private bool m_refreshingLine;
private StringBuilder m_activeLine;
{
m_lastCommandLine = null;
m_shutdown = false;
+ m_interactiveConsole = !Console.IsInputRedirected;
RefreshLine();
// Start keyboard processing
while (!m_shutdown) {
- ConsoleKeyInfo keyInfo = Console.ReadKey(true);
- await ProcessKeyInfo(keyInfo, dispatchCommand);
+ if (m_interactiveConsole)
+ {
+ ConsoleKeyInfo keyInfo = Console.ReadKey(true);
+ await ProcessKeyInfo(keyInfo, dispatchCommand);
+ }
+ else
+ {
+ // The input has been redirected (i.e. testing or in script)
+ WriteLine(OutputType.Normal, "<END_COMMAND_OUTPUT>");
+ string line = Console.ReadLine();
+ if (string.IsNullOrEmpty(line)) {
+ continue;
+ }
+ await Dispatch(line, dispatchCommand);
+ }
}
}
/// </summary>
private void OnCtrlBreakKeyPress(object sender, ConsoleCancelEventArgs e)
{
- if (!m_shutdown) {
+ if (!m_shutdown && m_interactiveConsole) {
if (m_interruptExecutingCommand != null) {
m_interruptExecutingCommand.Cancel();
}
private void ClearLine()
{
+ if (!m_interactiveConsole) {
+ return;
+ }
+
if (m_commandExecuting != 0) {
return;
}
private void PrintActiveLine()
{
+ if (!m_interactiveConsole) {
+ return;
+ }
+
if (m_shutdown) {
return;
}
// ctrl-c interrupted the command
m_lastCommandLine = null;
}
- catch (Exception ex) when (!(ex is NullReferenceException || ex is ArgumentNullException))
+ catch (Exception ex) when (!(ex is NullReferenceException || ex is ArgumentNullException || ex is ArgumentException))
{
WriteLine(OutputType.Error, "ERROR: {0}", ex.Message);
m_lastCommandLine = null;
uint descriptionSize,
out uint descriptionUsed)
{
- type = 0;
- processId = 0;
- threadId = 0;
- extraInformationSize = 0;
- extraInformationUsed = 0;
- descriptionUsed = 0;
- return E_NOTIMPL;
+ // Should never be called. This exception will take down the program.
+ throw new NotImplementedException("GetLastEventInformation");
}
int Disassemble(
DEBUG_DISASM flags,
StringBuilder buffer,
uint bufferSize,
- out uint disassemblySize,
- out ulong endOffset)
+ IntPtr pdisassemblySize, // uint
+ IntPtr pendOffset) // ulong
{
- disassemblySize = 0;
- endOffset = 0;
+ buffer.Clear();
+ WriteUInt32(pdisassemblySize, 0);
+ WriteUInt64(pendOffset, offset);
return E_NOTIMPL;
}
IntPtr frameContexts,
uint frameContextsSize,
uint frameContextsEntrySize,
- IntPtr pframesFilled)
+ IntPtr pframesFilled) // uint
{
- return E_NOTIMPL;
+ // Don't fail, but always return 0 native frames so "clrstack -f" still prints the managed frames
+ WriteUInt32(pframesFilled, 0);
+ return S_OK;
}
int ReadVirtual(
ulong address,
IntPtr buffer,
int bytesRequested,
- IntPtr pbytesRead)
+ IntPtr pbytesRead) // uint
{
if (_dataReader.ReadMemory(address, buffer, bytesRequested, out int bytesRead))
{
- if (pbytesRead != IntPtr.Zero)
- {
- Marshal.WriteInt32(pbytesRead, bytesRead);
- }
+ WriteUInt32(pbytesRead, (uint)bytesRead);
return S_OK;
}
return E_FAIL;
IntPtr pbytesWritten)
{
// This gets used by MemoryBarrier() calls in the dac, which really shouldn't matter what we do here.
- if (pbytesWritten != IntPtr.Zero) {
- Marshal.WriteInt32(pbytesWritten, (int)bytesRequested);
- }
+ WriteUInt32(pbytesWritten, bytesRequested);
return S_OK;
}
ulong offset,
StringBuilder nameBuffer,
uint nameBufferSize,
- out uint nameSize,
- out ulong displacement)
+ IntPtr pnameSize, // uint
+ IntPtr pdisplacement) // ulong
{
- nameSize = 0;
- displacement = 0;
+ nameBuffer.Clear();
+ WriteUInt32(pnameSize, 0);
+ WriteUInt64(pdisplacement, 0);
return E_NOTIMPL;
}
out ulong baseAddress)
{
baseAddress = 0;
-
try
{
ModuleInfo module = _dataReader.EnumerateModules().ElementAt((int)index);
{
return E_FAIL;
}
-
return S_OK;
}
Debug.Assert(startIndex == 0);
baseAddress = 0;
-
foreach (ModuleInfo module in _dataReader.EnumerateModules())
{
if (string.Equals(Path.GetFileName(module.FileName), name))
IntPtr self,
ulong offset,
uint startIndex,
- out uint index,
- out ulong baseAddress)
+ IntPtr pindex, // uint
+ IntPtr pbaseAddress) // ulong
{
- index = 0;
- baseAddress = 0;
+ WriteUInt32(pindex, 0);
+ WriteUInt64(pbaseAddress, 0);
return E_NOTIMPL;
}
ulong baseAddress,
StringBuilder imageNameBuffer,
uint imageNameBufferSize,
- out uint imageNameSize,
+ IntPtr pimageNameSize, // uint
StringBuilder moduleNameBuffer,
uint ModuleNameBufferSize,
- out uint moduleNameSize,
+ IntPtr pmoduleNameSize, // uint
StringBuilder loadedImageNameBuffer,
uint loadedImageNameBufferSize,
- out uint loadedImageNameSize)
+ IntPtr ploadedImageNameSize) // uint
{
- imageNameSize = 0;
- moduleNameSize = 0;
- loadedImageNameSize = 0;
+ WriteUInt32(pimageNameSize, 0);
+ WriteUInt32(pmoduleNameSize, 0);
+ WriteUInt32(ploadedImageNameSize, 0);
return E_NOTIMPL;
}
int GetLineByOffset(
IntPtr self,
ulong offset,
- out uint line,
+ IntPtr pline, // uint
StringBuilder fileBuffer,
uint fileBufferSize,
- out uint fileSize,
- out ulong displacement)
+ IntPtr pfileSize, // uint
+ IntPtr pdisplacement) // ulong
{
- line = 0;
- fileSize = 0;
- displacement = 0;
+ WriteUInt32(pline, 0);
+ WriteUInt32(pfileSize, 0);
+ WriteUInt64(pdisplacement, 0);
return E_NOTIMPL;
}
string file,
ulong[] buffer,
uint bufferLines,
- out uint fileLines)
+ IntPtr pfileLines) // uint
{
- fileLines = 0;
+ WriteUInt32(pfileLines, 0);
return E_NOTIMPL;
}
uint startElement,
string file,
uint flags,
- out uint foundElement,
+ IntPtr pfoundElement, // uint
StringBuilder buffer,
uint bufferSize,
- out uint foundSize)
+ IntPtr pfoundSize) // uint
{
- foundElement = 0;
- foundSize = 0;
+ WriteUInt32(pfoundElement, 0);
+ WriteUInt32(pfoundSize, 0);
return E_NOTIMPL;
}
#endregion
+ void WriteUInt32(IntPtr pointer, uint value)
+ {
+ if (pointer != IntPtr.Zero) {
+ Marshal.WriteInt32(pointer, unchecked((int)value));
+ }
+ }
+
+ void WriteUInt64(IntPtr pointer, ulong value)
+ {
+ if (pointer != IntPtr.Zero) {
+ Marshal.WriteInt64(pointer, unchecked((long)value));
+ }
+ }
+
// TODO: Support other architectures
int GetRegister(string register, out ulong value)
{
DEBUG_DISASM flags,
[Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder buffer,
uint bufferSize,
- out uint disassemblySize,
- out ulong endOffset);
+ IntPtr pdisassemblySize,
+ IntPtr pendOffset);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int GetContextStackTraceDelegate(
ulong offset,
[Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder nameBuffer,
uint nameBufferSize,
- out uint nameSize,
- out ulong displacement);
+ IntPtr pnameSize,
+ IntPtr pdisplacement);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int GetNumberModulesDelegate(
IntPtr self,
ulong offset,
uint startIndex,
- out uint index,
- out ulong baseAddress);
+ IntPtr pindex,
+ IntPtr pbaseAddress);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int GetModuleNamesDelegate(
ulong baseAddress,
[Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder imageNameBuffer,
uint imageNameBufferSize,
- out uint imageNameSize,
+ IntPtr pimageNameSize,
[Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder moduleNameBuffer,
uint ModuleNameBufferSize,
- out uint moduleNameSize,
+ IntPtr pmoduleNameSize,
[Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder loadedImageNameBuffer,
uint loadedImageNameBufferSize,
- out uint loadedImageNameSize);
+ IntPtr ploadedImageNameSize);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int GetLineByOffsetDelegate(
IntPtr self,
ulong offset,
- out uint line,
+ IntPtr line,
[Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder fileBuffer,
uint fileBufferSize,
- out uint fileSize,
- out ulong displacement);
+ IntPtr fileSize,
+ IntPtr displacement);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int GetSourceFileLineOffsetsDelegate(
[In, MarshalAs(UnmanagedType.LPStr)] string file,
[Out, MarshalAs(UnmanagedType.LPArray)] ulong[] buffer,
uint bufferLines,
- out uint fileLines);
+ IntPtr fileLines);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int FindSourceFileDelegate(
uint startElement,
[In, MarshalAs(UnmanagedType.LPStr)] string file,
uint flags,
- out uint foundElement,
+ IntPtr foundElement,
[Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder buffer,
uint bufferSize,
- out uint foundSize);
+ IntPtr foundSize);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int GetCurrentProcessIdDelegate(
<Options>
<Option Condition="$(OS) == Linux">
<SOSPath>$(InstallDir)/libsosplugin.so</SOSPath>
+ <DotNetDumpPath>$(RootBinDir)/bin/dotnet-dump/$(TargetConfiguration)/netcoreapp2.1/publish/dotnet-dump.dll</DotNetDumpPath>
<DebuggeeDumpOutputRootDir>$(DumpDir)/$(TestProduct)/$(RuntimeFrameworkVersion)/$(BuildProjectFramework)</DebuggeeDumpOutputRootDir>
<DebuggeeDumpInputRootDir>$(DebuggeeDumpOutputRootDir)</DebuggeeDumpInputRootDir>
</Option>
await runner.RunScript(scriptName);
}
- // Against a crash dump.
+ // Generate a crash dump.
if (IsCreateDumpConfig(config))
{
await SOSRunner.CreateDump(config, Output, testName, debuggeeName, debuggeeArguments, useCreateDump);
}
+ // Test against a crash dump.
if (IsOpenDumpConfig(config))
{
- using (SOSRunner runner = await SOSRunner.StartDebugger(config, Output, testName, debuggeeName, debuggeeArguments, loadDump: true))
+ // With cdb (Windows) or lldb (Linux or OSX)
+ using (SOSRunner runner = await SOSRunner.StartDebugger(config, Output, testName, debuggeeName, debuggeeArguments, SOSRunner.Options.LoadDump))
{
await runner.RunScript(scriptName);
}
+
+ // With the dotnet-dump analyze tool
+ if (OS.Kind == OSKind.Linux)
+ {
+ using (SOSRunner runner = await SOSRunner.StartDebugger(config, Output, testName, debuggeeName, debuggeeArguments, SOSRunner.Options.LoadDumpWithDotNetDump))
+ {
+ await runner.RunScript(scriptName);
+ }
+ }
}
}
string _lastCommandOutput;
string _previousCommandCapture;
+ public enum Options
+ {
+ None,
+ GenerateDump,
+ LoadDump,
+ LoadDumpWithDotNetDump,
+ }
+
public enum NativeDebugger
{
Unknown,
Cdb,
Lldb,
- Gdb
+ Gdb,
+ DotNetDump,
}
public const string HexValueRegEx = "[A-Fa-f0-9]+(`[A-Fa-f0-9]+)?";
if (!config.CreateDumpExists || !useCreateDump || config.GenerateDumpWithLLDB() || config.GenerateDumpWithGDB())
{
- using (SOSRunner runner = await SOSRunner.StartDebugger(config, output, testName, debuggeeName, debuggeeArguments, loadDump: false, generateDump: true))
+ using (SOSRunner runner = await SOSRunner.StartDebugger(config, output, testName, debuggeeName, debuggeeArguments, Options.GenerateDump))
{
try
{
// Run the debuggee with the createdump environment variables set to generate a coredump on unhandled exception
var testLogger = new TestRunner.TestLogger(outputHelper.IndentedOutput);
- var variables = GenerateVariables(config, debuggeeConfig, generateDump: true);
+ var variables = GenerateVariables(config, debuggeeConfig, Options.GenerateDump);
ProcessRunner processRunner = new ProcessRunner(exePath, ReplaceVariables(variables, arguments.ToString())).
WithLog(testLogger).
WithTimeout(TimeSpan.FromMinutes(5)).
/// <param name="testName">name of test</param>
/// <param name="debuggeeName">debuggee name</param>
/// <param name="debuggeeArguments">optional args to pass to debuggee</param>
- /// <param name="generateDump">if true, generate dump with native debugger</param>
- /// <param name="loadDump">if true, load dump with native debugger</param>
+ /// <param name="options">dump options</param>
/// <returns>sos runner instance</returns>
public static async Task<SOSRunner> StartDebugger(TestConfiguration config, ITestOutputHelper output, string testName, string debuggeeName,
- string debuggeeArguments = null, bool loadDump = false, bool generateDump = false)
+ string debuggeeArguments = null, Options options = Options.None)
{
TestRunner.OutputHelper outputHelper = null;
SOSRunner sosRunner = null;
// Figure out which native debugger to use
- NativeDebugger debugger = GetNativeDebuggerToUse(config, generateDump);
+ NativeDebugger debugger = GetNativeDebuggerToUse(config, options);
try
{
outputHelper.WriteLine("SOSRunner processing {0}", testName);
outputHelper.WriteLine("{");
- var variables = GenerateVariables(config, debuggeeConfig, generateDump);
+ var variables = GenerateVariables(config, debuggeeConfig, options);
var scriptLogger = new ScriptLogger(debugger, outputHelper.IndentedOutput);
- if (loadDump)
+ if (options == Options.LoadDump || options == Options.LoadDumpWithDotNetDump)
{
if (!variables.TryGetValue("%DUMP_NAME%", out string dumpName) || !File.Exists(dumpName))
{
- throw new Exception($"Dump file does not exist: {dumpName ?? ""}");
+ throw new FileNotFoundException($"Dump file does not exist: {dumpName ?? ""}");
}
}
string debuggerPath = GetNativeDebuggerPath(debugger, config);
if (string.IsNullOrWhiteSpace(debuggerPath) || !File.Exists(debuggerPath))
{
- throw new Exception($"Native debugger path not set or does not exist: {debuggerPath}");
+ throw new FileNotFoundException($"Native debugger path not set or does not exist: {debuggerPath}");
}
// Get the debugger arguments and commands to run initially
string helperExtension = config.CDBHelperExtension();
if (string.IsNullOrWhiteSpace(helperExtension) || !File.Exists(helperExtension))
{
- throw new Exception($"CDB helper script path not set or does not exist: {helperExtension}");
+ throw new ArgumentException($"CDB helper script path not set or does not exist: {helperExtension}");
}
arguments.AppendFormat(@"-c "".load {0}""", helperExtension);
- if (loadDump)
+ if (options == Options.LoadDump)
{
arguments.Append(" -z %DUMP_NAME%");
}
string lldbHelperScript = config.LLDBHelperScript();
if (string.IsNullOrWhiteSpace(lldbHelperScript) || !File.Exists(lldbHelperScript))
{
- throw new Exception("LLDB helper script path not set or does not exist: " + lldbHelperScript);
+ throw new ArgumentException("LLDB helper script path not set or does not exist: " + lldbHelperScript);
}
arguments.AppendFormat(@"--no-lldbinit -o ""settings set interpreter.prompt-on-quit false"" -o ""command script import {0}"" -o ""version""", lldbHelperScript);
// Load the dump or launch the debuggee process
- if (loadDump)
+ if (options == Options.LoadDump)
{
initialCommands.Add($@"target create --core ""%DUMP_NAME%"" ""{config.HostExe}""");
}
}
break;
case NativeDebugger.Gdb:
- if (loadDump)
+ if (options == Options.LoadDump || options == Options.LoadDumpWithDotNetDump)
{
- throw new Exception("GDB not meant for loading core dumps");
+ throw new ArgumentException("GDB not meant for loading core dumps");
}
arguments.AppendFormat("--args {0}", debuggeeCommandLine);
initialCommands.Add("set use-coredump-filter on");
initialCommands.Add("run");
break;
+
+ case NativeDebugger.DotNetDump:
+ if (options != Options.LoadDumpWithDotNetDump)
+ {
+ throw new ArgumentException($"{options} not supported for dotnet-dump testing");
+ }
+ if (string.IsNullOrWhiteSpace(config.HostExe))
+ {
+ throw new ArgumentException("No HostExe in configuration");
+ }
+ arguments.Append(debuggerPath);
+ arguments.Append(@" analyze %DUMP_NAME%");
+ debuggerPath = config.HostExe;
+ break;
}
// Create the native debugger process running
WithTimeout(TimeSpan.FromMinutes(10));
// Create the sos runner instance
- sosRunner = new SOSRunner(debugger, config, outputHelper, variables, scriptLogger, processRunner, loadDump);
+ sosRunner = new SOSRunner(debugger, config, outputHelper, variables, scriptLogger, processRunner, options == Options.LoadDump || options == Options.LoadDumpWithDotNetDump);
// Start the native debugger
processRunner.Start();
string scriptFile = Path.Combine(_config.ScriptRootDir, scriptRelativePath);
if (!File.Exists(scriptFile))
{
- throw new Exception("Script file does not exist: " + scriptFile);
+ throw new FileNotFoundException("Script file does not exist: " + scriptFile);
}
HashSet<string> enabledDefines = GetEnabledDefines();
LogProcessingReproInfo(scriptFile, enabledDefines);
string sosHostRuntime = _config.SOSHostRuntime();
string sosPath = _config.SOSPath();
List<string> commands = new List<string>();
+
switch (Debugger)
{
case NativeDebugger.Cdb:
{
commands.Add($"sos SetHostRuntime {sosHostRuntime}");
}
- if (_isDump)
- {
- // lldb doesn't load dump with the initial thread set to one with
- // the exception. This SOS command looks for a thread with a managed
- // exception and set the current thread to it.
- commands.Add("clrthreads -managedexception");
- }
+ SwitchToExceptionThread();
break;
case NativeDebugger.Gdb:
break;
+ case NativeDebugger.DotNetDump:
+ SwitchToExceptionThread();
+ break;
default:
throw new Exception($"{DebuggerToString} cannot load sos extension");
}
await RunCommands(commands);
+
+ // Helper function to switch to the thread with an exception
+ void SwitchToExceptionThread()
+ {
+ if (_isDump)
+ {
+ // lldb/dotnet-dump don't load dump with the initial thread set to one
+ // with the exception. This SOS command looks for a thread with a managed
+ // exception and set the current thread to it.
+ commands.Add("clrthreads -managedexception");
+ }
+ }
}
public async Task ContinueExecution()
case NativeDebugger.Gdb:
command = "continue";
break;
+ case NativeDebugger.DotNetDump:
+ break;
}
- if (!await RunCommand(command, addPrefix))
+ if (command != null)
{
- throw new Exception($"'{command}' FAILED");
+ if (!await RunCommand(command, addPrefix))
+ {
+ throw new Exception($"'{command}' FAILED");
+ }
}
}
case NativeDebugger.Lldb:
command = "sos " + command;
break;
+ case NativeDebugger.DotNetDump:
+ int index = command.IndexOf(' ');
+ if (index != -1) {
+ // lowercase just the command name not the rest of the command line
+ command = command.Substring(0, index).ToLowerInvariant() + command.Substring(index);
+ }
+ else {
+ // it is only the command name
+ command = command.ToLowerInvariant();
+ }
+ break;
default:
- throw new Exception(DebuggerToString + " cannot execute sos command");
+ throw new ArgumentException(DebuggerToString + " cannot execute sos command");
}
return await RunCommand(command);
}
{
if (string.IsNullOrWhiteSpace(command))
{
- throw new Exception("Debugger command empty or null");
+ throw new ArgumentException("Debugger command empty or null");
}
return await HandleCommand(command, addPrefix);
}
command = "q";
break;
case NativeDebugger.Lldb:
+ case NativeDebugger.DotNetDump:
command = "quit";
break;
}
}
}
- public static string GenerateDumpFileName(TestConfiguration config, string debuggeeName, bool generateDump)
+ public static string GenerateDumpFileName(TestConfiguration config, string debuggeeName, Options options)
{
- string dumpRoot = generateDump ? config.DebuggeeDumpOutputRootDir() : config.DebuggeeDumpInputRootDir();
+ string dumpRoot = options == Options.GenerateDump ? config.DebuggeeDumpOutputRootDir() : config.DebuggeeDumpInputRootDir();
if (dumpRoot != null)
{
return Path.Combine(dumpRoot, Path.GetFileNameWithoutExtension(debuggeeName) + ".dmp");
_outputHelper.Dispose();
}
- private static NativeDebugger GetNativeDebuggerToUse(TestConfiguration config, bool generateDump)
+ private static NativeDebugger GetNativeDebuggerToUse(TestConfiguration config, Options options)
{
switch (OS.Kind)
{
case OSKind.Linux:
case OSKind.OSX:
- return generateDump ? (config.GenerateDumpWithLLDB() ? NativeDebugger.Lldb : NativeDebugger.Gdb) : NativeDebugger.Lldb;
+ switch (options) {
+ case Options.GenerateDump:
+ return config.GenerateDumpWithLLDB() ? NativeDebugger.Lldb : NativeDebugger.Gdb;
+ case Options.LoadDumpWithDotNetDump:
+ return NativeDebugger.DotNetDump;
+ default:
+ return NativeDebugger.Lldb;
+ }
default:
throw new Exception(OS.Kind.ToString() + " not supported");
case NativeDebugger.Gdb:
return config.GDBPath();
+
+ case NativeDebugger.DotNetDump:
+ return config.DotNetDumpPath();
}
return null;
return true;
}
- private static Dictionary<string, string> GenerateVariables(TestConfiguration config, DebuggeeConfiguration debuggeeConfig, bool generateDump)
+ private static Dictionary<string, string> GenerateVariables(TestConfiguration config, DebuggeeConfiguration debuggeeConfig, Options options)
{
- Dictionary<string, string> vars = new Dictionary<string, string>();
+ var vars = new Dictionary<string, string>();
string debuggeeExe = debuggeeConfig.BinaryExePath;
- string dumpFileName = GenerateDumpFileName(config, Path.GetFileNameWithoutExtension(debuggeeExe), generateDump);
+ string dumpFileName = GenerateDumpFileName(config, Path.GetFileNameWithoutExtension(debuggeeExe), options);
vars.Add("%DEBUGGEE_EXE%", debuggeeExe);
if (dumpFileName != null)
{
case NativeDebugger.Cdb:
case NativeDebugger.Lldb:
+ case NativeDebugger.DotNetDump:
commandError = lastCommandOutput.EndsWith("<END_COMMAND_ERROR>");
commandEnd = commandError || lastCommandOutput.EndsWith("<END_COMMAND_OUTPUT>");
break;
return TestConfiguration.MakeCanonicalPath(gdbPath);
}
+ public static string DotNetDumpPath(this TestConfiguration config)
+ {
+ string dotnetDumpPath = config.GetValue("DotNetDumpPath");
+ return TestConfiguration.MakeCanonicalPath(dotnetDumpPath);
+ }
+
public static string SOSPath(this TestConfiguration config)
{
return TestConfiguration.MakeCanonicalPath(config.GetValue("SOSPath"));
# Verify that ClrStack with no options works
SOSCOMMAND:SetSymbolServer -ms
+!IFDEF:DOTNETDUMP
SOSCOMMAND:SetHostRuntime
+ENDIF:DOTNETDUMP
SOSCOMMAND:ClrStack
VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
VERIFY:\s+Child\s+SP\s+IP\s+Call Site\s+
VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.String\[\].*
ENDIF:PROJECTK
-# Verify DumpStack works
+!IFDEF:DOTNETDUMP
IFDEF:PROJECTK
+
+# Verify DumpStack works
SOSCOMMAND:DumpStack
VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+
VERIFY:(.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+(\+\s*0x<HEXVAL>\s+)?SymbolTestApp\.Program\.Foo4\(System\.String\)\),\s+calling.*\s+)|(.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+(\+\s*0x<HEXVAL>\s+)?SymbolTestApp\.Program\.Foo2\(Int32, System\.String\)\),\s+calling.*\s+)
-ENDIF:PROJECTK
# Verify DumpStack -EE works
-IFDEF:PROJECTK
SOSCOMMAND:DumpStack -EE
VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+
VERIFY:(.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+(\+\s*0x<HEXVAL>\s+)?SymbolTestApp\.Program\.Foo4\(System\.String\)\)\s+)|(.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+(\+\s*0x<HEXVAL>\s+)?SymbolTestApp\.Program\.Foo2\(Int32, System\.String\)\)\s+)
-ENDIF:PROJECTK
# Verify EEStack works
-IFDEF:PROJECTK
SOSCOMMAND:EEStack
VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+
VERIFY:(.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+(\+\s*0x<HEXVAL>\s+)?SymbolTestApp\.Program\.Foo4\(System\.String\)\),\s+calling.*\s+)|(.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+(\+\s*0x<HEXVAL>\s+)?SymbolTestApp\.Program\.Foo2\(Int32, System\.String\)\),\s+calling.*\s+)
+
ENDIF:PROJECTK
+ENDIF:DOTNETDUMP
# Verify that IP2MD works (uses IP from ClrStack)
SOSCOMMAND:ClrStack
VERIFY:.*\s+Method Name:\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+
VERIFY:.*\s+Source file:\s+(?i:.*[\\|/]SymbolTestApp\.cs) @ 54\s+
-# Verify that "u" works (depends on the IP2MD right above)
+!IFDEF:DOTNETDUMP
IFDEF:PROJECTK
+
+# Verify that "u" works (depends on the IP2MD right above)
SOSCOMMAND:u <POUT>\s*MethodDesc:\s+(<HEXVAL>)\s*<POUT>
VERIFY:\s*Normal JIT generated code\s+
VERIFY:\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+
VERIFY:\s+Begin\s+<HEXVAL>,\s+size\s+<HEXVAL>\s+
VERIFY:\s+(?i:.*[\\|/]SymbolTestApp\.cs) @ 54:\s+
-ENDIF:PROJECTK
# Verify that "u" with no line info works
-IFDEF:PROJECTK
SOSCOMMAND:u -n <PREVPOUT>
VERIFY:\s*Normal JIT generated code\s+
VERIFY:\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+
VERIFY:\s+Begin\s+<HEXVAL>,\s+size\s+<HEXVAL>\s+
-ENDIF:PROJECTK
# Verify that "u" with offsets info works
-IFDEF:PROJECTK
SOSCOMMAND:u -o <PREVPOUT>
VERIFY:\s*Normal JIT generated code\s+
VERIFY:\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+
VERIFY:\s+Begin\s+<HEXVAL>,\s+size\s+<HEXVAL>\s+
VERIFY:\s+(?i:.*[\\|/]SymbolTestApp\.cs) @ 54:\s+
+
ENDIF:PROJECTK
+ENDIF:DOTNETDUMP
# Verify that Name2EE works
IFDEF:PROJECTK
ENDIF:PROJECTK
# Verify that Threads (clrthreads) works
+IFDEF:DOTNETDUMP
+SOSCOMMAND:clrthreads
+ENDIF:DOTNETDUMP
+!IFDEF:DOTNETDUMP
SOSCOMMAND:Threads
+ENDIF:DOTNETDUMP
VERIFY:\s*ThreadCount:\s+<DECVAL>\s+
VERIFY:\s+UnstartedThread:\s+<DECVAL>\s+
VERIFY:\s+BackgroundThread:\s+<DECVAL>\s+
# 2) Verifying that ClrStack with managed/native mixed works
IFDEF:PROJECTK
SOSCOMMAND:SetSymbolServer -ms -loadsymbols
+!IFDEF:DOTNETDUMP
SOSCOMMAND:SetHostRuntime
+ENDIF:DOTNETDUMP
SOSCOMMAND:ClrStack -f
VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
VERIFY:\s+Child\s+SP\s+IP\s+Call Site\s+
VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.String.*
ENDIF:PROJECTK
+!IFDEF:DOTNETDUMP
+
# 9) Verify DumpStack works
SOSCOMMAND:DumpStack
VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
SOSCOMMAND:EEStack
VERIFY:.*Child(-SP|EBP)\s+RetAddr\s+Caller, Callee\s+
VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+\(MethodDesc\s+<HEXVAL>\s+\+\s*0x<HEXVAL>\s+NestedExceptionTest\.Program\.Main\(System\.String\[\]\)\),\s+calling.*
+
+ENDIF:DOTNETDUMP
help.
\\
+COMMAND: gcwhere.
+GCWhere <object address>
+
+!GCWhere displays the location in the GC heap of the argument passed in.
+
+ 0:002> !GCWhere 02800038
+ Address Gen Heap segment begin allocated size
+ 02800038 2 0 02800000 02800038 0282b740 12
+
+When the argument lies in the managed heap, but is not a valid *object* address
+the "size" is displayed as 0:
+
+ 0:002> !GCWhere 0280003c
+ Address Gen Heap segment begin allocated size
+ 0280003c 2 0 02800000 02800038 0282b740 0
+\\
+
COMMAND: dumplog.
DumpLog [-addr <addressOfStressLog>] [<Filename>]
interpreter.AddCommand("eestack", new sosCommand("EEStack"), "Runs dumpstack on all threads in the process.");
interpreter.AddCommand("finalizequeue", new sosCommand("FinalizeQueue"), "Displays all objects registered for finalization.");
interpreter.AddCommand("gcroot", new sosCommand("GCRoot"), "Displays info about references (or roots) to an object at the specified address.");
+ interpreter.AddCommand("gcwhere", new sosCommand("GCWhere"), "Displays the location in the GC heap of the argument passed in.");
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.");
[Command(Name = "dumpmodule", AliasExpansion = "DumpModule", Help = "Displays information about a EE module structure at the specified address.")]
[Command(Name = "dumpmt", AliasExpansion = "DumpMT", Help = "Displays information about a method table at the specified address.")]
[Command(Name = "dumpobj", AliasExpansion = "DumpObj", Help = "Displays info about an object at the specified address.")]
- [Command(Name = "dumpstack", AliasExpansion = "DumpStack", Help = "Displays a native and managed stack trace.")]
- [Command(Name = "dso", AliasExpansion = "DumpStackObjects", Help = "Displays all managed objects found within the bounds of the current stack.")]
+ [Command(Name = "dumpstackobjects", AliasExpansion = "DumpStackObjects", Help = "Displays all managed objects found within the bounds of the current stack.")]
+ [Command(Name = "dso")]
[Command(Name = "eeheap", AliasExpansion = "EEHeap", Help = "Displays info about process memory consumed by internal runtime data structures.")]
- [Command(Name = "eestack", AliasExpansion = "EEStack", Help = "Runs dumpstack on all threads in the process.")]
[Command(Name = "finalizequeue", AliasExpansion = "FinalizeQueue", Help = "Displays all objects registered for finalization.")]
[Command(Name = "gcroot", AliasExpansion = "GCRoot", Help = "Displays info about references (or roots) to an object at the specified address.")]
+ [Command(Name = "gcwhere", AliasExpansion = "GCWhere", Help = "Displays the location in the GC heap of the argument passed in.")]
[Command(Name = "ip2md", AliasExpansion = "IP2MD", Help = "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.")]
[Command(Name = "name2ee", AliasExpansion = "Name2EE", Help = "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.")]
- [Command(Name = "pe", AliasExpansion = "PrintException", Help = "Displays and formats fields of any object derived from the Exception class at the specified address.")]
+ [Command(Name = "printexception", AliasExpansion = "PrintException", Help = "Displays and formats fields of any object derived from the Exception class at the specified address.")]
+ [Command(Name = "pe")]
[Command(Name = "syncblk", AliasExpansion = "SyncBlk", Help = "Displays the SyncBlock holder info.")]
[Command(Name = "histclear", AliasExpansion = "HistClear", Help = "Releases any resources used by the family of Hist commands.")]
[Command(Name = "histinit", AliasExpansion = "HistInit", Help = "Initializes the SOS structures from the stress log saved in the debuggee.")]