blob: b07a38113b9ce66d5eca89a59e52a6c3a10b16bd [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 "garnet/bin/zxdb/console/analyze_memory.h"
#include "garnet/bin/zxdb/client/mock_frame.h"
#include "garnet/bin/zxdb/client/mock_process.h"
#include "garnet/bin/zxdb/client/register.h"
#include "garnet/bin/zxdb/client/session.h"
#include "garnet/bin/zxdb/client/stack.h"
#include "garnet/bin/zxdb/console/output_buffer.h"
#include "garnet/bin/zxdb/symbols/process_symbols_test_setup.h"
#include "garnet/lib/debug_ipc/helper/platform_message_loop.h"
#include "gtest/gtest.h"
namespace zxdb {
namespace {
using ::zxdb::internal::MemoryAnalysis;
using namespace debug_ipc;
// Provides just enough of a Process implementation for the analyzer to run.
// It just needs a ProcessSymbols implementation.
class MyMockProcess : public MockProcess {
public:
explicit MyMockProcess(Session* session) : MockProcess(session) {}
// Process overrides:
ProcessSymbols* GetSymbols() override { return &symbols_.process(); }
private:
ProcessSymbolsTestSetup symbols_;
};
class AnalyzeMemoryTest : public testing::Test {
public:
AnalyzeMemoryTest() { loop_.Init(); }
~AnalyzeMemoryTest() { loop_.Cleanup(); }
private:
debug_ipc::PlatformMessageLoop loop_;
};
class MyMockRegisterSet : public RegisterSet {
public:
void AddRegister(RegisterCategory::Type cat_type, RegisterID id,
uint32_t length, uint64_t value) {
std::vector<uint8_t> data(sizeof(value));
memcpy(&data[0], &value, sizeof(value));
debug_ipc::Register ipc_reg({id, std::move(data)});
cat_map_[cat_type].push_back(Register(std::move(ipc_reg)));
}
const CategoryMap& category_map() const override { return cat_map_; }
private:
CategoryMap cat_map_;
};
} // namespace
TEST_F(AnalyzeMemoryTest, Basic) {
Session session;
MyMockProcess process(&session);
constexpr uint64_t kBegin = 0x1000;
constexpr uint32_t kLen = 24; // 3 lines of output (8 bytes each).
AnalyzeMemoryOptions opts;
opts.process = &process;
opts.begin_address = kBegin;
opts.bytes_to_read = kLen;
// The callback just saves the buffer to "output".
OutputBuffer output;
auto analysis = fxl::MakeRefCounted<MemoryAnalysis>(
opts,
[&output](const Err& err, OutputBuffer analysis, uint64_t next_addr) {
output = analysis;
debug_ipc::MessageLoop::Current()->QuitNow();
});
// Setup address space. Make one region inside another. The innermost one
// should be the one reported.
std::vector<debug_ipc::AddressRegion> aspace;
aspace.resize(2);
aspace[0].name = "root";
aspace[0].base = 0x1000;
aspace[0].size = 0x800000000000;
aspace[0].depth = 0;
aspace[1].name = "inner";
aspace[1].base = 0x1000;
aspace[1].size = 0x1000;
aspace[1].depth = 1;
analysis->SetAspace(aspace);
// Setup frames.
std::vector<std::unique_ptr<Frame>> frames;
const uint64_t kStack0SP = kBegin;
const uint64_t kStack1SP = kBegin + 8;
frames.push_back(std::make_unique<MockFrame>(
nullptr, nullptr, debug_ipc::StackFrame(0x100, kStack0SP, kStack0SP),
Location(Location::State::kSymbolized, 0x1234)));
frames.push_back(std::make_unique<MockFrame>(
nullptr, nullptr, debug_ipc::StackFrame(0x108, kStack1SP, kStack1SP),
Location(Location::State::kSymbolized, 0x1234)));
// Stack to hold our mock frames. This stack doesn't need to do anything
// other than return the frames again, so the delegate can be null.
Stack temp_stack(nullptr);
temp_stack.SetFramesForTest(std::move(frames), true);
analysis->SetStack(temp_stack);
// Setup memory.
std::vector<debug_ipc::MemoryBlock> blocks;
blocks.resize(1);
blocks[0].address = kBegin;
blocks[0].valid = true;
blocks[0].size = 0x24;
blocks[0].data = {
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Points to inner.
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // Inside outer.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Nothing
};
analysis->SetMemory(MemoryDump(std::move(blocks)));
// Setup registers (ESP points to beginning of block).
constexpr uint64_t kAway = 0xFF00000000000;
MyMockRegisterSet registers;
registers.AddRegister(RegisterCategory::Type::kGeneral, RegisterID::kX64_rax,
8u, kBegin);
registers.AddRegister(RegisterCategory::Type::kGeneral, RegisterID::kX64_rcx,
8u, kAway);
analysis->SetRegisters(registers);
analysis->Schedule(opts);
debug_ipc::MessageLoop::Current()->Run();
// The pointer to "inner" aspace entry should be annotated. The "outer"
// aspace entry is too large and so will be omitted.
EXPECT_EQ(
"Address Data \n"
" 0x1000 0x0000000000001000 ◁ rax. ▷ inside map \"inner\"\n"
" 0x1008 0x0000000010000000 ◁ frame 1 BP, frame 1 SP\n"
" 0x1010 0x0000000000000000 \n",
output.AsString());
}
} // namespace zxdb