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