Index: test/tools/lldb-mi/TestMiGdbSetShow.py =================================================================== --- /dev/null +++ test/tools/lldb-mi/TestMiGdbSetShow.py @@ -0,0 +1,121 @@ +""" +Test lldb-mi -gdb-set and -gdb-show commands. +""" + +import lldbmi_testcase +from lldbtest import * +import unittest2 + +class MiGdbSetShowTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_lldbmi_gdb_set_target_async_default(self): + """Test that 'lldb-mi --interpreter' switches to async mode by default.""" + + self.spawnLldbMi(args = None) + + # Switch to sync mode + self.runCmd("-gdb-set target-async off") + self.expect("\^done") + self.runCmd("-gdb-show target-async") + self.expect("\^done,value=\"off\"") + + # Test that -gdb-set switches to async by default + self.runCmd("-gdb-set target-async") + self.expect("\^done") + self.runCmd("-gdb-show target-async") + self.expect("\^done,value=\"on\"") + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_lldbmi_gdb_set_target_async_on(self): + """Test that 'lldb-mi --interpreter' can execute commands in async mode.""" + + self.spawnLldbMi(args = None) + + # Switch to sync mode + self.runCmd("-gdb-set target-async off") + self.expect("\^done") + self.runCmd("-gdb-show target-async") + self.expect("\^done,value=\"off\"") + + # Test that -gdb-set can switch to async mode + self.runCmd("-gdb-set target-async on") + self.expect("\^done") + self.runCmd("-gdb-show target-async") + self.expect("\^done,value=\"on\"") + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Test that program is executed in async mode + self.runCmd("-exec-run") + self.expect("\*running") + self.expect("~\"argc=1") + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_lldbmi_gdb_set_target_async_off(self): + """Test that 'lldb-mi --interpreter' can execute commands in sync mode.""" + + self.spawnLldbMi(args = None) + + # Test that -gdb-set can switch to sync mode + self.runCmd("-gdb-set target-async off") + self.expect("\^done") + self.runCmd("-gdb-show target-async") + self.expect("\^done,value=\"off\"") + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Test that program is executed in async mode + self.runCmd("-exec-run") + unexpected = [ "\*running" ] # "\*running" is async notification + it = self.expect(unexpected + [ "~\"argc=1\\\\r\\\\n" ]) + if it < len(unexpected): + # generate error if it's not "~\"argc=1\\\\r\\\\n" + self.expect("$UNEXPECTED FOUND: %s.^" % unexpected[it], timeout = 0) + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_lldbmi_gdb_show_target_async(self): + """Test that 'lldb-mi --interpreter' in async mode by default.""" + + self.spawnLldbMi(args = None) + + # Test that default target-async value is "on" + self.runCmd("-gdb-show target-async") + self.expect("\^done,value=\"on\"") + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @unittest2.expectedFailure("-gdb-set ignores unknown properties") + def test_lldbmi_gdb_set_unknown(self): + """Test that 'lldb-mi --interpreter' fails when setting an unknown property.""" + + self.spawnLldbMi(args = None) + + # Test that -gdb-set fails if property is unknown + self.runCmd("-gdb-set unknown some_value") + self.expect("\^error") + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @unittest2.expectedFailure("-gdb-show ignores unknown properties") + def test_lldbmi_gdb_show_unknown(self): + """Test that 'lldb-mi --interpreter' fails when showing an unknown property.""" + + self.spawnLldbMi(args = None) + + # Test that -gdb-show fails if property is unknown + self.runCmd("-gdb-show unknown") + self.expect("\^error") + +if __name__ == '__main__': + unittest2.main() Index: tools/lldb-mi/CMakeLists.txt =================================================================== --- tools/lldb-mi/CMakeLists.txt +++ tools/lldb-mi/CMakeLists.txt @@ -23,6 +23,7 @@ MICmdCmdFile.cpp MICmdCmdGdbInfo.cpp MICmdCmdGdbSet.cpp + MICmdCmdGdbShow.cpp MICmdCmdGdbThread.cpp MICmdCmdMiscellanous.cpp MICmdCmdStack.cpp Index: tools/lldb-mi/MICmdCmdGdbSet.cpp =================================================================== --- tools/lldb-mi/MICmdCmdGdbSet.cpp +++ tools/lldb-mi/MICmdCmdGdbSet.cpp @@ -93,7 +93,8 @@ CMICMDBASE_GETOPTION(pArgGdbOption, ListOfN, m_constStrArgNamedGdbOption); const CMICmdArgValListBase::VecArgObjPtr_t &rVecWords(pArgGdbOption->GetExpectedOptions()); - // Get the gdb-set option to carry out + // Get the gdb-set option to carry out. This option will be used as an action + // which should be done. Further arguments will be used as parameters for it. CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = rVecWords.begin(); const CMICmdArgValString *pOption = static_cast(*it); const CMIUtilString strOption(pOption->GetValue()); Index: tools/lldb-mi/MICmdCmdGdbShow.h =================================================================== --- /dev/null +++ tools/lldb-mi/MICmdCmdGdbShow.h @@ -0,0 +1,84 @@ +//===-- MICmdCmdGdbShow.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdGdbShow interface. +// +// To implement new MI commands, derive a new command class from the command base +// class. To enable the new command for interpretation add the new command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" + +//++ ============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "gdb-show". +// This command does not follow the MI documentation exactly. While *this +// command is implemented it does not do anything with the gdb-set +// variable past in. +// The design of matching the info request to a request action (or +// command) is very simple. The request function which carries out +// the task of information gathering and printing to stdout is part of +// *this class. Should the request function become more complicated then +// that request should really reside in a command type class. Then this +// class instantiates a request info command for a matching request. The +// design/code of *this class then does not then become bloated. Use a +// lightweight version of the current MI command system. +//-- +class CMICmdCmdGdbShow : public CMICmdBase +{ + // Statics: + public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(void); + + // Methods: + public: + /* ctor */ CMICmdCmdGdbShow(void); + + // Overridden: + public: + // From CMICmdInvoker::ICmd + virtual bool Execute(void); + virtual bool Acknowledge(void); + virtual bool ParseArgs(void); + // From CMICmnBase + /* dtor */ virtual ~CMICmdCmdGdbShow(void); + + // Typedefs: + private: + typedef bool (CMICmdCmdGdbShow::*FnGdbOptionPtr)(const CMIUtilString::VecString_t &vrWords); + typedef std::map MapGdbOptionNameToFnGdbOptionPtr_t; + + // Methods: + private: + bool GetOptionFn(const CMIUtilString &vrGdbOptionName, FnGdbOptionPtr &vrwpFn) const; + bool OptionFnTargetAsync(const CMIUtilString::VecString_t &vrWords); + bool OptionFnFallback(const CMIUtilString::VecString_t &vrWords); + + // Attributes: + private: + const static MapGdbOptionNameToFnGdbOptionPtr_t ms_mapGdbOptionNameToFnGdbOptionPtr; + // + const CMIUtilString m_constStrArgNamedThreadGrp; + const CMIUtilString m_constStrArgNamedGdbOption; + bool m_bGdbOptionRecognised; // True = This command has a function with a name that matches the Print argument, false = not found + bool m_bGdbOptionFnSuccessful; // True = The print function completed its task ok, false = function failed for some reason + bool m_bGbbOptionFnHasError; // True = The option function has an error condition (not the command!), false = option function ok. + CMIUtilString m_strGdbOptionName; + CMIUtilString m_strGdbOptionFnError; + CMIUtilString m_strValue; +}; Index: tools/lldb-mi/MICmdCmdGdbShow.cpp =================================================================== --- /dev/null +++ tools/lldb-mi/MICmdCmdGdbShow.cpp @@ -0,0 +1,260 @@ +//===-- MICmdCmdGdbShow.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdGdbShow implementation. + +// In-house headers: +#include "MICmdCmdGdbShow.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" +#include "MICmdArgValString.h" +#include "MICmdArgValListOfN.h" +#include "MICmdArgValOptionLong.h" +#include "MICmnLLDBDebugSessionInfo.h" + +// Instantiations: +const CMICmdCmdGdbShow::MapGdbOptionNameToFnGdbOptionPtr_t CMICmdCmdGdbShow::ms_mapGdbOptionNameToFnGdbOptionPtr = { + {"target-async", &CMICmdCmdGdbShow::OptionFnTargetAsync}, + {"fallback", &CMICmdCmdGdbShow::OptionFnFallback}}; + +//++ ------------------------------------------------------------------------------------ +// Details: CMICmdCmdGdbShow constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdGdbShow::CMICmdCmdGdbShow(void) + : m_constStrArgNamedThreadGrp("thread-group") + , m_constStrArgNamedGdbOption("option") + , m_bGdbOptionRecognised(true) + , m_bGdbOptionFnSuccessful(false) + , m_bGbbOptionFnHasError(false) + , m_strGdbOptionFnError(MIRSRC(IDS_WORD_ERR_MSG_NOT_IMPLEMENTED_BRKTS)) +{ + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "gdb-show"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdGdbShow::CreateSelf; +} + +//++ ------------------------------------------------------------------------------------ +// Details: CMICmdCmdGdbShow destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdGdbShow::~CMICmdCmdGdbShow(void) +{ +} + +//++ ------------------------------------------------------------------------------------ +// Details: The invoker requires this function. The parses the command line options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool +CMICmdCmdGdbShow::ParseArgs(void) +{ + bool bOk = m_setCmdArgs.Add( + *(new CMICmdArgValOptionLong(m_constStrArgNamedThreadGrp, false, false, CMICmdArgValListBase::eArgValType_ThreadGrp, 1))); + bOk = bOk && + m_setCmdArgs.Add( + *(new CMICmdArgValListOfN(m_constStrArgNamedGdbOption, true, true, CMICmdArgValListBase::eArgValType_StringAnything))); + return (bOk && ParseValidateCmdOptions()); +} + +//++ ------------------------------------------------------------------------------------ +// Details: The invoker requires this function. The command is executed in this function. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool +CMICmdCmdGdbShow::Execute(void) +{ + CMICMDBASE_GETOPTION(pArgGdbOption, ListOfN, m_constStrArgNamedGdbOption); + const CMICmdArgValListBase::VecArgObjPtr_t &rVecWords(pArgGdbOption->GetExpectedOptions()); + + // Get the gdb-show option to carry out. This option will be used as an action + // which should be done. Further arguments will be used as parameters for it. + CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = rVecWords.begin(); + const CMICmdArgValString *pOption = static_cast(*it); + const CMIUtilString strOption(pOption->GetValue()); + ++it; + + // Retrieve the parameter(s) for the option + CMIUtilString::VecString_t vecWords; + while (it != rVecWords.end()) + { + const CMICmdArgValString *pWord = static_cast(*it); + vecWords.push_back(pWord->GetValue()); + + // Next + ++it; + } + + FnGdbOptionPtr pPrintRequestFn = nullptr; + if (!GetOptionFn(strOption, pPrintRequestFn)) + { + // For unimplemented option handlers, fallback to a generic handler + // ToDo: Remove this when ALL options have been implemented + if (!GetOptionFn("fallback", pPrintRequestFn)) + { + m_bGdbOptionRecognised = false; + m_strGdbOptionName = "fallback"; // This would be the strOption name + return MIstatus::success; + } + } + + m_bGdbOptionFnSuccessful = (this->*(pPrintRequestFn))(vecWords); + if (!m_bGdbOptionFnSuccessful && !m_bGbbOptionFnHasError) + return MIstatus::failure; + + return MIstatus::success; +} + +//++ ------------------------------------------------------------------------------------ +// Details: The invoker requires this function. The command prepares a MI Record Result +// for the work carried out in the Execute() method. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool +CMICmdCmdGdbShow::Acknowledge(void) +{ + // Print error if option isn't recognized: + // ^error,msg="The request '%s' was not recognized, not implemented" + if (!m_bGdbOptionRecognised) + { + const CMICmnMIValueConst miValueConst( + CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INFO_PRINTFN_NOT_FOUND), m_strGdbOptionName.c_str())); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + // ^done,value="%s" + if (m_bGdbOptionFnSuccessful && !m_strValue.empty()) + { + const CMICmnMIValueConst miValueConst(m_strValue); + const CMICmnMIValueResult miValueResult("value", miValueConst); + const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + else if (m_bGdbOptionFnSuccessful) + { + // Ignore empty value (for fallback) + const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + // Print error if request failed: + // ^error,msg="The request '%s' failed. + const CMICmnMIValueConst miValueConst(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INFO_PRINTFN_FAILED), m_strGdbOptionFnError.c_str())); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Required by the CMICmdFactory when registering *this command. The factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase * +CMICmdCmdGdbShow::CreateSelf(void) +{ + return new CMICmdCmdGdbShow(); +} + +//++ ------------------------------------------------------------------------------------ +// Details: Retrieve the print function's pointer for the matching print request. +// Type: Method. +// Args: vrPrintFnName - (R) The info requested. +// vrwpFn - (W) The print function's pointer of the function to carry out +// Return: bool - True = Print request is implemented, false = not found. +// Throws: None. +//-- +bool +CMICmdCmdGdbShow::GetOptionFn(const CMIUtilString &vrPrintFnName, FnGdbOptionPtr &vrwpFn) const +{ + vrwpFn = nullptr; + + const MapGdbOptionNameToFnGdbOptionPtr_t::const_iterator it = ms_mapGdbOptionNameToFnGdbOptionPtr.find(vrPrintFnName); + if (it != ms_mapGdbOptionNameToFnGdbOptionPtr.end()) + { + vrwpFn = (*it).second; + return true; + } + + return false; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Carry out work to complete the GDB show option 'target-async' to prepare +// and send back the requested information. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool +CMICmdCmdGdbShow::OptionFnTargetAsync(const CMIUtilString::VecString_t &vrWords) +{ + MIunused(vrWords); + + // Get async mode + CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); + const bool bAsyncMode = rSessionInfo.GetDebugger().GetAsync(); + + m_strValue = bAsyncMode ? "on" : "off"; + return MIstatus::success; +} + +//++ ------------------------------------------------------------------------------------ +// Details: Carry out work to complete the GDB show option to prepare and send back the +// requested information. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool +CMICmdCmdGdbShow::OptionFnFallback(const CMIUtilString::VecString_t &vrWords) +{ + MIunused(vrWords); + + // Do nothing - intentional. This is a fallback function to do nothing. + // This allows the search for gdb-show options to always succeed when the option is not + // found (implemented). + + return MIstatus::success; +} Index: tools/lldb-mi/MICmdCommands.cpp =================================================================== --- tools/lldb-mi/MICmdCommands.cpp +++ tools/lldb-mi/MICmdCommands.cpp @@ -27,6 +27,7 @@ #include "MICmdCmdFile.h" #include "MICmdCmdGdbInfo.h" #include "MICmdCmdGdbSet.h" +#include "MICmdCmdGdbShow.h" #include "MICmdCmdGdbThread.h" #include "MICmdCmdMiscellanous.h" #include "MICmdCmdStack.h" @@ -103,6 +104,7 @@ bOk &= Register(); bOk &= Register(); bOk &= Register(); + bOk &= Register(); bOk &= Register(); bOk &= Register(); bOk &= Register();