Index: lldb/trunk/test/tools/lldb-mi/target/Makefile =================================================================== --- lldb/trunk/test/tools/lldb-mi/target/Makefile +++ lldb/trunk/test/tools/lldb-mi/target/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := test_attach.cpp + +include $(LEVEL)/Makefile.rules Index: lldb/trunk/test/tools/lldb-mi/target/TestMiTarget.py =================================================================== --- lldb/trunk/test/tools/lldb-mi/target/TestMiTarget.py +++ lldb/trunk/test/tools/lldb-mi/target/TestMiTarget.py @@ -0,0 +1,125 @@ +""" +Test lldb-mi -target-xxx commands. +""" + +import lldbmi_testcase +from lldbtest import * +import unittest2 + +class MiTargetTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # cannot attach to process on linux + def test_lldbmi_target_attach_wait_for(self): + """Test that 'lldb-mi --interpreter' works for -target-attach -n --waitfor.""" + + # Build target executable with unique name + exeName = self.testMethodName + d = {'EXE': exeName} + self.buildProgram("test_attach.cpp", exeName) + self.addTearDownCleanup(dictionary=d) + + self.spawnLldbMi(args = None) + + # Load executable + # FIXME: -file-exec-and-sybmols is not required for target attach, but the test will not pass without this + self.runCmd("-file-exec-and-symbols %s" % exeName) + self.expect("\^done") + + # Set up attach + self.runCmd("-target-attach -n %s --waitfor" % exeName) + time.sleep(4) # Give attach time to setup + + # Start target process + self.spawnSubprocess(os.path.join(os.path.dirname(__file__), exeName)); + self.addTearDownHook(self.cleanupSubprocesses) + self.expect("\^done") + + # Set breakpoint on printf + line = line_number('test_attach.cpp', '// BP_i++') + self.runCmd("-break-insert -f test_attach.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + + # Continue to breakpoint + self.runCmd("-exec-continue") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Detach + self.runCmd("-target-detach") + self.expect("\^done") + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # cannot attach to process on linux + def test_lldbmi_target_attach_name(self): + """Test that 'lldb-mi --interpreter' works for -target-attach -n .""" + + # Build target executable with unique name + exeName = self.testMethodName + d = {'EXE': exeName} + self.buildProgram("test_attach.cpp", exeName) + self.addTearDownCleanup(dictionary=d) + + # Start target process + targetProcess = self.spawnSubprocess(os.path.join(os.path.dirname(__file__), exeName)); + self.addTearDownHook(self.cleanupSubprocesses) + + self.spawnLldbMi(args = None) + + # Set up atatch + self.runCmd("-target-attach -n %s" % exeName) + self.expect("\^done") + + # Set breakpoint on printf + line = line_number('test_attach.cpp', '// BP_i++') + self.runCmd("-break-insert -f test_attach.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + + # Continue to breakpoint + self.runCmd("-exec-continue") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Detach + self.runCmd("-target-detach") + self.expect("\^done") + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # cannot attach to process on linux + def test_lldbmi_target_attach_pid(self): + """Test that 'lldb-mi --interpreter' works for -target-attach .""" + + # Build target executable with unique name + exeName = self.testMethodName + d = {'EXE': exeName} + self.buildProgram("test_attach.cpp", exeName) + self.addTearDownCleanup(dictionary=d) + + # Start target process + targetProcess = self.spawnSubprocess(os.path.join(os.path.dirname(__file__), exeName)); + self.addTearDownHook(self.cleanupSubprocesses) + + self.spawnLldbMi(args = None) + + # Set up atatch + self.runCmd("-target-attach %d" % targetProcess.pid) + self.expect("\^done") + + # Set breakpoint on printf + line = line_number('test_attach.cpp', '// BP_i++') + self.runCmd("-break-insert -f test_attach.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + + # Continue to breakpoint + self.runCmd("-exec-continue") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Detach + self.runCmd("-target-detach") + self.expect("\^done") Index: lldb/trunk/test/tools/lldb-mi/target/test_attach.cpp =================================================================== --- lldb/trunk/test/tools/lldb-mi/target/test_attach.cpp +++ lldb/trunk/test/tools/lldb-mi/target/test_attach.cpp @@ -0,0 +1,21 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +int +main(int argc, char const *argv[]) +{ + int i = 0; + for (;;) + { + i++; // BP_i++ + } + return 0; +} Index: lldb/trunk/tools/lldb-mi/MICmdCmdTarget.h =================================================================== --- lldb/trunk/tools/lldb-mi/MICmdCmdTarget.h +++ lldb/trunk/tools/lldb-mi/MICmdCmdTarget.h @@ -58,3 +58,62 @@ const CMIUtilString m_constStrArgNamedType; const CMIUtilString m_constStrArgNamedParameters; }; + +//++ ============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "target-attach". +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation +//-- +class CMICmdCmdTargetAttach : public CMICmdBase +{ + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(void); + + // Methods: +public: + /* ctor */ CMICmdCmdTargetAttach(void); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + virtual bool Execute(void); + virtual bool Acknowledge(void); + virtual bool ParseArgs(void); + // From CMICmnBase + /* dtor */ virtual ~CMICmdCmdTargetAttach(void); + + // Attributes: +private: + const CMIUtilString m_constStrArgPid; + const CMIUtilString m_constStrArgNamedFile; + const CMIUtilString m_constStrArgWaitFor; +}; + +//++ ============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "target-attach". +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation +//-- +class CMICmdCmdTargetDetach : public CMICmdBase +{ + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(void); + + // Methods: +public: + /* ctor */ CMICmdCmdTargetDetach(void); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + virtual bool Execute(void); + virtual bool Acknowledge(void); + virtual bool ParseArgs(void); + // From CMICmnBase + /* dtor */ virtual ~CMICmdCmdTargetDetach(void); +}; + Index: lldb/trunk/tools/lldb-mi/MICmdCmdTarget.cpp =================================================================== --- lldb/trunk/tools/lldb-mi/MICmdCmdTarget.cpp +++ lldb/trunk/tools/lldb-mi/MICmdCmdTarget.cpp @@ -22,6 +22,9 @@ #include "MICmnLLDBDebugger.h" #include "MICmnLLDBDebugSessionInfo.h" #include "MICmdArgValString.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValOptionShort.h" +#include "MICmdArgValNumber.h" //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdTargetSelect constructor. @@ -204,3 +207,263 @@ { return new CMICmdCmdTargetSelect(); } + +//++ ------------------------------------------------------------------------------------ +// Details: CMICmdCmdTargetAttach constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdTargetAttach::CMICmdCmdTargetAttach(void) +: m_constStrArgPid("pid") +, m_constStrArgNamedFile("n") +, m_constStrArgWaitFor("waitfor") +{ + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "target-attach"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdTargetAttach::CreateSelf; +} + +//++ ------------------------------------------------------------------------------------ +// Details: CMICmdCmdTargetAttach destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdTargetAttach::~CMICmdCmdTargetAttach(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 - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool +CMICmdCmdTargetAttach::ParseArgs(void) +{ + bool bOk = m_setCmdArgs.Add(*(new CMICmdArgValNumber(m_constStrArgPid, false, true))); + bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValOptionShort(m_constStrArgNamedFile, false, true, + CMICmdArgValListBase::eArgValType_String, 1))); + bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValOptionLong(m_constStrArgWaitFor, false, true))); + return (bOk && ParseValidateCmdOptions()); +} + +//++ ------------------------------------------------------------------------------------ +// Details: The invoker requires this function. The command does work in this function. +// The command is likely to communicate with the LLDB SBDebugger in here. +// Synopsis: -target-attach file +// Ref: http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool +CMICmdCmdTargetAttach::Execute(void) +{ + CMICMDBASE_GETOPTION(pArgPid, Number, m_constStrArgPid); + CMICMDBASE_GETOPTION(pArgFile, OptionShort, m_constStrArgNamedFile); + CMICMDBASE_GETOPTION(pArgWaitFor, OptionLong, m_constStrArgWaitFor); + + CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); + + // If the current target is invalid, create one + lldb::SBTarget target = rSessionInfo.GetTarget(); + if (!target.IsValid()) + { + target = rSessionInfo.GetDebugger().CreateTarget(NULL); + if (!target.IsValid()) + { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_CURRENT), m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + } + + lldb::SBError error; + lldb::SBListener listener; + if (pArgPid->GetFound() && pArgPid->GetValid()) + { + lldb::pid_t pid; + pid = pArgPid->GetValue(); + target.AttachToProcessWithID(listener, pid, error); + } + else if (pArgFile->GetFound() && pArgFile->GetValid()) + { + bool bWaitFor = (pArgWaitFor->GetFound()); + CMIUtilString file; + pArgFile->GetExpectedOption(file); + target.AttachToProcessWithName(listener, file.c_str(), bWaitFor, error); + } + else + { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_ATTACH_BAD_ARGS), m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + lldb::SBStream errMsg; + if (error.Fail()) + { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_ATTACH_FAILED), m_cmdData.strMiCmd.c_str(), errMsg.GetData())); + 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(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool +CMICmdCmdTargetAttach::Acknowledge(void) +{ + const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + + CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); + lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID(); + // Prod the client i.e. Eclipse with out-of-band results to help it 'continue' because it is using LLDB debugger + // Give the client '=thread-group-started,id="i1"' + m_bHasResultRecordExtra = true; + const CMICmnMIValueConst miValueConst2("i1"); + const CMICmnMIValueResult miValueResult2("id", miValueConst2); + const CMIUtilString strPid(CMIUtilString::Format("%lld", pid)); + const CMICmnMIValueConst miValueConst(strPid); + const CMICmnMIValueResult miValueResult("pid", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBand(CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupStarted, miValueResult2); + miOutOfBand.Add(miValueResult); + m_miResultRecordExtra = miOutOfBand.GetString(); + + 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 * +CMICmdCmdTargetAttach::CreateSelf(void) +{ + return new CMICmdCmdTargetAttach(); +} + +//++ ------------------------------------------------------------------------------------ +// Details: CMICmdCmdTargetDetach constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdTargetDetach::CMICmdCmdTargetDetach() +{ + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "target-detach"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdTargetDetach::CreateSelf; +} + +//++ ------------------------------------------------------------------------------------ +// Details: CMICmdCmdTargetDetach destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdTargetDetach::~CMICmdCmdTargetDetach(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 - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool +CMICmdCmdTargetDetach::ParseArgs(void) +{ + return MIstatus::success; +} + +//++ ------------------------------------------------------------------------------------ +// Details: The invoker requires this function. The command does work in this function. +// The command is likely to communicate with the LLDB SBDebugger in here. +// Synopsis: -target-attach file +// Ref: http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool +CMICmdCmdTargetDetach::Execute(void) +{ + CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); + + lldb::SBProcess process = rSessionInfo.GetProcess(); + + if (!process.IsValid()) + { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS), m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + process.Detach(); + + return MIstatus::success; +} + +//++ ------------------------------------------------------------------------------------ +// Details: The invoker requires this function. The command prepares a MI Record Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool +CMICmdCmdTargetDetach::Acknowledge(void) +{ + const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + 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 * +CMICmdCmdTargetDetach::CreateSelf(void) +{ + return new CMICmdCmdTargetDetach(); +} Index: lldb/trunk/tools/lldb-mi/MICmdCommands.cpp =================================================================== --- lldb/trunk/tools/lldb-mi/MICmdCommands.cpp +++ lldb/trunk/tools/lldb-mi/MICmdCommands.cpp @@ -121,6 +121,8 @@ bOk &= Register(); bOk &= Register(); bOk &= Register(); + bOk &= Register(); + bOk &= Register(); bOk &= Register(); bOk &= Register(); bOk &= Register(); Index: lldb/trunk/tools/lldb-mi/MICmnResources.h =================================================================== --- lldb/trunk/tools/lldb-mi/MICmnResources.h +++ lldb/trunk/tools/lldb-mi/MICmnResources.h @@ -268,7 +268,9 @@ IDS_CMD_ERR_GDBSET_OPT_PRINT_UNKNOWN_OPTION, IDS_CMD_ERR_GDBSHOW_OPT_PRINT_BAD_ARGS, IDS_CMD_ERR_GDBSHOW_OPT_PRINT_UNKNOWN_OPTION, - IDS_CMD_ERR_EXPR_INVALID + IDS_CMD_ERR_EXPR_INVALID, + IDS_CMD_ERR_ATTACH_FAILED, + IDS_CMD_ERR_ATTACH_BAD_ARGS }; //++ ============================================================================ Index: lldb/trunk/tools/lldb-mi/MICmnResources.cpp =================================================================== --- lldb/trunk/tools/lldb-mi/MICmnResources.cpp +++ lldb/trunk/tools/lldb-mi/MICmnResources.cpp @@ -250,7 +250,9 @@ {IDS_CMD_ERR_GDBSET_OPT_PRINT_UNKNOWN_OPTION, "'print' error. The option '%s' not found"}, {IDS_CMD_ERR_GDBSHOW_OPT_PRINT_BAD_ARGS, "'print' expects option-name and \"on\" or \"off\""}, {IDS_CMD_ERR_GDBSHOW_OPT_PRINT_UNKNOWN_OPTION, "'print' error. The option '%s' not found"}, - {IDS_CMD_ERR_EXPR_INVALID, "Failed to evaluate expression: %s"}}; + {IDS_CMD_ERR_EXPR_INVALID, "Failed to evaluate expression: %s"}, + {IDS_CMD_ERR_ATTACH_FAILED, "Command '%s'. Attach to processs failed: %s"}, + {IDS_CMD_ERR_ATTACH_BAD_ARGS, "Command '%s'. Must specify either a PID or a Name"}}; //++ ------------------------------------------------------------------------------------ // Details: CMICmnResources constructor. Index: lldb/trunk/tools/lldb-mi/MIExtensions.txt =================================================================== --- lldb/trunk/tools/lldb-mi/MIExtensions.txt +++ lldb/trunk/tools/lldb-mi/MIExtensions.txt @@ -91,3 +91,13 @@ For example: =library-loaded,id="/Users/IliaK/p/hello",target-name="/Users/IliaK/p/hello",host-name="/Users/IliaK/p/hello",symbols-loaded="1",symbols-path="/Users/IliaK/p/hello.dSYM/Contents/Resources/DWARF/hello",loaded_addr="-" =library-loaded,id="/usr/lib/dyld",target-name="/usr/lib/dyld",host-name="/usr/lib/dyld",symbols-loaded="0",loaded_addr="0x00007fff5fc00000" + +# -target-attach + +Synopsis + +Additional syntax provided by lldb-mi: + -target-attach -n [--waitfor] + +Attach to an executable. Using -n allows specifying an executable name to attach to. +Using this with --watifor can do a deffered attach. The flags -n and --waitfor match the syntax of lldb proper's 'process attach' command.