blob: f9b0bf5ade3f28cd64e628b065b9395ac46f9bf6 [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/symbols/line_table.h"
#include <gtest/gtest.h>
#include "src/developer/debug/zxdb/symbols/mock_line_table.h"
#include "src/developer/debug/zxdb/symbols/symbol_context.h"
// This test file covers the non-virtual helper functions on the LineTable object.
namespace zxdb {
TEST(LineTable, GetRowForAddress) {
// This load address makes the addresses in the table start at 0x1000.
SymbolContext context(0x1000);
MockLineTable::FileNameVector files;
files.push_back("file.cc"); // Name for file #1.
MockLineTable empty_table(files, {});
auto result = empty_table.GetRowForAddress(context, 0x1000);
EXPECT_TRUE(result.empty()); // Nothing is in the table.
// This contains only odd addresses so even ones should be covered by the previous row.
MockLineTable::RowVector rows;
rows.push_back(MockLineTable::MakeStatementRow(0x1, 1, 2));
rows.push_back(MockLineTable::MakeStatementRow(0x3, 1, 1)); // Dupe for File 1, line 1.
rows.push_back(MockLineTable::MakeStatementRow(0x5, 1, 100));
rows.push_back(MockLineTable::MakeStatementRow(0x5, 1, 100)); // Address dupe.
// No end sequence marker.
MockLineTable table(files, rows);
// Without an end sequence marker, the rows won't be found.
result = table.GetRowForAddress(context, 0x1001);
EXPECT_TRUE(result.empty()); // Nothing is in the table.
// Make one with an end sequence marker, it should become valid.
rows.push_back(MockLineTable::MakeEndSequenceRow(0x7, 1, 100)); // End marker, not a real addr.
table = MockLineTable(files, rows);
result = table.GetRowForAddress(context, 0x1000);
EXPECT_TRUE(result.empty()); // Should be not found since it's before the table.
result = table.GetRowForAddress(context, 0x1001);
ASSERT_FALSE(result.empty());
EXPECT_EQ(5u, result.sequence.size());
EXPECT_EQ(0u, result.index);
// Should be covered by the previous entry since there isn't a specific one for this address.
result = table.GetRowForAddress(context, 0x1002);
ASSERT_FALSE(result.empty());
EXPECT_EQ(5u, result.sequence.size());
EXPECT_EQ(0u, result.index);
// Should find the first of the two dupes.
result = table.GetRowForAddress(context, 0x1005);
ASSERT_FALSE(result.empty());
EXPECT_EQ(5u, result.sequence.size());
EXPECT_EQ(2u, result.index);
// End of table.
result = table.GetRowForAddress(context, 0x1007);
ASSERT_TRUE(result.empty());
// Add some rows corresponding to stripped code (sequence starting @ 0). Note that this overlaps
// the previous valid sequence and this one should be ignored.
rows.push_back(MockLineTable::MakeStatementRow(0x0, 1, 100));
rows.push_back(MockLineTable::MakeStatementRow(0x0, 1, 101));
rows.push_back(MockLineTable::MakeEndSequenceRow(0x0, 1, 101));
// Add some more valid rows. These ones are added to the beginning so the initial table is
// out-of-order.
MockLineTable::RowVector new_rows;
new_rows.push_back(MockLineTable::MakeStatementRow(0x101, 1, 2));
new_rows.push_back(MockLineTable::MakeStatementRow(0x103, 1, 1)); // Dupe for File 1, line 1.
new_rows.push_back(MockLineTable::MakeStatementRow(0x105, 1, 100));
new_rows.push_back(MockLineTable::MakeEndSequenceRow(0x107, 1, 100));
rows.insert(rows.begin(), new_rows.begin(), new_rows.end());
table = MockLineTable(files, rows);
// Address 0 should still be not found (the sequence starting @ 0 should be considered stripped).
result = table.GetRowForAddress(context, 0x1000);
EXPECT_TRUE(result.empty()); // Should be not found since it's before the table.
// Valid query from above.
result = table.GetRowForAddress(context, 0x1001);
ASSERT_FALSE(result.empty());
EXPECT_EQ(0u, result.index);
EXPECT_EQ(0x1u, result.sequence[result.index].Address.Address);
// Valid query for sequence added later.
result = table.GetRowForAddress(context, 0x1104);
ASSERT_FALSE(result.empty());
EXPECT_EQ(1u, result.index); // Index within newer sequence.
EXPECT_EQ(0x103u, result.sequence[result.index].Address.Address); // Line before queried addr.
// Query in between the two valid sequences
result = table.GetRowForAddress(context, 0x1080);
EXPECT_TRUE(result.empty()); // Should be not found.
}
// Tests handling for addresses matching "line 0" entries (indicating compiler-generated).
TEST(LineTable, GetRowForAddress_Line0) {
// This load address makes the addresses in the table start at 0x1000.
SymbolContext context(0x1000);
MockLineTable::FileNameVector files;
files.push_back("file.cc"); // Name for file #1.
MockLineTable::RowVector rows;
rows.push_back(MockLineTable::MakeStatementRow(0x1, 1, 2));
rows.push_back(MockLineTable::MakeStatementRow(0x3, 1, 0)); // <- Compiler-generated.
rows.push_back(MockLineTable::MakeStatementRow(0x5, 1, 3));
rows.push_back(MockLineTable::MakeStatementRow(0x7, 1, 0)); // <- Compiler-generated
// The settings for the "end sequence" marker aren't really used. They're normally inherited from
// the previous line because of the way the data is encoded in DWARF using a state machine. We
// inherit the same file/line from the previous entry in this same way.
rows.push_back(MockLineTable::MakeEndSequenceRow(0x9, 1, 0));
MockLineTable table(files, rows);
// Exact match query for the "line 0" address should return it.
auto result = table.GetRowForAddress(context, 0x1003, LineTable::kExactMatch);
ASSERT_FALSE(result.empty());
EXPECT_EQ(0x3u, result.get().Address.Address);
EXPECT_EQ(0u, result.get().Line);
// Exact match query for the address immediately following 0x7 should also be covered by that
// entry.
result = table.GetRowForAddress(context, 0x1008, LineTable::kExactMatch);
ASSERT_FALSE(result.empty());
EXPECT_EQ(0x7u, result.get().Address.Address);
EXPECT_EQ(0u, result.get().Line);
// Querying the first "line 0" entry with skipping should yield the next address.
result = table.GetRowForAddress(context, 0x1003, LineTable::kSkipCompilerGenerated);
ASSERT_FALSE(result.empty());
EXPECT_EQ(0x5u, result.get().Address.Address);
EXPECT_EQ(3u, result.get().Line);
// Query the address immediately following 0x7. Since this is the last real entry in the table
// (the end sequence marker doesn't count), the line should not be advanced and the "line 0"
// entry should be returned.
result = table.GetRowForAddress(context, 0x1008, LineTable::kSkipCompilerGenerated);
ASSERT_FALSE(result.empty());
EXPECT_EQ(0x7u, result.get().Address.Address);
EXPECT_EQ(0u, result.get().Line);
}
// Tests that when an address matches two lines, one an EndSequence and one a good match, that the
// second one is returned. This construct can appear on function boundaries when there is no padding
// between them. The EndSequece marker is non-inclusive, it just marks the end of the previous
// function.
TEST(LineTable, GetRowForAddress_EndSequence) {
SymbolContext context(0x1000);
MockLineTable::FileNameVector files;
files.push_back("file.cc"); // Name for file #1.
MockLineTable::RowVector rows;
rows.push_back(MockLineTable::MakeStatementRow(0x4, 1, 2));
rows.push_back(MockLineTable::MakeEndSequenceRow(0x8, 1, 2));
rows.push_back(MockLineTable::MakeStatementRow(0x8, 1, 3)); // <- Duplicate address.
rows.push_back(MockLineTable::MakeStatementRow(0xa, 1, 4));
rows.push_back(MockLineTable::MakeEndSequenceRow(0xa, 1, 4));
MockLineTable table(files, rows);
auto found = table.GetRowForAddress(context, 0x1008, LineTable::kSkipCompilerGenerated);
ASSERT_FALSE(found.empty());
EXPECT_EQ(0x8u, found.get().Address.Address);
EXPECT_EQ(3u, found.get().Line); // This is the non-end-sequence row at this address.
}
} // namespace zxdb