Please use GitHub pull requests for new patches. Avoid migrating existing patches. Phabricator shutdown timeline
Changeset View
Changeset View
Standalone View
Standalone View
lldb/source/Plugins/Language/ObjC/Cocoa.cpp
//===-- Cocoa.cpp ---------------------------------------------------------===// | //===-- Cocoa.cpp ---------------------------------------------------------===// | ||||
// | // | ||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||||
// See https://llvm.org/LICENSE.txt for license information. | // See https://llvm.org/LICENSE.txt for license information. | ||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||||
// | // | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
#include "Cocoa.h" | #include "Cocoa.h" | ||||
#include "Plugins/Language/ObjC/Utilities.h" | |||||
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" | ||||
#include "lldb/Core/Mangled.h" | #include "lldb/Core/Mangled.h" | ||||
#include "lldb/Core/ValueObject.h" | #include "lldb/Core/ValueObject.h" | ||||
#include "lldb/Core/ValueObjectConstResult.h" | #include "lldb/Core/ValueObjectConstResult.h" | ||||
#include "lldb/DataFormatters/FormattersHelpers.h" | #include "lldb/DataFormatters/FormattersHelpers.h" | ||||
#include "lldb/DataFormatters/StringPrinter.h" | #include "lldb/DataFormatters/StringPrinter.h" | ||||
#include "lldb/DataFormatters/TypeSummary.h" | #include "lldb/DataFormatters/TypeSummary.h" | ||||
#include "lldb/Host/Time.h" | #include "lldb/Host/Time.h" | ||||
#include "lldb/Target/Language.h" | #include "lldb/Target/Language.h" | ||||
#include "lldb/Target/Process.h" | #include "lldb/Target/Process.h" | ||||
#include "lldb/Target/ProcessStructReader.h" | #include "lldb/Target/ProcessStructReader.h" | ||||
#include "lldb/Target/Target.h" | #include "lldb/Target/Target.h" | ||||
#include "lldb/Utility/DataBufferHeap.h" | #include "lldb/Utility/DataBufferHeap.h" | ||||
#include "lldb/Utility/Endian.h" | #include "lldb/Utility/Endian.h" | ||||
#include "lldb/Utility/Status.h" | #include "lldb/Utility/Status.h" | ||||
#include "lldb/Utility/Stream.h" | #include "lldb/Utility/Stream.h" | ||||
#include "llvm/ADT/APInt.h" | #include "llvm/ADT/APInt.h" | ||||
#include "llvm/ADT/bit.h" | #include "llvm/ADT/bit.h" | ||||
#include "llvm/Support/CheckedArithmetic.h" | |||||
#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" | #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" | ||||
#include "NSString.h" | #include "NSString.h" | ||||
#include <cmath> | |||||
using namespace lldb; | using namespace lldb; | ||||
using namespace lldb_private; | using namespace lldb_private; | ||||
using namespace lldb_private::formatters; | using namespace lldb_private::formatters; | ||||
bool lldb_private::formatters::NSBundleSummaryProvider( | bool lldb_private::formatters::NSBundleSummaryProvider( | ||||
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { | ||||
ProcessSP process_sp = valobj.GetProcessSP(); | ProcessSP process_sp = valobj.GetProcessSP(); | ||||
if (!process_sp) | if (!process_sp) | ||||
▲ Show 20 Lines • Show All 737 Lines • ▼ Show 20 Lines | static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) { | ||||
DoubleBits decodedBits; | DoubleBits decodedBits; | ||||
decodedBits.sign = encodedBits.sign; | decodedBits.sign = encodedBits.sign; | ||||
decodedBits.fraction = encodedBits.fraction; | decodedBits.fraction = encodedBits.fraction; | ||||
decodedBits.exponent = decodeExponent(encodedBits.exponent); | decodedBits.exponent = decodeExponent(encodedBits.exponent); | ||||
return llvm::bit_cast<double>(decodedBits); | return llvm::bit_cast<double>(decodedBits); | ||||
} | } | ||||
// POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 | |||||
// this call gives the POSIX equivalent of the Cocoa epoch | |||||
time_t lldb_private::formatters::GetOSXEpoch() { | |||||
static time_t epoch = 0; | |||||
#ifdef __APPLE__ | |||||
labath: I think that changing !_WIN32 to __APPLE__ is a step in the wrong direction. Ideally, this… | |||||
if (!epoch) { | |||||
tzset(); | |||||
tm tm_epoch; | |||||
Not Done ReplyInline ActionsMForster: This is the line where the "Illegal instruction" happens. Actually [[ https://github. | |||||
tm_epoch.tm_sec = 0; | |||||
tm_epoch.tm_hour = 0; | |||||
tm_epoch.tm_min = 0; | |||||
tm_epoch.tm_mon = 0; | |||||
tm_epoch.tm_mday = 1; | |||||
tm_epoch.tm_year = 2001 - 1900; | |||||
tm_epoch.tm_isdst = -1; | |||||
I think you can drop this comment [you just moved it, but it feels irrelevant at this point because there's only one vendor]. davide: I think you can drop this comment [you just moved it, but it feels irrelevant at this point… | |||||
I'll fix this before committing. vsk: I'll fix this before committing. | |||||
tm_epoch.tm_gmtoff = 0; | |||||
tm_epoch.tm_zone = nullptr; | |||||
epoch = timegm(&tm_epoch); | |||||
} | |||||
#endif // __APPLE__ | |||||
return epoch; | |||||
} | |||||
bool lldb_private::formatters::NSDate::FormatDateValue(double date_value, | |||||
Stream &stream) { | |||||
if (date_value == -63114076800) { | |||||
stream.Printf("0001-12-30 00:00:00 +0000"); | |||||
return true; | |||||
} | |||||
// Rely on implementation-defined behavior from std::llrint to clamp an | |||||
// out-of-range `double` value to a value representable by `time_t` without | |||||
// crashing LLDB with a floating-point exception. | |||||
// | |||||
// We're not distinguishing between the case where date_value "is" the max | |||||
// `time_t` value, and the case where std::llrint simply returns that value | |||||
// due to clamping, although technically we should be able to by inspecting | |||||
// the `math_errhandling` macro (which is set on a domain/overflow error). | |||||
const time_t date_value_as_time_t = | |||||
static_cast<time_t>(std::llrint(date_value)); | |||||
time_t epoch = GetOSXEpoch(); | |||||
if (auto osx_epoch = llvm::checkedAdd(epoch, date_value_as_time_t)) | |||||
epoch = *osx_epoch; | |||||
else | |||||
return false; | |||||
tm *tm_date = gmtime(&epoch); | |||||
if (!tm_date) | |||||
return false; | |||||
std::string buffer(1024, 0); | |||||
if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0) | |||||
return false; | |||||
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()); | |||||
return true; | |||||
} | |||||
bool lldb_private::formatters::NSDateSummaryProvider( | bool lldb_private::formatters::NSDateSummaryProvider( | ||||
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { | ||||
ProcessSP process_sp = valobj.GetProcessSP(); | ProcessSP process_sp = valobj.GetProcessSP(); | ||||
if (!process_sp) | if (!process_sp) | ||||
return false; | return false; | ||||
ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); | ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); | ||||
Show All 27 Lines | if (class_name.IsEmpty()) | ||||
return false; | return false; | ||||
uint64_t info_bits = 0, value_bits = 0; | uint64_t info_bits = 0, value_bits = 0; | ||||
if ((class_name == g_NSDate) || (class_name == g___NSDate) || | if ((class_name == g_NSDate) || (class_name == g___NSDate) || | ||||
(class_name == g___NSTaggedDate)) { | (class_name == g___NSTaggedDate)) { | ||||
if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) { | if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) { | ||||
date_value_bits = ((value_bits << 8) | (info_bits << 4)); | date_value_bits = ((value_bits << 8) | (info_bits << 4)); | ||||
memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); | memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); | ||||
// Accomodate the __NSTaggedDate format introduced in Foundation 1600. | |||||
if (class_name == g___NSTaggedDate) { | |||||
auto *apple_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( | |||||
ObjCLanguageRuntime::Get(*process_sp)); | |||||
if (!apple_runtime) | |||||
return false; | |||||
if (apple_runtime->GetFoundationVersion() >= 1600) | |||||
date_value = decodeTaggedTimeInterval(value_bits << 4); | |||||
} | |||||
} else { | } else { | ||||
llvm::Triple triple( | llvm::Triple triple( | ||||
process_sp->GetTarget().GetArchitecture().GetTriple()); | process_sp->GetTarget().GetArchitecture().GetTriple()); | ||||
uint32_t delta = | uint32_t delta = | ||||
(triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size; | (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size; | ||||
Status error; | Status error; | ||||
date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( | date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( | ||||
valobj_addr + delta, 8, 0, error); | valobj_addr + delta, 8, 0, error); | ||||
memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); | memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); | ||||
if (error.Fail()) | if (error.Fail()) | ||||
return false; | return false; | ||||
} | } | ||||
} else if (class_name == g_NSCalendarDate) { | } else if (class_name == g_NSCalendarDate) { | ||||
Status error; | Status error; | ||||
date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( | date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( | ||||
valobj_addr + 2 * ptr_size, 8, 0, error); | valobj_addr + 2 * ptr_size, 8, 0, error); | ||||
memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); | memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); | ||||
if (error.Fail()) | if (error.Fail()) | ||||
return false; | return false; | ||||
} else | } else | ||||
return false; | return false; | ||||
if (date_value == -63114076800) { | return NSDate::FormatDateValue(date_value, stream); | ||||
stream.Printf("0001-12-30 00:00:00 +0000"); | |||||
return true; | |||||
} | |||||
// Accomodate for the __NSTaggedDate format introduced in Foundation 1600. | |||||
if (class_name == g___NSTaggedDate) { | |||||
auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( | |||||
ObjCLanguageRuntime::Get(*process_sp)); | |||||
if (runtime && runtime->GetFoundationVersion() >= 1600) | |||||
date_value = decodeTaggedTimeInterval(value_bits << 4); | |||||
} | |||||
// this snippet of code assumes that time_t == seconds since Jan-1-1970 this | |||||
// is generally true and POSIXly happy, but might break if a library vendor | |||||
// decides to get creative | |||||
time_t epoch = GetOSXEpoch(); | |||||
epoch = epoch + (time_t)date_value; | |||||
tm *tm_date = gmtime(&epoch); | |||||
if (!tm_date) | |||||
return false; | |||||
std::string buffer(1024, 0); | |||||
if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0) | |||||
return false; | |||||
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()); | |||||
return true; | |||||
} | } | ||||
bool lldb_private::formatters::ObjCClassSummaryProvider( | bool lldb_private::formatters::ObjCClassSummaryProvider( | ||||
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { | ||||
ProcessSP process_sp = valobj.GetProcessSP(); | ProcessSP process_sp = valobj.GetProcessSP(); | ||||
if (!process_sp) | if (!process_sp) | ||||
return false; | return false; | ||||
▲ Show 20 Lines • Show All 204 Lines • ▼ Show 20 Lines | bool lldb_private::formatters::ObjCSELSummaryProvider( | ||||
if (!valobj_sp) | if (!valobj_sp) | ||||
return false; | return false; | ||||
stream.Printf("%s", valobj_sp->GetSummaryAsCString()); | stream.Printf("%s", valobj_sp->GetSummaryAsCString()); | ||||
return true; | return true; | ||||
} | } | ||||
// POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 | |||||
// this call gives the POSIX equivalent of the Cocoa epoch | |||||
time_t lldb_private::formatters::GetOSXEpoch() { | |||||
static time_t epoch = 0; | |||||
if (!epoch) { | |||||
#ifndef _WIN32 | |||||
tzset(); | |||||
tm tm_epoch; | |||||
tm_epoch.tm_sec = 0; | |||||
tm_epoch.tm_hour = 0; | |||||
tm_epoch.tm_min = 0; | |||||
tm_epoch.tm_mon = 0; | |||||
tm_epoch.tm_mday = 1; | |||||
tm_epoch.tm_year = 2001 - 1900; | |||||
tm_epoch.tm_isdst = -1; | |||||
tm_epoch.tm_gmtoff = 0; | |||||
tm_epoch.tm_zone = nullptr; | |||||
epoch = timegm(&tm_epoch); | |||||
#endif | |||||
} | |||||
return epoch; | |||||
} | |||||
template bool lldb_private::formatters::NSDataSummaryProvider<true>( | template bool lldb_private::formatters::NSDataSummaryProvider<true>( | ||||
ValueObject &, Stream &, const TypeSummaryOptions &); | ValueObject &, Stream &, const TypeSummaryOptions &); | ||||
template bool lldb_private::formatters::NSDataSummaryProvider<false>( | template bool lldb_private::formatters::NSDataSummaryProvider<false>( | ||||
ValueObject &, Stream &, const TypeSummaryOptions &); | ValueObject &, Stream &, const TypeSummaryOptions &); | ||||
template bool lldb_private::formatters::ObjCSELSummaryProvider<true>( | template bool lldb_private::formatters::ObjCSELSummaryProvider<true>( | ||||
ValueObject &, Stream &, const TypeSummaryOptions &); | ValueObject &, Stream &, const TypeSummaryOptions &); | ||||
template bool lldb_private::formatters::ObjCSELSummaryProvider<false>( | template bool lldb_private::formatters::ObjCSELSummaryProvider<false>( | ||||
ValueObject &, Stream &, const TypeSummaryOptions &); | ValueObject &, Stream &, const TypeSummaryOptions &); |
I think that changing !_WIN32 to APPLE is a step in the wrong direction. Ideally, this function should work the same everywhere. The way to achieve that would be to have a host function which selects between gmtime and _mkgmtime depending on the platform. But failing that, I don't see a reason to restrict the scope of this further.