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"
16#include "lldb/Utility/Endian.h"
17#include "lldb/Utility/Status.h"
18#include "lldb/Utility/Stream.h"
21
22#include "llvm/ADT/StringMap.h"
23#include "llvm/Support/Error.h"
24
25using namespace lldb;
26using namespace lldb_private;
27using namespace lldb_private::formatters;
28
29llvm::StringMap<CXXFunctionSummaryFormat::Callback> &
31 static llvm::StringMap<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 llvm::StringRef class_name = descriptor->GetClassName();
63
64 if (class_name.empty())
65 return false;
66
67 // For tagged pointers, the descriptor has everything needed.
68 bool is_tagged = descriptor->GetTaggedPointerInfo();
69 if (is_tagged) {
70 if (class_name == "NSTaggedPointerString")
71 return NSTaggedString_SummaryProvider(valobj, descriptor, stream,
72 summary_options);
73
74 if (class_name == "NSIndirectTaggedPointerString")
75 return NSIndirectTaggedString_SummaryProvider(valobj, descriptor, stream,
76 summary_options);
77 }
78
79 auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());
80 auto iter = additionals_map.find(class_name), end = additionals_map.end();
81 if (iter != end)
82 return iter->second(valobj, stream, summary_options);
83
84 // if not a tagged pointer that we know about, try the normal route
85 uint64_t info_bits_location = valobj_addr + ptr_size;
86 if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)
87 info_bits_location += 3;
88
90
91 uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(
92 info_bits_location, 1, 0, error);
93 if (error.Fail())
94 return false;
95
96 bool is_mutable = (info_bits & 1) == 1;
97 bool is_inline = (info_bits & 0x60) == 0;
98 bool has_explicit_length = (info_bits & (1 | 4)) != 4;
99 bool is_unicode = (info_bits & 0x10) == 0x10;
100 bool is_path_store = class_name == "NSPathStore2";
101 bool has_null = (info_bits & 8) == 8;
102
103 size_t explicit_length = 0;
104 if (!has_null && has_explicit_length && !is_path_store) {
105 lldb::addr_t explicit_length_offset = 2 * ptr_size;
106 if (is_mutable && !is_inline)
107 explicit_length_offset =
108 explicit_length_offset + ptr_size; // notInlineMutable.length;
109 else if (is_inline)
110 explicit_length = explicit_length + 0; // inline1.length;
111 else if (!is_inline && !is_mutable)
112 explicit_length_offset =
113 explicit_length_offset + ptr_size; // notInlineImmutable1.length;
114 else
115 explicit_length_offset = 0;
116
117 if (explicit_length_offset) {
118 explicit_length_offset = valobj_addr + explicit_length_offset;
119 explicit_length = process_sp->ReadUnsignedIntegerFromMemory(
120 explicit_length_offset, 4, 0, error);
121 }
122 }
123
124 const llvm::StringSet<> supported_string_classes = {
125 "NSString", "CFMutableStringRef",
126 "CFStringRef", "__NSCFConstantString",
127 "__NSCFString", "NSCFConstantString",
128 "NSCFString", "NSPathStore2"};
129 if (supported_string_classes.count(class_name) == 0) {
130 // not one of us - but tell me class name
131 stream.Format("class name = {0}", class_name);
132 return true;
133 }
134
135 llvm::StringRef prefix, suffix;
136 if (Language *language = Language::FindPlugin(summary_options.GetLanguage()))
137 std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
138
140 options.SetPrefixToken(prefix.str());
141 options.SetSuffixToken(suffix.str());
142
143 if (is_mutable) {
144 uint64_t location = 2 * ptr_size + valobj_addr;
145 llvm::Expected<lldb::addr_t> location_or_err =
146 process_sp->ReadPointerFromMemory(location);
147 if (!location_or_err) {
148 llvm::consumeError(location_or_err.takeError());
149 return false;
150 }
151 location = *location_or_err;
152 if (has_explicit_length && is_unicode) {
153 options.SetLocation(Address(location));
154 options.SetTargetSP(valobj.GetTargetSP());
155 options.SetStream(&stream);
156 options.SetQuote('"');
157 options.SetSourceSize(explicit_length);
158 options.SetHasSourceSize(has_explicit_length);
160 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
163 StringPrinter::StringElementType::UTF16>(options);
164 } else {
165 options.SetLocation(Address(location + 1));
166 options.SetTargetSP(valobj.GetTargetSP());
167 options.SetStream(&stream);
168 options.SetSourceSize(explicit_length);
169 options.SetHasSourceSize(has_explicit_length);
171 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
174 StringPrinter::StringElementType::ASCII>(options);
175 }
176 } else if (is_inline && has_explicit_length && !is_unicode &&
177 !is_path_store && !is_mutable) {
178 uint64_t location = 3 * ptr_size + valobj_addr;
179
180 options.SetLocation(Address(location));
181 options.SetTargetSP(valobj.GetTargetSP());
182 options.SetStream(&stream);
183 options.SetQuote('"');
184 options.SetSourceSize(explicit_length);
185 options.SetHasSourceSize(has_explicit_length);
186 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
189 StringPrinter::StringElementType::ASCII>(options);
190 } else if (is_unicode) {
191 uint64_t location = valobj_addr + 2 * ptr_size;
192 if (is_inline) {
193 if (!has_explicit_length) {
194 return false;
195 } else
196 location += ptr_size;
197 } else {
198 llvm::Expected<lldb::addr_t> location_or_err =
199 process_sp->ReadPointerFromMemory(location);
200 if (!location_or_err) {
201 llvm::consumeError(location_or_err.takeError());
202 return false;
203 }
204 location = *location_or_err;
205 }
206 options.SetLocation(Address(location));
207 options.SetTargetSP(valobj.GetTargetSP());
208 options.SetStream(&stream);
209 options.SetQuote('"');
210 options.SetSourceSize(explicit_length);
211 options.SetHasSourceSize(has_explicit_length);
212 if (has_explicit_length)
214 else
216 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
219 StringPrinter::StringElementType::UTF16>(options);
220 } else if (is_path_store) {
221 // _lengthAndRefCount is the first ivar of NSPathStore2 (after the isa).
222 uint64_t length_ivar_offset = 1 * ptr_size;
223 CompilerType length_type = valobj.GetCompilerType().GetBasicTypeFromAST(
225 ValueObjectSP length_valobj_sp =
226 valobj.GetSyntheticChildAtOffset(length_ivar_offset, length_type, true,
227 ConstString("_lengthAndRefCount"));
228 if (!length_valobj_sp)
229 return false;
230 // Get the length out of _lengthAndRefCount.
231 explicit_length = length_valobj_sp->GetValueAsUnsigned(0) >> 20;
232 lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
233
234 options.SetLocation(Address(location));
235 options.SetTargetSP(valobj.GetTargetSP());
236 options.SetStream(&stream);
237 options.SetQuote('"');
238 options.SetSourceSize(explicit_length);
239 options.SetHasSourceSize(has_explicit_length);
240 if (has_explicit_length)
242 else
244 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
247 StringPrinter::StringElementType::UTF16>(options);
248 } else if (is_inline) {
249 uint64_t location = valobj_addr + 2 * ptr_size;
250 if (!has_explicit_length) {
251 // in this kind of string, the byte before the string content is a length
252 // byte so let's try and use it to handle the embedded NUL case
254 explicit_length =
255 process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
256 has_explicit_length = !(error.Fail() || explicit_length == 0);
257 location++;
258 }
259 options.SetLocation(Address(location));
260 options.SetTargetSP(valobj.GetTargetSP());
261 options.SetStream(&stream);
262 options.SetSourceSize(explicit_length);
263 options.SetHasSourceSize(has_explicit_length);
264 if (has_explicit_length)
266 else
268 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
270 if (has_explicit_length)
272 StringPrinter::StringElementType::UTF8>(options);
273 else
275 StringPrinter::StringElementType::ASCII>(options);
276 } else {
277 uint64_t location = valobj_addr + 2 * ptr_size;
278 llvm::Expected<lldb::addr_t> location_or_err =
279 process_sp->ReadPointerFromMemory(location);
280 if (!location_or_err) {
281 llvm::consumeError(location_or_err.takeError());
282 return false;
283 }
284 location = *location_or_err;
285 if (has_explicit_length && !has_null)
286 explicit_length++; // account for the fact that there is no NULL and we
287 // need to have one added
288 options.SetLocation(Address(location));
289 options.SetTargetSP(valobj.GetTargetSP());
290 options.SetStream(&stream);
291 options.SetSourceSize(explicit_length);
292 options.SetHasSourceSize(has_explicit_length);
293 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
296 StringPrinter::StringElementType::ASCII>(options);
297 }
298}
299
301 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
302 TargetSP target_sp(valobj.GetTargetSP());
303 if (!target_sp)
304 return false;
305 uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();
306 uint64_t pointer_value = valobj.GetValueAsUnsigned(0);
307 if (!pointer_value)
308 return false;
309 pointer_value += addr_size;
310 CompilerType type(valobj.GetCompilerType());
311 ExecutionContext exe_ctx(target_sp, false);
313 "string_ptr", pointer_value, exe_ctx, type));
314 if (!child_ptr_sp)
315 return false;
316 DataExtractor data;
318 child_ptr_sp->GetData(data, error);
319 if (error.Fail())
320 return false;
321 ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData(
322 "string_data", data, exe_ctx, type));
323 child_sp->GetValueAsUnsigned(0);
324 if (child_sp)
325 return NSStringSummaryProvider(*child_sp, stream, options);
326 return false;
327}
328
330 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
331 return NSAttributedStringSummaryProvider(valobj, stream, options);
332}
333
336 Stream &stream, const TypeSummaryOptions &summary_options) {
337 static constexpr llvm::StringLiteral g_TypeHint("NSString");
338
339 if (!descriptor)
340 return false;
341 uint64_t len_bits = 0, data_bits = 0;
342 if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr))
343 return false;
344
345 static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN
346 static const int g_SixbitMaxLen = 9;
347 static const int g_fiveBitMaxLen = 11;
348
349 static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013"
350 "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
351
352 if (len_bits > g_fiveBitMaxLen)
353 return false;
354
355 llvm::StringRef prefix, suffix;
356 if (Language *language = Language::FindPlugin(summary_options.GetLanguage()))
357 std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
358
359 // this is a fairly ugly trick - pretend that the numeric value is actually a
360 // char* this works under a few assumptions: little endian architecture
361 // sizeof(uint64_t) > g_MaxNonBitmaskedLen
362 if (len_bits <= g_MaxNonBitmaskedLen) {
363 stream << prefix;
364 stream.Printf("\"%s\"", (const char *)&data_bits);
365 stream << suffix;
366 return true;
367 }
368
369 // if the data is bitmasked, we need to actually process the bytes
370 uint8_t bitmask = 0;
371 uint8_t shift_offset = 0;
372
373 if (len_bits <= g_SixbitMaxLen) {
374 bitmask = 0x03f;
375 shift_offset = 6;
376 } else {
377 bitmask = 0x01f;
378 shift_offset = 5;
379 }
380
381 std::vector<uint8_t> bytes;
382 bytes.resize(len_bits);
383 for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) {
384 uint8_t packed = data_bits & bitmask;
385 bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);
386 }
387
388 stream << prefix;
389 stream.Printf("\"%s\"", &bytes[0]);
390 stream << suffix;
391 return true;
392}
393
396 Stream &stream, const TypeSummaryOptions &summary_options) {
397 if (!descriptor)
398 return false;
399
400 uint64_t payload = 0;
401 if (!descriptor->GetTaggedPointerInfo(nullptr, nullptr, &payload))
402 return false;
403
404 // First 47 bits are the address of the contents.
405 addr_t ptr = payload & 0x7fffffffffffULL;
406 // Next 13 bits are the string's length.
407 size_t size = (payload >> 47) & 0x1fff;
408
409 Status status;
410 std::vector<char> buf(size);
411 if (auto process_sp = valobj.GetProcessSP())
412 if (process_sp->ReadMemory(ptr, buf.data(), size, status)) {
413 llvm::StringRef prefix, suffix;
414 if (auto *language = Language::FindPlugin(summary_options.GetLanguage()))
415 std::tie(prefix, suffix) =
416 language->GetFormatterPrefixSuffix("NSString");
417 stream << prefix << '"';
418 stream.PutCString({buf.data(), size});
419 stream << '"' << suffix;
420 return true;
421 }
422
423 if (status.Fail())
424 stream.Format("<{0}>", status);
425 return false;
426}
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
An data extractor class.
"lldb/Target/ExecutionContext.h" A class that contains an execution context.
static Language * FindPlugin(lldb::LanguageType language)
Definition Language.cpp:89
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 llvm::StringMap< 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:329
bool NSAttributedStringSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
Definition NSString.cpp:300
bool NSIndirectTaggedString_SummaryProvider(ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor, Stream &stream, const TypeSummaryOptions &summary_options)
Definition NSString.cpp:394
bool NSTaggedString_SummaryProvider(ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor, Stream &stream, const TypeSummaryOptions &summary_options)
Definition NSString.cpp:334
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