Index: packages/Python/lldbsuite/test/tools/lldb-mi/syntax/TestMiSyntax.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-mi/syntax/TestMiSyntax.py +++ packages/Python/lldbsuite/test/tools/lldb-mi/syntax/TestMiSyntax.py @@ -84,3 +84,79 @@ # Test that a process output is wrapped correctly self.expect("\@\"'\\\\r\\\\n\"") self.expect("\@\"` - it's \\\\\\\\n\\\\x12\\\\\"\\\\\\\\\\\\\"") + + @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_output_grammar(self): + """Test that 'lldb-mi --interpreter' uses standard output syntax.""" + + self.spawnLldbMi(args = None) + self.child.setecho(False) + + # Run all commands simultaneously + self.runCmd("-unknown-command") + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.runCmd("-break-insert -f main") + self.runCmd("-gdb-set target-async off") + self.runCmd("-exec-run") + self.runCmd("-gdb-set target-async on") + self.runCmd("-exec-continue") + self.runCmd("-gdb-exit") + + # Test that the program's output matches to the following pattern: + # ( async-record | stream-record )* [ result-record ] "(gdb)" nl + async_record = "^[0-9]*(\*|\+|=).+?\n" # 1 + stream_record = "^(~|@|&).+?\n" # 2 + result_record = "^[0-9]*\^.+?\n" # 3 + prompt = "^\(gdb\)\r\n" # 4 + command = "^\r\n" # 5 (it looks like empty line for pexpect) + error = "^.+?\n" # 6 + import pexpect # 7 (EOF) + all_patterns = [ async_record, stream_record, result_record, prompt, command, error, pexpect.EOF ] + def get_bit(pattern): return all_patterns.index(pattern) + def get_mask(pattern): return 1 << get_bit(pattern) + def or_op(x, y): return x | y + def get_state(*args): return reduce(or_op, map(get_mask, args)) + + next_state = get_state(command) + while True: + it = self.expect(all_patterns) + matched_pattern = all_patterns[it] + + # Check that state is acceptable + if not (next_state & get_mask(matched_pattern)): + self.fail("error: inconsistent pattern '%s' for state %#x (matched string: %s)" % (repr(matched_pattern), next_state, self.child.after)) + elif matched_pattern == async_record or matched_pattern == stream_record: + next_state = get_state(async_record, stream_record, result_record, prompt) + elif matched_pattern == result_record: + # FIXME lldb-mi prints the async-record out of turn + # ``` + # ^done + # (gdb) + # ^running + # =thread-group-started,id="i1",pid="13875" + # (gdb) + # ``` + # Therefore to pass that test I changed the grammar's rule: + # next_state = get_state(prompt) + # to: + next_state = get_state(async_record, prompt) + elif matched_pattern == prompt: + # FIXME lldb-mi prints the prompt out of turn + # ``` + # ^done + # (gdb) + # ^running + # (gdb) + # (gdb) + # ``` + # Therefore to pass that test I changed the grammar's rule: + # next_state = get_state(async_record, stream_record, result_record, command, pexpect.EOF) + # to: + next_state = get_state(async_record, stream_record, result_record, prompt, command, pexpect.EOF) + elif matched_pattern == command: + next_state = get_state(async_record, stream_record, result_record) + elif matched_pattern == pexpect.EOF: + break + else: + self.fail("error: pexpect returned an unknown state") Index: tools/lldb-mi/MIDriver.cpp =================================================================== --- tools/lldb-mi/MIDriver.cpp +++ tools/lldb-mi/MIDriver.cpp @@ -602,9 +602,6 @@ // Close and wait for the workers to stop StopWorkerThreads(); - // Ensure that a new line is sent as the last act of the dying driver - m_rStdOut.WriteMIResponse("\n", false); - return MIstatus::success; }