[lldb] Upstream and adopt ReadUnsignedIntegersFromMemory in AppleObjCRuntimeV2 (#190564)
This PR upstreams ReadUnsignedIntegersFromMemory, which uses the MultiMemRead packet to speed up reading unsigned numbers from memory. Felipe landed this on Github because we didn't have a use-case for it upstream, until now, which uses it to batch up reading ObjC runtime symbols.
This commit is contained in:
committed by
GitHub
parent
1f1ea1ae41
commit
de0cb1cd04
@@ -1695,11 +1695,22 @@ public:
|
||||
size_t byte_size, uint64_t fail_value,
|
||||
Status &error);
|
||||
|
||||
/// Use Process::ReadMemoryRanges to efficiently read multiple unsigned
|
||||
/// integers from memory at once.
|
||||
llvm::SmallVector<std::optional<uint64_t>>
|
||||
ReadUnsignedIntegersFromMemory(llvm::ArrayRef<lldb::addr_t> addresses,
|
||||
unsigned byte_size);
|
||||
|
||||
int64_t ReadSignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size,
|
||||
int64_t fail_value, Status &error);
|
||||
|
||||
lldb::addr_t ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error);
|
||||
|
||||
/// Use Process::ReadMemoryRanges to efficiently read multiple pointers from
|
||||
/// memory at once.
|
||||
llvm::SmallVector<std::optional<lldb::addr_t>>
|
||||
ReadPointersFromMemory(llvm::ArrayRef<lldb::addr_t> ptr_locs);
|
||||
|
||||
bool WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value,
|
||||
Status &error);
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/Basic/TargetInfo.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/ScopeExit.h"
|
||||
|
||||
#include <cstdint>
|
||||
@@ -69,6 +70,23 @@
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
||||
namespace {
|
||||
struct RuntimeGlobalSymbolSpec {
|
||||
ConstString name;
|
||||
|
||||
/// Whether to only return the address or also the value.
|
||||
bool read_value = true;
|
||||
|
||||
/// A byte size of 0 means use the process pointer size.
|
||||
uint8_t byte_size = 0;
|
||||
};
|
||||
|
||||
struct RuntimeGlobalSymbolResult {
|
||||
uint64_t value = LLDB_INVALID_ADDRESS;
|
||||
bool success = false;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
char AppleObjCRuntimeV2::ID = 0;
|
||||
|
||||
static const char *g_get_dynamic_class_info_name =
|
||||
@@ -759,6 +777,75 @@ ExtractRuntimeGlobalSymbol(Process *process, ConstString name,
|
||||
return symbol_load_addr;
|
||||
}
|
||||
|
||||
// Batched version of ExtractRuntimeGlobalSymbol. Resolves symbols and reads
|
||||
// their values in a single batch using ReadUnsignedIntegersFromMemory.
|
||||
static llvm::SmallVector<RuntimeGlobalSymbolResult>
|
||||
ExtractRuntimeGlobalSymbolsBatched(
|
||||
Process *process, const ModuleSP &module_sp,
|
||||
llvm::ArrayRef<RuntimeGlobalSymbolSpec> specs) {
|
||||
|
||||
// Start out with all results in a failed state.
|
||||
llvm::SmallVector<RuntimeGlobalSymbolResult> results(specs.size());
|
||||
|
||||
if (!process || !module_sp)
|
||||
return results;
|
||||
|
||||
const uint8_t ptr_size = process->GetAddressByteSize();
|
||||
|
||||
// Phase 1: Resolve all symbols to addresses. Build a work list of entries
|
||||
// that need their values read in Phase 2.
|
||||
struct ReadEntry {
|
||||
size_t result_idx;
|
||||
lldb::addr_t addr;
|
||||
uint8_t byte_size;
|
||||
};
|
||||
llvm::SmallVector<ReadEntry> work_list;
|
||||
for (auto [i, spec] : llvm::enumerate(specs)) {
|
||||
const uint8_t size = spec.byte_size ? spec.byte_size : ptr_size;
|
||||
const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(
|
||||
spec.name, lldb::eSymbolTypeData);
|
||||
|
||||
if (!symbol || !symbol->ValueIsAddress())
|
||||
continue;
|
||||
|
||||
lldb::addr_t symbol_load_addr =
|
||||
symbol->GetAddressRef().GetLoadAddress(&process->GetTarget());
|
||||
if (symbol_load_addr == LLDB_INVALID_ADDRESS)
|
||||
continue;
|
||||
|
||||
if (!spec.read_value)
|
||||
results[i] = {symbol_load_addr, true};
|
||||
else
|
||||
work_list.push_back({i, symbol_load_addr, size});
|
||||
}
|
||||
|
||||
// Phase 2: Batch read values, grouping consecutive entries with the same
|
||||
// byte size into a single ReadUnsignedIntegersFromMemory call.
|
||||
llvm::stable_sort(work_list, [](const ReadEntry &a, const ReadEntry &b) {
|
||||
return a.byte_size < b.byte_size;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < work_list.size();) {
|
||||
const uint8_t byte_size = work_list[i].byte_size;
|
||||
const size_t group_start = i;
|
||||
|
||||
llvm::SmallVector<lldb::addr_t> addrs;
|
||||
while (i < work_list.size() && work_list[i].byte_size == byte_size)
|
||||
addrs.push_back(work_list[i++].addr);
|
||||
|
||||
auto read_values =
|
||||
process->ReadUnsignedIntegersFromMemory(addrs, byte_size);
|
||||
|
||||
for (size_t j = 0; j < addrs.size(); ++j) {
|
||||
size_t idx = work_list[group_start + j].result_idx;
|
||||
if (read_values[j].has_value())
|
||||
results[idx] = {*read_values[j], true};
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
static void RegisterObjCExceptionRecognizer(Process *process);
|
||||
|
||||
AppleObjCRuntimeV2::AppleObjCRuntimeV2(Process *process,
|
||||
@@ -2835,56 +2922,61 @@ AppleObjCRuntimeV2::NonPointerISACache::CreateInstance(
|
||||
AppleObjCRuntimeV2 &runtime, const lldb::ModuleSP &objc_module_sp) {
|
||||
Process *process(runtime.GetProcess());
|
||||
|
||||
Status error;
|
||||
|
||||
Log *log = GetLog(LLDBLog::Types);
|
||||
|
||||
auto objc_debug_isa_magic_mask = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_isa_magic_mask"), objc_module_sp, error);
|
||||
if (error.Fail())
|
||||
// Batch read all the ISA-related symbols in one go.
|
||||
enum ISASymbol {
|
||||
kMagicMask,
|
||||
kMagicValue,
|
||||
kClassMask,
|
||||
kIndexedMagicMask,
|
||||
kIndexedMagicValue,
|
||||
kIndexedIndexMask,
|
||||
kIndexedIndexShift,
|
||||
kIndexedClasses,
|
||||
kISASymbolCount,
|
||||
};
|
||||
llvm::SmallVector<RuntimeGlobalSymbolSpec> specs = {
|
||||
{ConstString("objc_debug_isa_magic_mask")},
|
||||
{ConstString("objc_debug_isa_magic_value")},
|
||||
{ConstString("objc_debug_isa_class_mask")},
|
||||
{ConstString("objc_debug_indexed_isa_magic_mask")},
|
||||
{ConstString("objc_debug_indexed_isa_magic_value")},
|
||||
{ConstString("objc_debug_indexed_isa_index_mask")},
|
||||
{ConstString("objc_debug_indexed_isa_index_shift")},
|
||||
{ConstString("objc_indexed_classes"), /*read_value=*/false},
|
||||
};
|
||||
assert(specs.size() == kISASymbolCount);
|
||||
|
||||
auto results =
|
||||
ExtractRuntimeGlobalSymbolsBatched(process, objc_module_sp, specs);
|
||||
|
||||
// Check required symbols.
|
||||
if (!results[kMagicMask].success || !results[kMagicValue].success ||
|
||||
!results[kClassMask].success)
|
||||
return nullptr;
|
||||
|
||||
auto objc_debug_isa_magic_value = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_isa_magic_value"), objc_module_sp,
|
||||
error);
|
||||
if (error.Fail())
|
||||
return nullptr;
|
||||
|
||||
auto objc_debug_isa_class_mask = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_isa_class_mask"), objc_module_sp, error);
|
||||
if (error.Fail())
|
||||
return nullptr;
|
||||
auto objc_debug_isa_magic_mask = results[kMagicMask].value;
|
||||
auto objc_debug_isa_magic_value = results[kMagicValue].value;
|
||||
auto objc_debug_isa_class_mask = results[kClassMask].value;
|
||||
|
||||
if (log)
|
||||
log->PutCString("AOCRT::NPI: Found all the non-indexed ISA masks");
|
||||
|
||||
bool foundError = false;
|
||||
auto objc_debug_indexed_isa_magic_mask = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_indexed_isa_magic_mask"), objc_module_sp,
|
||||
error);
|
||||
foundError |= error.Fail();
|
||||
// Check optional indexed ISA symbols.
|
||||
bool foundError = !results[kIndexedMagicMask].success ||
|
||||
!results[kIndexedMagicValue].success ||
|
||||
!results[kIndexedIndexMask].success ||
|
||||
!results[kIndexedIndexShift].success ||
|
||||
!results[kIndexedClasses].success;
|
||||
|
||||
auto objc_debug_indexed_isa_magic_value = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_indexed_isa_magic_value"),
|
||||
objc_module_sp, error);
|
||||
foundError |= error.Fail();
|
||||
auto objc_debug_indexed_isa_magic_mask = results[kIndexedMagicMask].value;
|
||||
auto objc_debug_indexed_isa_magic_value = results[kIndexedMagicValue].value;
|
||||
auto objc_debug_indexed_isa_index_mask = results[kIndexedIndexMask].value;
|
||||
auto objc_debug_indexed_isa_index_shift = results[kIndexedIndexShift].value;
|
||||
auto objc_indexed_classes = results[kIndexedClasses].value;
|
||||
|
||||
auto objc_debug_indexed_isa_index_mask = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_indexed_isa_index_mask"), objc_module_sp,
|
||||
error);
|
||||
foundError |= error.Fail();
|
||||
|
||||
auto objc_debug_indexed_isa_index_shift = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_indexed_isa_index_shift"),
|
||||
objc_module_sp, error);
|
||||
foundError |= error.Fail();
|
||||
|
||||
auto objc_indexed_classes =
|
||||
ExtractRuntimeGlobalSymbol(process, ConstString("objc_indexed_classes"),
|
||||
objc_module_sp, error, false);
|
||||
foundError |= error.Fail();
|
||||
|
||||
if (log)
|
||||
if (log && !foundError)
|
||||
log->PutCString("AOCRT::NPI: Found all the indexed ISA masks");
|
||||
|
||||
// we might want to have some rules to outlaw these other values (e.g if the
|
||||
@@ -2903,84 +2995,72 @@ AppleObjCRuntimeV2::TaggedPointerVendorV2::CreateInstance(
|
||||
AppleObjCRuntimeV2 &runtime, const lldb::ModuleSP &objc_module_sp) {
|
||||
Process *process(runtime.GetProcess());
|
||||
|
||||
Status error;
|
||||
// Batch read all tagged pointer symbols in one go.
|
||||
enum TaggedPtrSymbol {
|
||||
kMask,
|
||||
kSlotShift,
|
||||
kSlotMask,
|
||||
kPayloadLshift,
|
||||
kPayloadRshift,
|
||||
kClasses,
|
||||
kExtMask,
|
||||
kExtSlotShift,
|
||||
kExtSlotMask,
|
||||
kExtClasses,
|
||||
kExtPayloadLshift,
|
||||
kExtPayloadRshift,
|
||||
kTaggedPtrSymbolCount,
|
||||
};
|
||||
llvm::SmallVector<RuntimeGlobalSymbolSpec> specs = {
|
||||
{ConstString("objc_debug_taggedpointer_mask")},
|
||||
{ConstString("objc_debug_taggedpointer_slot_shift"), true, 4},
|
||||
{ConstString("objc_debug_taggedpointer_slot_mask"), true, 4},
|
||||
{ConstString("objc_debug_taggedpointer_payload_lshift"), true, 4},
|
||||
{ConstString("objc_debug_taggedpointer_payload_rshift"), true, 4},
|
||||
{ConstString("objc_debug_taggedpointer_classes"), false},
|
||||
{ConstString("objc_debug_taggedpointer_ext_mask")},
|
||||
{ConstString("objc_debug_taggedpointer_ext_slot_shift"), true, 4},
|
||||
{ConstString("objc_debug_taggedpointer_ext_slot_mask"), true, 4},
|
||||
{ConstString("objc_debug_taggedpointer_ext_classes"), false},
|
||||
{ConstString("objc_debug_taggedpointer_ext_payload_lshift"), true, 4},
|
||||
{ConstString("objc_debug_taggedpointer_ext_payload_rshift"), true, 4},
|
||||
};
|
||||
assert(specs.size() == kTaggedPtrSymbolCount);
|
||||
|
||||
auto objc_debug_taggedpointer_mask = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_taggedpointer_mask"), objc_module_sp,
|
||||
error);
|
||||
if (error.Fail())
|
||||
auto results =
|
||||
ExtractRuntimeGlobalSymbolsBatched(process, objc_module_sp, specs);
|
||||
|
||||
// Check required symbols.
|
||||
bool required_success =
|
||||
results[kMask].success && results[kSlotShift].success &&
|
||||
results[kSlotMask].success && results[kPayloadLshift].success &&
|
||||
results[kPayloadRshift].success && results[kClasses].success;
|
||||
|
||||
if (!required_success)
|
||||
return new TaggedPointerVendorLegacy(runtime);
|
||||
|
||||
auto objc_debug_taggedpointer_slot_shift = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_taggedpointer_slot_shift"),
|
||||
objc_module_sp, error, true, 4);
|
||||
if (error.Fail())
|
||||
return new TaggedPointerVendorLegacy(runtime);
|
||||
auto objc_debug_taggedpointer_mask = results[kMask].value;
|
||||
auto objc_debug_taggedpointer_slot_shift = results[kSlotShift].value;
|
||||
auto objc_debug_taggedpointer_slot_mask = results[kSlotMask].value;
|
||||
auto objc_debug_taggedpointer_payload_lshift = results[kPayloadLshift].value;
|
||||
auto objc_debug_taggedpointer_payload_rshift = results[kPayloadRshift].value;
|
||||
auto objc_debug_taggedpointer_classes = results[kClasses].value;
|
||||
|
||||
auto objc_debug_taggedpointer_slot_mask = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_taggedpointer_slot_mask"),
|
||||
objc_module_sp, error, true, 4);
|
||||
if (error.Fail())
|
||||
return new TaggedPointerVendorLegacy(runtime);
|
||||
|
||||
auto objc_debug_taggedpointer_payload_lshift = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_taggedpointer_payload_lshift"),
|
||||
objc_module_sp, error, true, 4);
|
||||
if (error.Fail())
|
||||
return new TaggedPointerVendorLegacy(runtime);
|
||||
|
||||
auto objc_debug_taggedpointer_payload_rshift = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_taggedpointer_payload_rshift"),
|
||||
objc_module_sp, error, true, 4);
|
||||
if (error.Fail())
|
||||
return new TaggedPointerVendorLegacy(runtime);
|
||||
|
||||
auto objc_debug_taggedpointer_classes = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_taggedpointer_classes"), objc_module_sp,
|
||||
error, false);
|
||||
if (error.Fail())
|
||||
return new TaggedPointerVendorLegacy(runtime);
|
||||
|
||||
// try to detect the "extended tagged pointer" variables - if any are
|
||||
// missing, use the non-extended vendor
|
||||
do {
|
||||
auto objc_debug_taggedpointer_ext_mask = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_taggedpointer_ext_mask"),
|
||||
objc_module_sp, error);
|
||||
if (error.Fail())
|
||||
break;
|
||||
|
||||
auto objc_debug_taggedpointer_ext_slot_shift = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_taggedpointer_ext_slot_shift"),
|
||||
objc_module_sp, error, true, 4);
|
||||
if (error.Fail())
|
||||
break;
|
||||
|
||||
auto objc_debug_taggedpointer_ext_slot_mask = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_taggedpointer_ext_slot_mask"),
|
||||
objc_module_sp, error, true, 4);
|
||||
if (error.Fail())
|
||||
break;
|
||||
|
||||
auto objc_debug_taggedpointer_ext_classes = ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_taggedpointer_ext_classes"),
|
||||
objc_module_sp, error, false);
|
||||
if (error.Fail())
|
||||
break;
|
||||
// Check if extended symbols are all present.
|
||||
bool extended_success =
|
||||
results[kExtMask].success && results[kExtSlotShift].success &&
|
||||
results[kExtSlotMask].success && results[kExtClasses].success &&
|
||||
results[kExtPayloadLshift].success && results[kExtPayloadRshift].success;
|
||||
|
||||
if (extended_success) {
|
||||
auto objc_debug_taggedpointer_ext_mask = results[kExtMask].value;
|
||||
auto objc_debug_taggedpointer_ext_slot_shift = results[kExtSlotShift].value;
|
||||
auto objc_debug_taggedpointer_ext_slot_mask = results[kExtSlotMask].value;
|
||||
auto objc_debug_taggedpointer_ext_classes = results[kExtClasses].value;
|
||||
auto objc_debug_taggedpointer_ext_payload_lshift =
|
||||
ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_taggedpointer_ext_payload_lshift"),
|
||||
objc_module_sp, error, true, 4);
|
||||
if (error.Fail())
|
||||
break;
|
||||
|
||||
results[kExtPayloadLshift].value;
|
||||
auto objc_debug_taggedpointer_ext_payload_rshift =
|
||||
ExtractRuntimeGlobalSymbol(
|
||||
process, ConstString("objc_debug_taggedpointer_ext_payload_rshift"),
|
||||
objc_module_sp, error, true, 4);
|
||||
if (error.Fail())
|
||||
break;
|
||||
results[kExtPayloadRshift].value;
|
||||
|
||||
return new TaggedPointerVendorExtended(
|
||||
runtime, objc_debug_taggedpointer_mask,
|
||||
@@ -2993,7 +3073,7 @@ AppleObjCRuntimeV2::TaggedPointerVendorV2::CreateInstance(
|
||||
objc_debug_taggedpointer_ext_payload_lshift,
|
||||
objc_debug_taggedpointer_ext_payload_rshift,
|
||||
objc_debug_taggedpointer_classes, objc_debug_taggedpointer_ext_classes);
|
||||
} while (false);
|
||||
}
|
||||
|
||||
// we might want to have some rules to outlaw these values (e.g if the
|
||||
// table's address is zero)
|
||||
|
||||
@@ -2317,6 +2317,45 @@ uint64_t Process::ReadUnsignedIntegerFromMemory(lldb::addr_t vm_addr,
|
||||
return fail_value;
|
||||
}
|
||||
|
||||
llvm::SmallVector<std::optional<uint64_t>>
|
||||
Process::ReadUnsignedIntegersFromMemory(llvm::ArrayRef<addr_t> addresses,
|
||||
unsigned integer_byte_size) {
|
||||
if (addresses.empty())
|
||||
return {};
|
||||
// Like ReadUnsignedIntegerFromMemory, this only supports a handful
|
||||
// of widths.
|
||||
if (!llvm::is_contained({1u, 2u, 4u, 8u}, integer_byte_size))
|
||||
return llvm::SmallVector<std::optional<uint64_t>>(addresses.size(),
|
||||
std::nullopt);
|
||||
|
||||
llvm::SmallVector<Range<addr_t, size_t>> ranges{
|
||||
llvm::map_range(addresses, [=](addr_t ptr) {
|
||||
return Range<addr_t, size_t>(ptr, integer_byte_size);
|
||||
})};
|
||||
|
||||
std::vector<uint8_t> buffer(integer_byte_size * addresses.size(), 0);
|
||||
llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> memory =
|
||||
ReadMemoryRanges(ranges, buffer);
|
||||
|
||||
llvm::SmallVector<std::optional<uint64_t>> result;
|
||||
result.reserve(addresses.size());
|
||||
const uint32_t addr_size = GetAddressByteSize();
|
||||
const ByteOrder byte_order = GetByteOrder();
|
||||
|
||||
for (llvm::MutableArrayRef<uint8_t> range : memory) {
|
||||
if (range.size() != integer_byte_size) {
|
||||
result.push_back(std::nullopt);
|
||||
continue;
|
||||
}
|
||||
|
||||
DataExtractor data(range.data(), integer_byte_size, byte_order, addr_size);
|
||||
offset_t offset = 0;
|
||||
result.push_back(data.GetMaxU64(&offset, integer_byte_size));
|
||||
assert(offset == integer_byte_size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t Process::ReadSignedIntegerFromMemory(lldb::addr_t vm_addr,
|
||||
size_t integer_byte_size,
|
||||
int64_t fail_value,
|
||||
@@ -2336,6 +2375,12 @@ addr_t Process::ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error) {
|
||||
return LLDB_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
llvm::SmallVector<std::optional<addr_t>>
|
||||
Process::ReadPointersFromMemory(llvm::ArrayRef<addr_t> ptr_locs) {
|
||||
const size_t ptr_size = GetAddressByteSize();
|
||||
return ReadUnsignedIntegersFromMemory(ptr_locs, ptr_size);
|
||||
}
|
||||
|
||||
bool Process::WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value,
|
||||
Status &error) {
|
||||
Scalar scalar;
|
||||
|
||||
@@ -521,3 +521,98 @@ TEST_F(MemoryTest, TestReadCStringsFromMemory) {
|
||||
llvm::zip(expected_valid_strings, expected_answers))
|
||||
EXPECT_EQ(maybe_str, expected_answer);
|
||||
}
|
||||
|
||||
TEST_F(MemoryTest, TestReadPointersFromMemory) {
|
||||
ArchSpec arch("x86_64-apple-macosx-");
|
||||
Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));
|
||||
DebuggerSP debugger_sp = Debugger::CreateInstance();
|
||||
ASSERT_TRUE(debugger_sp);
|
||||
TargetSP target_sp = CreateTarget(debugger_sp, arch);
|
||||
ASSERT_TRUE(target_sp);
|
||||
ListenerSP listener_sp(Listener::MakeListener("dummy"));
|
||||
ProcessSP process =
|
||||
std::make_shared<DummyReaderProcess>(target_sp, listener_sp);
|
||||
ASSERT_TRUE(process);
|
||||
|
||||
// Read pointers at arbitrary addresses.
|
||||
llvm::SmallVector<addr_t> ptr_locs = {0x0, 0x100, 0x2000, 0x123400};
|
||||
// Because of how DummyReaderProcess works, each byte of a memory read result
|
||||
// is its address modulo 256:
|
||||
constexpr addr_t expected_result = 0x0706050403020100;
|
||||
|
||||
llvm::SmallVector<std::optional<addr_t>> read_results =
|
||||
process->ReadPointersFromMemory(ptr_locs);
|
||||
|
||||
for (std::optional<addr_t> maybe_ptr : read_results) {
|
||||
ASSERT_TRUE(maybe_ptr.has_value());
|
||||
EXPECT_EQ(*maybe_ptr, expected_result);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MemoryTest, TestReadUnsignedIntegersFromMemory) {
|
||||
ArchSpec arch("x86_64-apple-macosx-");
|
||||
|
||||
Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));
|
||||
DebuggerSP debugger_sp = Debugger::CreateInstance();
|
||||
ASSERT_TRUE(debugger_sp);
|
||||
TargetSP target_sp = CreateTarget(debugger_sp, arch);
|
||||
ASSERT_TRUE(target_sp);
|
||||
ListenerSP listener_sp(Listener::MakeListener("dummy"));
|
||||
ProcessSP process =
|
||||
std::make_shared<DummyReaderProcess>(target_sp, listener_sp);
|
||||
ASSERT_TRUE(process);
|
||||
|
||||
{ // Test reads of size 1
|
||||
llvm::SmallVector<addr_t> locs = {0x0, 0x101, 0x2002, 0x123403};
|
||||
llvm::SmallVector<std::optional<addr_t>> read_results =
|
||||
process->ReadUnsignedIntegersFromMemory(locs, /*byte_size=*/1);
|
||||
|
||||
for (auto [maybe_int, loc] : llvm::zip(read_results, locs)) {
|
||||
ASSERT_TRUE(maybe_int.has_value());
|
||||
EXPECT_EQ(*maybe_int, static_cast<uint8_t>(loc));
|
||||
}
|
||||
}
|
||||
|
||||
{ // Test reads of size 2
|
||||
llvm::SmallVector<addr_t> locs = {0x0, 0x101, 0x2002, 0x123403};
|
||||
llvm::SmallVector<std::optional<addr_t>> read_results =
|
||||
process->ReadUnsignedIntegersFromMemory(locs, /*byte_size=*/2);
|
||||
|
||||
for (auto [maybe_int, loc] : llvm::zip(read_results, locs)) {
|
||||
ASSERT_TRUE(maybe_int.has_value());
|
||||
uint64_t lsb = static_cast<uint8_t>(loc);
|
||||
uint64_t expected_result = ((lsb + 1) << 8) | lsb;
|
||||
EXPECT_EQ(*maybe_int, expected_result);
|
||||
}
|
||||
}
|
||||
|
||||
{ // Test reads of size 4
|
||||
llvm::SmallVector<addr_t> locs = {0x0, 0x101, 0x2002, 0x123403};
|
||||
llvm::SmallVector<std::optional<addr_t>> read_results =
|
||||
process->ReadUnsignedIntegersFromMemory(locs, /*byte_size=*/4);
|
||||
|
||||
for (auto [maybe_int, loc] : llvm::zip(read_results, locs)) {
|
||||
ASSERT_TRUE(maybe_int.has_value());
|
||||
uint64_t lsb = static_cast<uint8_t>(loc);
|
||||
uint64_t expected_result =
|
||||
((lsb + 3) << 24) | ((lsb + 2) << 16) | ((lsb + 1) << 8) | lsb;
|
||||
EXPECT_EQ(*maybe_int, expected_result);
|
||||
}
|
||||
}
|
||||
|
||||
{ // Test reads of size 8
|
||||
llvm::SmallVector<addr_t> locs = {0x0, 0x101, 0x2002, 0x123403};
|
||||
llvm::SmallVector<std::optional<addr_t>> read_results =
|
||||
process->ReadUnsignedIntegersFromMemory(locs, /*byte_size=*/8);
|
||||
|
||||
for (auto [maybe_int, loc] : llvm::zip(read_results, locs)) {
|
||||
ASSERT_TRUE(maybe_int.has_value());
|
||||
uint64_t lsb = static_cast<uint8_t>(loc);
|
||||
uint64_t expected_result = ((lsb + 7) << 56) | ((lsb + 6) << 48) |
|
||||
((lsb + 5) << 40) | ((lsb + 4) << 32) |
|
||||
((lsb + 3) << 24) | ((lsb + 2) << 16) |
|
||||
((lsb + 1) << 8) | lsb;
|
||||
EXPECT_EQ(*maybe_int, expected_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user