blob: 0af1f1cfc726f6ee50ce3ed6a8f90254846eb664 [file] [log] [blame]
// Copyright 2021 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/commands/verb_finish.h"
#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/console/console_test.h"
#include "src/developer/debug/zxdb/symbols/base_type.h"
#include "src/developer/debug/zxdb/symbols/function.h"
#include "src/developer/debug/zxdb/symbols/mock_symbol_data_provider.h"
#include "src/developer/debug/zxdb/symbols/modified_type.h"
namespace zxdb {
namespace {
using VerbFinishTest = ConsoleTest;
} // namespace
// This is an integration test covering "finish" plus printing the function return information.
TEST_F(VerbFinishTest, ReturnValue) {
// Make the called function. It returns a uint64_t*. We don't actually need to define the calling
// function symbol since it's never used.
auto uint64_type = fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeUnsigned, 8, "uint64_t");
auto uint64_ptr_type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, uint64_type);
auto called_function = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
called_function->set_assigned_name("MyFunction");
called_function->set_return_type(uint64_ptr_type);
SymbolContext symbol_context(0x8000000);
constexpr uint64_t kCalledIP = 0x8000000;
constexpr uint64_t kReturnIP = 0x8000010;
constexpr uint64_t kCalledSP = 0x2000000;
constexpr uint64_t kReturnSP = 0x2000008;
// Indicate a stop at the end of the called function.
std::vector<std::unique_ptr<Frame>> frames;
Location location(kCalledIP, FileLine(), 0, symbol_context, called_function);
Location return_location(kReturnIP, FileLine(), 0, symbol_context);
frames.push_back(std::make_unique<MockFrame>(&session(), thread(), location, kCalledSP));
frames.push_back(std::make_unique<MockFrame>(&session(), thread(), return_location, kReturnSP));
InjectExceptionWithStack(ConsoleTest::kProcessKoid, ConsoleTest::kThreadKoid,
debug_ipc::ExceptionType::kSingleStep, std::move(frames), true);
// That will produce some output we don't care about.
loop().RunUntilNoTasks();
console().FlushOutputEvents();
// The address the function returns and the data it points to.
constexpr uint64_t kReturnValuePtr = 0x67200000;
const std::vector<uint8_t> kPointedToData{42, 0, 0, 0, 0, 0, 0, 0}; // 42 little-endian.
// The returned pointer is set in rax. Make a frame and populate that.
EXPECT_EQ(debug::Arch::kX64, GetArch());
auto return_frame = std::make_unique<MockFrame>(&session(), thread(), return_location, kReturnSP);
MockSymbolDataProvider* provider = return_frame->GetMockSymbolDataProvider();
provider->AddRegisterValue(debug::RegisterID::kX64_rax, true, kReturnValuePtr);
provider->AddMemory(kReturnValuePtr, kPointedToData);
// Tell the debugger to finish this frame.
console().ProcessInputLine("finish");
console().FlushOutputEvents();
// Indicate a stop at the call site. This is the breakpoint that the "finish" controller will set.
std::vector<debug_ipc::BreakpointStats> hit_breakpoints;
hit_breakpoints.emplace_back();
hit_breakpoints[0].id = mock_remote_api()->last_breakpoint_id();
frames.push_back(std::move(return_frame));
InjectExceptionWithStack(ConsoleTest::kProcessKoid, ConsoleTest::kThreadKoid,
debug_ipc::ExceptionType::kSoftwareBreakpoint, std::move(frames), true,
hit_breakpoints);
// The system should evaluate the return value, print it, and then report the stop.
loop().RunUntilNoTasks();
// The return value should be decoded, the pointer should be resolved.
auto event = console().GetOutputEvent();
EXPECT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
EXPECT_EQ("MyFunction 🡲 (*)0x67200000 ➔ 42", event.output.AsString());
// After the return value should be the stop information (we didn't provide any calling symbols).
event = console().GetOutputEvent();
EXPECT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
EXPECT_EQ("🛑 0x8000010 (no symbol info)\n", event.output.AsString());
}
} // namespace zxdb