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