1 //===-- Cocoa.cpp -------------------------------------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "lldb/DataFormatters/CXXFormatterFunctions.h"
11
12 #include "lldb/Core/DataBufferHeap.h"
13 #include "lldb/Core/Error.h"
14 #include "lldb/Core/Stream.h"
15 #include "lldb/Core/ValueObject.h"
16 #include "lldb/Core/ValueObjectConstResult.h"
17 #include "lldb/Host/Endian.h"
18 #include "lldb/Symbol/ClangASTContext.h"
19 #include "lldb/Target/ObjCLanguageRuntime.h"
20 #include "lldb/Target/Target.h"
21
22 using namespace lldb;
23 using namespace lldb_private;
24 using namespace lldb_private::formatters;
25
26 bool
NSBundleSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)27 lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
28 {
29 ProcessSP process_sp = valobj.GetProcessSP();
30 if (!process_sp)
31 return false;
32
33 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
34
35 if (!runtime)
36 return false;
37
38 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
39
40 if (!descriptor.get() || !descriptor->IsValid())
41 return false;
42
43 uint32_t ptr_size = process_sp->GetAddressByteSize();
44
45 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
46
47 if (!valobj_addr)
48 return false;
49
50 const char* class_name = descriptor->GetClassName().GetCString();
51
52 if (!class_name || !*class_name)
53 return false;
54
55 if (!strcmp(class_name,"NSBundle"))
56 {
57 uint64_t offset = 5 * ptr_size;
58 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), true));
59
60 StreamString summary_stream;
61 bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options);
62 if (was_nsstring_ok && summary_stream.GetSize() > 0)
63 {
64 stream.Printf("%s",summary_stream.GetData());
65 return true;
66 }
67 }
68 // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
69 // which is encoded differently and needs to be handled by running code
70 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream);
71 }
72
73 bool
NSTimeZoneSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)74 lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
75 {
76 ProcessSP process_sp = valobj.GetProcessSP();
77 if (!process_sp)
78 return false;
79
80 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
81
82 if (!runtime)
83 return false;
84
85 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
86
87 if (!descriptor.get() || !descriptor->IsValid())
88 return false;
89
90 uint32_t ptr_size = process_sp->GetAddressByteSize();
91
92 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
93
94 if (!valobj_addr)
95 return false;
96
97 const char* class_name = descriptor->GetClassName().GetCString();
98
99 if (!class_name || !*class_name)
100 return false;
101
102 if (!strcmp(class_name,"__NSTimeZone"))
103 {
104 uint64_t offset = ptr_size;
105 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true));
106 StreamString summary_stream;
107 bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options);
108 if (was_nsstring_ok && summary_stream.GetSize() > 0)
109 {
110 stream.Printf("%s",summary_stream.GetData());
111 return true;
112 }
113 }
114 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
115 }
116
117 bool
NSNotificationSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)118 lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
119 {
120 ProcessSP process_sp = valobj.GetProcessSP();
121 if (!process_sp)
122 return false;
123
124 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
125
126 if (!runtime)
127 return false;
128
129 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
130
131 if (!descriptor.get() || !descriptor->IsValid())
132 return false;
133
134 uint32_t ptr_size = process_sp->GetAddressByteSize();
135
136 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
137
138 if (!valobj_addr)
139 return false;
140
141 const char* class_name = descriptor->GetClassName().GetCString();
142
143 if (!class_name || !*class_name)
144 return false;
145
146 if (!strcmp(class_name,"NSConcreteNotification"))
147 {
148 uint64_t offset = ptr_size;
149 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true));
150 StreamString summary_stream;
151 bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options);
152 if (was_nsstring_ok && summary_stream.GetSize() > 0)
153 {
154 stream.Printf("%s",summary_stream.GetData());
155 return true;
156 }
157 }
158 // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
159 // which is encoded differently and needs to be handled by running code
160 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
161 }
162
163 bool
NSMachPortSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)164 lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
165 {
166 ProcessSP process_sp = valobj.GetProcessSP();
167 if (!process_sp)
168 return false;
169
170 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
171
172 if (!runtime)
173 return false;
174
175 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
176
177 if (!descriptor.get() || !descriptor->IsValid())
178 return false;
179
180 uint32_t ptr_size = process_sp->GetAddressByteSize();
181
182 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
183
184 if (!valobj_addr)
185 return false;
186
187 const char* class_name = descriptor->GetClassName().GetCString();
188
189 if (!class_name || !*class_name)
190 return false;
191
192 uint64_t port_number = 0;
193
194 do
195 {
196 if (!strcmp(class_name,"NSMachPort"))
197 {
198 uint64_t offset = (ptr_size == 4 ? 12 : 20);
199 Error error;
200 port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error);
201 if (error.Success())
202 break;
203 }
204 if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number))
205 return false;
206 } while (false);
207
208 stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF));
209 return true;
210 }
211
212 bool
NSIndexSetSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)213 lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
214 {
215 ProcessSP process_sp = valobj.GetProcessSP();
216 if (!process_sp)
217 return false;
218
219 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
220
221 if (!runtime)
222 return false;
223
224 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
225
226 if (!descriptor.get() || !descriptor->IsValid())
227 return false;
228
229 uint32_t ptr_size = process_sp->GetAddressByteSize();
230
231 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
232
233 if (!valobj_addr)
234 return false;
235
236 const char* class_name = descriptor->GetClassName().GetCString();
237
238 if (!class_name || !*class_name)
239 return false;
240
241 uint64_t count = 0;
242
243 do {
244 if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet"))
245 {
246 Error error;
247 uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 4, 0, error);
248 if (error.Fail())
249 return false;
250 // this means the set is empty - count = 0
251 if ((mode & 1) == 1)
252 {
253 count = 0;
254 break;
255 }
256 if ((mode & 2) == 2)
257 mode = 1; // this means the set only has one range
258 else
259 mode = 2; // this means the set has multiple ranges
260 if (mode == 1)
261 {
262 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+3*ptr_size, ptr_size, 0, error);
263 if (error.Fail())
264 return false;
265 }
266 else
267 {
268 // read a pointer to the data at 2*ptr_size
269 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error);
270 if (error.Fail())
271 return false;
272 // read the data at 2*ptr_size from the first location
273 count = process_sp->ReadUnsignedIntegerFromMemory(count+2*ptr_size, ptr_size, 0, error);
274 if (error.Fail())
275 return false;
276 }
277 }
278 else
279 {
280 if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count))
281 return false;
282 }
283 } while (false);
284 stream.Printf("%" PRIu64 " index%s",
285 count,
286 (count == 1 ? "" : "es"));
287 return true;
288 }
289
290 bool
NSNumberSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)291 lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
292 {
293 ProcessSP process_sp = valobj.GetProcessSP();
294 if (!process_sp)
295 return false;
296
297 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
298
299 if (!runtime)
300 return false;
301
302 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
303
304 if (!descriptor.get() || !descriptor->IsValid())
305 return false;
306
307 uint32_t ptr_size = process_sp->GetAddressByteSize();
308
309 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
310
311 if (!valobj_addr)
312 return false;
313
314 const char* class_name = descriptor->GetClassName().GetCString();
315
316 if (!class_name || !*class_name)
317 return false;
318
319 if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber"))
320 {
321 uint64_t value = 0;
322 uint64_t i_bits = 0;
323 if (descriptor->GetTaggedPointerInfo(&i_bits,&value))
324 {
325 switch (i_bits)
326 {
327 case 0:
328 stream.Printf("(char)%hhd",(char)value);
329 break;
330 case 1:
331 case 4:
332 stream.Printf("(short)%hd",(short)value);
333 break;
334 case 2:
335 case 8:
336 stream.Printf("(int)%d",(int)value);
337 break;
338 case 3:
339 case 12:
340 stream.Printf("(long)%" PRId64,value);
341 break;
342 default:
343 return false;
344 }
345 return true;
346 }
347 else
348 {
349 Error error;
350 uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 0, error) & 0x1F);
351 uint64_t data_location = valobj_addr + 2*ptr_size;
352 uint64_t value = 0;
353 if (error.Fail())
354 return false;
355 switch (data_type)
356 {
357 case 1: // 0B00001
358 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error);
359 if (error.Fail())
360 return false;
361 stream.Printf("(char)%hhd",(char)value);
362 break;
363 case 2: // 0B0010
364 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error);
365 if (error.Fail())
366 return false;
367 stream.Printf("(short)%hd",(short)value);
368 break;
369 case 3: // 0B0011
370 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
371 if (error.Fail())
372 return false;
373 stream.Printf("(int)%d",(int)value);
374 break;
375 case 17: // 0B10001
376 data_location += 8;
377 case 4: // 0B0100
378 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
379 if (error.Fail())
380 return false;
381 stream.Printf("(long)%" PRId64,value);
382 break;
383 case 5: // 0B0101
384 {
385 uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
386 if (error.Fail())
387 return false;
388 float flt_value = *((float*)&flt_as_int);
389 stream.Printf("(float)%f",flt_value);
390 break;
391 }
392 case 6: // 0B0110
393 {
394 uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
395 if (error.Fail())
396 return false;
397 double dbl_value = *((double*)&dbl_as_lng);
398 stream.Printf("(double)%g",dbl_value);
399 break;
400 }
401 default:
402 return false;
403 }
404 return true;
405 }
406 }
407 else
408 {
409 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream);
410 }
411 }
412
413 bool
NSURLSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)414 lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
415 {
416 ProcessSP process_sp = valobj.GetProcessSP();
417 if (!process_sp)
418 return false;
419
420 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
421
422 if (!runtime)
423 return false;
424
425 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
426
427 if (!descriptor.get() || !descriptor->IsValid())
428 return false;
429
430 uint32_t ptr_size = process_sp->GetAddressByteSize();
431
432 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
433
434 if (!valobj_addr)
435 return false;
436
437 const char* class_name = descriptor->GetClassName().GetCString();
438
439 if (!class_name || !*class_name)
440 return false;
441
442 if (strcmp(class_name, "NSURL") == 0)
443 {
444 uint64_t offset_text = ptr_size + ptr_size + 8; // ISA + pointer + 8 bytes of data (even on 32bit)
445 uint64_t offset_base = offset_text + ptr_size;
446 ClangASTType type(valobj.GetClangType());
447 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
448 ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
449 if (!text)
450 return false;
451 if (text->GetValueAsUnsigned(0) == 0)
452 return false;
453 StreamString summary;
454 if (!NSStringSummaryProvider(*text, summary, options))
455 return false;
456 if (base && base->GetValueAsUnsigned(0))
457 {
458 if (summary.GetSize() > 0)
459 summary.GetString().resize(summary.GetSize()-1);
460 summary.Printf(" -- ");
461 StreamString base_summary;
462 if (NSURLSummaryProvider(*base, base_summary, options) && base_summary.GetSize() > 0)
463 summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData());
464 }
465 if (summary.GetSize())
466 {
467 stream.Printf("%s",summary.GetData());
468 return true;
469 }
470 }
471 else
472 {
473 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream);
474 }
475 return false;
476 }
477
478 bool
NSDateSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)479 lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
480 {
481 ProcessSP process_sp = valobj.GetProcessSP();
482 if (!process_sp)
483 return false;
484
485 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
486
487 if (!runtime)
488 return false;
489
490 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
491
492 if (!descriptor.get() || !descriptor->IsValid())
493 return false;
494
495 uint32_t ptr_size = process_sp->GetAddressByteSize();
496
497 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
498
499 if (!valobj_addr)
500 return false;
501
502 uint64_t date_value_bits = 0;
503 double date_value = 0.0;
504
505 const char* class_name = descriptor->GetClassName().GetCString();
506
507 if (!class_name || !*class_name)
508 return false;
509
510 if (strcmp(class_name,"NSDate") == 0 ||
511 strcmp(class_name,"__NSDate") == 0 ||
512 strcmp(class_name,"__NSTaggedDate") == 0)
513 {
514 uint64_t info_bits=0,value_bits = 0;
515 if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits))
516 {
517 date_value_bits = ((value_bits << 8) | (info_bits << 4));
518 date_value = *((double*)&date_value_bits);
519 }
520 else
521 {
522 Error error;
523 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error);
524 date_value = *((double*)&date_value_bits);
525 if (error.Fail())
526 return false;
527 }
528 }
529 else if (!strcmp(class_name,"NSCalendarDate"))
530 {
531 Error error;
532 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error);
533 date_value = *((double*)&date_value_bits);
534 if (error.Fail())
535 return false;
536 }
537 else
538 {
539 if (ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits) == false)
540 return false;
541 date_value = *((double*)&date_value_bits);
542 }
543 if (date_value == -63114076800)
544 {
545 stream.Printf("0001-12-30 00:00:00 +0000");
546 return true;
547 }
548 // this snippet of code assumes that time_t == seconds since Jan-1-1970
549 // this is generally true and POSIXly happy, but might break if a library
550 // vendor decides to get creative
551 time_t epoch = GetOSXEpoch();
552 epoch = epoch + (time_t)date_value;
553 tm *tm_date = gmtime(&epoch);
554 if (!tm_date)
555 return false;
556 std::string buffer(1024,0);
557 if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0)
558 return false;
559 stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year+1900, tm_date->tm_mon+1, tm_date->tm_mday, tm_date->tm_hour, tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
560 return true;
561 }
562