1314564Sdim//===-- CF.cpp ----------------------------------------------------*- C++
2314564Sdim//-*-===//
3292932Sdim//
4353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5353358Sdim// See https://llvm.org/LICENSE.txt for license information.
6353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7292932Sdim//
8292932Sdim//===----------------------------------------------------------------------===//
9292932Sdim
10292932Sdim#include "CF.h"
11292932Sdim
12292932Sdim#include "lldb/Core/ValueObject.h"
13292932Sdim#include "lldb/Core/ValueObjectConstResult.h"
14292932Sdim#include "lldb/DataFormatters/FormattersHelpers.h"
15292932Sdim#include "lldb/Symbol/ClangASTContext.h"
16292932Sdim#include "lldb/Target/Language.h"
17292932Sdim#include "lldb/Target/StackFrame.h"
18292932Sdim#include "lldb/Target/Target.h"
19321369Sdim#include "lldb/Utility/DataBufferHeap.h"
20321369Sdim#include "lldb/Utility/Endian.h"
21321369Sdim#include "lldb/Utility/Status.h"
22321369Sdim#include "lldb/Utility/Stream.h"
23292932Sdim
24353358Sdim#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
25353358Sdim
26292932Sdimusing namespace lldb;
27292932Sdimusing namespace lldb_private;
28292932Sdimusing namespace lldb_private::formatters;
29292932Sdim
30314564Sdimbool lldb_private::formatters::CFAbsoluteTimeSummaryProvider(
31314564Sdim    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
32314564Sdim  time_t epoch = GetOSXEpoch();
33314564Sdim  epoch = epoch + (time_t)valobj.GetValueAsUnsigned(0);
34314564Sdim  tm *tm_date = localtime(&epoch);
35314564Sdim  if (!tm_date)
36314564Sdim    return false;
37314564Sdim  std::string buffer(1024, 0);
38314564Sdim  if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)
39314564Sdim    return false;
40314564Sdim  stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,
41314564Sdim                tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,
42314564Sdim                tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
43314564Sdim  return true;
44292932Sdim}
45292932Sdim
46314564Sdimbool lldb_private::formatters::CFBagSummaryProvider(
47314564Sdim    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
48314564Sdim  static ConstString g_TypeHint("CFBag");
49314564Sdim
50314564Sdim  ProcessSP process_sp = valobj.GetProcessSP();
51314564Sdim  if (!process_sp)
52314564Sdim    return false;
53314564Sdim
54353358Sdim  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
55314564Sdim
56314564Sdim  if (!runtime)
57314564Sdim    return false;
58314564Sdim
59314564Sdim  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
60314564Sdim      runtime->GetClassDescriptor(valobj));
61314564Sdim
62314564Sdim  if (!descriptor.get() || !descriptor->IsValid())
63314564Sdim    return false;
64314564Sdim
65314564Sdim  uint32_t ptr_size = process_sp->GetAddressByteSize();
66314564Sdim
67314564Sdim  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
68314564Sdim
69314564Sdim  if (!valobj_addr)
70314564Sdim    return false;
71314564Sdim
72314564Sdim  uint32_t count = 0;
73314564Sdim
74314564Sdim  bool is_type_ok = false; // check to see if this is a CFBag we know about
75314564Sdim  if (descriptor->IsCFType()) {
76314564Sdim    ConstString type_name(valobj.GetTypeName());
77314564Sdim
78314564Sdim    static ConstString g___CFBag("__CFBag");
79314564Sdim    static ConstString g_conststruct__CFBag("const struct __CFBag");
80314564Sdim
81314564Sdim    if (type_name == g___CFBag || type_name == g_conststruct__CFBag) {
82314564Sdim      if (valobj.IsPointerType())
83314564Sdim        is_type_ok = true;
84292932Sdim    }
85314564Sdim  }
86314564Sdim
87314564Sdim  if (is_type_ok) {
88314564Sdim    lldb::addr_t offset = 2 * ptr_size + 4 + valobj_addr;
89321369Sdim    Status error;
90314564Sdim    count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error);
91314564Sdim    if (error.Fail())
92314564Sdim      return false;
93314564Sdim  } else
94314564Sdim    return false;
95314564Sdim
96314564Sdim  std::string prefix, suffix;
97314564Sdim  if (Language *language = Language::FindPlugin(options.GetLanguage())) {
98314564Sdim    if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
99314564Sdim                                            suffix)) {
100314564Sdim      prefix.clear();
101314564Sdim      suffix.clear();
102292932Sdim    }
103314564Sdim  }
104314564Sdim
105314564Sdim  stream.Printf("%s\"%u value%s\"%s", prefix.c_str(), count,
106314564Sdim                (count == 1 ? "" : "s"), suffix.c_str());
107314564Sdim  return true;
108292932Sdim}
109292932Sdim
110314564Sdimbool lldb_private::formatters::CFBitVectorSummaryProvider(
111314564Sdim    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
112314564Sdim  ProcessSP process_sp = valobj.GetProcessSP();
113314564Sdim  if (!process_sp)
114314564Sdim    return false;
115314564Sdim
116353358Sdim  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
117314564Sdim
118314564Sdim  if (!runtime)
119314564Sdim    return false;
120314564Sdim
121314564Sdim  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
122314564Sdim      runtime->GetClassDescriptor(valobj));
123314564Sdim
124314564Sdim  if (!descriptor.get() || !descriptor->IsValid())
125314564Sdim    return false;
126314564Sdim
127314564Sdim  uint32_t ptr_size = process_sp->GetAddressByteSize();
128314564Sdim
129314564Sdim  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
130314564Sdim
131314564Sdim  if (!valobj_addr)
132314564Sdim    return false;
133314564Sdim
134314564Sdim  uint32_t count = 0;
135314564Sdim
136314564Sdim  bool is_type_ok = false; // check to see if this is a CFBag we know about
137314564Sdim  if (descriptor->IsCFType()) {
138314564Sdim    ConstString type_name(valobj.GetTypeName());
139353358Sdim    if (type_name == "__CFMutableBitVector" || type_name == "__CFBitVector" ||
140353358Sdim        type_name == "CFMutableBitVectorRef" || type_name == "CFBitVectorRef") {
141314564Sdim      if (valobj.IsPointerType())
142314564Sdim        is_type_ok = true;
143292932Sdim    }
144314564Sdim  }
145314564Sdim
146344779Sdim  if (!is_type_ok)
147314564Sdim    return false;
148314564Sdim
149321369Sdim  Status error;
150314564Sdim  count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + 2 * ptr_size,
151314564Sdim                                                    ptr_size, 0, error);
152314564Sdim  if (error.Fail())
153314564Sdim    return false;
154314564Sdim  uint64_t num_bytes = count / 8 + ((count & 7) ? 1 : 0);
155314564Sdim  addr_t data_ptr = process_sp->ReadPointerFromMemory(
156314564Sdim      valobj_addr + 2 * ptr_size + 2 * ptr_size, error);
157314564Sdim  if (error.Fail())
158314564Sdim    return false;
159314564Sdim  // make sure we do not try to read huge amounts of data
160314564Sdim  if (num_bytes > 1024)
161314564Sdim    num_bytes = 1024;
162314564Sdim  DataBufferSP buffer_sp(new DataBufferHeap(num_bytes, 0));
163314564Sdim  num_bytes =
164314564Sdim      process_sp->ReadMemory(data_ptr, buffer_sp->GetBytes(), num_bytes, error);
165314564Sdim  if (error.Fail() || num_bytes == 0)
166314564Sdim    return false;
167314564Sdim  uint8_t *bytes = buffer_sp->GetBytes();
168314564Sdim  for (uint64_t byte_idx = 0; byte_idx < num_bytes - 1; byte_idx++) {
169314564Sdim    uint8_t byte = bytes[byte_idx];
170314564Sdim    bool bit0 = (byte & 1) == 1;
171314564Sdim    bool bit1 = (byte & 2) == 2;
172314564Sdim    bool bit2 = (byte & 4) == 4;
173314564Sdim    bool bit3 = (byte & 8) == 8;
174314564Sdim    bool bit4 = (byte & 16) == 16;
175314564Sdim    bool bit5 = (byte & 32) == 32;
176314564Sdim    bool bit6 = (byte & 64) == 64;
177314564Sdim    bool bit7 = (byte & 128) == 128;
178314564Sdim    stream.Printf("%c%c%c%c %c%c%c%c ", (bit7 ? '1' : '0'), (bit6 ? '1' : '0'),
179314564Sdim                  (bit5 ? '1' : '0'), (bit4 ? '1' : '0'), (bit3 ? '1' : '0'),
180314564Sdim                  (bit2 ? '1' : '0'), (bit1 ? '1' : '0'), (bit0 ? '1' : '0'));
181314564Sdim    count -= 8;
182314564Sdim  }
183314564Sdim  {
184314564Sdim    // print the last byte ensuring we do not print spurious bits
185314564Sdim    uint8_t byte = bytes[num_bytes - 1];
186314564Sdim    bool bit0 = (byte & 1) == 1;
187314564Sdim    bool bit1 = (byte & 2) == 2;
188314564Sdim    bool bit2 = (byte & 4) == 4;
189314564Sdim    bool bit3 = (byte & 8) == 8;
190314564Sdim    bool bit4 = (byte & 16) == 16;
191314564Sdim    bool bit5 = (byte & 32) == 32;
192314564Sdim    bool bit6 = (byte & 64) == 64;
193314564Sdim    bool bit7 = (byte & 128) == 128;
194314564Sdim    if (count) {
195314564Sdim      stream.Printf("%c", bit7 ? '1' : '0');
196314564Sdim      count -= 1;
197292932Sdim    }
198314564Sdim    if (count) {
199314564Sdim      stream.Printf("%c", bit6 ? '1' : '0');
200314564Sdim      count -= 1;
201292932Sdim    }
202314564Sdim    if (count) {
203314564Sdim      stream.Printf("%c", bit5 ? '1' : '0');
204314564Sdim      count -= 1;
205314564Sdim    }
206314564Sdim    if (count) {
207314564Sdim      stream.Printf("%c", bit4 ? '1' : '0');
208314564Sdim      count -= 1;
209314564Sdim    }
210314564Sdim    if (count) {
211314564Sdim      stream.Printf("%c", bit3 ? '1' : '0');
212314564Sdim      count -= 1;
213314564Sdim    }
214314564Sdim    if (count) {
215314564Sdim      stream.Printf("%c", bit2 ? '1' : '0');
216314564Sdim      count -= 1;
217314564Sdim    }
218314564Sdim    if (count) {
219314564Sdim      stream.Printf("%c", bit1 ? '1' : '0');
220314564Sdim      count -= 1;
221314564Sdim    }
222314564Sdim    if (count)
223314564Sdim      stream.Printf("%c", bit0 ? '1' : '0');
224314564Sdim  }
225314564Sdim  return true;
226292932Sdim}
227292932Sdim
228314564Sdimbool lldb_private::formatters::CFBinaryHeapSummaryProvider(
229314564Sdim    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
230314564Sdim  static ConstString g_TypeHint("CFBinaryHeap");
231309124Sdim
232314564Sdim  ProcessSP process_sp = valobj.GetProcessSP();
233314564Sdim  if (!process_sp)
234314564Sdim    return false;
235314564Sdim
236353358Sdim  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
237314564Sdim
238314564Sdim  if (!runtime)
239314564Sdim    return false;
240314564Sdim
241314564Sdim  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
242314564Sdim      runtime->GetClassDescriptor(valobj));
243314564Sdim
244314564Sdim  if (!descriptor.get() || !descriptor->IsValid())
245314564Sdim    return false;
246314564Sdim
247314564Sdim  uint32_t ptr_size = process_sp->GetAddressByteSize();
248314564Sdim
249314564Sdim  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
250314564Sdim
251314564Sdim  if (!valobj_addr)
252314564Sdim    return false;
253314564Sdim
254314564Sdim  uint32_t count = 0;
255314564Sdim
256314564Sdim  bool is_type_ok =
257314564Sdim      false; // check to see if this is a CFBinaryHeap we know about
258314564Sdim  if (descriptor->IsCFType()) {
259314564Sdim    ConstString type_name(valobj.GetTypeName());
260314564Sdim
261314564Sdim    static ConstString g___CFBinaryHeap("__CFBinaryHeap");
262314564Sdim    static ConstString g_conststruct__CFBinaryHeap(
263314564Sdim        "const struct __CFBinaryHeap");
264314564Sdim    static ConstString g_CFBinaryHeapRef("CFBinaryHeapRef");
265314564Sdim
266314564Sdim    if (type_name == g___CFBinaryHeap ||
267314564Sdim        type_name == g_conststruct__CFBinaryHeap ||
268314564Sdim        type_name == g_CFBinaryHeapRef) {
269314564Sdim      if (valobj.IsPointerType())
270314564Sdim        is_type_ok = true;
271292932Sdim    }
272314564Sdim  }
273314564Sdim
274314564Sdim  if (is_type_ok) {
275314564Sdim    lldb::addr_t offset = 2 * ptr_size + valobj_addr;
276321369Sdim    Status error;
277314564Sdim    count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error);
278314564Sdim    if (error.Fail())
279314564Sdim      return false;
280314564Sdim  } else
281314564Sdim    return false;
282314564Sdim
283314564Sdim  std::string prefix, suffix;
284314564Sdim  if (Language *language = Language::FindPlugin(options.GetLanguage())) {
285314564Sdim    if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
286314564Sdim                                            suffix)) {
287314564Sdim      prefix.clear();
288314564Sdim      suffix.clear();
289292932Sdim    }
290314564Sdim  }
291314564Sdim
292314564Sdim  stream.Printf("%s\"%u item%s\"%s", prefix.c_str(), count,
293314564Sdim                (count == 1 ? "" : "s"), suffix.c_str());
294314564Sdim  return true;
295292932Sdim}
296