From: Mike McLaughlin Date: Mon, 24 Sep 2018 17:49:44 +0000 (-0700) Subject: Add cdb extensions that prints a reliable prompt. (#75) X-Git-Tag: submit/tizen/20190813.035844~77 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ff051b0a792a83eee888e006c34f04bffec10140;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git Add cdb extensions that prints a reliable prompt. (#75) To fix issue #59. --- diff --git a/src/SOS/CMakeLists.txt b/src/SOS/CMakeLists.txt index 1530948cf..4d567398e 100644 --- a/src/SOS/CMakeLists.txt +++ b/src/SOS/CMakeLists.txt @@ -9,6 +9,8 @@ endif(CLR_CMAKE_PLATFORM_UNIX) if(WIN32) add_compile_options(/FIWarningControl.h) # force include of WarningControl.h add_compile_options(/Zl) # omit default library name in .OBJ + + add_subdirectory(runcommand) endif(WIN32) add_definitions(-D_SECURE_SCL=0) diff --git a/src/SOS/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt b/src/SOS/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt index ea70a5bf2..c73a0dabf 100644 --- a/src/SOS/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt +++ b/src/SOS/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt @@ -18,6 +18,7 @@ $(RootBinDir)\$(TargetConfiguration)\TestResults\sos.unittests_$(Timestamp) $(RootBinDir)\$(TargetConfiguration)\tmp\dumps $(NuGetPackageCacheDir)\cdb-sos\1.1.0\runtimes\win-$(TargetArchitecture)\native\cdb.exe + $(InstallDir)\runcommand.dll $(RepoRootDir)\src\SOS\SOS.UnitTests\Debuggees $(RootBinDir)\Debuggees diff --git a/src/SOS/SOS.UnitTests/SOSRunner.cs b/src/SOS/SOS.UnitTests/SOSRunner.cs index 8374e0ed4..fc51b5bca 100644 --- a/src/SOS/SOS.UnitTests/SOSRunner.cs +++ b/src/SOS/SOS.UnitTests/SOSRunner.cs @@ -238,25 +238,33 @@ public class SOSRunner : IDisposable // Get the debugger arguments and commands to run initially List initialCommands = new List(); - string arguments = null; + var arguments = new StringBuilder(); switch (debugger) { case NativeDebugger.Cdb: - initialCommands.Add(".sympath %DEBUG_ROOT%"); - initialCommands.Add(".extpath " + Path.GetDirectoryName(config.SOSPath())); + 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}"); + } + arguments.AppendFormat(@"-c "".load {0}""", helperExtension); + if (loadDump) { - arguments = "-z %DUMP_NAME%"; + arguments.Append(" -z %DUMP_NAME%"); } else { - arguments = "-Gsins " + debuggeeCommandLine; + arguments.AppendFormat(" -Gsins {0}", debuggeeCommandLine); // disable stopping on integer divide-by-zero and integer overflow exceptions initialCommands.Add("sxd dz"); initialCommands.Add("sxd iov"); } + initialCommands.Add(".sympath %DEBUG_ROOT%"); + initialCommands.Add(".extpath " + Path.GetDirectoryName(config.SOSPath())); + // Add the path to runtime so cdb/sos can find mscordbi. string runtimeSymbolsPath = config.RuntimeSymbolsPath; if (runtimeSymbolsPath != null) @@ -274,7 +282,7 @@ public class SOSRunner : IDisposable { throw new Exception("LLDB helper script path not set or does not exist: " + lldbHelperScript); } - arguments = string.Format(@"--no-lldbinit -o ""settings set interpreter.prompt-on-quit false"" -o ""command script import {0}"" -o ""version""", 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) @@ -323,7 +331,7 @@ public class SOSRunner : IDisposable { throw new Exception("GDB not meant for loading core dumps"); } - arguments = "--args " + debuggeeCommandLine; + arguments.AppendFormat("--args {0}", debuggeeCommandLine); // .NET Core 1.1 or less don't catch stack overflow and abort so need to catch SIGSEGV if (config.StackOverflowSIGSEGV) @@ -343,7 +351,7 @@ public class SOSRunner : IDisposable } // Create the native debugger process running - ProcessRunner processRunner = new ProcessRunner(debuggerPath, ReplaceVariables(variables, arguments)). + ProcessRunner processRunner = new ProcessRunner(debuggerPath, ReplaceVariables(variables, arguments.ToString())). WithLog(scriptLogger). WithTimeout(TimeSpan.FromMinutes(10)); @@ -492,7 +500,8 @@ public class SOSRunner : IDisposable { case NativeDebugger.Cdb: commands.Add($".load {sosPath}"); - commands.Add(".lines; .reload"); + commands.Add(".lines"); + commands.Add(".reload"); if (sosHostRuntime != null) { commands.Add($"!SetHostRuntime {sosHostRuntime}"); @@ -523,10 +532,14 @@ public class SOSRunner : IDisposable public async Task ContinueExecution() { string command = null; + bool addPrefix = true; switch (Debugger) { case NativeDebugger.Cdb: command = "g"; + // Don't add the !runcommand prefix because it gets printed when cdb stops + // again because the helper extension used .pcmd to set a stop command. + addPrefix = false; break; case NativeDebugger.Lldb: command = "process continue"; @@ -535,7 +548,7 @@ public class SOSRunner : IDisposable command = "continue"; break; } - if (!await RunCommand(command)) + if (!await RunCommand(command, addPrefix)) { throw new Exception($"'{command}' FAILED"); } @@ -568,13 +581,13 @@ public class SOSRunner : IDisposable } } - public async Task RunCommand(string command) + public async Task RunCommand(string command, bool addPrefix = true) { if (string.IsNullOrWhiteSpace(command)) { throw new Exception("Debugger command empty or null"); } - return await HandleCommand(command); + return await HandleCommand(command, addPrefix); } public async Task QuitDebugger() @@ -680,7 +693,7 @@ public class SOSRunner : IDisposable return null; } - private async Task HandleCommand(string input) + private async Task HandleCommand(string input, bool addPrefix) { if (!await _scriptLogger.WaitForCommandPrompt()) { @@ -736,7 +749,12 @@ public class SOSRunner : IDisposable input = input.Substring(0, firstPOUT) + poutMatchResult + input.Substring(secondPOUT + poutTag.Length); } } - _processRunner.StandardInputWriteLine(_scriptLogger.ProcessCommand(ReplaceVariables(input))); + string command = ReplaceVariables(input); + if (addPrefix) + { + command = _scriptLogger.ProcessCommand(command); + } + _processRunner.StandardInputWriteLine(command); ScriptLogger.CommandResult result = await _scriptLogger.WaitForCommandOutput(); _lastCommandOutput = result.CommandOutput; @@ -905,9 +923,15 @@ public class SOSRunner : IDisposable public string ProcessCommand(string command) { - if (_debugger == NativeDebugger.Lldb) + switch (_debugger) { - command = string.Format("runcommand {0}", command); + case NativeDebugger.Cdb: + command = string.Format("!runcommand {0}", command); + break; + + case NativeDebugger.Lldb: + command = string.Format("runcommand {0}", command); + break; } return command; } @@ -928,15 +952,6 @@ public class SOSRunner : IDisposable switch (_debugger) { case NativeDebugger.Cdb: - // Some commands like DumpStack have ===> or -> in the output that looks - // like the cdb prompt. Using a regex here to better match the cdb prompt - // is way to slow. - if (lastCommandOutput.EndsWith("=> ") || lastCommandOutput.EndsWith("-> ")) - { - return; - } - commandEnd = lastCommandOutput.EndsWith("> "); - break; case NativeDebugger.Lldb: commandError = lastCommandOutput.EndsWith(""); commandEnd = commandError || lastCommandOutput.EndsWith(""); @@ -991,6 +1006,11 @@ public static class TestConfigurationExtensions return TestConfiguration.MakeCanonicalExePath(config.GetValue("CDBPath")); } + public static string CDBHelperExtension(this TestConfiguration config) + { + return TestConfiguration.MakeCanonicalPath(config.GetValue("CDBHelperExtension")); + } + public static string LLDBHelperScript(this TestConfiguration config) { return TestConfiguration.MakeCanonicalPath(config.GetValue("LLDBHelperScript")); diff --git a/src/SOS/runcommand/CMakeLists.txt b/src/SOS/runcommand/CMakeLists.txt new file mode 100644 index 000000000..64a82ebe5 --- /dev/null +++ b/src/SOS/runcommand/CMakeLists.txt @@ -0,0 +1,39 @@ +project(runcommand) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +include_directories(inc) +include_directories("$ENV{VSInstallDir}/DIA SDK/include") + +add_definitions(-DUSE_STL) + +#use static crt +add_definitions(-MT) + +set(RUNCOMMAND_SOURCES + runcommand.cpp +) + +set(RUNCOMMAND_LIBRARY + ${STATIC_MT_CRT_LIB} + ${STATIC_MT_CPP_LIB} + ${STATIC_MT_VCRT_LIB} + kernel32.lib + user32.lib + ole32.lib + oleaut32.lib + dbghelp.lib + uuid.lib + version.lib + dbgeng.lib + advapi32.lib + psapi.lib + ntdll.lib +) + +add_library_clr(runcommand SHARED ${RUNCOMMAND_SOURCES}) + +target_link_libraries(runcommand ${RUNCOMMAND_LIBRARY}) + +# add the install targets +install_clr(runcommand) diff --git a/src/SOS/runcommand/runcommand.cpp b/src/SOS/runcommand/runcommand.cpp new file mode 100644 index 000000000..fd46d6706 --- /dev/null +++ b/src/SOS/runcommand/runcommand.cpp @@ -0,0 +1,109 @@ +//-------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: contains an entry points required by WinDbg +// +//-------------------------------------------------------------------- + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include + +// Windows Header Files +#include +#include + +// +// Define KDEXT_64BIT to make all wdbgexts APIs recognize 64 bit addresses +// It is recommended for extensions to use 64 bit headers from wdbgexts so +// the extensions could support 64 bit targets. +// +#define KDEXT_64BIT +#include + +#include +#include +#include + +#define DBGEXT_DEF extern "C" __declspec(dllexport) HRESULT __cdecl + +PDEBUG_CLIENT g_DebugClient = NULL; +PDEBUG_CONTROL4 g_DebugControl = NULL; + +EXTERN_C __declspec(dllexport) void __cdecl DebugExtensionUninitialize(void); +extern void __cdecl dprintf(PCSTR Format, ...); + +// DbgEng requires all extensions to implement this function. +EXTERN_C __declspec(dllexport) HRESULT __cdecl +DebugExtensionInitialize( + PULONG version, + PULONG flags) +{ + HRESULT hr; + + *version = DEBUG_EXTENSION_VERSION(1, 0); + *flags = 0; + + g_DebugClient = NULL; + g_DebugControl = NULL; + + hr = DebugCreate(__uuidof(IDebugClient), (void **)&g_DebugClient); + if (FAILED(hr)) { + goto exit; + } + hr = g_DebugClient->QueryInterface(__uuidof(IDebugControl4), (void **)&g_DebugControl); + if (FAILED(hr)) { + goto exit; + } + hr = g_DebugControl->Execute(DEBUG_OUTCTL_IGNORE, ".pcmd -s \".echo \"", 0); + if (FAILED(hr)) { + goto exit; + } + dprintf("\n"); +exit: + if (FAILED(hr)) { + dprintf("\n"); + DebugExtensionUninitialize(); + } + return hr; +} + +// WinDbg requires all extensions to implement this function. +EXTERN_C __declspec(dllexport) void __cdecl +DebugExtensionUninitialize(void) +{ + if (g_DebugControl != NULL) { + g_DebugControl->Release(); + g_DebugControl = NULL; + } + if (g_DebugClient != NULL) { + g_DebugClient->Release(); + g_DebugClient = NULL; + } +} + +DBGEXT_DEF runcommand(__in PDEBUG_CLIENT4 client, __in PCSTR args) +{ + HRESULT hr = g_DebugControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS, args, 0); + if (hr == S_OK) { + dprintf("\n"); + } + else { + dprintf("\n"); + } + return hr; +} + +void __cdecl +dprintf(PCSTR Format, ...) +{ + va_list Args; + + va_start(Args, Format); + g_DebugControl->OutputVaList(DEBUG_OUTPUT_ERROR, Format, Args); + va_end(Args); +}