blob: 68939310cc0d93dcf29a1d4659728fa039bdf5cd [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/substatement.h"
#include <gtest/gtest.h>
#include "src/developer/debug/zxdb/client/arch_info.h"
#include "src/developer/debug/zxdb/client/memory_dump.h"
#include "src/developer/debug/zxdb/client/mock_remote_api.h"
#include "src/developer/debug/zxdb/client/remote_api_test.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/symbols/line_details.h"
#include "src/developer/debug/zxdb/symbols/mock_module_symbols.h"
#include "src/developer/debug/zxdb/symbols/process_symbols_test_setup.h"
namespace zxdb {
namespace {
class SubstatementTest : public RemoteAPITest {};
} // namespace
// Several instructions on a single line with no inlines bit with some "line 0" line table entries
// in the middle. The "line 0" instructions should be skipped and all matching line table entries
// should be attributed to the line.
TEST_F(SubstatementTest, GetSubstatementCallsForLine_Line0) {
constexpr uint64_t kProcessKoid = 1234;
constexpr uint64_t kLoadAddress = 0x1000000;
Process* process = InjectProcess(kProcessKoid);
auto mock_module_symbols = InjectMockModule(process, kLoadAddress);
SymbolContext symbol_context(kLoadAddress);
std::vector<uint8_t> data{
0xe8, 0xce, 0x00, 0x00, 0x00, // call +0xce (relative to next instruction). [line 21]
0x48, 0x89, 0xde, // mov rsi, rbx [line 0]
0x48, 0x8d, 0x7c, 0x24, 0x0c, // lea rdi, [rsp + 0xc] [line 21]
0xff, 0xd0, // call rax [line 21]
0x48, 0x89, 0xde, // mov rsi, rbx [line 22]
};
mock_remote_api()->AddMemory(kLoadAddress, data);
// Ranges covering the addresses for each instruction in the data above.
const AddressRange kInstrAddressRanges[5] = {AddressRange(kLoadAddress, kLoadAddress + 5),
AddressRange(kLoadAddress + 5, kLoadAddress + 8),
AddressRange(kLoadAddress + 8, kLoadAddress + 13),
AddressRange(kLoadAddress + 13, kLoadAddress + 15),
AddressRange(kLoadAddress + 15, kLoadAddress + 18)};
FileLine source_file_line("file.cc", 21);
FileLine next_file_line("file.cc", 22);
FileLine zero_file_line;
// In this scheme, each instruction has its own line table entry.
mock_module_symbols->AddLineDetails(
kInstrAddressRanges[0].begin(),
LineDetails(source_file_line, {LineDetails::LineEntry(kInstrAddressRanges[0])}));
mock_module_symbols->AddLineDetails(
kInstrAddressRanges[1].begin(),
LineDetails(zero_file_line, {LineDetails::LineEntry(kInstrAddressRanges[1])}));
mock_module_symbols->AddLineDetails(
kInstrAddressRanges[2].begin(),
LineDetails(source_file_line, {LineDetails::LineEntry(kInstrAddressRanges[2])}));
mock_module_symbols->AddLineDetails(
kInstrAddressRanges[3].begin(),
LineDetails(source_file_line, {LineDetails::LineEntry(kInstrAddressRanges[3])}));
mock_module_symbols->AddLineDetails(
kInstrAddressRanges[4].begin(),
LineDetails(next_file_line, {LineDetails::LineEntry(kInstrAddressRanges[4])}));
AddressRange abs_extent(kLoadAddress, kLoadAddress + data.size());
// Containing function the current location is inside.
auto containing_function = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
containing_function->set_code_ranges(
symbol_context.AbsoluteToRelative(AddressRanges({abs_extent})));
Location loc(kLoadAddress, source_file_line, 0, symbol_context, containing_function);
std::vector<SubstatementCall> result;
GetSubstatementCallsForLine(process, loc,
[&result](const Err& err, std::vector<SubstatementCall> in_result) {
EXPECT_FALSE(err.has_error());
result = std::move(in_result);
});
EXPECT_TRUE(result.empty()); // Expect the callback to be run asynchronously.
loop().RunUntilNoTasks();
ASSERT_EQ(2u, result.size());
// First call is direct.
EXPECT_EQ(kInstrAddressRanges[0].begin(), result[0].call_addr);
ASSERT_TRUE(result[0].call_dest);
EXPECT_EQ(kInstrAddressRanges[1].begin() + 0xce, *result[0].call_dest);
EXPECT_FALSE(result[0].inline_call);
// 2nd call is indirect.
EXPECT_EQ(kInstrAddressRanges[3].begin(), result[1].call_addr);
EXPECT_FALSE(result[1].call_dest); // No known destintation for indirect call.
EXPECT_FALSE(result[1].inline_call);
}
TEST_F(SubstatementTest, GetSubstatementCallsForLine_WithInlines) {
constexpr uint64_t kProcessKoid = 1234;
constexpr uint64_t kLoadAddress = 0x1000000;
Process* process = InjectProcess(kProcessKoid);
auto mock_module_symbols = InjectMockModule(process, kLoadAddress);
SymbolContext symbol_context(kLoadAddress);
std::vector<uint8_t> data{
0xbf, 0xe0, 0xe5, 0x28, 0x00, // mov edi, 0x28e5e0
0x48, 0x89, 0xde, // mov rsi, rbx [inline routine]
0x48, 0x8d, 0x7c, 0x24, 0x0c, // lea rdi, [rsp + 0xc] [code block]
0xe8, 0xce, 0x00, 0x00, 0x00, // call +0xce (relative to next instruction).
0xe8, 0xd0, 0x00, 0x00, 0x00 // call +0xd0 (relative to next instruction).
};
mock_remote_api()->AddMemory(kLoadAddress, data);
// Ranges covering the addresses for each instruction in the data above.
const AddressRange kInstrAddressRanges[5] = {AddressRange(kLoadAddress, kLoadAddress + 5),
AddressRange(kLoadAddress + 5, kLoadAddress + 8),
AddressRange(kLoadAddress + 8, kLoadAddress + 13),
AddressRange(kLoadAddress + 13, kLoadAddress + 18),
AddressRange(kLoadAddress + 18, kLoadAddress + 23)};
FileLine source_file_line("file.cc", 21);
// Line information for the first instruction.
mock_module_symbols->AddLineDetails(
kInstrAddressRanges[0].begin(),
LineDetails(source_file_line, {LineDetails::LineEntry(kInstrAddressRanges[0])}));
// The second instruction is from some other file that was inlined.
mock_module_symbols->AddLineDetails(
kInstrAddressRanges[1].begin(),
LineDetails(FileLine("foo.h", 12), {LineDetails::LineEntry(kInstrAddressRanges[1])}));
// The third instruction's line entry covers the 3rd and 4th instructions.
mock_module_symbols->AddLineDetails(
kInstrAddressRanges[2].begin(),
LineDetails(source_file_line,
{LineDetails::LineEntry(AddressRange(kInstrAddressRanges[2].begin(),
kInstrAddressRanges[3].end()))}));
// The fifth instruction is on the next line.
mock_module_symbols->AddLineDetails(
kInstrAddressRanges[4].begin(),
LineDetails(FileLine(source_file_line.file(), source_file_line.line() + 1),
{LineDetails::LineEntry(kInstrAddressRanges[4])}));
AddressRange abs_extent(kLoadAddress, kLoadAddress + data.size());
// Containing function the current location is inside.
auto containing_function = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
containing_function->set_code_ranges(
symbol_context.AbsoluteToRelative(AddressRanges({abs_extent})));
// Inline function that counts as a call.
auto inline_function = fxl::MakeRefCounted<Function>(DwarfTag::kInlinedSubroutine);
constexpr TargetPointer kInlineStart = kLoadAddress + 5;
inline_function->set_code_ranges(symbol_context.AbsoluteToRelative(
AddressRanges(AddressRange(kInlineStart, kInlineStart + 3))));
inline_function->set_call_line(source_file_line);
// Lexical scope. This should not count toward the inline calls.
auto block = fxl::MakeRefCounted<CodeBlock>(DwarfTag::kLexicalBlock);
constexpr TargetPointer kBlockStart = kLoadAddress + 8;
block->set_code_ranges(
AddressRanges(symbol_context.AbsoluteToRelative(AddressRange(kBlockStart, kBlockStart + 5))));
containing_function->set_inner_blocks({LazySymbol(inline_function), LazySymbol(block)});
Location loc(kLoadAddress, source_file_line, 0, symbol_context, containing_function);
std::vector<SubstatementCall> result;
GetSubstatementCallsForLine(process, loc,
[&result](const Err& err, std::vector<SubstatementCall> in_result) {
EXPECT_FALSE(err.has_error());
result = std::move(in_result);
});
EXPECT_TRUE(result.empty()); // Expect the callback to be run asynchronously.
loop().RunUntilNoTasks();
ASSERT_EQ(2u, result.size());
// Inline call.
EXPECT_EQ(kInstrAddressRanges[1].begin(), result[0].call_addr);
EXPECT_EQ(kInstrAddressRanges[1].begin(), result[0].call_dest);
EXPECT_EQ(inline_function.get(), result[0].inline_call.get());
// Physical call is the 4th instruction.
EXPECT_EQ(kInstrAddressRanges[3].begin(), result[1].call_addr);
EXPECT_EQ(kLoadAddress + 0xe0, result[1].call_dest);
EXPECT_FALSE(result[1].inline_call);
}
} // namespace zxdb