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
25using namespace lldb;
26using namespace lldb_private;
27using namespace lldb_private::formatters;
28
29bool lldb_private::formatters::CFAbsoluteTimeSummaryProvider(
30 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
31 time_t epoch = GetOSXEpoch();
32 epoch = epoch + (time_t)valobj.GetValueAsSigned(fail_value: 0);
33 tm *tm_date = localtime(timer: &epoch);
34 if (!tm_date)
35 return false;
36 std::string buffer(1024, 0);
37 if (strftime(s: &buffer[0], maxsize: 1023, format: "%Z", tp: tm_date) == 0)
38 return false;
39 stream.Printf(format: "%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
45bool 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&: *process_sp);
54
55 if (!runtime)
56 return false;
57
58 ObjCLanguageRuntime::ClassDescriptorSP descriptor(
59 runtime->GetClassDescriptor(in_value&: 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(fail_value: 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(load_addr: offset, byte_size: 4, fail_value: 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(language: options.GetLanguage()))
97 std::tie(args&: prefix, args&: suffix) = language->GetFormatterPrefixSuffix(type_hint: g_TypeHint);
98
99 stream << prefix;
100 stream.Printf(format: "\"%u value%s\"", count, (count == 1 ? "" : "s"));
101 stream << suffix;
102 return true;
103}
104
105bool 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&: *process_sp);
112
113 if (!runtime)
114 return false;
115
116 ObjCLanguageRuntime::ClassDescriptorSP descriptor(
117 runtime->GetClassDescriptor(in_value&: 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(fail_value: 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(load_addr: valobj_addr + 2 * ptr_size,
146 byte_size: ptr_size, fail_value: 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 vm_addr: 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(vm_addr: data_ptr, buf: buffer_sp->GetBytes(), size: 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(format: "%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(format: "%c", bit7 ? '1' : '0');
191 count -= 1;
192 }
193 if (count) {
194 stream.Printf(format: "%c", bit6 ? '1' : '0');
195 count -= 1;
196 }
197 if (count) {
198 stream.Printf(format: "%c", bit5 ? '1' : '0');
199 count -= 1;
200 }
201 if (count) {
202 stream.Printf(format: "%c", bit4 ? '1' : '0');
203 count -= 1;
204 }
205 if (count) {
206 stream.Printf(format: "%c", bit3 ? '1' : '0');
207 count -= 1;
208 }
209 if (count) {
210 stream.Printf(format: "%c", bit2 ? '1' : '0');
211 count -= 1;
212 }
213 if (count) {
214 stream.Printf(format: "%c", bit1 ? '1' : '0');
215 count -= 1;
216 }
217 if (count)
218 stream.Printf(format: "%c", bit0 ? '1' : '0');
219 }
220 return true;
221}
222
223bool 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&: *process_sp);
232
233 if (!runtime)
234 return false;
235
236 ObjCLanguageRuntime::ClassDescriptorSP descriptor(
237 runtime->GetClassDescriptor(in_value&: 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(fail_value: 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(load_addr: offset, byte_size: 4, fail_value: 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(language: options.GetLanguage()))
280 std::tie(args&: prefix, args&: suffix) = language->GetFormatterPrefixSuffix(type_hint: g_TypeHint);
281
282 stream << prefix;
283 stream.Printf(format: "\"%u item%s\"", count, (count == 1 ? "" : "s"));
284 stream << suffix;
285 return true;
286}
287

source code of lldb/source/Plugins/Language/ObjC/CF.cpp