LLDB mainline
NSString.cpp
Go to the documentation of this file.
1//===-- NSString.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 "NSString.h"
10
14#include "lldb/Target/Target.h"
17#include "lldb/Utility/Endian.h"
18#include "lldb/Utility/Status.h"
19#include "lldb/Utility/Stream.h"
22
23#include "llvm/Support/Error.h"
24
25using namespace lldb;
26using namespace lldb_private;
27using namespace lldb_private::formatters;
28
29std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
31 static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
32 return g_map;
33}
34
36 ValueObject &valobj, Stream &stream,
37 const TypeSummaryOptions &summary_options) {
38 static constexpr llvm::StringLiteral g_TypeHint("NSString");
39
40 ProcessSP process_sp = valobj.GetProcessSP();
41 if (!process_sp)
42 return false;
43
44 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
45
46 if (!runtime)
47 return false;
48
50 runtime->GetClassDescriptor(valobj));
51
52 if (!descriptor.get() || !descriptor->IsValid())
53 return false;
54
55 uint32_t ptr_size = process_sp->GetAddressByteSize();
56
57 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
58
59 if (!valobj_addr)
60 return false;
61
62 ConstString class_name_cs = descriptor->GetClassName();
63 llvm::StringRef class_name = class_name_cs.GetStringRef();
64
65 if (class_name.empty())
66 return false;
67
68 // For tagged pointers, the descriptor has everything needed.
69 bool is_tagged = descriptor->GetTaggedPointerInfo();
70 if (is_tagged) {
71 if (class_name == "NSTaggedPointerString")
72 return NSTaggedString_SummaryProvider(valobj, descriptor, stream,
73 summary_options);
74
75 if (class_name == "NSIndirectTaggedPointerString")
76 return NSIndirectTaggedString_SummaryProvider(valobj, descriptor, stream,
77 summary_options);
78 }
79
80 auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());
81 auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();
82 if (iter != end)
83 return iter->second(valobj, stream, summary_options);
84
85 // if not a tagged pointer that we know about, try the normal route
86 uint64_t info_bits_location = valobj_addr + ptr_size;
87 if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)
88 info_bits_location += 3;
89
91
92 uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(
93 info_bits_location, 1, 0, error);
94 if (error.Fail())
95 return false;
96
97 bool is_mutable = (info_bits & 1) == 1;
98 bool is_inline = (info_bits & 0x60) == 0;
99 bool has_explicit_length = (info_bits & (1 | 4)) != 4;
100 bool is_unicode = (info_bits & 0x10) == 0x10;
101 bool is_path_store = class_name == "NSPathStore2";
102 bool has_null = (info_bits & 8) == 8;
103
104 size_t explicit_length = 0;
105 if (!has_null && has_explicit_length && !is_path_store) {
106 lldb::addr_t explicit_length_offset = 2 * ptr_size;
107 if (is_mutable && !is_inline)
108 explicit_length_offset =
109 explicit_length_offset + ptr_size; // notInlineMutable.length;
110 else if (is_inline)
111 explicit_length = explicit_length + 0; // inline1.length;
112 else if (!is_inline && !is_mutable)
113 explicit_length_offset =
114 explicit_length_offset + ptr_size; // notInlineImmutable1.length;
115 else
116 explicit_length_offset = 0;
117
118 if (explicit_length_offset) {
119 explicit_length_offset = valobj_addr + explicit_length_offset;
120 explicit_length = process_sp->ReadUnsignedIntegerFromMemory(
121 explicit_length_offset, 4, 0, error);
122 }
123 }
124
125 const llvm::StringSet<> supported_string_classes = {
126 "NSString", "CFMutableStringRef",
127 "CFStringRef", "__NSCFConstantString",
128 "__NSCFString", "NSCFConstantString",
129 "NSCFString", "NSPathStore2"};
130 if (supported_string_classes.count(class_name) == 0) {
131 // not one of us - but tell me class name
132 stream.Printf("class name = %s", class_name_cs.GetCString());
133 return true;
134 }
135
136 llvm::StringRef prefix, suffix;
137 if (Language *language = Language::FindPlugin(summary_options.GetLanguage()))
138 std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
139
141 options.SetPrefixToken(prefix.str());
142 options.SetSuffixToken(suffix.str());
143
144 if (is_mutable) {
145 uint64_t location = 2 * ptr_size + valobj_addr;
146 llvm::Expected<lldb::addr_t> location_or_err =
147 process_sp->ReadPointerFromMemory(location);
148 if (!location_or_err) {
149 llvm::consumeError(location_or_err.takeError());
150 return false;
151 }
152 location = *location_or_err;
153 if (has_explicit_length && is_unicode) {
154 options.SetLocation(Address(location));
155 options.SetTargetSP(valobj.GetTargetSP());
156 options.SetStream(&stream);
157 options.SetQuote('"');
158 options.SetSourceSize(explicit_length);
159 options.SetHasSourceSize(has_explicit_length);
161 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
164 StringPrinter::StringElementType::UTF16>(options);
165 } else {
166 options.SetLocation(Address(location + 1));
167 options.SetTargetSP(valobj.GetTargetSP());
168 options.SetStream(&stream);
169 options.SetSourceSize(explicit_length);
170 options.SetHasSourceSize(has_explicit_length);
172 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
175 StringPrinter::StringElementType::ASCII>(options);
176 }
177 } else if (is_inline && has_explicit_length && !is_unicode &&
178 !is_path_store && !is_mutable) {
179 uint64_t location = 3 * ptr_size + valobj_addr;
180
181 options.SetLocation(Address(location));
182 options.SetTargetSP(valobj.GetTargetSP());
183 options.SetStream(&stream);
184 options.SetQuote('"');
185 options.SetSourceSize(explicit_length);
186 options.SetHasSourceSize(has_explicit_length);
187 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
190 StringPrinter::StringElementType::ASCII>(options);
191 } else if (is_unicode) {
192 uint64_t location = valobj_addr + 2 * ptr_size;
193 if (is_inline) {
194 if (!has_explicit_length) {
195 return false;
196 } else
197 location += ptr_size;
198 } else {
199 llvm::Expected<lldb::addr_t> location_or_err =
200 process_sp->ReadPointerFromMemory(location);
201 if (!location_or_err) {
202 llvm::consumeError(location_or_err.takeError());
203 return false;
204 }
205 location = *location_or_err;
206 }
207 options.SetLocation(Address(location));
208 options.SetTargetSP(valobj.GetTargetSP());
209 options.SetStream(&stream);
210 options.SetQuote('"');
211 options.SetSourceSize(explicit_length);
212 options.SetHasSourceSize(has_explicit_length);
213 if (has_explicit_length)
215 else
217 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
220 StringPrinter::StringElementType::UTF16>(options);
221 } else if (is_path_store) {
222 // _lengthAndRefCount is the first ivar of NSPathStore2 (after the isa).
223 uint64_t length_ivar_offset = 1 * ptr_size;
224 CompilerType length_type = valobj.GetCompilerType().GetBasicTypeFromAST(
226 ValueObjectSP length_valobj_sp =
227 valobj.GetSyntheticChildAtOffset(length_ivar_offset, length_type, true,
228 ConstString("_lengthAndRefCount"));
229 if (!length_valobj_sp)
230 return false;
231 // Get the length out of _lengthAndRefCount.
232 explicit_length = length_valobj_sp->GetValueAsUnsigned(0) >> 20;
233 lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
234
235 options.SetLocation(Address(location));
236 options.SetTargetSP(valobj.GetTargetSP());
237 options.SetStream(&stream);
238 options.SetQuote('"');
239 options.SetSourceSize(explicit_length);
240 options.SetHasSourceSize(has_explicit_length);
241 if (has_explicit_length)
243 else
245 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
248 StringPrinter::StringElementType::UTF16>(options);
249 } else if (is_inline) {
250 uint64_t location = valobj_addr + 2 * ptr_size;
251 if (!has_explicit_length) {
252 // in this kind of string, the byte before the string content is a length
253 // byte so let's try and use it to handle the embedded NUL case
255 explicit_length =
256 process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
257 has_explicit_length = !(error.Fail() || explicit_length == 0);
258 location++;
259 }
260 options.SetLocation(Address(location));
261 options.SetTargetSP(valobj.GetTargetSP());
262 options.SetStream(&stream);
263 options.SetSourceSize(explicit_length);
264 options.SetHasSourceSize(has_explicit_length);
265 if (has_explicit_length)
267 else
269 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
271 if (has_explicit_length)
273 StringPrinter::StringElementType::UTF8>(options);
274 else
276 StringPrinter::StringElementType::ASCII>(options);
277 } else {
278 uint64_t location = valobj_addr + 2 * ptr_size;
279 llvm::Expected<lldb::addr_t> location_or_err =
280 process_sp->ReadPointerFromMemory(location);
281 if (!location_or_err) {
282 llvm::consumeError(location_or_err.takeError());
283 return false;
284 }
285 location = *location_or_err;
286 if (has_explicit_length && !has_null)
287 explicit_length++; // account for the fact that there is no NULL and we
288 // need to have one added
289 options.SetLocation(Address(location));
290 options.SetTargetSP(valobj.GetTargetSP());
291 options.SetStream(&stream);
292 options.SetSourceSize(explicit_length);
293 options.SetHasSourceSize(has_explicit_length);
294 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
297 StringPrinter::StringElementType::ASCII>(options);
298 }
299}
300
302 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
303 TargetSP target_sp(valobj.GetTargetSP());
304 if (!target_sp)
305 return false;
306 uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();
307 uint64_t pointer_value = valobj.GetValueAsUnsigned(0);
308 if (!pointer_value)
309 return false;
310 pointer_value += addr_size;
311 CompilerType type(valobj.GetCompilerType());
312 ExecutionContext exe_ctx(target_sp, false);
314 "string_ptr", pointer_value, exe_ctx, type));
315 if (!child_ptr_sp)
316 return false;
317 DataExtractor data;
319 child_ptr_sp->GetData(data, error);
320 if (error.Fail())
321 return false;
322 ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData(
323 "string_data", data, exe_ctx, type));
324 child_sp->GetValueAsUnsigned(0);
325 if (child_sp)
326 return NSStringSummaryProvider(*child_sp, stream, options);
327 return false;
328}
329
331 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
332 return NSAttributedStringSummaryProvider(valobj, stream, options);
333}
334
337 Stream &stream, const TypeSummaryOptions &summary_options) {
338 static constexpr llvm::StringLiteral g_TypeHint("NSString");
339
340 if (!descriptor)
341 return false;
342 uint64_t len_bits = 0, data_bits = 0;
343 if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr))
344 return false;
345
346 static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN
347 static const int g_SixbitMaxLen = 9;
348 static const int g_fiveBitMaxLen = 11;
349
350 static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013"
351 "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
352
353 if (len_bits > g_fiveBitMaxLen)
354 return false;
355
356 llvm::StringRef prefix, suffix;
357 if (Language *language = Language::FindPlugin(summary_options.GetLanguage()))
358 std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
359
360 // this is a fairly ugly trick - pretend that the numeric value is actually a
361 // char* this works under a few assumptions: little endian architecture
362 // sizeof(uint64_t) > g_MaxNonBitmaskedLen
363 if (len_bits <= g_MaxNonBitmaskedLen) {
364 stream << prefix;
365 stream.Printf("\"%s\"", (const char *)&data_bits);
366 stream << suffix;
367 return true;
368 }
369
370 // if the data is bitmasked, we need to actually process the bytes
371 uint8_t bitmask = 0;
372 uint8_t shift_offset = 0;
373
374 if (len_bits <= g_SixbitMaxLen) {
375 bitmask = 0x03f;
376 shift_offset = 6;
377 } else {
378 bitmask = 0x01f;
379 shift_offset = 5;
380 }
381
382 std::vector<uint8_t> bytes;
383 bytes.resize(len_bits);
384 for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) {
385 uint8_t packed = data_bits & bitmask;
386 bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);
387 }
388
389 stream << prefix;
390 stream.Printf("\"%s\"", &bytes[0]);
391 stream << suffix;
392 return true;
393}
394
397 Stream &stream, const TypeSummaryOptions &summary_options) {
398 if (!descriptor)
399 return false;
400
401 uint64_t payload = 0;
402 if (!descriptor->GetTaggedPointerInfo(nullptr, nullptr, &payload))
403 return false;
404
405 // First 47 bits are the address of the contents.
406 addr_t ptr = payload & 0x7fffffffffffULL;
407 // Next 13 bits are the string's length.
408 size_t size = (payload >> 47) & 0x1fff;
409
410 Status status;
411 std::vector<char> buf(size);
412 if (auto process_sp = valobj.GetProcessSP())
413 if (process_sp->ReadMemory(ptr, buf.data(), size, status)) {
414 llvm::StringRef prefix, suffix;
415 if (auto *language = Language::FindPlugin(summary_options.GetLanguage()))
416 std::tie(prefix, suffix) =
417 language->GetFormatterPrefixSuffix("NSString");
418 stream << prefix << '"';
419 stream.PutCString({buf.data(), size});
420 stream << '"' << suffix;
421 return true;
422 }
423
424 if (status.Fail())
425 stream.Format("<{0}>", status);
426 return false;
427}
static llvm::raw_ostream & error(Stream &strm)
A section + offset based address class.
Definition Address.h:62
Generic representation of a type in a programming language.
CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) const
Create related types using the current type's AST.
A uniqued constant string class.
Definition ConstString.h:40
llvm::StringRef GetStringRef() const
Get the string value as a llvm::StringRef.
const char * GetCString() const
Get the string value as a C string.
An data extractor class.
"lldb/Target/ExecutionContext.h" A class that contains an execution context.
static Language * FindPlugin(lldb::LanguageType language)
Definition Language.cpp:84
std::shared_ptr< ClassDescriptor > ClassDescriptorSP
static ObjCLanguageRuntime * Get(Process &process)
virtual ClassDescriptorSP GetClassDescriptor(ValueObject &in_value)
An error handling class.
Definition Status.h:118
bool Fail() const
Test for error condition.
Definition Status.cpp:293
A stream class that can stream formatted output to a file.
Definition Stream.h:28
void Format(const char *format, Args &&... args)
Forwards the arguments to llvm::formatv and writes to the stream.
Definition Stream.h:370
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition Stream.cpp:134
size_t PutCString(llvm::StringRef cstr)
Output a C string to the stream.
Definition Stream.cpp:63
lldb::LanguageType GetLanguage() const
lldb::TypeSummaryCapping GetCapping() const
lldb::ProcessSP GetProcessSP() const
lldb::ValueObjectSP CreateChildValueObjectFromAddress(llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, CompilerType type, bool do_deref=true)
Given an address either create a value object containing the value at that address,...
virtual lldb::ValueObjectSP GetSyntheticChildAtOffset(uint32_t offset, const CompilerType &type, bool can_create, ConstString name_const_str=ConstString())
virtual uint64_t GetValueAsUnsigned(uint64_t fail_value, bool *success=nullptr)
lldb::TargetSP GetTargetSP() const
CompilerType GetCompilerType()
static std::map< ConstString, CXXFunctionSummaryFormat::Callback > & GetAdditionalSummaries()
Definition NSString.cpp:30
@ ZeroTerminate
Stop printing at the first zero terminator.
@ Ignore
Don't look for a terminator - print the whole buffer.
static bool ReadStringAndDumpToStream(const ReadStringAndDumpToStreamOptions &options)
bool NSMutableAttributedStringSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
Definition NSString.cpp:330
bool NSAttributedStringSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
Definition NSString.cpp:301
bool NSIndirectTaggedString_SummaryProvider(ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor, Stream &stream, const TypeSummaryOptions &summary_options)
Definition NSString.cpp:395
bool NSTaggedString_SummaryProvider(ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor, Stream &stream, const TypeSummaryOptions &summary_options)
Definition NSString.cpp:335
bool NSStringSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
Definition NSString.cpp:35
A class that represents a running process on the host machine.
@ eBasicTypeUnsignedInt
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP
std::shared_ptr< lldb_private::Process > ProcessSP
uint64_t addr_t
Definition lldb-types.h:80
std::shared_ptr< lldb_private::Target > TargetSP