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 constexpr llvm::StringLiteral 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   llvm::StringRef prefix, suffix;
96   if (Language *language = Language::FindPlugin(options.GetLanguage()))
97     std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
98 
99   stream << prefix;
100   stream.Printf("\"%u value%s\"", count, (count == 1 ? "" : "s"));
101   stream << suffix;
102   return true;
103 }
104 
105 bool lldb_private::formatters::CFBitVectorSummaryProvider(
106     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
107   ProcessSP process_sp = valobj.GetProcessSP();
108   if (!process_sp)
109     return false;
110 
111   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
112 
113   if (!runtime)
114     return false;
115 
116   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
117       runtime->GetClassDescriptor(valobj));
118 
119   if (!descriptor.get() || !descriptor->IsValid())
120     return false;
121 
122   uint32_t ptr_size = process_sp->GetAddressByteSize();
123 
124   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
125 
126   if (!valobj_addr)
127     return false;
128 
129   uint32_t count = 0;
130 
131   bool is_type_ok = false; // check to see if this is a CFBag we know about
132   if (descriptor->IsCFType()) {
133     ConstString type_name(valobj.GetTypeName());
134     if (type_name == "__CFMutableBitVector" || type_name == "__CFBitVector" ||
135         type_name == "CFMutableBitVectorRef" || type_name == "CFBitVectorRef") {
136       if (valobj.IsPointerType())
137         is_type_ok = true;
138     }
139   }
140 
141   if (!is_type_ok)
142     return false;
143 
144   Status error;
145   count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + 2 * ptr_size,
146                                                     ptr_size, 0, error);
147   if (error.Fail())
148     return false;
149   uint64_t num_bytes = count / 8 + ((count & 7) ? 1 : 0);
150   addr_t data_ptr = process_sp->ReadPointerFromMemory(
151       valobj_addr + 2 * ptr_size + 2 * ptr_size, error);
152   if (error.Fail())
153     return false;
154   // make sure we do not try to read huge amounts of data
155   if (num_bytes > 1024)
156     num_bytes = 1024;
157   WritableDataBufferSP buffer_sp(new DataBufferHeap(num_bytes, 0));
158   num_bytes =
159       process_sp->ReadMemory(data_ptr, buffer_sp->GetBytes(), num_bytes, error);
160   if (error.Fail() || num_bytes == 0)
161     return false;
162   uint8_t *bytes = buffer_sp->GetBytes();
163   for (uint64_t byte_idx = 0; byte_idx < num_bytes - 1; byte_idx++) {
164     uint8_t byte = bytes[byte_idx];
165     bool bit0 = (byte & 1) == 1;
166     bool bit1 = (byte & 2) == 2;
167     bool bit2 = (byte & 4) == 4;
168     bool bit3 = (byte & 8) == 8;
169     bool bit4 = (byte & 16) == 16;
170     bool bit5 = (byte & 32) == 32;
171     bool bit6 = (byte & 64) == 64;
172     bool bit7 = (byte & 128) == 128;
173     stream.Printf("%c%c%c%c %c%c%c%c ", (bit7 ? '1' : '0'), (bit6 ? '1' : '0'),
174                   (bit5 ? '1' : '0'), (bit4 ? '1' : '0'), (bit3 ? '1' : '0'),
175                   (bit2 ? '1' : '0'), (bit1 ? '1' : '0'), (bit0 ? '1' : '0'));
176     count -= 8;
177   }
178   {
179     // print the last byte ensuring we do not print spurious bits
180     uint8_t byte = bytes[num_bytes - 1];
181     bool bit0 = (byte & 1) == 1;
182     bool bit1 = (byte & 2) == 2;
183     bool bit2 = (byte & 4) == 4;
184     bool bit3 = (byte & 8) == 8;
185     bool bit4 = (byte & 16) == 16;
186     bool bit5 = (byte & 32) == 32;
187     bool bit6 = (byte & 64) == 64;
188     bool bit7 = (byte & 128) == 128;
189     if (count) {
190       stream.Printf("%c", bit7 ? '1' : '0');
191       count -= 1;
192     }
193     if (count) {
194       stream.Printf("%c", bit6 ? '1' : '0');
195       count -= 1;
196     }
197     if (count) {
198       stream.Printf("%c", bit5 ? '1' : '0');
199       count -= 1;
200     }
201     if (count) {
202       stream.Printf("%c", bit4 ? '1' : '0');
203       count -= 1;
204     }
205     if (count) {
206       stream.Printf("%c", bit3 ? '1' : '0');
207       count -= 1;
208     }
209     if (count) {
210       stream.Printf("%c", bit2 ? '1' : '0');
211       count -= 1;
212     }
213     if (count) {
214       stream.Printf("%c", bit1 ? '1' : '0');
215       count -= 1;
216     }
217     if (count)
218       stream.Printf("%c", bit0 ? '1' : '0');
219   }
220   return true;
221 }
222 
223 bool lldb_private::formatters::CFBinaryHeapSummaryProvider(
224     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
225   static constexpr llvm::StringLiteral g_TypeHint("CFBinaryHeap");
226 
227   ProcessSP process_sp = valobj.GetProcessSP();
228   if (!process_sp)
229     return false;
230 
231   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
232 
233   if (!runtime)
234     return false;
235 
236   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
237       runtime->GetClassDescriptor(valobj));
238 
239   if (!descriptor.get() || !descriptor->IsValid())
240     return false;
241 
242   uint32_t ptr_size = process_sp->GetAddressByteSize();
243 
244   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
245 
246   if (!valobj_addr)
247     return false;
248 
249   uint32_t count = 0;
250 
251   bool is_type_ok =
252       false; // check to see if this is a CFBinaryHeap we know about
253   if (descriptor->IsCFType()) {
254     ConstString type_name(valobj.GetTypeName());
255 
256     static ConstString g_CFBinaryHeap("__CFBinaryHeap");
257     static ConstString g_conststruct__CFBinaryHeap(
258         "const struct __CFBinaryHeap");
259     static ConstString g_CFBinaryHeapRef("CFBinaryHeapRef");
260 
261     if (type_name == g_CFBinaryHeap ||
262         type_name == g_conststruct__CFBinaryHeap ||
263         type_name == g_CFBinaryHeapRef) {
264       if (valobj.IsPointerType())
265         is_type_ok = true;
266     }
267   }
268 
269   if (is_type_ok) {
270     lldb::addr_t offset = 2 * ptr_size + valobj_addr;
271     Status error;
272     count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error);
273     if (error.Fail())
274       return false;
275   } else
276     return false;
277 
278   llvm::StringRef prefix, suffix;
279   if (Language *language = Language::FindPlugin(options.GetLanguage()))
280     std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
281 
282   stream << prefix;
283   stream.Printf("\"%u item%s\"", count, (count == 1 ? "" : "s"));
284   stream << suffix;
285   return true;
286 }
287