summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Ingham <jingham@apple.com>2011-11-01 02:46:54 +0000
committerJim Ingham <jingham@apple.com>2011-11-01 02:46:54 +0000
commitce553d885abcce514e52618f0fd44f3c6940f171 (patch)
tree6eeb3eec23ea8d3a73cc27b1ed27f04efcc16cf3
parentfc08bdcc5761b64b0affc5ba9d7ab19812402089 (diff)
downloadbcm5719-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
-rw-r--r--lldb/include/lldb/Expression/IRDynamicChecks.h3
-rw-r--r--lldb/include/lldb/Target/ThreadPlanCallFunction.h11
-rw-r--r--lldb/include/lldb/Target/ThreadPlanCallUserExpression.h3
-rw-r--r--lldb/source/Expression/IRDynamicChecks.cpp19
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp60
-rw-r--r--lldb/source/Plugins/Process/Utility/StopInfoMachException.h1
-rw-r--r--lldb/source/Target/ThreadPlanCallFunction.cpp14
-rw-r--r--lldb/source/Target/ThreadPlanCallUserExpression.cpp16
-rw-r--r--lldb/test/lang/objc/objc-checker/Makefile6
-rw-r--r--lldb/test/lang/objc/objc-checker/TestObjCCheckers.py85
-rw-r--r--lldb/test/lang/objc/objc-checker/main.m30
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;
+}
OpenPOWER on IntegriCloud