| // 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 |