blob: ac9ad1f43f2d4fc22a51e46e2e27942658940bf3 [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/format_frame.h"
#include <gtest/gtest.h>
#include "src/developer/debug/shared/platform_message_loop.h"
#include "src/developer/debug/zxdb/client/mock_frame.h"
#include "src/developer/debug/zxdb/client/mock_stack_delegate.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/client/stack.h"
#include "src/developer/debug/zxdb/client/thread.h"
#include "src/developer/debug/zxdb/console/async_output_buffer_test_util.h"
#include "src/developer/debug/zxdb/console/command_utils.h"
#include "src/developer/debug/zxdb/console/console_test.h"
#include "src/developer/debug/zxdb/console/format_location.h"
#include "src/developer/debug/zxdb/console/format_node_console.h"
#include "src/developer/debug/zxdb/console/output_buffer.h"
#include "src/developer/debug/zxdb/symbols/function.h"
#include "src/developer/debug/zxdb/symbols/location.h"
#include "src/developer/debug/zxdb/symbols/symbol_context.h"
namespace zxdb {
namespace {
class FormatFrameTest : public ConsoleTest {
public:
// Synchronous wrappers around asynchronous formatting.
std::string SyncFormatStack(Thread* thread, const FormatStackOptions& opts) {
return LoopUntilAsyncOutputBufferComplete(FormatStack(thread, false, opts)).AsString();
}
std::string SyncFormatFrame(const Frame* frame, const FormatFrameOptions& opts, int id = -1) {
return LoopUntilAsyncOutputBufferComplete(FormatFrame(frame, opts, id)).AsString();
}
};
} // namespace
TEST_F(FormatFrameTest, FormatStack) {
Stack& stack = thread()->GetStack();
std::vector<std::unique_ptr<Frame>> frames;
frames.push_back(std::make_unique<MockFrame>(nullptr, nullptr, 0x1001, 0x2001, "Function0",
FileLine("file0.cc", 20)));
frames.push_back(std::make_unique<MockFrame>(nullptr, nullptr, 0x1002, 0x2002, "Function1",
FileLine("file1.cc", 21)));
frames.push_back(std::make_unique<MockFrame>(nullptr, nullptr, 0x1003, 0x2003, "Function2",
FileLine("file2.cc", 22)));
frames.push_back(std::make_unique<MockFrame>(nullptr, nullptr, 0x1004, 0x2004, "Function3",
FileLine("file3.cc", 23)));
stack.SetFramesForTest(std::move(frames), true);
FormatStackOptions opts;
// No pretty formatting.
EXPECT_EQ(
"▶ 0 Function0() • file0.cc:20\n"
" 1 Function1() • file1.cc:21\n"
" 2 Function2() • file2.cc:22\n"
" 3 Function3() • file3.cc:23\n",
SyncFormatStack(thread(), opts));
// Set up a PrettyStackManager to collapse the first two frames.
auto manager = fxl::MakeRefCounted<PrettyStackManager>();
std::vector<PrettyStackManager::StackGlob> matchers;
matchers.push_back(PrettyStackManager::StackGlob(
"Function0/1 Matcher",
{PrettyFrameGlob::Func("Function0"), PrettyFrameGlob::Func("Function1")}));
manager->SetMatchers(matchers);
opts.pretty_stack = manager;
// With pretty eliding of the first two frames.
EXPECT_EQ(
"▶ 0…1 «Function0/1 Matcher» (-r expands)\n"
" 2 Function2() • file2.cc:22\n"
" 3 Function3() • file3.cc:23\n",
SyncFormatStack(thread(), opts));
// Set the active frame to be frame 1. The matcher should no longer apply so the frame can be
// shown.
console().context().SetActiveFrameIdForThread(thread(), 1);
EXPECT_EQ(
" 0 Function0() • file0.cc:20\n"
"▶ 1 Function1() • file1.cc:21\n"
" 2 Function2() • file2.cc:22\n"
" 3 Function3() • file3.cc:23\n",
SyncFormatStack(thread(), opts));
}
TEST_F(FormatFrameTest, Unsymbolized) {
MockFrame frame(nullptr, nullptr, Location(Location::State::kSymbolized, 0x12345678), 0x567890, 0,
std::vector<debug::RegisterValue>(), 0xdeadbeef);
// Short format just prints the address.
FormatFrameOptions simple_opts;
simple_opts.detail = FormatFrameOptions::kSimple;
EXPECT_EQ("0x12345678", SyncFormatFrame(&frame, simple_opts));
// Long version should do the same.
FormatFrameOptions verbose_opts;
verbose_opts.detail = FormatFrameOptions::kVerbose;
EXPECT_EQ("0x12345678\n IP = 0x12345678, SP = 0x567890, base = 0xdeadbeef",
SyncFormatFrame(&frame, verbose_opts));
// Simple, with index.
EXPECT_EQ("Frame 3 0x12345678", SyncFormatFrame(&frame, simple_opts, 3));
}
TEST_F(FormatFrameTest, Inline) {
// This is to have some place for the inline frame to refer to as the underlying physical frame.
// The values are ignored.
MockFrame physical_frame(nullptr, nullptr, Location(Location::State::kSymbolized, 0x12345678),
0x567890);
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
auto function = fxl::MakeRefCounted<Function>(DwarfTag::kInlinedSubroutine);
function->set_assigned_name("Function");
MockFrame inline_frame(
nullptr, nullptr, Location(0x12345678, FileLine("file.cc", 22), 0, symbol_context, function),
0x567890, 0, std::vector<debug::RegisterValue>(), 0xdeadbeef, &physical_frame);
FormatFrameOptions simple_opts;
simple_opts.detail = FormatFrameOptions::kSimple;
EXPECT_EQ("Function() • file.cc:22 (inline)", SyncFormatFrame(&inline_frame, simple_opts));
FormatFrameOptions param_opts;
param_opts.detail = FormatFrameOptions::kParameters;
EXPECT_EQ("Function() • file.cc:22 (inline)", SyncFormatFrame(&inline_frame, param_opts));
FormatFrameOptions verbose_opts;
verbose_opts.detail = FormatFrameOptions::kVerbose;
EXPECT_EQ(
"Function() • file.cc:22 (inline)\n"
" IP = 0x12345678, SP = 0x567890, base = 0xdeadbeef",
SyncFormatFrame(&inline_frame, verbose_opts));
}
} // namespace zxdb