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