1# encoding: utf-8 2""" 3Test lldb Obj-C exception support. 4""" 5 6 7import lldb 8from lldbsuite.test.decorators import * 9from lldbsuite.test.lldbtest import * 10from lldbsuite.test import lldbutil 11 12 13class ObjCExceptionsTestCase(TestBase): 14 @skipIf(compiler="clang", compiler_version=["<", "13.0"]) 15 def test_objc_exceptions_at_throw(self): 16 self.build() 17 18 target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) 19 self.assertTrue(target, VALID_TARGET) 20 21 launch_info = lldb.SBLaunchInfo(["a.out", "0"]) 22 launch_info.SetLaunchFlags(lldb.eLaunchFlagInheritTCCFromParent) 23 lldbutil.run_to_name_breakpoint( 24 self, "objc_exception_throw", launch_info=launch_info 25 ) 26 27 self.expect( 28 "thread list", 29 substrs=["stopped", "stop reason = hit Objective-C exception"], 30 ) 31 32 self.expect( 33 "thread exception", 34 substrs=[ 35 "(NSException *) exception = ", 36 '"SomeReason"', 37 ], 38 ) 39 40 target = self.dbg.GetSelectedTarget() 41 thread = target.GetProcess().GetSelectedThread() 42 frame = thread.GetSelectedFrame() 43 44 opts = lldb.SBVariablesOptions() 45 opts.SetIncludeRecognizedArguments(True) 46 variables = frame.GetVariables(opts) 47 48 self.assertEqual(variables.GetSize(), 1) 49 self.assertEqual(variables.GetValueAtIndex(0).name, "exception") 50 self.assertEqual( 51 variables.GetValueAtIndex(0).GetValueType(), lldb.eValueTypeVariableArgument 52 ) 53 54 lldbutil.run_to_source_breakpoint( 55 self, 56 "// Set break point at this line.", 57 lldb.SBFileSpec("main.mm"), 58 launch_info=launch_info, 59 ) 60 61 self.expect( 62 "thread list", 63 STOPPED_DUE_TO_BREAKPOINT, 64 substrs=["stopped", "stop reason = breakpoint"], 65 ) 66 67 target = self.dbg.GetSelectedTarget() 68 thread = target.GetProcess().GetSelectedThread() 69 frame = thread.GetSelectedFrame() 70 71 # No exception being currently thrown/caught at this point 72 self.assertFalse(thread.GetCurrentException().IsValid()) 73 self.assertFalse(thread.GetCurrentExceptionBacktrace().IsValid()) 74 75 self.expect( 76 "frame variable e1", substrs=["(NSException *) e1 = ", '"SomeReason"'] 77 ) 78 79 self.expect( 80 "frame variable *e1", 81 substrs=[ 82 "(NSException) *e1 = ", 83 "name = ", 84 '"ExceptionName"', 85 "reason = ", 86 '"SomeReason"', 87 "userInfo = ", 88 "1 key/value pair", 89 "reserved = ", 90 ], 91 ) 92 93 e1 = frame.FindVariable("e1") 94 self.assertTrue(e1) 95 self.assertEqual(e1.type.name, "NSException *") 96 self.assertEqual(e1.GetSummary(), '"SomeReason"') 97 self.assertEqual(e1.GetChildMemberWithName("name").description, "ExceptionName") 98 self.assertEqual(e1.GetChildMemberWithName("reason").description, "SomeReason") 99 userInfo = e1.GetChildMemberWithName("userInfo").dynamic 100 self.assertEqual(userInfo.summary, "1 key/value pair") 101 self.assertEqual( 102 userInfo.GetChildAtIndex(0).GetChildAtIndex(0).description, "some_key" 103 ) 104 self.assertEqual( 105 userInfo.GetChildAtIndex(0).GetChildAtIndex(1).description, "some_value" 106 ) 107 108 self.expect( 109 "frame variable e2", substrs=["(NSException *) e2 = ", '"SomeReason"'] 110 ) 111 112 self.expect( 113 "frame variable *e2", 114 substrs=[ 115 "(NSException) *e2 = ", 116 "name = ", 117 '"ThrownException"', 118 "reason = ", 119 '"SomeReason"', 120 "userInfo = ", 121 "1 key/value pair", 122 "reserved = ", 123 ], 124 ) 125 126 e2 = frame.FindVariable("e2") 127 self.assertTrue(e2) 128 self.assertEqual(e2.type.name, "NSException *") 129 self.assertEqual(e2.GetSummary(), '"SomeReason"') 130 self.assertEqual( 131 e2.GetChildMemberWithName("name").description, "ThrownException" 132 ) 133 self.assertEqual(e2.GetChildMemberWithName("reason").description, "SomeReason") 134 userInfo = e2.GetChildMemberWithName("userInfo").dynamic 135 self.assertEqual(userInfo.summary, "1 key/value pair") 136 self.assertEqual( 137 userInfo.GetChildAtIndex(0).GetChildAtIndex(0).description, "some_key" 138 ) 139 self.assertEqual( 140 userInfo.GetChildAtIndex(0).GetChildAtIndex(1).description, "some_value" 141 ) 142 reserved = e2.GetChildMemberWithName("reserved").dynamic 143 self.assertGreater(reserved.num_children, 0) 144 callStackReturnAddresses = [ 145 reserved.GetChildAtIndex(i).GetChildAtIndex(1) 146 for i in range(0, reserved.GetNumChildren()) 147 if reserved.GetChildAtIndex(i).GetChildAtIndex(0).description 148 == "callStackReturnAddresses" 149 ][0].dynamic 150 children = [ 151 callStackReturnAddresses.GetChildAtIndex(i) 152 for i in range(0, callStackReturnAddresses.num_children) 153 ] 154 155 pcs = [i.unsigned for i in children] 156 names = [ 157 target.ResolveSymbolContextForAddress( 158 lldb.SBAddress(pc, target), lldb.eSymbolContextSymbol 159 ) 160 .GetSymbol() 161 .name 162 for pc in pcs 163 ] 164 for n in ["objc_exception_throw", "foo(int)", "main"]: 165 self.assertIn( 166 n, names, "%s is in the exception backtrace (%s)" % (n, names) 167 ) 168 169 @skipIf(compiler="clang", compiler_version=["<", "13.0"]) 170 def test_objc_exceptions_at_abort(self): 171 self.build() 172 173 target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) 174 self.assertTrue(target, VALID_TARGET) 175 176 self.runCmd("run 0") 177 178 # We should be stopped at pthread_kill because of an unhandled exception 179 self.expect("thread list", substrs=["stopped", "stop reason = signal SIGABRT"]) 180 181 self.expect( 182 "thread exception", 183 substrs=[ 184 "(NSException *) exception = ", 185 '"SomeReason"', 186 "libobjc.A.dylib`objc_exception_throw", 187 "a.out`foo", 188 "at main.mm:16", 189 "a.out`rethrow", 190 "at main.mm:27", 191 "a.out`main", 192 ], 193 ) 194 195 process = self.dbg.GetSelectedTarget().process 196 thread = process.GetSelectedThread() 197 198 # There is an exception being currently processed at this point 199 self.assertTrue(thread.GetCurrentException().IsValid()) 200 self.assertTrue(thread.GetCurrentExceptionBacktrace().IsValid()) 201 202 history_thread = thread.GetCurrentExceptionBacktrace() 203 self.assertGreaterEqual(history_thread.num_frames, 4) 204 for n in ["objc_exception_throw", "foo(int)", "rethrow(int)", "main"]: 205 self.assertEqual( 206 len([f for f in history_thread.frames if f.GetFunctionName() == n]), 1 207 ) 208 209 self.runCmd("kill") 210 211 self.runCmd("run 1") 212 # We should be stopped at pthread_kill because of an unhandled exception 213 self.expect("thread list", substrs=["stopped", "stop reason = signal SIGABRT"]) 214 215 self.expect( 216 "thread exception", 217 substrs=[ 218 "(MyCustomException *) exception = ", 219 "libobjc.A.dylib`objc_exception_throw", 220 "a.out`foo", 221 "at main.mm:18", 222 "a.out`rethrow", 223 "at main.mm:27", 224 "a.out`main", 225 ], 226 ) 227 228 process = self.dbg.GetSelectedTarget().process 229 thread = process.GetSelectedThread() 230 231 history_thread = thread.GetCurrentExceptionBacktrace() 232 self.assertGreaterEqual(history_thread.num_frames, 4) 233 for n in ["objc_exception_throw", "foo(int)", "rethrow(int)", "main"]: 234 self.assertEqual( 235 len([f for f in history_thread.frames if f.GetFunctionName() == n]), 1 236 ) 237 238 @skipIf(compiler="clang", compiler_version=["<", "13.0"]) 239 def test_cxx_exceptions_at_abort(self): 240 self.build() 241 242 target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) 243 self.assertTrue(target, VALID_TARGET) 244 245 self.runCmd("run 2") 246 247 # We should be stopped at pthread_kill because of an unhandled exception 248 self.expect("thread list", substrs=["stopped", "stop reason = signal SIGABRT"]) 249 250 self.expect("thread exception", substrs=["exception ="]) 251 252 process = self.dbg.GetSelectedTarget().process 253 thread = process.GetSelectedThread() 254 255 self.assertTrue(thread.GetCurrentException().IsValid()) 256 257 # C++ exception backtraces are not exposed in the API (yet). 258 self.assertFalse(thread.GetCurrentExceptionBacktrace().IsValid()) 259