259 lines · python
1# encoding: utf-82"""3Test lldb Obj-C exception support.4"""5 6 7import lldb8from lldbsuite.test.decorators import *9from lldbsuite.test.lldbtest import *10from lldbsuite.test import lldbutil11 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_info25 )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.eValueTypeVariableArgument52 )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 point72 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").dynamic100 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").dynamic135 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").dynamic143 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).description148 == "callStackReturnAddresses"149 ][0].dynamic150 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.eSymbolContextSymbol159 )160 .GetSymbol()161 .name162 for pc in pcs163 ]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 exception179 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().process196 thread = process.GetSelectedThread()197 198 # There is an exception being currently processed at this point199 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]), 1207 )208 209 self.runCmd("kill")210 211 self.runCmd("run 1")212 # We should be stopped at pthread_kill because of an unhandled exception213 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().process229 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]), 1236 )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 exception248 self.expect("thread list", substrs=["stopped", "stop reason = signal SIGABRT"])249 250 self.expect("thread exception", substrs=["exception ="])251 252 process = self.dbg.GetSelectedTarget().process253 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