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