blob: 6a3d7023463e5da0bdcbdf86a1543d72f0c42916 [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 <map>
#include "garnet/bin/zxdb/client/mock_frame.h"
#include "garnet/bin/zxdb/client/stack.h"
#include "garnet/bin/zxdb/symbols/function.h"
#include "gtest/gtest.h"
#include "lib/fxl/logging.h"
namespace zxdb {
namespace {
class MockStackDelegate : public Stack::Delegate {
public:
// Adds the given location to the list of things returned by
// GetSymbolizedLocationForStackFrame().
void AddLocation(const Location& loc) { locations_[loc.address()] = loc; }
void SyncFramesForStack(std::function<void()> callback) override {
// Not needed by this test.
FXL_NOTREACHED();
}
std::unique_ptr<Frame> MakeFrameForStack(const debug_ipc::StackFrame& input,
Location location) override {
return std::make_unique<MockFrame>(nullptr, nullptr, input, location);
}
Location GetSymbolizedLocationForStackFrame(
const debug_ipc::StackFrame& input) override {
auto found = locations_.find(input.ip);
if (found == locations_.end())
return Location(Location::State::kSymbolized, input.ip);
return found->second;
}
private:
std::map<uint64_t, Location> locations_;
};
} // namespace
// Tests that stack frames inside inline functions are expanded so that the
// inline functions have their own "inline" frames.
TEST(Stack, InlineExpansion) {
constexpr uint64_t kBottomAddr = 0x127365; // IP for bottom stack frame.
constexpr uint64_t kTopAddr = 0x893746123; // IP for top stack frale.
const char kFileName[] = "file.cc";
FileLine inline_call_line(kFileName, 10);
FileLine inline_exec_line(kFileName, 20);
FileLine top_line(kFileName, 30);
MockStackDelegate delegate;
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
// Non-inline location for the top stack frame.
auto top_func = fxl::MakeRefCounted<Function>(Symbol::kTagSubprogram);
top_func->set_assigned_name("Top");
Location top_location(kTopAddr, top_line, 0, symbol_context,
LazySymbol(top_func));
delegate.AddLocation(top_location);
// Bottom stack frame has a real function and an inline function.
auto bottom_inline_func =
fxl::MakeRefCounted<Function>(Symbol::kTagInlinedSubroutine);
bottom_inline_func->set_assigned_name("Inline");
bottom_inline_func->set_code_ranges(
{AddressRange(kBottomAddr, kBottomAddr + 8)});
bottom_inline_func->set_call_line(inline_call_line);
auto bottom_func = fxl::MakeRefCounted<Function>(Symbol::kTagSubprogram);
bottom_func->set_assigned_name("Bottom");
bottom_func->set_code_ranges(
{AddressRange(kBottomAddr - 8, kBottomAddr + 16)});
// For convenience, the inline function is nested inside the "bottom" func.
// This is not something you can actually do in C++ and will give a name
// "Bottom::Inline()". In real life the inline function will reference the
// actualy function definition in the correct namespace.
bottom_inline_func->set_parent(LazySymbol(bottom_func));
// The location returned by the symbol function will have the file/line
// inside the inline function.
Location bottom_location(kBottomAddr, inline_exec_line, 0,
symbol_context, LazySymbol(bottom_inline_func));
delegate.AddLocation(bottom_location);
Stack stack(&delegate);
// Send IPs that will map to the bottom and top addresses.
stack.SetFrames(debug_ipc::ThreadRecord::StackAmount::kFull,
{debug_ipc::StackFrame(kTopAddr, 0x100, 0x100),
debug_ipc::StackFrame(kBottomAddr, 0x200, 0x200)});
// This should expand to tree stack entries, the one in the middle should
// be the inline function expanded from the "bottom".
EXPECT_EQ(3u, stack.size());
// Bottom stack frame should be the non-inline bottom function.
EXPECT_FALSE(stack[2]->IsInline());
EXPECT_EQ(stack[2], stack[2]->GetPhysicalFrame());
EXPECT_EQ(kBottomAddr, stack[2]->GetAddress());
Location loc = stack[2]->GetLocation();
EXPECT_EQ(kBottomAddr, loc.address());
EXPECT_EQ(inline_call_line, loc.file_line());
EXPECT_EQ(bottom_func.get(), loc.symbol().Get()->AsFunction());
// Middle stack frame should be the inline bottom function at the same
// address, referencing the bottom one as the physical frame.
EXPECT_TRUE(stack[1]->IsInline());
EXPECT_EQ(stack[2], stack[1]->GetPhysicalFrame());
EXPECT_EQ(kBottomAddr, stack[1]->GetAddress());
loc = stack[1]->GetLocation();
EXPECT_EQ(kBottomAddr, loc.address());
EXPECT_EQ(inline_exec_line, loc.file_line());
EXPECT_EQ(bottom_inline_func.get(), loc.symbol().Get()->AsFunction());
// Top stack frame.
EXPECT_FALSE(stack[0]->IsInline());
EXPECT_EQ(stack[0], stack[0]->GetPhysicalFrame());
EXPECT_EQ(kTopAddr, stack[0]->GetAddress());
loc = stack[0]->GetLocation();
EXPECT_EQ(kTopAddr, loc.address());
EXPECT_EQ(top_line, loc.file_line());
EXPECT_EQ(top_func.get(), loc.symbol().Get()->AsFunction());
}
} // namespace zxdb