1 //===-- NSException.cpp -----------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // C Includes
11 // C++ Includes
12 // Other libraries and framework includes
13 #include "clang/AST/DeclCXX.h"
14 
15 // Project includes
16 #include "Cocoa.h"
17 
18 #include "lldb/Core/DataBufferHeap.h"
19 #include "lldb/Core/Error.h"
20 #include "lldb/Core/Stream.h"
21 #include "lldb/Core/ValueObject.h"
22 #include "lldb/Core/ValueObjectConstResult.h"
23 #include "lldb/DataFormatters/FormattersHelpers.h"
24 #include "lldb/Host/Endian.h"
25 #include "lldb/Symbol/ClangASTContext.h"
26 #include "lldb/Target/ObjCLanguageRuntime.h"
27 #include "lldb/Target/Target.h"
28 
29 #include "lldb/Utility/ProcessStructReader.h"
30 
31 #include "Plugins/Language/ObjC/NSString.h"
32 
33 using namespace lldb;
34 using namespace lldb_private;
35 using namespace lldb_private::formatters;
36 
37 bool lldb_private::formatters::NSException_SummaryProvider(
38     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
39   ProcessSP process_sp(valobj.GetProcessSP());
40   if (!process_sp)
41     return false;
42 
43   lldb::addr_t ptr_value = LLDB_INVALID_ADDRESS;
44 
45   CompilerType valobj_type(valobj.GetCompilerType());
46   Flags type_flags(valobj_type.GetTypeInfo());
47   if (type_flags.AllClear(eTypeHasValue)) {
48     if (valobj.IsBaseClass() && valobj.GetParent())
49       ptr_value = valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
50   } else
51     ptr_value = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
52 
53   if (ptr_value == LLDB_INVALID_ADDRESS)
54     return false;
55   size_t ptr_size = process_sp->GetAddressByteSize();
56   lldb::addr_t name_location = ptr_value + 1 * ptr_size;
57   lldb::addr_t reason_location = ptr_value + 2 * ptr_size;
58 
59   Error error;
60   lldb::addr_t name = process_sp->ReadPointerFromMemory(name_location, error);
61   if (error.Fail() || name == LLDB_INVALID_ADDRESS)
62     return false;
63 
64   lldb::addr_t reason =
65       process_sp->ReadPointerFromMemory(reason_location, error);
66   if (error.Fail() || reason == LLDB_INVALID_ADDRESS)
67     return false;
68 
69   InferiorSizedWord name_isw(name, *process_sp);
70   InferiorSizedWord reason_isw(reason, *process_sp);
71 
72   CompilerType voidstar = process_sp->GetTarget()
73                               .GetScratchClangASTContext()
74                               ->GetBasicType(lldb::eBasicTypeVoid)
75                               .GetPointerType();
76 
77   ValueObjectSP name_sp = ValueObject::CreateValueObjectFromData(
78       "name_str", name_isw.GetAsData(process_sp->GetByteOrder()),
79       valobj.GetExecutionContextRef(), voidstar);
80   ValueObjectSP reason_sp = ValueObject::CreateValueObjectFromData(
81       "reason_str", reason_isw.GetAsData(process_sp->GetByteOrder()),
82       valobj.GetExecutionContextRef(), voidstar);
83 
84   if (!name_sp || !reason_sp)
85     return false;
86 
87   StreamString name_str_summary;
88   StreamString reason_str_summary;
89   if (NSStringSummaryProvider(*name_sp, name_str_summary, options) &&
90       NSStringSummaryProvider(*reason_sp, reason_str_summary, options) &&
91       !name_str_summary.Empty() && !reason_str_summary.Empty()) {
92     stream.Printf("name: %s - reason: %s", name_str_summary.GetData(),
93                   reason_str_summary.GetData());
94     return true;
95   } else
96     return false;
97 }
98 
99 class NSExceptionSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
100 public:
101   NSExceptionSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
102       : SyntheticChildrenFrontEnd(*valobj_sp) {}
103 
104   ~NSExceptionSyntheticFrontEnd() override = default;
105   // no need to delete m_child_ptr - it's kept alive by the cluster manager on
106   // our behalf
107 
108   size_t CalculateNumChildren() override {
109     if (m_child_ptr)
110       return 1;
111     if (m_child_sp)
112       return 1;
113     return 0;
114   }
115 
116   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
117     if (idx != 0)
118       return lldb::ValueObjectSP();
119 
120     if (m_child_ptr)
121       return m_child_ptr->GetSP();
122     return m_child_sp;
123   }
124 
125   bool Update() override {
126     m_child_ptr = nullptr;
127     m_child_sp.reset();
128 
129     ProcessSP process_sp(m_backend.GetProcessSP());
130     if (!process_sp)
131       return false;
132 
133     lldb::addr_t userinfo_location = LLDB_INVALID_ADDRESS;
134 
135     CompilerType valobj_type(m_backend.GetCompilerType());
136     Flags type_flags(valobj_type.GetTypeInfo());
137     if (type_flags.AllClear(eTypeHasValue)) {
138       if (m_backend.IsBaseClass() && m_backend.GetParent())
139         userinfo_location =
140             m_backend.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
141     } else
142       userinfo_location = m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
143 
144     if (userinfo_location == LLDB_INVALID_ADDRESS)
145       return false;
146 
147     size_t ptr_size = process_sp->GetAddressByteSize();
148 
149     userinfo_location += 3 * ptr_size;
150     Error error;
151     lldb::addr_t userinfo =
152         process_sp->ReadPointerFromMemory(userinfo_location, error);
153     if (userinfo == LLDB_INVALID_ADDRESS || error.Fail())
154       return false;
155     InferiorSizedWord isw(userinfo, *process_sp);
156     m_child_sp = CreateValueObjectFromData(
157         "userInfo", isw.GetAsData(process_sp->GetByteOrder()),
158         m_backend.GetExecutionContextRef(),
159         process_sp->GetTarget().GetScratchClangASTContext()->GetBasicType(
160             lldb::eBasicTypeObjCID));
161     return false;
162   }
163 
164   bool MightHaveChildren() override { return true; }
165 
166   size_t GetIndexOfChildWithName(const ConstString &name) override {
167     static ConstString g___userInfo("userInfo");
168     if (name == g___userInfo)
169       return 0;
170     return UINT32_MAX;
171   }
172 
173 private:
174   // the child here can be "real" (i.e. an actual child of the root) or
175   // synthetized from raw memory
176   // if the former, I need to store a plain pointer to it - or else a loop of
177   // references will cause this entire hierarchy of values to leak
178   // if the latter, then I need to store a SharedPointer to it - so that it only
179   // goes away when everyone else in the cluster goes away
180   // oh joy!
181   ValueObject *m_child_ptr;
182   ValueObjectSP m_child_sp;
183 };
184 
185 SyntheticChildrenFrontEnd *
186 lldb_private::formatters::NSExceptionSyntheticFrontEndCreator(
187     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
188   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
189   if (!process_sp)
190     return nullptr;
191   ObjCLanguageRuntime *runtime =
192       (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
193           lldb::eLanguageTypeObjC);
194   if (!runtime)
195     return nullptr;
196 
197   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
198       runtime->GetClassDescriptor(*valobj_sp.get()));
199 
200   if (!descriptor.get() || !descriptor->IsValid())
201     return nullptr;
202 
203   const char *class_name = descriptor->GetClassName().GetCString();
204 
205   if (!class_name || !*class_name)
206     return nullptr;
207 
208   if (!strcmp(class_name, "NSException"))
209     return (new NSExceptionSyntheticFrontEnd(valobj_sp));
210   else if (!strcmp(class_name, "NSCFException"))
211     return (new NSExceptionSyntheticFrontEnd(valobj_sp));
212   else if (!strcmp(class_name, "__NSCFException"))
213     return (new NSExceptionSyntheticFrontEnd(valobj_sp));
214 
215   return nullptr;
216 }
217