diff options
author | Jim Ingham <jingham@apple.com> | 2011-11-01 02:46:54 +0000 |
---|---|---|
committer | Jim Ingham <jingham@apple.com> | 2011-11-01 02:46:54 +0000 |
commit | ce553d885abcce514e52618f0fd44f3c6940f171 (patch) | |
tree | 6eeb3eec23ea8d3a73cc27b1ed27f04efcc16cf3 | |
parent | fc08bdcc5761b64b0affc5ba9d7ab19812402089 (diff) | |
download | bcm5719-llvm-ce553d885abcce514e52618f0fd44f3c6940f171.tar.gz bcm5719-llvm-ce553d885abcce514e52618f0fd44f3c6940f171.zip |
Enhanced the ObjC DynamicCheckerFunction to test for "object responds to selector" as well as
"object borked"... Also made the error when the checker fails reflect this fact rather than
report a crash at 0x0.
Also a little cleanup:
- StopInfoMachException had a redundant copy of the description string.
- ThreadPlanCallFunction had a redundant copy of the thread, and had a
copy of the process that it didn't really need.
llvm-svn: 143419
11 files changed, 215 insertions, 33 deletions
diff --git a/lldb/include/lldb/Expression/IRDynamicChecks.h b/lldb/include/lldb/Expression/IRDynamicChecks.h index ab72cb1c6f7..a4c406446af 100644 --- a/lldb/include/lldb/Expression/IRDynamicChecks.h +++ b/lldb/include/lldb/Expression/IRDynamicChecks.h @@ -10,6 +10,7 @@ #ifndef liblldb_IRDynamicChecks_h_ #define liblldb_IRDynamicChecks_h_ +#include "lldb-types.h" #include "llvm/Pass.h" namespace llvm { @@ -74,6 +75,8 @@ public: bool Install (Stream &error_stream, ExecutionContext &exe_ctx); + bool DoCheckersExplainStop (lldb::addr_t addr, Stream &message); + std::auto_ptr<ClangUtilityFunction> m_valid_pointer_check; std::auto_ptr<ClangUtilityFunction> m_objc_object_check; }; diff --git a/lldb/include/lldb/Target/ThreadPlanCallFunction.h b/lldb/include/lldb/Target/ThreadPlanCallFunction.h index 76e0923214b..16d249d56ce 100644 --- a/lldb/include/lldb/Target/ThreadPlanCallFunction.h +++ b/lldb/include/lldb/Target/ThreadPlanCallFunction.h @@ -127,6 +127,12 @@ public: { return m_real_stop_info_sp; } + + lldb::addr_t + GetStopAddress () + { + return m_stop_address; + } protected: void ReportRegisterState (const char *message); @@ -148,8 +154,8 @@ private: Address m_function_addr; Address m_start_addr; lldb::addr_t m_function_sp; - Process &m_process; - Thread &m_thread; +// Process &m_process; +// Thread &m_thread; Thread::RegisterCheckpoint m_register_backup; lldb::ThreadPlanSP m_subplan_sp; LanguageRuntime *m_cxx_language_runtime; @@ -161,6 +167,7 @@ private: // This gets set in DoTakedown. lldb::ValueSP m_return_value_sp; // If this contains a valid pointer, use the ABI to extract values when complete bool m_takedown_done; // We want to ensure we only do the takedown once. This ensures that. + lldb::addr_t m_stop_address; // This is the address we stopped at. Also set in DoTakedown; DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallFunction); }; diff --git a/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h b/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h index 8e93d0dc7bf..c2f4ab6bbb8 100644 --- a/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h +++ b/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h @@ -48,6 +48,9 @@ public: m_user_expression_sp.reset(); } + virtual lldb::StopInfoSP + GetRealStopInfo(); + protected: private: ClangUserExpression::ClangUserExpressionSP m_user_expression_sp; // This is currently just used to ensure the diff --git a/lldb/source/Expression/IRDynamicChecks.cpp b/lldb/source/Expression/IRDynamicChecks.cpp index 17a91ee8897..1d6637dd033 100644 --- a/lldb/source/Expression/IRDynamicChecks.cpp +++ b/lldb/source/Expression/IRDynamicChecks.cpp @@ -74,6 +74,25 @@ DynamicCheckerFunctions::Install(Stream &error_stream, return true; } +bool +DynamicCheckerFunctions::DoCheckersExplainStop (lldb::addr_t addr, Stream &message) +{ + // FIXME: We have to get the checkers to know why they scotched the call in more detail, + // so we can print a better message here. + if (m_valid_pointer_check.get() != NULL && m_valid_pointer_check->ContainsAddress(addr)) + { + message.Printf ("Attempted to dereference an invalid pointer."); + return true; + } + else if (m_objc_object_check.get() != NULL && m_objc_object_check->ContainsAddress(addr)) + { + message.Printf ("Attempted to dereference an invalid ObjC Object or send it an unrecognized selector"); + return true; + } + return false; +} + + static std::string PrintValue(llvm::Value *V, bool truncate = false) { diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index 9d774d0dd52..1b72409e477 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -99,7 +99,7 @@ AppleObjCRuntimeV2::AppleObjCRuntimeV2 (Process *process, m_isa_to_name_cache(), m_isa_to_parent_cache() { - m_has_object_getClass = (objc_module_sp->FindFirstSymbolWithNameAndType(ConstString("gdb_object_getClass")) != NULL); + m_has_object_getClass = (objc_module_sp->FindFirstSymbolWithNameAndType(ConstString("gdb_object_getClass"), eSymbolTypeCode) != NULL); } bool @@ -492,38 +492,56 @@ AppleObjCRuntimeV2::SetExceptionBreakpoints () ClangUtilityFunction * AppleObjCRuntimeV2::CreateObjectChecker(const char *name) { - char check_function_code[1024]; + char check_function_code[2048]; int len = 0; if (m_has_object_getClass) { len = ::snprintf (check_function_code, sizeof(check_function_code), - "extern \"C\" void *gdb_object_getClass(void *); \n" - "extern \"C\" void \n" - "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) \n" - "{ \n" - " if ($__lldb_arg_obj == (void *)0) \n" - " return; // nil is ok \n" - " if (!gdb_object_getClass($__lldb_arg_obj)) \n" - " *((volatile int *)0) = 'ocgc'; \n" - "} \n", + "extern \"C\" void *gdb_object_getClass(void *); \n" + "extern \"C\" int printf(const char *format, ...); \n" + "extern \"C\" void \n" + "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) \n" + "{ \n" + " if ($__lldb_arg_obj == (void *)0) \n" + " return; // nil is ok \n" + " if (!gdb_object_getClass($__lldb_arg_obj)) \n" + " *((volatile int *)0) = 'ocgc'; \n" + " else if ($__lldb_arg_selector != (void *)0) \n" + " { \n" + " signed char responds = (signed char) [(id) $__lldb_arg_obj \n" + " respondsToSelector: \n" + " (struct objc_selector *) $__lldb_arg_selector]; \n" + " if (responds == (signed char) 0) \n" + " *((volatile int *)0) = 'ocgc'; \n" + " } \n" + "} \n", name); } else { len = ::snprintf (check_function_code, sizeof(check_function_code), - "extern \"C\" void *gdb_class_getClass(void *); \n" - "extern \"C\" void \n" - "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) \n" - "{ \n" - " if ($__lldb_arg_obj == (void *)0) \n" - " return; // nil is ok \n" - " void **$isa_ptr = (void **)$__lldb_arg_obj; \n" - " if (*$isa_ptr == (void *)0 || !gdb_class_getClass(*$isa_ptr)) \n" - " *((volatile int *)0) = 'ocgc'; \n" - "} \n", + "extern \"C\" void *gdb_class_getClass(void *); \n" + "extern \"C\" int printf(const char *format, ...); \n" + "extern \"C\" void \n" + "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) \n" + "{ \n" + " if ($__lldb_arg_obj == (void *)0) \n" + " return; // nil is ok \n" + " void **$isa_ptr = (void **)$__lldb_arg_obj; \n" + " if (*$isa_ptr == (void *)0 || !gdb_class_getClass(*$isa_ptr)) \n" + " *((volatile int *)0) = 'ocgc'; \n" + " else if ($__lldb_arg_selector != (void *)0) \n" + " { \n" + " signed char responds = (signed char) [(id) $__lldb_arg_obj \n" + " respondsToSelector: \n" + " (struct objc_selector *) $__lldb_arg_selector]; \n" + " if (responds == (signed char) 0) \n" + " *((volatile int *)0) = 'ocgc'; \n" + " } \n" + "} \n", name); } diff --git a/lldb/source/Plugins/Process/Utility/StopInfoMachException.h b/lldb/source/Plugins/Process/Utility/StopInfoMachException.h index 9ddf7a530ce..a40bd71a91a 100644 --- a/lldb/source/Plugins/Process/Utility/StopInfoMachException.h +++ b/lldb/source/Plugins/Process/Utility/StopInfoMachException.h @@ -67,7 +67,6 @@ protected: uint32_t m_exc_data_count; uint64_t m_exc_code; uint64_t m_exc_subcode; - std::string m_description; }; diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp index a5eb4b96a06..2494c7cacfd 100644 --- a/lldb/source/Target/ThreadPlanCallFunction.cpp +++ b/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -47,9 +47,8 @@ ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread, m_stop_other_threads (stop_other_threads), m_function_addr (function), m_function_sp (NULL), - m_process (thread.GetProcess()), - m_thread (thread), - m_takedown_done (false) + m_takedown_done (false), + m_stop_address (LLDB_INVALID_ADDRESS) { SetOkayToDiscard (discard_on_error); @@ -163,8 +162,6 @@ ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread, m_stop_other_threads (stop_other_threads), m_function_addr (function), m_function_sp(NULL), - m_process (thread.GetProcess()), - m_thread (thread), m_takedown_done (false) { SetOkayToDiscard (discard_on_error); @@ -294,6 +291,7 @@ ThreadPlanCallFunction::DoTakedown () if (log) log->Printf ("DoTakedown called for thread 0x%4.4llx, m_valid: %d complete: %d.\n", m_thread.GetID(), m_valid, IsPlanComplete()); m_takedown_done = true; + m_stop_address = m_thread.GetStackFrameAtIndex(0)->GetRegisterContext()->GetPC(); m_real_stop_info_sp = GetPrivateStopReason(); m_thread.RestoreThreadStateFromCheckpoint(m_stored_thread_state); SetPlanComplete(); @@ -324,7 +322,7 @@ ThreadPlanCallFunction::GetDescription (Stream *s, DescriptionLevel level) } else { - s->Printf("Thread plan to call 0x%llx", m_function_addr.GetLoadAddress(&m_process.GetTarget())); + s->Printf("Thread plan to call 0x%llx", m_function_addr.GetLoadAddress(&m_thread.GetProcess().GetTarget())); } } @@ -474,8 +472,8 @@ ThreadPlanCallFunction::MischiefManaged () void ThreadPlanCallFunction::SetBreakpoints () { - m_cxx_language_runtime = m_process.GetLanguageRuntime(eLanguageTypeC_plus_plus); - m_objc_language_runtime = m_process.GetLanguageRuntime(eLanguageTypeObjC); + m_cxx_language_runtime = m_thread.GetProcess().GetLanguageRuntime(eLanguageTypeC_plus_plus); + m_objc_language_runtime = m_thread.GetProcess().GetLanguageRuntime(eLanguageTypeObjC); if (m_cxx_language_runtime) m_cxx_language_runtime->SetExceptionBreakpoints(); diff --git a/lldb/source/Target/ThreadPlanCallUserExpression.cpp b/lldb/source/Target/ThreadPlanCallUserExpression.cpp index dc27b2be200..0499a7c7ce4 100644 --- a/lldb/source/Target/ThreadPlanCallUserExpression.cpp +++ b/lldb/source/Target/ThreadPlanCallUserExpression.cpp @@ -55,6 +55,20 @@ ThreadPlanCallUserExpression::~ThreadPlanCallUserExpression () void ThreadPlanCallUserExpression::GetDescription (Stream *s, lldb::DescriptionLevel level) -{ +{ ThreadPlanCallFunction::GetDescription (s, level); } + +StopInfoSP +ThreadPlanCallUserExpression::GetRealStopInfo() +{ + StopInfoSP stop_info_sp = ThreadPlanCallFunction::GetRealStopInfo(); + lldb::addr_t addr = GetStopAddress(); + DynamicCheckerFunctions *checkers = m_thread.GetProcess().GetDynamicCheckers(); + StreamString s; + + if (checkers && checkers->DoCheckersExplainStop(addr, s)) + stop_info_sp->SetDescription(s.GetData()); + + return stop_info_sp; +} diff --git a/lldb/test/lang/objc/objc-checker/Makefile b/lldb/test/lang/objc/objc-checker/Makefile new file mode 100644 index 00000000000..a1608fe5a66 --- /dev/null +++ b/lldb/test/lang/objc/objc-checker/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/lang/objc/objc-checker/TestObjCCheckers.py b/lldb/test/lang/objc/objc-checker/TestObjCCheckers.py new file mode 100644 index 00000000000..04ae9e26d1c --- /dev/null +++ b/lldb/test/lang/objc/objc-checker/TestObjCCheckers.py @@ -0,0 +1,85 @@ +""" +Use lldb Python API to make sure the dynamic checkers are doing their jobs. +""" + +import os, time +import re +import unittest2 +import lldb, lldbutil +from lldbtest import * + +class ObjCDynamicValueTestCase(TestBase): + + mydir = os.path.join("lang", "objc", "objc-checker") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @python_api_test + def test_get_dynamic_objc_vals_with_dsym(self): + """Test that checkers catch unrecognized selectors""" + self.buildDsym() + self.do_test_checkers() + + @python_api_test + def test_get_objc_dynamic_vals_with_dwarf(self): + """Test that checkers catch unrecognized selectors""" + self.buildDwarf() + self.do_test_checkers() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + # Find the line number to break for main.c. + + self.source_name = 'main.m' + + def do_test_checkers (self): + """Make sure the dynamic checkers catch messages to unrecognized selectors""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target from the debugger. + + target = self.dbg.CreateTarget (exe) + self.assertTrue(target, VALID_TARGET) + + # Set up our breakpoints: + + + main_bkpt = target.BreakpointCreateBySourceRegex ("Set a breakpoint here.", lldb.SBFileSpec (self.source_name)) + self.assertTrue(main_bkpt and + main_bkpt.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, os.getcwd()) + + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + threads = lldbutil.get_threads_stopped_at_breakpoint (process, main_bkpt) + self.assertTrue (len(threads) == 1) + thread = threads[0] + + # + # The class Simple doesn't have a count method. Make sure that we don't + # actually try to send count but catch it as an unrecognized selector. + + frame = thread.GetFrameAtIndex(0) + expr_value = frame.EvaluateExpression("(int) [my_simple count]", False) + expr_error = expr_value.GetError() + + self.assertTrue (expr_error.Fail()) + + # Make sure the call produced no NSLog stdout. + stdout = process.GetSTDOUT(100) + self.assertTrue (len(stdout) == 0) + + # Make sure the error is helpful: + err_string = expr_error.GetCString() + self.assertTrue ("selector" in err_string) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/lang/objc/objc-checker/main.m b/lldb/test/lang/objc/objc-checker/main.m new file mode 100644 index 00000000000..a52ce93e9e7 --- /dev/null +++ b/lldb/test/lang/objc/objc-checker/main.m @@ -0,0 +1,30 @@ +#import <Foundation/Foundation.h> + +@interface Simple : NSObject +{ + int _value; +} +- (int) value; +- (void) setValue: (int) newValue; +@end + +@implementation Simple +- (int) value +{ + return _value; +} + +- (void) setValue: (int) newValue +{ + _value = newValue; +} +@end + +int main () +{ + Simple *my_simple = [[Simple alloc] init]; + my_simple.value = 20; + // Set a breakpoint here. + NSLog (@"Object has value: %d.", my_simple.value); + return 0; +} |