Index: include/lldb/API/SBArgs.h =================================================================== --- include/lldb/API/SBArgs.h +++ include/lldb/API/SBArgs.h @@ -0,0 +1,83 @@ +//===-- SBArgs.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBArgs_h_ +#define LLDB_SBArgs_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBArgs +{ +public: + SBArgs (); + + SBArgs (bool force_create); + + SBArgs (const SBArgs &rhs); + + ~SBArgs (); + + const lldb::SBArgs & + operator = (const lldb::SBArgs &rhs); + + bool + IsValid () const; + + size_t + GetArgumentCount () const; + + const char * + GetArgumentAtIndex (size_t idx) const; + + char ** + GetArgumentVector (); + + const char ** + GetConstArgumentVector () const; + + const char * + AppendArgument (const char *arg_cstr); + + void + Clear (); + +protected: + + friend class SBTarget; + + lldb_private::Args * + get (); + + const lldb_private::Args * + get () const; + + lldb_private::Args * + operator -> (); + + const lldb_private::Args & + operator * () const; + + lldb_private::Args & + ref (); + + void + SetArgs (const lldb_private::Args &lldb_args); + +private: + std::unique_ptr m_opaque_ap; + + void + CreateIfNeeded (); +}; + +} // namespace lldb + +#endif // LLDB_SBArgs_h_ Index: include/lldb/API/SBDefines.h =================================================================== --- include/lldb/API/SBDefines.h +++ include/lldb/API/SBDefines.h @@ -29,6 +29,7 @@ namespace lldb { class LLDB_API SBAddress; +class LLDB_API SBArgs; class LLDB_API SBBlock; class LLDB_API SBBreakpoint; class LLDB_API SBBreakpointLocation; Index: include/lldb/API/SBTarget.h =================================================================== --- include/lldb/API/SBTarget.h +++ include/lldb/API/SBTarget.h @@ -1047,7 +1047,13 @@ lldb::addr_t GetStackRedZoneSize(); - + + lldb::SBArgs + GetRunArguments () const; + + bool + SetRunArguments (const lldb::SBArgs &args); + protected: friend class SBAddress; friend class SBBlock; Index: source/API/CMakeLists.txt =================================================================== --- source/API/CMakeLists.txt +++ source/API/CMakeLists.txt @@ -5,6 +5,7 @@ # well (where appropriate). add_lldb_library(lldbAPI SBAddress.cpp + SBArgs.cpp SBBlock.cpp SBBreakpoint.cpp SBBreakpointLocation.cpp Index: source/API/SBArgs.cpp =================================================================== --- source/API/SBArgs.cpp +++ source/API/SBArgs.cpp @@ -0,0 +1,153 @@ +//===-- SBArgs.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBArgs.h" +#include "lldb/Interpreter/Args.h" + + +using namespace lldb; +using namespace lldb_private; + + +SBArgs::SBArgs () : + m_opaque_ap () +{ +} + +SBArgs::SBArgs (bool force_create) : + m_opaque_ap () +{ + if (force_create) + m_opaque_ap.reset (new Args()); +} + +SBArgs::SBArgs (const SBArgs &rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + m_opaque_ap.reset (new Args(*rhs)); +} + +SBArgs::~SBArgs () +{ +} + +const SBArgs & +SBArgs::operator = (const SBArgs &rhs) +{ + if (rhs.IsValid()) + { + if (m_opaque_ap.get()) + *m_opaque_ap = *rhs; + else + m_opaque_ap.reset (new Args(*rhs)); + } + else + m_opaque_ap.reset(); + + return *this; +} + +bool +SBArgs::IsValid () const +{ + return m_opaque_ap.get() != NULL; +} + +void +SBArgs::SetArgs (const Args &lldb_args) +{ + CreateIfNeeded (); + *m_opaque_ap = lldb_args; +} + +size_t +SBArgs::GetArgumentCount () const +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetArgumentCount(); + return 0; +} + +const char * +SBArgs::GetArgumentAtIndex (size_t idx) const +{ + if (m_opaque_ap) + return m_opaque_ap->GetArgumentAtIndex(idx); + return NULL; +} + +char ** +SBArgs::GetArgumentVector () +{ + if (m_opaque_ap) + return m_opaque_ap->GetArgumentVector(); + return NULL; +} + +const char ** +SBArgs::GetConstArgumentVector () const +{ + if (m_opaque_ap) + return m_opaque_ap->GetConstArgumentVector(); + return NULL; +} + +const char * +SBArgs::AppendArgument (const char *arg_cstr) +{ + CreateIfNeeded (); + return m_opaque_ap->AppendArgument(arg_cstr); +} + +void +SBArgs::Clear () +{ + if (m_opaque_ap) + m_opaque_ap->Clear(); +} + +Args * +SBArgs::get () +{ + return m_opaque_ap.get(); +} + +const Args * +SBArgs::get () const +{ + return m_opaque_ap.get(); +} + +Args * +SBArgs::operator -> () +{ + return m_opaque_ap.get(); +} + +const Args & +SBArgs::operator * () const +{ + // Be sure to call "IsValid()" before calling this function or it will crash + return *m_opaque_ap; +} + +Args & +SBArgs::ref () +{ + CreateIfNeeded(); + return *m_opaque_ap; +} + +void +SBArgs::CreateIfNeeded () +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset(new Args()); +} Index: source/API/SBTarget.cpp =================================================================== --- source/API/SBTarget.cpp +++ source/API/SBTarget.cpp @@ -13,6 +13,7 @@ #include "lldb/lldb-public.h" +#include "lldb/API/SBArgs.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBExpressionOptions.h" @@ -2929,3 +2930,29 @@ return 0; } +SBArgs +SBTarget::GetRunArguments () const +{ + SBArgs sb_args; + TargetSP target_sp(GetSP()); + if (target_sp) + { + Args args; + if (m_opaque_sp->GetRunArguments(args)) + sb_args.SetArgs(args); + } + return sb_args; +} + +bool +SBTarget::SetRunArguments (const SBArgs &args) +{ + bool result = false; + TargetSP target_sp(GetSP()); + if (target_sp && args.IsValid()) + { + m_opaque_sp->SetRunArguments(*args.get()); + result = true; + } + return result; +} Index: test/tools/lldb-mi/TestMiProgramArgs.py =================================================================== --- test/tools/lldb-mi/TestMiProgramArgs.py +++ test/tools/lldb-mi/TestMiProgramArgs.py @@ -22,10 +22,9 @@ except: pass - @unittest2.skip("lldb-mi can't pass params to app.") @lldbmi_test - def test_lldbmi_paramargs(self): - """Test that 'lldb-mi --interpreter' can pass arguments to the app.""" + def test_lldbmi_clearargs(self): + """Test that 'lldb-mi --interpreter' can reset previously set args using -exec-arguments.""" import pexpect self.buildDefault() @@ -39,36 +38,98 @@ child.logfile_send = f_send child.logfile_read = f_read - child.sendline("-file-exec-and-symbols " + self.myexe) + # Load executable + child.sendline("-file-exec-and-symbols %s" % (self.myexe)) child.expect("\^done") - child.sendline("settings set target.run-args l") #FIXME: args not passed - #child.sendline("-exec-arguments l") #FIXME: not recognized and hung lldb-mi + # Set arguments + child.sendline("-exec-arguments foo bar baz") + child.expect("\^done") + child.sendline("-exec-arguments") + child.expect("\^done") - #run to main + # Run to main child.sendline("-break-insert -f main") child.expect("\^done,bkpt={number=\"1\"") child.sendline("-exec-run") child.expect("\^running") child.expect("\*stopped,reason=\"breakpoint-hit\"") - #check argc to see if arg passed + # Check argc to see if arg passed child.sendline("-data-evaluate-expression argc") - child.expect("value=\"2\"") + child.expect("\^done,value=\"1\"") - #set BP on code which is only executed if "l" was passed correctly (marked BP_argtest) - self.line = line_number('main.c', '//BP_argtest') - child.sendline("-break-insert main.c:%d" % self.line) - child.expect("\^done,bkpt={number=\"2\"") - child.sendline("-exec-continue") + # Now that the necessary logging is done, restore logfile to None to + # stop further logging. + child.logfile_send = None + child.logfile_read = None + + with open('child_send.txt', 'r') as fs: + if self.TraceOn(): + print "\n\nContents of child_send.txt:" + print fs.read() + with open('child_read.txt', 'r') as fr: + from_child = fr.read() + if self.TraceOn(): + print "\n\nContents of child_read.txt:" + print from_child + + @lldbmi_test + def test_lldbmi_setargs(self): + """Test that 'lldb-mi --interpreter' can pass args using -exec-arguments.""" + import pexpect + self.buildDefault() + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s --interpreter' % (self.lldbMiExec)) + child = self.child + child.setecho(True) + # Turn on logging for input/output to/from the child. + with open('child_send.txt', 'w') as f_send: + with open('child_read.txt', 'w') as f_read: + child.logfile_send = f_send + child.logfile_read = f_read + + # Load executable + child.sendline("-file-exec-and-symbols %s" % (self.myexe)) + child.expect("\^done") + + # Set arguments + child.sendline("-exec-arguments --arg1 \"2nd arg\" third_arg fourth=\"4th arg\"") + child.expect("\^done") + + # Run to main + child.sendline("-break-insert -f main") + child.expect("\^done,bkpt={number=\"1\"") + child.sendline("-exec-run") child.expect("\^running") child.expect("\*stopped,reason=\"breakpoint-hit\"") + # Check argc and argv to see if arg passed + child.sendline("-data-evaluate-expression argc") + child.expect("\^done,value=\"5\"") + #child.sendline("-data-evaluate-expression argv[1]") + #child.expect("\^done,value=\"--arg1\"") + child.sendline("-interpreter-exec command \"print argv[1]\"") + child.expect("\"--arg1\"") + #child.sendline("-data-evaluate-expression argv[2]") + #child.expect("\^done,value=\"2nd arg\"") + child.sendline("-interpreter-exec command \"print argv[2]\"") + child.expect("\"2nd arg\"") + #child.sendline("-data-evaluate-expression argv[3]") + #child.expect("\^done,value=\"third_arg\"") + child.sendline("-interpreter-exec command \"print argv[3]\"") + child.expect("\"third_arg\"") + #child.sendline("-data-evaluate-expression argv[4]") + #child.expect("\^done,value=\"fourth=\\\\\\\"4th arg\\\\\\\"\"") + child.sendline("-interpreter-exec command \"print argv[4]\"") + child.expect_exact("\"fourth=\\\"4th arg\\\"\"") + # Now that the necessary logging is done, restore logfile to None to # stop further logging. child.logfile_send = None child.logfile_read = None - + with open('child_send.txt', 'r') as fs: if self.TraceOn(): print "\n\nContents of child_send.txt:" Index: tools/lldb-mi/MICmdArgValListOfN.h =================================================================== --- tools/lldb-mi/MICmdArgValListOfN.h +++ tools/lldb-mi/MICmdArgValListOfN.h @@ -56,7 +56,7 @@ const ArgValType_e veType); // const VecArgObjPtr_t &GetExpectedOptions(void) const; - template bool GetExpectedOption(T2 &vrwValue) const; + template bool GetExpectedOption(T2 &vrwValue, const VecArgObjPtr_t::size_type vnAt = 0) const; // Overridden: public: @@ -76,6 +76,7 @@ // parsed from the command's options string. // Type: Template method. // Args: vrwValue - (W) Templated type return value. +// vnAt - (R) Value at the specific position. // T1 - The argument value's class type of the data hold in the list of options. // T2 - The type pf the variable which holds the value wanted. // Return: MIstatus::success - Functional succeeded. @@ -84,10 +85,13 @@ //-- template bool -CMICmdArgValListOfN::GetExpectedOption(T2 &vrwValue) const +CMICmdArgValListOfN::GetExpectedOption(T2 &vrwValue, const VecArgObjPtr_t::size_type vnAt) const { const VecArgObjPtr_t &rVecOptions(GetExpectedOptions()); - VecArgObjPtr_t::const_iterator it2 = rVecOptions.begin(); + if (rVecOptions.size() <= vnAt) + return MIstatus::failure; + + VecArgObjPtr_t::const_iterator it2 = rVecOptions.begin() + vnAt; if (it2 != rVecOptions.end()) { const T1 *pOption = static_cast(*it2); Index: tools/lldb-mi/MICmdCmdExec.h =================================================================== --- tools/lldb-mi/MICmdCmdExec.h +++ tools/lldb-mi/MICmdCmdExec.h @@ -18,6 +18,7 @@ // CMICmdCmdExecStepInstruction interface. // CMICmdCmdExecFinish interface. // CMICmdCmdExecInterrupt interface. +// CMICmdCmdExecArguments 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 @@ -307,3 +308,35 @@ private: lldb::SBCommandReturnObject m_lldbResult; }; + +//++ ============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "exec-arguments". +// Gotchas: None. +// Authors: Ilia Kirianovskii 25/11/2014. +// Changes: None. +//-- +class CMICmdCmdExecArguments : public CMICmdBase +{ + // Statics: + public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(void); + + // Methods: + public: + /* ctor */ CMICmdCmdExecArguments(void); + + // Overridden: + public: + // From CMICmdInvoker::ICmd + virtual bool Execute(void); + virtual bool Acknowledge(void); + virtual bool ParseArgs(void); + // From CMICmnBase + /* dtor */ virtual ~CMICmdCmdExecArguments(void); + + // Attributes: + private: + const CMIUtilString m_constStrArgArguments; +}; Index: tools/lldb-mi/MICmdCmdExec.cpp =================================================================== --- tools/lldb-mi/MICmdCmdExec.cpp +++ tools/lldb-mi/MICmdCmdExec.cpp @@ -18,6 +18,7 @@ // CMICmdCmdExecStepInstruction implementation. // CMICmdCmdExecFinish implementation. // CMICmdCmdExecInterrupt implementation. +// CMICmdCmdExecArguments implementation. // // Environment: Compilers: Visual C++ 12. // gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1 @@ -27,6 +28,7 @@ //-- // Third Party Headers: +#include #include #include #include @@ -90,8 +92,10 @@ CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); lldb::SBError error; lldb::SBStream errMsg; - uint32_t launch_flags = lldb::LaunchFlags::eLaunchFlagDebug; - lldb::SBProcess process = rSessionInfo.m_lldbTarget.Launch(rSessionInfo.m_rLlldbListener, nullptr, nullptr, nullptr, nullptr, nullptr, + const uint32_t launch_flags = lldb::LaunchFlags::eLaunchFlagDebug; + lldb::SBProcess process = rSessionInfo.m_lldbTarget.Launch(rSessionInfo.m_rLlldbListener, + rSessionInfo.m_lldbTarget.GetRunArguments().GetConstArgumentVector(), + nullptr, nullptr, nullptr, nullptr, nullptr, launch_flags, false, error); if ((!process.IsValid()) || (error.Fail())) @@ -1019,3 +1023,120 @@ { return new CMICmdCmdExecInterrupt(); } + +//--------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------- + +//++ ------------------------------------------------------------------------------------ +// Details: CMICmdCmdExecArguments constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecArguments::CMICmdCmdExecArguments(void) + : m_constStrArgArguments("arguments") +{ + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "exec-arguments"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdExecArguments::CreateSelf; +} + +//++ ------------------------------------------------------------------------------------ +// Details: CMICmdCmdExecArguments destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecArguments::~CMICmdCmdExecArguments(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 +CMICmdCmdExecArguments::ParseArgs(void) +{ + bool bOk = m_setCmdArgs.Add( + *(new CMICmdArgValListOfN(m_constStrArgArguments, false, true, CMICmdArgValListBase::eArgValType_StringAnything))); + 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. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool +CMICmdCmdExecArguments::Execute(void) +{ + CMICMDBASE_GETOPTION(pArgArguments, ListOfN, m_constStrArgArguments); + + lldb::SBArgs sbArgs(true); + CMIUtilString strArg; + size_t nArgIndex = 0; + while (pArgArguments->GetExpectedOption(strArg, nArgIndex)) + { + sbArgs.AppendArgument(strArg.c_str()); + ++nArgIndex; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBTarget sbTarget = rSessionInfo.m_lldbTarget; + if (!sbTarget.IsValid()) + { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_CURRENT), m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + sbTarget.SetRunArguments(sbArgs); + + 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 - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool +CMICmdCmdExecArguments::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 * +CMICmdCmdExecArguments::CreateSelf(void) +{ + return new CMICmdCmdExecArguments(); +} Index: tools/lldb-mi/MICmdCommands.cpp =================================================================== --- tools/lldb-mi/MICmdCommands.cpp +++ tools/lldb-mi/MICmdCommands.cpp @@ -98,6 +98,7 @@ bOk &= Register(); bOk &= Register(); bOk &= Register(); + bOk &= Register(); bOk &= Register(); bOk &= Register(); bOk &= Register();