Coverage Report

Created: 2022-01-22 13:19

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- NSError.cpp -------------------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "clang/AST/DeclCXX.h"
10
11
#include "Cocoa.h"
12
13
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
14
#include "lldb/Core/ValueObject.h"
15
#include "lldb/Core/ValueObjectConstResult.h"
16
#include "lldb/DataFormatters/FormattersHelpers.h"
17
#include "lldb/Target/ProcessStructReader.h"
18
#include "lldb/Target/Target.h"
19
#include "lldb/Utility/DataBufferHeap.h"
20
#include "lldb/Utility/Endian.h"
21
#include "lldb/Utility/Status.h"
22
#include "lldb/Utility/Stream.h"
23
24
#include "Plugins/Language/ObjC/NSString.h"
25
#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
26
27
using namespace lldb;
28
using namespace lldb_private;
29
using namespace lldb_private::formatters;
30
31
41
static lldb::addr_t DerefToNSErrorPointer(ValueObject &valobj) {
32
41
  CompilerType valobj_type(valobj.GetCompilerType());
33
41
  Flags type_flags(valobj_type.GetTypeInfo());
34
41
  if (type_flags.AllClear(eTypeHasValue)) {
35
0
    if (valobj.IsBaseClass() && valobj.GetParent())
36
0
      return valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
37
41
  } else {
38
41
    lldb::addr_t ptr_value = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
39
41
    if (type_flags.AllSet(eTypeIsPointer)) {
40
41
      CompilerType pointee_type(valobj_type.GetPointeeType());
41
41
      Flags pointee_flags(pointee_type.GetTypeInfo());
42
41
      if (pointee_flags.AllSet(eTypeIsPointer)) {
43
12
        if (ProcessSP process_sp = valobj.GetProcessSP()) {
44
12
          Status error;
45
12
          ptr_value = process_sp->ReadPointerFromMemory(ptr_value, error);
46
12
        }
47
12
      }
48
41
    }
49
41
    return ptr_value;
50
41
  }
51
52
0
  return LLDB_INVALID_ADDRESS;
53
41
}
54
55
bool lldb_private::formatters::NSError_SummaryProvider(
56
26
    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
57
26
  ProcessSP process_sp(valobj.GetProcessSP());
58
26
  if (!process_sp)
59
0
    return false;
60
61
26
  lldb::addr_t ptr_value = DerefToNSErrorPointer(valobj);
62
26
  if (ptr_value == LLDB_INVALID_ADDRESS)
63
0
    return false;
64
65
26
  size_t ptr_size = process_sp->GetAddressByteSize();
66
26
  lldb::addr_t code_location = ptr_value + 2 * ptr_size;
67
26
  lldb::addr_t domain_location = ptr_value + 3 * ptr_size;
68
69
26
  Status error;
70
26
  uint64_t code = process_sp->ReadUnsignedIntegerFromMemory(code_location,
71
26
                                                            ptr_size, 0, error);
72
26
  if (error.Fail())
73
0
    return false;
74
75
26
  lldb::addr_t domain_str_value =
76
26
      process_sp->ReadPointerFromMemory(domain_location, error);
77
26
  if (error.Fail() || domain_str_value == LLDB_INVALID_ADDRESS)
78
0
    return false;
79
80
26
  if (!domain_str_value) {
81
0
    stream.Printf("domain: nil - code: %" PRIu64, code);
82
0
    return true;
83
0
  }
84
85
26
  InferiorSizedWord isw(domain_str_value, *process_sp);
86
87
26
  ValueObjectSP domain_str_sp = ValueObject::CreateValueObjectFromData(
88
26
      "domain_str", isw.GetAsData(process_sp->GetByteOrder()),
89
26
      valobj.GetExecutionContextRef(),
90
26
      ScratchTypeSystemClang::GetForTarget(process_sp->GetTarget())
91
26
          ->GetBasicType(lldb::eBasicTypeVoid)
92
26
          .GetPointerType());
93
94
26
  if (!domain_str_sp)
95
0
    return false;
96
97
26
  StreamString domain_str_summary;
98
26
  if (NSStringSummaryProvider(*domain_str_sp, domain_str_summary, options) &&
99
26
      !domain_str_summary.Empty()) {
100
26
    stream.Printf("domain: %s - code: %" PRIu64, domain_str_summary.GetData(),
101
26
                  code);
102
26
    return true;
103
26
  } else {
104
0
    stream.Printf("domain: nil - code: %" PRIu64, code);
105
0
    return true;
106
0
  }
107
26
}
108
109
class NSErrorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
110
public:
111
  NSErrorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
112
15
      : SyntheticChildrenFrontEnd(*valobj_sp) {}
113
114
15
  ~NSErrorSyntheticFrontEnd() override = default;
115
  // no need to delete m_child_ptr - it's kept alive by the cluster manager on
116
  // our behalf
117
118
0
  size_t CalculateNumChildren() override {
119
0
    if (m_child_ptr)
120
0
      return 1;
121
0
    if (m_child_sp)
122
0
      return 1;
123
0
    return 0;
124
0
  }
125
126
0
  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
127
0
    if (idx != 0)
128
0
      return lldb::ValueObjectSP();
129
130
0
    if (m_child_ptr)
131
0
      return m_child_ptr->GetSP();
132
0
    return m_child_sp;
133
0
  }
134
135
15
  bool Update() override {
136
15
    m_child_ptr = nullptr;
137
15
    m_child_sp.reset();
138
139
15
    ProcessSP process_sp(m_backend.GetProcessSP());
140
15
    if (!process_sp)
141
0
      return false;
142
143
15
    lldb::addr_t userinfo_location = DerefToNSErrorPointer(m_backend);
144
15
    if (userinfo_location == LLDB_INVALID_ADDRESS)
145
0
      return false;
146
147
15
    size_t ptr_size = process_sp->GetAddressByteSize();
148
149
15
    userinfo_location += 4 * ptr_size;
150
15
    Status error;
151
15
    lldb::addr_t userinfo =
152
15
        process_sp->ReadPointerFromMemory(userinfo_location, error);
153
15
    if (userinfo == LLDB_INVALID_ADDRESS || error.Fail())
154
0
      return false;
155
15
    InferiorSizedWord isw(userinfo, *process_sp);
156
15
    m_child_sp = CreateValueObjectFromData(
157
15
        "_userInfo", isw.GetAsData(process_sp->GetByteOrder()),
158
15
        m_backend.GetExecutionContextRef(),
159
15
        ScratchTypeSystemClang::GetForTarget(process_sp->GetTarget())
160
15
            ->GetBasicType(lldb::eBasicTypeObjCID));
161
15
    return false;
162
15
  }
163
164
0
  bool MightHaveChildren() override { return true; }
165
166
0
  size_t GetIndexOfChildWithName(ConstString name) override {
167
0
    static ConstString g___userInfo("_userInfo");
168
0
    if (name == g___userInfo)
169
0
      return 0;
170
0
    return UINT32_MAX;
171
0
  }
172
173
private:
174
  // the child here can be "real" (i.e. an actual child of the root) or
175
  // synthetized from raw memory if the former, I need to store a plain pointer
176
  // to it - or else a loop of references will cause this entire hierarchy of
177
  // values to leak if the latter, then I need to store a SharedPointer to it -
178
  // so that it only goes away when everyone else in the cluster goes away oh
179
  // joy!
180
  ValueObject *m_child_ptr;
181
  ValueObjectSP m_child_sp;
182
};
183
184
SyntheticChildrenFrontEnd *
185
lldb_private::formatters::NSErrorSyntheticFrontEndCreator(
186
27
    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
187
27
  lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
188
27
  if (!process_sp)
189
0
    return nullptr;
190
27
  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
191
27
  if (!runtime)
192
0
    return nullptr;
193
194
27
  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
195
27
      runtime->GetClassDescriptor(*valobj_sp.get()));
196
197
27
  if (!descriptor.get() || 
!descriptor->IsValid()15
)
198
12
    return nullptr;
199
200
15
  const char *class_name = descriptor->GetClassName().GetCString();
201
202
15
  if (!class_name || !*class_name)
203
0
    return nullptr;
204
205
15
  if (!strcmp(class_name, "NSError"))
206
15
    return (new NSErrorSyntheticFrontEnd(valobj_sp));
207
0
  else if (!strcmp(class_name, "__NSCFError"))
208
0
    return (new NSErrorSyntheticFrontEnd(valobj_sp));
209
210
0
  return nullptr;
211
15
}