Index: test/tools/lldb-mi/TestMiSyntax.py =================================================================== --- test/tools/lldb-mi/TestMiSyntax.py +++ test/tools/lldb-mi/TestMiSyntax.py @@ -1,67 +0,0 @@ -""" -Test that the lldb-mi driver understands MI command syntax. -""" - -import lldbmi_testcase -from lldbtest import * -import unittest2 - -class MiSyntaxTestCase(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 - def test_lldbmi_tokens(self): - """Test that 'lldb-mi --interpreter' prints command tokens.""" - - self.spawnLldbMi(args = None) - - # Load executable - self.runCmd("000-file-exec-and-symbols %s" % self.myexe) - self.expect("000\^done") - - # Run to main - self.runCmd("100000001-break-insert -f main") - self.expect("100000001\^done,bkpt={number=\"1\"") - self.runCmd("2-exec-run") - self.expect("2\^running") - self.expect("\*stopped,reason=\"breakpoint-hit\"") - - # Exit - self.runCmd("0000000000000000000003-exec-continue") - self.expect("0000000000000000000003\^running") - self.expect("\*stopped,reason=\"exited-normally\"") - - @lldbmi_test - @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") - def test_lldbmi_specialchars(self): - """Test that 'lldb-mi --interpreter' handles complicated strings.""" - - self.spawnLldbMi(args = None) - - # Create alias for myexe - complicated_myexe = "C--mpl-x file's`s @#$%^&*()_+-={}[]| name" - if os.path.exists(complicated_myexe): - os.unlink(complicated_myexe) - os.symlink(self.myexe, complicated_myexe) - - try: - # Try to load executable with complicated filename - self.runCmd("-file-exec-and-symbols \"%s\"" % complicated_myexe) - self.expect("\^done") - - # Check that it was loaded correctly - self.runCmd("-break-insert -f main") - self.expect("\^done,bkpt={number=\"1\"") - self.runCmd("-exec-run") - self.expect("\^running") - self.expect("\*stopped,reason=\"breakpoint-hit\"") - - finally: - # Clean up - os.unlink(complicated_myexe) - -if __name__ == '__main__': - unittest2.main() Index: test/tools/lldb-mi/syntax/Makefile =================================================================== --- /dev/null +++ test/tools/lldb-mi/syntax/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules Index: test/tools/lldb-mi/syntax/TestMiSyntax.py =================================================================== --- test/tools/lldb-mi/syntax/TestMiSyntax.py +++ test/tools/lldb-mi/syntax/TestMiSyntax.py @@ -2,6 +2,10 @@ Test that the lldb-mi driver understands MI command syntax. """ +# adjust path for lldbmi_testcase.py +import sys, os.path +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + import lldbmi_testcase from lldbtest import * import unittest2 @@ -63,5 +67,24 @@ # Clean up os.unlink(complicated_myexe) + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_process_output(self): + """Test that 'lldb-mi --interpreter' wraps process output correctly.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run + self.runCmd("-exec-run") + self.expect("\^running") + + # Test that a process output is wrapped correctly + self.expect("\~\"'\\\\r\\\\n` - it's \\\\\\\\n\\\\x12\\\\\"\\\\\\\\\\\\\"") + if __name__ == '__main__': unittest2.main() Index: test/tools/lldb-mi/syntax/main.cpp =================================================================== --- /dev/null +++ test/tools/lldb-mi/syntax/main.cpp @@ -0,0 +1,17 @@ +//===-- 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[]) +{ + printf("'\n` - it's \\n\x12\"\\\""); + return 0; +} Index: tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.h =================================================================== --- tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.h +++ tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.h @@ -86,7 +86,6 @@ bool TextToStdout(const CMIUtilString &vrTxt); bool TextToStderr(const CMIUtilString &vrTxt); bool UpdateSelectedThread(void); - bool ConvertPrintfCtrlCodeToString(const MIchar vCtrl, CMIUtilString &vwrStrEquivalent); // Overridden: private: Index: tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp =================================================================== --- tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp +++ tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp @@ -1379,26 +1379,24 @@ bool CMICmnLLDBDebuggerHandleEvents::GetProcessStdout(void) { - bool bOk = MIstatus::success; - - char c; - size_t nBytes = 0; CMIUtilString text; + std::unique_ptr apStdoutBuffer(new char[1024]); lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance().GetDebugger().GetSelectedTarget().GetProcess(); - while (process.GetSTDOUT(&c, 1) > 0) + while (1) { - CMIUtilString str; - if (ConvertPrintfCtrlCodeToString(c, str)) - text += str; - nBytes++; - } - if (nBytes > 0) - { - const CMIUtilString t(CMIUtilString::Format("~\"%s\"", text.c_str())); - bOk = TextToStdout(t); + const size_t nBytes = process.GetSTDOUT(apStdoutBuffer.get(), 1024); + if (nBytes == 0) + break; + + text.append(apStdoutBuffer.get(), nBytes); } - return bOk; + if (text.empty()) + return MIstatus::success; + + const bool bEscapeQuotes(true); + const CMIUtilString t(CMIUtilString::Format("~\"%s\"", text.Escape(bEscapeQuotes).c_str())); + return TextToStdout(t); } //++ ------------------------------------------------------------------------------------ @@ -1414,72 +1412,24 @@ bool CMICmnLLDBDebuggerHandleEvents::GetProcessStderr(void) { - bool bOk = MIstatus::success; - - char c; - size_t nBytes = 0; CMIUtilString text; + std::unique_ptr apStderrBuffer(new char[1024]); lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance().GetDebugger().GetSelectedTarget().GetProcess(); - while (process.GetSTDERR(&c, 1) > 0) - { - CMIUtilString str; - if (ConvertPrintfCtrlCodeToString(c, str)) - text += str; - nBytes++; - } - if (nBytes > 0) - { - const CMIUtilString t(CMIUtilString::Format("~\"%s\"", text.c_str())); - bOk = TextToStdout(t); - } - - return bOk; -} - -//++ ------------------------------------------------------------------------------------ -// Details: Convert text stream control codes to text equivalent. -// Type: Method. -// Args: vCtrl - (R) The control code. -// vwrStrEquivalent - (W) The text equivalent. -// Return: MIstatus::success - Functionality succeeded. -// MIstatus::failure - Functionality failed. -// Throws: None. -//-- -bool -CMICmnLLDBDebuggerHandleEvents::ConvertPrintfCtrlCodeToString(const MIchar vCtrl, CMIUtilString &vwrStrEquivalent) -{ - switch (vCtrl) + while (1) { - case '\033': - vwrStrEquivalent = "\\e"; - break; - case '\a': - vwrStrEquivalent = "\\a"; - break; - case '\b': - vwrStrEquivalent = "\\b"; - break; - case '\f': - vwrStrEquivalent = "\\f"; - break; - case '\n': - vwrStrEquivalent = "\\n"; - break; - case '\r': - vwrStrEquivalent = "\\r"; - break; - case '\t': - vwrStrEquivalent = "\\t"; - break; - case '\v': - vwrStrEquivalent = "\\v"; - break; - default: - vwrStrEquivalent = CMIUtilString::Format("%c", vCtrl); + const size_t nBytes = process.GetSTDERR(apStderrBuffer.get(), 1024); + if (nBytes == 0) break; + + text.append(apStderrBuffer.get(), nBytes); } - return MIstatus::success; + if (text.empty()) + return MIstatus::success; + + const bool bEscapeQuotes(true); + const CMIUtilString t(CMIUtilString::Format("~\"%s\"", text.Escape(bEscapeQuotes).c_str())); + return TextToStdout(t); } //++ ------------------------------------------------------------------------------------ Index: tools/lldb-mi/MIUtilString.h =================================================================== --- tools/lldb-mi/MIUtilString.h +++ tools/lldb-mi/MIUtilString.h @@ -72,6 +72,7 @@ MIuint FindFirst(const CMIUtilString &vrPattern, const bool vbSkipQuotedText, bool &vrwbNotFoundClosedQuote, const MIuint vnPos = 0) const; MIuint FindFirstNot(const CMIUtilString &vrPattern, const MIuint vnPos = 0) const; + CMIUtilString Escape(const bool vbEscapeQuotes = false) const; // CMIUtilString &operator=(const MIchar *vpRhs); CMIUtilString &operator=(const std::string &vrRhs); Index: tools/lldb-mi/MIUtilString.cpp =================================================================== --- tools/lldb-mi/MIUtilString.cpp +++ tools/lldb-mi/MIUtilString.cpp @@ -20,11 +20,12 @@ //-- // Third party headers -#include // std::unique_ptr -#include // va_list, va_start, var_end -#include // std::stringstream -#include // for strncmp -#include // for ULONG_MAX +#include // for PRIx8 +#include // for ULONG_MAX +#include // std::unique_ptr +#include // std::stringstream +#include // va_list, va_start, var_end +#include // for strncmp // In-house headers: #include "MIUtilString.h" @@ -797,3 +798,70 @@ return (MIuint)std::string::npos; } + +//++ ------------------------------------------------------------------------------------ +// Details: Get escaped string from *this string. +// Type: Method. +// Args: None. +// Return: CMIUtilString - The escaped version of the initial string. +// Throws: None. +//-- +CMIUtilString +CMIUtilString::Escape(const bool vbEscapeQuotes /* = false */) const +{ + const MIuint nLen(length()); + CMIUtilString strNew; + strNew.reserve(nLen); + for (MIuint nIndex(0); nIndex < nLen; ++nIndex) + { + const MIchar cUnescapedChar((*this)[nIndex]); + switch (cUnescapedChar) + { + case '\a': + strNew.append("\\a"); + break; + case '\b': + strNew.append("\\b"); + break; + case '\t': + strNew.append("\\t"); + break; + case '\n': + strNew.append("\\n"); + break; + case '\v': + strNew.append("\\v"); + break; + case '\f': + strNew.append("\\f"); + break; + case '\r': + strNew.append("\\r"); + break; + case '\033': + strNew.append("\\e"); + break; + case '\\': + strNew.append("\\\\"); + break; + case '\"': + if (vbEscapeQuotes) + { + strNew.append("\\\""); + break; + } + // FALLTHROUGH + default: + if (::isprint(cUnescapedChar)) + strNew.push_back(cUnescapedChar); + else + { + char strEscapedChar[sizeof("\\xXX")]; + ::sprintf(strEscapedChar, "\\x%02" PRIx8, cUnescapedChar); + strNew.append(strEscapedChar); + } + break; + } + } + return strNew; +}