blob: 2728af684e98d22f8e6b41daab7272004ce2b557 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/client/memory_dump.h"
#include <limits>
#include <gtest/gtest.h>
#include "src/lib/unwinder/error.h"
namespace zxdb {
TEST(MemoryDump, Empty) {
MemoryDump empty;
EXPECT_EQ(0u, empty.address());
EXPECT_EQ(0u, empty.size());
uint8_t byte = 65;
EXPECT_FALSE(empty.GetByte(0u, &byte));
EXPECT_EQ(0, byte);
byte = 65;
EXPECT_FALSE(empty.GetByte(0x1234556, &byte));
EXPECT_EQ(0, byte);
}
TEST(MemoryDump, Valid) {
std::vector<debug_ipc::MemoryBlock> input;
input.resize(3);
uint64_t begin1 = 0x1000;
uint64_t begin2 = 0x2000;
uint64_t begin3 = 0x3000;
uint64_t end = 0x4000;
// Invalid block.
input[0].address = begin1;
input[0].size = begin2 - begin1;
input[0].valid = false;
// Valid block filled with cycling bytes.
input[1].address = begin2;
input[1].size = begin3 - begin2;
input[1].valid = true;
for (uint64_t i = 0; i < input[1].size; i++) {
input[1].data.push_back(static_cast<uint8_t>(i % 0x100));
}
// Invalid block.
input[2].address = begin3;
input[2].size = end - begin3;
input[2].valid = false;
MemoryDump dump(std::move(input));
// Read from before begin.
uint8_t byte;
EXPECT_FALSE(dump.GetByte(0x100, &byte));
EXPECT_EQ(0u, byte);
// Read from first invalid block.
EXPECT_FALSE(dump.GetByte(begin1, &byte));
EXPECT_FALSE(dump.GetByte(begin1 + 10, &byte));
EXPECT_FALSE(dump.GetByte(begin2 - 1, &byte));
// Read from valid block.
EXPECT_TRUE(dump.GetByte(begin2, &byte));
EXPECT_EQ(0u, byte);
EXPECT_TRUE(dump.GetByte(begin2 + 10, &byte));
EXPECT_EQ(10u, byte);
EXPECT_TRUE(dump.GetByte(begin3 - 1, &byte));
EXPECT_EQ((begin3 - 1) % 0x100, byte);
// Read from third invalid block.
EXPECT_FALSE(dump.GetByte(begin3, &byte));
EXPECT_FALSE(dump.GetByte(begin3 + 10, &byte));
EXPECT_FALSE(dump.GetByte(end - 1, &byte));
// Read from past the end.
EXPECT_FALSE(dump.GetByte(end, &byte));
EXPECT_FALSE(dump.GetByte(end + 1000, &byte));
}
TEST(MemoryDump, Limits) {
uint64_t max = std::numeric_limits<uint64_t>::max();
std::vector<debug_ipc::MemoryBlock> block;
block.resize(1);
block[0].size = 0x1000;
block[0].address = max - block[0].size + 1;
block[0].valid = true;
for (uint64_t i = 0; i < block[0].size; i++) {
block[0].data.push_back(static_cast<uint8_t>(i % 0x100));
}
MemoryDump dump(std::move(block));
// Query last byte.
uint8_t byte = 10;
EXPECT_TRUE(dump.GetByte(max, &byte));
EXPECT_EQ(max % 0x100, byte);
}
TEST(MemoryDump, ReadBytes) {
std::vector<debug_ipc::MemoryBlock> input;
input.resize(3);
uint64_t begin1 = 0x1000;
uint64_t begin2 = 0x2000;
uint64_t begin3 = 0x3000;
uint64_t end = 0x4000;
// Valid blocks filled with cycling bytes.
input[0].address = begin1;
input[0].size = begin2 - begin1;
input[0].valid = true;
for (uint64_t i = 0; i < input[0].size; i++) {
input[0].data.push_back(static_cast<uint8_t>(i % 0x100));
}
input[1].address = begin2;
input[1].size = begin3 - begin2;
input[1].valid = true;
for (uint64_t i = 0; i < input[1].size; i++) {
input[1].data.push_back(static_cast<uint8_t>(i % 0x100));
}
input[2].address = begin3;
input[2].size = end - begin3;
input[2].valid = true;
for (uint64_t i = 0; i < input[2].size; i++) {
input[2].data.push_back(static_cast<uint8_t>(i % 0x100));
}
MemoryDump dump(std::move(input));
// Reading from the first block.
std::array<uint8_t, 8> buf;
constexpr std::array expected = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
ASSERT_TRUE(dump.ReadBytes(begin1, buf.size(), buf.data()).ok());
for (size_t i = 0; i < buf.size(); i++) {
EXPECT_EQ(buf[i], expected[i]);
}
// Second block.
buf = {};
ASSERT_TRUE(dump.ReadBytes(begin2, buf.size(), buf.data()).ok());
for (size_t i = 0; i < buf.size(); i++) {
EXPECT_EQ(buf[i], expected[i]);
}
// Third block.
buf = {};
ASSERT_TRUE(dump.ReadBytes(begin3, buf.size(), buf.data()).ok());
for (size_t i = 0; i < buf.size(); i++) {
EXPECT_EQ(buf[i], expected[i]);
}
// Reading from anywhere in the block should also work.
buf = {};
ASSERT_TRUE(dump.ReadBytes(begin3 + 0x100, buf.size(), buf.data()).ok());
for (size_t i = 0; i < buf.size(); i++) {
EXPECT_EQ(buf[i], expected[i]);
}
}
TEST(MemoryDump, ReadBytesErrors) {
std::vector<debug_ipc::MemoryBlock> input;
input.resize(3);
uint64_t begin1 = 0x1000;
uint64_t begin2 = 0x2000;
uint64_t begin3 = 0x3000;
uint64_t end = 0x4000;
// Valid block filled with cycling bytes.
input[0].address = begin1;
input[0].size = begin2 - begin1;
input[0].valid = true;
for (uint64_t i = 0; i < input[1].size; i++) {
input[0].data.push_back(static_cast<uint8_t>(i % 0x100));
}
input[1].address = begin2;
input[1].size = begin3 - begin2;
input[1].valid = true;
for (uint64_t i = 0; i < input[1].size; i++) {
input[1].data.push_back(static_cast<uint8_t>(i % 0x100));
}
// Invalid block.
input[2].address = begin3;
input[2].size = end - begin3;
input[2].valid = false;
MemoryDump dump(std::move(input));
std::array<uint8_t, 8> buf;
buf.fill(0);
// Reading from the end address.
EXPECT_TRUE(dump.ReadBytes(end, buf.size(), buf.data()).has_err());
for (const auto& b : buf) {
ASSERT_EQ(b, 0);
}
// Reading an address that isn't in the dump.
EXPECT_TRUE(dump.ReadBytes(0x5000, buf.size(), buf.data()).has_err());
for (const auto& b : buf) {
ASSERT_EQ(b, 0);
}
// Request more bytes than any block can provide.
EXPECT_TRUE(dump.ReadBytes(begin1, 0x10000, buf.data()).has_err());
for (const auto& b : buf) {
ASSERT_EQ(b, 0);
}
// Reading across the boundaries of different blocks will fail.
EXPECT_TRUE(dump.ReadBytes(begin2 - 4, buf.size(), buf.data()).has_err());
for (const auto& b : buf) {
ASSERT_EQ(b, 0);
}
// Reading from invalid blocks will fail.
EXPECT_TRUE(dump.ReadBytes(begin3, buf.size(), buf.data()).has_err());
for (const auto& b : buf) {
ASSERT_EQ(b, 0);
}
}
} // namespace zxdb