blob: 1db61c5ca35e4231d2c172a7d19426d2eac09cb8 [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/symbols/find_line.h"
#include <gtest/gtest.h>
#include "src/developer/debug/zxdb/symbols/function.h"
#include "src/developer/debug/zxdb/symbols/mock_line_table.h"
namespace zxdb {
TEST(FindLine, GetAllLineTableMatchesInUnit) {
// The same file name can appear more than once as a line table "file" (they could be duplicates,
// or they could be encoded with a different directory that still resolves to the same file).
MockLineTable::FileNameVector files;
files.push_back("file1.cc"); // Name for file ID #1.
files.push_back("file2.cc"); // Name for file ID #2.
files.push_back("file1.cc"); // Name for file ID #3 (duplicate of #1).
MockLineTable::RowVector rows;
rows.push_back(MockLineTable::MakeStatementRow(0x1000, 1, 1)); // File #1, line 1.
rows.push_back(MockLineTable::MakeStatementRow(0x1001, 1, 2));
rows.push_back(MockLineTable::MakeStatementRow(0x1002, 2, 1)); // File #2, line 1.
rows.push_back(MockLineTable::MakeStatementRow(0x1003, 1, 1)); // Dupe for File 1, line 1.
rows.push_back(MockLineTable::MakeStatementRow(0x1004, 1, 90));
rows.push_back(MockLineTable::MakeStatementRow(0x1005, 1, 100));
rows.push_back(MockLineTable::MakeStatementRow(0x1006, 3, 95));
rows.push_back(MockLineTable::MakeStatementRow(0x1007, 3, 100));
rows.push_back(MockLineTable::MakeStatementRow(0x1008, 1, 98));
rows.push_back(MockLineTable::MakeEndSequenceRow(0x1009, 1, 98));
MockLineTable table(files, rows);
// There are two exact matches for line 1.
auto out = GetAllLineTableMatchesInUnit(table, "file1.cc", 1);
ASSERT_EQ(2u, out.size());
EXPECT_EQ(LineMatch(0x1000, 1, 0), out[0]);
EXPECT_EQ(LineMatch(0x1003, 1, 0), out[1]);
// Searching for line 99 should catch both the 90->100 and the 95->100 transitions.
out = GetAllLineTableMatchesInUnit(table, "file1.cc", 99);
ASSERT_EQ(2u, out.size());
EXPECT_EQ(LineMatch(0x1005, 100, 0), out[0]);
EXPECT_EQ(LineMatch(0x1007, 100, 0), out[1]);
// Searching for something greater than 100 should fail.
out = GetAllLineTableMatchesInUnit(table, "file1.cc", 101);
EXPECT_TRUE(out.empty());
}
// Out-of-order lines. In this case there was some later code moved before the line being searched
// for, even though the transition of addresses goes in the opposite direction (high to low), we
// should find the line.
TEST(FindLine, GetAllLineTableMatchesInUnit_Reverse) {
MockLineTable::FileNameVector files = {"file1.cc"};
MockLineTable::RowVector rows;
rows.push_back(MockLineTable::MakeStatementRow(0x1000, 1, 105)); // Later code moved before.
rows.push_back(MockLineTable::MakeStatementRow(0x1001, 1, 101)); // Best match.
rows.push_back(MockLineTable::MakeStatementRow(0x1002, 1, 91)); //
rows.push_back(MockLineTable::MakeStatementRow(0x1003, 1, 103)); // Less-good match.
rows.push_back(MockLineTable::MakeEndSequenceRow(0x1004, 1, 103));
MockLineTable table(files, rows);
auto out = GetAllLineTableMatchesInUnit(table, "file1.cc", 100);
ASSERT_EQ(1u, out.size());
EXPECT_EQ(LineMatch(0x1001, 101, 0), out[0]);
}
TEST(FindLine, GetBestLineMatches) {
// Empty input.
auto out = GetBestLineMatches({});
EXPECT_TRUE(out.empty());
// Should return the smallest line #.
out = GetBestLineMatches(
{LineMatch(0x1000, 10, 0), LineMatch(0x1001, 7, 0), LineMatch(0x1002, 100, 0)});
ASSERT_EQ(1u, out.size());
EXPECT_EQ(LineMatch(0x1001, 7, 0), out[0]);
// When the smallest match has dupes, all should be returned assuming the functions are different.
out = GetBestLineMatches({LineMatch(0x1000, 10, 0), LineMatch(0x1001, 20, 1),
LineMatch(0x1002, 10, 2), LineMatch(0x1003, 30, 3)});
ASSERT_EQ(2u, out.size());
EXPECT_EQ(LineMatch(0x1000, 10, 0), out[0]);
EXPECT_EQ(LineMatch(0x1002, 10, 2), out[1]);
// Dupes in the same function should return the smallest match.
out = GetBestLineMatches({LineMatch(0x1002, 10, 0), // Match, discarded due to higher addr.
LineMatch(0x1001, 20, 0), // No line match.
LineMatch(0x1000, 10, 0), // Match, this one last lowest addr.
LineMatch(0x1003, 10, 1)}); // Same line, different function.
ASSERT_EQ(2u, out.size());
EXPECT_EQ(LineMatch(0x1000, 10, 0), out[0]);
EXPECT_EQ(LineMatch(0x1003, 10, 1), out[1]);
}
// Tests looking for a prologue end marker that's not present.
TEST(FindLine, GetFunctionPrologueSize_NotFound) {
MockLineTable::FileNameVector files = {"file.cc"};
// This line table matches what's generated by GCC (which doesn't seem to generate prologue_end
// annotations) for the code:
// 1 #include <stdio.h>
// 2
// 3 void PrologueTest() { int a; scanf("%d", &a); printf("Scanned %d\n", a);
// 4 printf("END\n");
// 5 }
// 6
// 7 int main(int argc, char **argv) {
// 8 PrologueTest();
// 9 return 0;
// 10 }
MockLineTable::RowVector rows;
// clang-format off
rows.push_back(MockLineTable::MakeStatementRow( 0x1155, 1, 3)); // PrologueTest function start.
rows.push_back(MockLineTable::MakeStatementRow( 0x115d, 1, 3)); // First code in function.
rows.push_back(MockLineTable::MakeStatementRow( 0x1175, 1, 3));
rows.push_back(MockLineTable::MakeStatementRow( 0x118b, 1, 4));
rows.push_back(MockLineTable::MakeStatementRow( 0x1197, 1, 5));
rows.push_back(MockLineTable::MakeStatementRow( 0x119a, 1, 7)); // main function start.
rows.push_back(MockLineTable::MakeStatementRow( 0x11a9, 1, 8)); // First code in function.
rows.push_back(MockLineTable::MakeStatementRow( 0x11ae, 1, 9));
rows.push_back(MockLineTable::MakeStatementRow( 0x11b3, 1, 10));
rows.push_back(MockLineTable::MakeEndSequenceRow(0x11b5, 1, 10));
// clang-format on
MockLineTable table(files, rows);
auto prologue_test_fn = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
prologue_test_fn->set_code_ranges(AddressRanges(AddressRange(0x1155, 0x119a)));
auto main_fn = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
main_fn->set_code_ranges(AddressRanges(AddressRange(0x119a, 0x11b5)));
// Prologue ends at 2nd line table entry (0x115d for PrologueTest(), 0x11a9 for main()).
EXPECT_EQ(0x8u, GetFunctionPrologueSize(table, prologue_test_fn.get()));
EXPECT_EQ(0xfu, GetFunctionPrologueSize(table, main_fn.get()));
}
// Test looking for a prologue end marker that's present.
TEST(FindLine, GetFunctionPrologueSize_Marked) {
MockLineTable::FileNameVector files = {"file.cc"};
// This line table is the Clang version of the same code from above. It marks the prologue end.
// Here, we manually added an additional row that Clang didn't to push the marked prologue end
// past the 2nd row of a function. Otherwise this case would be identical to the above.
MockLineTable::RowVector rows;
// clang-format off
rows.push_back(MockLineTable::MakeStatementRow( 0x2010d0, 1, 3)); // PrologueTest fn start.
rows.push_back(MockLineTable::MakeStatementRow( 0x2010d1, 1, 3)); // Added manually.
rows.push_back(MockLineTable::MakePrologueEndRow( 0x2010d8, 1, 3)); // prologue_end
rows.push_back(MockLineTable::MakeNonStatementRow(0x2010ed, 1, 3));
rows.push_back(MockLineTable::MakeNonStatementRow(0x2010f0, 1, 3));
rows.push_back(MockLineTable::MakeStatementRow( 0x201104, 1, 4)); // Invalid fn decl. here.
rows.push_back(MockLineTable::MakeStatementRow( 0x201118, 1, 5));
rows.push_back(MockLineTable::MakeStatementRow( 0x201120, 1, 7)); // main function start.
rows.push_back(MockLineTable::MakePrologueEndRow( 0x201136, 1, 8)); // prologue_end
rows.push_back(MockLineTable::MakeStatementRow( 0x20113d, 1, 9));
rows.push_back(MockLineTable::MakeEndSequenceRow( 0x201143, 1, 9));
// clang-format on
MockLineTable table(files, rows);
auto prologue_test_fn = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
prologue_test_fn->set_code_ranges(AddressRanges(AddressRange(0x2010d0, 0x20111e)));
auto main_fn = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
main_fn->set_code_ranges(AddressRanges(AddressRange(0x201120, 0x201143)));
// Prologue ends at 2nd line table entry (0x115d for PrologueTest(), 0x11a9 for main()).
EXPECT_EQ(0x8u, GetFunctionPrologueSize(table, prologue_test_fn.get()));
EXPECT_EQ(0x16u, GetFunctionPrologueSize(table, main_fn.get()));
// Make a function declaration that consists of exactly one line. This is invalid and not
// actually in the example code. The prologue computation code should not try to go outside of
// this function to get the prologue, so it will return 0.
auto invalid_fn = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
invalid_fn->set_code_ranges(AddressRanges(AddressRange(0x201104, 0x201118)));
EXPECT_EQ(0u, GetFunctionPrologueSize(table, invalid_fn.get()));
// Make a function that's before the table
auto floating_fn = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
floating_fn->set_code_ranges(AddressRanges(AddressRange(0x10000, 0x10010)));
EXPECT_EQ(0u, GetFunctionPrologueSize(table, floating_fn.get()));
// One that's after the table.
floating_fn->set_code_ranges(AddressRanges(AddressRange(0x300000, 0x300020)));
EXPECT_EQ(0u, GetFunctionPrologueSize(table, floating_fn.get()));
}
// A synthetic test for when the prologue end is immediately followed by compiler-synthesized code
// (marked as "line 0") that the user doesn't want to see.
TEST(FindLine, GetFunctionPrologueSize_ZeroRows) {
MockLineTable::FileNameVector files = {"file.cc"};
// This line table is the Clang version of the same code from above. It marks the prologue end.
MockLineTable::RowVector rows;
// clang-format off
rows.push_back(MockLineTable::MakeStatementRow( 0x1155, 1, 3)); // PrologueTest function start.
rows.push_back(MockLineTable::MakeStatementRow( 0x115d, 1, 0)); // Generated code
rows.push_back(MockLineTable::MakeStatementRow( 0x1175, 1, 3)); // Identified first addr.
rows.push_back(MockLineTable::MakeStatementRow( 0x118b, 1, 4));
rows.push_back(MockLineTable::MakeStatementRow( 0x1197, 1, 5));
rows.push_back(MockLineTable::MakeEndSequenceRow(0x119a, 1, 5));
// clang-format on
MockLineTable table(files, rows);
auto fn = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
fn->set_code_ranges(AddressRanges(AddressRange(0x1155, 0x119a)));
// Test with no explicltly marked prologue_end.
EXPECT_EQ(0x20u, GetFunctionPrologueSize(table, fn.get()));
// Explicitly mark the prologue end and try again.
rows[1].PrologueEnd = 1;
EXPECT_EQ(0x20u, GetFunctionPrologueSize(table, fn.get()));
}
} // namespace zxdb