blob: 8e916c489a128bdce499a2c5439923a1e75233a0 [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/index.h"
#include <inttypes.h>
#include <time.h>
#include <ostream>
#include <sstream>
#include <gtest/gtest.h>
#include "src/developer/debug/zxdb/common/string_util.h"
#include "src/developer/debug/zxdb/symbols/dwarf_binary.h"
#include "src/developer/debug/zxdb/symbols/dwarf_symbol_factory.h"
#include "src/developer/debug/zxdb/symbols/module_symbols_impl.h"
#include "src/developer/debug/zxdb/symbols/test_symbol_module.h"
#include "src/lib/fxl/strings/split_string.h"
namespace zxdb {
// Generates the symbol index of our simple test app. This may get updated if we change things
// but the important thing is that when this happens to check that the new index makes sense and
// then add it.
TEST(Index, IndexDump) {
TestSymbolModule setup(TestSymbolModule::kCheckedIn);
ASSERT_TRUE(setup.Init("", false).ok());
Index index;
index.CreateIndex(setup.symbols()->binary()->GetLLVMObjectFile());
// Symbol index.
std::ostringstream out;
index.root().Dump(out, setup.symbols()->symbol_factory(), 0);
const char kExpected[] = R"( Namespaces:
<<empty index string>>
Functions:
AnonNSFunction: {[0x1520, 0x152f)}
LineLookupTest<0>: {[0x1040, 0x104f)}
LineLookupTest<1>: {[0x1050, 0x105d)}
my_ns
Types:
Base1: 0x26a
Base2: 0x280
MyClass: 0x8aa
Types:
Inner: 0x8d5
Functions:
MyMemberTwo: {[0x1440, 0x144b)}
Functions:
MyMemberOne: {[0x1530, 0x153f)}
Variables:
kClassStatic: 0x897
Struct: 0x1f3
Functions:
MyFunc: {[0x1350, 0x1363)}
Variables:
kConstInt: 0x22d
kConstLongDouble: 0x239
StructMemberPtr: 0x35a
TypeForUsing: 0x1a3
Functions:
DoStructCall: {[0x1100, 0x114c)}
GetStruct: {[0x10a0, 0x10c5)}
GetStructMemberPtr: {[0x10d0, 0x10e1)}
InlinedFunction: {[0x1150, 0x115f)}, {[0x12a0, 0x12a6)}
NamespaceFunction: {[0x1450, 0x145b)}
PassRValueRef: {[0x10f0, 0x10fa)}
Variables:
kGlobal: 0x87e
std
Types:
nullptr_t: 0x186
Types:
ClassInTest2: 0x9ea
Functions:
FunctionInTest2: {[0x1540, 0x154b)}
ForInline: 0x4d4
Functions:
ForInline: {[0x1390, 0x13a4)}
InlinedFunction: {[0x124f, 0x125d)}
MyTemplate<my_ns::Struct, 42>: 0x465
Functions:
MyTemplate: {[0x1370, 0x1385)}
StructWithEnums: 0x104
Types:
RegularEnum: 0x119
TypedEnum: 0x152
VirtualBase: 0x6d0
Functions:
DoIt: {[0x1410, 0x141a)}
VirtualBase: {[0x13f0, 0x140e)}
VirtualDerived: 0x68e
Functions:
DoIt: {[0x1410, 0x141a)}
VirtualDerived: {[0x13b0, 0x13ec)}
__ARRAY_SIZE_TYPE__: 0x80b
char: 0x3d7
int: 0xd2
long double: 0x3cb
signed char: 0x17a
unsigned int: 0x16c
Functions:
CallInline: {[0x1290, 0x12a8)}
CallInlineMember: {[0x11e0, 0x1287)}
DoLineLookupTest: {[0x1000, 0x1034)}
GetIntPtr: {[0x1060, 0x1068)}
GetNullPtrT: {[0x12e0, 0x12e8)}
GetString: {[0x1070, 0x1099)}
GetStructWithEnums: {[0x12b0, 0x12da)}
GetTemplate: {[0x1190, 0x11d6)}
GetUsing: {[0x12f0, 0x1309)}
GetVirtualDerived: {[0x1310, 0x134f)}
My2DArray: {[0x1160, 0x118b)}
MyFunction: {[0x1460, 0x1516)}
)";
EXPECT_EQ(kExpected, out.str());
// File index.
std::ostringstream files;
index.DumpFileIndex(files);
const char kExpectedFiles[] =
R"(line_lookup_symbol_test.cc -> ../../src/developer/debug/zxdb/symbols/test_data/line_lookup_symbol_test.cc -> 1 units
type_test.cc -> ../../src/developer/debug/zxdb/symbols/test_data/type_test.cc -> 1 units
zxdb_symbol_test.cc -> ../../src/developer/debug/zxdb/symbols/test_data/zxdb_symbol_test.cc -> 1 units
zxdb_symbol_test2.cc -> ../../src/developer/debug/zxdb/symbols/test_data/zxdb_symbol_test2.cc -> 1 units
)";
EXPECT_EQ(kExpectedFiles, files.str());
// Test that the slow indexing path produces the same result as the fast path.
Index slow_index;
slow_index.CreateIndex(setup.symbols()->binary()->GetLLVMObjectFile(), true);
out = std::ostringstream();
slow_index.root().Dump(out, setup.symbols()->symbol_factory(), 0);
EXPECT_EQ(kExpected, out.str());
}
TEST(Index, FindExactFunction) {
TestSymbolModule setup(TestSymbolModule::kCheckedIn);
ASSERT_TRUE(setup.Init("", false).ok());
Index index;
index.CreateIndex(setup.symbols()->binary()->GetLLVMObjectFile());
// Standalone function search.
auto result = index.FindExact(TestSymbolModule::SplitName(TestSymbolModule::kMyFunctionName));
EXPECT_EQ(1u, result.size()) << "Symbol not found: " << TestSymbolModule::kMyFunctionName;
// Standalone function inside a named namespace.
result = index.FindExact(TestSymbolModule::SplitName(TestSymbolModule::kNamespaceFunctionName));
EXPECT_EQ(1u, result.size()) << "Symbol not found: " << TestSymbolModule::kNamespaceFunctionName;
// Standalone function inside an anonymous namespace. Currently this is indexed as if the
// anonymous namespace wasn't there, but this may need to change in the future.
result = index.FindExact(TestSymbolModule::SplitName(TestSymbolModule::kAnonNSFunctionName));
EXPECT_EQ(1u, result.size()) << "Symbol not found: " << TestSymbolModule::kAnonNSFunctionName;
// Namespace + class member function search.
result = index.FindExact(TestSymbolModule::SplitName(TestSymbolModule::kMyMemberOneName));
EXPECT_EQ(1u, result.size()) << "Symbol not found: " << TestSymbolModule::kMyMemberOneName;
// Same but in the 2nd compilation unit (tests unit-relative addressing).
result = index.FindExact(TestSymbolModule::SplitName(TestSymbolModule::kFunctionInTest2Name));
EXPECT_EQ(1u, result.size()) << "Symbol not found: " << TestSymbolModule::kFunctionInTest2Name;
// Namespace + class + struct with static member function search.
result = index.FindExact(TestSymbolModule::SplitName(TestSymbolModule::kMyMemberTwoName));
EXPECT_EQ(1u, result.size()) << "Symbol not found: " << TestSymbolModule::kMyMemberTwoName;
// Global variable.
result = index.FindExact(TestSymbolModule::SplitName(TestSymbolModule::kGlobalName));
EXPECT_EQ(1u, result.size()) << "Symbol not found: " << TestSymbolModule::kGlobalName;
// Class static variable.
result = index.FindExact(TestSymbolModule::SplitName(TestSymbolModule::kClassStaticName));
EXPECT_EQ(1u, result.size()) << "Symbol not found: " << TestSymbolModule::kClassStaticName;
// Something not found.
result = index.FindExact(TestSymbolModule::SplitName("my_ns::MyClass::NotFoundThing"));
EXPECT_TRUE(result.empty());
}
TEST(Index, FindFileMatches) {
TestSymbolModule setup(TestSymbolModule::kCheckedIn);
ASSERT_TRUE(setup.Init("", false).ok());
Index index;
index.CreateIndex(setup.symbols()->binary()->GetLLVMObjectFile());
// Simple filename-only query that succeeds.
std::vector<std::string> result = index.FindFileMatches("zxdb_symbol_test.cc");
ASSERT_EQ(1u, result.size());
EXPECT_TRUE(StringEndsWith(result[0], "symbols/test_data/zxdb_symbol_test.cc"));
// Save the full path for later.
std::string full_path = result[0];
// Simple filename-only query that fails.
result = index.FindFileMatches("nonexistant.cc");
EXPECT_EQ(0u, result.size());
// Multiple path components.
result = index.FindFileMatches("symbols/test_data/zxdb_symbol_test.cc");
EXPECT_EQ(1u, result.size());
// Ends-with match but doesn't start on a slash boundary.
result = index.FindFileMatches("nt/test_data/zxdb_symbol_test.cc");
EXPECT_EQ(0u, result.size());
// Full path match.
result = index.FindFileMatches(full_path);
EXPECT_EQ(1u, result.size());
// More-than-full path match.
result = index.FindFileMatches("/a" + full_path);
EXPECT_EQ(0u, result.size());
}
TEST(Index, FindFilePrefixes) {
TestSymbolModule setup(TestSymbolModule::kCheckedIn);
ASSERT_TRUE(setup.Init("", false).ok());
Index index;
index.CreateIndex(setup.symbols()->binary()->GetLLVMObjectFile());
// Should find both files. Order not guaranteed.
std::vector<std::string> result = index.FindFilePrefixes("z");
ASSERT_EQ(2u, result.size());
EXPECT_NE(result.end(), std::find(result.begin(), result.end(), "zxdb_symbol_test.cc"));
EXPECT_NE(result.end(), std::find(result.begin(), result.end(), "zxdb_symbol_test2.cc"));
}
// Enable and substitute a path on your system to dump the index for a DWARF file.
#if 0
TEST(Index, DumpIndex) {
TestSymbolModule setup("chrome", "test");
ASSERT_TRUE(setup.Init(false).ok());
Index index;
index.CreateIndex(setup.symbols()->binary()->GetLLVMObjectFile());
std::cout << index.main_functions().size() << " main function(s) found.\n\n";
std::cout << "Symbol index dump:\n";
index.root().Dump(std::cout, module->symbol_factory(), 1);
std::cout << "File index dump:\n";
index.DumpFileIndex(std::cout);
}
#endif
// Enable and substitute a path on your system for kFilename to run the
// indexing benchmark.
#if 0
static int64_t GetTickMicroseconds() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
constexpr int64_t kMicrosecondsPerSecond = 1000000;
constexpr int64_t kNanosecondsPerMicrosecond = 1000;
int64_t result = ts.tv_sec * kMicrosecondsPerSecond;
result += (ts.tv_nsec / kNanosecondsPerMicrosecond);
return result;
}
TEST(Index, BenchmarkIndexing) {
const char kFilename[] = "chrome";
int64_t begin_us = GetTickMicroseconds();
TestSymbolModule setup(kFileName, "");
ASSERT_TRUE(setup.Init(false).ok());
int64_t load_complete_us = GetTickMicroseconds();
Index index;
index.CreateIndex(setup.symbols()->binary()->GetLLVMObjectFile());
int64_t index_complete_us = GetTickMicroseconds();
printf("\nIndexing results for %s:\n Load: %" PRId64
" µs\n Index: %" PRId64 " µs\n\n",
kFilename, load_complete_us - begin_us,
index_complete_us - load_complete_us);
sleep(10);
}
#endif // End indexing benchmark.
} // namespace zxdb