Index: test/tools/lldb-mi/startup_options/TestMiStartupOptions.py =================================================================== --- test/tools/lldb-mi/startup_options/TestMiStartupOptions.py +++ test/tools/lldb-mi/startup_options/TestMiStartupOptions.py @@ -127,6 +127,105 @@ # Test that lldb-mi is ready when executable was loaded self.expect(self.child_prompt, exactly = True) + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_lldbmi_source_option_start_script(self): + """Test that 'lldb-mi --interpreter' can execute user's commands after initial commands were executed.""" + + # Prepared source file + sourceFile = "start_script" + + self.spawnLldbMi(args = "--source %s" % sourceFile) + + # After '-file-exec-and-symbols a.out' + self.expect("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # After '-break-insert -f main' + self.expect("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + + # After '-exec-run' + self.expect("-exec-run") + self.expect("\^running") + + # After '-break-insert main.cpp:BP_return' + line = line_number('main.cpp', '// BP_return') + self.expect("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"2\"") + + # After '-exec-continue' + self.expect("-exec-continue") + self.expect("\^running") + + # Test that lldb-mi is ready after execution of --source start_script + self.expect(self.child_prompt, exactly = True) + + # Try to evaluate 'a' expression + self.runCmd("-data-evaluate-expression a") + self.expect("\^done,value=\"10\"") + self.expect(self.child_prompt, exactly = True) + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_lldbmi_source_option_start_script_exit(self): + """Test that 'lldb-mi --interpreter' can execute a prepared file which passed via --source option.""" + + # Prepared source file + sourceFile = "start_script_exit" + + self.spawnLldbMi(args = "--source %s" % sourceFile) + + # After '-file-exec-and-symbols a.out' + self.expect("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # After '-break-insert -f main' + self.expect("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + + # After '-exec-run' + self.expect("-exec-run") + self.expect("\^running") + + # After '-break-insert main.cpp:BP_return' + line = line_number('main.cpp', '// BP_return') + self.expect("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"2\"") + + # After '-exec-continue' + self.expect("-exec-continue") + self.expect("\^running") + + # After '-data-evaluate-expression a' + self.expect("-data-evaluate-expression a") + self.expect("\^done,value=\"10\"") + + # After '-gdb-exit' + self.expect("-gdb-exit") + self.expect("\^exit") + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_lldbmi_source_option_start_script_error(self): + """Test that 'lldb-mi --interpreter' stops execution of initial commands in case of error.""" + + # Prepared source file + sourceFile = "start_script_error" + + self.spawnLldbMi(args = "--source %s" % sourceFile) + + # After '-file-exec-and-symbols a.out' + self.expect("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # After '-break-ins -f main' + self.expect("-break-ins -f main") + self.expect("\^error") + + # Test that lldb-mi is ready after execution of --source start_script + self.expect(self.child_prompt, exactly = True) @lldbmi_test @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") Index: test/tools/lldb-mi/startup_options/main.cpp =================================================================== --- test/tools/lldb-mi/startup_options/main.cpp +++ test/tools/lldb-mi/startup_options/main.cpp @@ -10,5 +10,6 @@ int main(int argc, char const *argv[]) { - return 0; + int a = 10; + return 0; // BP_return } Index: test/tools/lldb-mi/startup_options/start_script =================================================================== --- /dev/null +++ test/tools/lldb-mi/startup_options/start_script @@ -0,0 +1,5 @@ +-file-exec-and-symbols a.out +-break-insert -f main +-exec-run +-break-insert main.cpp:14 +-exec-continue Index: test/tools/lldb-mi/startup_options/start_script_error =================================================================== --- /dev/null +++ test/tools/lldb-mi/startup_options/start_script_error @@ -0,0 +1,2 @@ +-file-exec-and-symbols a.out +-break-ins -f main Index: test/tools/lldb-mi/startup_options/start_script_exit =================================================================== --- /dev/null +++ test/tools/lldb-mi/startup_options/start_script_exit @@ -0,0 +1,7 @@ +-file-exec-and-symbols a.out +-break-insert -f main +-exec-run +-break-insert main.cpp:14 +-exec-continue +-data-evaluate-expression a +-gdb-exit Index: tools/lldb-mi/MIDriver.h =================================================================== --- tools/lldb-mi/MIDriver.h +++ tools/lldb-mi/MIDriver.h @@ -137,6 +137,7 @@ bool InitClientIDEToMIDriver(void) const; bool InitClientIDEEclipse(void) const; bool LocalDebugSessionStartupExecuteCommands(void); + bool ExecuteCommandFile(const bool vbAsyncMode); // Overridden: private: @@ -161,4 +162,6 @@ CMIUtilString m_strCmdLineArgExecuteableFileNamePath; bool m_bDriverDebuggingArgExecutable; // True = the MI Driver (MI mode) is debugging executable passed as argument, // false = running via a client (e.g. Eclipse) + bool m_bHaveCommandFileNamePathOnCmdLine; // True = file with initial commands given as one of the parameters to the MI Driver, false = not found + CMIUtilString m_strCmdLineArgCommandFileNamePath; }; Index: tools/lldb-mi/MIDriver.cpp =================================================================== --- tools/lldb-mi/MIDriver.cpp +++ tools/lldb-mi/MIDriver.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// // Third party headers: +#include #include "lldb/API/SBError.h" // In-house headers: @@ -54,6 +55,7 @@ , m_eCurrentDriverState(eDriverState_NotRunning) , m_bHaveExecutableFileNamePathOnCmdLine(false) , m_bDriverDebuggingArgExecutable(false) + , m_bHaveCommandFileNamePathOnCmdLine(false) { } @@ -407,20 +409,40 @@ if (bHaveArgs) { - // Search right to left to look for the executable + // Search right to left to look for filenames for (MIint i = argc - 1; i > 0; i--) { const CMIUtilString strArg(argv[i]); const CMICmdArgValFile argFile; + + // Check for a filename if (argFile.IsFilePath(strArg) || CMICmdArgValString(true, false, true).IsStringArg(strArg)) { + // Is this the command file for the '-s' or '--source' options? + const CMIUtilString strPrevArg(argv[i - 1]); + if (strPrevArg.compare("-s") == 0 || strPrevArg.compare("--source") == 0) + { + m_strCmdLineArgCommandFileNamePath = strArg; + m_bHaveCommandFileNamePathOnCmdLine = true; + i--; // skip '-s' on the next loop + continue; + } + // Else, must be the executable bHaveExecutableFileNamePath = true; m_strCmdLineArgExecuteableFileNamePath = strArg; m_bHaveExecutableFileNamePathOnCmdLine = true; } - // This argument is also check for in CMIDriverMgr::ParseArgs() - if (0 == strArg.compare("--executable")) // Used to specify that there is executable argument also on the command line - { // See fn description. + // Report error if no command file was specified for the '-s' or '--source' options + else if (strArg.compare("-s") == 0 || strArg.compare("--source") == 0) + { + vwbExiting = true; + const CMIUtilString errMsg = CMIUtilString::Format(MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_MISSING_INF), strArg.c_str()); + errStatus.SetErrorString(errMsg.c_str()); + break; + } + // This argument is also checked for in CMIDriverMgr::ParseArgs() + else if (strArg.compare("--executable") == 0) // Used to specify that there is executable argument also on the command line + { // See fn description. bHaveExecutableLongOption = true; } } @@ -526,6 +548,13 @@ // App is not quitting currently m_bExitApp = false; + // Handle source file + if (m_bHaveCommandFileNamePathOnCmdLine) + { + const bool bAsyncMode = false; + ExecuteCommandFile(bAsyncMode); + } + // While the app is active while (bOk && !m_bExitApp) { @@ -1229,6 +1258,74 @@ } //++ ------------------------------------------------------------------------------------ +// Details: Execute commands from prepared source file +// Type: Method. +// Args: vbAsyncMode - (R) True = execute commands in asynchronous mode, false = otherwise. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool +CMIDriver::ExecuteCommandFile(const bool vbAsyncMode) +{ + std::ifstream ifsStartScript(m_strCmdLineArgCommandFileNamePath.c_str()); + if (!ifsStartScript.is_open()) + { + const CMIUtilString errMsg( + CMIUtilString::Format(MIRSRC(IDS_UTIL_FILE_ERR_OPENING_FILE_UNKNOWN), m_strCmdLineArgCommandFileNamePath.c_str())); + SetErrorDescription(errMsg.c_str()); + const bool bForceExit = true; + SetExitApplicationFlag(bForceExit); + return MIstatus::failure; + } + + // Switch lldb to synchronous mode + CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); + const bool bAsyncSetting = rSessionInfo.GetDebugger().GetAsync(); + rSessionInfo.GetDebugger().SetAsync(vbAsyncMode); + + // Execute commands from file + bool bOk = MIstatus::success; + CMIUtilString strCommand; + while (!m_bExitApp && std::getline(ifsStartScript, strCommand)) + { + // Print command + bOk = CMICmnStreamStdout::TextToStdout(strCommand); + + // Skip if it's a comment or empty line + if (strCommand.empty() || strCommand[0] == '#') + continue; + + // Execute if no error + if (bOk) + { + CMIUtilThreadLock lock(rSessionInfo.GetSessionMutex()); + bOk = InterpretCommand(strCommand); + } + + // Draw the prompt after command will be executed (if enabled) + if (bOk && m_rStdin.GetEnablePrompt()) + bOk = m_rStdOut.WriteMIResponse(m_rStdin.GetPrompt()); + + // Exit if there is an error + if (!bOk) + { + const bool bForceExit = true; + SetExitApplicationFlag(bForceExit); + break; + } + + // Wait while the handler thread handles incoming events + CMICmnLLDBDebugger::Instance().WaitForHandleEvent(); + } + + // Switch lldb back to initial mode + rSessionInfo.GetDebugger().SetAsync(bAsyncSetting); + + return bOk; +} + +//++ ------------------------------------------------------------------------------------ // Details: Gets called when lldb-mi gets a signal. Stops the process if it was SIGINT. // // Type: Method.