Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp
39644 views
//===-- NSError.cpp -------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/AST/DeclCXX.h"910#include "Cocoa.h"1112#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"13#include "lldb/Core/ValueObject.h"14#include "lldb/Core/ValueObjectConstResult.h"15#include "lldb/DataFormatters/FormattersHelpers.h"16#include "lldb/Target/Target.h"17#include "lldb/Utility/DataBufferHeap.h"18#include "lldb/Utility/Endian.h"19#include "lldb/Utility/Status.h"20#include "lldb/Utility/Stream.h"2122#include "Plugins/Language/ObjC/NSString.h"23#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"2425using namespace lldb;26using namespace lldb_private;27using namespace lldb_private::formatters;2829static lldb::addr_t DerefToNSErrorPointer(ValueObject &valobj) {30CompilerType valobj_type(valobj.GetCompilerType());31Flags type_flags(valobj_type.GetTypeInfo());32if (type_flags.AllClear(eTypeHasValue)) {33if (valobj.IsBaseClass() && valobj.GetParent())34return valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);35} else {36lldb::addr_t ptr_value = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);37if (type_flags.AllSet(eTypeIsPointer)) {38CompilerType pointee_type(valobj_type.GetPointeeType());39Flags pointee_flags(pointee_type.GetTypeInfo());40if (pointee_flags.AllSet(eTypeIsPointer)) {41if (ProcessSP process_sp = valobj.GetProcessSP()) {42Status error;43ptr_value = process_sp->ReadPointerFromMemory(ptr_value, error);44}45}46}47return ptr_value;48}4950return LLDB_INVALID_ADDRESS;51}5253bool lldb_private::formatters::NSError_SummaryProvider(54ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {55ProcessSP process_sp(valobj.GetProcessSP());56if (!process_sp)57return false;5859lldb::addr_t ptr_value = DerefToNSErrorPointer(valobj);60if (ptr_value == LLDB_INVALID_ADDRESS)61return false;6263size_t ptr_size = process_sp->GetAddressByteSize();64lldb::addr_t code_location = ptr_value + 2 * ptr_size;65lldb::addr_t domain_location = ptr_value + 3 * ptr_size;6667Status error;68uint64_t code = process_sp->ReadUnsignedIntegerFromMemory(code_location,69ptr_size, 0, error);70if (error.Fail())71return false;7273lldb::addr_t domain_str_value =74process_sp->ReadPointerFromMemory(domain_location, error);75if (error.Fail() || domain_str_value == LLDB_INVALID_ADDRESS)76return false;7778if (!domain_str_value) {79stream.Printf("domain: nil - code: %" PRIu64, code);80return true;81}8283InferiorSizedWord isw(domain_str_value, *process_sp);84TypeSystemClangSP scratch_ts_sp =85ScratchTypeSystemClang::GetForTarget(process_sp->GetTarget());8687if (!scratch_ts_sp)88return false;89ValueObjectSP domain_str_sp = ValueObject::CreateValueObjectFromData(90"domain_str", isw.GetAsData(process_sp->GetByteOrder()),91valobj.GetExecutionContextRef(),92scratch_ts_sp->GetBasicType(lldb::eBasicTypeVoid).GetPointerType());9394if (!domain_str_sp)95return false;9697StreamString domain_str_summary;98if (NSStringSummaryProvider(*domain_str_sp, domain_str_summary, options) &&99!domain_str_summary.Empty()) {100stream.Printf("domain: %s - code: %" PRIu64, domain_str_summary.GetData(),101code);102return true;103} else {104stream.Printf("domain: nil - code: %" PRIu64, code);105return true;106}107}108109class NSErrorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {110public:111NSErrorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)112: SyntheticChildrenFrontEnd(*valobj_sp) {}113114~NSErrorSyntheticFrontEnd() override = default;115// no need to delete m_child_ptr - it's kept alive by the cluster manager on116// our behalf117118llvm::Expected<uint32_t> CalculateNumChildren() override {119if (m_child_ptr)120return 1;121if (m_child_sp)122return 1;123return 0;124}125126lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {127if (idx != 0)128return lldb::ValueObjectSP();129130if (m_child_ptr)131return m_child_ptr->GetSP();132return m_child_sp;133}134135lldb::ChildCacheState Update() override {136m_child_ptr = nullptr;137m_child_sp.reset();138139ProcessSP process_sp(m_backend.GetProcessSP());140if (!process_sp)141return lldb::ChildCacheState::eRefetch;142143lldb::addr_t userinfo_location = DerefToNSErrorPointer(m_backend);144if (userinfo_location == LLDB_INVALID_ADDRESS)145return lldb::ChildCacheState::eRefetch;146147size_t ptr_size = process_sp->GetAddressByteSize();148149userinfo_location += 4 * ptr_size;150Status error;151lldb::addr_t userinfo =152process_sp->ReadPointerFromMemory(userinfo_location, error);153if (userinfo == LLDB_INVALID_ADDRESS || error.Fail())154return lldb::ChildCacheState::eRefetch;155InferiorSizedWord isw(userinfo, *process_sp);156TypeSystemClangSP scratch_ts_sp =157ScratchTypeSystemClang::GetForTarget(process_sp->GetTarget());158if (!scratch_ts_sp)159return lldb::ChildCacheState::eRefetch;160m_child_sp = CreateValueObjectFromData(161"_userInfo", isw.GetAsData(process_sp->GetByteOrder()),162m_backend.GetExecutionContextRef(),163scratch_ts_sp->GetBasicType(lldb::eBasicTypeObjCID));164return lldb::ChildCacheState::eRefetch;165}166167bool MightHaveChildren() override { return true; }168169size_t GetIndexOfChildWithName(ConstString name) override {170static ConstString g_userInfo("_userInfo");171if (name == g_userInfo)172return 0;173return UINT32_MAX;174}175176private:177// the child here can be "real" (i.e. an actual child of the root) or178// synthetized from raw memory if the former, I need to store a plain pointer179// to it - or else a loop of references will cause this entire hierarchy of180// values to leak if the latter, then I need to store a SharedPointer to it -181// so that it only goes away when everyone else in the cluster goes away oh182// joy!183ValueObject *m_child_ptr = nullptr;184ValueObjectSP m_child_sp;185};186187SyntheticChildrenFrontEnd *188lldb_private::formatters::NSErrorSyntheticFrontEndCreator(189CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {190lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());191if (!process_sp)192return nullptr;193ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);194if (!runtime)195return nullptr;196197ObjCLanguageRuntime::ClassDescriptorSP descriptor(198runtime->GetClassDescriptor(*valobj_sp.get()));199200if (!descriptor.get() || !descriptor->IsValid())201return nullptr;202203const char *class_name = descriptor->GetClassName().GetCString();204205if (!class_name || !*class_name)206return nullptr;207208if (!strcmp(class_name, "NSError"))209return (new NSErrorSyntheticFrontEnd(valobj_sp));210else if (!strcmp(class_name, "__NSCFError"))211return (new NSErrorSyntheticFrontEnd(valobj_sp));212213return nullptr;214}215216217