Daniel Carvalho has submitted this change. ( https://gem5-review.googlesource.com/c/public/gem5/+/43250 )

Change subject: base,tests: Add unit test for SymbolTable
......................................................................

base,tests: Add unit test for SymbolTable

Add a unit test for base/loader/symtab.*.

Change-Id: I81c14826ab629439897235cbaaf79047e603ff8d
Signed-off-by: Daniel R. Carvalho <oda...@yahoo.com.br>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/43250
Reviewed-by: Bobby R. Bruce <bbr...@ucdavis.edu>
Maintainer: Bobby R. Bruce <bbr...@ucdavis.edu>
Tested-by: kokoro <noreply+kok...@google.com>
---
M src/base/loader/SConscript
A src/base/loader/symtab.test.cc
2 files changed, 826 insertions(+), 0 deletions(-)

Approvals:
  Bobby R. Bruce: Looks good to me, approved; Looks good to me, approved
  kokoro: Regressions pass




diff --git a/src/base/loader/SConscript b/src/base/loader/SConscript
index d17875f..8b43512 100644
--- a/src/base/loader/SConscript
+++ b/src/base/loader/SConscript
@@ -35,3 +35,5 @@
 Source('memory_image.cc')
 Source('object_file.cc')
 Source('symtab.cc')
+GTest('symtab.test', 'symtab.test.cc', 'symtab.cc',
+    with_any_tags('gem5 serialize', 'gem5 trace'))
diff --git a/src/base/loader/symtab.test.cc b/src/base/loader/symtab.test.cc
new file mode 100644
index 0000000..e9edb11
--- /dev/null
+++ b/src/base/loader/symtab.test.cc
@@ -0,0 +1,824 @@
+/*
+ * Copyright (c) 2021 Daniel R. Carvalho
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest-spi.h>
+#include <gtest/gtest.h>
+
+#include <cassert>
+#include <initializer_list>
+#include <sstream>
+
+#include "base/gtest/serialization_fixture.hh"
+#include "base/loader/symtab.hh"
+
+using namespace gem5;
+
+/**
+ * Checks if a symbol's contents matches the expected contents to generate
+ * an error message. On matches an empty string is returned.
+ *
+ * @param symbol The symbol to check for a match.
+ * @param expected The expected symbol value.
+ * @return The error string, if any.
+ */
+std::string
+getSymbolError(const Loader::Symbol& symbol, const Loader::Symbol& expected)
+{
+    std::stringstream ss;
+
+    if (symbol.binding != expected.binding) {
+        ss << "    symbols' bindings do not match: seen `" <<
+            (int)symbol.binding << "`, expected `" <<
+            (int)expected.binding << "`.\n";
+    }
+
+    if (symbol.name != expected.name) {
+        ss << "    symbols' names do not match: seen `" << symbol.name <<
+            "`, expected `" << expected.name << "`.\n";
+    }
+
+    if (symbol.address != expected.address) {
+        ss << "    symbols' addresses do not match: seen `" <<
+ symbol.address << "`, expected `" << expected.address << "`.\n";
+    }
+
+    // No error, symbols match
+    return ss.str();
+}
+
+/**
+ * Checks that a symbol's contents matches the expected contents.
+ *
+ * @param m_symbol
+ * @param m_expected
+ * @param symbol The symbol to check for a match.
+ * @param expected The expected symbol value.
+ * @return A GTest's assertion result, with error message on failure.
+ */
+::testing::AssertionResult
+checkSymbol(const char* m_symbol, const char* m_expected,
+    const Loader::Symbol& symbol, const Loader::Symbol& expected)
+{
+    const std::string error = getSymbolError(symbol, expected);
+    if (!error.empty()) {
+        return ::testing::AssertionFailure() << "Symbols do not match (" <<
+            m_symbol << " != " << m_expected << ")\n" << error;
+    }
+    return ::testing::AssertionSuccess();
+}
+
+/**
+ * Checks that a symbol table contains only the expected symbols.
+ *
+ * @param symtab The table to check for matches.
+ * @param expected The expected table's contents.
+ * @return A GTest's assertion result, with error message on failure.
+ */
+::testing::AssertionResult
+checkTable(const Loader::SymbolTable& symtab,
+    const std::initializer_list<Loader::Symbol>& expected)
+{
+    if (expected.size() != (symtab.end() - symtab.begin())) {
+        return ::testing::AssertionFailure() << "the number of symbols in "
+            "the table does not match expectation (seen " <<
+            (symtab.end() - symtab.begin()) << ",  expected " <<
+            expected.size();
+    }
+
+ // @todo We should not assume that the symbols are seen in the given order
+    auto it = symtab.begin();
+    for (const auto& symbol : expected) {
+        const std::string error = getSymbolError(*(it++), symbol);
+        if (!error.empty()) {
+            return ::testing::AssertionFailure() << error;
+        }
+    }
+
+    return ::testing::AssertionSuccess();
+}
+
+/** Test that the constructor creates an empty table. */
+TEST(LoaderSymtabTest, EmptyConstruction)
+{
+    Loader::SymbolTable symtab;
+    ASSERT_TRUE(symtab.empty());
+    ASSERT_TRUE(checkTable(symtab, {}));
+}
+
+/** Test that the insertion of a symbol with no name fails. */
+TEST(LoaderSymtabTest, InsertSymbolNoName)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "", 0x10};
+    ASSERT_FALSE(symtab.insert(symbol));
+    ASSERT_TRUE(checkTable(symtab, {}));
+}
+
+/** Test that the insertion of one symbol in an empty table works. */
+TEST(LoaderSymtabTest, InsertOneSymbol)
+{
+    Loader::SymbolTable symtab;
+
+ Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "symbol", 0x10};
+    ASSERT_TRUE(symtab.insert(symbol));
+
+    ASSERT_FALSE(symtab.empty());
+    ASSERT_TRUE(checkTable(symtab, {symbol}));
+}
+
+/** Test that the insertion of a symbol with an existing name fails. */
+TEST(LoaderSymtabTest, InsertSymbolExistingName)
+{
+    Loader::SymbolTable symtab;
+
+    const std::string name = "symbol";
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, name, 0x10},
+        {Loader::Symbol::Binding::Local, name, 0x20},
+    };
+    ASSERT_TRUE(symtab.insert(symbols[0]));
+    ASSERT_FALSE(symtab.insert(symbols[1]));
+
+    // Check that the second symbol has not been inserted
+    ASSERT_TRUE(checkTable(symtab, {symbols[0]}));
+}
+
+/** Test that the insertion of a symbol with an existing address works. */
+TEST(LoaderSymtabTest, InsertSymbolExistingAddress)
+{
+    Loader::SymbolTable symtab;
+
+    const Addr addr = 0x10;
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", addr},
+        {Loader::Symbol::Binding::Local, "symbol2", addr},
+    };
+    ASSERT_TRUE(symtab.insert(symbols[0]));
+    ASSERT_TRUE(symtab.insert(symbols[1]));
+
+    // Check that all symbols are present
+    ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1]}));
+}
+
+/** Test that the insertion of one symbol in a non-empty table works. */
+TEST(LoaderSymtabTest, InsertMultipleSymbols)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+
+    ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2]}));
+}
+
+/**
+ * Test that a table with multiple entries becomes empty after being cleared.
+ */
+TEST(LoaderSymtabTest, ClearMultiple)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+
+    symtab.clear();
+    ASSERT_TRUE(symtab.empty());
+    ASSERT_TRUE(checkTable(symtab, {}));
+}
+
+/**
+ * Test the creation of a new table with offsets applied to the original
+ * symbols' addresses. Also verifies that the original table is kept the same.
+ */
+TEST(LoaderSymtabTest, Offset)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+
+    Addr offset = 0x5;
+    const auto symtab_new = symtab.offset(offset);
+
+    // Check that the original table is not modified
+    ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2]}));
+
+    // Check that the new table is offset
+    Loader::Symbol expected_symbols[] = {
+        {symbols[0].binding, symbols[0].name, symbols[0].address + offset},
+        {symbols[1].binding, symbols[1].name, symbols[1].address + offset},
+        {symbols[2].binding, symbols[2].name, symbols[2].address + offset},
+    };
+    ASSERT_TRUE(checkTable(*symtab_new, {expected_symbols[0],
+        expected_symbols[1], expected_symbols[2]}));
+}
+
+/**
+ * Test the creation of a new table with masks applied to the original
+ * symbols' addresses. Also verifies that the original table is kept the same.
+ */
+TEST(LoaderSymtabTest, Mask)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x1310},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x2810},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x2920},
+        {Loader::Symbol::Binding::Local, "symbol4", 0x3C20},
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+    EXPECT_TRUE(symtab.insert(symbols[3]));
+
+    Addr mask = 0x0110;
+    const auto symtab_new = symtab.mask(mask);
+
+    // Check that the original table is not modified
+    ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2],
+        symbols[3]}));
+
+    // Check that the new table is masked
+    Loader::Symbol expected_symbols[] = {
+        {symbols[0].binding, symbols[0].name, symbols[0].address & mask},
+        {symbols[1].binding, symbols[1].name, symbols[1].address & mask},
+        {symbols[2].binding, symbols[2].name, symbols[2].address & mask},
+        {symbols[3].binding, symbols[3].name, symbols[3].address & mask},
+    };
+    ASSERT_TRUE(checkTable(*symtab_new, {expected_symbols[0],
+        expected_symbols[1], expected_symbols[2], expected_symbols[3]}));
+}
+
+/**
+ * Test the creation of a new table with renamed symbols. Also verifies
+ * that the original table is kept the same.
+ */
+TEST(LoaderSymtabTest, Rename)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+        {Loader::Symbol::Binding::Local, "symbol4", 0x40},
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+    EXPECT_TRUE(symtab.insert(symbols[3]));
+
+    const auto symtab_new =
+        symtab.rename([](std::string &name) { name = name + "_suffix"; });
+
+    // Check that the original table is not modified
+    ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2],
+        symbols[3]}));
+
+    // Check that the new table's symbols have been renamed
+    Loader::Symbol expected_symbols[] = {
+ {symbols[0].binding, symbols[0].name + "_suffix", symbols[0].address}, + {symbols[1].binding, symbols[1].name + "_suffix", symbols[1].address}, + {symbols[2].binding, symbols[2].name + "_suffix", symbols[2].address}, + {symbols[3].binding, symbols[3].name + "_suffix", symbols[3].address},
+    };
+    ASSERT_TRUE(checkTable(*symtab_new, {expected_symbols[0],
+        expected_symbols[1], expected_symbols[2], expected_symbols[3]}));
+}
+
+/**
+ * Tests that renaming symbols respects the rule that symbols in a table
+ * must have unique names.
+ */
+TEST(LoaderSymtabTest, RenameNonUnique)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+        {Loader::Symbol::Binding::Local, "symbol4", 0x40},
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+    EXPECT_TRUE(symtab.insert(symbols[3]));
+
+    int i = 0;
+    const auto symtab_new = symtab.rename([&i](std::string &name)
+        {
+            if ((i++ % 2) == 0) {
+                name = "NonUniqueName";
+            }
+        });
+
+    // Check that the original table is not modified
+    ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2],
+        symbols[3]}));
+
+ // Check that the new table's symbols have been renamed, yet it does not
+    // contain the symbols with duplicated names
+    Loader::Symbol expected_symbols[] = {
+        {symbols[0].binding, "NonUniqueName", symbols[0].address},
+        {symbols[1].binding, symbols[1].name, symbols[1].address},
+        {symbols[3].binding, symbols[3].name, symbols[3].address},
+    };
+    ASSERT_TRUE(checkTable(*symtab_new, {expected_symbols[0],
+        expected_symbols[1], expected_symbols[2]}));
+}
+
+/**
+ * Test the creation of a new filtered table containing only the global symbols + * of the original table. Also verifies if the original table is kept the same.
+ */
+TEST(LoaderSymtabTest, Globals)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Global, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+        {Loader::Symbol::Binding::Weak, "symbol4", 0x40},
+        {Loader::Symbol::Binding::Weak, "symbol5", 0x50}
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+    EXPECT_TRUE(symtab.insert(symbols[3]));
+    EXPECT_TRUE(symtab.insert(symbols[4]));
+
+    const auto symtab_new = symtab.globals();
+
+    // Check that the original table is not modified
+    ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2],
+        symbols[3], symbols[4]}));
+
+    // Check that the new table only contains globals
+    ASSERT_TRUE(checkTable(*symtab_new, {symbols[1]}));
+}
+
+/**
+ * Test the creation of a new filtered table containing only the local symbols + * of the original table. Also verifies if the original table is kept the same.
+ */
+TEST(LoaderSymtabTest, Locals)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Global, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+        {Loader::Symbol::Binding::Weak, "symbol4", 0x40},
+        {Loader::Symbol::Binding::Weak, "symbol5", 0x50}
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+    EXPECT_TRUE(symtab.insert(symbols[3]));
+    EXPECT_TRUE(symtab.insert(symbols[4]));
+
+    const auto symtab_new = symtab.locals();
+
+    // Check that the original table is not modified
+    ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2],
+        symbols[3], symbols[4]}));
+
+    // Check that the new table only contains locals
+    ASSERT_TRUE(checkTable(*symtab_new, {symbols[0], symbols[2]}));
+}
+
+/**
+ * Test the creation of a new filtered table containing only the weak symbols + * of the original table. Also verifies if the original table is kept the same.
+ */
+TEST(LoaderSymtabTest, Weaks)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Global, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+        {Loader::Symbol::Binding::Weak, "symbol4", 0x40},
+        {Loader::Symbol::Binding::Weak, "symbol5", 0x50}
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+    EXPECT_TRUE(symtab.insert(symbols[3]));
+    EXPECT_TRUE(symtab.insert(symbols[4]));
+
+    const auto symtab_new = symtab.weaks();
+
+    // Check that the original table is not modified
+    ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2],
+        symbols[3], symbols[4]}));
+
+    // Check that the new table only contains weaks
+    ASSERT_TRUE(checkTable(*symtab_new, {symbols[3], symbols[4]}));
+}
+
+/** Test searching for a non-existent address. */
+TEST(LoaderSymtabTest, FindNonExistentAddress)
+{
+    Loader::SymbolTable symtab;
+
+ Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "symbol", 0x10};
+    EXPECT_TRUE(symtab.insert(symbol));
+
+    ASSERT_EQ(symtab.find(0x0), symtab.end());
+}
+
+/** Test searching for a unique address. */
+TEST(LoaderSymtabTest, FindUniqueAddress)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+
+    const auto it = symtab.find(symbols[2].address);
+    ASSERT_NE(it, symtab.end());
+    ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[2]);
+}
+
+/**
+ * Test that searching for a non-unique address returns the first occurrence.
+ */
+TEST(LoaderSymtabTest, FindNonUniqueAddress)
+{
+    Loader::SymbolTable symtab;
+
+    const Addr addr = 0x20;
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", addr},
+        {Loader::Symbol::Binding::Local, "symbol3", addr},
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+
+    const auto it = symtab.find(symbols[1].address);
+    ASSERT_NE(it, symtab.end());
+    ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[1]);
+}
+
+/** Test searching for a non-existent name. */
+TEST(LoaderSymtabTest, FindNonExistentName)
+{
+    Loader::SymbolTable symtab;
+
+ Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "symbol", 0x10};
+    EXPECT_TRUE(symtab.insert(symbol));
+
+    const auto it = symtab.find("symbol2");
+    ASSERT_EQ(it, symtab.end());
+}
+
+/** Test searching for an existing name. */
+TEST(LoaderSymtabTest, FindExistingName)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+
+    const auto it = symtab.find(symbols[1].name);
+    ASSERT_NE(it, symtab.end());
+    ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[1]);
+}
+
+/** Test searching for an existent address using findNearest. */
+TEST(LoaderSymtabTest, FindNearestExact)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+
+    const auto it = symtab.findNearest(symbols[1].address);
+    ASSERT_NE(it, symtab.end());
+    ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[1]);
+}
+
+/**
+ * Test that, in a table containing address A, searching for the nearest
+ * address of an address B where B=A+x returns A.
+ */
+TEST(LoaderSymtabTest, FindNearestRound)
+{
+    Loader::SymbolTable symtab;
+
+ Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "symbol", 0x10};
+    EXPECT_TRUE(symtab.insert(symbol));
+
+    const auto it = symtab.findNearest(symbol.address + 0x1);
+    ASSERT_NE(it, symtab.end());
+    ASSERT_PRED_FORMAT2(checkSymbol, *it, symbol);
+}
+
+/**
+ * Test that, in a table containing address A1 and A2, where A1<A2, searching + * for the nearest address of an address B where B=A1+x and B<A2 returns A1,
+ * and marks A2 as the next address.
+ */
+TEST(LoaderSymtabTest, FindNearestRoundWithNext)
+{
+    Loader::SymbolTable symtab;
+
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+
+    Addr next_addr;
+ const auto it = symtab.findNearest(symbols[0].address + 0x1, next_addr);
+    ASSERT_NE(it, symtab.end());
+    ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[0]);
+    ASSERT_EQ(next_addr, symbols[1].address);
+}
+
+/**
+ * Test that, in a table containing address A, searching for the nearest
+ * address of an address B where B=A+x returns A; however, the next address
+ * is non-existent, so it is marked as not valid.
+ */
+TEST(LoaderSymtabTest, FindNearestRoundWithNextNonExistent)
+{
+    Loader::SymbolTable symtab;
+
+ Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "symbol", 0x10};
+    EXPECT_TRUE(symtab.insert(symbol));
+
+    Addr next_addr;
+    const auto it = symtab.findNearest(symbol.address + 0x1, next_addr);
+    ASSERT_NE(it, symtab.end());
+    ASSERT_PRED_FORMAT2(checkSymbol, *it, symbol);
+    ASSERT_EQ(next_addr, 0);
+}
+
+/**
+ * Test that searching for the nearest address of an address lower than the
+ * lowest address fails.
+ */
+TEST(LoaderSymtabTest, FindNearestNonExistent)
+{
+    Loader::SymbolTable symtab;
+
+ Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "symbol", 0x10};
+    EXPECT_TRUE(symtab.insert(symbol));
+
+    const auto it = symtab.findNearest(symbol.address - 0x1);
+    ASSERT_EQ(it, symtab.end());
+}
+
+/**
+ * Test that the insertion of a symbol table's symbols in another table works
+ * when any symbol name conflicts.
+ */
+TEST(LoaderSymtabTest, InsertTableConflicting)
+{
+    const std::string name = "symbol";
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, name, 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+        {Loader::Symbol::Binding::Local, "symbol4", 0x40},
+        // Introduce name conflict
+        {Loader::Symbol::Binding::Local, name, 0x50},
+    };
+
+    // Populate table 1
+    Loader::SymbolTable symtab;
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+
+    // Populate table 2
+    Loader::SymbolTable symtab2;
+    EXPECT_TRUE(symtab2.insert(symbols[3]));
+    EXPECT_TRUE(symtab2.insert(symbols[4]));
+
+    // Do the insertion
+    ASSERT_FALSE(symtab.insert(symtab2));
+
+    // Check that none of the tables change
+    ASSERT_TRUE(checkTable(symtab2, {symbols[3], symbols[4]}));
+    ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2]}));
+}
+
+/**
+ * Test that the insertion of a symbol table's symbols in another table works
+ * when no symbols conflict.
+ */
+TEST(LoaderSymtabTest, InsertTable)
+{
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+        {Loader::Symbol::Binding::Local, "symbol4", 0x40},
+        {Loader::Symbol::Binding::Local, "symbol5", 0x50},
+    };
+
+    // Populate table 1
+    Loader::SymbolTable symtab;
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+
+    // Populate table 2
+    Loader::SymbolTable symtab2;
+    EXPECT_TRUE(symtab2.insert(symbols[3]));
+    EXPECT_TRUE(symtab2.insert(symbols[4]));
+
+    // Do the insertion
+    symtab.insert(symtab2);
+
+    // Check that symtab2 does not change
+    ASSERT_TRUE(checkTable(symtab2, {symbols[3], symbols[4]}));
+
+    // Check that the symbols from symtab2 have been inserted in symtab
+    ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2],
+        symbols[3], symbols[4]}));
+}
+
+using LoaderSymtabSerializationFixture = SerializationFixture;
+
+/** Test serialization. */
+TEST_F(LoaderSymtabSerializationFixture, Serialization)
+{
+    // Populate the table
+    Loader::SymbolTable symtab;
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+    };
+    EXPECT_TRUE(symtab.insert(symbols[0]));
+    EXPECT_TRUE(symtab.insert(symbols[1]));
+    EXPECT_TRUE(symtab.insert(symbols[2]));
+
+    // Serialization
+    std::ostringstream cp;
+    Serializable::ScopedCheckpointSection scs(cp, "Section1");
+    symtab.serialize("test", cp);
+
+    // Verify the output
+    ASSERT_THAT(cp.str(), ::testing::StrEq("\n[Section1]\ntest.size=3\n"
+        "test.addr_0=16\ntest.symbol_0=symbol\ntest.binding_0=1\n"
+        "test.addr_1=32\ntest.symbol_1=symbol2\ntest.binding_1=1\n"
+        "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n"));
+}
+
+/** Test unserialization. */
+TEST_F(LoaderSymtabSerializationFixture, Unserialization)
+{
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Local, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+    };
+    simulateSerialization("\n[Section1]\ntest.size=3\n"
+        "test.addr_0=16\ntest.symbol_0=symbol\ntest.binding_0=1\n"
+        "test.addr_1=32\ntest.symbol_1=symbol2\ntest.binding_1=1\n"
+        "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n");
+
+    Loader::SymbolTable unserialized_symtab;
+    CheckpointIn cp(getDirName());
+    Serializable::ScopedCheckpointSection scs(cp, "Section1");
+    unserialized_symtab.unserialize("test", cp);
+
+    // Make sure that the symbols in symtab are present in the
+    // unserialized table
+    ASSERT_TRUE(checkTable(unserialized_symtab, {symbols[0], symbols[1],
+        symbols[2]}));
+}
+
+/**
+ * Test unserialization missing binding.
+ * @todo Since there is no way to create a checkpoint without binding anymore,
+ * this functionality should be deprecated at some point.
+ */
+TEST_F(LoaderSymtabSerializationFixture, UnserializationMissingBinding)
+{
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Global, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+    };
+    simulateSerialization("\n[Section1]\ntest.size=3\n"
+        "test.addr_0=16\ntest.symbol_0=symbol\ntest.binding_0=1\n"
+        "test.addr_1=32\ntest.symbol_1=symbol2\n"
+        "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n");
+
+    Loader::SymbolTable unserialized_symtab;
+    CheckpointIn cp(getDirName());
+    Serializable::ScopedCheckpointSection scs(cp, "Section1");
+
+    unserialized_symtab.unserialize("test", cp);
+
+    // Make sure that the symbols in symtab are present in the
+    // unserialized table
+    ASSERT_TRUE(checkTable(unserialized_symtab, {symbols[0], symbols[1],
+        symbols[2]}));
+}
+
+/**
+ * Test unserialization missing binding with a different default value.
+ * @todo Since there is no way to create a checkpoint without binding anymore,
+ * this functionality should be deprecated at some point.
+ */
+TEST_F(LoaderSymtabSerializationFixture,
+    UnserializationMissingBindingChangeDefault)
+{
+    Loader::Symbol symbols[] = {
+        {Loader::Symbol::Binding::Local, "symbol", 0x10},
+        {Loader::Symbol::Binding::Weak, "symbol2", 0x20},
+        {Loader::Symbol::Binding::Local, "symbol3", 0x30},
+    };
+    simulateSerialization("\n[Section1]\ntest.size=3\n"
+        "test.addr_0=16\ntest.symbol_0=symbol\ntest.binding_0=1\n"
+        "test.addr_1=32\ntest.symbol_1=symbol2\n"
+        "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n");
+
+    Loader::SymbolTable unserialized_symtab;
+    CheckpointIn cp(getDirName());
+    Serializable::ScopedCheckpointSection scs(cp, "Section1");
+
+    unserialized_symtab.unserialize("test", cp,
+        Loader::Symbol::Binding::Weak);
+
+    // Make sure that the symbols in symtab are present in the
+    // unserialized table
+    ASSERT_TRUE(checkTable(unserialized_symtab, {symbols[0], symbols[1],
+        symbols[2]}));
+}

--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/43250
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: I81c14826ab629439897235cbaaf79047e603ff8d
Gerrit-Change-Number: 43250
Gerrit-PatchSet: 16
Gerrit-Owner: Daniel Carvalho <oda...@yahoo.com.br>
Gerrit-Reviewer: Bobby R. Bruce <bbr...@ucdavis.edu>
Gerrit-Reviewer: Daniel Carvalho <oda...@yahoo.com.br>
Gerrit-Reviewer: Gabe Black <gabe.bl...@gmail.com>
Gerrit-Reviewer: kokoro <noreply+kok...@google.com>
Gerrit-CC: Andreas Sandberg <andreas.sandb...@arm.com>
Gerrit-MessageType: merged
_______________________________________________
gem5-dev mailing list -- gem5-dev@gem5.org
To unsubscribe send an email to gem5-dev-le...@gem5.org
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s

Reply via email to