kubamracek created this revision.
kubamracek added reviewers: jingham, jasonmolenda.

This patch teaches LLDB about more fields on NSException Obj-C objects, 
specifically we can now retrieve the "name" and "reason" of an NSException. The 
goal is to eventually be able to have SB API that can provide details about the 
currently thrown/caught/processed exception.


https://reviews.llvm.org/D43884

Files:
  packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile
  packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
  packages/Python/lldbsuite/test/lang/objc/exceptions/main.m
  source/Plugins/Language/ObjC/NSArray.cpp
  source/Plugins/Language/ObjC/NSException.cpp

Index: source/Plugins/Language/ObjC/NSException.cpp
===================================================================
--- source/Plugins/Language/ObjC/NSException.cpp
+++ source/Plugins/Language/ObjC/NSException.cpp
@@ -33,56 +33,75 @@
 using namespace lldb_private;
 using namespace lldb_private::formatters;
 
-bool lldb_private::formatters::NSException_SummaryProvider(
-    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+static bool ExtractFields(ValueObject &valobj, ValueObjectSP *name_sp,
+                          ValueObjectSP *reason_sp, ValueObjectSP *userinfo_sp,
+                          ValueObjectSP *reserved_sp) {
   ProcessSP process_sp(valobj.GetProcessSP());
-  if (!process_sp)
-    return false;
+  if (!process_sp) return false;
 
-  lldb::addr_t ptr_value = LLDB_INVALID_ADDRESS;
+  lldb::addr_t ptr = LLDB_INVALID_ADDRESS;
 
   CompilerType valobj_type(valobj.GetCompilerType());
   Flags type_flags(valobj_type.GetTypeInfo());
   if (type_flags.AllClear(eTypeHasValue)) {
     if (valobj.IsBaseClass() && valobj.GetParent())
-      ptr_value = valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+      ptr = valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
   } else
-    ptr_value = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+    ptr = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
 
-  if (ptr_value == LLDB_INVALID_ADDRESS)
+  if (ptr == LLDB_INVALID_ADDRESS)
     return false;
   size_t ptr_size = process_sp->GetAddressByteSize();
-  lldb::addr_t name_location = ptr_value + 1 * ptr_size;
-  lldb::addr_t reason_location = ptr_value + 2 * ptr_size;
 
   Status error;
-  lldb::addr_t name = process_sp->ReadPointerFromMemory(name_location, error);
-  if (error.Fail() || name == LLDB_INVALID_ADDRESS)
-    return false;
-
-  lldb::addr_t reason =
-      process_sp->ReadPointerFromMemory(reason_location, error);
-  if (error.Fail() || reason == LLDB_INVALID_ADDRESS)
-    return false;
+  auto name = process_sp->ReadPointerFromMemory(ptr + 1 * ptr_size, error);
+  if (error.Fail() || name == LLDB_INVALID_ADDRESS) return false;
+  auto reason = process_sp->ReadPointerFromMemory(ptr + 2 * ptr_size, error);
+  if (error.Fail() || reason == LLDB_INVALID_ADDRESS) return false;
+  auto userinfo = process_sp->ReadPointerFromMemory(ptr + 3 * ptr_size, error);
+  if (error.Fail() || reason == LLDB_INVALID_ADDRESS) return false;
+  auto reserved = process_sp->ReadPointerFromMemory(ptr + 4 * ptr_size, error);
+  if (error.Fail() || reason == LLDB_INVALID_ADDRESS) return false;
 
   InferiorSizedWord name_isw(name, *process_sp);
   InferiorSizedWord reason_isw(reason, *process_sp);
+  InferiorSizedWord userinfo_isw(userinfo, *process_sp);
+  InferiorSizedWord reserved_isw(reserved, *process_sp);
 
   CompilerType voidstar = process_sp->GetTarget()
-                              .GetScratchClangASTContext()
-                              ->GetBasicType(lldb::eBasicTypeVoid)
-                              .GetPointerType();
-
-  ValueObjectSP name_sp = ValueObject::CreateValueObjectFromData(
-      "name_str", name_isw.GetAsData(process_sp->GetByteOrder()),
-      valobj.GetExecutionContextRef(), voidstar);
-  ValueObjectSP reason_sp = ValueObject::CreateValueObjectFromData(
-      "reason_str", reason_isw.GetAsData(process_sp->GetByteOrder()),
-      valobj.GetExecutionContextRef(), voidstar);
-
-  if (!name_sp || !reason_sp)
+  .GetScratchClangASTContext()
+  ->GetBasicType(lldb::eBasicTypeVoid)
+  .GetPointerType();
+
+  if (name_sp)
+    *name_sp = ValueObject::CreateValueObjectFromData(
+        "name", name_isw.GetAsData(process_sp->GetByteOrder()),
+        valobj.GetExecutionContextRef(), voidstar);
+  if (reason_sp)
+    *reason_sp = ValueObject::CreateValueObjectFromData(
+        "reason", reason_isw.GetAsData(process_sp->GetByteOrder()),
+        valobj.GetExecutionContextRef(), voidstar);
+  if (userinfo_sp)
+    *userinfo_sp = ValueObject::CreateValueObjectFromData(
+        "userInfo", userinfo_isw.GetAsData(process_sp->GetByteOrder()),
+        valobj.GetExecutionContextRef(), voidstar);
+  if (reserved_sp)
+    *reserved_sp = ValueObject::CreateValueObjectFromData(
+        "reserved", reserved_isw.GetAsData(process_sp->GetByteOrder()),
+        valobj.GetExecutionContextRef(), voidstar);
+
+  return true;
+}
+
+bool lldb_private::formatters::NSException_SummaryProvider(
+    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+  lldb::ValueObjectSP name_sp;
+  lldb::ValueObjectSP reason_sp;
+  if (!ExtractFields(valobj, &name_sp, &reason_sp, nullptr, nullptr))
     return false;
 
+  if (!name_sp || !reason_sp) return false;
+
   StreamString name_str_summary;
   StreamString reason_str_summary;
   if (NSStringSummaryProvider(*name_sp, name_str_summary, options) &&
@@ -101,84 +120,53 @@
       : SyntheticChildrenFrontEnd(*valobj_sp) {}
 
   ~NSExceptionSyntheticFrontEnd() override = default;
-  // no need to delete m_child_ptr - it's kept alive by the cluster manager on
-  // our behalf
 
   size_t CalculateNumChildren() override {
-    if (m_child_ptr)
-      return 1;
-    if (m_child_sp)
-      return 1;
-    return 0;
+    return 4;
   }
 
   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
-    if (idx != 0)
-      return lldb::ValueObjectSP();
-
-    if (m_child_ptr)
-      return m_child_ptr->GetSP();
-    return m_child_sp;
+    switch (idx) {
+      case 0: return m_name_sp;
+      case 1: return m_reason_sp;
+      case 2: return m_userinfo_sp;
+      case 3: return m_reserved_sp;
+    }
+    return lldb::ValueObjectSP();
   }
 
   bool Update() override {
-    m_child_ptr = nullptr;
-    m_child_sp.reset();
-
-    ProcessSP process_sp(m_backend.GetProcessSP());
-    if (!process_sp)
-      return false;
-
-    lldb::addr_t userinfo_location = LLDB_INVALID_ADDRESS;
+    m_name_sp.reset();
+    m_reason_sp.reset();
+    m_userinfo_sp.reset();
+    m_reserved_sp.reset();
 
-    CompilerType valobj_type(m_backend.GetCompilerType());
-    Flags type_flags(valobj_type.GetTypeInfo());
-    if (type_flags.AllClear(eTypeHasValue)) {
-      if (m_backend.IsBaseClass() && m_backend.GetParent())
-        userinfo_location =
-            m_backend.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
-    } else
-      userinfo_location = m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
-
-    if (userinfo_location == LLDB_INVALID_ADDRESS)
+    if (!ExtractFields(m_backend, &m_name_sp, &m_reason_sp, &m_userinfo_sp,
+                       &m_reserved_sp))
       return false;
 
-    size_t ptr_size = process_sp->GetAddressByteSize();
-
-    userinfo_location += 3 * ptr_size;
-    Status error;
-    lldb::addr_t userinfo =
-        process_sp->ReadPointerFromMemory(userinfo_location, error);
-    if (userinfo == LLDB_INVALID_ADDRESS || error.Fail())
-      return false;
-    InferiorSizedWord isw(userinfo, *process_sp);
-    m_child_sp = CreateValueObjectFromData(
-        "userInfo", isw.GetAsData(process_sp->GetByteOrder()),
-        m_backend.GetExecutionContextRef(),
-        process_sp->GetTarget().GetScratchClangASTContext()->GetBasicType(
-            lldb::eBasicTypeObjCID));
-    return false;
+    return true;
   }
 
   bool MightHaveChildren() override { return true; }
 
   size_t GetIndexOfChildWithName(const ConstString &name) override {
+    static ConstString g___name("name");
+    static ConstString g___reason("reason");
     static ConstString g___userInfo("userInfo");
-    if (name == g___userInfo)
-      return 0;
+    static ConstString g___reserved("reserved");
+    if (name == g___name) return 0;
+    if (name == g___reason) return 1;
+    if (name == g___userInfo) return 2;
+    if (name == g___reserved) return 3;
     return UINT32_MAX;
   }
 
 private:
-  // the child here can be "real" (i.e. an actual child of the root) or
-  // synthetized from raw memory
-  // if the former, I need to store a plain pointer to it - or else a loop of
-  // references will cause this entire hierarchy of values to leak
-  // if the latter, then I need to store a SharedPointer to it - so that it only
-  // goes away when everyone else in the cluster goes away
-  // oh joy!
-  ValueObject *m_child_ptr;
-  ValueObjectSP m_child_sp;
+  ValueObjectSP m_name_sp;
+  ValueObjectSP m_reason_sp;
+  ValueObjectSP m_userinfo_sp;
+  ValueObjectSP m_reserved_sp;
 };
 
 SyntheticChildrenFrontEnd *
Index: source/Plugins/Language/ObjC/NSArray.cpp
===================================================================
--- source/Plugins/Language/ObjC/NSArray.cpp
+++ source/Plugins/Language/ObjC/NSArray.cpp
@@ -218,6 +218,25 @@
 
 }
 
+namespace CallStackArray {
+  struct DataDescriptor_32 {
+    uint32_t _data;
+    uint32_t _used;
+    uint32_t _offset;
+    const uint32_t _size = 0;
+  };
+
+  struct DataDescriptor_64 {
+    uint64_t _data;
+    uint64_t _used;
+    uint64_t _offset;
+    const uint64_t _size = 0;
+  };
+
+  using NSCallStackArraySyntheticFrontEnd =
+      GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
 template <typename D32, typename D64, bool Inline>
 class GenericNSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
 public:
@@ -368,6 +387,7 @@
   static const ConstString g_NSArrayCF("__NSCFArray");
   static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy");
   static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable");
+  static const ConstString g_NSCallStackArray("_NSCallStackArray");
 
   if (class_name.IsEmpty())
     return false;
@@ -423,6 +443,8 @@
         valobj_addr + 2 * ptr_size, ptr_size, 0, error);
     if (error.Fail())
       return false;
+  } else if (class_name == g_NSCallStackArray) {
+    return false;
   } else {
     auto &map(NSArray_Additionals::GetAdditionalSummaries());
     auto iter = map.find(class_name), end = map.end();
@@ -817,6 +839,7 @@
   static const ConstString g_NSArray1("__NSSingleObjectArrayI");
   static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy");
   static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable");
+  static const ConstString g_NSCallStackArray("_NSCallStackArray");
 
   if (class_name.IsEmpty())
     return nullptr;
@@ -846,6 +869,8 @@
       return (new Foundation1010::NSArrayMSyntheticFrontEnd(valobj_sp));
     else
       return (new Foundation109::NSArrayMSyntheticFrontEnd(valobj_sp));
+  } else if (class_name == g_NSCallStackArray) {
+    return (new CallStackArray::NSCallStackArraySyntheticFrontEnd(valobj_sp));
   } else {
     auto &map(NSArray_Additionals::GetAdditionalSynthetics());
     auto iter = map.find(class_name), end = map.end();
Index: packages/Python/lldbsuite/test/lang/objc/exceptions/main.m
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/lang/objc/exceptions/main.m
@@ -0,0 +1,36 @@
+//===-- main.m ------------------------------------------------*- ObjC -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#import <Foundation/Foundation.h>
+
+void foo()
+{
+    NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:@"some_key", @"some_value", nil];
+    @throw [[NSException alloc] initWithName:@"ThrownException" reason:@"SomeReason" userInfo:info];
+}
+
+int main (int argc, const char * argv[])
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:@"some_key", @"some_value", nil];
+    NSException *e1 = [[NSException alloc] initWithName:@"ExceptionName" reason:@"SomeReason" userInfo:info];
+    NSException *e2;
+
+    @try {
+        foo();
+    } @catch(NSException *e) {
+        e2 = e;
+    }
+
+    NSLog(@"1"); // Set break point at this line.
+    [pool drain];
+    return 0;
+}
+
Index: packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
@@ -0,0 +1,71 @@
+# encoding: utf-8
+"""
+Test lldb Obj-C exception support.
+"""
+
+from __future__ import print_function
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class ObjCExceptionsTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+        # Find the line number to break at.
+        self.line = line_number('main.m', '// Set break point at this line.')
+
+    @skipUnlessDarwin
+    def test_objc_exceptions_1(self):
+        self.build()
+
+        self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line(
+            self, "main.m", self.line, num_expected_locations=1, loc_exact=True)
+
+        self.runCmd("run", RUN_SUCCEEDED)
+
+        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+                    substrs=['stopped', 'stop reason = breakpoint'])
+
+        self.expect(
+            'frame variable e1',
+            substrs=[
+                '(NSException *) e1 = ',
+                'name: @"ExceptionName" - reason: @"SomeReason"'
+            ])
+
+        self.expect(
+            'frame variable --dynamic-type no-run-target *e1',
+            substrs=[
+                '(NSException) *e1 = ',
+                'name = ', '@"ExceptionName"',
+                'reason = ', '@"SomeReason"',
+                'userInfo = ', '1 key/value pair',
+                'reserved = ', 'nil',
+            ])
+
+        self.expect(
+            'frame variable e2',
+            substrs=[
+                '(NSException *) e2 = ',
+                'name: @"ThrownException" - reason: @"SomeReason"'
+            ])
+
+        self.expect(
+            'frame variable --dynamic-type no-run-target *e2',
+            substrs=[
+                '(NSException) *e2 = ',
+                'name = ', '@"ThrownException"',
+                'reason = ', '@"SomeReason"',
+                'userInfo = ', '1 key/value pair',
+                'reserved = ',
+            ])
Index: packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile
@@ -0,0 +1,9 @@
+LEVEL = ../../../make
+
+OBJC_SOURCES := main.m
+
+CFLAGS_EXTRAS += -w
+
+include $(LEVEL)/Makefile.rules
+
+LDFLAGS += -framework Foundation
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to