15ffd83dbSDimitry Andric //===-- NSError.cpp -------------------------------------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "clang/AST/DeclCXX.h" 100b57cec5SDimitry Andric 110b57cec5SDimitry Andric #include "Cocoa.h" 120b57cec5SDimitry Andric 135ffd83dbSDimitry Andric #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" 140b57cec5SDimitry Andric #include "lldb/Core/ValueObject.h" 150b57cec5SDimitry Andric #include "lldb/Core/ValueObjectConstResult.h" 160b57cec5SDimitry Andric #include "lldb/DataFormatters/FormattersHelpers.h" 170b57cec5SDimitry Andric #include "lldb/Target/Target.h" 180b57cec5SDimitry Andric #include "lldb/Utility/DataBufferHeap.h" 190b57cec5SDimitry Andric #include "lldb/Utility/Endian.h" 200b57cec5SDimitry Andric #include "lldb/Utility/Status.h" 210b57cec5SDimitry Andric #include "lldb/Utility/Stream.h" 220b57cec5SDimitry Andric 230b57cec5SDimitry Andric #include "Plugins/Language/ObjC/NSString.h" 240b57cec5SDimitry Andric #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" 250b57cec5SDimitry Andric 260b57cec5SDimitry Andric using namespace lldb; 270b57cec5SDimitry Andric using namespace lldb_private; 280b57cec5SDimitry Andric using namespace lldb_private::formatters; 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric static lldb::addr_t DerefToNSErrorPointer(ValueObject &valobj) { 310b57cec5SDimitry Andric CompilerType valobj_type(valobj.GetCompilerType()); 320b57cec5SDimitry Andric Flags type_flags(valobj_type.GetTypeInfo()); 330b57cec5SDimitry Andric if (type_flags.AllClear(eTypeHasValue)) { 340b57cec5SDimitry Andric if (valobj.IsBaseClass() && valobj.GetParent()) 350b57cec5SDimitry Andric return valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 360b57cec5SDimitry Andric } else { 370b57cec5SDimitry Andric lldb::addr_t ptr_value = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 380b57cec5SDimitry Andric if (type_flags.AllSet(eTypeIsPointer)) { 390b57cec5SDimitry Andric CompilerType pointee_type(valobj_type.GetPointeeType()); 400b57cec5SDimitry Andric Flags pointee_flags(pointee_type.GetTypeInfo()); 410b57cec5SDimitry Andric if (pointee_flags.AllSet(eTypeIsPointer)) { 420b57cec5SDimitry Andric if (ProcessSP process_sp = valobj.GetProcessSP()) { 430b57cec5SDimitry Andric Status error; 440b57cec5SDimitry Andric ptr_value = process_sp->ReadPointerFromMemory(ptr_value, error); 450b57cec5SDimitry Andric } 460b57cec5SDimitry Andric } 470b57cec5SDimitry Andric } 480b57cec5SDimitry Andric return ptr_value; 490b57cec5SDimitry Andric } 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric return LLDB_INVALID_ADDRESS; 520b57cec5SDimitry Andric } 530b57cec5SDimitry Andric 540b57cec5SDimitry Andric bool lldb_private::formatters::NSError_SummaryProvider( 550b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 560b57cec5SDimitry Andric ProcessSP process_sp(valobj.GetProcessSP()); 570b57cec5SDimitry Andric if (!process_sp) 580b57cec5SDimitry Andric return false; 590b57cec5SDimitry Andric 600b57cec5SDimitry Andric lldb::addr_t ptr_value = DerefToNSErrorPointer(valobj); 610b57cec5SDimitry Andric if (ptr_value == LLDB_INVALID_ADDRESS) 620b57cec5SDimitry Andric return false; 630b57cec5SDimitry Andric 640b57cec5SDimitry Andric size_t ptr_size = process_sp->GetAddressByteSize(); 650b57cec5SDimitry Andric lldb::addr_t code_location = ptr_value + 2 * ptr_size; 660b57cec5SDimitry Andric lldb::addr_t domain_location = ptr_value + 3 * ptr_size; 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric Status error; 690b57cec5SDimitry Andric uint64_t code = process_sp->ReadUnsignedIntegerFromMemory(code_location, 700b57cec5SDimitry Andric ptr_size, 0, error); 710b57cec5SDimitry Andric if (error.Fail()) 720b57cec5SDimitry Andric return false; 730b57cec5SDimitry Andric 740b57cec5SDimitry Andric lldb::addr_t domain_str_value = 750b57cec5SDimitry Andric process_sp->ReadPointerFromMemory(domain_location, error); 760b57cec5SDimitry Andric if (error.Fail() || domain_str_value == LLDB_INVALID_ADDRESS) 770b57cec5SDimitry Andric return false; 780b57cec5SDimitry Andric 790b57cec5SDimitry Andric if (!domain_str_value) { 800b57cec5SDimitry Andric stream.Printf("domain: nil - code: %" PRIu64, code); 810b57cec5SDimitry Andric return true; 820b57cec5SDimitry Andric } 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric InferiorSizedWord isw(domain_str_value, *process_sp); 85bdd1243dSDimitry Andric TypeSystemClangSP scratch_ts_sp = 86bdd1243dSDimitry Andric ScratchTypeSystemClang::GetForTarget(process_sp->GetTarget()); 870b57cec5SDimitry Andric 88bdd1243dSDimitry Andric if (!scratch_ts_sp) 89bdd1243dSDimitry Andric return false; 900b57cec5SDimitry Andric ValueObjectSP domain_str_sp = ValueObject::CreateValueObjectFromData( 910b57cec5SDimitry Andric "domain_str", isw.GetAsData(process_sp->GetByteOrder()), 92480093f4SDimitry Andric valobj.GetExecutionContextRef(), 93bdd1243dSDimitry Andric scratch_ts_sp->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()); 940b57cec5SDimitry Andric 950b57cec5SDimitry Andric if (!domain_str_sp) 960b57cec5SDimitry Andric return false; 970b57cec5SDimitry Andric 980b57cec5SDimitry Andric StreamString domain_str_summary; 990b57cec5SDimitry Andric if (NSStringSummaryProvider(*domain_str_sp, domain_str_summary, options) && 1000b57cec5SDimitry Andric !domain_str_summary.Empty()) { 1010b57cec5SDimitry Andric stream.Printf("domain: %s - code: %" PRIu64, domain_str_summary.GetData(), 1020b57cec5SDimitry Andric code); 1030b57cec5SDimitry Andric return true; 1040b57cec5SDimitry Andric } else { 1050b57cec5SDimitry Andric stream.Printf("domain: nil - code: %" PRIu64, code); 1060b57cec5SDimitry Andric return true; 1070b57cec5SDimitry Andric } 1080b57cec5SDimitry Andric } 1090b57cec5SDimitry Andric 1100b57cec5SDimitry Andric class NSErrorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { 1110b57cec5SDimitry Andric public: 1120b57cec5SDimitry Andric NSErrorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 1130b57cec5SDimitry Andric : SyntheticChildrenFrontEnd(*valobj_sp) {} 1140b57cec5SDimitry Andric 1150b57cec5SDimitry Andric ~NSErrorSyntheticFrontEnd() override = default; 1160b57cec5SDimitry Andric // no need to delete m_child_ptr - it's kept alive by the cluster manager on 1170b57cec5SDimitry Andric // our behalf 1180b57cec5SDimitry Andric 119*0fca6ea1SDimitry Andric llvm::Expected<uint32_t> CalculateNumChildren() override { 1200b57cec5SDimitry Andric if (m_child_ptr) 1210b57cec5SDimitry Andric return 1; 1220b57cec5SDimitry Andric if (m_child_sp) 1230b57cec5SDimitry Andric return 1; 1240b57cec5SDimitry Andric return 0; 1250b57cec5SDimitry Andric } 1260b57cec5SDimitry Andric 127*0fca6ea1SDimitry Andric lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { 1280b57cec5SDimitry Andric if (idx != 0) 1290b57cec5SDimitry Andric return lldb::ValueObjectSP(); 1300b57cec5SDimitry Andric 1310b57cec5SDimitry Andric if (m_child_ptr) 1320b57cec5SDimitry Andric return m_child_ptr->GetSP(); 1330b57cec5SDimitry Andric return m_child_sp; 1340b57cec5SDimitry Andric } 1350b57cec5SDimitry Andric 136*0fca6ea1SDimitry Andric lldb::ChildCacheState Update() override { 1370b57cec5SDimitry Andric m_child_ptr = nullptr; 1380b57cec5SDimitry Andric m_child_sp.reset(); 1390b57cec5SDimitry Andric 1400b57cec5SDimitry Andric ProcessSP process_sp(m_backend.GetProcessSP()); 1410b57cec5SDimitry Andric if (!process_sp) 142*0fca6ea1SDimitry Andric return lldb::ChildCacheState::eRefetch; 1430b57cec5SDimitry Andric 1440b57cec5SDimitry Andric lldb::addr_t userinfo_location = DerefToNSErrorPointer(m_backend); 1450b57cec5SDimitry Andric if (userinfo_location == LLDB_INVALID_ADDRESS) 146*0fca6ea1SDimitry Andric return lldb::ChildCacheState::eRefetch; 1470b57cec5SDimitry Andric 1480b57cec5SDimitry Andric size_t ptr_size = process_sp->GetAddressByteSize(); 1490b57cec5SDimitry Andric 1500b57cec5SDimitry Andric userinfo_location += 4 * ptr_size; 1510b57cec5SDimitry Andric Status error; 1520b57cec5SDimitry Andric lldb::addr_t userinfo = 1530b57cec5SDimitry Andric process_sp->ReadPointerFromMemory(userinfo_location, error); 1540b57cec5SDimitry Andric if (userinfo == LLDB_INVALID_ADDRESS || error.Fail()) 155*0fca6ea1SDimitry Andric return lldb::ChildCacheState::eRefetch; 1560b57cec5SDimitry Andric InferiorSizedWord isw(userinfo, *process_sp); 157bdd1243dSDimitry Andric TypeSystemClangSP scratch_ts_sp = 158bdd1243dSDimitry Andric ScratchTypeSystemClang::GetForTarget(process_sp->GetTarget()); 159bdd1243dSDimitry Andric if (!scratch_ts_sp) 160*0fca6ea1SDimitry Andric return lldb::ChildCacheState::eRefetch; 1610b57cec5SDimitry Andric m_child_sp = CreateValueObjectFromData( 1620b57cec5SDimitry Andric "_userInfo", isw.GetAsData(process_sp->GetByteOrder()), 1630b57cec5SDimitry Andric m_backend.GetExecutionContextRef(), 164bdd1243dSDimitry Andric scratch_ts_sp->GetBasicType(lldb::eBasicTypeObjCID)); 165*0fca6ea1SDimitry Andric return lldb::ChildCacheState::eRefetch; 1660b57cec5SDimitry Andric } 1670b57cec5SDimitry Andric 1680b57cec5SDimitry Andric bool MightHaveChildren() override { return true; } 1690b57cec5SDimitry Andric 1700b57cec5SDimitry Andric size_t GetIndexOfChildWithName(ConstString name) override { 17181ad6265SDimitry Andric static ConstString g_userInfo("_userInfo"); 17281ad6265SDimitry Andric if (name == g_userInfo) 1730b57cec5SDimitry Andric return 0; 1740b57cec5SDimitry Andric return UINT32_MAX; 1750b57cec5SDimitry Andric } 1760b57cec5SDimitry Andric 1770b57cec5SDimitry Andric private: 1780b57cec5SDimitry Andric // the child here can be "real" (i.e. an actual child of the root) or 1790b57cec5SDimitry Andric // synthetized from raw memory if the former, I need to store a plain pointer 1800b57cec5SDimitry Andric // to it - or else a loop of references will cause this entire hierarchy of 1810b57cec5SDimitry Andric // values to leak if the latter, then I need to store a SharedPointer to it - 1820b57cec5SDimitry Andric // so that it only goes away when everyone else in the cluster goes away oh 1830b57cec5SDimitry Andric // joy! 184fcaf7f86SDimitry Andric ValueObject *m_child_ptr = nullptr; 1850b57cec5SDimitry Andric ValueObjectSP m_child_sp; 1860b57cec5SDimitry Andric }; 1870b57cec5SDimitry Andric 1880b57cec5SDimitry Andric SyntheticChildrenFrontEnd * 1890b57cec5SDimitry Andric lldb_private::formatters::NSErrorSyntheticFrontEndCreator( 1900b57cec5SDimitry Andric CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 1910b57cec5SDimitry Andric lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 1920b57cec5SDimitry Andric if (!process_sp) 1930b57cec5SDimitry Andric return nullptr; 1940b57cec5SDimitry Andric ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 1950b57cec5SDimitry Andric if (!runtime) 1960b57cec5SDimitry Andric return nullptr; 1970b57cec5SDimitry Andric 1980b57cec5SDimitry Andric ObjCLanguageRuntime::ClassDescriptorSP descriptor( 1990b57cec5SDimitry Andric runtime->GetClassDescriptor(*valobj_sp.get())); 2000b57cec5SDimitry Andric 2010b57cec5SDimitry Andric if (!descriptor.get() || !descriptor->IsValid()) 2020b57cec5SDimitry Andric return nullptr; 2030b57cec5SDimitry Andric 2040b57cec5SDimitry Andric const char *class_name = descriptor->GetClassName().GetCString(); 2050b57cec5SDimitry Andric 2060b57cec5SDimitry Andric if (!class_name || !*class_name) 2070b57cec5SDimitry Andric return nullptr; 2080b57cec5SDimitry Andric 2090b57cec5SDimitry Andric if (!strcmp(class_name, "NSError")) 2100b57cec5SDimitry Andric return (new NSErrorSyntheticFrontEnd(valobj_sp)); 2110b57cec5SDimitry Andric else if (!strcmp(class_name, "__NSCFError")) 2120b57cec5SDimitry Andric return (new NSErrorSyntheticFrontEnd(valobj_sp)); 2130b57cec5SDimitry Andric 2140b57cec5SDimitry Andric return nullptr; 2150b57cec5SDimitry Andric } 216