blob: be037e7378873bdcf7535e92844eb63b05be7d7e [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "WasmModule.h"
#include "WasmVendorPlugins.h"
#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h"
#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
#include "Plugins/SymbolVendor/wasm/SymbolVendorWasm.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/lldb-types.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include <algorithm>
#include <utility>
#include <vector>
namespace symbols_backend {
namespace {
using LocationRange = std::pair<lldb::addr_t, lldb::addr_t>;
symbols_backend::PluginRegistryContext<symbols_backend::WasmPlatform,
lldb_private::FileSystem,
lldb_private::CPlusPlusLanguage,
lldb_private::TypeSystemClang,
lldb_private::wasm::ObjectFileWasm,
lldb_private::wasm::SymbolVendorWasm,
symbols_backend::WasmProcess,
symbols_backend::SymbolFileWasmDWARF>
c;
template <typename ContainerT>
auto MakeRange(const ContainerT& c)
-> llvm::iterator_range<decltype(c.begin())> {
return c;
}
} // namespace
TEST(WasmModuleTest, AddScript) {
llvm::Expected<std::unique_ptr<WasmModule>> module =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/hello.s.wasm");
ASSERT_TRUE(!!module);
EXPECT_TRUE((*module)->Valid());
}
TEST(WasmModuleTest, SourceScripts) {
auto module =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/hello.s.wasm");
ASSERT_TRUE(!!module);
EXPECT_EQ((*module)->GetSourceScripts().sources.size(), 2u);
}
TEST(WasmModuleTest, HelloAddScript) {
auto module =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/hello.s.wasm");
ASSERT_TRUE(!!module);
EXPECT_TRUE((*module)->Valid());
auto scripts = (*module)->GetSourceScripts();
llvm::SmallVector<llvm::StringRef, 2> filenames;
EXPECT_EQ(scripts.sources.size(), 2u);
EXPECT_EQ(scripts.dwos.size(), 0u);
for (auto& s : scripts.sources) {
filenames.push_back(s);
}
EXPECT_THAT(filenames, testing::UnorderedElementsAre("hello.c", "printf.h"));
}
TEST(WasmModuleTest, HelloSourceToRawLocation) {
auto module =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/hello.s.wasm");
ASSERT_TRUE(!!module);
SourceLocation source_location("hello.c", 3, 0);
auto raw_locations = (*module)->GetOffsetFromSourceLocation(source_location);
EXPECT_THAT(raw_locations, testing::ElementsAre(std::make_pair(2, 3)));
}
TEST(WasmModuleTest, HelloRawToSourceLocation) {
auto module =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/hello.s.wasm");
ASSERT_TRUE(!!module);
auto loc = (*module)->GetSourceLocationFromOffset(2, 0);
EXPECT_EQ(loc.size(), 1u);
if (loc.empty()) {
return;
}
const SourceLocation& front_location = *loc.begin();
EXPECT_EQ(front_location.file, "hello.c");
EXPECT_EQ(front_location.column, 0u);
EXPECT_EQ(front_location.line, 3u);
}
TEST(WasmModuleTest, RegressCrbug1153147) {
auto module = WasmModule::CreateFromFile(
"cxx_debugging/tests/inputs/windows_paths.s.wasm");
ASSERT_TRUE(!!module);
SourceLocation source_location("c:\\src\\temp.c", 5, 5);
auto raw_locations = (*module)->GetOffsetFromSourceLocation(source_location);
EXPECT_THAT(raw_locations, testing::ElementsAre(std::make_pair(2, 5)));
}
TEST(WasmModuleTest, InlineSourceToRawLocation) {
auto module =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/inline.s.wasm");
ASSERT_TRUE(!!module);
SourceLocation source_location("inline.c", 10, 0);
auto raw_locations = (*module)->GetOffsetFromSourceLocation(source_location);
EXPECT_THAT(raw_locations,
testing::ElementsAre(std::make_pair(lldb::addr_t(0x5), 1)));
}
TEST(WasmModuleTest, InlineRawToSourceLocation) {
auto m =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/inline.s.wasm");
ASSERT_TRUE(!!m);
{
auto loc = (*m)->GetSourceLocationFromOffset(0x2, 0);
EXPECT_EQ(loc.size(), 1u);
if (loc.empty()) {
return;
}
const SourceLocation& front_location = *loc.begin();
EXPECT_EQ(front_location.file, "inline.c");
EXPECT_EQ(front_location.column, 0u);
EXPECT_EQ(front_location.line, 1u);
}
{
auto loc = (*m)->GetSourceLocationFromOffset(0x5, 0);
EXPECT_EQ(loc.size(), 1u);
if (loc.empty()) {
return;
}
const SourceLocation& front_location = *loc.begin();
EXPECT_EQ(front_location.file, "inline.c");
EXPECT_EQ(front_location.column, 0u);
EXPECT_EQ(front_location.line, 10u);
}
{
auto loc = (*m)->GetSourceLocationFromOffset(0x6, 0);
EXPECT_EQ(loc.size(), 1u);
if (loc.empty()) {
return;
}
const SourceLocation& front_location = *loc.begin();
EXPECT_EQ(front_location.file, "inline.c");
EXPECT_EQ(front_location.column, 0u);
EXPECT_EQ(front_location.line, 2u);
}
}
TEST(WasmModuleTest, InlineFunctionInfo) {
auto m =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/inline.s.wasm");
ASSERT_TRUE(!!m);
{
auto function_names = (*m)->GetFunctionInfo(0x2).names;
EXPECT_THAT(function_names, testing::ElementsAre("caller"));
}
{
auto function_names = (*m)->GetFunctionInfo(0x5).names;
EXPECT_THAT(function_names, testing::ElementsAre("callee", "caller"));
}
}
TEST(WasmModuleTest, InlineFunctionRanges) {
auto m =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/inline.s.wasm");
ASSERT_TRUE(!!m);
{
llvm::SmallVector<LocationRange, 1> function_ranges(
MakeRange((*m)->GetInlineFunctionAddressRanges(0x5)));
EXPECT_THAT(function_ranges.size(), 1u);
EXPECT_THAT(function_ranges, testing::ElementsAre(std::make_pair(0x5, 1)));
}
}
TEST(WasmModuleTest, ChildInlineFunctionRanges) {
auto m =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/inline.s.wasm");
ASSERT_TRUE(!!m);
llvm::SmallVector<LocationRange, 1> function_ranges(
MakeRange((*m)->GetChildInlineFunctionAddressRanges(0x2)));
EXPECT_THAT(function_ranges.size(), 1u);
EXPECT_THAT(function_ranges, testing::ElementsAre(std::make_pair(0x5, 1)));
}
TEST(WasmModuleTest, AddScriptMissingScript) {
auto m = WasmModule::CreateFromFile("@InvalidPath");
EXPECT_FALSE(m);
llvm::consumeError(m.takeError());
}
TEST(WasmModuleTest, GlobalVariable) {
auto module =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/globals.s.wasm");
ASSERT_TRUE(!!module);
auto variables = (*module)->GetVariablesInScope(0x3, 0);
std::vector<llvm::StringRef> names;
names.reserve(variables.size());
for (auto& v : variables) {
names.push_back(v.name);
}
llvm::sort(names);
EXPECT_THAT(names, testing::UnorderedElementsAreArray({
"i",
"S<int>::buffer",
"::i_ptr",
"::v_ptr",
"::var_bool",
"::var_char",
"::var_double",
"::var_float",
"::var_int",
"::var_int16_t",
"::var_int32_t",
"::var_int64_t",
"::var_int8_t",
"::var_long",
"::var_long_int",
"::var_long_long",
"::var_long_long_int",
"::var_short",
"::var_short_int",
"::var_signed",
"::var_signed_char",
"::var_signed_int",
"::var_signed_long",
"::var_signed_long_int",
"::var_signed_long_long",
"::var_signed_long_long_int",
"::var_signed_short",
"::var_signed_short_int",
"::var_uint16_t",
"::var_uint32_t",
"::var_uint64_t",
"::var_uint8_t",
"::var_unsigned",
"::var_unsigned_char",
"::var_unsigned_int",
"::var_unsigned_long",
"::var_unsigned_long_int",
"::var_unsigned_long_long",
"::var_unsigned_long_long_int",
"::var_unsigned_short",
"::var_unsigned_short_int",
"::var_int128",
"::var_uint128",
}));
}
TEST(WasmModuleTest, ClassStaticVariable) {
auto module = WasmModule::CreateFromFile(
"cxx_debugging/tests/inputs/classstatic.s.wasm");
ASSERT_TRUE(!!module);
auto variables = (*module)->GetVariablesInScope(0x3, 0);
llvm::SmallVector<llvm::StringRef, 1> names;
for (auto& v : variables) {
names.push_back(v.name);
}
EXPECT_THAT(names, testing::UnorderedElementsAre("MyClass::I"));
}
TEST(WasmModuleTest, NamespacedGlobalVariables) {
auto m = WasmModule::CreateFromFile(
"cxx_debugging/tests/inputs/namespaces.s.wasm");
ASSERT_TRUE(!!m);
auto variables = (*m)->GetVariablesInScope(0x03, 0);
llvm::SmallVector<llvm::StringRef, 5> names;
for (auto& v : variables) {
names.push_back(v.name);
}
EXPECT_THAT(names, testing::UnorderedElementsAre("L", "n1::n2::I", "n1::I",
"n1::MyClass::I",
"(anonymous namespace)::K"));
}
TEST(WasmModuleTest, InlineLocalVariable) {
auto m =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/inline.s.wasm");
ASSERT_TRUE(!!m);
{
auto variables = (*m)->GetVariablesInScope(0x2, 0);
llvm::SmallVector<llvm::StringRef, 2> names;
for (auto& v : variables) {
names.push_back(v.name);
}
EXPECT_THAT(names, testing::UnorderedElementsAre("outer_var"));
}
{
auto variables = (*m)->GetVariablesInScope(0x5, 0);
llvm::SmallVector<llvm::StringRef, 2> names;
for (auto& v : variables) {
names.push_back(v.name);
}
EXPECT_THAT(names,
testing::UnorderedElementsAre("inner_var", "inner_param"));
}
}
TEST(WasmModuleTest, ShadowingVariables) {
auto module =
WasmModule::CreateFromFile("cxx_debugging/tests/inputs/shadowing.s.wasm");
ASSERT_TRUE(!!module);
auto variables = (*module)->GetVariablesInScope(0x04, 0);
ASSERT_EQ(variables.size(), 1);
auto var = variables.begin();
ASSERT_EQ(var->name, "a");
ASSERT_EQ(var->scope, lldb::eValueTypeVariableLocal);
variables = (*module)->GetVariablesInScope(0x02, 0);
ASSERT_EQ(variables.size(), 1);
var = variables.begin();
ASSERT_EQ(var->name, "a");
ASSERT_EQ(var->scope, lldb::eValueTypeVariableArgument);
}
} // namespace symbols_backend