1"""
2Test the 'memory read' command.
3"""
4
5import lldb
6import lldbsuite.test.lldbutil as lldbutil
7
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10
11
12class MemoryReadTestCase(TestBase):
13
14    mydir = TestBase.compute_mydir(__file__)
15    NO_DEBUG_INFO_TESTCASE = True
16
17    def build_run_stop(self):
18        self.build()
19        lldbutil.run_to_source_breakpoint(self, "// break here",
20                lldb.SBFileSpec("main.c"))
21
22    def test_memory_read_c_string(self):
23        """Test that reading memory as a c string respects the size limit given
24           and warns if the null terminator is missing."""
25        self.build_run_stop()
26
27        # The size here is the size in memory so it includes the null terminator.
28        cmd = "memory read --format \"c-string\" --size {} &my_string"
29
30        # Size matches the size of the array.
31        self.expect(cmd.format(8), substrs=['\"abcdefg\"'])
32
33        # If size would take us past the terminator we stop at the terminator.
34        self.expect(cmd.format(10), substrs=['\"abcdefg\"'])
35
36        # Size 3 means 2 chars and a terminator. So we print 2 chars but warn because
37        # the third isn't 0 as expected.
38        self.expect(cmd.format(3), substrs=['\"ab\"'])
39        self.assertRegex(self.res.GetError(),
40            "unable to find a NULL terminated string at 0x[0-9A-Fa-f]+."
41            " Consider increasing the maximum read length.")
42
43    def test_memory_read(self):
44        """Test the 'memory read' command with plain and vector formats."""
45        self.build_run_stop()
46
47        # (lldb) memory read -f d -c 1 `&argc`
48        # 0x7fff5fbff9a0: 1
49        self.runCmd("memory read -f d -c 1 `&argc`")
50
51        # Find the starting address for variable 'argc' to verify later that the
52        # '--format uint32_t[] --size 4 --count 4' option increments the address
53        # correctly.
54        line = self.res.GetOutput().splitlines()[0]
55        items = line.split(':')
56        address = int(items[0], 0)
57        argc = int(items[1], 0)
58        self.assertGreater(address, 0)
59        self.assertEquals(argc, 1)
60
61        # (lldb) memory read --format uint32_t[] --size 4 --count 4 `&argc`
62        # 0x7fff5fbff9a0: {0x00000001}
63        # 0x7fff5fbff9a4: {0x00000000}
64        # 0x7fff5fbff9a8: {0x0ec0bf27}
65        # 0x7fff5fbff9ac: {0x215db505}
66        self.runCmd(
67            "memory read --format uint32_t[] --size 4 --count 4 `&argc`")
68        lines = self.res.GetOutput().splitlines()
69        for i in range(4):
70            if i == 0:
71                # Verify that the printout for argc is correct.
72                self.assertEqual(
73                    argc, int(lines[i].split(':')[1].strip(' {}'), 0))
74            addr = int(lines[i].split(':')[0], 0)
75            # Verify that the printout for addr is incremented correctly.
76            self.assertEqual(addr, (address + i * 4))
77
78        # (lldb) memory read --format char[] --size 7 --count 1 `&my_string`
79        # 0x7fff5fbff990: {abcdefg}
80        self.expect(
81            "memory read --format char[] --size 7 --count 1 `&my_string`",
82            substrs=['abcdefg'])
83
84        # (lldb) memory read --format 'hex float' --size 16 `&argc`
85        # 0x7fff5fbff5b0: error: unsupported byte size (16) for hex float
86        # format
87        self.expect(
88            "memory read --format 'hex float' --size 16 `&argc`",
89            substrs=['unsupported byte size (16) for hex float format'])
90
91        self.expect(
92            "memory read --format 'float' --count 1 --size 8 `&my_double`",
93            substrs=['1234.'])
94
95        # (lldb) memory read --format 'float' --count 1 --size 20 `&my_double`
96        # 0x7fff5fbff598: error: unsupported byte size (20) for float format
97        self.expect(
98            "memory read --format 'float' --count 1 --size 20 `&my_double`",
99            substrs=['unsupported byte size (20) for float format'])
100
101        self.expect('memory read --type int --count 5 `&my_ints[0]`',
102                    substrs=['(int) 0x', '2', '4', '6', '8', '10'])
103
104        self.expect(
105            'memory read --type int --count 5 --format hex `&my_ints[0]`',
106            substrs=[
107                '(int) 0x',
108                '0x',
109                '0a'])
110
111        self.expect(
112            'memory read --type int --count 5 --offset 5 `&my_ints[0]`',
113            substrs=[
114                '(int) 0x',
115                '12',
116                '14',
117                '16',
118                '18',
119                '20'])
120
121        # the gdb format specifier and the size in characters for
122        # the returned values including the 0x prefix.
123        variations = [['b', 4], ['h', 6], ['w', 10], ['g', 18]]
124        for v in variations:
125          formatter = v[0]
126          expected_object_length = v[1]
127          self.runCmd(
128              "memory read --gdb-format 4%s &my_uint64s" % formatter)
129          lines = self.res.GetOutput().splitlines()
130          objects_read = []
131          for l in lines:
132              objects_read.extend(l.split(':')[1].split())
133          # Check that we got back 4 0x0000 etc bytes
134          for o in objects_read:
135              self.assertEqual(len(o), expected_object_length)
136          self.assertEquals(len(objects_read), 4)
137
138    def test_memory_read_file(self):
139        self.build_run_stop()
140        res = lldb.SBCommandReturnObject()
141        self.ci.HandleCommand("memory read -f d -c 1 `&argc`", res)
142        self.assertTrue(res.Succeeded(), "memory read failed:" + res.GetError())
143
144        # Record golden output.
145        golden_output = res.GetOutput()
146
147        memory_read_file = self.getBuildArtifact("memory-read-output")
148
149        def check_file_content(expected):
150            with open(memory_read_file) as f:
151                lines = f.readlines()
152                lines = [s.strip() for s in lines]
153                expected = [s.strip() for s in expected]
154                self.assertEqual(lines, expected)
155
156        # Sanity check.
157        self.runCmd("memory read -f d -c 1 -o '{}' `&argc`".format(memory_read_file))
158        check_file_content([golden_output])
159
160        # Write some garbage to the file.
161        with open(memory_read_file, 'w') as f:
162            f.write("some garbage")
163
164        # Make sure the file is truncated when we run the command again.
165        self.runCmd("memory read -f d -c 1 -o '{}' `&argc`".format(memory_read_file))
166        check_file_content([golden_output])
167
168        # Make sure the file is appended when we run the command with --append-outfile.
169        self.runCmd(
170            "memory read -f d -c 1 -o '{}' --append-outfile `&argc`".format(
171                memory_read_file))
172        check_file_content([golden_output, golden_output])
173