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 
26 using namespace lldb;
27 using namespace lldb_private;
28 using namespace lldb_private::formatters;
29 
30 bool 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 
46 bool 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 
110 bool 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 
228 bool 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