blob: 2a6bacdce73f90470d8fd03372ffa7dc28ae9dfe [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/console/analyze_memory.h"
#include <gtest/gtest.h>
#include "src/developer/debug/zxdb/client/mock_frame.h"
#include "src/developer/debug/zxdb/client/mock_process.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/client/stack.h"
#include "src/developer/debug/zxdb/common/test_with_loop.h"
#include "src/developer/debug/zxdb/console/output_buffer.h"
#include "src/developer/debug/zxdb/symbols/process_symbols_test_setup.h"
namespace zxdb {
namespace {
using ::zxdb::internal::MemoryAnalysis;
class AnalyzeMemoryTest : public TestWithLoop {};
} // namespace
TEST_F(AnalyzeMemoryTest, Basic) {
Session session;
ProcessSymbolsTestSetup setup;
MockProcess process(&session);
process.set_symbols(&setup.process());
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::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);
const uint64_t kStack0SP = kBegin;
const uint64_t kStack1SP = kBegin + 8;
constexpr uint64_t kAway = 0xFF00000000000; // Points out of the dump.
std::vector<debug::RegisterValue> frame0_regs = {
debug::RegisterValue(debug::RegisterID::kX64_rax, kBegin),
debug::RegisterValue(debug::RegisterID::kX64_rcx, kAway),
debug::RegisterValue(debug::RegisterID::kX64_rsp, kStack0SP)};
// Frame 1 duplicates rax (should not have both in the output), but rcx is different and this
// should be called out in the dump.
std::vector<debug::RegisterValue> frame1_regs = {
debug::RegisterValue(debug::RegisterID::kX64_rax, kBegin),
debug::RegisterValue(debug::RegisterID::kX64_rcx, kBegin + 16),
debug::RegisterValue(debug::RegisterID::kX64_rsp, kStack1SP)};
// Setup frames. This creates a top frame, an intermediate inline frame, and a bottom frame.
std::vector<std::unique_ptr<Frame>> frames;
frames.push_back(std::make_unique<MockFrame>(nullptr, nullptr,
Location(Location::State::kSymbolized, 0x1234),
kStack0SP, 0, frame0_regs, kStack0SP));
auto bottom_frame =
std::make_unique<MockFrame>(nullptr, nullptr, Location(Location::State::kSymbolized, 0x1200),
kStack1SP, 0, frame1_regs, kStack1SP);
// Inline frame (needs to reference the bottom frame below it).
frames.push_back(
std::make_unique<MockFrame>(nullptr, nullptr, Location(Location::State::kSymbolized, 0x1210),
kStack1SP, 0, frame1_regs, kStack1SP, bottom_frame.get()));
frames.push_back(std::move(bottom_frame));
// 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)));
analysis->Schedule(opts);
debug::MessageLoop::Current()->Run();
// The pointer to "inner" aspace entry should be annotated. The "outer" aspace entry is too large
// and so will be omitted.
//
// The "frame 2" registers should be omitted because they were covered by the inline "frame 1"
// registers above it.
EXPECT_EQ(
"Address Data \n"
" 0x1000 0x0000000000001000 ◁ rax, rsp, frame 0 base. ▷ inside map "
"\"inner\"\n"
" 0x1008 0x0000000010000000 ◁ frame 1 rsp, frame 1 base\n"
" 0x1010 0x0000000000000000 ◁ frame 1 rcx\n",
output.AsString());
}
} // namespace zxdb