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 == "__CFMutableBitVector" || type_name == "__CFBitVector" ||
143         type_name == "CFMutableBitVectorRef" || type_name == "CFBitVectorRef") {
144       if (valobj.IsPointerType())
145         is_type_ok = true;
146     }
147   }
148 
149   if (!is_type_ok)
150     return false;
151 
152   Status error;
153   count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + 2 * ptr_size,
154                                                     ptr_size, 0, error);
155   if (error.Fail())
156     return false;
157   uint64_t num_bytes = count / 8 + ((count & 7) ? 1 : 0);
158   addr_t data_ptr = process_sp->ReadPointerFromMemory(
159       valobj_addr + 2 * ptr_size + 2 * ptr_size, error);
160   if (error.Fail())
161     return false;
162   // make sure we do not try to read huge amounts of data
163   if (num_bytes > 1024)
164     num_bytes = 1024;
165   DataBufferSP buffer_sp(new DataBufferHeap(num_bytes, 0));
166   num_bytes =
167       process_sp->ReadMemory(data_ptr, buffer_sp->GetBytes(), num_bytes, error);
168   if (error.Fail() || num_bytes == 0)
169     return false;
170   uint8_t *bytes = buffer_sp->GetBytes();
171   for (uint64_t byte_idx = 0; byte_idx < num_bytes - 1; byte_idx++) {
172     uint8_t byte = bytes[byte_idx];
173     bool bit0 = (byte & 1) == 1;
174     bool bit1 = (byte & 2) == 2;
175     bool bit2 = (byte & 4) == 4;
176     bool bit3 = (byte & 8) == 8;
177     bool bit4 = (byte & 16) == 16;
178     bool bit5 = (byte & 32) == 32;
179     bool bit6 = (byte & 64) == 64;
180     bool bit7 = (byte & 128) == 128;
181     stream.Printf("%c%c%c%c %c%c%c%c ", (bit7 ? '1' : '0'), (bit6 ? '1' : '0'),
182                   (bit5 ? '1' : '0'), (bit4 ? '1' : '0'), (bit3 ? '1' : '0'),
183                   (bit2 ? '1' : '0'), (bit1 ? '1' : '0'), (bit0 ? '1' : '0'));
184     count -= 8;
185   }
186   {
187     // print the last byte ensuring we do not print spurious bits
188     uint8_t byte = bytes[num_bytes - 1];
189     bool bit0 = (byte & 1) == 1;
190     bool bit1 = (byte & 2) == 2;
191     bool bit2 = (byte & 4) == 4;
192     bool bit3 = (byte & 8) == 8;
193     bool bit4 = (byte & 16) == 16;
194     bool bit5 = (byte & 32) == 32;
195     bool bit6 = (byte & 64) == 64;
196     bool bit7 = (byte & 128) == 128;
197     if (count) {
198       stream.Printf("%c", bit7 ? '1' : '0');
199       count -= 1;
200     }
201     if (count) {
202       stream.Printf("%c", bit6 ? '1' : '0');
203       count -= 1;
204     }
205     if (count) {
206       stream.Printf("%c", bit5 ? '1' : '0');
207       count -= 1;
208     }
209     if (count) {
210       stream.Printf("%c", bit4 ? '1' : '0');
211       count -= 1;
212     }
213     if (count) {
214       stream.Printf("%c", bit3 ? '1' : '0');
215       count -= 1;
216     }
217     if (count) {
218       stream.Printf("%c", bit2 ? '1' : '0');
219       count -= 1;
220     }
221     if (count) {
222       stream.Printf("%c", bit1 ? '1' : '0');
223       count -= 1;
224     }
225     if (count)
226       stream.Printf("%c", bit0 ? '1' : '0');
227   }
228   return true;
229 }
230 
231 bool lldb_private::formatters::CFBinaryHeapSummaryProvider(
232     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
233   static ConstString g_TypeHint("CFBinaryHeap");
234 
235   ProcessSP process_sp = valobj.GetProcessSP();
236   if (!process_sp)
237     return false;
238 
239   ObjCLanguageRuntime *runtime =
240       (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
241           lldb::eLanguageTypeObjC);
242 
243   if (!runtime)
244     return false;
245 
246   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
247       runtime->GetClassDescriptor(valobj));
248 
249   if (!descriptor.get() || !descriptor->IsValid())
250     return false;
251 
252   uint32_t ptr_size = process_sp->GetAddressByteSize();
253 
254   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
255 
256   if (!valobj_addr)
257     return false;
258 
259   uint32_t count = 0;
260 
261   bool is_type_ok =
262       false; // check to see if this is a CFBinaryHeap we know about
263   if (descriptor->IsCFType()) {
264     ConstString type_name(valobj.GetTypeName());
265 
266     static ConstString g___CFBinaryHeap("__CFBinaryHeap");
267     static ConstString g_conststruct__CFBinaryHeap(
268         "const struct __CFBinaryHeap");
269     static ConstString g_CFBinaryHeapRef("CFBinaryHeapRef");
270 
271     if (type_name == g___CFBinaryHeap ||
272         type_name == g_conststruct__CFBinaryHeap ||
273         type_name == g_CFBinaryHeapRef) {
274       if (valobj.IsPointerType())
275         is_type_ok = true;
276     }
277   }
278 
279   if (is_type_ok) {
280     lldb::addr_t offset = 2 * ptr_size + valobj_addr;
281     Status error;
282     count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error);
283     if (error.Fail())
284       return false;
285   } else
286     return false;
287 
288   std::string prefix, suffix;
289   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
290     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
291                                             suffix)) {
292       prefix.clear();
293       suffix.clear();
294     }
295   }
296 
297   stream.Printf("%s\"%u item%s\"%s", prefix.c_str(), count,
298                 (count == 1 ? "" : "s"), suffix.c_str());
299   return true;
300 }
301