1""" 2Test dwim-print with variables, variable paths, and expressions. 3""" 4 5import re 6import lldb 7from lldbsuite.test.lldbtest import * 8from lldbsuite.test.decorators import * 9import lldbsuite.test.lldbutil as lldbutil 10 11 12class TestCase(TestBase): 13 def _run_cmd(self, cmd: str) -> str: 14 """Run the given lldb command and return its output.""" 15 result = lldb.SBCommandReturnObject() 16 self.ci.HandleCommand(cmd, result) 17 return result.GetOutput().rstrip() 18 19 VAR_IDENT = re.compile(r"(?:\$\d+|[\w.]+) = ") 20 21 def _strip_result_var(self, string: str) -> str: 22 """ 23 Strip (persistent) result variables (ex '$0 = ', or 'someVar = ', etc). 24 25 This allows for using the output of `expression`/`frame variable`, to 26 compare it to `dwim-print` output, which disables result variables. 27 """ 28 return self.VAR_IDENT.subn("", string, 1)[0] 29 30 def _expect_cmd( 31 self, 32 dwim_cmd: str, 33 actual_cmd: str, 34 ) -> None: 35 """Run dwim-print and verify the output against the expected command.""" 36 # Resolve the dwim-print command to either `expression` or `frame variable`. 37 substitute_cmd = dwim_cmd.replace("dwim-print", actual_cmd, 1) 38 interp = self.dbg.GetCommandInterpreter() 39 result = lldb.SBCommandReturnObject() 40 interp.ResolveCommand(substitute_cmd, result) 41 self.assertTrue(result.Succeeded(), result.GetError()) 42 43 resolved_cmd = result.GetOutput() 44 if actual_cmd == "frame variable": 45 resolved_cmd = resolved_cmd.replace(" -- ", " ", 1) 46 47 resolved_cmd_output = self._run_cmd(resolved_cmd) 48 dwim_cmd_output = self._strip_result_var(resolved_cmd_output) 49 50 # Verify dwim-print chose the expected command. 51 self.runCmd("settings set dwim-print-verbosity full") 52 53 self.expect( 54 dwim_cmd, 55 substrs=[ 56 f"note: ran `{resolved_cmd}`", 57 dwim_cmd_output, 58 ], 59 ) 60 61 def test_variables(self): 62 """Test dwim-print with variables.""" 63 self.build() 64 lldbutil.run_to_name_breakpoint(self, "main") 65 vars = ("argc", "argv") 66 for var in vars: 67 self._expect_cmd(f"dwim-print {var}", "frame variable") 68 69 def test_variable_paths(self): 70 """Test dwim-print with variable path expressions.""" 71 self.build() 72 lldbutil.run_to_name_breakpoint(self, "main") 73 exprs = ("&argc", "*argv", "argv[0]") 74 for expr in exprs: 75 self._expect_cmd(f"dwim-print {expr}", "expression") 76 77 def test_expressions(self): 78 """Test dwim-print with expressions.""" 79 self.build() 80 lldbutil.run_to_name_breakpoint(self, "main") 81 exprs = ("argc + 1", "(void)argc", "(int)abs(argc)") 82 for expr in exprs: 83 self._expect_cmd(f"dwim-print {expr}", "expression") 84 85 def test_dummy_target_expressions(self): 86 """Test dwim-print's ability to evaluate expressions without a target.""" 87 self._expect_cmd("dwim-print 1 + 2", "expression") 88 89 def test_gdb_format(self): 90 self.build() 91 lldbutil.run_to_name_breakpoint(self, "main") 92 self._expect_cmd(f"dwim-print/x argc", "frame variable") 93 self._expect_cmd(f"dwim-print/x argc + 1", "expression") 94 95 def test_format_flags(self): 96 self.build() 97 lldbutil.run_to_name_breakpoint(self, "main") 98 self._expect_cmd(f"dwim-print -fx -- argc", "frame variable") 99 self._expect_cmd(f"dwim-print -fx -- argc + 1", "expression") 100 101 def test_display_flags(self): 102 self.build() 103 lldbutil.run_to_name_breakpoint(self, "main") 104 self._expect_cmd(f"dwim-print -T -- argc", "frame variable") 105 self._expect_cmd(f"dwim-print -T -- argc + 1", "expression") 106 107 def test_expression_language(self): 108 """Test that the language flag doesn't affect the choice of command.""" 109 self.build() 110 lldbutil.run_to_name_breakpoint(self, "main") 111 self._expect_cmd(f"dwim-print -l c++ -- argc", "frame variable") 112 self._expect_cmd(f"dwim-print -l c++ -- argc + 1", "expression") 113 114 def test_empty_expression(self): 115 self.build() 116 lldbutil.run_to_name_breakpoint(self, "main") 117 error_msg = "error: 'dwim-print' takes a variable or expression" 118 self.expect(f"dwim-print", error=True, startstr=error_msg) 119 self.expect(f"dwim-print -- ", error=True, startstr=error_msg) 120 121 def test_nested_values(self): 122 """Test dwim-print with nested values (structs, etc).""" 123 self.build() 124 lldbutil.run_to_source_breakpoint( 125 self, "break here", lldb.SBFileSpec("main.cpp") 126 ) 127 self.runCmd("settings set auto-one-line-summaries false") 128 self._expect_cmd(f"dwim-print s", "frame variable") 129 self._expect_cmd(f"dwim-print (struct Structure)s", "expression") 130 131 def test_summary_strings(self): 132 """Test dwim-print with type summaries.""" 133 self.build() 134 lldbutil.run_to_source_breakpoint( 135 self, "break here", lldb.SBFileSpec("main.cpp") 136 ) 137 self.runCmd("settings set auto-one-line-summaries false") 138 self.runCmd("type summary add -e -s 'stub summary' Structure") 139 self._expect_cmd(f"dwim-print s", "frame variable") 140 self._expect_cmd(f"dwim-print (struct Structure)s", "expression") 141 self.runCmd("type summary delete Structure") 142 143 def test_void_result(self): 144 """Test dwim-print does not surface an error message for void expressions.""" 145 self.build() 146 lldbutil.run_to_source_breakpoint( 147 self, "break here", lldb.SBFileSpec("main.cpp") 148 ) 149 self.expect("dwim-print (void)15", matching=False, patterns=["(?i)error"]) 150 151 def test_preserves_persistent_variables(self): 152 """Test dwim-print does not delete persistent variables.""" 153 self.build() 154 lldbutil.run_to_source_breakpoint( 155 self, "break here", lldb.SBFileSpec("main.cpp") 156 ) 157 self.expect("dwim-print int $i = 15") 158 # Run the same expression twice and verify success. This ensures the 159 # first command does not delete the persistent variable. 160 for _ in range(2): 161 self.expect("dwim-print $i", startstr="(int) 15") 162 163 def test_missing_type(self): 164 """The expected output of po opaque is its address (no error)""" 165 self.build() 166 lldbutil.run_to_source_breakpoint( 167 self, "break here", lldb.SBFileSpec("main.cpp") 168 ) 169 self.expect("dwim-print -O -- opaque", substrs=["0x"]) 170 171 def test_variable_expression_path(self): 172 """Test dwim-print supports certain variable expression paths.""" 173 self.build() 174 lldbutil.run_to_source_breakpoint( 175 self, "break here", lldb.SBFileSpec("main.cpp") 176 ) 177 self.runCmd("settings set auto-one-line-summaries false") 178 self._expect_cmd("dwim-print w.s", "frame variable") 179 self._expect_cmd("dwim-print wp->s", "expression") 180 181 def test_direct_child_access(self): 182 """Test dwim-print supports accessing members/ivars without qualification.""" 183 self.build() 184 lldbutil.run_to_source_breakpoint( 185 self, "break inside", lldb.SBFileSpec("main.cpp") 186 ) 187 self._expect_cmd("dwim-print number", "frame variable") 188