You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
295 lines
8.8 KiB
295 lines
8.8 KiB
4 months ago
|
//===-- CF.cpp ------------------------------------------------------------===//
|
||
|
//
|
||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "CF.h"
|
||
|
|
||
|
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
|
||
|
#include "lldb/Core/ValueObject.h"
|
||
|
#include "lldb/Core/ValueObjectConstResult.h"
|
||
|
#include "lldb/DataFormatters/FormattersHelpers.h"
|
||
|
#include "lldb/Target/Language.h"
|
||
|
#include "lldb/Target/StackFrame.h"
|
||
|
#include "lldb/Target/Target.h"
|
||
|
#include "lldb/Utility/DataBufferHeap.h"
|
||
|
#include "lldb/Utility/Endian.h"
|
||
|
#include "lldb/Utility/Status.h"
|
||
|
#include "lldb/Utility/Stream.h"
|
||
|
|
||
|
#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
|
||
|
|
||
|
using namespace lldb;
|
||
|
using namespace lldb_private;
|
||
|
using namespace lldb_private::formatters;
|
||
|
|
||
|
bool lldb_private::formatters::CFAbsoluteTimeSummaryProvider(
|
||
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
||
|
time_t epoch = GetOSXEpoch();
|
||
|
epoch = epoch + (time_t)valobj.GetValueAsSigned(0);
|
||
|
tm *tm_date = localtime(&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::CFBagSummaryProvider(
|
||
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
||
|
static ConstString g_TypeHint("CFBag");
|
||
|
|
||
|
ProcessSP process_sp = valobj.GetProcessSP();
|
||
|
if (!process_sp)
|
||
|
return false;
|
||
|
|
||
|
ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
|
||
|
|
||
|
if (!runtime)
|
||
|
return false;
|
||
|
|
||
|
ObjCLanguageRuntime::ClassDescriptorSP descriptor(
|
||
|
runtime->GetClassDescriptor(valobj));
|
||
|
|
||
|
if (!descriptor.get() || !descriptor->IsValid())
|
||
|
return false;
|
||
|
|
||
|
uint32_t ptr_size = process_sp->GetAddressByteSize();
|
||
|
|
||
|
lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
|
||
|
|
||
|
if (!valobj_addr)
|
||
|
return false;
|
||
|
|
||
|
uint32_t count = 0;
|
||
|
|
||
|
bool is_type_ok = false; // check to see if this is a CFBag we know about
|
||
|
if (descriptor->IsCFType()) {
|
||
|
ConstString type_name(valobj.GetTypeName());
|
||
|
|
||
|
static ConstString g___CFBag("__CFBag");
|
||
|
static ConstString g_conststruct__CFBag("const struct __CFBag");
|
||
|
|
||
|
if (type_name == g___CFBag || type_name == g_conststruct__CFBag) {
|
||
|
if (valobj.IsPointerType())
|
||
|
is_type_ok = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (is_type_ok) {
|
||
|
lldb::addr_t offset = 2 * ptr_size + 4 + valobj_addr;
|
||
|
Status error;
|
||
|
count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error);
|
||
|
if (error.Fail())
|
||
|
return false;
|
||
|
} else
|
||
|
return false;
|
||
|
|
||
|
std::string prefix, suffix;
|
||
|
if (Language *language = Language::FindPlugin(options.GetLanguage())) {
|
||
|
if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
|
||
|
suffix)) {
|
||
|
prefix.clear();
|
||
|
suffix.clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
stream.Printf("%s\"%u value%s\"%s", prefix.c_str(), count,
|
||
|
(count == 1 ? "" : "s"), suffix.c_str());
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool lldb_private::formatters::CFBitVectorSummaryProvider(
|
||
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
||
|
ProcessSP process_sp = valobj.GetProcessSP();
|
||
|
if (!process_sp)
|
||
|
return false;
|
||
|
|
||
|
ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
|
||
|
|
||
|
if (!runtime)
|
||
|
return false;
|
||
|
|
||
|
ObjCLanguageRuntime::ClassDescriptorSP descriptor(
|
||
|
runtime->GetClassDescriptor(valobj));
|
||
|
|
||
|
if (!descriptor.get() || !descriptor->IsValid())
|
||
|
return false;
|
||
|
|
||
|
uint32_t ptr_size = process_sp->GetAddressByteSize();
|
||
|
|
||
|
lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
|
||
|
|
||
|
if (!valobj_addr)
|
||
|
return false;
|
||
|
|
||
|
uint32_t count = 0;
|
||
|
|
||
|
bool is_type_ok = false; // check to see if this is a CFBag we know about
|
||
|
if (descriptor->IsCFType()) {
|
||
|
ConstString type_name(valobj.GetTypeName());
|
||
|
if (type_name == "__CFMutableBitVector" || type_name == "__CFBitVector" ||
|
||
|
type_name == "CFMutableBitVectorRef" || type_name == "CFBitVectorRef") {
|
||
|
if (valobj.IsPointerType())
|
||
|
is_type_ok = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!is_type_ok)
|
||
|
return false;
|
||
|
|
||
|
Status error;
|
||
|
count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + 2 * ptr_size,
|
||
|
ptr_size, 0, error);
|
||
|
if (error.Fail())
|
||
|
return false;
|
||
|
uint64_t num_bytes = count / 8 + ((count & 7) ? 1 : 0);
|
||
|
addr_t data_ptr = process_sp->ReadPointerFromMemory(
|
||
|
valobj_addr + 2 * ptr_size + 2 * ptr_size, error);
|
||
|
if (error.Fail())
|
||
|
return false;
|
||
|
// make sure we do not try to read huge amounts of data
|
||
|
if (num_bytes > 1024)
|
||
|
num_bytes = 1024;
|
||
|
DataBufferSP buffer_sp(new DataBufferHeap(num_bytes, 0));
|
||
|
num_bytes =
|
||
|
process_sp->ReadMemory(data_ptr, buffer_sp->GetBytes(), num_bytes, error);
|
||
|
if (error.Fail() || num_bytes == 0)
|
||
|
return false;
|
||
|
uint8_t *bytes = buffer_sp->GetBytes();
|
||
|
for (uint64_t byte_idx = 0; byte_idx < num_bytes - 1; byte_idx++) {
|
||
|
uint8_t byte = bytes[byte_idx];
|
||
|
bool bit0 = (byte & 1) == 1;
|
||
|
bool bit1 = (byte & 2) == 2;
|
||
|
bool bit2 = (byte & 4) == 4;
|
||
|
bool bit3 = (byte & 8) == 8;
|
||
|
bool bit4 = (byte & 16) == 16;
|
||
|
bool bit5 = (byte & 32) == 32;
|
||
|
bool bit6 = (byte & 64) == 64;
|
||
|
bool bit7 = (byte & 128) == 128;
|
||
|
stream.Printf("%c%c%c%c %c%c%c%c ", (bit7 ? '1' : '0'), (bit6 ? '1' : '0'),
|
||
|
(bit5 ? '1' : '0'), (bit4 ? '1' : '0'), (bit3 ? '1' : '0'),
|
||
|
(bit2 ? '1' : '0'), (bit1 ? '1' : '0'), (bit0 ? '1' : '0'));
|
||
|
count -= 8;
|
||
|
}
|
||
|
{
|
||
|
// print the last byte ensuring we do not print spurious bits
|
||
|
uint8_t byte = bytes[num_bytes - 1];
|
||
|
bool bit0 = (byte & 1) == 1;
|
||
|
bool bit1 = (byte & 2) == 2;
|
||
|
bool bit2 = (byte & 4) == 4;
|
||
|
bool bit3 = (byte & 8) == 8;
|
||
|
bool bit4 = (byte & 16) == 16;
|
||
|
bool bit5 = (byte & 32) == 32;
|
||
|
bool bit6 = (byte & 64) == 64;
|
||
|
bool bit7 = (byte & 128) == 128;
|
||
|
if (count) {
|
||
|
stream.Printf("%c", bit7 ? '1' : '0');
|
||
|
count -= 1;
|
||
|
}
|
||
|
if (count) {
|
||
|
stream.Printf("%c", bit6 ? '1' : '0');
|
||
|
count -= 1;
|
||
|
}
|
||
|
if (count) {
|
||
|
stream.Printf("%c", bit5 ? '1' : '0');
|
||
|
count -= 1;
|
||
|
}
|
||
|
if (count) {
|
||
|
stream.Printf("%c", bit4 ? '1' : '0');
|
||
|
count -= 1;
|
||
|
}
|
||
|
if (count) {
|
||
|
stream.Printf("%c", bit3 ? '1' : '0');
|
||
|
count -= 1;
|
||
|
}
|
||
|
if (count) {
|
||
|
stream.Printf("%c", bit2 ? '1' : '0');
|
||
|
count -= 1;
|
||
|
}
|
||
|
if (count) {
|
||
|
stream.Printf("%c", bit1 ? '1' : '0');
|
||
|
count -= 1;
|
||
|
}
|
||
|
if (count)
|
||
|
stream.Printf("%c", bit0 ? '1' : '0');
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool lldb_private::formatters::CFBinaryHeapSummaryProvider(
|
||
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
||
|
static ConstString g_TypeHint("CFBinaryHeap");
|
||
|
|
||
|
ProcessSP process_sp = valobj.GetProcessSP();
|
||
|
if (!process_sp)
|
||
|
return false;
|
||
|
|
||
|
ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
|
||
|
|
||
|
if (!runtime)
|
||
|
return false;
|
||
|
|
||
|
ObjCLanguageRuntime::ClassDescriptorSP descriptor(
|
||
|
runtime->GetClassDescriptor(valobj));
|
||
|
|
||
|
if (!descriptor.get() || !descriptor->IsValid())
|
||
|
return false;
|
||
|
|
||
|
uint32_t ptr_size = process_sp->GetAddressByteSize();
|
||
|
|
||
|
lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
|
||
|
|
||
|
if (!valobj_addr)
|
||
|
return false;
|
||
|
|
||
|
uint32_t count = 0;
|
||
|
|
||
|
bool is_type_ok =
|
||
|
false; // check to see if this is a CFBinaryHeap we know about
|
||
|
if (descriptor->IsCFType()) {
|
||
|
ConstString type_name(valobj.GetTypeName());
|
||
|
|
||
|
static ConstString g___CFBinaryHeap("__CFBinaryHeap");
|
||
|
static ConstString g_conststruct__CFBinaryHeap(
|
||
|
"const struct __CFBinaryHeap");
|
||
|
static ConstString g_CFBinaryHeapRef("CFBinaryHeapRef");
|
||
|
|
||
|
if (type_name == g___CFBinaryHeap ||
|
||
|
type_name == g_conststruct__CFBinaryHeap ||
|
||
|
type_name == g_CFBinaryHeapRef) {
|
||
|
if (valobj.IsPointerType())
|
||
|
is_type_ok = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (is_type_ok) {
|
||
|
lldb::addr_t offset = 2 * ptr_size + valobj_addr;
|
||
|
Status error;
|
||
|
count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error);
|
||
|
if (error.Fail())
|
||
|
return false;
|
||
|
} else
|
||
|
return false;
|
||
|
|
||
|
std::string prefix, suffix;
|
||
|
if (Language *language = Language::FindPlugin(options.GetLanguage())) {
|
||
|
if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
|
||
|
suffix)) {
|
||
|
prefix.clear();
|
||
|
suffix.clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
stream.Printf("%s\"%u item%s\"%s", prefix.c_str(), count,
|
||
|
(count == 1 ? "" : "s"), suffix.c_str());
|
||
|
return true;
|
||
|
}
|