blob: 2ab615fb0a78fad3afecd7be3498100884c983cd [file] [log] [blame]
// Copyright 2019 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 <gtest/gtest.h>
#include "src/developer/debug/zxdb/client/mock_frame.h"
#include "src/developer/debug/zxdb/client/mock_remote_api.h"
#include "src/developer/debug/zxdb/client/remote_api_test.h"
#include "src/developer/debug/zxdb/console/mock_console.h"
namespace zxdb {
namespace {
using debug::MessageLoop;
class MemoryMockRemoteAPI : public MockRemoteAPI {
public:
// Return an empty AddressSpace reply.
void AddressSpace(const debug_ipc::AddressSpaceRequest& request,
fit::callback<void(const Err&, debug_ipc::AddressSpaceReply)> cb) override {
MessageLoop::Current()->PostTask(
FROM_HERE, [cb = std::move(cb)]() mutable { cb(Err(), debug_ipc::AddressSpaceReply()); });
}
};
class VerbsMemoryTest : public RemoteAPITest {
public:
std::unique_ptr<RemoteAPI> GetRemoteAPIImpl() {
auto remote_api = std::make_unique<MemoryMockRemoteAPI>();
memory_mock_remote_api_ = remote_api.get();
return remote_api;
}
MemoryMockRemoteAPI* memory_mock_remote_api() const { return memory_mock_remote_api_; }
private:
MemoryMockRemoteAPI* memory_mock_remote_api_ = nullptr; // Owned by System.
};
} // namespace
// This tests that the stack command is hooked up. The register and memory decoding are tested by
// the analyze memory tests.
//
// TODO(brettw) convert to a ConsoleTest to remove some boilerplate.
TEST_F(VerbsMemoryTest, Stack) {
MockConsole console(&session());
// Error case with nothing running.
console.ProcessInputLine("stack");
auto event = console.GetOutputEvent();
ASSERT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
ASSERT_EQ("\"stack\" requires a thread but there is no current thread.", event.output.AsString());
// Inject a fake running process.
constexpr uint64_t kProcessKoid = 1234;
InjectProcess(kProcessKoid);
constexpr uint64_t kThreadKoid = 5678;
Thread* thread = InjectThread(kProcessKoid, kThreadKoid);
// Eat the output from process attaching (this is asynchronously appended).
loop().RunUntilNoTasks();
console.FlushOutputEvents();
// Error case with no stopped thread.
console.ProcessInputLine("stack");
event = console.GetOutputEvent();
EXPECT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
EXPECT_EQ(
"\"stack\" requires a suspended thread but thread 1 is Running.\n"
"To view and sync thread state with the remote system, type \"thread\".\n"
"Or type \"pause\" to pause a running thread.",
event.output.AsString());
// Thread needs to be stopped. Add two frames with some different registers.
constexpr uint64_t kIP0 = 0x987654321;
constexpr uint64_t kSP0 = 0x10000000;
constexpr uint64_t kIP1 = kIP0 - 0x10;
constexpr uint64_t kSP1 = kSP0 + 0x10;
std::vector<std::unique_ptr<Frame>> frames;
frames.push_back(std::make_unique<MockFrame>(
&session(), thread, Location(Location::State::kSymbolized, kIP0), kSP0, 0,
std::vector<debug::RegisterValue>{debug::RegisterValue(debug::RegisterID::kX64_rsp, kSP0)}));
frames.push_back(std::make_unique<MockFrame>(
&session(), thread, Location(Location::State::kSymbolized, kIP1), kSP1, 0,
std::vector<debug::RegisterValue>{
debug::RegisterValue(debug::RegisterID::kX64_rsp, kSP1),
debug::RegisterValue(debug::RegisterID::kX64_rax, kSP0 + 0x20)}));
InjectExceptionWithStack(kProcessKoid, kThreadKoid, debug_ipc::ExceptionType::kSingleStep,
std::move(frames), true);
// Eat output from the exception.
loop().RunUntilNoTasks();
console.FlushOutputEvents();
// Supply some memory.
std::vector<uint8_t> mem_data;
mem_data.resize(1024);
mem_data[0] = 0xff;
mem_data[1] = 0xee;
memory_mock_remote_api()->AddMemory(kSP0, mem_data);
console.ProcessInputLine("stack");
loop().RunUntilNoTasks();
event = console.GetOutputEvent();
ASSERT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
ASSERT_EQ(
" Address Data \n"
"0x10000000 0x000000000000eeff ◁ rsp\n"
"0x10000008 0x0000000000000000 \n"
"0x10000010 0x0000000000000000 ◁ frame 1 rsp\n"
"0x10000018 0x0000000000000000 \n"
"0x10000020 0x0000000000000000 ◁ frame 1 rax\n"
"0x10000028 0x0000000000000000 \n"
"0x10000030 0x0000000000000000 \n"
"0x10000038 0x0000000000000000 \n"
"0x10000040 0x0000000000000000 \n"
"0x10000048 0x0000000000000000 \n"
"0x10000050 0x0000000000000000 \n"
"0x10000058 0x0000000000000000 \n"
"0x10000060 0x0000000000000000 \n"
"0x10000068 0x0000000000000000 \n"
"0x10000070 0x0000000000000000 \n"
"0x10000078 0x0000000000000000 \n"
"0x10000080 0x0000000000000000 \n"
"0x10000088 0x0000000000000000 \n"
"0x10000090 0x0000000000000000 \n"
"0x10000098 0x0000000000000000 \n"
"↓ For more lines: stack -n 20 0x100000a0",
event.output.AsString());
}
} // namespace zxdb