blob: d77ff56beb73467423645a15e12d18d72a0bf32d [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 "src/developer/debug/zxdb/client/inline_thread_controller_test.h"
#include "src/developer/debug/ipc/records.h"
#include "src/developer/debug/zxdb/symbols/function.h"
#include "src/developer/debug/zxdb/symbols/symbol_context.h"
namespace zxdb {
namespace {
fxl::RefPtr<Function> MakeFunction(const char* name, bool is_inline, AddressRanges ranges) {
DwarfTag tag = is_inline ? DwarfTag::kInlinedSubroutine : DwarfTag::kSubprogram;
auto func = fxl::MakeRefCounted<Function>(tag);
func->set_assigned_name(name);
func->set_code_ranges(std::move(ranges));
return func;
}
} // namespace
const uint64_t InlineThreadControllerTest::kTopSP = 0x2010;
const uint64_t InlineThreadControllerTest::kMiddleSP = 0x2020;
const uint64_t InlineThreadControllerTest::kBottomSP = 0x2040;
// These address ranges must all be inside the symbolized module address so tests can mock symbols
// and line lookups inside of them.
const AddressRange InlineThreadControllerTest::kTopFunctionRange(kSymbolizedModuleAddress + 0x30000,
kSymbolizedModuleAddress +
0x40000);
// Must be inside the top function.
const AddressRange InlineThreadControllerTest::kTopInlineFunctionRange(
kSymbolizedModuleAddress + 0x30100, kSymbolizedModuleAddress + 0x30200);
const AddressRange InlineThreadControllerTest::kMiddleFunctionRange(
kSymbolizedModuleAddress + 0x10000, kSymbolizedModuleAddress + 0x20000);
// Must be inside the middle function
const AddressRange InlineThreadControllerTest::kMiddleInline1FunctionRange(
kSymbolizedModuleAddress + 0x10100, kSymbolizedModuleAddress + 0x10200);
// Must be inside the middle inline 1 function with same start address.
const AddressRange InlineThreadControllerTest::kMiddleInline2FunctionRange(
kSymbolizedModuleAddress + 0x10100, kSymbolizedModuleAddress + 0x10110);
// Note that the Stack object currently treats the location of caller of an inline frame to be the
// inline call site, while for physical frames this will be the following line. The reason for the
// difference is that inline functions don't necessarily have a clear return address, and the actual
// call is the easiest thing to compute.
const FileLine InlineThreadControllerTest::kTopInlineFileLine("file.cc", 11);
const FileLine InlineThreadControllerTest::kTopFileLine("file.cc", 15);
const FileLine InlineThreadControllerTest::kMiddleInline2FileLine("file.cc", 22);
const FileLine InlineThreadControllerTest::kMiddleInline1FileLine("file.cc", 25);
const FileLine InlineThreadControllerTest::kMiddleFileLine("file.cc", 30);
// static
fxl::RefPtr<Function> InlineThreadControllerTest::GetTopFunction() {
return MakeFunction("Top", false, AddressRanges(kTopFunctionRange));
}
// static
fxl::RefPtr<Function> InlineThreadControllerTest::GetTopInlineFunction() {
auto func = MakeFunction("TopInline", true, AddressRanges(kTopInlineFunctionRange));
func->set_call_line(kTopFileLine);
return func;
}
// static
fxl::RefPtr<Function> InlineThreadControllerTest::GetMiddleFunction() {
return MakeFunction("Middle", false, AddressRanges(kMiddleFunctionRange));
}
// static
fxl::RefPtr<Function> InlineThreadControllerTest::GetMiddleInline1Function() {
auto func = MakeFunction("MiddleInline1", true, AddressRanges(kMiddleInline1FunctionRange));
func->set_call_line(kMiddleFileLine);
return func;
}
// static
fxl::RefPtr<Function> InlineThreadControllerTest::GetMiddleInline2Function() {
auto func = MakeFunction("MiddleInline2", true, AddressRanges(kMiddleInline2FunctionRange));
func->set_call_line(kMiddleInline1FileLine);
return func;
}
// static
Location InlineThreadControllerTest::GetTopLocation(uint64_t address) {
return Location(address, kTopFileLine, 0, SymbolContext::ForRelativeAddresses(),
GetTopFunction());
}
// static
Location InlineThreadControllerTest::GetTopInlineLocation(uint64_t address) {
return Location(address, kTopInlineFileLine, 0, SymbolContext::ForRelativeAddresses(),
GetTopInlineFunction());
}
// static
Location InlineThreadControllerTest::GetMiddleLocation(uint64_t address) {
return Location(address, kMiddleFileLine, 0, SymbolContext::ForRelativeAddresses(),
GetMiddleFunction());
}
// static
Location InlineThreadControllerTest::GetMiddleInline1Location(uint64_t address) {
return Location(address, kMiddleInline1FileLine, 0, SymbolContext::ForRelativeAddresses(),
GetMiddleInline1Function());
}
// static
Location InlineThreadControllerTest::GetMiddleInline2Location(uint64_t address) {
return Location(address, kMiddleInline2FileLine, 0, SymbolContext::ForRelativeAddresses(),
GetMiddleInline2Function());
}
// static
std::unique_ptr<MockFrame> InlineThreadControllerTest::GetTopFrame(uint64_t address) {
return std::make_unique<MockFrame>(nullptr, nullptr, GetTopLocation(address), kTopSP, kMiddleSP);
}
// static
std::unique_ptr<MockFrame> InlineThreadControllerTest::GetTopInlineFrame(uint64_t address,
MockFrame* top) {
// The location is ambiguous if the address is at the beginning of the range.
return std::make_unique<MockFrame>(nullptr, nullptr, GetTopInlineLocation(address), kTopSP,
kMiddleSP, std::vector<debug::RegisterValue>(), kTopSP, top,
address == kTopInlineFunctionRange.begin());
}
// static
std::unique_ptr<MockFrame> InlineThreadControllerTest::GetMiddleFrame(uint64_t address) {
return std::make_unique<MockFrame>(nullptr, nullptr, GetMiddleLocation(address), kMiddleSP,
kBottomSP, std::vector<debug::RegisterValue>(), kMiddleSP);
}
// static
std::unique_ptr<MockFrame> InlineThreadControllerTest::GetMiddleInline1Frame(uint64_t address,
MockFrame* middle) {
return std::make_unique<MockFrame>(nullptr, nullptr, GetMiddleInline1Location(address), kMiddleSP,
kBottomSP, std::vector<debug::RegisterValue>(), kMiddleSP,
middle, address == kMiddleInline1FunctionRange.begin());
}
// static
std::unique_ptr<MockFrame> InlineThreadControllerTest::GetMiddleInline2Frame(uint64_t address,
MockFrame* middle) {
return std::make_unique<MockFrame>(nullptr, nullptr, GetMiddleInline2Location(address), kMiddleSP,
kBottomSP, std::vector<debug::RegisterValue>(), kMiddleSP,
middle, address == kMiddleInline2FunctionRange.begin());
}
// static
std::unique_ptr<MockFrame> InlineThreadControllerTest::GetBottomFrame(uint64_t address) {
return std::make_unique<MockFrame>(nullptr, nullptr,
Location(Location::State::kSymbolized, address), kBottomSP);
}
// static
std::vector<std::unique_ptr<MockFrame>> InlineThreadControllerTest::GetStack() {
AddressRange top_inline_range = kTopInlineFunctionRange;
auto top_frame = GetTopFrame(top_inline_range.begin());
AddressRange middle_inline2_range = kMiddleInline2FunctionRange;
auto middle_frame = GetMiddleFrame(middle_inline2_range.begin());
std::vector<std::unique_ptr<MockFrame>> frames;
frames.push_back(GetTopInlineFrame(top_inline_range.begin(), top_frame.get()));
frames.push_back(std::move(top_frame));
// These inlined functions in the middle of the stack must not be ambiguous because the stack will
// never generate ambiguous inlined functions for anything but the top frame. To do this, the
// address bust be after the beginning of the code range.
frames.push_back(GetMiddleInline2Frame(middle_inline2_range.begin() + 1, middle_frame.get()));
frames.push_back(GetMiddleInline1Frame(middle_inline2_range.begin() + 1, middle_frame.get()));
frames.push_back(std::move(middle_frame));
frames.push_back(GetBottomFrame(0x100000000));
return frames;
}
// static
std::vector<std::unique_ptr<Frame>> InlineThreadControllerTest::MockFrameVectorToFrameVector(
std::vector<std::unique_ptr<MockFrame>> mock_frames) {
std::vector<std::unique_ptr<Frame>> frames;
for (auto& mf : mock_frames)
frames.push_back(std::move(mf));
return frames;
}
} // namespace zxdb