| """ |
| Test that the lldb-mi driver understands MI command syntax. |
| """ |
| |
| from __future__ import print_function |
| |
| |
| import lldbmi_testcase |
| from lldbsuite.test.decorators import * |
| from lldbsuite.test.lldbtest import * |
| from lldbsuite.test import lldbutil |
| from functools import reduce |
| |
| |
| class MiSyntaxTestCase(lldbmi_testcase.MiTestCaseBase): |
| |
| mydir = TestBase.compute_mydir(__file__) |
| |
| @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows |
| @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races |
| @skipIfRemote # We do not currently support remote debugging via the MI. |
| @skipIfDarwin |
| 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\"") |
| |
| @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows |
| @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races |
| @skipIfRemote # We do not currently support remote debugging via the MI. |
| @skipIfDarwin |
| def test_lldbmi_specialchars(self): |
| """Test that 'lldb-mi --interpreter' handles complicated strings.""" |
| |
| # Create an alias for myexe |
| complicated_myexe = self.getBuildArtifact("C--mpl-x file's`s @#$%^&*()_+-={}[]| name") |
| os.symlink(self.myexe, complicated_myexe) |
| self.addTearDownHook(lambda: os.unlink(complicated_myexe)) |
| |
| self.spawnLldbMi(args="\"%s\"" % complicated_myexe) |
| |
| # Test that the executable was loaded |
| self.expect( |
| "-file-exec-and-symbols \"%s\"" % |
| complicated_myexe, exactly=True) |
| 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\"") |
| |
| @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows |
| @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races |
| @skipIfDarwin |
| @skipIfRemote # We do not currently support remote debugging via the MI. |
| 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("-interpreter-exec command help") |
| 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] |
| |
| # Routines to get a bit-mask for the specified list of patterns |
| 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 async-records 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") |