1//===-- CF.cpp ----------------------------------------------------*- C++
2//-*-===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#include "CF.h"
11
12#include "lldb/Core/ValueObject.h"
13#include "lldb/Core/ValueObjectConstResult.h"
14#include "lldb/DataFormatters/FormattersHelpers.h"
15#include "lldb/Symbol/ClangASTContext.h"
16#include "lldb/Target/Language.h"
17#include "lldb/Target/StackFrame.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/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
25
26using namespace lldb;
27using namespace lldb_private;
28using namespace lldb_private::formatters;
29
30bool lldb_private::formatters::CFAbsoluteTimeSummaryProvider(
31    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
32  time_t epoch = GetOSXEpoch();
33  epoch = epoch + (time_t)valobj.GetValueAsUnsigned(0);
34  tm *tm_date = localtime(&epoch);
35  if (!tm_date)
36    return false;
37  std::string buffer(1024, 0);
38  if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)
39    return false;
40  stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,
41                tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,
42                tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
43  return true;
44}
45
46bool lldb_private::formatters::CFBagSummaryProvider(
47    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
48  static ConstString g_TypeHint("CFBag");
49
50  ProcessSP process_sp = valobj.GetProcessSP();
51  if (!process_sp)
52    return false;
53
54  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
55
56  if (!runtime)
57    return false;
58
59  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
60      runtime->GetClassDescriptor(valobj));
61
62  if (!descriptor.get() || !descriptor->IsValid())
63    return false;
64
65  uint32_t ptr_size = process_sp->GetAddressByteSize();
66
67  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
68
69  if (!valobj_addr)
70    return false;
71
72  uint32_t count = 0;
73
74  bool is_type_ok = false; // check to see if this is a CFBag we know about
75  if (descriptor->IsCFType()) {
76    ConstString type_name(valobj.GetTypeName());
77
78    static ConstString g___CFBag("__CFBag");
79    static ConstString g_conststruct__CFBag("const struct __CFBag");
80
81    if (type_name == g___CFBag || type_name == g_conststruct__CFBag) {
82      if (valobj.IsPointerType())
83        is_type_ok = true;
84    }
85  }
86
87  if (is_type_ok) {
88    lldb::addr_t offset = 2 * ptr_size + 4 + valobj_addr;
89    Status error;
90    count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error);
91    if (error.Fail())
92      return false;
93  } else
94    return false;
95
96  std::string prefix, suffix;
97  if (Language *language = Language::FindPlugin(options.GetLanguage())) {
98    if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
99                                            suffix)) {
100      prefix.clear();
101      suffix.clear();
102    }
103  }
104
105  stream.Printf("%s\"%u value%s\"%s", prefix.c_str(), count,
106                (count == 1 ? "" : "s"), suffix.c_str());
107  return true;
108}
109
110bool lldb_private::formatters::CFBitVectorSummaryProvider(
111    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
112  ProcessSP process_sp = valobj.GetProcessSP();
113  if (!process_sp)
114    return false;
115
116  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
117
118  if (!runtime)
119    return false;
120
121  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
122      runtime->GetClassDescriptor(valobj));
123
124  if (!descriptor.get() || !descriptor->IsValid())
125    return false;
126
127  uint32_t ptr_size = process_sp->GetAddressByteSize();
128
129  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
130
131  if (!valobj_addr)
132    return false;
133
134  uint32_t count = 0;
135
136  bool is_type_ok = false; // check to see if this is a CFBag we know about
137  if (descriptor->IsCFType()) {
138    ConstString type_name(valobj.GetTypeName());
139    if (type_name == "__CFMutableBitVector" || type_name == "__CFBitVector" ||
140        type_name == "CFMutableBitVectorRef" || type_name == "CFBitVectorRef") {
141      if (valobj.IsPointerType())
142        is_type_ok = true;
143    }
144  }
145
146  if (!is_type_ok)
147    return false;
148
149  Status error;
150  count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + 2 * ptr_size,
151                                                    ptr_size, 0, error);
152  if (error.Fail())
153    return false;
154  uint64_t num_bytes = count / 8 + ((count & 7) ? 1 : 0);
155  addr_t data_ptr = process_sp->ReadPointerFromMemory(
156      valobj_addr + 2 * ptr_size + 2 * ptr_size, error);
157  if (error.Fail())
158    return false;
159  // make sure we do not try to read huge amounts of data
160  if (num_bytes > 1024)
161    num_bytes = 1024;
162  DataBufferSP buffer_sp(new DataBufferHeap(num_bytes, 0));
163  num_bytes =
164      process_sp->ReadMemory(data_ptr, buffer_sp->GetBytes(), num_bytes, error);
165  if (error.Fail() || num_bytes == 0)
166    return false;
167  uint8_t *bytes = buffer_sp->GetBytes();
168  for (uint64_t byte_idx = 0; byte_idx < num_bytes - 1; byte_idx++) {
169    uint8_t byte = bytes[byte_idx];
170    bool bit0 = (byte & 1) == 1;
171    bool bit1 = (byte & 2) == 2;
172    bool bit2 = (byte & 4) == 4;
173    bool bit3 = (byte & 8) == 8;
174    bool bit4 = (byte & 16) == 16;
175    bool bit5 = (byte & 32) == 32;
176    bool bit6 = (byte & 64) == 64;
177    bool bit7 = (byte & 128) == 128;
178    stream.Printf("%c%c%c%c %c%c%c%c ", (bit7 ? '1' : '0'), (bit6 ? '1' : '0'),
179                  (bit5 ? '1' : '0'), (bit4 ? '1' : '0'), (bit3 ? '1' : '0'),
180                  (bit2 ? '1' : '0'), (bit1 ? '1' : '0'), (bit0 ? '1' : '0'));
181    count -= 8;
182  }
183  {
184    // print the last byte ensuring we do not print spurious bits
185    uint8_t byte = bytes[num_bytes - 1];
186    bool bit0 = (byte & 1) == 1;
187    bool bit1 = (byte & 2) == 2;
188    bool bit2 = (byte & 4) == 4;
189    bool bit3 = (byte & 8) == 8;
190    bool bit4 = (byte & 16) == 16;
191    bool bit5 = (byte & 32) == 32;
192    bool bit6 = (byte & 64) == 64;
193    bool bit7 = (byte & 128) == 128;
194    if (count) {
195      stream.Printf("%c", bit7 ? '1' : '0');
196      count -= 1;
197    }
198    if (count) {
199      stream.Printf("%c", bit6 ? '1' : '0');
200      count -= 1;
201    }
202    if (count) {
203      stream.Printf("%c", bit5 ? '1' : '0');
204      count -= 1;
205    }
206    if (count) {
207      stream.Printf("%c", bit4 ? '1' : '0');
208      count -= 1;
209    }
210    if (count) {
211      stream.Printf("%c", bit3 ? '1' : '0');
212      count -= 1;
213    }
214    if (count) {
215      stream.Printf("%c", bit2 ? '1' : '0');
216      count -= 1;
217    }
218    if (count) {
219      stream.Printf("%c", bit1 ? '1' : '0');
220      count -= 1;
221    }
222    if (count)
223      stream.Printf("%c", bit0 ? '1' : '0');
224  }
225  return true;
226}
227
228bool lldb_private::formatters::CFBinaryHeapSummaryProvider(
229    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
230  static ConstString g_TypeHint("CFBinaryHeap");
231
232  ProcessSP process_sp = valobj.GetProcessSP();
233  if (!process_sp)
234    return false;
235
236  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
237
238  if (!runtime)
239    return false;
240
241  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
242      runtime->GetClassDescriptor(valobj));
243
244  if (!descriptor.get() || !descriptor->IsValid())
245    return false;
246
247  uint32_t ptr_size = process_sp->GetAddressByteSize();
248
249  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
250
251  if (!valobj_addr)
252    return false;
253
254  uint32_t count = 0;
255
256  bool is_type_ok =
257      false; // check to see if this is a CFBinaryHeap we know about
258  if (descriptor->IsCFType()) {
259    ConstString type_name(valobj.GetTypeName());
260
261    static ConstString g___CFBinaryHeap("__CFBinaryHeap");
262    static ConstString g_conststruct__CFBinaryHeap(
263        "const struct __CFBinaryHeap");
264    static ConstString g_CFBinaryHeapRef("CFBinaryHeapRef");
265
266    if (type_name == g___CFBinaryHeap ||
267        type_name == g_conststruct__CFBinaryHeap ||
268        type_name == g_CFBinaryHeapRef) {
269      if (valobj.IsPointerType())
270        is_type_ok = true;
271    }
272  }
273
274  if (is_type_ok) {
275    lldb::addr_t offset = 2 * ptr_size + valobj_addr;
276    Status error;
277    count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error);
278    if (error.Fail())
279      return false;
280  } else
281    return false;
282
283  std::string prefix, suffix;
284  if (Language *language = Language::FindPlugin(options.GetLanguage())) {
285    if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
286                                            suffix)) {
287      prefix.clear();
288      suffix.clear();
289    }
290  }
291
292  stream.Printf("%s\"%u item%s\"%s", prefix.c_str(), count,
293                (count == 1 ? "" : "s"), suffix.c_str());
294  return true;
295}
296