xref: /llvm-project/lldb/test/API/lang/c/register_variables/TestRegisterVariables.py (revision 9c2468821ec51defd09c246fea4a47886fff8c01)
1"""Check that compiler-generated register values work correctly"""
2
3import re
4import lldb
5from lldbsuite.test.decorators import *
6from lldbsuite.test.lldbtest import *
7from lldbsuite.test import lldbutil
8
9
10def re_expr_equals(val_type, val):
11    # Match ({val_type}) ${sum_digits} = {val}
12    return re.compile(r"\(" + val_type + "\) \$\d+ = " + str(val))
13
14
15class RegisterVariableTestCase(TestBase):
16    @expectedFailureAll(compiler="clang", compiler_version=["<", "3.5"])
17    @expectedFailureAll(
18        compiler="gcc", compiler_version=[">=", "4.8.2"], archs=["i386"]
19    )
20    @expectedFailureAll(compiler="gcc", compiler_version=["<", "4.9"], archs=["x86_64"])
21    def test_and_run_command(self):
22        """Test expressions on register values."""
23
24        # This test now ensures that each probable
25        # register variable location is actually a register, and
26        # if so, whether we can print out the variable there.
27        # It only requires one of them to be handled in a non-error
28        # way.
29        register_variables_count = 0
30
31        self.build()
32        exe = self.getBuildArtifact("a.out")
33        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
34
35        # Break inside the main.
36        lldbutil.run_break_set_by_source_regexp(self, "break", num_expected_locations=3)
37
38        ####################
39        # First breakpoint
40
41        self.runCmd("run", RUN_SUCCEEDED)
42
43        # The stop reason of the thread should be breakpoint.
44        self.expect(
45            "thread list",
46            STOPPED_DUE_TO_BREAKPOINT,
47            substrs=["stopped", "stop reason = breakpoint"],
48        )
49
50        # The breakpoint should have a hit count of 1.
51        lldbutil.check_breakpoint(
52            self, bpno=1, location_id=1, expected_location_hit_count=1
53        )
54
55        # Try some variables that should be visible
56        frame = (
57            self.dbg.GetSelectedTarget()
58            .GetProcess()
59            .GetSelectedThread()
60            .GetSelectedFrame()
61        )
62        if self.is_variable_in_register(frame, "a"):
63            register_variables_count += 1
64            self.expect(
65                "expr a",
66                VARIABLES_DISPLAYED_CORRECTLY,
67                patterns=[re_expr_equals("int", 2)],
68            )
69
70        if self.is_struct_pointer_in_register(frame, "b", self.TraceOn()):
71            register_variables_count += 1
72            self.expect(
73                "expr b->m1",
74                VARIABLES_DISPLAYED_CORRECTLY,
75                patterns=[re_expr_equals("int", 3)],
76            )
77
78        #####################
79        # Second breakpoint
80
81        self.runCmd("continue")
82
83        # The stop reason of the thread should be breakpoint.
84        self.expect(
85            "thread list",
86            STOPPED_DUE_TO_BREAKPOINT,
87            substrs=["stopped", "stop reason = breakpoint"],
88        )
89
90        # The breakpoint should have a hit count of 1.
91        lldbutil.check_breakpoint(
92            self, bpno=1, location_id=2, expected_location_hit_count=1
93        )
94
95        # Try some variables that should be visible
96        frame = (
97            self.dbg.GetSelectedTarget()
98            .GetProcess()
99            .GetSelectedThread()
100            .GetSelectedFrame()
101        )
102        if self.is_struct_pointer_in_register(frame, "b", self.TraceOn()):
103            register_variables_count += 1
104            self.expect(
105                "expr b->m2",
106                VARIABLES_DISPLAYED_CORRECTLY,
107                patterns=[re_expr_equals("int", 5)],
108            )
109
110        if self.is_variable_in_register(frame, "c"):
111            register_variables_count += 1
112            self.expect(
113                "expr c",
114                VARIABLES_DISPLAYED_CORRECTLY,
115                patterns=[re_expr_equals("int", 5)],
116            )
117
118        #####################
119        # Third breakpoint
120
121        self.runCmd("continue")
122
123        # The stop reason of the thread should be breakpoint.
124        self.expect(
125            "thread list",
126            STOPPED_DUE_TO_BREAKPOINT,
127            substrs=["stopped", "stop reason = breakpoint"],
128        )
129
130        # The breakpoint should have a hit count of 1.
131        lldbutil.check_breakpoint(
132            self, bpno=1, location_id=3, expected_location_hit_count=1
133        )
134
135        # Try some variables that should be visible
136        frame = (
137            self.dbg.GetSelectedTarget()
138            .GetProcess()
139            .GetSelectedThread()
140            .GetSelectedFrame()
141        )
142        if self.is_variable_in_register(frame, "f"):
143            register_variables_count += 1
144            self.expect(
145                "expr f",
146                VARIABLES_DISPLAYED_CORRECTLY,
147                patterns=[re_expr_equals("float", "3.1")],
148            )
149
150        # Validate that we verified at least one register variable
151        self.assertGreater(
152            register_variables_count,
153            0,
154            "expected to verify at least one variable in a register",
155        )
156        self.trace(
157            "executed {} expressions with values in registers".format(
158                register_variables_count
159            )
160        )
161
162        self.runCmd("kill")
163
164    def is_variable_in_register(self, frame, var_name):
165        # Ensure we can lookup the variable.
166        var = frame.FindVariable(var_name)
167        self.trace("\nchecking {}...".format(var_name))
168        if var is None or not var.IsValid():
169            self.trace("{} cannot be found".format(var_name))
170            return False
171
172        # Check that we can get its value.  If not, this
173        # may be a variable that is just out of scope at this point.
174        value = var.GetValue()
175        self.trace("checking value...")
176        if value is None:
177            self.trace("value is invalid")
178            return False
179        else:
180            self.trace("value is {}".format(value))
181
182        # We have a variable and we can get its value.  The variable is in a
183        # register if we cannot get an address for it, assuming it is not a
184        # struct pointer.  (This is an approximation - compilers can do other
185        # things with spitting up a value into multiple parts of multiple
186        # registers, but what we're verifying here is much more than it was
187        # doing before).
188        var_addr = var.GetAddress()
189        self.trace("checking address...")
190        if var_addr.IsValid():
191            # We have an address, it must not be in a register.
192            self.trace(
193                "var {} is not in a register: has a valid address {}".format(
194                    var_name, var_addr
195                )
196            )
197            return False
198        else:
199            # We don't have an address but we can read the value.
200            # It is likely stored in a register.
201            self.trace(
202                "var {} is in a register (we don't have an address for it)".format(
203                    var_name
204                )
205            )
206            return True
207
208    def is_struct_pointer_in_register(self, frame, var_name, trace):
209        # Ensure we can lookup the variable.
210        var = frame.FindVariable(var_name)
211        if trace:
212            print("\nchecking {}...".format(var_name))
213
214        if var is None or not var.IsValid():
215            self.trace("{} cannot be found".format(var_name))
216            return False
217
218        # Check that we can get its value.  If not, this
219        # may be a variable that is just out of scope at this point.
220        value = var.GetValue()
221        self.trace("checking value...")
222        if value is None:
223            if trace:
224                print("value is invalid")
225            return False
226        else:
227            if trace:
228                print("value is {}".format(value))
229
230        var_loc = var.GetLocation()
231        if trace:
232            print("checking location: {}".format(var_loc))
233        if var_loc is None or var_loc.startswith("0x"):
234            # The frame var is not in a register but rather a memory location.
235            self.trace("frame var {} is not in a register".format(var_name))
236            return False
237        else:
238            self.trace("frame var {} is in a register".format(var_name))
239            return True
240