diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:26:05 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:26:05 +0000 |
commit | 14f1b3e8826ce43b978db93a62d1166055db5394 (patch) | |
tree | 0a00ad8d3498783fe0193f3b656bca17c4c8697d /unittests | |
parent | 4ee8c119c71a06dcad1e0fecc8c675e480e59337 (diff) | |
download | src-14f1b3e8826ce43b978db93a62d1166055db5394.tar.gz src-14f1b3e8826ce43b978db93a62d1166055db5394.zip |
Vendor import of lldb trunk r290819:vendor/lldb/lldb-trunk-r290819
Notes
Notes:
svn path=/vendor/lldb/dist/; revision=311128
svn path=/vendor/lldb/lldb-trunk-r290819/; revision=311129; tag=vendor/lldb/lldb-trunk-r290819
Diffstat (limited to 'unittests')
69 files changed, 8835 insertions, 3022 deletions
diff --git a/unittests/Breakpoint/BreakpointIDTest.cpp b/unittests/Breakpoint/BreakpointIDTest.cpp new file mode 100644 index 000000000000..a449d40a335a --- /dev/null +++ b/unittests/Breakpoint/BreakpointIDTest.cpp @@ -0,0 +1,30 @@ +//===-- BreakpointIDTest.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Breakpoint/BreakpointID.h" +#include "lldb/Core/Error.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +TEST(BreakpointIDTest, StringIsBreakpointName) { + Error E; + EXPECT_FALSE(BreakpointID::StringIsBreakpointName("1breakpoint", E)); + EXPECT_FALSE(BreakpointID::StringIsBreakpointName("-", E)); + EXPECT_FALSE(BreakpointID::StringIsBreakpointName("", E)); + EXPECT_FALSE(BreakpointID::StringIsBreakpointName("3.4", E)); + + EXPECT_TRUE(BreakpointID::StringIsBreakpointName("_", E)); + EXPECT_TRUE(BreakpointID::StringIsBreakpointName("a123", E)); + EXPECT_TRUE(BreakpointID::StringIsBreakpointName("test", E)); +} diff --git a/unittests/Breakpoint/CMakeLists.txt b/unittests/Breakpoint/CMakeLists.txt new file mode 100644 index 000000000000..ca1c282aaa66 --- /dev/null +++ b/unittests/Breakpoint/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_unittest(LLDBBreakpointTests + BreakpointIDTest.cpp + ) diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index bdcb51675a0f..a865dbe244f8 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -12,6 +12,14 @@ endif () include(${LLDB_PROJECT_ROOT}/cmake/LLDBDependencies.cmake) +if (LLDB_BUILT_STANDALONE) + # Build the gtest library needed for unittests, if we have LLVM sources + # handy. + if (EXISTS ${LLVM_MAIN_SRC_DIR}/utils/unittest AND NOT TARGET gtest) + add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/unittest utils/unittest) + endif() +endif() + function(add_lldb_unittest test_name) add_unittest(LLDBUnitTests ${test_name} @@ -38,12 +46,17 @@ function(add_unittest_inputs test_name inputs) endforeach() endfunction() +add_subdirectory(Breakpoint) add_subdirectory(Core) add_subdirectory(Editline) add_subdirectory(Expression) add_subdirectory(Host) add_subdirectory(Interpreter) +add_subdirectory(Language) +add_subdirectory(Platform) +add_subdirectory(Process) add_subdirectory(ScriptInterpreter) add_subdirectory(Symbol) add_subdirectory(SymbolFile) +add_subdirectory(UnwindAssembly) add_subdirectory(Utility) diff --git a/unittests/Core/ArchSpecTest.cpp b/unittests/Core/ArchSpecTest.cpp new file mode 100644 index 000000000000..32b363652f75 --- /dev/null +++ b/unittests/Core/ArchSpecTest.cpp @@ -0,0 +1,136 @@ +//===-- ArchSpecTest.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Core/ArchSpec.h" + +#include "llvm/Support/MachO.h" + +using namespace lldb; +using namespace lldb_private; + +TEST(ArchSpecTest, TestParseMachCPUDashSubtypeTripleSimple) { + + // Success conditions. Valid cpu/subtype combinations using both - and . + ArchSpec AS; + EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12-10", AS)); + EXPECT_EQ(12u, AS.GetMachOCPUType()); + EXPECT_EQ(10u, AS.GetMachOCPUSubType()); + + AS = ArchSpec(); + EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12-15", AS)); + EXPECT_EQ(12u, AS.GetMachOCPUType()); + EXPECT_EQ(15u, AS.GetMachOCPUSubType()); + + AS = ArchSpec(); + EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12.15", AS)); + EXPECT_EQ(12u, AS.GetMachOCPUType()); + EXPECT_EQ(15u, AS.GetMachOCPUSubType()); + + // Failure conditions. + + // Valid string, unknown cpu/subtype. + AS = ArchSpec(); + EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("13.11", AS)); + EXPECT_EQ(0u, AS.GetMachOCPUType()); + EXPECT_EQ(0u, AS.GetMachOCPUSubType()); + + // Missing / invalid cpu or subtype + AS = ArchSpec(); + EXPECT_FALSE(ParseMachCPUDashSubtypeTriple("13", AS)); + + AS = ArchSpec(); + EXPECT_FALSE(ParseMachCPUDashSubtypeTriple("13.A", AS)); + + AS = ArchSpec(); + EXPECT_FALSE(ParseMachCPUDashSubtypeTriple("A.13", AS)); + + // Empty string. + AS = ArchSpec(); + EXPECT_FALSE(ParseMachCPUDashSubtypeTriple("", AS)); +} + +TEST(ArchSpecTest, TestParseMachCPUDashSubtypeTripleExtra) { + ArchSpec AS; + EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12-15-vendor-os", AS)); + EXPECT_EQ(12u, AS.GetMachOCPUType()); + EXPECT_EQ(15u, AS.GetMachOCPUSubType()); + EXPECT_EQ("vendor", AS.GetTriple().getVendorName()); + EXPECT_EQ("os", AS.GetTriple().getOSName()); + + AS = ArchSpec(); + EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12-10-vendor-os-name", AS)); + EXPECT_EQ(12u, AS.GetMachOCPUType()); + EXPECT_EQ(10u, AS.GetMachOCPUSubType()); + EXPECT_EQ("vendor", AS.GetTriple().getVendorName()); + EXPECT_EQ("os", AS.GetTriple().getOSName()); + + AS = ArchSpec(); + EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12-15-vendor.os-name", AS)); + EXPECT_EQ(12u, AS.GetMachOCPUType()); + EXPECT_EQ(15u, AS.GetMachOCPUSubType()); + EXPECT_EQ("vendor.os", AS.GetTriple().getVendorName()); + EXPECT_EQ("name", AS.GetTriple().getOSName()); + + // These there should parse correctly, but the vendor / OS should be defaulted + // since they are unrecognized. + AS = ArchSpec(); + EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12-10-vendor", AS)); + EXPECT_EQ(12u, AS.GetMachOCPUType()); + EXPECT_EQ(10u, AS.GetMachOCPUSubType()); + EXPECT_EQ("apple", AS.GetTriple().getVendorName()); + EXPECT_EQ("", AS.GetTriple().getOSName()); + + AS = ArchSpec(); + EXPECT_FALSE(ParseMachCPUDashSubtypeTriple("12.10.10", AS)); + + AS = ArchSpec(); + EXPECT_FALSE(ParseMachCPUDashSubtypeTriple("12-10.10", AS)); +} + +TEST(ArchSpecTest, TestSetTriple) { + ArchSpec AS; + + // Various flavors of valid triples. + EXPECT_TRUE(AS.SetTriple("12-10-apple-darwin")); + EXPECT_EQ(uint32_t(llvm::MachO::CPU_TYPE_ARM), AS.GetMachOCPUType()); + EXPECT_EQ(10u, AS.GetMachOCPUSubType()); + EXPECT_TRUE(llvm::StringRef(AS.GetTriple().str()) + .consume_front("armv7f-apple-darwin")); + EXPECT_EQ(ArchSpec::eCore_arm_armv7f, AS.GetCore()); + + AS = ArchSpec(); + EXPECT_TRUE(AS.SetTriple("18.100-apple-darwin")); + EXPECT_EQ(uint32_t(llvm::MachO::CPU_TYPE_POWERPC), AS.GetMachOCPUType()); + EXPECT_EQ(100u, AS.GetMachOCPUSubType()); + EXPECT_TRUE(llvm::StringRef(AS.GetTriple().str()) + .consume_front("powerpc-apple-darwin")); + EXPECT_EQ(ArchSpec::eCore_ppc_ppc970, AS.GetCore()); + + AS = ArchSpec(); + EXPECT_TRUE(AS.SetTriple("i686-pc-windows")); + EXPECT_EQ(llvm::Triple::x86, AS.GetTriple().getArch()); + EXPECT_EQ(llvm::Triple::PC, AS.GetTriple().getVendor()); + EXPECT_EQ(llvm::Triple::Win32, AS.GetTriple().getOS()); + EXPECT_TRUE( + llvm::StringRef(AS.GetTriple().str()).consume_front("i686-pc-windows")); + EXPECT_STREQ("i686", AS.GetArchitectureName()); + EXPECT_EQ(ArchSpec::eCore_x86_32_i686, AS.GetCore()); + + // Various flavors of invalid triples. + AS = ArchSpec(); + EXPECT_FALSE(AS.SetTriple("unknown-unknown-unknown")); + + AS = ArchSpec(); + EXPECT_FALSE(AS.SetTriple("unknown")); + + AS = ArchSpec(); + EXPECT_FALSE(AS.SetTriple("")); +} diff --git a/unittests/Core/BroadcasterTest.cpp b/unittests/Core/BroadcasterTest.cpp new file mode 100644 index 000000000000..e3795822f9ac --- /dev/null +++ b/unittests/Core/BroadcasterTest.cpp @@ -0,0 +1,74 @@ +//===-- BroadcasterTest.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Listener.h" +#include "lldb/Host/Predicate.h" + +#include <thread> + +using namespace lldb; +using namespace lldb_private; + +TEST(BroadcasterTest, BroadcastEvent) { + EventSP event_sp; + Broadcaster broadcaster(nullptr, "test-broadcaster"); + std::chrono::seconds timeout(0); + + // Create a listener, sign it up, make sure it recieves an event. + ListenerSP listener1_sp = Listener::MakeListener("test-listener1"); + const uint32_t event_mask1 = 1; + EXPECT_EQ(event_mask1, + listener1_sp->StartListeningForEvents(&broadcaster, event_mask1)); + broadcaster.BroadcastEvent(event_mask1, nullptr); + EXPECT_TRUE(listener1_sp->GetEvent(event_sp, timeout)); + EXPECT_EQ(event_mask1, event_sp->GetType()); + + { + // Add one more listener, make sure it works as well. + ListenerSP listener2_sp = Listener::MakeListener("test-listener2"); + const uint32_t event_mask2 = 1; + EXPECT_EQ(event_mask2, listener2_sp->StartListeningForEvents( + &broadcaster, event_mask1 | event_mask2)); + broadcaster.BroadcastEvent(event_mask2, nullptr); + EXPECT_TRUE(listener2_sp->GetEvent(event_sp, timeout)); + EXPECT_EQ(event_mask2, event_sp->GetType()); + + // Both listeners should get this event. + broadcaster.BroadcastEvent(event_mask1, nullptr); + EXPECT_TRUE(listener1_sp->GetEvent(event_sp, timeout)); + EXPECT_EQ(event_mask1, event_sp->GetType()); + EXPECT_TRUE(listener2_sp->GetEvent(event_sp, timeout)); + EXPECT_EQ(event_mask2, event_sp->GetType()); + } + + // Now again only one listener should be active. + broadcaster.BroadcastEvent(event_mask1, nullptr); + EXPECT_TRUE(listener1_sp->GetEvent(event_sp, timeout)); + EXPECT_EQ(event_mask1, event_sp->GetType()); +} + +TEST(BroadcasterTest, EventTypeHasListeners) { + EventSP event_sp; + Broadcaster broadcaster(nullptr, "test-broadcaster"); + + const uint32_t event_mask = 1; + EXPECT_FALSE(broadcaster.EventTypeHasListeners(event_mask)); + + { + ListenerSP listener_sp = Listener::MakeListener("test-listener"); + EXPECT_EQ(event_mask, + listener_sp->StartListeningForEvents(&broadcaster, event_mask)); + EXPECT_TRUE(broadcaster.EventTypeHasListeners(event_mask)); + } + + EXPECT_FALSE(broadcaster.EventTypeHasListeners(event_mask)); +} diff --git a/unittests/Core/CMakeLists.txt b/unittests/Core/CMakeLists.txt index ad9def181de5..da880010e4be 100644 --- a/unittests/Core/CMakeLists.txt +++ b/unittests/Core/CMakeLists.txt @@ -1,4 +1,9 @@ add_lldb_unittest(LLDBCoreTests + ArchSpecTest.cpp + BroadcasterTest.cpp DataExtractorTest.cpp + ListenerTest.cpp ScalarTest.cpp + StructuredDataTest.cpp + TimerTest.cpp ) diff --git a/unittests/Core/DataExtractorTest.cpp b/unittests/Core/DataExtractorTest.cpp index f22883875055..d474e21dc0c3 100644 --- a/unittests/Core/DataExtractorTest.cpp +++ b/unittests/Core/DataExtractorTest.cpp @@ -8,7 +8,8 @@ //===----------------------------------------------------------------------===// #if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) -// Workaround for MSVC standard library bug, which fails to include <thread> when +// Workaround for MSVC standard library bug, which fails to include <thread> +// when // exceptions are disabled. #include <eh.h> #endif @@ -19,21 +20,39 @@ using namespace lldb_private; -TEST(DataExtractorTest, GetBitfield) -{ - char buffer[] = { 0x01, 0x23, 0x45, 0x67 }; - DataExtractor LE(buffer, sizeof(buffer), lldb::eByteOrderLittle, sizeof(void *)); - DataExtractor BE(buffer, sizeof(buffer), lldb::eByteOrderBig, sizeof(void *)); +TEST(DataExtractorTest, GetBitfield) { + uint8_t buffer[] = {0x01, 0x23, 0x45, 0x67}; + DataExtractor LE(buffer, sizeof(buffer), lldb::eByteOrderLittle, + sizeof(void *)); + DataExtractor BE(buffer, sizeof(buffer), lldb::eByteOrderBig, sizeof(void *)); + + lldb::offset_t offset; + + offset = 0; + ASSERT_EQ(buffer[1], LE.GetMaxU64Bitfield(&offset, sizeof(buffer), 8, 8)); + offset = 0; + ASSERT_EQ(buffer[1], BE.GetMaxU64Bitfield(&offset, sizeof(buffer), 8, 8)); + + offset = 0; + ASSERT_EQ(int8_t(buffer[1]), + LE.GetMaxS64Bitfield(&offset, sizeof(buffer), 8, 8)); + offset = 0; + ASSERT_EQ(int8_t(buffer[1]), + BE.GetMaxS64Bitfield(&offset, sizeof(buffer), 8, 8)); +} + +TEST(DataExtractorTest, PeekData) { + uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04}; + DataExtractor E(buffer, sizeof buffer, lldb::eByteOrderLittle, 4); - lldb::offset_t offset; + EXPECT_EQ(buffer + 0, E.PeekData(0, 0)); + EXPECT_EQ(buffer + 0, E.PeekData(0, 4)); + EXPECT_EQ(nullptr, E.PeekData(0, 5)); - offset = 0; - ASSERT_EQ(buffer[1], LE.GetMaxU64Bitfield(&offset, sizeof(buffer), 8, 8)); - offset = 0; - ASSERT_EQ(buffer[1], BE.GetMaxU64Bitfield(&offset, sizeof(buffer), 8, 8)); + EXPECT_EQ(buffer + 2, E.PeekData(2, 0)); + EXPECT_EQ(buffer + 2, E.PeekData(2, 2)); + EXPECT_EQ(nullptr, E.PeekData(2, 3)); - offset = 0; - ASSERT_EQ(buffer[1], LE.GetMaxS64Bitfield(&offset, sizeof(buffer), 8, 8)); - offset = 0; - ASSERT_EQ(buffer[1], BE.GetMaxS64Bitfield(&offset, sizeof(buffer), 8, 8)); + EXPECT_EQ(buffer + 4, E.PeekData(4, 0)); + EXPECT_EQ(nullptr, E.PeekData(4, 1)); } diff --git a/unittests/Core/ListenerTest.cpp b/unittests/Core/ListenerTest.cpp new file mode 100644 index 000000000000..7c5a52e3dc4b --- /dev/null +++ b/unittests/Core/ListenerTest.cpp @@ -0,0 +1,114 @@ +//===-- ListenerTest.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Listener.h" +#include <future> +#include <thread> + +using namespace lldb; +using namespace lldb_private; + +TEST(ListenerTest, GetEventImmediate) { + EventSP event_sp; + Broadcaster broadcaster(nullptr, "test-broadcaster"); + + // Create a listener, sign it up, make sure it recieves an event. + ListenerSP listener_sp = Listener::MakeListener("test-listener"); + const uint32_t event_mask = 1; + ASSERT_EQ(event_mask, + listener_sp->StartListeningForEvents(&broadcaster, event_mask)); + + const std::chrono::seconds timeout(0); + // Without any events sent, these should return false. + EXPECT_FALSE(listener_sp->GetEvent(event_sp, timeout)); + EXPECT_FALSE(listener_sp->GetEventForBroadcaster(nullptr, event_sp, timeout)); + EXPECT_FALSE( + listener_sp->GetEventForBroadcaster(&broadcaster, event_sp, timeout)); + EXPECT_FALSE(listener_sp->GetEventForBroadcasterWithType( + &broadcaster, event_mask, event_sp, timeout)); + + // Now send events and make sure they get it. + broadcaster.BroadcastEvent(event_mask, nullptr); + EXPECT_TRUE(listener_sp->GetEvent(event_sp, timeout)); + + broadcaster.BroadcastEvent(event_mask, nullptr); + EXPECT_TRUE(listener_sp->GetEventForBroadcaster(nullptr, event_sp, timeout)); + + broadcaster.BroadcastEvent(event_mask, nullptr); + EXPECT_TRUE( + listener_sp->GetEventForBroadcaster(&broadcaster, event_sp, timeout)); + + broadcaster.BroadcastEvent(event_mask, nullptr); + EXPECT_FALSE(listener_sp->GetEventForBroadcasterWithType( + &broadcaster, event_mask * 2, event_sp, timeout)); + EXPECT_TRUE(listener_sp->GetEventForBroadcasterWithType( + &broadcaster, event_mask, event_sp, timeout)); +} + +TEST(ListenerTest, GetEventWait) { + EventSP event_sp; + Broadcaster broadcaster(nullptr, "test-broadcaster"); + + // Create a listener, sign it up, make sure it recieves an event. + ListenerSP listener_sp = Listener::MakeListener("test-listener"); + const uint32_t event_mask = 1; + ASSERT_EQ(event_mask, + listener_sp->StartListeningForEvents(&broadcaster, event_mask)); + + // Without any events sent, these should make a short wait and return false. + std::chrono::microseconds timeout(10); + EXPECT_FALSE(listener_sp->GetEvent(event_sp, timeout)); + EXPECT_FALSE(listener_sp->GetEventForBroadcaster(nullptr, event_sp, timeout)); + EXPECT_FALSE( + listener_sp->GetEventForBroadcaster(&broadcaster, event_sp, timeout)); + EXPECT_FALSE(listener_sp->GetEventForBroadcasterWithType( + &broadcaster, event_mask, event_sp, timeout)); + + // Now send events and make sure they get it. + broadcaster.BroadcastEvent(event_mask, nullptr); + EXPECT_TRUE(listener_sp->GetEvent(event_sp, timeout)); + + broadcaster.BroadcastEvent(event_mask, nullptr); + EXPECT_TRUE(listener_sp->GetEventForBroadcaster(nullptr, event_sp, timeout)); + + broadcaster.BroadcastEvent(event_mask, nullptr); + EXPECT_TRUE( + listener_sp->GetEventForBroadcaster(&broadcaster, event_sp, timeout)); + + broadcaster.BroadcastEvent(event_mask, nullptr); + EXPECT_FALSE(listener_sp->GetEventForBroadcasterWithType( + &broadcaster, event_mask * 2, event_sp, timeout)); + EXPECT_TRUE(listener_sp->GetEventForBroadcasterWithType( + &broadcaster, event_mask, event_sp, timeout)); + + auto delayed_broadcast = [&] { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + broadcaster.BroadcastEvent(event_mask, nullptr); + }; + + // These should do an infinite wait at return the event our asynchronous + // broadcast sends. + std::future<void> async_broadcast = + std::async(std::launch::async, delayed_broadcast); + EXPECT_TRUE(listener_sp->GetEvent(event_sp, llvm::None)); + async_broadcast.get(); + + async_broadcast = std::async(std::launch::async, delayed_broadcast); + EXPECT_TRUE( + listener_sp->GetEventForBroadcaster(&broadcaster, event_sp, llvm::None)); + async_broadcast.get(); + + async_broadcast = std::async(std::launch::async, delayed_broadcast); + EXPECT_TRUE(listener_sp->GetEventForBroadcasterWithType( + &broadcaster, event_mask, event_sp, llvm::None)); + async_broadcast.get(); +} diff --git a/unittests/Core/ScalarTest.cpp b/unittests/Core/ScalarTest.cpp index bf85f8e9623b..b79854f97ae3 100644 --- a/unittests/Core/ScalarTest.cpp +++ b/unittests/Core/ScalarTest.cpp @@ -8,98 +8,134 @@ //===----------------------------------------------------------------------===// #if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) -// Workaround for MSVC standard library bug, which fails to include <thread> when +// Workaround for MSVC standard library bug, which fails to include <thread> +// when // exceptions are disabled. #include <eh.h> #endif #include "gtest/gtest.h" +#include "lldb/Core/DataExtractor.h" #include "lldb/Core/Error.h" #include "lldb/Core/Scalar.h" -#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamString.h" #include "lldb/Host/Endian.h" using namespace lldb_private; -TEST(ScalarTest, RightShiftOperator) -{ - int a = 0x00001000; - int b = 0xFFFFFFFF; - int c = 4; - Scalar a_scalar(a); - Scalar b_scalar(b); - Scalar c_scalar(c); - ASSERT_EQ(a >> c, a_scalar >> c_scalar); - ASSERT_EQ(b >> c, b_scalar >> c_scalar); +TEST(ScalarTest, RightShiftOperator) { + int a = 0x00001000; + int b = 0xFFFFFFFF; + int c = 4; + Scalar a_scalar(a); + Scalar b_scalar(b); + Scalar c_scalar(c); + ASSERT_EQ(a >> c, a_scalar >> c_scalar); + ASSERT_EQ(b >> c, b_scalar >> c_scalar); } -TEST(ScalarTest, GetBytes) -{ - int a = 0x01020304; - long long b = 0x0102030405060708LL; - float c = 1234567.89e42; - double d = 1234567.89e42; - char e[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; - char f[32] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }; - Scalar a_scalar(a); - Scalar b_scalar(b); - Scalar c_scalar(c); - Scalar d_scalar(d); - Scalar e_scalar; - Scalar f_scalar; - DataExtractor e_data(e, sizeof(e), endian::InlHostByteOrder(), sizeof(void *)); - Error e_error = e_scalar.SetValueFromData(e_data, lldb::eEncodingUint, sizeof(e)); - DataExtractor f_data(f, sizeof(f), endian::InlHostByteOrder(), sizeof(void *)); - Error f_error = f_scalar.SetValueFromData(f_data, lldb::eEncodingUint, sizeof(f)); - ASSERT_EQ(0, memcmp(&a, a_scalar.GetBytes(), sizeof(a))); - ASSERT_EQ(0, memcmp(&b, b_scalar.GetBytes(), sizeof(b))); - ASSERT_EQ(0, memcmp(&c, c_scalar.GetBytes(), sizeof(c))); - ASSERT_EQ(0, memcmp(&d, d_scalar.GetBytes(), sizeof(d))); - ASSERT_EQ(0, e_error.Fail()); - ASSERT_EQ(0, memcmp(e, e_scalar.GetBytes(), sizeof(e))); - ASSERT_EQ(0, f_error.Fail()); - ASSERT_EQ(0, memcmp(f, f_scalar.GetBytes(), sizeof(f))); +TEST(ScalarTest, GetBytes) { + int a = 0x01020304; + long long b = 0x0102030405060708LL; + float c = 1234567.89e42; + double d = 1234567.89e42; + char e[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + char f[32] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; + Scalar a_scalar(a); + Scalar b_scalar(b); + Scalar c_scalar(c); + Scalar d_scalar(d); + Scalar e_scalar; + Scalar f_scalar; + DataExtractor e_data(e, sizeof(e), endian::InlHostByteOrder(), + sizeof(void *)); + Error e_error = + e_scalar.SetValueFromData(e_data, lldb::eEncodingUint, sizeof(e)); + DataExtractor f_data(f, sizeof(f), endian::InlHostByteOrder(), + sizeof(void *)); + Error f_error = + f_scalar.SetValueFromData(f_data, lldb::eEncodingUint, sizeof(f)); + ASSERT_EQ(0, memcmp(&a, a_scalar.GetBytes(), sizeof(a))); + ASSERT_EQ(0, memcmp(&b, b_scalar.GetBytes(), sizeof(b))); + ASSERT_EQ(0, memcmp(&c, c_scalar.GetBytes(), sizeof(c))); + ASSERT_EQ(0, memcmp(&d, d_scalar.GetBytes(), sizeof(d))); + ASSERT_EQ(0, e_error.Fail()); + ASSERT_EQ(0, memcmp(e, e_scalar.GetBytes(), sizeof(e))); + ASSERT_EQ(0, f_error.Fail()); + ASSERT_EQ(0, memcmp(f, f_scalar.GetBytes(), sizeof(f))); } -TEST(ScalarTest, CastOperations) -{ - long long a = 0xf1f2f3f4f5f6f7f8LL; - Scalar a_scalar(a); - ASSERT_EQ((signed char)a, a_scalar.SChar()); - ASSERT_EQ((unsigned char)a, a_scalar.UChar()); - ASSERT_EQ((signed short)a, a_scalar.SShort()); - ASSERT_EQ((unsigned short)a, a_scalar.UShort()); - ASSERT_EQ((signed int)a, a_scalar.SInt()); - ASSERT_EQ((unsigned int)a, a_scalar.UInt()); - ASSERT_EQ((signed long)a, a_scalar.SLong()); - ASSERT_EQ((unsigned long)a, a_scalar.ULong()); - ASSERT_EQ((signed long long)a, a_scalar.SLongLong()); - ASSERT_EQ((unsigned long long)a, a_scalar.ULongLong()); +TEST(ScalarTest, CastOperations) { + long long a = 0xf1f2f3f4f5f6f7f8LL; + Scalar a_scalar(a); + ASSERT_EQ((signed char)a, a_scalar.SChar()); + ASSERT_EQ((unsigned char)a, a_scalar.UChar()); + ASSERT_EQ((signed short)a, a_scalar.SShort()); + ASSERT_EQ((unsigned short)a, a_scalar.UShort()); + ASSERT_EQ((signed int)a, a_scalar.SInt()); + ASSERT_EQ((unsigned int)a, a_scalar.UInt()); + ASSERT_EQ((signed long)a, a_scalar.SLong()); + ASSERT_EQ((unsigned long)a, a_scalar.ULong()); + ASSERT_EQ((signed long long)a, a_scalar.SLongLong()); + ASSERT_EQ((unsigned long long)a, a_scalar.ULongLong()); } -TEST(ScalarTest, ExtractBitfield) -{ - uint32_t len = sizeof(long long) * 8; +TEST(ScalarTest, ExtractBitfield) { + uint32_t len = sizeof(long long) * 8; + + long long a1 = 0xf1f2f3f4f5f6f7f8LL; + long long b1 = 0xff1f2f3f4f5f6f7fLL; + Scalar s_scalar(a1); + ASSERT_TRUE(s_scalar.ExtractBitfield(0, 0)); + ASSERT_EQ(0, memcmp(&a1, s_scalar.GetBytes(), sizeof(a1))); + ASSERT_TRUE(s_scalar.ExtractBitfield(len, 0)); + ASSERT_EQ(0, memcmp(&a1, s_scalar.GetBytes(), sizeof(a1))); + ASSERT_TRUE(s_scalar.ExtractBitfield(len - 4, 4)); + ASSERT_EQ(0, memcmp(&b1, s_scalar.GetBytes(), sizeof(b1))); + + unsigned long long a2 = 0xf1f2f3f4f5f6f7f8ULL; + unsigned long long b2 = 0x0f1f2f3f4f5f6f7fULL; + Scalar u_scalar(a2); + ASSERT_TRUE(u_scalar.ExtractBitfield(0, 0)); + ASSERT_EQ(0, memcmp(&a2, u_scalar.GetBytes(), sizeof(a2))); + ASSERT_TRUE(u_scalar.ExtractBitfield(len, 0)); + ASSERT_EQ(0, memcmp(&a2, u_scalar.GetBytes(), sizeof(a2))); + ASSERT_TRUE(u_scalar.ExtractBitfield(len - 4, 4)); + ASSERT_EQ(0, memcmp(&b2, u_scalar.GetBytes(), sizeof(b2))); +} + +template <typename T> static std::string ScalarGetValue(T value) { + StreamString stream; + Scalar(value).GetValue(&stream, false); + return stream.GetString(); +} + +TEST(ScalarTest, GetValue) { + EXPECT_EQ("12345", ScalarGetValue<signed short>(12345)); + EXPECT_EQ("-12345", ScalarGetValue<signed short>(-12345)); + EXPECT_EQ("12345", ScalarGetValue<unsigned short>(12345)); + EXPECT_EQ(std::to_string(std::numeric_limits<unsigned short>::max()), + ScalarGetValue(std::numeric_limits<unsigned short>::max())); + + EXPECT_EQ("12345", ScalarGetValue<signed int>(12345)); + EXPECT_EQ("-12345", ScalarGetValue<signed int>(-12345)); + EXPECT_EQ("12345", ScalarGetValue<unsigned int>(12345)); + EXPECT_EQ(std::to_string(std::numeric_limits<unsigned int>::max()), + ScalarGetValue(std::numeric_limits<unsigned int>::max())); - long long a1 = 0xf1f2f3f4f5f6f7f8LL; - long long b1 = 0xff1f2f3f4f5f6f7fLL; - Scalar s_scalar(a1); - ASSERT_TRUE(s_scalar.ExtractBitfield(0, 0)); - ASSERT_EQ(0, memcmp(&a1, s_scalar.GetBytes(), sizeof(a1))); - ASSERT_TRUE(s_scalar.ExtractBitfield(len, 0)); - ASSERT_EQ(0, memcmp(&a1, s_scalar.GetBytes(), sizeof(a1))); - ASSERT_TRUE(s_scalar.ExtractBitfield(len - 4, 4)); - ASSERT_EQ(0, memcmp(&b1, s_scalar.GetBytes(), sizeof(b1))); + EXPECT_EQ("12345678", ScalarGetValue<signed long>(12345678L)); + EXPECT_EQ("-12345678", ScalarGetValue<signed long>(-12345678L)); + EXPECT_EQ("12345678", ScalarGetValue<unsigned long>(12345678UL)); + EXPECT_EQ(std::to_string(std::numeric_limits<unsigned long>::max()), + ScalarGetValue(std::numeric_limits<unsigned long>::max())); - unsigned long long a2 = 0xf1f2f3f4f5f6f7f8ULL; - unsigned long long b2 = 0x0f1f2f3f4f5f6f7fULL; - Scalar u_scalar(a2); - ASSERT_TRUE(u_scalar.ExtractBitfield(0, 0)); - ASSERT_EQ(0, memcmp(&a2, u_scalar.GetBytes(), sizeof(a2))); - ASSERT_TRUE(u_scalar.ExtractBitfield(len, 0)); - ASSERT_EQ(0, memcmp(&a2, u_scalar.GetBytes(), sizeof(a2))); - ASSERT_TRUE(u_scalar.ExtractBitfield(len - 4, 4)); - ASSERT_EQ(0, memcmp(&b2, u_scalar.GetBytes(), sizeof(b2))); + EXPECT_EQ("1234567890123", ScalarGetValue<signed long long>(1234567890123LL)); + EXPECT_EQ("-1234567890123", + ScalarGetValue<signed long long>(-1234567890123LL)); + EXPECT_EQ("1234567890123", + ScalarGetValue<unsigned long long>(1234567890123ULL)); + EXPECT_EQ(std::to_string(std::numeric_limits<unsigned long long>::max()), + ScalarGetValue(std::numeric_limits<unsigned long long>::max())); } diff --git a/unittests/Core/StructuredDataTest.cpp b/unittests/Core/StructuredDataTest.cpp new file mode 100644 index 000000000000..8e10d571fbfa --- /dev/null +++ b/unittests/Core/StructuredDataTest.cpp @@ -0,0 +1,32 @@ +//===-- StructuredDataTest.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Core/StructuredData.h" +#include "lldb/Core/StreamString.h" + +#include "llvm/Support/MachO.h" + +using namespace lldb; +using namespace lldb_private; + +TEST(StructuredDataTest, StringDump) { + std::pair<llvm::StringRef, llvm::StringRef> TestCases[] = { + { R"(asdfg)", R"("asdfg")" }, + { R"(as"df)", R"("as\"df")" }, + { R"(as\df)", R"("as\\df")" }, + }; + for(auto P : TestCases) { + StreamString S; + const bool pretty_print = false; + StructuredData::String(P.first).Dump(S, pretty_print); + EXPECT_EQ(P.second, S.GetString()); + } +} diff --git a/unittests/Core/TimerTest.cpp b/unittests/Core/TimerTest.cpp new file mode 100644 index 000000000000..d37d281f61af --- /dev/null +++ b/unittests/Core/TimerTest.cpp @@ -0,0 +1,72 @@ +//===-- TimerTest.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) +// Workaround for MSVC standard library bug, which fails to include <thread> +// when exceptions are disabled. +#include <eh.h> +#endif + +#include "lldb/Core/Timer.h" +#include "gtest/gtest.h" + +#include "lldb/Core/StreamString.h" +#include <thread> + +using namespace lldb_private; + +TEST(TimerTest, CategoryTimes) { + Timer::ResetCategoryTimes(); + { + Timer t("CAT1", ""); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + StreamString ss; + Timer::DumpCategoryTimes(&ss); + double seconds; + ASSERT_EQ(1, sscanf(ss.GetData(), "%lf sec for CAT1", &seconds)); + EXPECT_LT(0.001, seconds); + EXPECT_GT(0.1, seconds); +} + +TEST(TimerTest, CategoryTimesNested) { + Timer::ResetCategoryTimes(); + { + Timer t1("CAT1", ""); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + Timer t2("CAT1", ""); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + StreamString ss; + Timer::DumpCategoryTimes(&ss); + double seconds; + ASSERT_EQ(1, sscanf(ss.GetData(), "%lf sec for CAT1", &seconds)); + EXPECT_LT(0.002, seconds); + EXPECT_GT(0.2, seconds); +} + +TEST(TimerTest, CategoryTimes2) { + Timer::ResetCategoryTimes(); + { + Timer t1("CAT1", ""); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + Timer t2("CAT2", ""); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + StreamString ss; + Timer::DumpCategoryTimes(&ss); + double seconds1, seconds2; + ASSERT_EQ(2, sscanf(ss.GetData(), "%lf sec for CAT1%*[\n ]%lf sec for CAT2", + &seconds1, &seconds2)) + << "String: " << ss.GetData(); + EXPECT_LT(0.01, seconds1); + EXPECT_GT(1, seconds1); + EXPECT_LT(0.001, seconds2); + EXPECT_GT(0.1, seconds2); +} diff --git a/unittests/Editline/EditlineTest.cpp b/unittests/Editline/EditlineTest.cpp index e2552ffdd3b8..55dae525ddc3 100644 --- a/unittests/Editline/EditlineTest.cpp +++ b/unittests/Editline/EditlineTest.cpp @@ -25,348 +25,289 @@ #include "lldb/Host/Pipe.h" #include "lldb/Utility/PseudoTerminal.h" -namespace -{ - const size_t TIMEOUT_MILLIS = 5000; +namespace { +const size_t TIMEOUT_MILLIS = 5000; } -class FilePointer -{ +class FilePointer { public: + FilePointer() = delete; - FilePointer () = delete; + FilePointer(const FilePointer &) = delete; - FilePointer (const FilePointer&) = delete; + FilePointer(FILE *file_p) : _file_p(file_p) {} - FilePointer (FILE *file_p) - : _file_p (file_p) - { + ~FilePointer() { + if (_file_p != nullptr) { + const int close_result = fclose(_file_p); + EXPECT_EQ(0, close_result); } + } - ~FilePointer () - { - if (_file_p != nullptr) - { - const int close_result = fclose (_file_p); - EXPECT_EQ(0, close_result); - } - } - - operator FILE* () - { - return _file_p; - } + operator FILE *() { return _file_p; } private: - - FILE *_file_p; - + FILE *_file_p; }; /** Wraps an Editline class, providing a simple way to feed input (as if from the keyboard) and receive output from Editline. */ -class EditlineAdapter -{ +class EditlineAdapter { public: + EditlineAdapter(); - EditlineAdapter (); + void CloseInput(); - void - CloseInput (); + bool IsValid() const { return _editline_sp.get() != nullptr; } - bool - IsValid () const - { - return _editline_sp.get () != nullptr; - } + lldb_private::Editline &GetEditline() { return *_editline_sp; } - lldb_private::Editline& - GetEditline () - { - return *_editline_sp; - } + bool SendLine(const std::string &line); - bool - SendLine (const std::string &line); + bool SendLines(const std::vector<std::string> &lines); - bool - SendLines (const std::vector<std::string> &lines); + bool GetLine(std::string &line, bool &interrupted, size_t timeout_millis); - bool - GetLine (std::string &line, bool &interrupted, size_t timeout_millis); + bool GetLines(lldb_private::StringList &lines, bool &interrupted, + size_t timeout_millis); - bool - GetLines (lldb_private::StringList &lines, bool &interrupted, size_t timeout_millis); - - void - ConsumeAllOutput (); + void ConsumeAllOutput(); private: + static bool IsInputComplete(lldb_private::Editline *editline, + lldb_private::StringList &lines, void *baton); - static bool - IsInputComplete ( - lldb_private::Editline * editline, - lldb_private::StringList & lines, - void * baton); - - std::unique_ptr<lldb_private::Editline> _editline_sp; + std::unique_ptr<lldb_private::Editline> _editline_sp; - lldb_utility::PseudoTerminal _pty; - int _pty_master_fd; - int _pty_slave_fd; + lldb_utility::PseudoTerminal _pty; + int _pty_master_fd; + int _pty_slave_fd; - std::unique_ptr<FilePointer> _el_slave_file; + std::unique_ptr<FilePointer> _el_slave_file; }; -EditlineAdapter::EditlineAdapter () : - _editline_sp (), - _pty (), - _pty_master_fd (-1), - _pty_slave_fd (-1), - _el_slave_file () -{ - lldb_private::Error error; - - // Open the first master pty available. - char error_string[256]; - error_string[0] = '\0'; - if (!_pty.OpenFirstAvailableMaster (O_RDWR, error_string, sizeof (error_string))) - { - fprintf(stderr, "failed to open first available master pty: '%s'\n", error_string); - return; - } - - // Grab the master fd. This is a file descriptor we will: - // (1) write to when we want to send input to editline. - // (2) read from when we want to see what editline sends back. - _pty_master_fd = _pty.GetMasterFileDescriptor(); - - // Open the corresponding slave pty. - if (!_pty.OpenSlave (O_RDWR, error_string, sizeof (error_string))) - { - fprintf(stderr, "failed to open slave pty: '%s'\n", error_string); - return; - } - _pty_slave_fd = _pty.GetSlaveFileDescriptor(); +EditlineAdapter::EditlineAdapter() + : _editline_sp(), _pty(), _pty_master_fd(-1), _pty_slave_fd(-1), + _el_slave_file() { + lldb_private::Error error; + + // Open the first master pty available. + char error_string[256]; + error_string[0] = '\0'; + if (!_pty.OpenFirstAvailableMaster(O_RDWR, error_string, + sizeof(error_string))) { + fprintf(stderr, "failed to open first available master pty: '%s'\n", + error_string); + return; + } + + // Grab the master fd. This is a file descriptor we will: + // (1) write to when we want to send input to editline. + // (2) read from when we want to see what editline sends back. + _pty_master_fd = _pty.GetMasterFileDescriptor(); + + // Open the corresponding slave pty. + if (!_pty.OpenSlave(O_RDWR, error_string, sizeof(error_string))) { + fprintf(stderr, "failed to open slave pty: '%s'\n", error_string); + return; + } + _pty_slave_fd = _pty.GetSlaveFileDescriptor(); + + _el_slave_file.reset(new FilePointer(fdopen(_pty_slave_fd, "rw"))); + EXPECT_FALSE(nullptr == *_el_slave_file); + if (*_el_slave_file == nullptr) + return; + + // Create an Editline instance. + _editline_sp.reset(new lldb_private::Editline("gtest editor", *_el_slave_file, + *_el_slave_file, + *_el_slave_file, false)); + _editline_sp->SetPrompt("> "); + + // Hookup our input complete callback. + _editline_sp->SetIsInputCompleteCallback(IsInputComplete, this); +} - _el_slave_file.reset (new FilePointer (fdopen (_pty_slave_fd, "rw"))); - EXPECT_FALSE (nullptr == *_el_slave_file); - if (*_el_slave_file == nullptr) - return; +void EditlineAdapter::CloseInput() { + if (_el_slave_file != nullptr) + _el_slave_file.reset(nullptr); +} - // Create an Editline instance. - _editline_sp.reset (new lldb_private::Editline("gtest editor", *_el_slave_file, *_el_slave_file, *_el_slave_file, false)); - _editline_sp->SetPrompt ("> "); +bool EditlineAdapter::SendLine(const std::string &line) { + // Ensure we're valid before proceeding. + if (!IsValid()) + return false; - // Hookup our input complete callback. - _editline_sp->SetIsInputCompleteCallback(IsInputComplete, this); -} + // Write the line out to the pipe connected to editline's input. + ssize_t input_bytes_written = + ::write(_pty_master_fd, line.c_str(), + line.length() * sizeof(std::string::value_type)); -void -EditlineAdapter::CloseInput () -{ - if (_el_slave_file != nullptr) - _el_slave_file.reset (nullptr); -} + const char *eoln = "\n"; + const size_t eoln_length = strlen(eoln); + input_bytes_written = + ::write(_pty_master_fd, eoln, eoln_length * sizeof(char)); -bool -EditlineAdapter::SendLine (const std::string &line) -{ - // Ensure we're valid before proceeding. - if (!IsValid ()) - return false; - - // Write the line out to the pipe connected to editline's input. - ssize_t input_bytes_written = - ::write (_pty_master_fd, - line.c_str(), - line.length() * sizeof (std::string::value_type)); - - const char *eoln = "\n"; - const size_t eoln_length = strlen(eoln); - input_bytes_written = - ::write (_pty_master_fd, - eoln, - eoln_length * sizeof (char)); - - EXPECT_NE(-1, input_bytes_written) << strerror(errno); - EXPECT_EQ (eoln_length * sizeof (char), size_t(input_bytes_written)); - return eoln_length * sizeof (char) == size_t(input_bytes_written); + EXPECT_NE(-1, input_bytes_written) << strerror(errno); + EXPECT_EQ(eoln_length * sizeof(char), size_t(input_bytes_written)); + return eoln_length * sizeof(char) == size_t(input_bytes_written); } -bool -EditlineAdapter::SendLines (const std::vector<std::string> &lines) -{ - for (auto &line : lines) - { +bool EditlineAdapter::SendLines(const std::vector<std::string> &lines) { + for (auto &line : lines) { #if EDITLINE_TEST_DUMP_OUTPUT - printf ("<stdin> sending line \"%s\"\n", line.c_str()); + printf("<stdin> sending line \"%s\"\n", line.c_str()); #endif - if (!SendLine (line)) - return false; - } - return true; + if (!SendLine(line)) + return false; + } + return true; } // We ignore the timeout for now. -bool -EditlineAdapter::GetLine (std::string &line, bool &interrupted, size_t /* timeout_millis */) -{ - // Ensure we're valid before proceeding. - if (!IsValid ()) - return false; - - _editline_sp->GetLine (line, interrupted); - return true; +bool EditlineAdapter::GetLine(std::string &line, bool &interrupted, + size_t /* timeout_millis */) { + // Ensure we're valid before proceeding. + if (!IsValid()) + return false; + + _editline_sp->GetLine(line, interrupted); + return true; } -bool -EditlineAdapter::GetLines (lldb_private::StringList &lines, bool &interrupted, size_t /* timeout_millis */) -{ - // Ensure we're valid before proceeding. - if (!IsValid ()) - return false; - - _editline_sp->GetLines (1, lines, interrupted); - return true; +bool EditlineAdapter::GetLines(lldb_private::StringList &lines, + bool &interrupted, size_t /* timeout_millis */) { + // Ensure we're valid before proceeding. + if (!IsValid()) + return false; + + _editline_sp->GetLines(1, lines, interrupted); + return true; } -bool -EditlineAdapter::IsInputComplete ( - lldb_private::Editline * editline, - lldb_private::StringList & lines, - void * baton) -{ - // We'll call ourselves complete if we've received a balanced set of braces. - int start_block_count = 0; - int brace_balance = 0; - - for (size_t i = 0; i < lines.GetSize (); ++i) - { - for (auto ch : lines[i]) - { - if (ch == '{') - { - ++start_block_count; - ++brace_balance; - } - else if (ch == '}') - --brace_balance; - } +bool EditlineAdapter::IsInputComplete(lldb_private::Editline *editline, + lldb_private::StringList &lines, + void *baton) { + // We'll call ourselves complete if we've received a balanced set of braces. + int start_block_count = 0; + int brace_balance = 0; + + for (size_t i = 0; i < lines.GetSize(); ++i) { + for (auto ch : lines[i]) { + if (ch == '{') { + ++start_block_count; + ++brace_balance; + } else if (ch == '}') + --brace_balance; } + } - return (start_block_count > 0) && (brace_balance == 0); + return (start_block_count > 0) && (brace_balance == 0); } -void -EditlineAdapter::ConsumeAllOutput () -{ - FilePointer output_file (fdopen (_pty_master_fd, "r")); +void EditlineAdapter::ConsumeAllOutput() { + FilePointer output_file(fdopen(_pty_master_fd, "r")); - int ch; - while ((ch = fgetc(output_file)) != EOF) - { + int ch; + while ((ch = fgetc(output_file)) != EOF) { #if EDITLINE_TEST_DUMP_OUTPUT - char display_str[] = { 0, 0, 0 }; - switch (ch) - { - case '\t': - display_str[0] = '\\'; - display_str[1] = 't'; - break; - case '\n': - display_str[0] = '\\'; - display_str[1] = 'n'; - break; - case '\r': - display_str[0] = '\\'; - display_str[1] = 'r'; - break; - default: - display_str[0] = ch; - break; - } - printf ("<stdout> 0x%02x (%03d) (%s)\n", ch, ch, display_str); - // putc(ch, stdout); -#endif + char display_str[] = {0, 0, 0}; + switch (ch) { + case '\t': + display_str[0] = '\\'; + display_str[1] = 't'; + break; + case '\n': + display_str[0] = '\\'; + display_str[1] = 'n'; + break; + case '\r': + display_str[0] = '\\'; + display_str[1] = 'r'; + break; + default: + display_str[0] = ch; + break; } + printf("<stdout> 0x%02x (%03d) (%s)\n", ch, ch, display_str); +// putc(ch, stdout); +#endif + } } -class EditlineTestFixture : public ::testing::Test -{ +class EditlineTestFixture : public ::testing::Test { private: - EditlineAdapter _el_adapter; - std::shared_ptr<std::thread> _sp_output_thread; + EditlineAdapter _el_adapter; + std::shared_ptr<std::thread> _sp_output_thread; public: - void SetUp() - { - // We need a TERM set properly for editline to work as expected. - setenv("TERM", "vt100", 1); - - // Validate the editline adapter. - EXPECT_TRUE(_el_adapter.IsValid()); - if (!_el_adapter.IsValid()) - return; - - // Dump output. - _sp_output_thread.reset(new std::thread([&] { _el_adapter.ConsumeAllOutput(); })); - } - - void TearDown() - { - _el_adapter.CloseInput(); - if (_sp_output_thread) - _sp_output_thread->join(); - } - - EditlineAdapter &GetEditlineAdapter() { return _el_adapter; } + void SetUp() { + // We need a TERM set properly for editline to work as expected. + setenv("TERM", "vt100", 1); + + // Validate the editline adapter. + EXPECT_TRUE(_el_adapter.IsValid()); + if (!_el_adapter.IsValid()) + return; + + // Dump output. + _sp_output_thread.reset( + new std::thread([&] { _el_adapter.ConsumeAllOutput(); })); + } + + void TearDown() { + _el_adapter.CloseInput(); + if (_sp_output_thread) + _sp_output_thread->join(); + } + + EditlineAdapter &GetEditlineAdapter() { return _el_adapter; } }; -TEST_F(EditlineTestFixture, EditlineReceivesSingleLineText) -{ - // Send it some text via our virtual keyboard. - const std::string input_text ("Hello, world"); - EXPECT_TRUE(GetEditlineAdapter().SendLine(input_text)); +TEST_F(EditlineTestFixture, EditlineReceivesSingleLineText) { + // Send it some text via our virtual keyboard. + const std::string input_text("Hello, world"); + EXPECT_TRUE(GetEditlineAdapter().SendLine(input_text)); - // Verify editline sees what we put in. - std::string el_reported_line; - bool input_interrupted = false; - const bool received_line = GetEditlineAdapter().GetLine(el_reported_line, input_interrupted, TIMEOUT_MILLIS); + // Verify editline sees what we put in. + std::string el_reported_line; + bool input_interrupted = false; + const bool received_line = GetEditlineAdapter().GetLine( + el_reported_line, input_interrupted, TIMEOUT_MILLIS); - EXPECT_TRUE (received_line); - EXPECT_FALSE (input_interrupted); - EXPECT_EQ (input_text, el_reported_line); + EXPECT_TRUE(received_line); + EXPECT_FALSE(input_interrupted); + EXPECT_EQ(input_text, el_reported_line); } -TEST_F(EditlineTestFixture, EditlineReceivesMultiLineText) -{ - // Send it some text via our virtual keyboard. - std::vector<std::string> input_lines; - input_lines.push_back ("int foo()"); - input_lines.push_back ("{"); - input_lines.push_back ("printf(\"Hello, world\");"); - input_lines.push_back ("}"); - input_lines.push_back (""); - - EXPECT_TRUE(GetEditlineAdapter().SendLines(input_lines)); - - // Verify editline sees what we put in. - lldb_private::StringList el_reported_lines; - bool input_interrupted = false; - - EXPECT_TRUE(GetEditlineAdapter().GetLines(el_reported_lines, input_interrupted, TIMEOUT_MILLIS)); - EXPECT_FALSE (input_interrupted); - - // Without any auto indentation support, our output should directly match our input. - EXPECT_EQ (input_lines.size (), el_reported_lines.GetSize ()); - if (input_lines.size () == el_reported_lines.GetSize ()) - { - for (size_t i = 0; i < input_lines.size(); ++i) - EXPECT_EQ (input_lines[i], el_reported_lines[i]); - } +TEST_F(EditlineTestFixture, EditlineReceivesMultiLineText) { + // Send it some text via our virtual keyboard. + std::vector<std::string> input_lines; + input_lines.push_back("int foo()"); + input_lines.push_back("{"); + input_lines.push_back("printf(\"Hello, world\");"); + input_lines.push_back("}"); + input_lines.push_back(""); + + EXPECT_TRUE(GetEditlineAdapter().SendLines(input_lines)); + + // Verify editline sees what we put in. + lldb_private::StringList el_reported_lines; + bool input_interrupted = false; + + EXPECT_TRUE(GetEditlineAdapter().GetLines(el_reported_lines, + input_interrupted, TIMEOUT_MILLIS)); + EXPECT_FALSE(input_interrupted); + + // Without any auto indentation support, our output should directly match our + // input. + EXPECT_EQ(input_lines.size(), el_reported_lines.GetSize()); + if (input_lines.size() == el_reported_lines.GetSize()) { + for (size_t i = 0; i < input_lines.size(); ++i) + EXPECT_EQ(input_lines[i], el_reported_lines[i]); + } } #endif diff --git a/unittests/Expression/GoParserTest.cpp b/unittests/Expression/GoParserTest.cpp index 76483979f353..de489e43ef22 100644 --- a/unittests/Expression/GoParserTest.cpp +++ b/unittests/Expression/GoParserTest.cpp @@ -1,4 +1,5 @@ -//===-- GoParserTest.cpp ------------------------------------------*- C++ -*-===// +//===-- GoParserTest.cpp ------------------------------------------*- C++ +//-*-===// // // The LLVM Compiler Infrastructure // @@ -8,7 +9,8 @@ //===----------------------------------------------------------------------===// #if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) -// Workaround for MSVC standard library bug, which fails to include <thread> when +// Workaround for MSVC standard library bug, which fails to include <thread> +// when // exceptions are disabled. #include <eh.h> #endif @@ -17,234 +19,262 @@ #include "gtest/gtest.h" -#include "lldb/Core/Error.h" #include "Plugins/ExpressionParser/Go/GoParser.h" +#include "lldb/Core/Error.h" using namespace lldb_private; -namespace -{ -struct ASTPrinter -{ - ASTPrinter(GoASTNode *n) { (*this)(n); } - - void - operator()(GoASTNode *n) - { - if (n == nullptr) - { - m_stream << "nil "; - return; - } - m_stream << "(" << n->GetKindName() << " "; - n->WalkChildren(*this); - if (auto *nn = llvm::dyn_cast<GoASTAssignStmt>(n)) - m_stream << nn->GetDefine() << " "; - if (auto *nn = llvm::dyn_cast<GoASTBasicLit>(n)) - m_stream << nn->GetValue().m_value.str() << " "; - if (auto *nn = llvm::dyn_cast<GoASTBinaryExpr>(n)) - m_stream << GoLexer::LookupToken(nn->GetOp()).str() << " "; - if (auto *nn = llvm::dyn_cast<GoASTIdent>(n)) - m_stream << nn->GetName().m_value.str() << " "; - if (auto *nn = llvm::dyn_cast<GoASTBranchStmt>(n)) - m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " "; - if (auto *nn = llvm::dyn_cast<GoASTCallExpr>(n)) - m_stream << (nn->GetEllipsis() ? "..." : "") << " "; - if (auto *nn = llvm::dyn_cast<GoASTChanType>(n)) - m_stream << nn->GetDir() << " "; - if (auto *nn = llvm::dyn_cast<GoASTGenDecl>(n)) - m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " "; - if (auto *nn = llvm::dyn_cast<GoASTIncDecStmt>(n)) - m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " "; - if (auto *nn = llvm::dyn_cast<GoASTRangeStmt>(n)) - m_stream << nn->GetDefine() << " "; - if (auto *nn = llvm::dyn_cast<GoASTSliceExpr>(n)) - m_stream << nn->GetSlice3() << " "; - if (auto *nn = llvm::dyn_cast<GoASTUnaryExpr>(n)) - m_stream << GoLexer::LookupToken(nn->GetOp()).str() << " "; - m_stream << ") "; - } +namespace { +struct ASTPrinter { + ASTPrinter(GoASTNode *n) { (*this)(n); } - const std::string - str() const - { - return m_stream.str(); + void operator()(GoASTNode *n) { + if (n == nullptr) { + m_stream << "nil "; + return; } - std::stringstream m_stream; + m_stream << "(" << n->GetKindName() << " "; + n->WalkChildren(*this); + if (auto *nn = llvm::dyn_cast<GoASTAssignStmt>(n)) + m_stream << nn->GetDefine() << " "; + if (auto *nn = llvm::dyn_cast<GoASTBasicLit>(n)) + m_stream << nn->GetValue().m_value.str() << " "; + if (auto *nn = llvm::dyn_cast<GoASTBinaryExpr>(n)) + m_stream << GoLexer::LookupToken(nn->GetOp()).str() << " "; + if (auto *nn = llvm::dyn_cast<GoASTIdent>(n)) + m_stream << nn->GetName().m_value.str() << " "; + if (auto *nn = llvm::dyn_cast<GoASTBranchStmt>(n)) + m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " "; + if (auto *nn = llvm::dyn_cast<GoASTCallExpr>(n)) + m_stream << (nn->GetEllipsis() ? "..." : "") << " "; + if (auto *nn = llvm::dyn_cast<GoASTChanType>(n)) + m_stream << nn->GetDir() << " "; + if (auto *nn = llvm::dyn_cast<GoASTGenDecl>(n)) + m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " "; + if (auto *nn = llvm::dyn_cast<GoASTIncDecStmt>(n)) + m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " "; + if (auto *nn = llvm::dyn_cast<GoASTRangeStmt>(n)) + m_stream << nn->GetDefine() << " "; + if (auto *nn = llvm::dyn_cast<GoASTSliceExpr>(n)) + m_stream << nn->GetSlice3() << " "; + if (auto *nn = llvm::dyn_cast<GoASTUnaryExpr>(n)) + m_stream << GoLexer::LookupToken(nn->GetOp()).str() << " "; + m_stream << ") "; + } + + const std::string str() const { return m_stream.str(); } + std::stringstream m_stream; }; -testing::AssertionResult -CheckStatement(const char *_s, const char *c_expr, const char *sexpr, const char *code) -{ - GoParser parser(code); - std::unique_ptr<GoASTStmt> stmt(parser.Statement()); - if (parser.Failed() || !stmt) - { - Error err; - parser.GetError(err); - return testing::AssertionFailure() << "Error parsing " << c_expr << "\n\t" << err.AsCString(); - } - std::string actual_sexpr = ASTPrinter(stmt.get()).str(); - if (actual_sexpr == sexpr) - return testing::AssertionSuccess(); - return testing::AssertionFailure() << "Parsing: " << c_expr << "\nExpected: " << sexpr - << "\nGot: " << actual_sexpr; +testing::AssertionResult CheckStatement(const char *_s, const char *c_expr, + const char *sexpr, const char *code) { + GoParser parser(code); + std::unique_ptr<GoASTStmt> stmt(parser.Statement()); + if (parser.Failed() || !stmt) { + Error err; + parser.GetError(err); + return testing::AssertionFailure() << "Error parsing " << c_expr << "\n\t" + << err.AsCString(); + } + std::string actual_sexpr = ASTPrinter(stmt.get()).str(); + if (actual_sexpr == sexpr) + return testing::AssertionSuccess(); + return testing::AssertionFailure() << "Parsing: " << c_expr + << "\nExpected: " << sexpr + << "\nGot: " << actual_sexpr; } } // namespace #define EXPECT_PARSE(s, c) EXPECT_PRED_FORMAT2(CheckStatement, s, c) -TEST(GoParserTest, ParseBasicLiterals) -{ - EXPECT_PARSE("(ExprStmt (BasicLit 0 ) ) ", "0"); - EXPECT_PARSE("(ExprStmt (BasicLit 42 ) ) ", "42"); - EXPECT_PARSE("(ExprStmt (BasicLit 0600 ) ) ", "0600"); - EXPECT_PARSE("(ExprStmt (BasicLit 0xBadFace ) ) ", "0xBadFace"); - EXPECT_PARSE("(ExprStmt (BasicLit 170141183460469231731687303715884105727 ) ) ", - "170141183460469231731687303715884105727"); - - EXPECT_PARSE("(ExprStmt (BasicLit 0. ) ) ", "0."); - EXPECT_PARSE("(ExprStmt (BasicLit 72.40 ) ) ", "72.40"); - EXPECT_PARSE("(ExprStmt (BasicLit 072.40 ) ) ", "072.40"); - EXPECT_PARSE("(ExprStmt (BasicLit 2.71828 ) ) ", "2.71828"); - EXPECT_PARSE("(ExprStmt (BasicLit 1.e+0 ) ) ", "1.e+0"); - EXPECT_PARSE("(ExprStmt (BasicLit 6.67428e-11 ) ) ", "6.67428e-11"); - EXPECT_PARSE("(ExprStmt (BasicLit 1E6 ) ) ", "1E6"); - EXPECT_PARSE("(ExprStmt (BasicLit .12345E+6 ) ) ", ".12345E+6"); - - EXPECT_PARSE("(ExprStmt (BasicLit 0i ) ) ", "0i"); - EXPECT_PARSE("(ExprStmt (BasicLit 011i ) ) ", "011i"); - EXPECT_PARSE("(ExprStmt (BasicLit 0.i ) ) ", "0.i"); - EXPECT_PARSE("(ExprStmt (BasicLit 2.71828i ) ) ", "2.71828i"); - EXPECT_PARSE("(ExprStmt (BasicLit 6.67428e-11i ) ) ", "6.67428e-11i"); - EXPECT_PARSE("(ExprStmt (BasicLit 1E6i ) ) ", "1E6i"); - EXPECT_PARSE("(ExprStmt (BasicLit .12345E+6i ) ) ", ".12345E+6i"); - - EXPECT_PARSE("(ExprStmt (BasicLit 'a' ) ) ", "'a'"); - EXPECT_PARSE("(ExprStmt (BasicLit '本' ) ) ", "'本'"); - EXPECT_PARSE("(ExprStmt (BasicLit \"abc\" ) ) ", "\"abc\""); - EXPECT_PARSE("(ExprStmt (BasicLit `abc` ) ) ", "`abc`"); - EXPECT_PARSE("(ExprStmt (BasicLit `ab\nc` ) ) ", "`ab\nc`"); +TEST(GoParserTest, ParseBasicLiterals) { + EXPECT_PARSE("(ExprStmt (BasicLit 0 ) ) ", "0"); + EXPECT_PARSE("(ExprStmt (BasicLit 42 ) ) ", "42"); + EXPECT_PARSE("(ExprStmt (BasicLit 0600 ) ) ", "0600"); + EXPECT_PARSE("(ExprStmt (BasicLit 0xBadFace ) ) ", "0xBadFace"); + EXPECT_PARSE( + "(ExprStmt (BasicLit 170141183460469231731687303715884105727 ) ) ", + "170141183460469231731687303715884105727"); + + EXPECT_PARSE("(ExprStmt (BasicLit 0. ) ) ", "0."); + EXPECT_PARSE("(ExprStmt (BasicLit 72.40 ) ) ", "72.40"); + EXPECT_PARSE("(ExprStmt (BasicLit 072.40 ) ) ", "072.40"); + EXPECT_PARSE("(ExprStmt (BasicLit 2.71828 ) ) ", "2.71828"); + EXPECT_PARSE("(ExprStmt (BasicLit 1.e+0 ) ) ", "1.e+0"); + EXPECT_PARSE("(ExprStmt (BasicLit 6.67428e-11 ) ) ", "6.67428e-11"); + EXPECT_PARSE("(ExprStmt (BasicLit 1E6 ) ) ", "1E6"); + EXPECT_PARSE("(ExprStmt (BasicLit .12345E+6 ) ) ", ".12345E+6"); + + EXPECT_PARSE("(ExprStmt (BasicLit 0i ) ) ", "0i"); + EXPECT_PARSE("(ExprStmt (BasicLit 011i ) ) ", "011i"); + EXPECT_PARSE("(ExprStmt (BasicLit 0.i ) ) ", "0.i"); + EXPECT_PARSE("(ExprStmt (BasicLit 2.71828i ) ) ", "2.71828i"); + EXPECT_PARSE("(ExprStmt (BasicLit 6.67428e-11i ) ) ", "6.67428e-11i"); + EXPECT_PARSE("(ExprStmt (BasicLit 1E6i ) ) ", "1E6i"); + EXPECT_PARSE("(ExprStmt (BasicLit .12345E+6i ) ) ", ".12345E+6i"); + + EXPECT_PARSE("(ExprStmt (BasicLit 'a' ) ) ", "'a'"); + EXPECT_PARSE("(ExprStmt (BasicLit '本' ) ) ", "'本'"); + EXPECT_PARSE("(ExprStmt (BasicLit \"abc\" ) ) ", "\"abc\""); + EXPECT_PARSE("(ExprStmt (BasicLit `abc` ) ) ", "`abc`"); + EXPECT_PARSE("(ExprStmt (BasicLit `ab\nc` ) ) ", "`ab\nc`"); } -TEST(GoParserTest, ParseOperand) -{ - EXPECT_PARSE("(ExprStmt (Ident a ) ) ", "a"); - EXPECT_PARSE("(ExprStmt (Ident _x9 ) ) ", "_x9"); - EXPECT_PARSE("(ExprStmt (Ident ThisVariableIsExported ) ) ", "ThisVariableIsExported"); - EXPECT_PARSE("(ExprStmt (Ident αβ ) ) ", "αβ"); +TEST(GoParserTest, ParseOperand) { + EXPECT_PARSE("(ExprStmt (Ident a ) ) ", "a"); + EXPECT_PARSE("(ExprStmt (Ident _x9 ) ) ", "_x9"); + EXPECT_PARSE("(ExprStmt (Ident ThisVariableIsExported ) ) ", + "ThisVariableIsExported"); + EXPECT_PARSE("(ExprStmt (Ident αβ ) ) ", "αβ"); - EXPECT_PARSE("(ExprStmt (SelectorExpr (Ident math ) (Ident Sin ) ) ) ", "math.Sin"); + EXPECT_PARSE("(ExprStmt (SelectorExpr (Ident math ) (Ident Sin ) ) ) ", + "math.Sin"); } -TEST(GoParserTest, ParseCompositeLiterals) -{ - EXPECT_PARSE("(ExprStmt (CompositeLit (Ident Point3D ) ) ) ", "Point3D{}"); - EXPECT_PARSE("(ExprStmt (CompositeLit (Ident Line ) (Ident origin ) (CompositeLit (Ident Point3D ) (KeyValueExpr " - "(Ident y ) (UnaryExpr (BasicLit 4 ) - ) ) (KeyValueExpr (Ident z ) (BasicLit 12.3 ) ) ) ) ) ", - "Line{origin, Point3D{y: -4, z: 12.3}}"); - EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 10 ) (Ident string ) ) ) ) ", "[10]string{}"); - EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 6 ) (Ident int ) ) (BasicLit 1 ) (BasicLit 2 ) " - "(BasicLit 3 ) (BasicLit 5 ) ) ) ", - "[6]int {1, 2, 3, 5}"); - EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType nil (Ident int ) ) (BasicLit 2 ) (BasicLit 3 ) (BasicLit 5 ) " - "(BasicLit 7 ) (BasicLit 9 ) (BasicLit 2147483647 ) ) ) ", - "[]int{2, 3, 5, 7, 9, 2147483647}"); - EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 128 ) (Ident bool ) ) (KeyValueExpr (BasicLit 'a' ) " - "(Ident true ) ) (KeyValueExpr (BasicLit 'e' ) (Ident true ) ) (KeyValueExpr (BasicLit 'i' ) (Ident " - "true ) ) (KeyValueExpr (BasicLit 'o' ) (Ident true ) ) (KeyValueExpr (BasicLit 'u' ) (Ident true ) ) " - "(KeyValueExpr (BasicLit 'y' ) (Ident true ) ) ) ) ", - "[128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}"); - EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 10 ) (Ident float32 ) ) (UnaryExpr (BasicLit 1 ) - ) " - "(KeyValueExpr (BasicLit 4 ) (UnaryExpr (BasicLit 0.1 ) - ) ) (UnaryExpr (BasicLit 0.1 ) - ) " - "(KeyValueExpr (BasicLit 9 ) (UnaryExpr (BasicLit 1 ) - ) ) ) ) ", - "[10]float32{-1, 4: -0.1, -0.1, 9: -1}"); +TEST(GoParserTest, ParseCompositeLiterals) { + EXPECT_PARSE("(ExprStmt (CompositeLit (Ident Point3D ) ) ) ", "Point3D{}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (Ident Line ) (Ident origin ) " + "(CompositeLit (Ident Point3D ) (KeyValueExpr " + "(Ident y ) (UnaryExpr (BasicLit 4 ) - ) ) (KeyValueExpr (Ident " + "z ) (BasicLit 12.3 ) ) ) ) ) ", + "Line{origin, Point3D{y: -4, z: 12.3}}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 10 ) (Ident " + "string ) ) ) ) ", + "[10]string{}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 6 ) (Ident int ) " + ") (BasicLit 1 ) (BasicLit 2 ) " + "(BasicLit 3 ) (BasicLit 5 ) ) ) ", + "[6]int {1, 2, 3, 5}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType nil (Ident int ) ) " + "(BasicLit 2 ) (BasicLit 3 ) (BasicLit 5 ) " + "(BasicLit 7 ) (BasicLit 9 ) (BasicLit 2147483647 ) ) ) ", + "[]int{2, 3, 5, 7, 9, 2147483647}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 128 ) (Ident bool " + ") ) (KeyValueExpr (BasicLit 'a' ) " + "(Ident true ) ) (KeyValueExpr (BasicLit 'e' ) (Ident true ) ) " + "(KeyValueExpr (BasicLit 'i' ) (Ident " + "true ) ) (KeyValueExpr (BasicLit 'o' ) (Ident true ) ) " + "(KeyValueExpr (BasicLit 'u' ) (Ident true ) ) " + "(KeyValueExpr (BasicLit 'y' ) (Ident true ) ) ) ) ", + "[128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': " + "true, 'y': true}"); + EXPECT_PARSE( + "(ExprStmt (CompositeLit (ArrayType (BasicLit 10 ) (Ident float32 ) ) " + "(UnaryExpr (BasicLit 1 ) - ) " + "(KeyValueExpr (BasicLit 4 ) (UnaryExpr (BasicLit 0.1 ) - ) ) (UnaryExpr " + "(BasicLit 0.1 ) - ) " + "(KeyValueExpr (BasicLit 9 ) (UnaryExpr (BasicLit 1 ) - ) ) ) ) ", + "[10]float32{-1, 4: -0.1, -0.1, 9: -1}"); } -TEST(GoParserTest, ParseEllipsisArray) -{ - EXPECT_PARSE( - "(ExprStmt (CompositeLit (ArrayType (Ellipsis nil ) (Ident string ) ) (BasicLit `Sat` ) (BasicLit `Sun` ) ) ) ", - "[...]string {`Sat`, `Sun`}"); - EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (Ellipsis nil ) (Ident Point ) ) (CompositeLit nil (BasicLit 1.5 " - ") (UnaryExpr (BasicLit 3.5 ) - ) ) (CompositeLit nil (BasicLit 0 ) (BasicLit 0 ) ) ) ) ", - "[...]Point{{1.5, -3.5}, {0, 0}}"); +TEST(GoParserTest, ParseEllipsisArray) { + EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (Ellipsis nil ) (Ident " + "string ) ) (BasicLit `Sat` ) (BasicLit `Sun` ) ) ) ", + "[...]string {`Sat`, `Sun`}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (Ellipsis nil ) (Ident " + "Point ) ) (CompositeLit nil (BasicLit 1.5 " + ") (UnaryExpr (BasicLit 3.5 ) - ) ) (CompositeLit nil (BasicLit " + "0 ) (BasicLit 0 ) ) ) ) ", + "[...]Point{{1.5, -3.5}, {0, 0}}"); } -TEST(GoParserTest, ParseMap) -{ - EXPECT_PARSE("(ExprStmt (CompositeLit (MapType (Ident string ) (Ident float32 ) ) (KeyValueExpr (BasicLit `C0` ) " - "(BasicLit 16.35 ) ) (KeyValueExpr (BasicLit `D0` ) (BasicLit 18.35 ) ) ) ) ", - "map[string]float32{`C0`: 16.35, `D0`: 18.35, }"); +TEST(GoParserTest, ParseMap) { + EXPECT_PARSE("(ExprStmt (CompositeLit (MapType (Ident string ) (Ident " + "float32 ) ) (KeyValueExpr (BasicLit `C0` ) " + "(BasicLit 16.35 ) ) (KeyValueExpr (BasicLit `D0` ) (BasicLit " + "18.35 ) ) ) ) ", + "map[string]float32{`C0`: 16.35, `D0`: 18.35, }"); } -TEST(GoParserTest, UnaryExpr) -{ - EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) + ) ) ", "+x"); - EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) - ) ) ", "-x"); - EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) ! ) ) ", "!x"); - EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) ^ ) ) ", "^x"); - EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) & ) ) ", "&x"); - EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) <- ) ) ", "<-x"); - EXPECT_PARSE("(ExprStmt (StarExpr (Ident x ) ) ) ", "*x"); +TEST(GoParserTest, UnaryExpr) { + EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) + ) ) ", "+x"); + EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) - ) ) ", "-x"); + EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) ! ) ) ", "!x"); + EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) ^ ) ) ", "^x"); + EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) & ) ) ", "&x"); + EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) <- ) ) ", "<-x"); + EXPECT_PARSE("(ExprStmt (StarExpr (Ident x ) ) ) ", "*x"); } -TEST(GoParserTest, BinaryExpr) -{ - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) || ) ) ", "a || b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) && ) ) ", "a && b"); - - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) == ) ) ", "a == b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) != ) ) ", "a != b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) < ) ) ", "a < b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) <= ) ) ", "a <= b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) > ) ) ", "a > b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) >= ) ) ", "a >= b"); - - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) + ) ) ", "a + b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) - ) ) ", "a - b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) | ) ) ", "a | b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) ^ ) ) ", "a ^ b"); - - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) * ) ) ", "a * b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) / ) ) ", "a / b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) % ) ) ", "a % b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) << ) ) ", "a << b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) >> ) ) ", "a >> b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) & ) ) ", "a & b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) &^ ) ) ", "a &^ b"); - - EXPECT_PARSE( - "(ExprStmt (BinaryExpr (BasicLit 23 ) (BinaryExpr (BasicLit 3 ) (IndexExpr (Ident x ) (Ident i ) ) * ) + ) ) ", - "23 + 3*x[i]"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (UnaryExpr (UnaryExpr (Ident a ) + ) + ) + ) ) ", "a + + + a"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (UnaryExpr (Ident a ) ^ ) (Ident b ) >> ) ) ", "^a >> b"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (CallExpr (Ident f ) ) (CallExpr (Ident g ) ) || ) ) ", "f() || g()"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (BinaryExpr (Ident x ) (BinaryExpr (Ident y ) (BasicLit 1 ) + ) == ) " - "(BinaryExpr (UnaryExpr (Ident chanPtr ) <- ) (BasicLit 0 ) > ) && ) ) ", - "x == y+1 && <-chanPtr > 0"); +TEST(GoParserTest, BinaryExpr) { + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) || ) ) ", "a || b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) && ) ) ", "a && b"); + + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) == ) ) ", "a == b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) != ) ) ", "a != b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) < ) ) ", "a < b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) <= ) ) ", "a <= b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) > ) ) ", "a > b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) >= ) ) ", "a >= b"); + + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) + ) ) ", "a + b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) - ) ) ", "a - b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) | ) ) ", "a | b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) ^ ) ) ", "a ^ b"); + + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) * ) ) ", "a * b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) / ) ) ", "a / b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) % ) ) ", "a % b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) << ) ) ", "a << b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) >> ) ) ", "a >> b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) & ) ) ", "a & b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) &^ ) ) ", "a &^ b"); + + EXPECT_PARSE("(ExprStmt (BinaryExpr (BasicLit 23 ) (BinaryExpr (BasicLit 3 ) " + "(IndexExpr (Ident x ) (Ident i ) ) * ) + ) ) ", + "23 + 3*x[i]"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (UnaryExpr (UnaryExpr (Ident " + "a ) + ) + ) + ) ) ", + "a + + + a"); + EXPECT_PARSE( + "(ExprStmt (BinaryExpr (UnaryExpr (Ident a ) ^ ) (Ident b ) >> ) ) ", + "^a >> b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (CallExpr (Ident f ) ) (CallExpr (Ident " + "g ) ) || ) ) ", + "f() || g()"); + EXPECT_PARSE( + "(ExprStmt (BinaryExpr (BinaryExpr (Ident x ) (BinaryExpr (Ident y ) " + "(BasicLit 1 ) + ) == ) " + "(BinaryExpr (UnaryExpr (Ident chanPtr ) <- ) (BasicLit 0 ) > ) && ) ) ", + "x == y+1 && <-chanPtr > 0"); } -TEST(GoParserTest, PrimaryExpr) -{ - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident x ) (CallExpr (Ident f ) ) <= ) ) ", "x <= f()"); - EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident s ) (BasicLit `.txt` ) + ) ) ", "(s + `.txt`)"); - EXPECT_PARSE("(ExprStmt (CallExpr (Ident f ) (BasicLit 3.1415 ) (Ident true ) ) ) ", "f(3.1415, true)"); - EXPECT_PARSE("(ExprStmt (CallExpr (Ident f ) (BasicLit 3.1415 ) (Ident a ) ... ) ) ", "f(3.1415, a...)"); - EXPECT_PARSE("(ExprStmt (IndexExpr (Ident m ) (BasicLit '1' ) ) ) ", "m['1']"); - EXPECT_PARSE("(ExprStmt (SliceExpr (Ident s ) (Ident i ) (BinaryExpr (Ident j ) (BasicLit 1 ) + ) nil 0 ) ) ", - "s[i : j + 1]"); - EXPECT_PARSE("(ExprStmt (SelectorExpr (Ident obj ) (Ident color ) ) ) ", "obj.color"); - EXPECT_PARSE("(ExprStmt (CallExpr (SelectorExpr (IndexExpr (SelectorExpr (Ident f ) (Ident p ) ) (Ident i ) ) " - "(Ident x ) ) ) ) ", - "f.p[i].x()"); +TEST(GoParserTest, PrimaryExpr) { + EXPECT_PARSE( + "(ExprStmt (BinaryExpr (Ident x ) (CallExpr (Ident f ) ) <= ) ) ", + "x <= f()"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident s ) (BasicLit `.txt` ) + ) ) ", + "(s + `.txt`)"); + EXPECT_PARSE( + "(ExprStmt (CallExpr (Ident f ) (BasicLit 3.1415 ) (Ident true ) ) ) ", + "f(3.1415, true)"); + EXPECT_PARSE( + "(ExprStmt (CallExpr (Ident f ) (BasicLit 3.1415 ) (Ident a ) ... ) ) ", + "f(3.1415, a...)"); + EXPECT_PARSE("(ExprStmt (IndexExpr (Ident m ) (BasicLit '1' ) ) ) ", + "m['1']"); + EXPECT_PARSE("(ExprStmt (SliceExpr (Ident s ) (Ident i ) (BinaryExpr (Ident " + "j ) (BasicLit 1 ) + ) nil 0 ) ) ", + "s[i : j + 1]"); + EXPECT_PARSE("(ExprStmt (SelectorExpr (Ident obj ) (Ident color ) ) ) ", + "obj.color"); + EXPECT_PARSE("(ExprStmt (CallExpr (SelectorExpr (IndexExpr (SelectorExpr " + "(Ident f ) (Ident p ) ) (Ident i ) ) " + "(Ident x ) ) ) ) ", + "f.p[i].x()"); } -TEST(GoParserTest, Conversions) -{ - EXPECT_PARSE("(ExprStmt (StarExpr (CallExpr (Ident Point ) (Ident p ) ) ) ) ", "*Point(p)"); - EXPECT_PARSE("(ExprStmt (CallExpr (StarExpr (Ident Point ) ) (Ident p ) ) ) ", "(*Point)(p)"); - EXPECT_PARSE("(ExprStmt (UnaryExpr (CallExpr (ChanType (Ident int ) 0 ) (Ident c ) ) <- ) ) ", "<-chan int(c)"); - EXPECT_PARSE("(ExprStmt (TypeAssertExpr (Ident y ) (SelectorExpr (Ident io ) (Ident Reader ) ) ) ) ", - "y.(io.Reader)"); +TEST(GoParserTest, Conversions) { + EXPECT_PARSE( + "(ExprStmt (StarExpr (CallExpr (Ident Point ) (Ident p ) ) ) ) ", + "*Point(p)"); + EXPECT_PARSE( + "(ExprStmt (CallExpr (StarExpr (Ident Point ) ) (Ident p ) ) ) ", + "(*Point)(p)"); + EXPECT_PARSE("(ExprStmt (UnaryExpr (CallExpr (ChanType (Ident int ) 0 ) " + "(Ident c ) ) <- ) ) ", + "<-chan int(c)"); + EXPECT_PARSE("(ExprStmt (TypeAssertExpr (Ident y ) (SelectorExpr (Ident io ) " + "(Ident Reader ) ) ) ) ", + "y.(io.Reader)"); } diff --git a/unittests/Host/CMakeLists.txt b/unittests/Host/CMakeLists.txt index be0450874203..4c20f820e3c8 100644 --- a/unittests/Host/CMakeLists.txt +++ b/unittests/Host/CMakeLists.txt @@ -1,5 +1,6 @@ add_lldb_unittest(HostTests FileSpecTest.cpp + FileSystemTest.cpp SocketAddressTest.cpp SocketTest.cpp SymbolsTest.cpp diff --git a/unittests/Host/FileSpecTest.cpp b/unittests/Host/FileSpecTest.cpp index 4e619529773f..55a66b3b37eb 100644 --- a/unittests/Host/FileSpecTest.cpp +++ b/unittests/Host/FileSpecTest.cpp @@ -13,99 +13,274 @@ using namespace lldb_private; -TEST(FileSpecTest, FileAndDirectoryComponents) -{ - FileSpec fs_posix("/foo/bar", false, FileSpec::ePathSyntaxPosix); - EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); - EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString()); - EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString()); - - FileSpec fs_windows("F:\\bar", false, FileSpec::ePathSyntaxWindows); - EXPECT_STREQ("F:\\bar", fs_windows.GetCString()); - // EXPECT_STREQ("F:\\", fs_windows.GetDirectory().GetCString()); // It returns "F:/" - EXPECT_STREQ("bar", fs_windows.GetFilename().GetCString()); - - FileSpec fs_posix_root("/", false, FileSpec::ePathSyntaxPosix); - EXPECT_STREQ("/", fs_posix_root.GetCString()); - EXPECT_EQ(nullptr, fs_posix_root.GetDirectory().GetCString()); - EXPECT_STREQ("/", fs_posix_root.GetFilename().GetCString()); - - FileSpec fs_windows_drive("F:", false, FileSpec::ePathSyntaxWindows); - EXPECT_STREQ("F:", fs_windows_drive.GetCString()); - EXPECT_EQ(nullptr, fs_windows_drive.GetDirectory().GetCString()); - EXPECT_STREQ("F:", fs_windows_drive.GetFilename().GetCString()); - - FileSpec fs_windows_root("F:\\", false, FileSpec::ePathSyntaxWindows); - EXPECT_STREQ("F:\\", fs_windows_root.GetCString()); - EXPECT_STREQ("F:", fs_windows_root.GetDirectory().GetCString()); - // EXPECT_STREQ("\\", fs_windows_root.GetFilename().GetCString()); // It returns "/" - - FileSpec fs_posix_long("/foo/bar/baz", false, FileSpec::ePathSyntaxPosix); - EXPECT_STREQ("/foo/bar/baz", fs_posix_long.GetCString()); - EXPECT_STREQ("/foo/bar", fs_posix_long.GetDirectory().GetCString()); - EXPECT_STREQ("baz", fs_posix_long.GetFilename().GetCString()); - - FileSpec fs_windows_long("F:\\bar\\baz", false, FileSpec::ePathSyntaxWindows); - EXPECT_STREQ("F:\\bar\\baz", fs_windows_long.GetCString()); - // EXPECT_STREQ("F:\\bar", fs_windows_long.GetDirectory().GetCString()); // It returns "F:/bar" - EXPECT_STREQ("baz", fs_windows_long.GetFilename().GetCString()); - - FileSpec fs_posix_trailing_slash("/foo/bar/", false, FileSpec::ePathSyntaxPosix); - EXPECT_STREQ("/foo/bar/.", fs_posix_trailing_slash.GetCString()); - EXPECT_STREQ("/foo/bar", fs_posix_trailing_slash.GetDirectory().GetCString()); - EXPECT_STREQ(".", fs_posix_trailing_slash.GetFilename().GetCString()); - - FileSpec fs_windows_trailing_slash("F:\\bar\\", false, FileSpec::ePathSyntaxWindows); - EXPECT_STREQ("F:\\bar\\.", fs_windows_trailing_slash.GetCString()); - // EXPECT_STREQ("F:\\bar", fs_windows_trailing_slash.GetDirectory().GetCString()); // It returns "F:/bar" - EXPECT_STREQ(".", fs_windows_trailing_slash.GetFilename().GetCString()); +TEST(FileSpecTest, FileAndDirectoryComponents) { + FileSpec fs_posix("/foo/bar", false, FileSpec::ePathSyntaxPosix); + EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); + EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString()); + EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString()); + + FileSpec fs_windows("F:\\bar", false, FileSpec::ePathSyntaxWindows); + EXPECT_STREQ("F:\\bar", fs_windows.GetCString()); + // EXPECT_STREQ("F:\\", fs_windows.GetDirectory().GetCString()); // It returns + // "F:/" + EXPECT_STREQ("bar", fs_windows.GetFilename().GetCString()); + + FileSpec fs_posix_root("/", false, FileSpec::ePathSyntaxPosix); + EXPECT_STREQ("/", fs_posix_root.GetCString()); + EXPECT_EQ(nullptr, fs_posix_root.GetDirectory().GetCString()); + EXPECT_STREQ("/", fs_posix_root.GetFilename().GetCString()); + + FileSpec fs_windows_drive("F:", false, FileSpec::ePathSyntaxWindows); + EXPECT_STREQ("F:", fs_windows_drive.GetCString()); + EXPECT_EQ(nullptr, fs_windows_drive.GetDirectory().GetCString()); + EXPECT_STREQ("F:", fs_windows_drive.GetFilename().GetCString()); + + FileSpec fs_windows_root("F:\\", false, FileSpec::ePathSyntaxWindows); + EXPECT_STREQ("F:\\", fs_windows_root.GetCString()); + EXPECT_STREQ("F:", fs_windows_root.GetDirectory().GetCString()); + // EXPECT_STREQ("\\", fs_windows_root.GetFilename().GetCString()); // It + // returns "/" + + FileSpec fs_posix_long("/foo/bar/baz", false, FileSpec::ePathSyntaxPosix); + EXPECT_STREQ("/foo/bar/baz", fs_posix_long.GetCString()); + EXPECT_STREQ("/foo/bar", fs_posix_long.GetDirectory().GetCString()); + EXPECT_STREQ("baz", fs_posix_long.GetFilename().GetCString()); + + FileSpec fs_windows_long("F:\\bar\\baz", false, FileSpec::ePathSyntaxWindows); + EXPECT_STREQ("F:\\bar\\baz", fs_windows_long.GetCString()); + // EXPECT_STREQ("F:\\bar", fs_windows_long.GetDirectory().GetCString()); // It + // returns "F:/bar" + EXPECT_STREQ("baz", fs_windows_long.GetFilename().GetCString()); + + FileSpec fs_posix_trailing_slash("/foo/bar/", false, + FileSpec::ePathSyntaxPosix); + EXPECT_STREQ("/foo/bar/.", fs_posix_trailing_slash.GetCString()); + EXPECT_STREQ("/foo/bar", fs_posix_trailing_slash.GetDirectory().GetCString()); + EXPECT_STREQ(".", fs_posix_trailing_slash.GetFilename().GetCString()); + + FileSpec fs_windows_trailing_slash("F:\\bar\\", false, + FileSpec::ePathSyntaxWindows); + EXPECT_STREQ("F:\\bar\\.", fs_windows_trailing_slash.GetCString()); + // EXPECT_STREQ("F:\\bar", + // fs_windows_trailing_slash.GetDirectory().GetCString()); // It returns + // "F:/bar" + EXPECT_STREQ(".", fs_windows_trailing_slash.GetFilename().GetCString()); +} + +TEST(FileSpecTest, AppendPathComponent) { + FileSpec fs_posix("/foo", false, FileSpec::ePathSyntaxPosix); + fs_posix.AppendPathComponent("bar"); + EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); + EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString()); + EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString()); + + FileSpec fs_posix_2("/foo", false, FileSpec::ePathSyntaxPosix); + fs_posix_2.AppendPathComponent("//bar/baz"); + EXPECT_STREQ("/foo/bar/baz", fs_posix_2.GetCString()); + EXPECT_STREQ("/foo/bar", fs_posix_2.GetDirectory().GetCString()); + EXPECT_STREQ("baz", fs_posix_2.GetFilename().GetCString()); + + FileSpec fs_windows("F:\\bar", false, FileSpec::ePathSyntaxWindows); + fs_windows.AppendPathComponent("baz"); + EXPECT_STREQ("F:\\bar\\baz", fs_windows.GetCString()); + // EXPECT_STREQ("F:\\bar", fs_windows.GetDirectory().GetCString()); // It + // returns "F:/bar" + EXPECT_STREQ("baz", fs_windows.GetFilename().GetCString()); + + FileSpec fs_posix_root("/", false, FileSpec::ePathSyntaxPosix); + fs_posix_root.AppendPathComponent("bar"); + EXPECT_STREQ("/bar", fs_posix_root.GetCString()); + EXPECT_STREQ("/", fs_posix_root.GetDirectory().GetCString()); + EXPECT_STREQ("bar", fs_posix_root.GetFilename().GetCString()); + + FileSpec fs_windows_root("F:\\", false, FileSpec::ePathSyntaxWindows); + fs_windows_root.AppendPathComponent("bar"); + EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString()); + // EXPECT_STREQ("F:\\", fs_windows_root.GetDirectory().GetCString()); // It + // returns "F:/" + EXPECT_STREQ("bar", fs_windows_root.GetFilename().GetCString()); +} + +TEST(FileSpecTest, CopyByAppendingPathComponent) { + FileSpec fs = FileSpec("/foo", false, FileSpec::ePathSyntaxPosix) + .CopyByAppendingPathComponent("bar"); + EXPECT_STREQ("/foo/bar", fs.GetCString()); + EXPECT_STREQ("/foo", fs.GetDirectory().GetCString()); + EXPECT_STREQ("bar", fs.GetFilename().GetCString()); +} + +static void Compare(const FileSpec &one, const FileSpec &two, bool full_match, + bool remove_backup_dots, bool result) { + EXPECT_EQ(result, FileSpec::Equal(one, two, full_match, remove_backup_dots)) + << "File one: " << one.GetCString() << "\nFile two: " << two.GetCString() + << "\nFull match: " << full_match + << "\nRemove backup dots: " << remove_backup_dots; } -TEST(FileSpecTest, AppendPathComponent) -{ - FileSpec fs_posix("/foo", false, FileSpec::ePathSyntaxPosix); - fs_posix.AppendPathComponent("bar"); - EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); - EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString()); - EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString()); - - FileSpec fs_windows("F:\\bar", false, FileSpec::ePathSyntaxWindows); - fs_windows.AppendPathComponent("baz"); - EXPECT_STREQ("F:\\bar\\baz", fs_windows.GetCString()); - // EXPECT_STREQ("F:\\bar", fs_windows.GetDirectory().GetCString()); // It returns "F:/bar" - EXPECT_STREQ("baz", fs_windows.GetFilename().GetCString()); - - FileSpec fs_posix_root("/", false, FileSpec::ePathSyntaxPosix); - fs_posix_root.AppendPathComponent("bar"); - EXPECT_STREQ("/bar", fs_posix_root.GetCString()); - EXPECT_STREQ("/", fs_posix_root.GetDirectory().GetCString()); - EXPECT_STREQ("bar", fs_posix_root.GetFilename().GetCString()); - - FileSpec fs_windows_root("F:\\", false, FileSpec::ePathSyntaxWindows); - fs_windows_root.AppendPathComponent("bar"); - EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString()); - // EXPECT_STREQ("F:\\", fs_windows_root.GetDirectory().GetCString()); // It returns "F:/" - EXPECT_STREQ("bar", fs_windows_root.GetFilename().GetCString()); +TEST(FileSpecTest, EqualSeparator) { + FileSpec backward("C:\\foo\\bar", false, FileSpec::ePathSyntaxWindows); + FileSpec forward("C:/foo/bar", false, FileSpec::ePathSyntaxWindows); + EXPECT_EQ(forward, backward); + + const bool full_match = true; + const bool remove_backup_dots = true; + const bool match = true; + Compare(forward, backward, full_match, remove_backup_dots, match); + Compare(forward, backward, full_match, !remove_backup_dots, match); + Compare(forward, backward, !full_match, remove_backup_dots, match); + Compare(forward, backward, !full_match, !remove_backup_dots, match); } -TEST(FileSpecTest, CopyByAppendingPathComponent) -{ - FileSpec fs = FileSpec("/foo", false, FileSpec::ePathSyntaxPosix).CopyByAppendingPathComponent("bar"); - EXPECT_STREQ("/foo/bar", fs.GetCString()); - EXPECT_STREQ("/foo", fs.GetDirectory().GetCString()); - EXPECT_STREQ("bar", fs.GetFilename().GetCString()); +TEST(FileSpecTest, EqualDotsWindows) { + const bool full_match = true; + const bool remove_backup_dots = true; + const bool match = true; + std::pair<const char *, const char *> tests[] = { + {R"(C:\foo\bar\baz)", R"(C:\foo\foo\..\bar\baz)"}, + {R"(C:\bar\baz)", R"(C:\foo\..\bar\baz)"}, + {R"(C:\bar\baz)", R"(C:/foo/../bar/baz)"}, + {R"(C:/bar/baz)", R"(C:\foo\..\bar\baz)"}, + {R"(C:\bar)", R"(C:\foo\..\bar)"}, + {R"(C:\foo\bar)", R"(C:\foo\.\bar)"}, + }; + + for(const auto &test: tests) { + FileSpec one(test.first, false, FileSpec::ePathSyntaxWindows); + FileSpec two(test.second, false, FileSpec::ePathSyntaxWindows); + EXPECT_NE(one, two); + Compare(one, two, full_match, remove_backup_dots, match); + Compare(one, two, full_match, !remove_backup_dots, !match); + Compare(one, two, !full_match, remove_backup_dots, match); + Compare(one, two, !full_match, !remove_backup_dots, !match); + } + } -TEST(FileSpecTest, Equal) -{ - FileSpec backward("C:\\foo\\bar", false, FileSpec::ePathSyntaxWindows); - FileSpec forward("C:/foo/bar", false, FileSpec::ePathSyntaxWindows); - EXPECT_EQ(forward, backward); - - const bool full_match = true; - const bool remove_backup_dots = true; - EXPECT_TRUE(FileSpec::Equal(forward, backward, full_match, remove_backup_dots)); - EXPECT_TRUE(FileSpec::Equal(forward, backward, full_match, !remove_backup_dots)); - EXPECT_TRUE(FileSpec::Equal(forward, backward, !full_match, remove_backup_dots)); - EXPECT_TRUE(FileSpec::Equal(forward, backward, !full_match, !remove_backup_dots)); +TEST(FileSpecTest, EqualDotsPosix) { + const bool full_match = true; + const bool remove_backup_dots = true; + const bool match = true; + std::pair<const char *, const char *> tests[] = { + {R"(/foo/bar/baz)", R"(/foo/foo/../bar/baz)"}, + {R"(/bar/baz)", R"(/foo/../bar/baz)"}, + {R"(/bar)", R"(/foo/../bar)"}, + {R"(/foo/bar)", R"(/foo/./bar)"}, + }; + + for(const auto &test: tests) { + FileSpec one(test.first, false, FileSpec::ePathSyntaxPosix); + FileSpec two(test.second, false, FileSpec::ePathSyntaxPosix); + EXPECT_NE(one, two); + Compare(one, two, full_match, remove_backup_dots, match); + Compare(one, two, full_match, !remove_backup_dots, !match); + Compare(one, two, !full_match, remove_backup_dots, match); + Compare(one, two, !full_match, !remove_backup_dots, !match); + } + +} + +TEST(FileSpecTest, EqualDotsPosixRoot) { + const bool full_match = true; + const bool remove_backup_dots = true; + const bool match = true; + std::pair<const char *, const char *> tests[] = { + {R"(/)", R"(/..)"}, {R"(/)", R"(/.)"}, {R"(/)", R"(/foo/..)"}, + }; + + for(const auto &test: tests) { + FileSpec one(test.first, false, FileSpec::ePathSyntaxPosix); + FileSpec two(test.second, false, FileSpec::ePathSyntaxPosix); + EXPECT_NE(one, two); + Compare(one, two, full_match, remove_backup_dots, match); + Compare(one, two, full_match, !remove_backup_dots, !match); + Compare(one, two, !full_match, remove_backup_dots, !match); + Compare(one, two, !full_match, !remove_backup_dots, !match); + } } + +TEST(FileSpecTest, GetNormalizedPath) { + std::pair<const char *, const char *> posix_tests[] = { + {"/foo/.././bar", "/bar"}, + {"/foo/./../bar", "/bar"}, + {"/foo/../bar", "/bar"}, + {"/foo/./bar", "/foo/bar"}, + {"/foo/..", "/"}, + {"/foo/.", "/foo"}, + {"/foo//bar", "/foo/bar"}, + {"/foo//bar/baz", "/foo/bar/baz"}, + {"/foo//bar/./baz", "/foo/bar/baz"}, + {"/./foo", "/foo"}, + {"/", "/"}, + {"//", "//"}, + {"//net", "//net"}, + {"/..", "/"}, + {"/.", "/"}, + {"..", ".."}, + {".", "."}, + {"../..", "../.."}, + {"foo/..", "."}, + {"foo/../bar", "bar"}, + {"../foo/..", ".."}, + {"./foo", "foo"}, + }; + for (auto test : posix_tests) { + EXPECT_EQ(test.second, + FileSpec(test.first, false, FileSpec::ePathSyntaxPosix) + .GetNormalizedPath() + .GetPath()); + } + + std::pair<const char *, const char *> windows_tests[] = { + {R"(c:\bar\..\bar)", R"(c:\bar)"}, + {R"(c:\bar\.\bar)", R"(c:\bar\bar)"}, + {R"(c:\bar\..)", R"(c:\)"}, + {R"(c:\bar\.)", R"(c:\bar)"}, + {R"(c:\.\bar)", R"(c:\bar)"}, + {R"(\)", R"(\)"}, + // {R"(\\)", R"(\\)"}, + // {R"(\\net)", R"(\\net)"}, + {R"(c:\..)", R"(c:\)"}, + {R"(c:\.)", R"(c:\)"}, + {R"(\..)", R"(\)"}, + // {R"(c:..)", R"(c:..)"}, + {R"(..)", R"(..)"}, + {R"(.)", R"(.)"}, + {R"(c:..\..)", R"(c:..\..)"}, + {R"(..\..)", R"(..\..)"}, + {R"(foo\..)", R"(.)"}, + {R"(foo\..\bar)", R"(bar)"}, + {R"(..\foo\..)", R"(..)"}, + {R"(.\foo)", R"(foo)"}, + }; + for (auto test : windows_tests) { + EXPECT_EQ(test.second, + FileSpec(test.first, false, FileSpec::ePathSyntaxWindows) + .GetNormalizedPath() + .GetPath()) + << "Original path: " << test.first; + } +} + +TEST(FileSpecTest, FormatFileSpec) { + auto win = FileSpec::ePathSyntaxWindows; + + FileSpec F; + EXPECT_EQ("(empty)", llvm::formatv("{0}", F).str()); + EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str()); + EXPECT_EQ("(empty)", llvm::formatv("{0:F}", F).str()); + + F = FileSpec("C:\\foo\\bar.txt", false, win); + EXPECT_EQ("C:\\foo\\bar.txt", llvm::formatv("{0}", F).str()); + EXPECT_EQ("C:\\foo\\", llvm::formatv("{0:D}", F).str()); + EXPECT_EQ("bar.txt", llvm::formatv("{0:F}", F).str()); + + F = FileSpec("foo\\bar.txt", false, win); + EXPECT_EQ("foo\\bar.txt", llvm::formatv("{0}", F).str()); + EXPECT_EQ("foo\\", llvm::formatv("{0:D}", F).str()); + EXPECT_EQ("bar.txt", llvm::formatv("{0:F}", F).str()); + + F = FileSpec("foo", false, win); + EXPECT_EQ("foo", llvm::formatv("{0}", F).str()); + EXPECT_EQ("foo", llvm::formatv("{0:F}", F).str()); + EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str()); +}
\ No newline at end of file diff --git a/unittests/Host/FileSystemTest.cpp b/unittests/Host/FileSystemTest.cpp new file mode 100644 index 000000000000..d5160b1a0da1 --- /dev/null +++ b/unittests/Host/FileSystemTest.cpp @@ -0,0 +1,32 @@ +//===-- FileSystemTest.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Host/FileSystem.h" + +extern const char *TestMainArgv0; + +using namespace lldb_private; + +TEST(FileSystemTest, FileAndDirectoryComponents) { + using namespace std::chrono; + + const bool resolve = true; +#ifdef _WIN32 + FileSpec fs1("C:\\FILE\\THAT\\DOES\\NOT\\EXIST.TXT", !resolve); +#else + FileSpec fs1("/file/that/does/not/exist.txt", !resolve); +#endif + FileSpec fs2(TestMainArgv0, resolve); + + EXPECT_EQ(system_clock::time_point(), FileSystem::GetModificationTime(fs1)); + EXPECT_LT(system_clock::time_point() + hours(24 * 365 * 20), + FileSystem::GetModificationTime(fs2)); +} diff --git a/unittests/Host/SocketAddressTest.cpp b/unittests/Host/SocketAddressTest.cpp index 6b27e04ce702..3c18137ac063 100644 --- a/unittests/Host/SocketAddressTest.cpp +++ b/unittests/Host/SocketAddressTest.cpp @@ -7,68 +7,64 @@ // //===----------------------------------------------------------------------===// - #include "gtest/gtest.h" #include "lldb/Host/SocketAddress.h" -namespace -{ - class SocketAddressTest: public ::testing::Test - { - }; +namespace { +class SocketAddressTest : public ::testing::Test {}; } using namespace lldb_private; -TEST_F (SocketAddressTest, Set) -{ - SocketAddress sa; - ASSERT_TRUE (sa.SetToLocalhost (AF_INET, 1138)); - ASSERT_STREQ ("127.0.0.1", sa.GetIPAddress ().c_str ()); - ASSERT_EQ (1138, sa.GetPort ()); +TEST_F(SocketAddressTest, Set) { + SocketAddress sa; + ASSERT_TRUE(sa.SetToLocalhost(AF_INET, 1138)); + ASSERT_STREQ("127.0.0.1", sa.GetIPAddress().c_str()); + ASSERT_EQ(1138, sa.GetPort()); - ASSERT_TRUE (sa.SetToAnyAddress (AF_INET, 0)); - ASSERT_STREQ ("0.0.0.0", sa.GetIPAddress ().c_str ()); - ASSERT_EQ (0, sa.GetPort ()); + ASSERT_TRUE(sa.SetToAnyAddress(AF_INET, 0)); + ASSERT_STREQ("0.0.0.0", sa.GetIPAddress().c_str()); + ASSERT_EQ(0, sa.GetPort()); - ASSERT_TRUE (sa.SetToLocalhost (AF_INET6, 1139)); - ASSERT_TRUE(sa.GetIPAddress() == "::1" || sa.GetIPAddress() == "0:0:0:0:0:0:0:1") << "Address was: " - << sa.GetIPAddress(); - ASSERT_EQ (1139, sa.GetPort ()); + ASSERT_TRUE(sa.SetToLocalhost(AF_INET6, 1139)); + ASSERT_TRUE(sa.GetIPAddress() == "::1" || + sa.GetIPAddress() == "0:0:0:0:0:0:0:1") + << "Address was: " << sa.GetIPAddress(); + ASSERT_EQ(1139, sa.GetPort()); } #ifdef _WIN32 // we need to test our inet_ntop implementation for Windows XP -const char* inet_ntop (int af, const void * src, - char * dst, socklen_t size); +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); -TEST_F (SocketAddressTest, inet_ntop) -{ - const uint8_t address4[4] = {255, 0, 1, 100}; - const uint8_t address6[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 255, 0}; - - char buffer[INET6_ADDRSTRLEN]; - memset (buffer, 'x', sizeof (buffer)); - EXPECT_STREQ ("1:203:405:607:809:a0b:c0d:ff00", inet_ntop (AF_INET6, address6, buffer, sizeof (buffer))); - memset (buffer, 'x', sizeof (buffer)); - EXPECT_STREQ ("1:203:405:607:809:a0b:c0d:ff00", inet_ntop (AF_INET6, address6, buffer, 31)); - memset (buffer, 'x', sizeof (buffer)); - EXPECT_STREQ (nullptr, inet_ntop (AF_INET6, address6, buffer, 0)); - memset (buffer, 'x', sizeof (buffer)); - EXPECT_STREQ (nullptr, inet_ntop (AF_INET6, address6, buffer, 30)); - - memset (buffer, 'x', sizeof (buffer)); - EXPECT_STREQ ("255.0.1.100", inet_ntop (AF_INET, address4, buffer, sizeof (buffer))); - memset (buffer, 'x', sizeof (buffer)); - EXPECT_STREQ ("255.0.1.100", inet_ntop (AF_INET, address4, buffer, 12)); - memset (buffer, 'x', sizeof (buffer)); - EXPECT_STREQ (nullptr, inet_ntop (AF_INET, address4, buffer, 0)); - memset (buffer, 'x', sizeof (buffer)); - EXPECT_STREQ (nullptr, inet_ntop (AF_INET, address4, buffer, 11)); -} +TEST_F(SocketAddressTest, inet_ntop) { + const uint8_t address4[4] = {255, 0, 1, 100}; + const uint8_t address6[16] = {0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 255, 0}; -#endif + char buffer[INET6_ADDRSTRLEN]; + memset(buffer, 'x', sizeof(buffer)); + EXPECT_STREQ("1:203:405:607:809:a0b:c0d:ff00", + inet_ntop(AF_INET6, address6, buffer, sizeof(buffer))); + memset(buffer, 'x', sizeof(buffer)); + EXPECT_STREQ("1:203:405:607:809:a0b:c0d:ff00", + inet_ntop(AF_INET6, address6, buffer, 31)); + memset(buffer, 'x', sizeof(buffer)); + EXPECT_STREQ(nullptr, inet_ntop(AF_INET6, address6, buffer, 0)); + memset(buffer, 'x', sizeof(buffer)); + EXPECT_STREQ(nullptr, inet_ntop(AF_INET6, address6, buffer, 30)); + memset(buffer, 'x', sizeof(buffer)); + EXPECT_STREQ("255.0.1.100", + inet_ntop(AF_INET, address4, buffer, sizeof(buffer))); + memset(buffer, 'x', sizeof(buffer)); + EXPECT_STREQ("255.0.1.100", inet_ntop(AF_INET, address4, buffer, 12)); + memset(buffer, 'x', sizeof(buffer)); + EXPECT_STREQ(nullptr, inet_ntop(AF_INET, address4, buffer, 0)); + memset(buffer, 'x', sizeof(buffer)); + EXPECT_STREQ(nullptr, inet_ntop(AF_INET, address4, buffer, 11)); +} +#endif diff --git a/unittests/Host/SocketTest.cpp b/unittests/Host/SocketTest.cpp index e3e522744760..569a4404131e 100644 --- a/unittests/Host/SocketTest.cpp +++ b/unittests/Host/SocketTest.cpp @@ -8,7 +8,8 @@ //===----------------------------------------------------------------------===// #if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) -// Workaround for MSVC standard library bug, which fails to include <thread> when +// Workaround for MSVC standard library bug, which fails to include <thread> +// when // exceptions are disabled. #include <eh.h> #endif @@ -30,182 +31,189 @@ using namespace lldb_private; -class SocketTest : public testing::Test -{ - public: - void - SetUp() override - { +class SocketTest : public testing::Test { +public: + void SetUp() override { #if defined(_MSC_VER) - WSADATA data; - ::WSAStartup(MAKEWORD(2, 2), &data); + WSADATA data; + ::WSAStartup(MAKEWORD(2, 2), &data); #endif - } + } - void - TearDown() override - { + void TearDown() override { #if defined(_MSC_VER) - ::WSACleanup(); + ::WSACleanup(); #endif - } - - protected: - static void - AcceptThread(Socket *listen_socket, const char *listen_remote_address, bool child_processes_inherit, - Socket **accept_socket, Error *error) - { - *error = listen_socket->Accept(listen_remote_address, child_processes_inherit, *accept_socket); - } - - template<typename SocketType> - void - CreateConnectedSockets(const char *listen_remote_address, const std::function<std::string(const SocketType&)> &get_connect_addr, std::unique_ptr<SocketType> *a_up, std::unique_ptr<SocketType> *b_up) - { - bool child_processes_inherit = false; - Error error; - std::unique_ptr<SocketType> listen_socket_up(new SocketType(child_processes_inherit, error)); - EXPECT_FALSE(error.Fail()); - error = listen_socket_up->Listen(listen_remote_address, 5); - EXPECT_FALSE(error.Fail()); - EXPECT_TRUE(listen_socket_up->IsValid()); - - Error accept_error; - Socket *accept_socket; - std::thread accept_thread(AcceptThread, listen_socket_up.get(), listen_remote_address, child_processes_inherit, - &accept_socket, &accept_error); - - std::string connect_remote_address = get_connect_addr(*listen_socket_up); - std::unique_ptr<SocketType> connect_socket_up(new SocketType(child_processes_inherit, error)); - EXPECT_FALSE(error.Fail()); - error = connect_socket_up->Connect(connect_remote_address.c_str()); - EXPECT_FALSE(error.Fail()); - EXPECT_TRUE(connect_socket_up->IsValid()); - - a_up->swap(connect_socket_up); - EXPECT_TRUE(error.Success()); - EXPECT_NE(nullptr, a_up->get()); - EXPECT_TRUE((*a_up)->IsValid()); - - accept_thread.join(); - b_up->reset(static_cast<SocketType*>(accept_socket)); - EXPECT_TRUE(accept_error.Success()); - EXPECT_NE(nullptr, b_up->get()); - EXPECT_TRUE((*b_up)->IsValid()); - - listen_socket_up.reset(); - } -}; - -TEST_F (SocketTest, DecodeHostAndPort) -{ - std::string host_str; - std::string port_str; - int32_t port; + } + +protected: + static void AcceptThread(Socket *listen_socket, + const char *listen_remote_address, + bool child_processes_inherit, Socket **accept_socket, + Error *error) { + *error = listen_socket->Accept(listen_remote_address, + child_processes_inherit, *accept_socket); + } + + template <typename SocketType> + void CreateConnectedSockets( + const char *listen_remote_address, + const std::function<std::string(const SocketType &)> &get_connect_addr, + std::unique_ptr<SocketType> *a_up, std::unique_ptr<SocketType> *b_up) { + bool child_processes_inherit = false; Error error; - EXPECT_TRUE (Socket::DecodeHostAndPort ("localhost:1138", host_str, port_str, port, &error)); - EXPECT_STREQ ("localhost", host_str.c_str ()); - EXPECT_STREQ ("1138", port_str.c_str ()); - EXPECT_EQ (1138, port); - EXPECT_TRUE (error.Success ()); - - EXPECT_FALSE (Socket::DecodeHostAndPort ("google.com:65536", host_str, port_str, port, &error)); - EXPECT_TRUE (error.Fail ()); - EXPECT_STREQ ("invalid host:port specification: 'google.com:65536'", error.AsCString ()); - - EXPECT_FALSE (Socket::DecodeHostAndPort ("google.com:-1138", host_str, port_str, port, &error)); - EXPECT_TRUE (error.Fail ()); - EXPECT_STREQ ("invalid host:port specification: 'google.com:-1138'", error.AsCString ()); - - EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:65536", host_str, port_str, port, &error)); - EXPECT_TRUE(error.Fail()); - EXPECT_STREQ("invalid host:port specification: 'google.com:65536'", error.AsCString()); - - EXPECT_TRUE (Socket::DecodeHostAndPort ("12345", host_str, port_str, port, &error)); - EXPECT_STREQ ("", host_str.c_str ()); - EXPECT_STREQ ("12345", port_str.c_str ()); - EXPECT_EQ (12345, port); - EXPECT_TRUE (error.Success ()); - - EXPECT_TRUE (Socket::DecodeHostAndPort ("*:0", host_str, port_str, port, &error)); - EXPECT_STREQ ("*", host_str.c_str ()); - EXPECT_STREQ ("0", port_str.c_str ()); - EXPECT_EQ (0, port); - EXPECT_TRUE (error.Success ()); - - EXPECT_TRUE(Socket::DecodeHostAndPort("*:65535", host_str, port_str, port, &error)); - EXPECT_STREQ("*", host_str.c_str()); - EXPECT_STREQ("65535", port_str.c_str()); - EXPECT_EQ(65535, port); + std::unique_ptr<SocketType> listen_socket_up( + new SocketType(child_processes_inherit, error)); + EXPECT_FALSE(error.Fail()); + error = listen_socket_up->Listen(listen_remote_address, 5); + EXPECT_FALSE(error.Fail()); + EXPECT_TRUE(listen_socket_up->IsValid()); + + Error accept_error; + Socket *accept_socket; + std::thread accept_thread(AcceptThread, listen_socket_up.get(), + listen_remote_address, child_processes_inherit, + &accept_socket, &accept_error); + + std::string connect_remote_address = get_connect_addr(*listen_socket_up); + std::unique_ptr<SocketType> connect_socket_up( + new SocketType(child_processes_inherit, error)); + EXPECT_FALSE(error.Fail()); + error = connect_socket_up->Connect(connect_remote_address); + EXPECT_FALSE(error.Fail()); + EXPECT_TRUE(connect_socket_up->IsValid()); + + a_up->swap(connect_socket_up); EXPECT_TRUE(error.Success()); + EXPECT_NE(nullptr, a_up->get()); + EXPECT_TRUE((*a_up)->IsValid()); + + accept_thread.join(); + b_up->reset(static_cast<SocketType *>(accept_socket)); + EXPECT_TRUE(accept_error.Success()); + EXPECT_NE(nullptr, b_up->get()); + EXPECT_TRUE((*b_up)->IsValid()); + + listen_socket_up.reset(); + } +}; + +TEST_F(SocketTest, DecodeHostAndPort) { + std::string host_str; + std::string port_str; + int32_t port; + Error error; + EXPECT_TRUE(Socket::DecodeHostAndPort("localhost:1138", host_str, port_str, + port, &error)); + EXPECT_STREQ("localhost", host_str.c_str()); + EXPECT_STREQ("1138", port_str.c_str()); + EXPECT_EQ(1138, port); + EXPECT_TRUE(error.Success()); + + EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:65536", host_str, port_str, + port, &error)); + EXPECT_TRUE(error.Fail()); + EXPECT_STREQ("invalid host:port specification: 'google.com:65536'", + error.AsCString()); + + EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:-1138", host_str, port_str, + port, &error)); + EXPECT_TRUE(error.Fail()); + EXPECT_STREQ("invalid host:port specification: 'google.com:-1138'", + error.AsCString()); + + EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:65536", host_str, port_str, + port, &error)); + EXPECT_TRUE(error.Fail()); + EXPECT_STREQ("invalid host:port specification: 'google.com:65536'", + error.AsCString()); + + EXPECT_TRUE( + Socket::DecodeHostAndPort("12345", host_str, port_str, port, &error)); + EXPECT_STREQ("", host_str.c_str()); + EXPECT_STREQ("12345", port_str.c_str()); + EXPECT_EQ(12345, port); + EXPECT_TRUE(error.Success()); + + EXPECT_TRUE( + Socket::DecodeHostAndPort("*:0", host_str, port_str, port, &error)); + EXPECT_STREQ("*", host_str.c_str()); + EXPECT_STREQ("0", port_str.c_str()); + EXPECT_EQ(0, port); + EXPECT_TRUE(error.Success()); + + EXPECT_TRUE( + Socket::DecodeHostAndPort("*:65535", host_str, port_str, port, &error)); + EXPECT_STREQ("*", host_str.c_str()); + EXPECT_STREQ("65535", port_str.c_str()); + EXPECT_EQ(65535, port); + EXPECT_TRUE(error.Success()); } #ifndef LLDB_DISABLE_POSIX -TEST_F (SocketTest, DomainListenConnectAccept) -{ - char* file_name_str = tempnam(nullptr, nullptr); - EXPECT_NE (nullptr, file_name_str); - const std::string file_name(file_name_str); - free(file_name_str); - - std::unique_ptr<DomainSocket> socket_a_up; - std::unique_ptr<DomainSocket> socket_b_up; - CreateConnectedSockets<DomainSocket>(file_name.c_str(), - [=](const DomainSocket &) - { - return file_name; - }, - &socket_a_up, &socket_b_up); +TEST_F(SocketTest, DomainListenConnectAccept) { + char *file_name_str = tempnam(nullptr, nullptr); + EXPECT_NE(nullptr, file_name_str); + const std::string file_name(file_name_str); + free(file_name_str); + + std::unique_ptr<DomainSocket> socket_a_up; + std::unique_ptr<DomainSocket> socket_b_up; + CreateConnectedSockets<DomainSocket>( + file_name.c_str(), [=](const DomainSocket &) { return file_name; }, + &socket_a_up, &socket_b_up); } #endif -TEST_F (SocketTest, TCPListen0ConnectAccept) -{ - std::unique_ptr<TCPSocket> socket_a_up; - std::unique_ptr<TCPSocket> socket_b_up; - CreateConnectedSockets<TCPSocket>("127.0.0.1:0", - [=](const TCPSocket &s) - { - char connect_remote_address[64]; - snprintf(connect_remote_address, sizeof(connect_remote_address), "localhost:%u", s.GetLocalPortNumber()); - return std::string(connect_remote_address); - }, - &socket_a_up, &socket_b_up); +TEST_F(SocketTest, TCPListen0ConnectAccept) { + std::unique_ptr<TCPSocket> socket_a_up; + std::unique_ptr<TCPSocket> socket_b_up; + CreateConnectedSockets<TCPSocket>( + "127.0.0.1:0", + [=](const TCPSocket &s) { + char connect_remote_address[64]; + snprintf(connect_remote_address, sizeof(connect_remote_address), + "localhost:%u", s.GetLocalPortNumber()); + return std::string(connect_remote_address); + }, + &socket_a_up, &socket_b_up); } -TEST_F (SocketTest, TCPGetAddress) -{ - std::unique_ptr<TCPSocket> socket_a_up; - std::unique_ptr<TCPSocket> socket_b_up; - CreateConnectedSockets<TCPSocket>("127.0.0.1:0", - [=](const TCPSocket &s) - { - char connect_remote_address[64]; - snprintf(connect_remote_address, sizeof(connect_remote_address), "localhost:%u", s.GetLocalPortNumber()); - return std::string(connect_remote_address); - }, - &socket_a_up, - &socket_b_up); - - EXPECT_EQ (socket_a_up->GetLocalPortNumber (), socket_b_up->GetRemotePortNumber ()); - EXPECT_EQ (socket_b_up->GetLocalPortNumber (), socket_a_up->GetRemotePortNumber ()); - EXPECT_NE (socket_a_up->GetLocalPortNumber (), socket_b_up->GetLocalPortNumber ()); - EXPECT_STREQ ("127.0.0.1", socket_a_up->GetRemoteIPAddress ().c_str ()); - EXPECT_STREQ ("127.0.0.1", socket_b_up->GetRemoteIPAddress ().c_str ()); +TEST_F(SocketTest, TCPGetAddress) { + std::unique_ptr<TCPSocket> socket_a_up; + std::unique_ptr<TCPSocket> socket_b_up; + CreateConnectedSockets<TCPSocket>( + "127.0.0.1:0", + [=](const TCPSocket &s) { + char connect_remote_address[64]; + snprintf(connect_remote_address, sizeof(connect_remote_address), + "localhost:%u", s.GetLocalPortNumber()); + return std::string(connect_remote_address); + }, + &socket_a_up, &socket_b_up); + + EXPECT_EQ(socket_a_up->GetLocalPortNumber(), + socket_b_up->GetRemotePortNumber()); + EXPECT_EQ(socket_b_up->GetLocalPortNumber(), + socket_a_up->GetRemotePortNumber()); + EXPECT_NE(socket_a_up->GetLocalPortNumber(), + socket_b_up->GetLocalPortNumber()); + EXPECT_STREQ("127.0.0.1", socket_a_up->GetRemoteIPAddress().c_str()); + EXPECT_STREQ("127.0.0.1", socket_b_up->GetRemoteIPAddress().c_str()); } -TEST_F (SocketTest, UDPConnect) -{ - Socket* socket_a; - Socket* socket_b; +TEST_F(SocketTest, UDPConnect) { + Socket *socket_a; + Socket *socket_b; + + bool child_processes_inherit = false; + auto error = UDPSocket::Connect("127.0.0.1:0", child_processes_inherit, + socket_a, socket_b); - bool child_processes_inherit = false; - auto error = UDPSocket::Connect("127.0.0.1:0", child_processes_inherit, socket_a, socket_b); - - std::unique_ptr<Socket> a_up(socket_a); - std::unique_ptr<Socket> b_up(socket_b); + std::unique_ptr<Socket> a_up(socket_a); + std::unique_ptr<Socket> b_up(socket_b); - EXPECT_TRUE(error.Success ()); - EXPECT_TRUE(a_up->IsValid()); - EXPECT_TRUE(b_up->IsValid()); + EXPECT_TRUE(error.Success()); + EXPECT_TRUE(a_up->IsValid()); + EXPECT_TRUE(b_up->IsValid()); } diff --git a/unittests/Host/SymbolsTest.cpp b/unittests/Host/SymbolsTest.cpp index cb59b2c56539..d3073310e3e7 100644 --- a/unittests/Host/SymbolsTest.cpp +++ b/unittests/Host/SymbolsTest.cpp @@ -8,23 +8,25 @@ //===----------------------------------------------------------------------===// #include "gtest/gtest.h" + #include "lldb/Host/Symbols.h" #include "lldb/Core/ModuleSpec.h" using namespace lldb_private; -TEST(SymbolsTest, LocateExecutableSymbolFileForUnknownExecutableAndUnknownSymbolFile) -{ - ModuleSpec module_spec; - FileSpec symbol_file_spec = Symbols::LocateExecutableSymbolFile(module_spec); - EXPECT_TRUE(symbol_file_spec.GetFilename().IsEmpty()); +TEST(SymbolsTest, + LocateExecutableSymbolFileForUnknownExecutableAndUnknownSymbolFile) { + ModuleSpec module_spec; + FileSpec symbol_file_spec = Symbols::LocateExecutableSymbolFile(module_spec); + EXPECT_TRUE(symbol_file_spec.GetFilename().IsEmpty()); } -TEST(SymbolsTest, LocateExecutableSymbolFileForUnknownExecutableAndMissingSymbolFile) -{ - ModuleSpec module_spec; - // using a GUID here because the symbol file shouldn't actually exist on disk - module_spec.GetSymbolFileSpec().SetFile("4A524676-B24B-4F4E-968A-551D465EBAF1.so", false); - FileSpec symbol_file_spec = Symbols::LocateExecutableSymbolFile(module_spec); - EXPECT_TRUE(symbol_file_spec.GetFilename().IsEmpty()); +TEST(SymbolsTest, + LocateExecutableSymbolFileForUnknownExecutableAndMissingSymbolFile) { + ModuleSpec module_spec; + // using a GUID here because the symbol file shouldn't actually exist on disk + module_spec.GetSymbolFileSpec().SetFile( + "4A524676-B24B-4F4E-968A-551D465EBAF1.so", false); + FileSpec symbol_file_spec = Symbols::LocateExecutableSymbolFile(module_spec); + EXPECT_TRUE(symbol_file_spec.GetFilename().IsEmpty()); } diff --git a/unittests/Interpreter/TestArgs.cpp b/unittests/Interpreter/TestArgs.cpp index e2cf1c4d4a0a..9dcf09d54aea 100644 --- a/unittests/Interpreter/TestArgs.cpp +++ b/unittests/Interpreter/TestArgs.cpp @@ -11,60 +11,333 @@ #include "lldb/Interpreter/Args.h" +#include <limits> +#include <sstream> + using namespace lldb_private; -TEST(ArgsTest, TestSingleArg) -{ - Args args; - args.SetCommandString("arg"); - EXPECT_EQ(1u, args.GetArgumentCount()); - EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg"); -} - -TEST(ArgsTest, TestSingleQuotedArgWithSpace) -{ - Args args; - args.SetCommandString("\"arg with space\""); - EXPECT_EQ(1u, args.GetArgumentCount()); - EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space"); -} - -TEST(ArgsTest, TestSingleArgWithQuotedSpace) -{ - Args args; - args.SetCommandString("arg\\ with\\ space"); - EXPECT_EQ(1u, args.GetArgumentCount()); - EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space"); -} - -TEST(ArgsTest, TestMultipleArgs) -{ - Args args; - args.SetCommandString("this has multiple args"); - EXPECT_EQ(4u, args.GetArgumentCount()); - EXPECT_STREQ(args.GetArgumentAtIndex(0), "this"); - EXPECT_STREQ(args.GetArgumentAtIndex(1), "has"); - EXPECT_STREQ(args.GetArgumentAtIndex(2), "multiple"); - EXPECT_STREQ(args.GetArgumentAtIndex(3), "args"); -} - -TEST(ArgsTest, TestOverwriteArgs) -{ - Args args; - args.SetCommandString("this has multiple args"); - EXPECT_EQ(4u, args.GetArgumentCount()); - args.SetCommandString("arg"); - EXPECT_EQ(1u, args.GetArgumentCount()); - EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg"); -} - -TEST(ArgsTest, TestAppendArg) -{ - Args args; - args.SetCommandString("first_arg"); - EXPECT_EQ(1u, args.GetArgumentCount()); - args.AppendArgument("second_arg"); - EXPECT_EQ(2u, args.GetArgumentCount()); - EXPECT_STREQ(args.GetArgumentAtIndex(0), "first_arg"); - EXPECT_STREQ(args.GetArgumentAtIndex(1), "second_arg"); +TEST(ArgsTest, TestSingleArg) { + Args args; + args.SetCommandString("arg"); + EXPECT_EQ(1u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg"); +} + +TEST(ArgsTest, TestSingleQuotedArgWithSpace) { + Args args; + args.SetCommandString("\"arg with space\""); + EXPECT_EQ(1u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space"); +} + +TEST(ArgsTest, TestSingleArgWithQuotedSpace) { + Args args; + args.SetCommandString("arg\\ with\\ space"); + EXPECT_EQ(1u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space"); +} + +TEST(ArgsTest, TestMultipleArgs) { + Args args; + args.SetCommandString("this has multiple args"); + EXPECT_EQ(4u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "this"); + EXPECT_STREQ(args.GetArgumentAtIndex(1), "has"); + EXPECT_STREQ(args.GetArgumentAtIndex(2), "multiple"); + EXPECT_STREQ(args.GetArgumentAtIndex(3), "args"); +} + +TEST(ArgsTest, TestOverwriteArgs) { + Args args; + args.SetCommandString("this has multiple args"); + EXPECT_EQ(4u, args.GetArgumentCount()); + args.SetCommandString("arg"); + EXPECT_EQ(1u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg"); +} + +TEST(ArgsTest, TestAppendArg) { + Args args; + args.SetCommandString("first_arg"); + EXPECT_EQ(1u, args.GetArgumentCount()); + args.AppendArgument(llvm::StringRef("second_arg")); + EXPECT_EQ(2u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "first_arg"); + EXPECT_STREQ(args.GetArgumentAtIndex(1), "second_arg"); +} + +TEST(ArgsTest, TestInsertArg) { + Args args; + args.AppendArgument("1"); + args.AppendArgument("2"); + args.AppendArgument("3"); + args.InsertArgumentAtIndex(1, "1.5"); + args.InsertArgumentAtIndex(4, "3.5"); + + ASSERT_EQ(5u, args.GetArgumentCount()); + EXPECT_STREQ("1", args.GetArgumentAtIndex(0)); + EXPECT_STREQ("1.5", args.GetArgumentAtIndex(1)); + EXPECT_STREQ("2", args.GetArgumentAtIndex(2)); + EXPECT_STREQ("3", args.GetArgumentAtIndex(3)); + EXPECT_STREQ("3.5", args.GetArgumentAtIndex(4)); +} + +TEST(ArgsTest, TestArgv) { + Args args; + EXPECT_EQ(nullptr, args.GetArgumentVector()); + + args.AppendArgument("1"); + EXPECT_NE(nullptr, args.GetArgumentVector()[0]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[1]); + + args.AppendArgument("2"); + EXPECT_NE(nullptr, args.GetArgumentVector()[0]); + EXPECT_NE(nullptr, args.GetArgumentVector()[1]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[2]); + + args.AppendArgument("3"); + EXPECT_NE(nullptr, args.GetArgumentVector()[0]); + EXPECT_NE(nullptr, args.GetArgumentVector()[1]); + EXPECT_NE(nullptr, args.GetArgumentVector()[2]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[3]); + + args.InsertArgumentAtIndex(1, "1.5"); + EXPECT_NE(nullptr, args.GetArgumentVector()[0]); + EXPECT_NE(nullptr, args.GetArgumentVector()[1]); + EXPECT_NE(nullptr, args.GetArgumentVector()[2]); + EXPECT_NE(nullptr, args.GetArgumentVector()[3]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[4]); + + args.InsertArgumentAtIndex(4, "3.5"); + EXPECT_NE(nullptr, args.GetArgumentVector()[0]); + EXPECT_NE(nullptr, args.GetArgumentVector()[1]); + EXPECT_NE(nullptr, args.GetArgumentVector()[2]); + EXPECT_NE(nullptr, args.GetArgumentVector()[3]); + EXPECT_NE(nullptr, args.GetArgumentVector()[4]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[5]); +} + +TEST(ArgsTest, GetQuotedCommandString) { + Args args; + const char *str = "process launch -o stdout.txt -- \"a b c\""; + args.SetCommandString(str); + + std::string stdstr; + ASSERT_TRUE(args.GetQuotedCommandString(stdstr)); + EXPECT_EQ(str, stdstr); +} + +TEST(ArgsTest, BareSingleQuote) { + Args args; + args.SetCommandString("a\\'b"); + EXPECT_EQ(1u, args.GetArgumentCount()); + + EXPECT_STREQ("a'b", args.GetArgumentAtIndex(0)); +} + +TEST(ArgsTest, DoubleQuotedItem) { + Args args; + args.SetCommandString("\"a b c\""); + EXPECT_EQ(1u, args.GetArgumentCount()); + + EXPECT_STREQ("a b c", args.GetArgumentAtIndex(0)); +} + +TEST(ArgsTest, AppendArguments) { + Args args; + const char *argv[] = {"1", "2", nullptr}; + const char *argv2[] = {"3", "4", nullptr}; + + args.AppendArguments(argv); + ASSERT_EQ(2u, args.GetArgumentCount()); + EXPECT_STREQ("1", args.GetArgumentVector()[0]); + EXPECT_STREQ("2", args.GetArgumentVector()[1]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[2]); + EXPECT_STREQ("1", args.GetArgumentAtIndex(0)); + EXPECT_STREQ("2", args.GetArgumentAtIndex(1)); + + args.AppendArguments(argv2); + ASSERT_EQ(4u, args.GetArgumentCount()); + EXPECT_STREQ("1", args.GetArgumentVector()[0]); + EXPECT_STREQ("2", args.GetArgumentVector()[1]); + EXPECT_STREQ("3", args.GetArgumentVector()[2]); + EXPECT_STREQ("4", args.GetArgumentVector()[3]); + EXPECT_EQ(nullptr, args.GetArgumentVector()[4]); + EXPECT_STREQ("1", args.GetArgumentAtIndex(0)); + EXPECT_STREQ("2", args.GetArgumentAtIndex(1)); + EXPECT_STREQ("3", args.GetArgumentAtIndex(2)); + EXPECT_STREQ("4", args.GetArgumentAtIndex(3)); +} + +TEST(ArgsTest, StringToBoolean) { + bool success = false; + EXPECT_TRUE(Args::StringToBoolean(llvm::StringRef("true"), false, nullptr)); + EXPECT_TRUE(Args::StringToBoolean(llvm::StringRef("on"), false, nullptr)); + EXPECT_TRUE(Args::StringToBoolean(llvm::StringRef("yes"), false, nullptr)); + EXPECT_TRUE(Args::StringToBoolean(llvm::StringRef("1"), false, nullptr)); + + EXPECT_TRUE(Args::StringToBoolean(llvm::StringRef("true"), false, &success)); + EXPECT_TRUE(success); + EXPECT_TRUE(Args::StringToBoolean(llvm::StringRef("on"), false, &success)); + EXPECT_TRUE(success); + EXPECT_TRUE(Args::StringToBoolean(llvm::StringRef("yes"), false, &success)); + EXPECT_TRUE(success); + EXPECT_TRUE(Args::StringToBoolean(llvm::StringRef("1"), false, &success)); + EXPECT_TRUE(success); + + EXPECT_FALSE(Args::StringToBoolean(llvm::StringRef("false"), true, nullptr)); + EXPECT_FALSE(Args::StringToBoolean(llvm::StringRef("off"), true, nullptr)); + EXPECT_FALSE(Args::StringToBoolean(llvm::StringRef("no"), true, nullptr)); + EXPECT_FALSE(Args::StringToBoolean(llvm::StringRef("0"), true, nullptr)); + + EXPECT_FALSE(Args::StringToBoolean(llvm::StringRef("false"), true, &success)); + EXPECT_TRUE(success); + EXPECT_FALSE(Args::StringToBoolean(llvm::StringRef("off"), true, &success)); + EXPECT_TRUE(success); + EXPECT_FALSE(Args::StringToBoolean(llvm::StringRef("no"), true, &success)); + EXPECT_TRUE(success); + EXPECT_FALSE(Args::StringToBoolean(llvm::StringRef("0"), true, &success)); + EXPECT_TRUE(success); + + EXPECT_FALSE(Args::StringToBoolean(llvm::StringRef("10"), false, &success)); + EXPECT_FALSE(success); + EXPECT_TRUE(Args::StringToBoolean(llvm::StringRef("10"), true, &success)); + EXPECT_FALSE(success); + EXPECT_TRUE(Args::StringToBoolean(llvm::StringRef(""), true, &success)); + EXPECT_FALSE(success); +} + +TEST(ArgsTest, StringToChar) { + bool success = false; + + EXPECT_EQ('A', Args::StringToChar("A", 'B', nullptr)); + EXPECT_EQ('B', Args::StringToChar("B", 'A', nullptr)); + + EXPECT_EQ('A', Args::StringToChar("A", 'B', &success)); + EXPECT_TRUE(success); + EXPECT_EQ('B', Args::StringToChar("B", 'A', &success)); + EXPECT_TRUE(success); + + EXPECT_EQ('A', Args::StringToChar("", 'A', &success)); + EXPECT_FALSE(success); + EXPECT_EQ('A', Args::StringToChar("ABC", 'A', &success)); + EXPECT_FALSE(success); +} + +TEST(ArgsTest, StringToScriptLanguage) { + bool success = false; + + EXPECT_EQ(lldb::eScriptLanguageDefault, + Args::StringToScriptLanguage(llvm::StringRef("default"), + lldb::eScriptLanguageNone, nullptr)); + EXPECT_EQ(lldb::eScriptLanguagePython, + Args::StringToScriptLanguage(llvm::StringRef("python"), + lldb::eScriptLanguageNone, nullptr)); + EXPECT_EQ(lldb::eScriptLanguageNone, + Args::StringToScriptLanguage(llvm::StringRef("none"), + lldb::eScriptLanguagePython, nullptr)); + + EXPECT_EQ(lldb::eScriptLanguageDefault, + Args::StringToScriptLanguage(llvm::StringRef("default"), + lldb::eScriptLanguageNone, &success)); + EXPECT_TRUE(success); + EXPECT_EQ(lldb::eScriptLanguagePython, + Args::StringToScriptLanguage(llvm::StringRef("python"), + lldb::eScriptLanguageNone, &success)); + EXPECT_TRUE(success); + EXPECT_EQ(lldb::eScriptLanguageNone, + Args::StringToScriptLanguage(llvm::StringRef("none"), + lldb::eScriptLanguagePython, + &success)); + EXPECT_TRUE(success); + + EXPECT_EQ(lldb::eScriptLanguagePython, + Args::StringToScriptLanguage(llvm::StringRef("invalid"), + lldb::eScriptLanguagePython, + &success)); + EXPECT_FALSE(success); +} + +TEST(ArgsTest, StringToVersion) {} + +// Environment Variable Tests + +class EnvVarFixture: public ::testing::Test { +protected: + + void SetUp() { + args.AppendArgument(llvm::StringRef("Arg1=foo")); + args.AppendArgument(llvm::StringRef("Arg2")); + args.AppendArgument(llvm::StringRef("Arg3=bar")); + } + + size_t GetIndexForEnvVar(llvm::StringRef envvar_name) { + size_t argument_index = std::numeric_limits<size_t>::max(); + EXPECT_TRUE(args.ContainsEnvironmentVariable(envvar_name, + &argument_index)); + EXPECT_LT(argument_index, args.GetArgumentCount()); + return argument_index; + } + + Args args; +}; + + +TEST_F(EnvVarFixture, TestContainsEnvironmentVariableNoValue) { + EXPECT_TRUE(args.ContainsEnvironmentVariable(llvm::StringRef("Arg2"))); +} + +TEST_F(EnvVarFixture, TestContainsEnvironmentVariableWithValue) { + EXPECT_TRUE(args.ContainsEnvironmentVariable(llvm::StringRef("Arg3"))); +} + +TEST_F(EnvVarFixture, TestContainsEnvironmentVariableNonExistentVariable) { + auto nonexistent_envvar = llvm::StringRef("ThisEnvVarShouldNotExist"); + EXPECT_FALSE(args.ContainsEnvironmentVariable(nonexistent_envvar)); +} + +TEST_F(EnvVarFixture, TestReplaceEnvironmentVariableInitialNoValueWithNoValue) { + auto envvar_name = llvm::StringRef("Arg2"); + auto argument_index = GetIndexForEnvVar(envvar_name); + + args.AddOrReplaceEnvironmentVariable(envvar_name, llvm::StringRef("")); + EXPECT_TRUE(args.ContainsEnvironmentVariable(envvar_name)); + EXPECT_EQ(envvar_name, args.GetArgumentAtIndex(argument_index)); +} + +TEST_F(EnvVarFixture, TestReplaceEnvironmentVariableInitialNoValueWithValue) { + auto envvar_name = llvm::StringRef("Arg2"); + auto argument_index = GetIndexForEnvVar(envvar_name); + + auto new_value = llvm::StringRef("NewValue"); + args.AddOrReplaceEnvironmentVariable(envvar_name, new_value); + EXPECT_TRUE(args.ContainsEnvironmentVariable(envvar_name)); + + std::stringstream stream; + stream << envvar_name.str() << '=' << new_value.str(); + EXPECT_EQ(llvm::StringRef(stream.str()), + args.GetArgumentAtIndex(argument_index)); +} + +TEST_F(EnvVarFixture, TestReplaceEnvironmentVariableInitialValueWithNoValue) { + auto envvar_name = llvm::StringRef("Arg1"); + auto argument_index = GetIndexForEnvVar(envvar_name); + + args.AddOrReplaceEnvironmentVariable(envvar_name, llvm::StringRef("")); + EXPECT_TRUE(args.ContainsEnvironmentVariable(envvar_name)); + EXPECT_EQ(envvar_name, args.GetArgumentAtIndex(argument_index)); +} + +TEST_F(EnvVarFixture, TestReplaceEnvironmentVariableInitialValueWithValue) { + auto envvar_name = llvm::StringRef("Arg1"); + auto argument_index = GetIndexForEnvVar(envvar_name); + + auto new_value = llvm::StringRef("NewValue"); + args.AddOrReplaceEnvironmentVariable(envvar_name, new_value); + EXPECT_TRUE(args.ContainsEnvironmentVariable(envvar_name)); + + std::stringstream stream; + stream << envvar_name.str() << '=' << new_value.str(); + EXPECT_EQ(llvm::StringRef(stream.str()), + args.GetArgumentAtIndex(argument_index)); } diff --git a/unittests/Language/CMakeLists.txt b/unittests/Language/CMakeLists.txt new file mode 100644 index 000000000000..d5e17c1f2a19 --- /dev/null +++ b/unittests/Language/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(CPlusPlus) diff --git a/unittests/Language/CPlusPlus/CMakeLists.txt b/unittests/Language/CPlusPlus/CMakeLists.txt new file mode 100644 index 000000000000..6cad97cc6d3c --- /dev/null +++ b/unittests/Language/CPlusPlus/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_unittest(LanguageCPlusPlusTests + CPlusPlusLanguageTest.cpp + ) diff --git a/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp b/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp new file mode 100644 index 000000000000..26de31d52a33 --- /dev/null +++ b/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp @@ -0,0 +1,40 @@ +//===-- CPlusPlusLanguageTest.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" + +using namespace lldb_private; + +TEST(CPlusPlusLanguage, MethodName) { + struct TestCase { + std::string input; + std::string context, basename, arguments, qualifiers, scope_qualified_name; + }; + + TestCase test_cases[] = { + {"foo::bar(baz)", "foo", "bar", "(baz)", "", "foo::bar"}, + {"std::basic_ostream<char, std::char_traits<char> >& " + "std::operator<<<std::char_traits<char> >" + "(std::basic_ostream<char, std::char_traits<char> >&, char const*)", + "std", "operator<<<std::char_traits<char> >", + "(std::basic_ostream<char, std::char_traits<char> >&, char const*)", "", + "std::operator<<<std::char_traits<char> >"}}; + + for (const auto &test : test_cases) { + CPlusPlusLanguage::MethodName method(ConstString(test.input)); + EXPECT_TRUE(method.IsValid()); + EXPECT_EQ(test.context, method.GetContext()); + EXPECT_EQ(test.basename, method.GetBasename()); + EXPECT_EQ(test.arguments, method.GetArguments()); + EXPECT_EQ(test.qualifiers, method.GetQualifiers()); + EXPECT_EQ(test.scope_qualified_name, method.GetScopeQualifiedName()); + } +} diff --git a/unittests/Platform/CMakeLists.txt b/unittests/Platform/CMakeLists.txt new file mode 100644 index 000000000000..af1121dac7b6 --- /dev/null +++ b/unittests/Platform/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_unittest(LLDBPlatformTests + PlatformDarwinTest.cpp + ) diff --git a/unittests/Platform/PlatformDarwinTest.cpp b/unittests/Platform/PlatformDarwinTest.cpp new file mode 100644 index 000000000000..a16e2d99ede6 --- /dev/null +++ b/unittests/Platform/PlatformDarwinTest.cpp @@ -0,0 +1,56 @@ +//===-- PlatformDarwinTest.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "Plugins/Platform/MacOSX/PlatformDarwin.h" + +#include "llvm/ADT/StringRef.h" + +#include <tuple> + +using namespace lldb; +using namespace lldb_private; + +TEST(PlatformDarwinTest, TestParseVersionBuildDir) { + uint32_t A, B, C; + llvm::StringRef D; + + std::tie(A, B, C, D) = PlatformDarwin::ParseVersionBuildDir("1.2.3 (test1)"); + EXPECT_EQ(1u, A); + EXPECT_EQ(2u, B); + EXPECT_EQ(3u, C); + EXPECT_EQ("test1", D); + + std::tie(A, B, C, D) = PlatformDarwin::ParseVersionBuildDir("2.3 (test2)"); + EXPECT_EQ(2u, A); + EXPECT_EQ(3u, B); + EXPECT_EQ("test2", D); + + std::tie(A, B, C, D) = PlatformDarwin::ParseVersionBuildDir("3 (test3)"); + EXPECT_EQ(3u, A); + EXPECT_EQ("test3", D); + + std::tie(A, B, C, D) = PlatformDarwin::ParseVersionBuildDir("1.2.3 (test"); + EXPECT_EQ(1u, A); + EXPECT_EQ(2u, B); + EXPECT_EQ(3u, C); + EXPECT_EQ("test", D); + + std::tie(A, B, C, D) = PlatformDarwin::ParseVersionBuildDir("2.3.4 test"); + EXPECT_EQ(2u, A); + EXPECT_EQ(3u, B); + EXPECT_EQ(4u, C); + EXPECT_EQ("", D); + + std::tie(A, B, C, D) = PlatformDarwin::ParseVersionBuildDir("3.4.5"); + EXPECT_EQ(3u, A); + EXPECT_EQ(4u, B); + EXPECT_EQ(5u, C); +} diff --git a/unittests/Process/CMakeLists.txt b/unittests/Process/CMakeLists.txt new file mode 100644 index 000000000000..70f59382afa1 --- /dev/null +++ b/unittests/Process/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(gdb-remote) +add_subdirectory(minidump) diff --git a/unittests/Process/gdb-remote/CMakeLists.txt b/unittests/Process/gdb-remote/CMakeLists.txt new file mode 100644 index 000000000000..de4cac11b233 --- /dev/null +++ b/unittests/Process/gdb-remote/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_unittest(ProcessGdbRemoteTests + GDBRemoteClientBaseTest.cpp + GDBRemoteCommunicationClientTest.cpp + GDBRemoteTestUtils.cpp + ) diff --git a/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp b/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp new file mode 100644 index 000000000000..2cfd52f5767a --- /dev/null +++ b/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp @@ -0,0 +1,378 @@ +//===-- GDBRemoteClientBaseTest.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) +// Workaround for MSVC standard library bug, which fails to include <thread> +// when +// exceptions are disabled. +#include <eh.h> +#endif +#include <future> + +#include "GDBRemoteTestUtils.h" + +#include "Plugins/Process/Utility/LinuxSignals.h" +#include "Plugins/Process/gdb-remote/GDBRemoteClientBase.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h" +#include "lldb/Core/StreamGDBRemote.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private; +using namespace lldb; +typedef GDBRemoteCommunication::PacketResult PacketResult; + +namespace { + +struct MockDelegate : public GDBRemoteClientBase::ContinueDelegate { + std::string output; + std::string misc_data; + unsigned stop_reply_called = 0; + std::vector<std::string> structured_data_packets; + + void HandleAsyncStdout(llvm::StringRef out) { output += out; } + void HandleAsyncMisc(llvm::StringRef data) { misc_data += data; } + void HandleStopReply() { ++stop_reply_called; } + + void HandleAsyncStructuredDataPacket(llvm::StringRef data) { + structured_data_packets.push_back(data); + } +}; + +struct TestClient : public GDBRemoteClientBase { + TestClient() : GDBRemoteClientBase("test.client", "test.client.listener") { + m_send_acks = false; + } +}; + +struct ContinueFixture { + MockDelegate delegate; + TestClient client; + MockServer server; + ListenerSP listener_sp; + + ContinueFixture(); + + StateType SendCPacket(StringExtractorGDBRemote &response) { + return client.SendContinuePacketAndWaitForResponse(delegate, LinuxSignals(), + "c", response); + } + + void WaitForRunEvent() { + EventSP event_sp; + listener_sp->GetEventForBroadcasterWithType( + &client, TestClient::eBroadcastBitRunPacketSent, event_sp, llvm::None); + } +}; + +ContinueFixture::ContinueFixture() + : listener_sp(Listener::MakeListener("listener")) { + Connect(client, server); + listener_sp->StartListeningForEvents(&client, + TestClient::eBroadcastBitRunPacketSent); +} + +} // end anonymous namespace + +class GDBRemoteClientBaseTest : public GDBRemoteTest {}; + +TEST_F(GDBRemoteClientBaseTest, SendContinueAndWait) { + StringExtractorGDBRemote response; + ContinueFixture fix; + if (HasFailure()) + return; + + // Continue. The inferior will stop with a signal. + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, fix.SendCPacket(response)); + ASSERT_EQ("T01", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + + // Continue. The inferior will exit. + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("W01")); + ASSERT_EQ(eStateExited, fix.SendCPacket(response)); + ASSERT_EQ("W01", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + + // Continue. The inferior will get killed. + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("X01")); + ASSERT_EQ(eStateExited, fix.SendCPacket(response)); + ASSERT_EQ("X01", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueAndAsyncSignal) { + StringExtractorGDBRemote continue_response, response; + ContinueFixture fix; + if (HasFailure()) + return; + + // SendAsyncSignal should do nothing when we are not running. + ASSERT_FALSE(fix.client.SendAsyncSignal(0x47)); + + // Continue. After the run packet is sent, send an async signal. + std::future<StateType> continue_state = std::async( + std::launch::async, [&] { return fix.SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + fix.WaitForRunEvent(); + + std::future<bool> async_result = std::async( + std::launch::async, [&] { return fix.client.SendAsyncSignal(0x47); }); + + // First we'll get interrupted. + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("\x03", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); + + // Then we get the signal packet. + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("C47", response.GetStringRef()); + ASSERT_TRUE(async_result.get()); + + // And we report back a signal stop. + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T47")); + ASSERT_EQ(eStateStopped, continue_state.get()); + ASSERT_EQ("T47", continue_response.GetStringRef()); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueAndAsyncPacket) { + StringExtractorGDBRemote continue_response, async_response, response; + const bool send_async = true; + ContinueFixture fix; + if (HasFailure()) + return; + + // Continue. After the run packet is sent, send an async packet. + std::future<StateType> continue_state = std::async( + std::launch::async, [&] { return fix.SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + fix.WaitForRunEvent(); + + // Sending without async enabled should fail. + ASSERT_EQ( + PacketResult::ErrorSendFailed, + fix.client.SendPacketAndWaitForResponse("qTest1", response, !send_async)); + + std::future<PacketResult> async_result = std::async(std::launch::async, [&] { + return fix.client.SendPacketAndWaitForResponse("qTest2", async_response, + send_async); + }); + + // First we'll get interrupted. + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("\x03", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); + + // Then we get the async packet. + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("qTest2", response.GetStringRef()); + + // Send the response and receive it. + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("QTest2")); + ASSERT_EQ(PacketResult::Success, async_result.get()); + ASSERT_EQ("QTest2", async_response.GetStringRef()); + + // And we get resumed again. + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, continue_state.get()); + ASSERT_EQ("T01", continue_response.GetStringRef()); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueAndInterrupt) { + StringExtractorGDBRemote continue_response, response; + ContinueFixture fix; + if (HasFailure()) + return; + + // Interrupt should do nothing when we're not running. + ASSERT_FALSE(fix.client.Interrupt()); + + // Continue. After the run packet is sent, send an interrupt. + std::future<StateType> continue_state = std::async( + std::launch::async, [&] { return fix.SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + fix.WaitForRunEvent(); + + std::future<bool> async_result = + std::async(std::launch::async, [&] { return fix.client.Interrupt(); }); + + // We get interrupted. + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("\x03", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); + + // And that's it. + ASSERT_EQ(eStateStopped, continue_state.get()); + ASSERT_EQ("T13", continue_response.GetStringRef()); + ASSERT_TRUE(async_result.get()); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueAndLateInterrupt) { + StringExtractorGDBRemote continue_response, response; + ContinueFixture fix; + if (HasFailure()) + return; + + // Continue. After the run packet is sent, send an interrupt. + std::future<StateType> continue_state = std::async( + std::launch::async, [&] { return fix.SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + fix.WaitForRunEvent(); + + std::future<bool> async_result = + std::async(std::launch::async, [&] { return fix.client.Interrupt(); }); + + // However, the target stops due to a different reason than the original + // interrupt. + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("\x03", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, continue_state.get()); + ASSERT_EQ("T01", continue_response.GetStringRef()); + ASSERT_TRUE(async_result.get()); + + // The subsequent continue packet should work normally. + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, fix.SendCPacket(response)); + ASSERT_EQ("T01", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueAndInterrupt2PacketBug) { + StringExtractorGDBRemote continue_response, async_response, response; + const bool send_async = true; + ContinueFixture fix; + if (HasFailure()) + return; + + // Interrupt should do nothing when we're not running. + ASSERT_FALSE(fix.client.Interrupt()); + + // Continue. After the run packet is sent, send an async signal. + std::future<StateType> continue_state = std::async( + std::launch::async, [&] { return fix.SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + fix.WaitForRunEvent(); + + std::future<bool> interrupt_result = + std::async(std::launch::async, [&] { return fix.client.Interrupt(); }); + + // We get interrupted. We'll send two packets to simulate a buggy stub. + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("\x03", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); + + // We should stop. + ASSERT_EQ(eStateStopped, continue_state.get()); + ASSERT_EQ("T13", continue_response.GetStringRef()); + ASSERT_TRUE(interrupt_result.get()); + + // Packet stream should remain synchronized. + std::future<PacketResult> send_result = std::async(std::launch::async, [&] { + return fix.client.SendPacketAndWaitForResponse("qTest", async_response, + !send_async); + }); + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("qTest", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("QTest")); + ASSERT_EQ(PacketResult::Success, send_result.get()); + ASSERT_EQ("QTest", async_response.GetStringRef()); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueDelegateInterface) { + StringExtractorGDBRemote response; + ContinueFixture fix; + if (HasFailure()) + return; + + // Continue. We'll have the server send a bunch of async packets before it + // stops. + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("O4142")); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("Apro")); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("O4344")); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("Afile")); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, fix.SendCPacket(response)); + ASSERT_EQ("T01", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + + EXPECT_EQ("ABCD", fix.delegate.output); + EXPECT_EQ("profile", fix.delegate.misc_data); + EXPECT_EQ(1u, fix.delegate.stop_reply_called); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueDelegateStructuredDataReceipt) { + // Build the plain-text version of the JSON data we will have the + // server send. + const std::string json_payload = + "{ \"type\": \"MyFeatureType\", " + " \"elements\": [ \"entry1\", \"entry2\" ] }"; + const std::string json_packet = "JSON-async:" + json_payload; + + // Escape it properly for transit. + StreamGDBRemote stream; + stream.PutEscapedBytes(json_packet.c_str(), json_packet.length()); + stream.Flush(); + + // Set up the + StringExtractorGDBRemote response; + ContinueFixture fix; + if (HasFailure()) + return; + + // Send async structured data packet, then stop. + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket(stream.GetData())); + ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, fix.SendCPacket(response)); + ASSERT_EQ("T01", response.GetStringRef()); + ASSERT_EQ(1ul, fix.delegate.structured_data_packets.size()); + + // Verify the packet contents. It should have been unescaped upon packet + // reception. + ASSERT_EQ(json_packet, fix.delegate.structured_data_packets[0]); +} + +TEST_F(GDBRemoteClientBaseTest, InterruptNoResponse) { + StringExtractorGDBRemote continue_response, response; + ContinueFixture fix; + if (HasFailure()) + return; + + // Continue. After the run packet is sent, send an interrupt. + std::future<StateType> continue_state = std::async( + std::launch::async, [&] { return fix.SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + fix.WaitForRunEvent(); + + std::future<bool> async_result = + std::async(std::launch::async, [&] { return fix.client.Interrupt(); }); + + // We get interrupted, but we don't send a stop packet. + ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ("\x03", response.GetStringRef()); + + // The functions should still terminate (after a timeout). + ASSERT_TRUE(async_result.get()); + ASSERT_EQ(eStateInvalid, continue_state.get()); +} diff --git a/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp b/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp new file mode 100644 index 000000000000..84b354d75170 --- /dev/null +++ b/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp @@ -0,0 +1,307 @@ +//===-- GDBRemoteCommunicationClientTest.cpp --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) +// Workaround for MSVC standard library bug, which fails to include <thread> +// when +// exceptions are disabled. +#include <eh.h> +#endif +#include <future> + +#include "GDBRemoteTestUtils.h" + +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StructuredData.h" + +#include "llvm/ADT/ArrayRef.h" + +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private; +using namespace lldb; +using namespace llvm; + +namespace { + +typedef GDBRemoteCommunication::PacketResult PacketResult; + +struct TestClient : public GDBRemoteCommunicationClient { + TestClient() { m_send_acks = false; } +}; + +void Handle_QThreadSuffixSupported(MockServer &server, bool supported) { + StringExtractorGDBRemote request; + ASSERT_EQ(PacketResult::Success, server.GetPacket(request)); + ASSERT_EQ("QThreadSuffixSupported", request.GetStringRef()); + if (supported) + ASSERT_EQ(PacketResult::Success, server.SendOKResponse()); + else + ASSERT_EQ(PacketResult::Success, server.SendUnimplementedResponse(nullptr)); +} + +void HandlePacket(MockServer &server, StringRef expected, StringRef response) { + StringExtractorGDBRemote request; + ASSERT_EQ(PacketResult::Success, server.GetPacket(request)); + ASSERT_EQ(expected, request.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.SendPacket(response)); +} + +uint8_t all_registers[] = {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'}; +std::string all_registers_hex = "404142434445464748494a4b4c4d4e4f"; +uint8_t one_register[] = {'A', 'B', 'C', 'D'}; +std::string one_register_hex = "41424344"; + +} // end anonymous namespace + +class GDBRemoteCommunicationClientTest : public GDBRemoteTest {}; + +TEST_F(GDBRemoteCommunicationClientTest, WriteRegister) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + const lldb::tid_t tid = 0x47; + const uint32_t reg_num = 4; + std::future<bool> write_result = std::async(std::launch::async, [&] { + return client.WriteRegister(tid, reg_num, one_register); + }); + + Handle_QThreadSuffixSupported(server, true); + + HandlePacket(server, "P4=" + one_register_hex + ";thread:0047;", "OK"); + ASSERT_TRUE(write_result.get()); + + write_result = std::async(std::launch::async, [&] { + return client.WriteAllRegisters(tid, all_registers); + }); + + HandlePacket(server, "G" + all_registers_hex + ";thread:0047;", "OK"); + ASSERT_TRUE(write_result.get()); +} + +TEST_F(GDBRemoteCommunicationClientTest, WriteRegisterNoSuffix) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + const lldb::tid_t tid = 0x47; + const uint32_t reg_num = 4; + std::future<bool> write_result = std::async(std::launch::async, [&] { + return client.WriteRegister(tid, reg_num, one_register); + }); + + Handle_QThreadSuffixSupported(server, false); + HandlePacket(server, "Hg47", "OK"); + HandlePacket(server, "P4=" + one_register_hex, "OK"); + ASSERT_TRUE(write_result.get()); + + write_result = std::async(std::launch::async, [&] { + return client.WriteAllRegisters(tid, all_registers); + }); + + HandlePacket(server, "G" + all_registers_hex, "OK"); + ASSERT_TRUE(write_result.get()); +} + +TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + const lldb::tid_t tid = 0x47; + const uint32_t reg_num = 4; + std::future<bool> async_result = std::async( + std::launch::async, [&] { return client.GetpPacketSupported(tid); }); + Handle_QThreadSuffixSupported(server, true); + HandlePacket(server, "p0;thread:0047;", one_register_hex); + ASSERT_TRUE(async_result.get()); + + std::future<DataBufferSP> read_result = std::async( + std::launch::async, [&] { return client.ReadRegister(tid, reg_num); }); + HandlePacket(server, "p4;thread:0047;", "41424344"); + auto buffer_sp = read_result.get(); + ASSERT_TRUE(bool(buffer_sp)); + ASSERT_EQ(0, + memcmp(buffer_sp->GetBytes(), one_register, sizeof one_register)); + + read_result = std::async(std::launch::async, + [&] { return client.ReadAllRegisters(tid); }); + HandlePacket(server, "g;thread:0047;", all_registers_hex); + buffer_sp = read_result.get(); + ASSERT_TRUE(bool(buffer_sp)); + ASSERT_EQ(0, + memcmp(buffer_sp->GetBytes(), all_registers, sizeof all_registers)); +} + +TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + const lldb::tid_t tid = 0x47; + uint32_t save_id; + std::future<bool> async_result = std::async(std::launch::async, [&] { + return client.SaveRegisterState(tid, save_id); + }); + Handle_QThreadSuffixSupported(server, false); + HandlePacket(server, "Hg47", "OK"); + HandlePacket(server, "QSaveRegisterState", "1"); + ASSERT_TRUE(async_result.get()); + EXPECT_EQ(1u, save_id); + + async_result = std::async(std::launch::async, [&] { + return client.RestoreRegisterState(tid, save_id); + }); + HandlePacket(server, "QRestoreRegisterState:1", "OK"); + ASSERT_TRUE(async_result.get()); +} + +TEST_F(GDBRemoteCommunicationClientTest, SyncThreadState) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + const lldb::tid_t tid = 0x47; + std::future<bool> async_result = std::async( + std::launch::async, [&] { return client.SyncThreadState(tid); }); + HandlePacket(server, "qSyncThreadStateSupported", "OK"); + HandlePacket(server, "QSyncThreadState:0047;", "OK"); + ASSERT_TRUE(async_result.get()); +} + +TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + llvm::Triple triple("i386-pc-linux"); + + FileSpec file_specs[] = { + FileSpec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix), + FileSpec("/foo/baz.so", false, FileSpec::ePathSyntaxPosix)}; + std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result = + std::async(std::launch::async, + [&] { return client.GetModulesInfo(file_specs, triple); }); + HandlePacket( + server, "jModulesInfo:[" + R"({"file":"/foo/bar.so","triple":"i386-pc-linux"},)" + R"({"file":"/foo/baz.so","triple":"i386-pc-linux"}])", + R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" + R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])"); + + auto result = async_result.get(); + ASSERT_TRUE(result.hasValue()); + ASSERT_EQ(1u, result->size()); + EXPECT_EQ("/foo/bar.so", result.getValue()[0].GetFileSpec().GetPath()); + EXPECT_EQ(triple, result.getValue()[0].GetArchitecture().GetTriple()); + EXPECT_EQ(UUID("@ABCDEFGHIJKLMNO", 16), result.getValue()[0].GetUUID()); + EXPECT_EQ(0u, result.getValue()[0].GetObjectOffset()); + EXPECT_EQ(1234u, result.getValue()[0].GetObjectSize()); +} + +TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + llvm::Triple triple("i386-pc-linux"); + FileSpec file_spec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix); + + const char *invalid_responses[] = { + "OK", "E47", "[]", + // no UUID + R"([{"triple":"i386-pc-linux",)" + R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}])", + // no triple + R"([{"uuid":"404142434445464748494a4b4c4d4e4f",)" + R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}])", + // no file_path + R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" + R"("file_offset":0,"file_size":1234}])", + // no file_offset + R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" + R"("file_path":"/foo/bar.so","file_size":1234}])", + // no file_size + R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" + R"("file_path":"/foo/bar.so","file_offset":0}])", + }; + + for (const char *response : invalid_responses) { + std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result = + std::async(std::launch::async, + [&] { return client.GetModulesInfo(file_spec, triple); }); + HandlePacket( + server, + R"(jModulesInfo:[{"file":"/foo/bar.so","triple":"i386-pc-linux"}])", + response); + + ASSERT_FALSE(async_result.get().hasValue()) << "response was: " << response; + } +} + +TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + std::thread server_thread([&server] { + for (;;) { + StringExtractorGDBRemote request; + PacketResult result = server.GetPacket(request); + if (result == PacketResult::ErrorDisconnected) + return; + ASSERT_EQ(PacketResult::Success, result); + StringRef ref = request.GetStringRef(); + ASSERT_TRUE(ref.consume_front("qSpeedTest:response_size:")); + int size; + ASSERT_FALSE(ref.consumeInteger(10, size)) << "ref: " << ref; + std::string response(size, 'X'); + ASSERT_EQ(PacketResult::Success, server.SendPacket(response)); + } + }); + + StreamString ss; + client.TestPacketSpeed(10, 32, 32, 4096, true, ss); + client.Disconnect(); + server_thread.join(); + + auto object_sp = StructuredData::ParseJSON(ss.GetString()); + ASSERT_TRUE(bool(object_sp)); + auto dict_sp = object_sp->GetAsDictionary(); + ASSERT_TRUE(bool(dict_sp)); + + object_sp = dict_sp->GetValueForKey("packet_speeds"); + ASSERT_TRUE(bool(object_sp)); + dict_sp = object_sp->GetAsDictionary(); + ASSERT_TRUE(bool(dict_sp)); + + int num_packets; + ASSERT_TRUE(dict_sp->GetValueForKeyAsInteger("num_packets", num_packets)) + << ss.GetString(); + ASSERT_EQ(10, num_packets); +} diff --git a/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp b/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp new file mode 100644 index 000000000000..58cc9f50586a --- /dev/null +++ b/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp @@ -0,0 +1,69 @@ +//===-- GDBRemoteTestUtils.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) +// Workaround for MSVC standard library bug, which fails to include <thread> +// when +// exceptions are disabled. +#include <eh.h> +#endif + +#include "GDBRemoteTestUtils.h" + +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" + +#include <future> + +namespace lldb_private { +namespace process_gdb_remote { + +void GDBRemoteTest::SetUpTestCase() { +#if defined(_MSC_VER) + WSADATA data; + ::WSAStartup(MAKEWORD(2, 2), &data); +#endif +} + +void GDBRemoteTest::TearDownTestCase() { +#if defined(_MSC_VER) + ::WSACleanup(); +#endif +} + +void Connect(GDBRemoteCommunication &client, GDBRemoteCommunication &server) { + bool child_processes_inherit = false; + Error error; + TCPSocket listen_socket(child_processes_inherit, error); + ASSERT_FALSE(error.Fail()); + error = listen_socket.Listen("127.0.0.1:0", 5); + ASSERT_FALSE(error.Fail()); + + Socket *accept_socket; + std::future<Error> accept_error = std::async(std::launch::async, [&] { + return listen_socket.Accept("127.0.0.1:0", child_processes_inherit, + accept_socket); + }); + + char connect_remote_address[64]; + snprintf(connect_remote_address, sizeof(connect_remote_address), + "connect://localhost:%u", listen_socket.GetLocalPortNumber()); + + std::unique_ptr<ConnectionFileDescriptor> conn_ap( + new ConnectionFileDescriptor()); + ASSERT_EQ(conn_ap->Connect(connect_remote_address, nullptr), + lldb::eConnectionStatusSuccess); + + client.SetConnection(conn_ap.release()); + ASSERT_TRUE(accept_error.get().Success()); + server.SetConnection(new ConnectionFileDescriptor(accept_socket)); +} + +} // namespace process_gdb_remote +} // namespace lldb_private diff --git a/unittests/Process/gdb-remote/GDBRemoteTestUtils.h b/unittests/Process/gdb-remote/GDBRemoteTestUtils.h new file mode 100644 index 000000000000..d7700f23e5ab --- /dev/null +++ b/unittests/Process/gdb-remote/GDBRemoteTestUtils.h @@ -0,0 +1,51 @@ +//===-- GDBRemoteTestUtils.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef lldb_unittests_Process_gdb_remote_GDBRemoteTestUtils_h +#define lldb_unittests_Process_gdb_remote_GDBRemoteTestUtils_h + +#include "gtest/gtest.h" + +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h" + +namespace lldb_private { +namespace process_gdb_remote { + +class GDBRemoteTest : public testing::Test { +public: + static void SetUpTestCase(); + + static void TearDownTestCase(); +}; + +void Connect(GDBRemoteCommunication &client, GDBRemoteCommunication &server); + +struct MockServer : public GDBRemoteCommunicationServer { + MockServer() + : GDBRemoteCommunicationServer("mock-server", "mock-server.listener") { + m_send_acks = false; + } + + PacketResult SendPacket(llvm::StringRef payload) { + return GDBRemoteCommunicationServer::SendPacketNoLock(payload); + } + + PacketResult GetPacket(StringExtractorGDBRemote &response) { + const bool sync_on_timeout = false; + return WaitForPacketNoLock(response, std::chrono::seconds(1), + sync_on_timeout); + } + + using GDBRemoteCommunicationServer::SendOKResponse; + using GDBRemoteCommunicationServer::SendUnimplementedResponse; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // lldb_unittests_Process_gdb_remote_GDBRemoteTestUtils_h diff --git a/unittests/Process/minidump/CMakeLists.txt b/unittests/Process/minidump/CMakeLists.txt new file mode 100644 index 000000000000..10cb8c34f352 --- /dev/null +++ b/unittests/Process/minidump/CMakeLists.txt @@ -0,0 +1,12 @@ +add_lldb_unittest(LLDBMinidumpTests + MinidumpParserTest.cpp + ) + +set(test_inputs + linux-i386.dmp + linux-x86_64.dmp + linux-x86_64_not_crashed.dmp + fizzbuzz_no_heap.dmp + fizzbuzz_wow64.dmp) + +add_unittest_inputs(LLDBMinidumpTests "${test_inputs}") diff --git a/unittests/Process/minidump/Inputs/fizzbuzz_no_heap.dmp b/unittests/Process/minidump/Inputs/fizzbuzz_no_heap.dmp Binary files differnew file mode 100644 index 000000000000..19008c91fc3e --- /dev/null +++ b/unittests/Process/minidump/Inputs/fizzbuzz_no_heap.dmp diff --git a/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp b/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp Binary files differnew file mode 100644 index 000000000000..3d97186f2cd2 --- /dev/null +++ b/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp diff --git a/unittests/Process/minidump/Inputs/linux-i386.dmp b/unittests/Process/minidump/Inputs/linux-i386.dmp Binary files differnew file mode 100644 index 000000000000..946a7cf0355b --- /dev/null +++ b/unittests/Process/minidump/Inputs/linux-i386.dmp diff --git a/unittests/Process/minidump/Inputs/linux-x86_64.cpp b/unittests/Process/minidump/Inputs/linux-x86_64.cpp new file mode 100644 index 000000000000..827fe67b503b --- /dev/null +++ b/unittests/Process/minidump/Inputs/linux-x86_64.cpp @@ -0,0 +1,28 @@ +// Example source from breakpad's linux tutorial +// https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/linux_starter_guide.md + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +#include "client/linux/handler/exception_handler.h" + +static bool dumpCallback(const google_breakpad::MinidumpDescriptor &descriptor, + void *context, bool succeeded) { + printf("Dump path: %s\n", descriptor.path()); + return succeeded; +} + +void crash() { + volatile int *a = (int *)(NULL); + *a = 1; +} + +int main(int argc, char *argv[]) { + google_breakpad::MinidumpDescriptor descriptor("/tmp"); + google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, + true, -1); + printf("pid: %d\n", getpid()); + crash(); + return 0; +} diff --git a/unittests/Process/minidump/Inputs/linux-x86_64.dmp b/unittests/Process/minidump/Inputs/linux-x86_64.dmp Binary files differnew file mode 100644 index 000000000000..29a12d6a2ebc --- /dev/null +++ b/unittests/Process/minidump/Inputs/linux-x86_64.dmp diff --git a/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp b/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp Binary files differnew file mode 100644 index 000000000000..ad4b61a7bbb4 --- /dev/null +++ b/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp diff --git a/unittests/Process/minidump/MinidumpParserTest.cpp b/unittests/Process/minidump/MinidumpParserTest.cpp new file mode 100644 index 000000000000..83225e88ee03 --- /dev/null +++ b/unittests/Process/minidump/MinidumpParserTest.cpp @@ -0,0 +1,453 @@ +//===-- MinidumpTypesTest.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Project includes +#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" +#include "Plugins/Process/minidump/MinidumpParser.h" +#include "Plugins/Process/minidump/MinidumpTypes.h" +#include "Plugins/Process/minidump/RegisterContextMinidump_x86_32.h" +#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h" + +// Other libraries and framework includes +#include "gtest/gtest.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Target/MemoryRegionInfo.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +// C includes + +// C++ includes +#include <memory> + +extern const char *TestMainArgv0; + +using namespace lldb_private; +using namespace minidump; + +class MinidumpParserTest : public testing::Test { +public: + void SetUp() override { + llvm::StringRef dmp_folder = llvm::sys::path::parent_path(TestMainArgv0); + inputs_folder = dmp_folder; + llvm::sys::path::append(inputs_folder, "Inputs"); + } + + void SetUpData(const char *minidump_filename, size_t load_size = SIZE_MAX) { + llvm::SmallString<128> filename = inputs_folder; + llvm::sys::path::append(filename, minidump_filename); + FileSpec minidump_file(filename.c_str(), false); + lldb::DataBufferSP data_sp( + minidump_file.MemoryMapFileContents(0, load_size)); + llvm::Optional<MinidumpParser> optional_parser = + MinidumpParser::Create(data_sp); + ASSERT_TRUE(optional_parser.hasValue()); + parser.reset(new MinidumpParser(optional_parser.getValue())); + ASSERT_GT(parser->GetData().size(), 0UL); + } + + llvm::SmallString<128> inputs_folder; + std::unique_ptr<MinidumpParser> parser; +}; + +TEST_F(MinidumpParserTest, GetThreadsAndGetThreadContext) { + SetUpData("linux-x86_64.dmp"); + llvm::ArrayRef<MinidumpThread> thread_list; + + thread_list = parser->GetThreads(); + ASSERT_EQ(1UL, thread_list.size()); + + const MinidumpThread thread = thread_list[0]; + + EXPECT_EQ(16001UL, thread.thread_id); + + llvm::ArrayRef<uint8_t> context = parser->GetThreadContext(thread); + EXPECT_EQ(1232UL, context.size()); +} + +TEST_F(MinidumpParserTest, GetThreadsTruncatedFile) { + SetUpData("linux-x86_64.dmp", 200); + llvm::ArrayRef<MinidumpThread> thread_list; + + thread_list = parser->GetThreads(); + ASSERT_EQ(0UL, thread_list.size()); +} + +TEST_F(MinidumpParserTest, GetArchitecture) { + SetUpData("linux-x86_64.dmp"); + ASSERT_EQ(llvm::Triple::ArchType::x86_64, + parser->GetArchitecture().GetMachine()); + ASSERT_EQ(llvm::Triple::OSType::Linux, + parser->GetArchitecture().GetTriple().getOS()); +} + +TEST_F(MinidumpParserTest, GetMiscInfo) { + SetUpData("linux-x86_64.dmp"); + const MinidumpMiscInfo *misc_info = parser->GetMiscInfo(); + ASSERT_EQ(nullptr, misc_info); +} + +TEST_F(MinidumpParserTest, GetLinuxProcStatus) { + SetUpData("linux-x86_64.dmp"); + llvm::Optional<LinuxProcStatus> proc_status = parser->GetLinuxProcStatus(); + ASSERT_TRUE(proc_status.hasValue()); + lldb::pid_t pid = proc_status->GetPid(); + ASSERT_EQ(16001UL, pid); +} + +TEST_F(MinidumpParserTest, GetPid) { + SetUpData("linux-x86_64.dmp"); + llvm::Optional<lldb::pid_t> pid = parser->GetPid(); + ASSERT_TRUE(pid.hasValue()); + ASSERT_EQ(16001UL, pid.getValue()); +} + +TEST_F(MinidumpParserTest, GetModuleList) { + SetUpData("linux-x86_64.dmp"); + llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList(); + ASSERT_EQ(8UL, modules.size()); + std::string module_names[8] = { + "/usr/local/google/home/dvlahovski/projects/test_breakpad/a.out", + "/lib/x86_64-linux-gnu/libm-2.19.so", + "/lib/x86_64-linux-gnu/libc-2.19.so", + "/lib/x86_64-linux-gnu/libgcc_s.so.1", + "/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19", + "/lib/x86_64-linux-gnu/libpthread-2.19.so", + "/lib/x86_64-linux-gnu/ld-2.19.so", + "linux-gate.so", + }; + + for (int i = 0; i < 8; ++i) { + llvm::Optional<std::string> name = + parser->GetMinidumpString(modules[i].module_name_rva); + ASSERT_TRUE(name.hasValue()); + EXPECT_EQ(module_names[i], name.getValue()); + } +} + +TEST_F(MinidumpParserTest, GetFilteredModuleList) { + SetUpData("linux-x86_64_not_crashed.dmp"); + llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList(); + std::vector<const MinidumpModule *> filtered_modules = + parser->GetFilteredModuleList(); + EXPECT_EQ(10UL, modules.size()); + EXPECT_EQ(9UL, filtered_modules.size()); + // EXPECT_GT(modules.size(), filtered_modules.size()); + bool found = false; + for (size_t i = 0; i < filtered_modules.size(); ++i) { + llvm::Optional<std::string> name = + parser->GetMinidumpString(filtered_modules[i]->module_name_rva); + ASSERT_TRUE(name.hasValue()); + if (name.getValue() == "/tmp/test/linux-x86_64_not_crashed") { + ASSERT_FALSE(found) << "There should be only one module with this name " + "in the filtered module list"; + found = true; + ASSERT_EQ(0x400000UL, filtered_modules[i]->base_of_image); + } + } +} + +TEST_F(MinidumpParserTest, GetExceptionStream) { + SetUpData("linux-x86_64.dmp"); + const MinidumpExceptionStream *exception_stream = + parser->GetExceptionStream(); + ASSERT_NE(nullptr, exception_stream); + ASSERT_EQ(11UL, exception_stream->exception_record.exception_code); +} + +void check_mem_range_exists(std::unique_ptr<MinidumpParser> &parser, + const uint64_t range_start, + const uint64_t range_size) { + llvm::Optional<minidump::Range> range = parser->FindMemoryRange(range_start); + ASSERT_TRUE(range.hasValue()) << "There is no range containing this address"; + EXPECT_EQ(range_start, range->start); + EXPECT_EQ(range_start + range_size, range->start + range->range_ref.size()); +} + +TEST_F(MinidumpParserTest, FindMemoryRange) { + SetUpData("linux-x86_64.dmp"); + // There are two memory ranges in the file (size is in bytes, decimal): + // 1) 0x401d46 256 + // 2) 0x7ffceb34a000 12288 + EXPECT_FALSE(parser->FindMemoryRange(0x00).hasValue()); + EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue()); + + check_mem_range_exists(parser, 0x401d46, 256); + EXPECT_FALSE(parser->FindMemoryRange(0x401d46 + 256).hasValue()); + + check_mem_range_exists(parser, 0x7ffceb34a000, 12288); + EXPECT_FALSE(parser->FindMemoryRange(0x7ffceb34a000 + 12288).hasValue()); +} + +TEST_F(MinidumpParserTest, GetMemory) { + SetUpData("linux-x86_64.dmp"); + + EXPECT_EQ(128UL, parser->GetMemory(0x401d46, 128).size()); + EXPECT_EQ(256UL, parser->GetMemory(0x401d46, 512).size()); + + EXPECT_EQ(12288UL, parser->GetMemory(0x7ffceb34a000, 12288).size()); + EXPECT_EQ(1024UL, parser->GetMemory(0x7ffceb34a000, 1024).size()); + + EXPECT_TRUE(parser->GetMemory(0x500000, 512).empty()); +} + +TEST_F(MinidumpParserTest, FindMemoryRangeWithFullMemoryMinidump) { + SetUpData("fizzbuzz_wow64.dmp"); + + // There are a lot of ranges in the file, just testing with some of them + EXPECT_FALSE(parser->FindMemoryRange(0x00).hasValue()); + EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue()); + check_mem_range_exists(parser, 0x10000, 65536); // first range + check_mem_range_exists(parser, 0x40000, 4096); + EXPECT_FALSE(parser->FindMemoryRange(0x40000 + 4096).hasValue()); + check_mem_range_exists(parser, 0x77c12000, 8192); + check_mem_range_exists(parser, 0x7ffe0000, 4096); // last range + EXPECT_FALSE(parser->FindMemoryRange(0x7ffe0000 + 4096).hasValue()); +} + +void check_region_info(std::unique_ptr<MinidumpParser> &parser, + const uint64_t addr, MemoryRegionInfo::OptionalBool read, + MemoryRegionInfo::OptionalBool write, + MemoryRegionInfo::OptionalBool exec) { + auto range_info = parser->GetMemoryRegionInfo(addr); + ASSERT_TRUE(range_info.hasValue()); + EXPECT_EQ(read, range_info->GetReadable()); + EXPECT_EQ(write, range_info->GetWritable()); + EXPECT_EQ(exec, range_info->GetExecutable()); +} + +TEST_F(MinidumpParserTest, GetMemoryRegionInfo) { + SetUpData("fizzbuzz_wow64.dmp"); + + const auto yes = MemoryRegionInfo::eYes; + const auto no = MemoryRegionInfo::eNo; + + check_region_info(parser, 0x00000, no, no, no); + check_region_info(parser, 0x10000, yes, yes, no); + check_region_info(parser, 0x20000, yes, yes, no); + check_region_info(parser, 0x30000, yes, yes, no); + check_region_info(parser, 0x31000, no, no, no); + check_region_info(parser, 0x40000, yes, no, no); +} + +// Windows Minidump tests +// fizzbuzz_no_heap.dmp is copied from the WinMiniDump tests +TEST_F(MinidumpParserTest, GetArchitectureWindows) { + SetUpData("fizzbuzz_no_heap.dmp"); + ASSERT_EQ(llvm::Triple::ArchType::x86, + parser->GetArchitecture().GetMachine()); + ASSERT_EQ(llvm::Triple::OSType::Win32, + parser->GetArchitecture().GetTriple().getOS()); +} + +TEST_F(MinidumpParserTest, GetLinuxProcStatusWindows) { + SetUpData("fizzbuzz_no_heap.dmp"); + llvm::Optional<LinuxProcStatus> proc_status = parser->GetLinuxProcStatus(); + ASSERT_FALSE(proc_status.hasValue()); +} + +TEST_F(MinidumpParserTest, GetMiscInfoWindows) { + SetUpData("fizzbuzz_no_heap.dmp"); + const MinidumpMiscInfo *misc_info = parser->GetMiscInfo(); + ASSERT_NE(nullptr, misc_info); + llvm::Optional<lldb::pid_t> pid = misc_info->GetPid(); + ASSERT_TRUE(pid.hasValue()); + ASSERT_EQ(4440UL, pid.getValue()); +} + +TEST_F(MinidumpParserTest, GetPidWindows) { + SetUpData("fizzbuzz_no_heap.dmp"); + llvm::Optional<lldb::pid_t> pid = parser->GetPid(); + ASSERT_TRUE(pid.hasValue()); + ASSERT_EQ(4440UL, pid.getValue()); +} + +// wow64 +TEST_F(MinidumpParserTest, GetPidWow64) { + SetUpData("fizzbuzz_wow64.dmp"); + llvm::Optional<lldb::pid_t> pid = parser->GetPid(); + ASSERT_TRUE(pid.hasValue()); + ASSERT_EQ(7836UL, pid.getValue()); +} + +TEST_F(MinidumpParserTest, GetModuleListWow64) { + SetUpData("fizzbuzz_wow64.dmp"); + llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList(); + ASSERT_EQ(16UL, modules.size()); + std::string module_names[16] = { + R"(D:\src\llvm\llvm\tools\lldb\packages\Python\lldbsuite\test\functionalities\postmortem\wow64_minidump\fizzbuzz.exe)", + R"(C:\Windows\System32\ntdll.dll)", + R"(C:\Windows\System32\wow64.dll)", + R"(C:\Windows\System32\wow64win.dll)", + R"(C:\Windows\System32\wow64cpu.dll)", + R"(D:\src\llvm\llvm\tools\lldb\packages\Python\lldbsuite\test\functionalities\postmortem\wow64_minidump\fizzbuzz.exe)", + R"(C:\Windows\SysWOW64\ntdll.dll)", + R"(C:\Windows\SysWOW64\kernel32.dll)", + R"(C:\Windows\SysWOW64\KERNELBASE.dll)", + R"(C:\Windows\SysWOW64\advapi32.dll)", + R"(C:\Windows\SysWOW64\msvcrt.dll)", + R"(C:\Windows\SysWOW64\sechost.dll)", + R"(C:\Windows\SysWOW64\rpcrt4.dll)", + R"(C:\Windows\SysWOW64\sspicli.dll)", + R"(C:\Windows\SysWOW64\CRYPTBASE.dll)", + R"(C:\Windows\System32\api-ms-win-core-synch-l1-2-0.DLL)", + }; + + for (int i = 0; i < 16; ++i) { + llvm::Optional<std::string> name = + parser->GetMinidumpString(modules[i].module_name_rva); + ASSERT_TRUE(name.hasValue()); + EXPECT_EQ(module_names[i], name.getValue()); + } +} + +// Register tests +#define REG_VAL32(x) *(reinterpret_cast<uint32_t *>(x)) +#define REG_VAL64(x) *(reinterpret_cast<uint64_t *>(x)) + +TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_32) { + SetUpData("linux-i386.dmp"); + llvm::ArrayRef<MinidumpThread> thread_list = parser->GetThreads(); + const MinidumpThread thread = thread_list[0]; + llvm::ArrayRef<uint8_t> registers(parser->GetThreadContext(thread)); + + ArchSpec arch = parser->GetArchitecture(); + RegisterInfoInterface *reg_interface = new RegisterContextLinux_i386(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContext_x86_32(registers, reg_interface); + ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize()); + + const RegisterInfo *reg_info = reg_interface->GetRegisterInfo(); + + std::map<uint64_t, uint32_t> reg_values; + + reg_values[lldb_eax_i386] = 0x00000000; + reg_values[lldb_ebx_i386] = 0xf7778000; + reg_values[lldb_ecx_i386] = 0x00000001; + reg_values[lldb_edx_i386] = 0xff9dd4a3; + reg_values[lldb_edi_i386] = 0x080482a8; + reg_values[lldb_esi_i386] = 0xff9dd55c; + reg_values[lldb_ebp_i386] = 0xff9dd53c; + reg_values[lldb_esp_i386] = 0xff9dd52c; + reg_values[lldb_eip_i386] = 0x080482a0; + reg_values[lldb_eflags_i386] = 0x00010282; + reg_values[lldb_cs_i386] = 0x00000023; + reg_values[lldb_fs_i386] = 0x00000000; + reg_values[lldb_gs_i386] = 0x00000063; + reg_values[lldb_ss_i386] = 0x0000002b; + reg_values[lldb_ds_i386] = 0x0000002b; + reg_values[lldb_es_i386] = 0x0000002b; + + for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount(); + ++reg_index) { + if (reg_values.find(reg_index) != reg_values.end()) { + EXPECT_EQ(reg_values[reg_index], + REG_VAL32(buf->GetBytes() + reg_info[reg_index].byte_offset)); + } + } +} + +TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_64) { + SetUpData("linux-x86_64.dmp"); + llvm::ArrayRef<MinidumpThread> thread_list = parser->GetThreads(); + const MinidumpThread thread = thread_list[0]; + llvm::ArrayRef<uint8_t> registers(parser->GetThreadContext(thread)); + + ArchSpec arch = parser->GetArchitecture(); + RegisterInfoInterface *reg_interface = new RegisterContextLinux_x86_64(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContext_x86_64(registers, reg_interface); + ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize()); + + const RegisterInfo *reg_info = reg_interface->GetRegisterInfo(); + + std::map<uint64_t, uint64_t> reg_values; + + reg_values[lldb_rax_x86_64] = 0x0000000000000000; + reg_values[lldb_rbx_x86_64] = 0x0000000000000000; + reg_values[lldb_rcx_x86_64] = 0x0000000000000010; + reg_values[lldb_rdx_x86_64] = 0x0000000000000000; + reg_values[lldb_rdi_x86_64] = 0x00007ffceb349cf0; + reg_values[lldb_rsi_x86_64] = 0x0000000000000000; + reg_values[lldb_rbp_x86_64] = 0x00007ffceb34a210; + reg_values[lldb_rsp_x86_64] = 0x00007ffceb34a210; + reg_values[lldb_r8_x86_64] = 0x00007fe9bc1aa9c0; + reg_values[lldb_r9_x86_64] = 0x0000000000000000; + reg_values[lldb_r10_x86_64] = 0x00007fe9bc3f16a0; + reg_values[lldb_r11_x86_64] = 0x0000000000000246; + reg_values[lldb_r12_x86_64] = 0x0000000000401c92; + reg_values[lldb_r13_x86_64] = 0x00007ffceb34a430; + reg_values[lldb_r14_x86_64] = 0x0000000000000000; + reg_values[lldb_r15_x86_64] = 0x0000000000000000; + reg_values[lldb_rip_x86_64] = 0x0000000000401dc6; + reg_values[lldb_rflags_x86_64] = 0x0000000000010206; + reg_values[lldb_cs_x86_64] = 0x0000000000000033; + reg_values[lldb_fs_x86_64] = 0x0000000000000000; + reg_values[lldb_gs_x86_64] = 0x0000000000000000; + reg_values[lldb_ss_x86_64] = 0x0000000000000000; + reg_values[lldb_ds_x86_64] = 0x0000000000000000; + reg_values[lldb_es_x86_64] = 0x0000000000000000; + + for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount(); + ++reg_index) { + if (reg_values.find(reg_index) != reg_values.end()) { + EXPECT_EQ(reg_values[reg_index], + REG_VAL64(buf->GetBytes() + reg_info[reg_index].byte_offset)); + } + } +} + +TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_32_wow64) { + SetUpData("fizzbuzz_wow64.dmp"); + llvm::ArrayRef<MinidumpThread> thread_list = parser->GetThreads(); + const MinidumpThread thread = thread_list[0]; + llvm::ArrayRef<uint8_t> registers(parser->GetThreadContextWow64(thread)); + + ArchSpec arch = parser->GetArchitecture(); + RegisterInfoInterface *reg_interface = new RegisterContextLinux_i386(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContext_x86_32(registers, reg_interface); + ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize()); + + const RegisterInfo *reg_info = reg_interface->GetRegisterInfo(); + + std::map<uint64_t, uint32_t> reg_values; + + reg_values[lldb_eax_i386] = 0x00000000; + reg_values[lldb_ebx_i386] = 0x0037f608; + reg_values[lldb_ecx_i386] = 0x00e61578; + reg_values[lldb_edx_i386] = 0x00000008; + reg_values[lldb_edi_i386] = 0x00000000; + reg_values[lldb_esi_i386] = 0x00000002; + reg_values[lldb_ebp_i386] = 0x0037f654; + reg_values[lldb_esp_i386] = 0x0037f5b8; + reg_values[lldb_eip_i386] = 0x77ce01fd; + reg_values[lldb_eflags_i386] = 0x00000246; + reg_values[lldb_cs_i386] = 0x00000023; + reg_values[lldb_fs_i386] = 0x00000053; + reg_values[lldb_gs_i386] = 0x0000002b; + reg_values[lldb_ss_i386] = 0x0000002b; + reg_values[lldb_ds_i386] = 0x0000002b; + reg_values[lldb_es_i386] = 0x0000002b; + + for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount(); + ++reg_index) { + if (reg_values.find(reg_index) != reg_values.end()) { + EXPECT_EQ(reg_values[reg_index], + REG_VAL32(buf->GetBytes() + reg_info[reg_index].byte_offset)); + } + } +}
\ No newline at end of file diff --git a/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp b/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp index c239a1601b32..192e64d07034 100644 --- a/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp +++ b/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp @@ -7,591 +7,593 @@ // //===----------------------------------------------------------------------===// +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" #include "gtest/gtest.h" +#include "Plugins/ScriptInterpreter/Python/PythonDataObjects.h" +#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h" #include "lldb/Host/File.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" -#include "Plugins/ScriptInterpreter/Python/lldb-python.h" -#include "Plugins/ScriptInterpreter/Python/PythonDataObjects.h" -#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h" #include "PythonTestSuite.h" using namespace lldb_private; -class PythonDataObjectsTest : public PythonTestSuite -{ - public: - void - SetUp() override - { - PythonTestSuite::SetUp(); - - PythonString sys_module("sys"); - m_sys_module.Reset(PyRefType::Owned, PyImport_Import(sys_module.get())); - m_main_module = PythonModule::MainModule(); - m_builtins_module = PythonModule::BuiltinsModule(); - } - - void - TearDown() override - { - m_sys_module.Reset(); - m_main_module.Reset(); - m_builtins_module.Reset(); - - PythonTestSuite::TearDown(); - } - - protected: - PythonModule m_sys_module; - PythonModule m_main_module; - PythonModule m_builtins_module; +class PythonDataObjectsTest : public PythonTestSuite { +public: + void SetUp() override { + PythonTestSuite::SetUp(); + + PythonString sys_module("sys"); + m_sys_module.Reset(PyRefType::Owned, PyImport_Import(sys_module.get())); + m_main_module = PythonModule::MainModule(); + m_builtins_module = PythonModule::BuiltinsModule(); + } + + void TearDown() override { + m_sys_module.Reset(); + m_main_module.Reset(); + m_builtins_module.Reset(); + + PythonTestSuite::TearDown(); + } + +protected: + PythonModule m_sys_module; + PythonModule m_main_module; + PythonModule m_builtins_module; }; -TEST_F(PythonDataObjectsTest, TestOwnedReferences) -{ - // After creating a new object, the refcount should be >= 1 - PyObject *obj = PyLong_FromLong(3); - Py_ssize_t original_refcnt = obj->ob_refcnt; - EXPECT_LE(1, original_refcnt); +TEST_F(PythonDataObjectsTest, TestOwnedReferences) { + // After creating a new object, the refcount should be >= 1 + PyObject *obj = PyLong_FromLong(3); + Py_ssize_t original_refcnt = obj->ob_refcnt; + EXPECT_LE(1, original_refcnt); - // If we take an owned reference, the refcount should be the same - PythonObject owned_long(PyRefType::Owned, obj); - EXPECT_EQ(original_refcnt, owned_long.get()->ob_refcnt); + // If we take an owned reference, the refcount should be the same + PythonObject owned_long(PyRefType::Owned, obj); + EXPECT_EQ(original_refcnt, owned_long.get()->ob_refcnt); - // Take another reference and verify that the refcount increases by 1 - PythonObject strong_ref(owned_long); - EXPECT_EQ(original_refcnt + 1, strong_ref.get()->ob_refcnt); + // Take another reference and verify that the refcount increases by 1 + PythonObject strong_ref(owned_long); + EXPECT_EQ(original_refcnt + 1, strong_ref.get()->ob_refcnt); - // If we reset the first one, the refcount should be the original value. - owned_long.Reset(); - EXPECT_EQ(original_refcnt, strong_ref.get()->ob_refcnt); + // If we reset the first one, the refcount should be the original value. + owned_long.Reset(); + EXPECT_EQ(original_refcnt, strong_ref.get()->ob_refcnt); } -TEST_F(PythonDataObjectsTest, TestResetting) -{ - PythonDictionary dict(PyInitialValue::Empty); +TEST_F(PythonDataObjectsTest, TestResetting) { + PythonDictionary dict(PyInitialValue::Empty); - PyObject *new_dict = PyDict_New(); - dict.Reset(PyRefType::Owned, new_dict); - EXPECT_EQ(new_dict, dict.get()); + PyObject *new_dict = PyDict_New(); + dict.Reset(PyRefType::Owned, new_dict); + EXPECT_EQ(new_dict, dict.get()); - dict.Reset(PyRefType::Owned, nullptr); - EXPECT_EQ(nullptr, dict.get()); + dict.Reset(PyRefType::Owned, nullptr); + EXPECT_EQ(nullptr, dict.get()); - dict.Reset(PyRefType::Owned, PyDict_New()); - EXPECT_NE(nullptr, dict.get()); - dict.Reset(); - EXPECT_EQ(nullptr, dict.get()); + dict.Reset(PyRefType::Owned, PyDict_New()); + EXPECT_NE(nullptr, dict.get()); + dict.Reset(); + EXPECT_EQ(nullptr, dict.get()); } -TEST_F(PythonDataObjectsTest, TestBorrowedReferences) -{ - PythonInteger long_value(PyRefType::Owned, PyLong_FromLong(3)); - Py_ssize_t original_refcnt = long_value.get()->ob_refcnt; - EXPECT_LE(1, original_refcnt); +TEST_F(PythonDataObjectsTest, TestBorrowedReferences) { + PythonInteger long_value(PyRefType::Owned, PyLong_FromLong(3)); + Py_ssize_t original_refcnt = long_value.get()->ob_refcnt; + EXPECT_LE(1, original_refcnt); - PythonInteger borrowed_long(PyRefType::Borrowed, long_value.get()); - EXPECT_EQ(original_refcnt + 1, borrowed_long.get()->ob_refcnt); + PythonInteger borrowed_long(PyRefType::Borrowed, long_value.get()); + EXPECT_EQ(original_refcnt + 1, borrowed_long.get()->ob_refcnt); } -TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionNoDot) -{ - PythonObject sys_module = m_main_module.ResolveName("sys"); - EXPECT_EQ(m_sys_module.get(), sys_module.get()); - EXPECT_TRUE(sys_module.IsAllocated()); - EXPECT_TRUE(PythonModule::Check(sys_module.get())); +TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionNoDot) { + PythonObject sys_module = m_main_module.ResolveName("sys"); + EXPECT_EQ(m_sys_module.get(), sys_module.get()); + EXPECT_TRUE(sys_module.IsAllocated()); + EXPECT_TRUE(PythonModule::Check(sys_module.get())); } -TEST_F(PythonDataObjectsTest, TestModuleNameResolutionNoDot) -{ - PythonObject sys_path = m_sys_module.ResolveName("path"); - PythonObject sys_version_info = m_sys_module.ResolveName("version_info"); - EXPECT_TRUE(sys_path.IsAllocated()); - EXPECT_TRUE(sys_version_info.IsAllocated()); +TEST_F(PythonDataObjectsTest, TestModuleNameResolutionNoDot) { + PythonObject sys_path = m_sys_module.ResolveName("path"); + PythonObject sys_version_info = m_sys_module.ResolveName("version_info"); + EXPECT_TRUE(sys_path.IsAllocated()); + EXPECT_TRUE(sys_version_info.IsAllocated()); - EXPECT_TRUE(PythonList::Check(sys_path.get())); + EXPECT_TRUE(PythonList::Check(sys_path.get())); } -TEST_F(PythonDataObjectsTest, TestTypeNameResolutionNoDot) -{ - PythonObject sys_version_info = m_sys_module.ResolveName("version_info"); +TEST_F(PythonDataObjectsTest, TestTypeNameResolutionNoDot) { + PythonObject sys_version_info = m_sys_module.ResolveName("version_info"); - PythonObject version_info_type(PyRefType::Owned, PyObject_Type(sys_version_info.get())); - EXPECT_TRUE(version_info_type.IsAllocated()); - PythonObject major_version_field = version_info_type.ResolveName("major"); - EXPECT_TRUE(major_version_field.IsAllocated()); + PythonObject version_info_type(PyRefType::Owned, + PyObject_Type(sys_version_info.get())); + EXPECT_TRUE(version_info_type.IsAllocated()); + PythonObject major_version_field = version_info_type.ResolveName("major"); + EXPECT_TRUE(major_version_field.IsAllocated()); } -TEST_F(PythonDataObjectsTest, TestInstanceNameResolutionNoDot) -{ - PythonObject sys_version_info = m_sys_module.ResolveName("version_info"); - PythonObject major_version_field = sys_version_info.ResolveName("major"); - PythonObject minor_version_field = sys_version_info.ResolveName("minor"); +TEST_F(PythonDataObjectsTest, TestInstanceNameResolutionNoDot) { + PythonObject sys_version_info = m_sys_module.ResolveName("version_info"); + PythonObject major_version_field = sys_version_info.ResolveName("major"); + PythonObject minor_version_field = sys_version_info.ResolveName("minor"); - EXPECT_TRUE(major_version_field.IsAllocated()); - EXPECT_TRUE(minor_version_field.IsAllocated()); + EXPECT_TRUE(major_version_field.IsAllocated()); + EXPECT_TRUE(minor_version_field.IsAllocated()); - PythonInteger major_version_value = major_version_field.AsType<PythonInteger>(); - PythonInteger minor_version_value = minor_version_field.AsType<PythonInteger>(); + PythonInteger major_version_value = + major_version_field.AsType<PythonInteger>(); + PythonInteger minor_version_value = + minor_version_field.AsType<PythonInteger>(); - EXPECT_EQ(PY_MAJOR_VERSION, major_version_value.GetInteger()); - EXPECT_EQ(PY_MINOR_VERSION, minor_version_value.GetInteger()); + EXPECT_EQ(PY_MAJOR_VERSION, major_version_value.GetInteger()); + EXPECT_EQ(PY_MINOR_VERSION, minor_version_value.GetInteger()); } -TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionWithDot) -{ - PythonObject sys_path = m_main_module.ResolveName("sys.path"); - EXPECT_TRUE(sys_path.IsAllocated()); - EXPECT_TRUE(PythonList::Check(sys_path.get())); +TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionWithDot) { + PythonObject sys_path = m_main_module.ResolveName("sys.path"); + EXPECT_TRUE(sys_path.IsAllocated()); + EXPECT_TRUE(PythonList::Check(sys_path.get())); - PythonInteger version_major = m_main_module.ResolveName( - "sys.version_info.major").AsType<PythonInteger>(); - PythonInteger version_minor = m_main_module.ResolveName( - "sys.version_info.minor").AsType<PythonInteger>(); - EXPECT_TRUE(version_major.IsAllocated()); - EXPECT_TRUE(version_minor.IsAllocated()); - EXPECT_EQ(PY_MAJOR_VERSION, version_major.GetInteger()); - EXPECT_EQ(PY_MINOR_VERSION, version_minor.GetInteger()); + PythonInteger version_major = + m_main_module.ResolveName("sys.version_info.major") + .AsType<PythonInteger>(); + PythonInteger version_minor = + m_main_module.ResolveName("sys.version_info.minor") + .AsType<PythonInteger>(); + EXPECT_TRUE(version_major.IsAllocated()); + EXPECT_TRUE(version_minor.IsAllocated()); + EXPECT_EQ(PY_MAJOR_VERSION, version_major.GetInteger()); + EXPECT_EQ(PY_MINOR_VERSION, version_minor.GetInteger()); } -TEST_F(PythonDataObjectsTest, TestDictionaryResolutionWithDot) -{ - // Make up a custom dictionary with "sys" pointing to the `sys` module. - PythonDictionary dict(PyInitialValue::Empty); - dict.SetItemForKey(PythonString("sys"), m_sys_module); +TEST_F(PythonDataObjectsTest, TestDictionaryResolutionWithDot) { + // Make up a custom dictionary with "sys" pointing to the `sys` module. + PythonDictionary dict(PyInitialValue::Empty); + dict.SetItemForKey(PythonString("sys"), m_sys_module); - // Now use that dictionary to resolve `sys.version_info.major` - PythonInteger version_major = PythonObject::ResolveNameWithDictionary( - "sys.version_info.major", dict).AsType<PythonInteger>(); - PythonInteger version_minor = PythonObject::ResolveNameWithDictionary( - "sys.version_info.minor", dict).AsType<PythonInteger>(); - EXPECT_EQ(PY_MAJOR_VERSION, version_major.GetInteger()); - EXPECT_EQ(PY_MINOR_VERSION, version_minor.GetInteger()); + // Now use that dictionary to resolve `sys.version_info.major` + PythonInteger version_major = + PythonObject::ResolveNameWithDictionary("sys.version_info.major", dict) + .AsType<PythonInteger>(); + PythonInteger version_minor = + PythonObject::ResolveNameWithDictionary("sys.version_info.minor", dict) + .AsType<PythonInteger>(); + EXPECT_EQ(PY_MAJOR_VERSION, version_major.GetInteger()); + EXPECT_EQ(PY_MINOR_VERSION, version_minor.GetInteger()); } -TEST_F(PythonDataObjectsTest, TestPythonInteger) -{ +TEST_F(PythonDataObjectsTest, TestPythonInteger) { // Test that integers behave correctly when wrapped by a PythonInteger. #if PY_MAJOR_VERSION < 3 - // Verify that `PythonInt` works correctly when given a PyInt object. - // Note that PyInt doesn't exist in Python 3.x, so this is only for 2.x - PyObject *py_int = PyInt_FromLong(12); - EXPECT_TRUE(PythonInteger::Check(py_int)); - PythonInteger python_int(PyRefType::Owned, py_int); - - EXPECT_EQ(PyObjectType::Integer, python_int.GetObjectType()); - EXPECT_EQ(12, python_int.GetInteger()); + // Verify that `PythonInt` works correctly when given a PyInt object. + // Note that PyInt doesn't exist in Python 3.x, so this is only for 2.x + PyObject *py_int = PyInt_FromLong(12); + EXPECT_TRUE(PythonInteger::Check(py_int)); + PythonInteger python_int(PyRefType::Owned, py_int); + + EXPECT_EQ(PyObjectType::Integer, python_int.GetObjectType()); + EXPECT_EQ(12, python_int.GetInteger()); #endif - // Verify that `PythonInteger` works correctly when given a PyLong object. - PyObject *py_long = PyLong_FromLong(12); - EXPECT_TRUE(PythonInteger::Check(py_long)); - PythonInteger python_long(PyRefType::Owned, py_long); - EXPECT_EQ(PyObjectType::Integer, python_long.GetObjectType()); + // Verify that `PythonInteger` works correctly when given a PyLong object. + PyObject *py_long = PyLong_FromLong(12); + EXPECT_TRUE(PythonInteger::Check(py_long)); + PythonInteger python_long(PyRefType::Owned, py_long); + EXPECT_EQ(PyObjectType::Integer, python_long.GetObjectType()); - // Verify that you can reset the value and that it is reflected properly. - python_long.SetInteger(40); - EXPECT_EQ(40, python_long.GetInteger()); + // Verify that you can reset the value and that it is reflected properly. + python_long.SetInteger(40); + EXPECT_EQ(40, python_long.GetInteger()); - // Test that creating a `PythonInteger` object works correctly with the - // int constructor. - PythonInteger constructed_int(7); - EXPECT_EQ(7, constructed_int.GetInteger()); + // Test that creating a `PythonInteger` object works correctly with the + // int constructor. + PythonInteger constructed_int(7); + EXPECT_EQ(7, constructed_int.GetInteger()); } -TEST_F(PythonDataObjectsTest, TestPythonBytes) -{ - static const char *test_bytes = "PythonDataObjectsTest::TestPythonBytes"; - PyObject *py_bytes = PyBytes_FromString(test_bytes); - EXPECT_TRUE(PythonBytes::Check(py_bytes)); - PythonBytes python_bytes(PyRefType::Owned, py_bytes); +TEST_F(PythonDataObjectsTest, TestPythonBytes) { + static const char *test_bytes = "PythonDataObjectsTest::TestPythonBytes"; + PyObject *py_bytes = PyBytes_FromString(test_bytes); + EXPECT_TRUE(PythonBytes::Check(py_bytes)); + PythonBytes python_bytes(PyRefType::Owned, py_bytes); #if PY_MAJOR_VERSION < 3 - EXPECT_TRUE(PythonString::Check(py_bytes)); - EXPECT_EQ(PyObjectType::String, python_bytes.GetObjectType()); + EXPECT_TRUE(PythonString::Check(py_bytes)); + EXPECT_EQ(PyObjectType::String, python_bytes.GetObjectType()); #else - EXPECT_FALSE(PythonString::Check(py_bytes)); - EXPECT_EQ(PyObjectType::Bytes, python_bytes.GetObjectType()); + EXPECT_FALSE(PythonString::Check(py_bytes)); + EXPECT_EQ(PyObjectType::Bytes, python_bytes.GetObjectType()); #endif - llvm::ArrayRef<uint8_t> bytes = python_bytes.GetBytes(); - EXPECT_EQ(bytes.size(), strlen(test_bytes)); - EXPECT_EQ(0, ::memcmp(bytes.data(), test_bytes, bytes.size())); + llvm::ArrayRef<uint8_t> bytes = python_bytes.GetBytes(); + EXPECT_EQ(bytes.size(), strlen(test_bytes)); + EXPECT_EQ(0, ::memcmp(bytes.data(), test_bytes, bytes.size())); } -TEST_F(PythonDataObjectsTest, TestPythonByteArray) -{ - static const char *test_bytes = "PythonDataObjectsTest::TestPythonByteArray"; - llvm::StringRef orig_bytes(test_bytes); - PyObject *py_bytes = PyByteArray_FromStringAndSize(test_bytes, orig_bytes.size()); - EXPECT_TRUE(PythonByteArray::Check(py_bytes)); - PythonByteArray python_bytes(PyRefType::Owned, py_bytes); - EXPECT_EQ(PyObjectType::ByteArray, python_bytes.GetObjectType()); +TEST_F(PythonDataObjectsTest, TestPythonByteArray) { + static const char *test_bytes = "PythonDataObjectsTest::TestPythonByteArray"; + llvm::StringRef orig_bytes(test_bytes); + PyObject *py_bytes = + PyByteArray_FromStringAndSize(test_bytes, orig_bytes.size()); + EXPECT_TRUE(PythonByteArray::Check(py_bytes)); + PythonByteArray python_bytes(PyRefType::Owned, py_bytes); + EXPECT_EQ(PyObjectType::ByteArray, python_bytes.GetObjectType()); - llvm::ArrayRef<uint8_t> after_bytes = python_bytes.GetBytes(); - EXPECT_EQ(after_bytes.size(), orig_bytes.size()); - EXPECT_EQ(0, ::memcmp(orig_bytes.data(), test_bytes, orig_bytes.size())); + llvm::ArrayRef<uint8_t> after_bytes = python_bytes.GetBytes(); + EXPECT_EQ(after_bytes.size(), orig_bytes.size()); + EXPECT_EQ(0, ::memcmp(orig_bytes.data(), test_bytes, orig_bytes.size())); } -TEST_F(PythonDataObjectsTest, TestPythonString) -{ - // Test that strings behave correctly when wrapped by a PythonString. +TEST_F(PythonDataObjectsTest, TestPythonString) { + // Test that strings behave correctly when wrapped by a PythonString. - static const char *test_string = "PythonDataObjectsTest::TestPythonString1"; - static const char *test_string2 = "PythonDataObjectsTest::TestPythonString2"; + static const char *test_string = "PythonDataObjectsTest::TestPythonString1"; + static const char *test_string2 = "PythonDataObjectsTest::TestPythonString2"; #if PY_MAJOR_VERSION < 3 - // Verify that `PythonString` works correctly when given a PyString object. - // Note that PyString doesn't exist in Python 3.x, so this is only for 2.x - PyObject *py_string = PyString_FromString(test_string); - EXPECT_TRUE(PythonString::Check(py_string)); - PythonString python_string(PyRefType::Owned, py_string); - - EXPECT_EQ(PyObjectType::String, python_string.GetObjectType()); - EXPECT_STREQ(test_string, python_string.GetString().data()); + // Verify that `PythonString` works correctly when given a PyString object. + // Note that PyString doesn't exist in Python 3.x, so this is only for 2.x + PyObject *py_string = PyString_FromString(test_string); + EXPECT_TRUE(PythonString::Check(py_string)); + PythonString python_string(PyRefType::Owned, py_string); + + EXPECT_EQ(PyObjectType::String, python_string.GetObjectType()); + EXPECT_STREQ(test_string, python_string.GetString().data()); #else - // Verify that `PythonString` works correctly when given a PyUnicode object. - PyObject *py_unicode = PyUnicode_FromString(test_string); - EXPECT_TRUE(PythonString::Check(py_unicode)); - PythonString python_unicode(PyRefType::Owned, py_unicode); - EXPECT_EQ(PyObjectType::String, python_unicode.GetObjectType()); - EXPECT_STREQ(test_string, python_unicode.GetString().data()); + // Verify that `PythonString` works correctly when given a PyUnicode object. + PyObject *py_unicode = PyUnicode_FromString(test_string); + EXPECT_TRUE(PythonString::Check(py_unicode)); + PythonString python_unicode(PyRefType::Owned, py_unicode); + EXPECT_EQ(PyObjectType::String, python_unicode.GetObjectType()); + EXPECT_STREQ(test_string, python_unicode.GetString().data()); #endif - // Test that creating a `PythonString` object works correctly with the - // string constructor - PythonString constructed_string(test_string2); - EXPECT_STREQ(test_string2, constructed_string.GetString().str().c_str()); + // Test that creating a `PythonString` object works correctly with the + // string constructor + PythonString constructed_string(test_string2); + EXPECT_EQ(test_string2, constructed_string.GetString()); } -TEST_F(PythonDataObjectsTest, TestPythonStringToStr) -{ - const char *c_str = "PythonDataObjectsTest::TestPythonStringToStr"; +TEST_F(PythonDataObjectsTest, TestPythonStringToStr) { + const char *GetString = "PythonDataObjectsTest::TestPythonStringToStr"; - PythonString str(c_str); - EXPECT_STREQ(c_str, str.GetString().str().c_str()); + PythonString str(GetString); + EXPECT_EQ(GetString, str.GetString()); - PythonString str_str = str.Str(); - EXPECT_STREQ(c_str, str_str.GetString().str().c_str()); + PythonString str_str = str.Str(); + EXPECT_EQ(GetString, str_str.GetString()); } -TEST_F(PythonDataObjectsTest, TestPythonIntegerToStr) -{ -} +TEST_F(PythonDataObjectsTest, TestPythonIntegerToStr) {} -TEST_F(PythonDataObjectsTest, TestPythonIntegerToStructuredInteger) -{ - PythonInteger integer(7); - auto int_sp = integer.CreateStructuredInteger(); - EXPECT_EQ(7U, int_sp->GetValue()); +TEST_F(PythonDataObjectsTest, TestPythonIntegerToStructuredInteger) { + PythonInteger integer(7); + auto int_sp = integer.CreateStructuredInteger(); + EXPECT_EQ(7U, int_sp->GetValue()); } -TEST_F(PythonDataObjectsTest, TestPythonStringToStructuredString) -{ - static const char *test_string = "PythonDataObjectsTest::TestPythonStringToStructuredString"; - PythonString constructed_string(test_string); - auto string_sp = constructed_string.CreateStructuredString(); - EXPECT_STREQ(test_string, string_sp->GetStringValue().c_str()); +TEST_F(PythonDataObjectsTest, TestPythonStringToStructuredString) { + static const char *test_string = + "PythonDataObjectsTest::TestPythonStringToStructuredString"; + PythonString constructed_string(test_string); + auto string_sp = constructed_string.CreateStructuredString(); + EXPECT_EQ(test_string, string_sp->GetStringValue()); } -TEST_F(PythonDataObjectsTest, TestPythonListValueEquality) -{ - // Test that a list which is built through the native - // Python API behaves correctly when wrapped by a PythonList. - static const unsigned list_size = 2; - static const long long_value0 = 5; - static const char *const string_value1 = "String Index 1"; +TEST_F(PythonDataObjectsTest, TestPythonListValueEquality) { + // Test that a list which is built through the native + // Python API behaves correctly when wrapped by a PythonList. + static const unsigned list_size = 2; + static const long long_value0 = 5; + static const char *const string_value1 = "String Index 1"; - PyObject *py_list = PyList_New(2); - EXPECT_TRUE(PythonList::Check(py_list)); - PythonList list(PyRefType::Owned, py_list); + PyObject *py_list = PyList_New(2); + EXPECT_TRUE(PythonList::Check(py_list)); + PythonList list(PyRefType::Owned, py_list); - PythonObject list_items[list_size]; - list_items[0].Reset(PythonInteger(long_value0)); - list_items[1].Reset(PythonString(string_value1)); + PythonObject list_items[list_size]; + list_items[0].Reset(PythonInteger(long_value0)); + list_items[1].Reset(PythonString(string_value1)); - for (unsigned i = 0; i < list_size; ++i) - list.SetItemAtIndex(i, list_items[i]); + for (unsigned i = 0; i < list_size; ++i) + list.SetItemAtIndex(i, list_items[i]); - EXPECT_EQ(list_size, list.GetSize()); - EXPECT_EQ(PyObjectType::List, list.GetObjectType()); + EXPECT_EQ(list_size, list.GetSize()); + EXPECT_EQ(PyObjectType::List, list.GetObjectType()); - // Verify that the values match - PythonObject chk_value1 = list.GetItemAtIndex(0); - PythonObject chk_value2 = list.GetItemAtIndex(1); - EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); - EXPECT_TRUE(PythonString::Check(chk_value2.get())); + // Verify that the values match + PythonObject chk_value1 = list.GetItemAtIndex(0); + PythonObject chk_value2 = list.GetItemAtIndex(1); + EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); + EXPECT_TRUE(PythonString::Check(chk_value2.get())); - PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); - PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); + PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); + PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); - EXPECT_EQ(long_value0, chk_int.GetInteger()); - EXPECT_STREQ(string_value1, chk_str.GetString().str().c_str()); + EXPECT_EQ(long_value0, chk_int.GetInteger()); + EXPECT_EQ(string_value1, chk_str.GetString()); } -TEST_F(PythonDataObjectsTest, TestPythonListManipulation) -{ - // Test that manipulation of a PythonList behaves correctly when - // wrapped by a PythonDictionary. +TEST_F(PythonDataObjectsTest, TestPythonListManipulation) { + // Test that manipulation of a PythonList behaves correctly when + // wrapped by a PythonDictionary. - static const long long_value0 = 5; - static const char *const string_value1 = "String Index 1"; + static const long long_value0 = 5; + static const char *const string_value1 = "String Index 1"; - PythonList list(PyInitialValue::Empty); - PythonInteger integer(long_value0); - PythonString string(string_value1); + PythonList list(PyInitialValue::Empty); + PythonInteger integer(long_value0); + PythonString string(string_value1); - list.AppendItem(integer); - list.AppendItem(string); - EXPECT_EQ(2U, list.GetSize()); + list.AppendItem(integer); + list.AppendItem(string); + EXPECT_EQ(2U, list.GetSize()); - // Verify that the values match - PythonObject chk_value1 = list.GetItemAtIndex(0); - PythonObject chk_value2 = list.GetItemAtIndex(1); - EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); - EXPECT_TRUE(PythonString::Check(chk_value2.get())); + // Verify that the values match + PythonObject chk_value1 = list.GetItemAtIndex(0); + PythonObject chk_value2 = list.GetItemAtIndex(1); + EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); + EXPECT_TRUE(PythonString::Check(chk_value2.get())); - PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); - PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); + PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); + PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); - EXPECT_EQ(long_value0, chk_int.GetInteger()); - EXPECT_STREQ(string_value1, chk_str.GetString().str().c_str()); + EXPECT_EQ(long_value0, chk_int.GetInteger()); + EXPECT_EQ(string_value1, chk_str.GetString()); } -TEST_F(PythonDataObjectsTest, TestPythonListToStructuredList) -{ - static const long long_value0 = 5; - static const char *const string_value1 = "String Index 1"; +TEST_F(PythonDataObjectsTest, TestPythonListToStructuredList) { + static const long long_value0 = 5; + static const char *const string_value1 = "String Index 1"; - PythonList list(PyInitialValue::Empty); - list.AppendItem(PythonInteger(long_value0)); - list.AppendItem(PythonString(string_value1)); + PythonList list(PyInitialValue::Empty); + list.AppendItem(PythonInteger(long_value0)); + list.AppendItem(PythonString(string_value1)); - auto array_sp = list.CreateStructuredArray(); - EXPECT_EQ(StructuredData::Type::eTypeInteger, array_sp->GetItemAtIndex(0)->GetType()); - EXPECT_EQ(StructuredData::Type::eTypeString, array_sp->GetItemAtIndex(1)->GetType()); + auto array_sp = list.CreateStructuredArray(); + EXPECT_EQ(StructuredData::Type::eTypeInteger, + array_sp->GetItemAtIndex(0)->GetType()); + EXPECT_EQ(StructuredData::Type::eTypeString, + array_sp->GetItemAtIndex(1)->GetType()); - auto int_sp = array_sp->GetItemAtIndex(0)->GetAsInteger(); - auto string_sp = array_sp->GetItemAtIndex(1)->GetAsString(); + auto int_sp = array_sp->GetItemAtIndex(0)->GetAsInteger(); + auto string_sp = array_sp->GetItemAtIndex(1)->GetAsString(); - EXPECT_EQ(long_value0, long(int_sp->GetValue())); - EXPECT_STREQ(string_value1, string_sp->GetValue().c_str()); + EXPECT_EQ(long_value0, long(int_sp->GetValue())); + EXPECT_EQ(string_value1, string_sp->GetValue()); } -TEST_F(PythonDataObjectsTest, TestPythonTupleSize) -{ - PythonTuple tuple(PyInitialValue::Empty); - EXPECT_EQ(0U, tuple.GetSize()); +TEST_F(PythonDataObjectsTest, TestPythonTupleSize) { + PythonTuple tuple(PyInitialValue::Empty); + EXPECT_EQ(0U, tuple.GetSize()); - tuple = PythonTuple(3); - EXPECT_EQ(3U, tuple.GetSize()); + tuple = PythonTuple(3); + EXPECT_EQ(3U, tuple.GetSize()); } -TEST_F(PythonDataObjectsTest, TestPythonTupleValues) -{ - PythonTuple tuple(3); +TEST_F(PythonDataObjectsTest, TestPythonTupleValues) { + PythonTuple tuple(3); - PythonInteger int_value(1); - PythonString string_value("Test"); - PythonObject none_value(PyRefType::Borrowed, Py_None); + PythonInteger int_value(1); + PythonString string_value("Test"); + PythonObject none_value(PyRefType::Borrowed, Py_None); - tuple.SetItemAtIndex(0, int_value); - tuple.SetItemAtIndex(1, string_value); - tuple.SetItemAtIndex(2, none_value); + tuple.SetItemAtIndex(0, int_value); + tuple.SetItemAtIndex(1, string_value); + tuple.SetItemAtIndex(2, none_value); - EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); - EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); - EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get()); } -TEST_F(PythonDataObjectsTest, TestPythonTupleInitializerList) -{ - PythonInteger int_value(1); - PythonString string_value("Test"); - PythonObject none_value(PyRefType::Borrowed, Py_None); - PythonTuple tuple{ int_value, string_value, none_value }; - EXPECT_EQ(3U, tuple.GetSize()); +TEST_F(PythonDataObjectsTest, TestPythonTupleInitializerList) { + PythonInteger int_value(1); + PythonString string_value("Test"); + PythonObject none_value(PyRefType::Borrowed, Py_None); + PythonTuple tuple{int_value, string_value, none_value}; + EXPECT_EQ(3U, tuple.GetSize()); - EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); - EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); - EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get()); } -TEST_F(PythonDataObjectsTest, TestPythonTupleInitializerList2) -{ - PythonInteger int_value(1); - PythonString string_value("Test"); - PythonObject none_value(PyRefType::Borrowed, Py_None); +TEST_F(PythonDataObjectsTest, TestPythonTupleInitializerList2) { + PythonInteger int_value(1); + PythonString string_value("Test"); + PythonObject none_value(PyRefType::Borrowed, Py_None); - PythonTuple tuple{ int_value.get(), string_value.get(), none_value.get() }; - EXPECT_EQ(3U, tuple.GetSize()); + PythonTuple tuple{int_value.get(), string_value.get(), none_value.get()}; + EXPECT_EQ(3U, tuple.GetSize()); - EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); - EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); - EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get()); } -TEST_F(PythonDataObjectsTest, TestPythonTupleToStructuredList) -{ - PythonInteger int_value(1); - PythonString string_value("Test"); +TEST_F(PythonDataObjectsTest, TestPythonTupleToStructuredList) { + PythonInteger int_value(1); + PythonString string_value("Test"); - PythonTuple tuple{ int_value.get(), string_value.get() }; + PythonTuple tuple{int_value.get(), string_value.get()}; - auto array_sp = tuple.CreateStructuredArray(); - EXPECT_EQ(tuple.GetSize(), array_sp->GetSize()); - EXPECT_EQ(StructuredData::Type::eTypeInteger, array_sp->GetItemAtIndex(0)->GetType()); - EXPECT_EQ(StructuredData::Type::eTypeString, array_sp->GetItemAtIndex(1)->GetType()); + auto array_sp = tuple.CreateStructuredArray(); + EXPECT_EQ(tuple.GetSize(), array_sp->GetSize()); + EXPECT_EQ(StructuredData::Type::eTypeInteger, + array_sp->GetItemAtIndex(0)->GetType()); + EXPECT_EQ(StructuredData::Type::eTypeString, + array_sp->GetItemAtIndex(1)->GetType()); } -TEST_F(PythonDataObjectsTest, TestPythonDictionaryValueEquality) -{ - // Test that a dictionary which is built through the native - // Python API behaves correctly when wrapped by a PythonDictionary. - static const unsigned dict_entries = 2; - const char *key_0 = "Key 0"; - int key_1 = 1; - const int value_0 = 0; - const char *value_1 = "Value 1"; +TEST_F(PythonDataObjectsTest, TestPythonDictionaryValueEquality) { + // Test that a dictionary which is built through the native + // Python API behaves correctly when wrapped by a PythonDictionary. + static const unsigned dict_entries = 2; + const char *key_0 = "Key 0"; + int key_1 = 1; + const int value_0 = 0; + const char *value_1 = "Value 1"; - PythonObject py_keys[dict_entries]; - PythonObject py_values[dict_entries]; + PythonObject py_keys[dict_entries]; + PythonObject py_values[dict_entries]; - py_keys[0].Reset(PythonString(key_0)); - py_keys[1].Reset(PythonInteger(key_1)); - py_values[0].Reset(PythonInteger(value_0)); - py_values[1].Reset(PythonString(value_1)); + py_keys[0].Reset(PythonString(key_0)); + py_keys[1].Reset(PythonInteger(key_1)); + py_values[0].Reset(PythonInteger(value_0)); + py_values[1].Reset(PythonString(value_1)); - PyObject *py_dict = PyDict_New(); - EXPECT_TRUE(PythonDictionary::Check(py_dict)); - PythonDictionary dict(PyRefType::Owned, py_dict); + PyObject *py_dict = PyDict_New(); + EXPECT_TRUE(PythonDictionary::Check(py_dict)); + PythonDictionary dict(PyRefType::Owned, py_dict); - for (unsigned i = 0; i < dict_entries; ++i) - PyDict_SetItem(py_dict, py_keys[i].get(), py_values[i].get()); - EXPECT_EQ(dict.GetSize(), dict_entries); - EXPECT_EQ(PyObjectType::Dictionary, dict.GetObjectType()); + for (unsigned i = 0; i < dict_entries; ++i) + PyDict_SetItem(py_dict, py_keys[i].get(), py_values[i].get()); + EXPECT_EQ(dict.GetSize(), dict_entries); + EXPECT_EQ(PyObjectType::Dictionary, dict.GetObjectType()); - // Verify that the values match - PythonObject chk_value1 = dict.GetItemForKey(py_keys[0]); - PythonObject chk_value2 = dict.GetItemForKey(py_keys[1]); - EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); - EXPECT_TRUE(PythonString::Check(chk_value2.get())); + // Verify that the values match + PythonObject chk_value1 = dict.GetItemForKey(py_keys[0]); + PythonObject chk_value2 = dict.GetItemForKey(py_keys[1]); + EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); + EXPECT_TRUE(PythonString::Check(chk_value2.get())); - PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); - PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); + PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); + PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); - EXPECT_EQ(value_0, chk_int.GetInteger()); - EXPECT_STREQ(value_1, chk_str.GetString().str().c_str()); + EXPECT_EQ(value_0, chk_int.GetInteger()); + EXPECT_EQ(value_1, chk_str.GetString()); } -TEST_F(PythonDataObjectsTest, TestPythonDictionaryManipulation) -{ - // Test that manipulation of a dictionary behaves correctly when wrapped - // by a PythonDictionary. - static const unsigned dict_entries = 2; +TEST_F(PythonDataObjectsTest, TestPythonDictionaryManipulation) { + // Test that manipulation of a dictionary behaves correctly when wrapped + // by a PythonDictionary. + static const unsigned dict_entries = 2; - const char *const key_0 = "Key 0"; - const char *const key_1 = "Key 1"; - const long value_0 = 1; - const char *const value_1 = "Value 1"; + const char *const key_0 = "Key 0"; + const char *const key_1 = "Key 1"; + const long value_0 = 1; + const char *const value_1 = "Value 1"; - PythonString keys[dict_entries]; - PythonObject values[dict_entries]; + PythonString keys[dict_entries]; + PythonObject values[dict_entries]; - keys[0].Reset(PythonString(key_0)); - keys[1].Reset(PythonString(key_1)); - values[0].Reset(PythonInteger(value_0)); - values[1].Reset(PythonString(value_1)); + keys[0].Reset(PythonString(key_0)); + keys[1].Reset(PythonString(key_1)); + values[0].Reset(PythonInteger(value_0)); + values[1].Reset(PythonString(value_1)); - PythonDictionary dict(PyInitialValue::Empty); - for (int i = 0; i < 2; ++i) - dict.SetItemForKey(keys[i], values[i]); + PythonDictionary dict(PyInitialValue::Empty); + for (int i = 0; i < 2; ++i) + dict.SetItemForKey(keys[i], values[i]); - EXPECT_EQ(dict_entries, dict.GetSize()); + EXPECT_EQ(dict_entries, dict.GetSize()); - // Verify that the keys and values match - PythonObject chk_value1 = dict.GetItemForKey(keys[0]); - PythonObject chk_value2 = dict.GetItemForKey(keys[1]); - EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); - EXPECT_TRUE(PythonString::Check(chk_value2.get())); + // Verify that the keys and values match + PythonObject chk_value1 = dict.GetItemForKey(keys[0]); + PythonObject chk_value2 = dict.GetItemForKey(keys[1]); + EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); + EXPECT_TRUE(PythonString::Check(chk_value2.get())); - PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); - PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); + PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); + PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); - EXPECT_EQ(value_0, chk_int.GetInteger()); - EXPECT_STREQ(value_1, chk_str.GetString().str().c_str()); + EXPECT_EQ(value_0, chk_int.GetInteger()); + EXPECT_EQ(value_1, chk_str.GetString()); } -TEST_F(PythonDataObjectsTest, TestPythonDictionaryToStructuredDictionary) -{ - static const char *const string_key0 = "String Key 0"; - static const char *const string_key1 = "String Key 1"; - - static const char *const string_value0 = "String Value 0"; - static const long int_value1 = 7; - - PythonDictionary dict(PyInitialValue::Empty); - dict.SetItemForKey(PythonString(string_key0), PythonString(string_value0)); - dict.SetItemForKey(PythonString(string_key1), PythonInteger(int_value1)); - - auto dict_sp = dict.CreateStructuredDictionary(); - EXPECT_EQ(2U, dict_sp->GetSize()); +TEST_F(PythonDataObjectsTest, TestPythonDictionaryToStructuredDictionary) { + static const char *const string_key0 = "String Key 0"; + static const char *const string_key1 = "String Key 1"; - EXPECT_TRUE(dict_sp->HasKey(string_key0)); - EXPECT_TRUE(dict_sp->HasKey(string_key1)); + static const char *const string_value0 = "String Value 0"; + static const long int_value1 = 7; - auto string_sp = dict_sp->GetValueForKey(string_key0)->GetAsString(); - auto int_sp = dict_sp->GetValueForKey(string_key1)->GetAsInteger(); + PythonDictionary dict(PyInitialValue::Empty); + dict.SetItemForKey(PythonString(string_key0), PythonString(string_value0)); + dict.SetItemForKey(PythonString(string_key1), PythonInteger(int_value1)); + + auto dict_sp = dict.CreateStructuredDictionary(); + EXPECT_EQ(2U, dict_sp->GetSize()); - EXPECT_STREQ(string_value0, string_sp->GetValue().c_str()); - EXPECT_EQ(int_value1, long(int_sp->GetValue())); + EXPECT_TRUE(dict_sp->HasKey(string_key0)); + EXPECT_TRUE(dict_sp->HasKey(string_key1)); + + auto string_sp = dict_sp->GetValueForKey(string_key0)->GetAsString(); + auto int_sp = dict_sp->GetValueForKey(string_key1)->GetAsInteger(); + + EXPECT_EQ(string_value0, string_sp->GetValue()); + EXPECT_EQ(int_value1, long(int_sp->GetValue())); } - -TEST_F(PythonDataObjectsTest, TestPythonCallableCheck) -{ - PythonObject sys_exc_info = m_sys_module.ResolveName("exc_info"); - PythonObject none(PyRefType::Borrowed, Py_None); - - EXPECT_TRUE(PythonCallable::Check(sys_exc_info.get())); - EXPECT_FALSE(PythonCallable::Check(none.get())); + +TEST_F(PythonDataObjectsTest, TestPythonCallableCheck) { + PythonObject sys_exc_info = m_sys_module.ResolveName("exc_info"); + PythonObject none(PyRefType::Borrowed, Py_None); + + EXPECT_TRUE(PythonCallable::Check(sys_exc_info.get())); + EXPECT_FALSE(PythonCallable::Check(none.get())); } -TEST_F(PythonDataObjectsTest, TestPythonCallableInvoke) -{ - auto list = m_builtins_module.ResolveName("list").AsType<PythonCallable>(); - PythonInteger one(1); - PythonString two("two"); - PythonTuple three = { one, two }; +TEST_F(PythonDataObjectsTest, TestPythonCallableInvoke) { + auto list = m_builtins_module.ResolveName("list").AsType<PythonCallable>(); + PythonInteger one(1); + PythonString two("two"); + PythonTuple three = {one, two}; - PythonTuple tuple_to_convert = { one, two, three }; - PythonObject result = list({ tuple_to_convert }); + PythonTuple tuple_to_convert = {one, two, three}; + PythonObject result = list({tuple_to_convert}); - EXPECT_TRUE(PythonList::Check(result.get())); - auto list_result = result.AsType<PythonList>(); - EXPECT_EQ(3U, list_result.GetSize()); - EXPECT_EQ(one.get(), list_result.GetItemAtIndex(0).get()); - EXPECT_EQ(two.get(), list_result.GetItemAtIndex(1).get()); - EXPECT_EQ(three.get(), list_result.GetItemAtIndex(2).get()); + EXPECT_TRUE(PythonList::Check(result.get())); + auto list_result = result.AsType<PythonList>(); + EXPECT_EQ(3U, list_result.GetSize()); + EXPECT_EQ(one.get(), list_result.GetItemAtIndex(0).get()); + EXPECT_EQ(two.get(), list_result.GetItemAtIndex(1).get()); + EXPECT_EQ(three.get(), list_result.GetItemAtIndex(2).get()); } -TEST_F(PythonDataObjectsTest, TestPythonFile) -{ - File file(FileSystem::DEV_NULL, File::eOpenOptionRead); - PythonFile py_file(file, "r"); - EXPECT_TRUE(PythonFile::Check(py_file.get())); +TEST_F(PythonDataObjectsTest, TestPythonFile) { + File file(FileSystem::DEV_NULL, File::eOpenOptionRead); + PythonFile py_file(file, "r"); + EXPECT_TRUE(PythonFile::Check(py_file.get())); } -TEST_F(PythonDataObjectsTest, TestObjectAttributes) -{ - PythonInteger py_int(42); - EXPECT_TRUE(py_int.HasAttribute("numerator")); - EXPECT_FALSE(py_int.HasAttribute("this_should_not_exist")); +TEST_F(PythonDataObjectsTest, TestObjectAttributes) { + PythonInteger py_int(42); + EXPECT_TRUE(py_int.HasAttribute("numerator")); + EXPECT_FALSE(py_int.HasAttribute("this_should_not_exist")); - PythonInteger numerator_attr = py_int.GetAttributeValue("numerator").AsType<PythonInteger>(); - EXPECT_TRUE(numerator_attr.IsAllocated()); - EXPECT_EQ(42, numerator_attr.GetInteger()); + PythonInteger numerator_attr = + py_int.GetAttributeValue("numerator").AsType<PythonInteger>(); + EXPECT_TRUE(numerator_attr.IsAllocated()); + EXPECT_EQ(42, numerator_attr.GetInteger()); +} + +TEST_F(PythonDataObjectsTest, TestExtractingUInt64ThroughStructuredData) { + // Make up a custom dictionary with "sys" pointing to the `sys` module. + const char *key_name = "addr"; + const uint64_t value = 0xf000000000000000ull; + PythonDictionary python_dict(PyInitialValue::Empty); + PythonInteger python_ull_value(PyRefType::Owned, + PyLong_FromUnsignedLongLong(value)); + python_dict.SetItemForKey(PythonString(key_name), python_ull_value); + StructuredData::ObjectSP structured_data_sp = + python_dict.CreateStructuredObject(); + EXPECT_TRUE((bool)structured_data_sp); + if (structured_data_sp) { + StructuredData::Dictionary *structured_dict_ptr = + structured_data_sp->GetAsDictionary(); + EXPECT_TRUE(structured_dict_ptr != nullptr); + if (structured_dict_ptr) { + StructuredData::ObjectSP structured_addr_value_sp = + structured_dict_ptr->GetValueForKey(key_name); + EXPECT_TRUE((bool)structured_addr_value_sp); + const uint64_t extracted_value = + structured_addr_value_sp->GetIntegerValue(123); + EXPECT_TRUE(extracted_value == value); + } + } } diff --git a/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp b/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp index ddac220c7954..523a07ee8079 100644 --- a/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp +++ b/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp @@ -1,4 +1,5 @@ -//===-- PythonExceptionStateTest.cpp ------------------------------*- C++ -*-===// +//===-- PythonExceptionStateTest.cpp ------------------------------*- C++ +//-*-===// // // The LLVM Compiler Infrastructure // @@ -9,166 +10,156 @@ #include "gtest/gtest.h" -#include "Plugins/ScriptInterpreter/Python/lldb-python.h" #include "Plugins/ScriptInterpreter/Python/PythonDataObjects.h" #include "Plugins/ScriptInterpreter/Python/PythonExceptionState.h" #include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h" +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" #include "PythonTestSuite.h" using namespace lldb_private; -class PythonExceptionStateTest : public PythonTestSuite -{ - public: - protected: - void - RaiseException() - { - PyErr_SetString(PyExc_RuntimeError, "PythonExceptionStateTest test error"); - } +class PythonExceptionStateTest : public PythonTestSuite { +public: +protected: + void RaiseException() { + PyErr_SetString(PyExc_RuntimeError, "PythonExceptionStateTest test error"); + } }; -TEST_F(PythonExceptionStateTest, TestExceptionStateChecking) -{ - PyErr_Clear(); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); +TEST_F(PythonExceptionStateTest, TestExceptionStateChecking) { + PyErr_Clear(); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - RaiseException(); - EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); + RaiseException(); + EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); - PyErr_Clear(); + PyErr_Clear(); } -TEST_F(PythonExceptionStateTest, TestAcquisitionSemantics) -{ - PyErr_Clear(); - PythonExceptionState no_error(false); - EXPECT_FALSE(no_error.IsError()); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - - PyErr_Clear(); - RaiseException(); - PythonExceptionState error(false); - EXPECT_TRUE(error.IsError()); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - error.Discard(); - - PyErr_Clear(); - RaiseException(); - error.Acquire(false); - EXPECT_TRUE(error.IsError()); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - - PyErr_Clear(); +TEST_F(PythonExceptionStateTest, TestAcquisitionSemantics) { + PyErr_Clear(); + PythonExceptionState no_error(false); + EXPECT_FALSE(no_error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); + RaiseException(); + PythonExceptionState error(false); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + error.Discard(); + + PyErr_Clear(); + RaiseException(); + error.Acquire(false); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); } -TEST_F(PythonExceptionStateTest, TestDiscardSemantics) -{ - PyErr_Clear(); +TEST_F(PythonExceptionStateTest, TestDiscardSemantics) { + PyErr_Clear(); - // Test that discarding an exception does not restore the exception - // state even when auto-restore==true is set - RaiseException(); - PythonExceptionState error(true); - EXPECT_TRUE(error.IsError()); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + // Test that discarding an exception does not restore the exception + // state even when auto-restore==true is set + RaiseException(); + PythonExceptionState error(true); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - error.Discard(); - EXPECT_FALSE(error.IsError()); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + error.Discard(); + EXPECT_FALSE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); } -TEST_F(PythonExceptionStateTest, TestResetSemantics) -{ - PyErr_Clear(); - - // Resetting when auto-restore is true should restore. - RaiseException(); - PythonExceptionState error(true); - EXPECT_TRUE(error.IsError()); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - error.Reset(); - EXPECT_FALSE(error.IsError()); - EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); +TEST_F(PythonExceptionStateTest, TestResetSemantics) { + PyErr_Clear(); + + // Resetting when auto-restore is true should restore. + RaiseException(); + PythonExceptionState error(true); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + error.Reset(); + EXPECT_FALSE(error.IsError()); + EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); + + // Resetting when auto-restore is false should discard. + RaiseException(); + PythonExceptionState error2(false); + EXPECT_TRUE(error2.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + error2.Reset(); + EXPECT_FALSE(error2.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); +} - PyErr_Clear(); +TEST_F(PythonExceptionStateTest, TestManualRestoreSemantics) { + PyErr_Clear(); + RaiseException(); + PythonExceptionState error(false); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - // Resetting when auto-restore is false should discard. - RaiseException(); - PythonExceptionState error2(false); - EXPECT_TRUE(error2.IsError()); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - error2.Reset(); - EXPECT_FALSE(error2.IsError()); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + error.Restore(); + EXPECT_FALSE(error.IsError()); + EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); - PyErr_Clear(); + PyErr_Clear(); } -TEST_F(PythonExceptionStateTest, TestManualRestoreSemantics) -{ - PyErr_Clear(); +TEST_F(PythonExceptionStateTest, TestAutoRestoreSemantics) { + PyErr_Clear(); + // Test that using the auto-restore flag correctly restores the exception + // state on destruction, and not using the auto-restore flag correctly + // does NOT restore the state on destruction. + { RaiseException(); PythonExceptionState error(false); EXPECT_TRUE(error.IsError()); EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + } + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - error.Restore(); - EXPECT_FALSE(error.IsError()); - EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); - - PyErr_Clear(); -} - -TEST_F(PythonExceptionStateTest, TestAutoRestoreSemantics) -{ - PyErr_Clear(); - // Test that using the auto-restore flag correctly restores the exception - // state on destruction, and not using the auto-restore flag correctly - // does NOT restore the state on destruction. - { - RaiseException(); - PythonExceptionState error(false); - EXPECT_TRUE(error.IsError()); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - } + PyErr_Clear(); + { + RaiseException(); + PythonExceptionState error(true); + EXPECT_TRUE(error.IsError()); EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + } + EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); - PyErr_Clear(); - { - RaiseException(); - PythonExceptionState error(true); - EXPECT_TRUE(error.IsError()); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - } - EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); - - PyErr_Clear(); + PyErr_Clear(); } -TEST_F(PythonExceptionStateTest, TestAutoRestoreChanged) -{ - // Test that if we re-acquire with different auto-restore semantics, - // that the new semantics are respected. - PyErr_Clear(); +TEST_F(PythonExceptionStateTest, TestAutoRestoreChanged) { + // Test that if we re-acquire with different auto-restore semantics, + // that the new semantics are respected. + PyErr_Clear(); - RaiseException(); - PythonExceptionState error(false); - EXPECT_TRUE(error.IsError()); + RaiseException(); + PythonExceptionState error(false); + EXPECT_TRUE(error.IsError()); - error.Reset(); - EXPECT_FALSE(error.IsError()); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + error.Reset(); + EXPECT_FALSE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - RaiseException(); - error.Acquire(true); - EXPECT_TRUE(error.IsError()); - EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + RaiseException(); + error.Acquire(true); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); - error.Reset(); - EXPECT_FALSE(error.IsError()); - EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); + error.Reset(); + EXPECT_FALSE(error.IsError()); + EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); - PyErr_Clear(); + PyErr_Clear(); } diff --git a/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp index 5eb1c72598a8..608f3ee45e5e 100644 --- a/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -7,37 +7,33 @@ // //===----------------------------------------------------------------------===// +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" #include "gtest/gtest.h" -#include "lldb/Host/HostInfo.h" -#include "Plugins/ScriptInterpreter/Python/lldb-python.h" #include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h" +#include "lldb/Host/HostInfo.h" #include "PythonTestSuite.h" using namespace lldb_private; -void -PythonTestSuite::SetUp() -{ - HostInfoBase::Initialize(); - // ScriptInterpreterPython::Initialize() depends on HostInfo being - // initializedso it can compute the python directory etc. - ScriptInterpreterPython::Initialize(); - ScriptInterpreterPython::InitializePrivate(); - - // Although we don't care about concurrency for the purposes of running - // this test suite, Python requires the GIL to be locked even for - // deallocating memory, which can happen when you call Py_DECREF or - // Py_INCREF. So acquire the GIL for the entire duration of this - // test suite. - m_gil_state = PyGILState_Ensure(); +void PythonTestSuite::SetUp() { + HostInfoBase::Initialize(); + // ScriptInterpreterPython::Initialize() depends on HostInfo being + // initializedso it can compute the python directory etc. + ScriptInterpreterPython::Initialize(); + ScriptInterpreterPython::InitializePrivate(); + + // Although we don't care about concurrency for the purposes of running + // this test suite, Python requires the GIL to be locked even for + // deallocating memory, which can happen when you call Py_DECREF or + // Py_INCREF. So acquire the GIL for the entire duration of this + // test suite. + m_gil_state = PyGILState_Ensure(); } -void -PythonTestSuite::TearDown() -{ - PyGILState_Release(m_gil_state); +void PythonTestSuite::TearDown() { + PyGILState_Release(m_gil_state); - ScriptInterpreterPython::Terminate(); + ScriptInterpreterPython::Terminate(); } diff --git a/unittests/ScriptInterpreter/Python/PythonTestSuite.h b/unittests/ScriptInterpreter/Python/PythonTestSuite.h index 461fc1d5676c..ad0478875bef 100644 --- a/unittests/ScriptInterpreter/Python/PythonTestSuite.h +++ b/unittests/ScriptInterpreter/Python/PythonTestSuite.h @@ -11,16 +11,12 @@ using namespace lldb_private; -class PythonTestSuite : public testing::Test -{ +class PythonTestSuite : public testing::Test { public: - void - SetUp() override; + void SetUp() override; - void - TearDown() override; + void TearDown() override; private: - PyGILState_STATE m_gil_state; + PyGILState_STATE m_gil_state; }; - diff --git a/unittests/Symbol/TestClangASTContext.cpp b/unittests/Symbol/TestClangASTContext.cpp index 3f166ab9cc72..ed49c2657c7a 100644 --- a/unittests/Symbol/TestClangASTContext.cpp +++ b/unittests/Symbol/TestClangASTContext.cpp @@ -1,4 +1,5 @@ -//===-- TestClangASTContext.cpp ---------------------------------------*- C++ -*-===// +//===-- TestClangASTContext.cpp ---------------------------------------*- C++ +//-*-===// // // The LLVM Compiler Infrastructure @@ -20,296 +21,358 @@ using namespace clang; using namespace lldb; using namespace lldb_private; -class TestClangASTContext : public testing::Test -{ +class TestClangASTContext : public testing::Test { public: - static void - SetUpTestCase() - { - HostInfo::Initialize(); - } - - static void - TearDownTestCase() - { - HostInfo::Terminate(); - } - - virtual void - SetUp() override - { - std::string triple = HostInfo::GetTargetTriple(); - m_ast.reset(new ClangASTContext(triple.c_str())); - } - - virtual void - TearDown() override - { - m_ast.reset(); - } + static void SetUpTestCase() { HostInfo::Initialize(); } + + static void TearDownTestCase() { HostInfo::Terminate(); } + + virtual void SetUp() override { + std::string triple = HostInfo::GetTargetTriple(); + m_ast.reset(new ClangASTContext(triple.c_str())); + } + + virtual void TearDown() override { m_ast.reset(); } protected: - std::unique_ptr<ClangASTContext> m_ast; - - QualType - GetBasicQualType(BasicType type) const - { - return ClangUtil::GetQualType(m_ast->GetBasicTypeFromAST(type)); - } - - QualType - GetBasicQualType(const char *name) const - { - return ClangUtil::GetQualType(m_ast->GetBuiltinTypeByName(ConstString(name))); - } + std::unique_ptr<ClangASTContext> m_ast; + + QualType GetBasicQualType(BasicType type) const { + return ClangUtil::GetQualType(m_ast->GetBasicTypeFromAST(type)); + } + + QualType GetBasicQualType(const char *name) const { + return ClangUtil::GetQualType( + m_ast->GetBuiltinTypeByName(ConstString(name))); + } }; -TEST_F(TestClangASTContext, TestGetBasicTypeFromEnum) -{ - clang::ASTContext *context = m_ast->getASTContext(); - - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeBool), context->BoolTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeChar), context->CharTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeChar16), context->Char16Ty)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeChar32), context->Char32Ty)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeDouble), context->DoubleTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeDoubleComplex), context->DoubleComplexTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeFloat), context->FloatTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeFloatComplex), context->FloatComplexTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeHalf), context->HalfTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeInt), context->IntTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeInt128), context->Int128Ty)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeLong), context->LongTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeLongDouble), context->LongDoubleTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeLongDoubleComplex), context->LongDoubleComplexTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeLongLong), context->LongLongTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeNullPtr), context->NullPtrTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeObjCClass), context->getObjCClassType())); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeObjCID), context->getObjCIdType())); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeObjCSel), context->getObjCSelType())); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeShort), context->ShortTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeSignedChar), context->SignedCharTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedChar), context->UnsignedCharTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedInt), context->UnsignedIntTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedInt128), context->UnsignedInt128Ty)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedLong), context->UnsignedLongTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedLongLong), context->UnsignedLongLongTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedShort), context->UnsignedShortTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeVoid), context->VoidTy)); - EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeWChar), context->WCharTy)); +TEST_F(TestClangASTContext, TestGetBasicTypeFromEnum) { + clang::ASTContext *context = m_ast->getASTContext(); + + EXPECT_TRUE( + context->hasSameType(GetBasicQualType(eBasicTypeBool), context->BoolTy)); + EXPECT_TRUE( + context->hasSameType(GetBasicQualType(eBasicTypeChar), context->CharTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeChar16), + context->Char16Ty)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeChar32), + context->Char32Ty)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeDouble), + context->DoubleTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeDoubleComplex), + context->DoubleComplexTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeFloat), + context->FloatTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeFloatComplex), + context->FloatComplexTy)); + EXPECT_TRUE( + context->hasSameType(GetBasicQualType(eBasicTypeHalf), context->HalfTy)); + EXPECT_TRUE( + context->hasSameType(GetBasicQualType(eBasicTypeInt), context->IntTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeInt128), + context->Int128Ty)); + EXPECT_TRUE( + context->hasSameType(GetBasicQualType(eBasicTypeLong), context->LongTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeLongDouble), + context->LongDoubleTy)); + EXPECT_TRUE( + context->hasSameType(GetBasicQualType(eBasicTypeLongDoubleComplex), + context->LongDoubleComplexTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeLongLong), + context->LongLongTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeNullPtr), + context->NullPtrTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeObjCClass), + context->getObjCClassType())); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeObjCID), + context->getObjCIdType())); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeObjCSel), + context->getObjCSelType())); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeShort), + context->ShortTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeSignedChar), + context->SignedCharTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedChar), + context->UnsignedCharTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedInt), + context->UnsignedIntTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedInt128), + context->UnsignedInt128Ty)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedLong), + context->UnsignedLongTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedLongLong), + context->UnsignedLongLongTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedShort), + context->UnsignedShortTy)); + EXPECT_TRUE( + context->hasSameType(GetBasicQualType(eBasicTypeVoid), context->VoidTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeWChar), + context->WCharTy)); } -TEST_F(TestClangASTContext, TestGetBasicTypeFromName) -{ - EXPECT_EQ(GetBasicQualType(eBasicTypeChar), GetBasicQualType("char")); - EXPECT_EQ(GetBasicQualType(eBasicTypeSignedChar), GetBasicQualType("signed char")); - EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedChar), GetBasicQualType("unsigned char")); - EXPECT_EQ(GetBasicQualType(eBasicTypeWChar), GetBasicQualType("wchar_t")); - EXPECT_EQ(GetBasicQualType(eBasicTypeSignedWChar), GetBasicQualType("signed wchar_t")); - EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedWChar), GetBasicQualType("unsigned wchar_t")); - EXPECT_EQ(GetBasicQualType(eBasicTypeShort), GetBasicQualType("short")); - EXPECT_EQ(GetBasicQualType(eBasicTypeShort), GetBasicQualType("short int")); - EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedShort), GetBasicQualType("unsigned short")); - EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedShort), GetBasicQualType("unsigned short int")); - EXPECT_EQ(GetBasicQualType(eBasicTypeInt), GetBasicQualType("int")); - EXPECT_EQ(GetBasicQualType(eBasicTypeInt), GetBasicQualType("signed int")); - EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt), GetBasicQualType("unsigned int")); - EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt), GetBasicQualType("unsigned")); - EXPECT_EQ(GetBasicQualType(eBasicTypeLong), GetBasicQualType("long")); - EXPECT_EQ(GetBasicQualType(eBasicTypeLong), GetBasicQualType("long int")); - EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLong), GetBasicQualType("unsigned long")); - EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLong), GetBasicQualType("unsigned long int")); - EXPECT_EQ(GetBasicQualType(eBasicTypeLongLong), GetBasicQualType("long long")); - EXPECT_EQ(GetBasicQualType(eBasicTypeLongLong), GetBasicQualType("long long int")); - EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLongLong), GetBasicQualType("unsigned long long")); - EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLongLong), GetBasicQualType("unsigned long long int")); - EXPECT_EQ(GetBasicQualType(eBasicTypeInt128), GetBasicQualType("__int128_t")); - EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt128), GetBasicQualType("__uint128_t")); - EXPECT_EQ(GetBasicQualType(eBasicTypeVoid), GetBasicQualType("void")); - EXPECT_EQ(GetBasicQualType(eBasicTypeBool), GetBasicQualType("bool")); - EXPECT_EQ(GetBasicQualType(eBasicTypeFloat), GetBasicQualType("float")); - EXPECT_EQ(GetBasicQualType(eBasicTypeDouble), GetBasicQualType("double")); - EXPECT_EQ(GetBasicQualType(eBasicTypeLongDouble), GetBasicQualType("long double")); - EXPECT_EQ(GetBasicQualType(eBasicTypeObjCID), GetBasicQualType("id")); - EXPECT_EQ(GetBasicQualType(eBasicTypeObjCSel), GetBasicQualType("SEL")); - EXPECT_EQ(GetBasicQualType(eBasicTypeNullPtr), GetBasicQualType("nullptr")); +TEST_F(TestClangASTContext, TestGetBasicTypeFromName) { + EXPECT_EQ(GetBasicQualType(eBasicTypeChar), GetBasicQualType("char")); + EXPECT_EQ(GetBasicQualType(eBasicTypeSignedChar), + GetBasicQualType("signed char")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedChar), + GetBasicQualType("unsigned char")); + EXPECT_EQ(GetBasicQualType(eBasicTypeWChar), GetBasicQualType("wchar_t")); + EXPECT_EQ(GetBasicQualType(eBasicTypeSignedWChar), + GetBasicQualType("signed wchar_t")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedWChar), + GetBasicQualType("unsigned wchar_t")); + EXPECT_EQ(GetBasicQualType(eBasicTypeShort), GetBasicQualType("short")); + EXPECT_EQ(GetBasicQualType(eBasicTypeShort), GetBasicQualType("short int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedShort), + GetBasicQualType("unsigned short")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedShort), + GetBasicQualType("unsigned short int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeInt), GetBasicQualType("int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeInt), GetBasicQualType("signed int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt), + GetBasicQualType("unsigned int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt), + GetBasicQualType("unsigned")); + EXPECT_EQ(GetBasicQualType(eBasicTypeLong), GetBasicQualType("long")); + EXPECT_EQ(GetBasicQualType(eBasicTypeLong), GetBasicQualType("long int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLong), + GetBasicQualType("unsigned long")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLong), + GetBasicQualType("unsigned long int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeLongLong), + GetBasicQualType("long long")); + EXPECT_EQ(GetBasicQualType(eBasicTypeLongLong), + GetBasicQualType("long long int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLongLong), + GetBasicQualType("unsigned long long")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLongLong), + GetBasicQualType("unsigned long long int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeInt128), GetBasicQualType("__int128_t")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt128), + GetBasicQualType("__uint128_t")); + EXPECT_EQ(GetBasicQualType(eBasicTypeVoid), GetBasicQualType("void")); + EXPECT_EQ(GetBasicQualType(eBasicTypeBool), GetBasicQualType("bool")); + EXPECT_EQ(GetBasicQualType(eBasicTypeFloat), GetBasicQualType("float")); + EXPECT_EQ(GetBasicQualType(eBasicTypeDouble), GetBasicQualType("double")); + EXPECT_EQ(GetBasicQualType(eBasicTypeLongDouble), + GetBasicQualType("long double")); + EXPECT_EQ(GetBasicQualType(eBasicTypeObjCID), GetBasicQualType("id")); + EXPECT_EQ(GetBasicQualType(eBasicTypeObjCSel), GetBasicQualType("SEL")); + EXPECT_EQ(GetBasicQualType(eBasicTypeNullPtr), GetBasicQualType("nullptr")); } -void -VerifyEncodingAndBitSize(clang::ASTContext *context, lldb::Encoding encoding, int bit_size) -{ - CompilerType type = ClangASTContext::GetBuiltinTypeForEncodingAndBitSize(context, encoding, bit_size); - EXPECT_TRUE(type.IsValid()); - - QualType qtype = ClangUtil::GetQualType(type); - EXPECT_FALSE(qtype.isNull()); - if (qtype.isNull()) - return; - - uint64_t actual_size = context->getTypeSize(qtype); - EXPECT_EQ(bit_size, actual_size); - - const clang::Type *type_ptr = qtype.getTypePtr(); - EXPECT_NE(nullptr, type_ptr); - if (!type_ptr) - return; - - EXPECT_TRUE(type_ptr->isBuiltinType()); - if (encoding == eEncodingSint) - EXPECT_TRUE(type_ptr->isSignedIntegerType()); - else if (encoding == eEncodingUint) - EXPECT_TRUE(type_ptr->isUnsignedIntegerType()); - else if (encoding == eEncodingIEEE754) - EXPECT_TRUE(type_ptr->isFloatingType()); +void VerifyEncodingAndBitSize(clang::ASTContext *context, + lldb::Encoding encoding, unsigned int bit_size) { + CompilerType type = ClangASTContext::GetBuiltinTypeForEncodingAndBitSize( + context, encoding, bit_size); + EXPECT_TRUE(type.IsValid()); + + QualType qtype = ClangUtil::GetQualType(type); + EXPECT_FALSE(qtype.isNull()); + if (qtype.isNull()) + return; + + uint64_t actual_size = context->getTypeSize(qtype); + EXPECT_EQ(bit_size, actual_size); + + const clang::Type *type_ptr = qtype.getTypePtr(); + EXPECT_NE(nullptr, type_ptr); + if (!type_ptr) + return; + + EXPECT_TRUE(type_ptr->isBuiltinType()); + if (encoding == eEncodingSint) + EXPECT_TRUE(type_ptr->isSignedIntegerType()); + else if (encoding == eEncodingUint) + EXPECT_TRUE(type_ptr->isUnsignedIntegerType()); + else if (encoding == eEncodingIEEE754) + EXPECT_TRUE(type_ptr->isFloatingType()); } -TEST_F(TestClangASTContext, TestBuiltinTypeForEncodingAndBitSize) -{ - clang::ASTContext *context = m_ast->getASTContext(); - - // Make sure we can get types of every possible size in every possible encoding. - // We can't make any guarantee about which specific type we get, because the standard - // isn't that specific. We only need to make sure the compiler hands us some type that - // is both a builtin type and matches the requested bit size. - VerifyEncodingAndBitSize(context, eEncodingSint, 8); - VerifyEncodingAndBitSize(context, eEncodingSint, 16); - VerifyEncodingAndBitSize(context, eEncodingSint, 32); - VerifyEncodingAndBitSize(context, eEncodingSint, 64); - VerifyEncodingAndBitSize(context, eEncodingSint, 128); - - VerifyEncodingAndBitSize(context, eEncodingUint, 8); - VerifyEncodingAndBitSize(context, eEncodingUint, 16); - VerifyEncodingAndBitSize(context, eEncodingUint, 32); - VerifyEncodingAndBitSize(context, eEncodingUint, 64); - VerifyEncodingAndBitSize(context, eEncodingUint, 128); - - VerifyEncodingAndBitSize(context, eEncodingIEEE754, 32); - VerifyEncodingAndBitSize(context, eEncodingIEEE754, 64); +TEST_F(TestClangASTContext, TestBuiltinTypeForEncodingAndBitSize) { + clang::ASTContext *context = m_ast->getASTContext(); + + // Make sure we can get types of every possible size in every possible + // encoding. + // We can't make any guarantee about which specific type we get, because the + // standard + // isn't that specific. We only need to make sure the compiler hands us some + // type that + // is both a builtin type and matches the requested bit size. + VerifyEncodingAndBitSize(context, eEncodingSint, 8); + VerifyEncodingAndBitSize(context, eEncodingSint, 16); + VerifyEncodingAndBitSize(context, eEncodingSint, 32); + VerifyEncodingAndBitSize(context, eEncodingSint, 64); + VerifyEncodingAndBitSize(context, eEncodingSint, 128); + + VerifyEncodingAndBitSize(context, eEncodingUint, 8); + VerifyEncodingAndBitSize(context, eEncodingUint, 16); + VerifyEncodingAndBitSize(context, eEncodingUint, 32); + VerifyEncodingAndBitSize(context, eEncodingUint, 64); + VerifyEncodingAndBitSize(context, eEncodingUint, 128); + + VerifyEncodingAndBitSize(context, eEncodingIEEE754, 32); + VerifyEncodingAndBitSize(context, eEncodingIEEE754, 64); } -TEST_F(TestClangASTContext, TestIsClangType) -{ - clang::ASTContext *context = m_ast->getASTContext(); - lldb::opaque_compiler_type_t bool_ctype = ClangASTContext::GetOpaqueCompilerType(context, lldb::eBasicTypeBool); - CompilerType bool_type(m_ast.get(), bool_ctype); - CompilerType record_type = m_ast->CreateRecordType(nullptr, lldb::eAccessPublic, "FooRecord", clang::TTK_Struct, - lldb::eLanguageTypeC_plus_plus, nullptr); - // Clang builtin type and record type should pass - EXPECT_TRUE(ClangUtil::IsClangType(bool_type)); - EXPECT_TRUE(ClangUtil::IsClangType(record_type)); - - // Default constructed type should fail - EXPECT_FALSE(ClangUtil::IsClangType(CompilerType())); - - // Go type should fail - GoASTContext go_ast; - CompilerType go_type(&go_ast, bool_ctype); - EXPECT_FALSE(ClangUtil::IsClangType(go_type)); +TEST_F(TestClangASTContext, TestIsClangType) { + clang::ASTContext *context = m_ast->getASTContext(); + lldb::opaque_compiler_type_t bool_ctype = + ClangASTContext::GetOpaqueCompilerType(context, lldb::eBasicTypeBool); + CompilerType bool_type(m_ast.get(), bool_ctype); + CompilerType record_type = m_ast->CreateRecordType( + nullptr, lldb::eAccessPublic, "FooRecord", clang::TTK_Struct, + lldb::eLanguageTypeC_plus_plus, nullptr); + // Clang builtin type and record type should pass + EXPECT_TRUE(ClangUtil::IsClangType(bool_type)); + EXPECT_TRUE(ClangUtil::IsClangType(record_type)); + + // Default constructed type should fail + EXPECT_FALSE(ClangUtil::IsClangType(CompilerType())); + + // Go type should fail + GoASTContext go_ast; + CompilerType go_type(&go_ast, bool_ctype); + EXPECT_FALSE(ClangUtil::IsClangType(go_type)); } -TEST_F(TestClangASTContext, TestRemoveFastQualifiers) -{ - CompilerType record_type = m_ast->CreateRecordType(nullptr, lldb::eAccessPublic, "FooRecord", clang::TTK_Struct, - lldb::eLanguageTypeC_plus_plus, nullptr); - QualType qt; - - qt = ClangUtil::GetQualType(record_type); - EXPECT_EQ(0, qt.getLocalFastQualifiers()); - record_type = record_type.AddConstModifier(); - record_type = record_type.AddVolatileModifier(); - record_type = record_type.AddRestrictModifier(); - qt = ClangUtil::GetQualType(record_type); - EXPECT_NE(0, qt.getLocalFastQualifiers()); - record_type = ClangUtil::RemoveFastQualifiers(record_type); - qt = ClangUtil::GetQualType(record_type); - EXPECT_EQ(0, qt.getLocalFastQualifiers()); +TEST_F(TestClangASTContext, TestRemoveFastQualifiers) { + CompilerType record_type = m_ast->CreateRecordType( + nullptr, lldb::eAccessPublic, "FooRecord", clang::TTK_Struct, + lldb::eLanguageTypeC_plus_plus, nullptr); + QualType qt; + + qt = ClangUtil::GetQualType(record_type); + EXPECT_EQ(0u, qt.getLocalFastQualifiers()); + record_type = record_type.AddConstModifier(); + record_type = record_type.AddVolatileModifier(); + record_type = record_type.AddRestrictModifier(); + qt = ClangUtil::GetQualType(record_type); + EXPECT_NE(0u, qt.getLocalFastQualifiers()); + record_type = ClangUtil::RemoveFastQualifiers(record_type); + qt = ClangUtil::GetQualType(record_type); + EXPECT_EQ(0u, qt.getLocalFastQualifiers()); } -TEST_F(TestClangASTContext, TestConvertAccessTypeToAccessSpecifier) -{ - EXPECT_EQ(AS_none, ClangASTContext::ConvertAccessTypeToAccessSpecifier(eAccessNone)); - EXPECT_EQ(AS_none, ClangASTContext::ConvertAccessTypeToAccessSpecifier(eAccessPackage)); - EXPECT_EQ(AS_public, ClangASTContext::ConvertAccessTypeToAccessSpecifier(eAccessPublic)); - EXPECT_EQ(AS_private, ClangASTContext::ConvertAccessTypeToAccessSpecifier(eAccessPrivate)); - EXPECT_EQ(AS_protected, ClangASTContext::ConvertAccessTypeToAccessSpecifier(eAccessProtected)); +TEST_F(TestClangASTContext, TestConvertAccessTypeToAccessSpecifier) { + EXPECT_EQ(AS_none, + ClangASTContext::ConvertAccessTypeToAccessSpecifier(eAccessNone)); + EXPECT_EQ(AS_none, ClangASTContext::ConvertAccessTypeToAccessSpecifier( + eAccessPackage)); + EXPECT_EQ(AS_public, + ClangASTContext::ConvertAccessTypeToAccessSpecifier(eAccessPublic)); + EXPECT_EQ(AS_private, ClangASTContext::ConvertAccessTypeToAccessSpecifier( + eAccessPrivate)); + EXPECT_EQ(AS_protected, ClangASTContext::ConvertAccessTypeToAccessSpecifier( + eAccessProtected)); } -TEST_F(TestClangASTContext, TestUnifyAccessSpecifiers) -{ - // Unifying two of the same type should return the same type - EXPECT_EQ(AS_public, ClangASTContext::UnifyAccessSpecifiers(AS_public, AS_public)); - EXPECT_EQ(AS_private, ClangASTContext::UnifyAccessSpecifiers(AS_private, AS_private)); - EXPECT_EQ(AS_protected, ClangASTContext::UnifyAccessSpecifiers(AS_protected, AS_protected)); - - // Otherwise the result should be the strictest of the two. - EXPECT_EQ(AS_private, ClangASTContext::UnifyAccessSpecifiers(AS_private, AS_public)); - EXPECT_EQ(AS_private, ClangASTContext::UnifyAccessSpecifiers(AS_private, AS_protected)); - EXPECT_EQ(AS_private, ClangASTContext::UnifyAccessSpecifiers(AS_public, AS_private)); - EXPECT_EQ(AS_private, ClangASTContext::UnifyAccessSpecifiers(AS_protected, AS_private)); - EXPECT_EQ(AS_protected, ClangASTContext::UnifyAccessSpecifiers(AS_protected, AS_public)); - EXPECT_EQ(AS_protected, ClangASTContext::UnifyAccessSpecifiers(AS_public, AS_protected)); - - // None is stricter than everything (by convention) - EXPECT_EQ(AS_none, ClangASTContext::UnifyAccessSpecifiers(AS_none, AS_public)); - EXPECT_EQ(AS_none, ClangASTContext::UnifyAccessSpecifiers(AS_none, AS_protected)); - EXPECT_EQ(AS_none, ClangASTContext::UnifyAccessSpecifiers(AS_none, AS_private)); - EXPECT_EQ(AS_none, ClangASTContext::UnifyAccessSpecifiers(AS_public, AS_none)); - EXPECT_EQ(AS_none, ClangASTContext::UnifyAccessSpecifiers(AS_protected, AS_none)); - EXPECT_EQ(AS_none, ClangASTContext::UnifyAccessSpecifiers(AS_private, AS_none)); +TEST_F(TestClangASTContext, TestUnifyAccessSpecifiers) { + // Unifying two of the same type should return the same type + EXPECT_EQ(AS_public, + ClangASTContext::UnifyAccessSpecifiers(AS_public, AS_public)); + EXPECT_EQ(AS_private, + ClangASTContext::UnifyAccessSpecifiers(AS_private, AS_private)); + EXPECT_EQ(AS_protected, + ClangASTContext::UnifyAccessSpecifiers(AS_protected, AS_protected)); + + // Otherwise the result should be the strictest of the two. + EXPECT_EQ(AS_private, + ClangASTContext::UnifyAccessSpecifiers(AS_private, AS_public)); + EXPECT_EQ(AS_private, + ClangASTContext::UnifyAccessSpecifiers(AS_private, AS_protected)); + EXPECT_EQ(AS_private, + ClangASTContext::UnifyAccessSpecifiers(AS_public, AS_private)); + EXPECT_EQ(AS_private, + ClangASTContext::UnifyAccessSpecifiers(AS_protected, AS_private)); + EXPECT_EQ(AS_protected, + ClangASTContext::UnifyAccessSpecifiers(AS_protected, AS_public)); + EXPECT_EQ(AS_protected, + ClangASTContext::UnifyAccessSpecifiers(AS_public, AS_protected)); + + // None is stricter than everything (by convention) + EXPECT_EQ(AS_none, + ClangASTContext::UnifyAccessSpecifiers(AS_none, AS_public)); + EXPECT_EQ(AS_none, + ClangASTContext::UnifyAccessSpecifiers(AS_none, AS_protected)); + EXPECT_EQ(AS_none, + ClangASTContext::UnifyAccessSpecifiers(AS_none, AS_private)); + EXPECT_EQ(AS_none, + ClangASTContext::UnifyAccessSpecifiers(AS_public, AS_none)); + EXPECT_EQ(AS_none, + ClangASTContext::UnifyAccessSpecifiers(AS_protected, AS_none)); + EXPECT_EQ(AS_none, + ClangASTContext::UnifyAccessSpecifiers(AS_private, AS_none)); } -TEST_F(TestClangASTContext, TestRecordHasFields) -{ - CompilerType int_type = ClangASTContext::GetBasicType(m_ast->getASTContext(), eBasicTypeInt); - - // Test that a record with no fields returns false - CompilerType empty_base = m_ast->CreateRecordType(nullptr, lldb::eAccessPublic, "EmptyBase", clang::TTK_Struct, - lldb::eLanguageTypeC_plus_plus, nullptr); - ClangASTContext::StartTagDeclarationDefinition(empty_base); - ClangASTContext::CompleteTagDeclarationDefinition(empty_base); - - RecordDecl *empty_base_decl = ClangASTContext::GetAsRecordDecl(empty_base); - EXPECT_NE(nullptr, empty_base_decl); - EXPECT_FALSE(ClangASTContext::RecordHasFields(empty_base_decl)); - - // Test that a record with direct fields returns true - CompilerType non_empty_base = m_ast->CreateRecordType(nullptr, lldb::eAccessPublic, "NonEmptyBase", - clang::TTK_Struct, lldb::eLanguageTypeC_plus_plus, nullptr); - ClangASTContext::StartTagDeclarationDefinition(non_empty_base); - FieldDecl *non_empty_base_field_decl = - m_ast->AddFieldToRecordType(non_empty_base, "MyField", int_type, eAccessPublic, 0); - ClangASTContext::CompleteTagDeclarationDefinition(non_empty_base); - RecordDecl *non_empty_base_decl = ClangASTContext::GetAsRecordDecl(non_empty_base); - EXPECT_NE(nullptr, non_empty_base_decl); - EXPECT_NE(nullptr, non_empty_base_field_decl); - EXPECT_TRUE(ClangASTContext::RecordHasFields(non_empty_base_decl)); - - // Test that a record with no direct fields, but fields in a base returns true - CompilerType empty_derived = m_ast->CreateRecordType(nullptr, lldb::eAccessPublic, "EmptyDerived", - clang::TTK_Struct, lldb::eLanguageTypeC_plus_plus, nullptr); - ClangASTContext::StartTagDeclarationDefinition(empty_derived); - CXXBaseSpecifier *non_empty_base_spec = - m_ast->CreateBaseClassSpecifier(non_empty_base.GetOpaqueQualType(), lldb::eAccessPublic, false, false); - bool result = m_ast->SetBaseClassesForClassType(empty_derived.GetOpaqueQualType(), &non_empty_base_spec, 1); - ClangASTContext::CompleteTagDeclarationDefinition(empty_derived); - EXPECT_TRUE(result); - CXXRecordDecl *empty_derived_non_empty_base_cxx_decl = m_ast->GetAsCXXRecordDecl(empty_derived.GetOpaqueQualType()); - RecordDecl *empty_derived_non_empty_base_decl = ClangASTContext::GetAsRecordDecl(empty_derived); - EXPECT_EQ(1, ClangASTContext::GetNumBaseClasses(empty_derived_non_empty_base_cxx_decl, false)); - EXPECT_TRUE(ClangASTContext::RecordHasFields(empty_derived_non_empty_base_decl)); - - // Test that a record with no direct fields, but fields in a virtual base returns true - CompilerType empty_derived2 = m_ast->CreateRecordType(nullptr, lldb::eAccessPublic, "EmptyDerived2", - clang::TTK_Struct, lldb::eLanguageTypeC_plus_plus, nullptr); - ClangASTContext::StartTagDeclarationDefinition(empty_derived2); - CXXBaseSpecifier *non_empty_vbase_spec = - m_ast->CreateBaseClassSpecifier(non_empty_base.GetOpaqueQualType(), lldb::eAccessPublic, true, false); - result = m_ast->SetBaseClassesForClassType(empty_derived2.GetOpaqueQualType(), &non_empty_vbase_spec, 1); - ClangASTContext::CompleteTagDeclarationDefinition(empty_derived2); - EXPECT_TRUE(result); - CXXRecordDecl *empty_derived_non_empty_vbase_cxx_decl = - m_ast->GetAsCXXRecordDecl(empty_derived2.GetOpaqueQualType()); - RecordDecl *empty_derived_non_empty_vbase_decl = ClangASTContext::GetAsRecordDecl(empty_derived2); - EXPECT_EQ(1, ClangASTContext::GetNumBaseClasses(empty_derived_non_empty_vbase_cxx_decl, false)); - EXPECT_TRUE(ClangASTContext::RecordHasFields(empty_derived_non_empty_vbase_decl)); +TEST_F(TestClangASTContext, TestRecordHasFields) { + CompilerType int_type = + ClangASTContext::GetBasicType(m_ast->getASTContext(), eBasicTypeInt); + + // Test that a record with no fields returns false + CompilerType empty_base = m_ast->CreateRecordType( + nullptr, lldb::eAccessPublic, "EmptyBase", clang::TTK_Struct, + lldb::eLanguageTypeC_plus_plus, nullptr); + ClangASTContext::StartTagDeclarationDefinition(empty_base); + ClangASTContext::CompleteTagDeclarationDefinition(empty_base); + + RecordDecl *empty_base_decl = ClangASTContext::GetAsRecordDecl(empty_base); + EXPECT_NE(nullptr, empty_base_decl); + EXPECT_FALSE(ClangASTContext::RecordHasFields(empty_base_decl)); + + // Test that a record with direct fields returns true + CompilerType non_empty_base = m_ast->CreateRecordType( + nullptr, lldb::eAccessPublic, "NonEmptyBase", clang::TTK_Struct, + lldb::eLanguageTypeC_plus_plus, nullptr); + ClangASTContext::StartTagDeclarationDefinition(non_empty_base); + FieldDecl *non_empty_base_field_decl = m_ast->AddFieldToRecordType( + non_empty_base, "MyField", int_type, eAccessPublic, 0); + ClangASTContext::CompleteTagDeclarationDefinition(non_empty_base); + RecordDecl *non_empty_base_decl = + ClangASTContext::GetAsRecordDecl(non_empty_base); + EXPECT_NE(nullptr, non_empty_base_decl); + EXPECT_NE(nullptr, non_empty_base_field_decl); + EXPECT_TRUE(ClangASTContext::RecordHasFields(non_empty_base_decl)); + + // Test that a record with no direct fields, but fields in a base returns true + CompilerType empty_derived = m_ast->CreateRecordType( + nullptr, lldb::eAccessPublic, "EmptyDerived", clang::TTK_Struct, + lldb::eLanguageTypeC_plus_plus, nullptr); + ClangASTContext::StartTagDeclarationDefinition(empty_derived); + CXXBaseSpecifier *non_empty_base_spec = m_ast->CreateBaseClassSpecifier( + non_empty_base.GetOpaqueQualType(), lldb::eAccessPublic, false, false); + bool result = m_ast->SetBaseClassesForClassType( + empty_derived.GetOpaqueQualType(), &non_empty_base_spec, 1); + ClangASTContext::CompleteTagDeclarationDefinition(empty_derived); + EXPECT_TRUE(result); + CXXRecordDecl *empty_derived_non_empty_base_cxx_decl = + m_ast->GetAsCXXRecordDecl(empty_derived.GetOpaqueQualType()); + RecordDecl *empty_derived_non_empty_base_decl = + ClangASTContext::GetAsRecordDecl(empty_derived); + EXPECT_EQ(1u, ClangASTContext::GetNumBaseClasses( + empty_derived_non_empty_base_cxx_decl, false)); + EXPECT_TRUE( + ClangASTContext::RecordHasFields(empty_derived_non_empty_base_decl)); + + // Test that a record with no direct fields, but fields in a virtual base + // returns true + CompilerType empty_derived2 = m_ast->CreateRecordType( + nullptr, lldb::eAccessPublic, "EmptyDerived2", clang::TTK_Struct, + lldb::eLanguageTypeC_plus_plus, nullptr); + ClangASTContext::StartTagDeclarationDefinition(empty_derived2); + CXXBaseSpecifier *non_empty_vbase_spec = m_ast->CreateBaseClassSpecifier( + non_empty_base.GetOpaqueQualType(), lldb::eAccessPublic, true, false); + result = m_ast->SetBaseClassesForClassType(empty_derived2.GetOpaqueQualType(), + &non_empty_vbase_spec, 1); + ClangASTContext::CompleteTagDeclarationDefinition(empty_derived2); + EXPECT_TRUE(result); + CXXRecordDecl *empty_derived_non_empty_vbase_cxx_decl = + m_ast->GetAsCXXRecordDecl(empty_derived2.GetOpaqueQualType()); + RecordDecl *empty_derived_non_empty_vbase_decl = + ClangASTContext::GetAsRecordDecl(empty_derived2); + EXPECT_EQ(1u, ClangASTContext::GetNumBaseClasses( + empty_derived_non_empty_vbase_cxx_decl, false)); + EXPECT_TRUE( + ClangASTContext::RecordHasFields(empty_derived_non_empty_vbase_decl)); } diff --git a/unittests/SymbolFile/CMakeLists.txt b/unittests/SymbolFile/CMakeLists.txt index dcf9ebdb1161..cc12e467905a 100644 --- a/unittests/SymbolFile/CMakeLists.txt +++ b/unittests/SymbolFile/CMakeLists.txt @@ -1 +1,4 @@ -add_subdirectory(PDB) +add_subdirectory(DWARF) +if (LLVM_ENABLE_DIA_SDK) + add_subdirectory(PDB) +endif() diff --git a/unittests/SymbolFile/DWARF/CMakeLists.txt b/unittests/SymbolFile/DWARF/CMakeLists.txt new file mode 100644 index 000000000000..c0e4300dadd7 --- /dev/null +++ b/unittests/SymbolFile/DWARF/CMakeLists.txt @@ -0,0 +1,8 @@ +add_lldb_unittest(SymbolFileDWARFTests + SymbolFileDWARFTests.cpp + ) + +set(test_inputs + test-dwarf.exe) + +add_unittest_inputs(SymbolFileDWARFTests "${test_inputs}") diff --git a/unittests/SymbolFile/PDB/Inputs/test-dwarf.cpp b/unittests/SymbolFile/DWARF/Inputs/test-dwarf.cpp index f86ff3d875b4..a987e6a9a363 100644 --- a/unittests/SymbolFile/PDB/Inputs/test-dwarf.cpp +++ b/unittests/SymbolFile/DWARF/Inputs/test-dwarf.cpp @@ -1,14 +1,6 @@ // Compile with "cl /c /Zi /GR- test.cpp" // Link with "link test.obj /debug /nodefaultlib /entry:main /out:test.exe" -int __cdecl _purecall(void) -{ - return 0; -} +int __cdecl _purecall(void) { return 0; } -int -main(int argc, char **argv) -{ - - return 0; -} +int main(int argc, char **argv) { return 0; } diff --git a/unittests/SymbolFile/PDB/Inputs/test-dwarf.exe b/unittests/SymbolFile/DWARF/Inputs/test-dwarf.exe Binary files differindex 15e1910b4b8b..15e1910b4b8b 100644 --- a/unittests/SymbolFile/PDB/Inputs/test-dwarf.exe +++ b/unittests/SymbolFile/DWARF/Inputs/test-dwarf.exe diff --git a/unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp b/unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp new file mode 100644 index 000000000000..10465d785ac9 --- /dev/null +++ b/unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp @@ -0,0 +1,83 @@ +//===-- PythonDataObjectsTests.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/SymbolVendor.h" + +#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" +#include "Plugins/SymbolFile/PDB/SymbolFilePDB.h" + +extern const char *TestMainArgv0; + +using namespace lldb_private; + +class SymbolFileDWARFTests : public testing::Test { +public: + void SetUp() override { +// Initialize and TearDown the plugin every time, so we get a brand new +// AST every time so that modifications to the AST from each test don't +// leak into the next test. + HostInfo::Initialize(); + ObjectFilePECOFF::Initialize(); + SymbolFileDWARF::Initialize(); + ClangASTContext::Initialize(); + SymbolFilePDB::Initialize(); + + llvm::StringRef exe_folder = llvm::sys::path::parent_path(TestMainArgv0); + llvm::SmallString<128> inputs_folder = exe_folder; + llvm::sys::path::append(inputs_folder, "Inputs"); + + m_dwarf_test_exe = inputs_folder; + llvm::sys::path::append(m_dwarf_test_exe, "test-dwarf.exe"); + } + + void TearDown() override { + SymbolFilePDB::Terminate(); + ClangASTContext::Initialize(); + SymbolFileDWARF::Terminate(); + ObjectFilePECOFF::Terminate(); + HostInfo::Terminate(); + } + +protected: + llvm::SmallString<128> m_dwarf_test_exe; +}; + +TEST_F(SymbolFileDWARFTests, TestAbilitiesForDWARF) { + // Test that when we have Dwarf debug info, SymbolFileDWARF is used. + FileSpec fspec(m_dwarf_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + EXPECT_NE(nullptr, symfile); + EXPECT_EQ(symfile->GetPluginName(), SymbolFileDWARF::GetPluginNameStatic()); + + uint32_t expected_abilities = SymbolFile::kAllAbilities; + EXPECT_EQ(expected_abilities, symfile->CalculateAbilities()); +} diff --git a/unittests/SymbolFile/PDB/CMakeLists.txt b/unittests/SymbolFile/PDB/CMakeLists.txt index fcfb5e3062c9..37adf39463d4 100644 --- a/unittests/SymbolFile/PDB/CMakeLists.txt +++ b/unittests/SymbolFile/PDB/CMakeLists.txt @@ -5,8 +5,7 @@ add_lldb_unittest(SymbolFilePDBTests set(test_inputs test-pdb.exe test-pdb.pdb - test-dwarf.exe test-pdb-types.exe test-pdb-types.pdb) -add_unittest_inputs(SymbolFilePDBTests "${test_inputs}") +add_unittest_inputs(SymbolFilePDBTests "${test_inputs}") diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb-alt.cpp b/unittests/SymbolFile/PDB/Inputs/test-pdb-alt.cpp index d36f15e53fb7..33d7df0e4a82 100644 --- a/unittests/SymbolFile/PDB/Inputs/test-pdb-alt.cpp +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb-alt.cpp @@ -1,9 +1,7 @@ // Compile with "cl /c /Zi /GR- test-pdb-alt.cpp" -// Link with "link test-pdb.obj test-pdb-alt.obj /debug /nodefaultlib /entry:main /out:test-pdb.exe" +// Link with "link test-pdb.obj test-pdb-alt.obj /debug /nodefaultlib +// /entry:main /out:test-pdb.exe" #include "test-pdb.h" -int bar(int n) -{ - return n-1; -} +int bar(int n) { return n - 1; } diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb-nested.h b/unittests/SymbolFile/PDB/Inputs/test-pdb-nested.h index fc63b50d13c9..d0a93d8f39e8 100644 --- a/unittests/SymbolFile/PDB/Inputs/test-pdb-nested.h +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb-nested.h @@ -1,9 +1,6 @@ #ifndef TEST_PDB_NESTED_H #define TEST_PDB_NESTED_H -inline int baz(int n) -{ - return n+1; -} +inline int baz(int n) { return n + 1; } #endif
\ No newline at end of file diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb-types.cpp b/unittests/SymbolFile/PDB/Inputs/test-pdb-types.cpp index 9639a3f4b722..89d69c12963e 100644 --- a/unittests/SymbolFile/PDB/Inputs/test-pdb-types.cpp +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb-types.cpp @@ -1,5 +1,6 @@ // Compile with "cl /c /Zi /GR- /EHsc test-pdb-types.cpp"
-// Link with "link test-pdb-types.obj /debug /nodefaultlib /entry:main /out:test-pdb-types.exe"
+// Link with "link test-pdb-types.obj /debug /nodefaultlib /entry:main
+// /out:test-pdb-types.exe"
using namespace std;
@@ -21,42 +22,29 @@ static const int sizeof_double = sizeof(double); static const int sizeof_bool = sizeof(bool);
static const int sizeof_wchar = sizeof(wchar_t);
-enum Enum
-{
- EValue1 = 1,
- EValue2 = 2,
+enum Enum {
+ EValue1 = 1,
+ EValue2 = 2,
};
-enum ShortEnum : short
-{
- ESValue1 = 1,
- ESValue2 = 2
-};
+enum ShortEnum : short { ESValue1 = 1, ESValue2 = 2 };
-namespace NS
-{
-class NSClass
-{
- float f;
- double d;
+namespace NS {
+class NSClass {
+ float f;
+ double d;
};
}
-class Class
-{
+class Class {
public:
- class NestedClass
- {
- Enum e;
- };
- ShortEnum se;
+ class NestedClass {
+ Enum e;
+ };
+ ShortEnum se;
};
-int
-test_func(int a, int b)
-{
- return a + b;
-}
+int test_func(int a, int b) { return a + b; }
typedef Class ClassTypedef;
typedef NS::NSClass NSClassTypedef;
@@ -71,16 +59,14 @@ static const int sizeof_ClassTypedef = sizeof(ClassTypedef); static const int sizeof_NSClassTypedef = sizeof(NSClassTypedef);
static const int sizeof_GlobalArray = sizeof(GlobalArray);
-int
-main(int argc, char **argv)
-{
- ShortEnum e1;
- Enum e2;
- Class c1;
- Class::NestedClass c2;
- NS::NSClass c3;
+int main(int argc, char **argv) {
+ ShortEnum e1;
+ Enum e2;
+ Class c1;
+ Class::NestedClass c2;
+ NS::NSClass c3;
- ClassTypedef t1;
- NSClassTypedef t2;
- return test_func(1, 2);
+ ClassTypedef t1;
+ NSClassTypedef t2;
+ return test_func(1, 2);
}
diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb.cpp b/unittests/SymbolFile/PDB/Inputs/test-pdb.cpp index c9bf057cfbfb..77956acd8750 100644 --- a/unittests/SymbolFile/PDB/Inputs/test-pdb.cpp +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb.cpp @@ -1,15 +1,9 @@ // Compile with "cl /c /Zi /GR- test-pdb.cpp" -// Link with "link test-pdb.obj /debug /nodefaultlib /entry:main /out:test-pdb.exe" +// Link with "link test-pdb.obj /debug /nodefaultlib /entry:main +// /out:test-pdb.exe" #include "test-pdb.h" -int __cdecl _purecall(void) -{ - return 0; -} +int __cdecl _purecall(void) { return 0; } -int -main(int argc, char **argv) -{ - return foo(argc) + bar(argc); -} +int main(int argc, char **argv) { return foo(argc) + bar(argc); } diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb.h b/unittests/SymbolFile/PDB/Inputs/test-pdb.h index 273343bb03b0..077d9d6b1fac 100644 --- a/unittests/SymbolFile/PDB/Inputs/test-pdb.h +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb.h @@ -5,9 +5,6 @@ int bar(int n); -inline int foo(int n) -{ - return baz(n)+1; -} +inline int foo(int n) { return baz(n) + 1; } #endif
\ No newline at end of file diff --git a/unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp b/unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp index b303ae7bac4b..5aedffecfe90 100644 --- a/unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp +++ b/unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp @@ -10,7 +10,6 @@ #include "gtest/gtest.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/Config/config.h" #include "llvm/DebugInfo/PDB/PDBSymbolData.h" #include "llvm/DebugInfo/PDB/PDBSymbolExe.h" #include "llvm/Support/FileSystem.h" @@ -42,542 +41,535 @@ extern const char *TestMainArgv0; using namespace lldb_private; -class SymbolFilePDBTests : public testing::Test -{ +class SymbolFilePDBTests : public testing::Test { public: - void - SetUp() override - { + void SetUp() override { // Initialize and TearDown the plugin every time, so we get a brand new // AST every time so that modifications to the AST from each test don't // leak into the next test. #if defined(_MSC_VER) - ::CoInitializeEx(nullptr, COINIT_MULTITHREADED); + ::CoInitializeEx(nullptr, COINIT_MULTITHREADED); #endif - HostInfo::Initialize(); - ObjectFilePECOFF::Initialize(); - SymbolFileDWARF::Initialize(); - ClangASTContext::Initialize(); - SymbolFilePDB::Initialize(); - - llvm::StringRef exe_folder = llvm::sys::path::parent_path(TestMainArgv0); - llvm::SmallString<128> inputs_folder = exe_folder; - llvm::sys::path::append(inputs_folder, "Inputs"); - - m_pdb_test_exe = inputs_folder; - m_dwarf_test_exe = inputs_folder; - m_types_test_exe = inputs_folder; - llvm::sys::path::append(m_pdb_test_exe, "test-pdb.exe"); - llvm::sys::path::append(m_dwarf_test_exe, "test-dwarf.exe"); - llvm::sys::path::append(m_types_test_exe, "test-pdb-types.exe"); - } - - void - TearDown() override - { - SymbolFilePDB::Terminate(); - ClangASTContext::Initialize(); - SymbolFileDWARF::Terminate(); - ObjectFilePECOFF::Terminate(); - HostInfo::Terminate(); + HostInfo::Initialize(); + ObjectFilePECOFF::Initialize(); + SymbolFileDWARF::Initialize(); + ClangASTContext::Initialize(); + SymbolFilePDB::Initialize(); + + llvm::StringRef exe_folder = llvm::sys::path::parent_path(TestMainArgv0); + llvm::SmallString<128> inputs_folder = exe_folder; + llvm::sys::path::append(inputs_folder, "Inputs"); + + m_pdb_test_exe = inputs_folder; + m_types_test_exe = inputs_folder; + llvm::sys::path::append(m_pdb_test_exe, "test-pdb.exe"); + llvm::sys::path::append(m_types_test_exe, "test-pdb-types.exe"); + } + + void TearDown() override { + SymbolFilePDB::Terminate(); + ClangASTContext::Initialize(); + SymbolFileDWARF::Terminate(); + ObjectFilePECOFF::Terminate(); + HostInfo::Terminate(); #if defined(_MSC_VER) - ::CoUninitialize(); + ::CoUninitialize(); #endif - } + } protected: - llvm::SmallString<128> m_pdb_test_exe; - llvm::SmallString<128> m_dwarf_test_exe; - llvm::SmallString<128> m_types_test_exe; - - bool - FileSpecMatchesAsBaseOrFull(const FileSpec &left, const FileSpec &right) const - { - // If the filenames don't match, the paths can't be equal - if (!left.FileEquals(right)) - return false; - // If BOTH have a directory, also compare the directories. - if (left.GetDirectory() && right.GetDirectory()) - return left.DirectoryEquals(right); - - // If one has a directory but not the other, they match. + llvm::SmallString<128> m_pdb_test_exe; + llvm::SmallString<128> m_types_test_exe; + + bool FileSpecMatchesAsBaseOrFull(const FileSpec &left, + const FileSpec &right) const { + // If the filenames don't match, the paths can't be equal + if (!left.FileEquals(right)) + return false; + // If BOTH have a directory, also compare the directories. + if (left.GetDirectory() && right.GetDirectory()) + return left.DirectoryEquals(right); + + // If one has a directory but not the other, they match. + return true; + } + + void VerifyLineEntry(lldb::ModuleSP module, const SymbolContext &sc, + const FileSpec &spec, LineTable <, uint32_t line, + lldb::addr_t addr) { + LineEntry entry; + Address address; + EXPECT_TRUE(module->ResolveFileAddress(addr, address)); + + EXPECT_TRUE(lt.FindLineEntryByAddress(address, entry)); + EXPECT_EQ(line, entry.line); + EXPECT_EQ(address, entry.range.GetBaseAddress()); + + EXPECT_TRUE(FileSpecMatchesAsBaseOrFull(spec, entry.file)); + } + + bool ContainsCompileUnit(const SymbolContextList &sc_list, + const FileSpec &spec) const { + for (size_t i = 0; i < sc_list.GetSize(); ++i) { + const SymbolContext &sc = sc_list[i]; + if (FileSpecMatchesAsBaseOrFull(*sc.comp_unit, spec)) return true; } - - void - VerifyLineEntry(lldb::ModuleSP module, const SymbolContext &sc, const FileSpec &spec, LineTable <, uint32_t line, - lldb::addr_t addr) - { - LineEntry entry; - Address address; - EXPECT_TRUE(module->ResolveFileAddress(addr, address)); - - EXPECT_TRUE(lt.FindLineEntryByAddress(address, entry)); - EXPECT_EQ(line, entry.line); - EXPECT_EQ(address, entry.range.GetBaseAddress()); - - EXPECT_TRUE(FileSpecMatchesAsBaseOrFull(spec, entry.file)); - } - - bool - ContainsCompileUnit(const SymbolContextList &sc_list, const FileSpec &spec) const - { - for (size_t i = 0; i < sc_list.GetSize(); ++i) - { - const SymbolContext &sc = sc_list[i]; - if (FileSpecMatchesAsBaseOrFull(*sc.comp_unit, spec)) - return true; - } - return false; - } - - int - GetGlobalConstantInteger(const llvm::pdb::IPDBSession &session, llvm::StringRef var) const - { - auto global = session.getGlobalScope(); - auto results = - global->findChildren(llvm::pdb::PDB_SymType::Data, var, llvm::pdb::PDB_NameSearchFlags::NS_Default); - uint32_t count = results->getChildCount(); - if (count == 0) - return -1; - - auto item = results->getChildAtIndex(0); - auto symbol = llvm::dyn_cast<llvm::pdb::PDBSymbolData>(item.get()); - if (!symbol) - return -1; - llvm::pdb::Variant value = symbol->getValue(); - switch (value.Type) - { - case llvm::pdb::PDB_VariantType::Int16: - return value.Value.Int16; - case llvm::pdb::PDB_VariantType::Int32: - return value.Value.Int32; - case llvm::pdb::PDB_VariantType::UInt16: - return value.Value.UInt16; - case llvm::pdb::PDB_VariantType::UInt32: - return value.Value.UInt32; - default: - return 0; - } + return false; + } + + uint64_t GetGlobalConstantInteger(const llvm::pdb::IPDBSession &session, + llvm::StringRef var) const { + auto global = session.getGlobalScope(); + auto results = + global->findChildren(llvm::pdb::PDB_SymType::Data, var, + llvm::pdb::PDB_NameSearchFlags::NS_Default); + uint32_t count = results->getChildCount(); + if (count == 0) + return -1; + + auto item = results->getChildAtIndex(0); + auto symbol = llvm::dyn_cast<llvm::pdb::PDBSymbolData>(item.get()); + if (!symbol) + return -1; + llvm::pdb::Variant value = symbol->getValue(); + switch (value.Type) { + case llvm::pdb::PDB_VariantType::Int16: + return value.Value.Int16; + case llvm::pdb::PDB_VariantType::Int32: + return value.Value.Int32; + case llvm::pdb::PDB_VariantType::UInt16: + return value.Value.UInt16; + case llvm::pdb::PDB_VariantType::UInt32: + return value.Value.UInt32; + default: + return 0; } + } }; -#if defined(HAVE_DIA_SDK) -#define REQUIRES_DIA_SDK(TestName) TestName -#else -#define REQUIRES_DIA_SDK(TestName) DISABLED_##TestName -#endif - -TEST_F(SymbolFilePDBTests, TestAbilitiesForDWARF) -{ - // Test that when we have Dwarf debug info, SymbolFileDWARF is used. - FileSpec fspec(m_dwarf_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - EXPECT_NE(nullptr, plugin); - SymbolFile *symfile = plugin->GetSymbolFile(); - EXPECT_NE(nullptr, symfile); - EXPECT_EQ(symfile->GetPluginName(), SymbolFileDWARF::GetPluginNameStatic()); - - uint32_t expected_abilities = SymbolFile::kAllAbilities; - EXPECT_EQ(expected_abilities, symfile->CalculateAbilities()); +TEST_F(SymbolFilePDBTests, TestAbilitiesForPDB) { + // Test that when we have PDB debug info, SymbolFilePDB is used. + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + EXPECT_NE(nullptr, symfile); + EXPECT_EQ(symfile->GetPluginName(), SymbolFilePDB::GetPluginNameStatic()); + + uint32_t expected_abilities = + SymbolFile::CompileUnits | SymbolFile::LineTables; + EXPECT_EQ(expected_abilities, symfile->CalculateAbilities()); } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestAbilitiesForPDB)) -{ - // Test that when we have PDB debug info, SymbolFilePDB is used. - FileSpec fspec(m_pdb_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - EXPECT_NE(nullptr, plugin); - SymbolFile *symfile = plugin->GetSymbolFile(); - EXPECT_NE(nullptr, symfile); - EXPECT_EQ(symfile->GetPluginName(), SymbolFilePDB::GetPluginNameStatic()); - - uint32_t expected_abilities = SymbolFile::CompileUnits | SymbolFile::LineTables; - EXPECT_EQ(expected_abilities, symfile->CalculateAbilities()); +TEST_F(SymbolFilePDBTests, TestResolveSymbolContextBasename) { + // Test that attempting to call ResolveSymbolContext with only a basename + // finds all full paths + // with the same basename + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + + FileSpec header_spec("test-pdb.cpp", false); + SymbolContextList sc_list; + uint32_t result_count = symfile->ResolveSymbolContext( + header_spec, 0, false, lldb::eSymbolContextCompUnit, sc_list); + EXPECT_EQ(1u, result_count); + EXPECT_TRUE(ContainsCompileUnit(sc_list, header_spec)); } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestResolveSymbolContextBasename)) -{ - // Test that attempting to call ResolveSymbolContext with only a basename finds all full paths - // with the same basename - FileSpec fspec(m_pdb_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - EXPECT_NE(nullptr, plugin); - SymbolFile *symfile = plugin->GetSymbolFile(); +TEST_F(SymbolFilePDBTests, TestResolveSymbolContextFullPath) { + // Test that attempting to call ResolveSymbolContext with a full path only + // finds the one source + // file that matches the full path. + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + + FileSpec header_spec( + R"spec(D:\src\llvm\tools\lldb\unittests\SymbolFile\PDB\Inputs\test-pdb.cpp)spec", + false); + SymbolContextList sc_list; + uint32_t result_count = symfile->ResolveSymbolContext( + header_spec, 0, false, lldb::eSymbolContextCompUnit, sc_list); + EXPECT_GE(1u, result_count); + EXPECT_TRUE(ContainsCompileUnit(sc_list, header_spec)); +} - FileSpec header_spec("test-pdb.cpp", false); +TEST_F(SymbolFilePDBTests, + TestLookupOfHeaderFileWithInlines) { + // Test that when looking up a header file via ResolveSymbolContext (i.e. a + // file that was not by itself + // compiled, but only contributes to the combined code of other source files), + // a SymbolContext is returned + // for each compiland which has line contributions from the requested header. + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + + FileSpec header_specs[] = {FileSpec("test-pdb.h", false), + FileSpec("test-pdb-nested.h", false)}; + FileSpec main_cpp_spec("test-pdb.cpp", false); + FileSpec alt_cpp_spec("test-pdb-alt.cpp", false); + for (const auto &hspec : header_specs) { SymbolContextList sc_list; - uint32_t result_count = symfile->ResolveSymbolContext(header_spec, 0, false, lldb::eSymbolContextCompUnit, sc_list); - EXPECT_EQ(1u, result_count); - EXPECT_TRUE(ContainsCompileUnit(sc_list, header_spec)); + uint32_t result_count = symfile->ResolveSymbolContext( + hspec, 0, true, lldb::eSymbolContextCompUnit, sc_list); + EXPECT_EQ(2u, result_count); + EXPECT_TRUE(ContainsCompileUnit(sc_list, main_cpp_spec)); + EXPECT_TRUE(ContainsCompileUnit(sc_list, alt_cpp_spec)); + } } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestResolveSymbolContextFullPath)) -{ - // Test that attempting to call ResolveSymbolContext with a full path only finds the one source - // file that matches the full path. - FileSpec fspec(m_pdb_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - EXPECT_NE(nullptr, plugin); - SymbolFile *symfile = plugin->GetSymbolFile(); - - FileSpec header_spec(R"spec(D:\src\llvm\tools\lldb\unittests\SymbolFile\PDB\Inputs\test-pdb.cpp)spec", false); +TEST_F(SymbolFilePDBTests, TestLookupOfHeaderFileWithNoInlines) { + // Test that when looking up a header file via ResolveSymbolContext (i.e. a + // file that was not by itself + // compiled, but only contributes to the combined code of other source files), + // that if check_inlines + // is false, no SymbolContexts are returned. + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + + FileSpec header_specs[] = {FileSpec("test-pdb.h", false), + FileSpec("test-pdb-nested.h", false)}; + for (const auto &hspec : header_specs) { SymbolContextList sc_list; - uint32_t result_count = symfile->ResolveSymbolContext(header_spec, 0, false, lldb::eSymbolContextCompUnit, sc_list); - EXPECT_GE(1u, result_count); - EXPECT_TRUE(ContainsCompileUnit(sc_list, header_spec)); + uint32_t result_count = symfile->ResolveSymbolContext( + hspec, 0, false, lldb::eSymbolContextCompUnit, sc_list); + EXPECT_EQ(0u, result_count); + } } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLookupOfHeaderFileWithInlines)) -{ - // Test that when looking up a header file via ResolveSymbolContext (i.e. a file that was not by itself - // compiled, but only contributes to the combined code of other source files), a SymbolContext is returned - // for each compiland which has line contributions from the requested header. - FileSpec fspec(m_pdb_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - EXPECT_NE(nullptr, plugin); - SymbolFile *symfile = plugin->GetSymbolFile(); - - FileSpec header_specs[] = {FileSpec("test-pdb.h", false), FileSpec("test-pdb-nested.h", false)}; - FileSpec main_cpp_spec("test-pdb.cpp", false); - FileSpec alt_cpp_spec("test-pdb-alt.cpp", false); - for (const auto &hspec : header_specs) - { - SymbolContextList sc_list; - uint32_t result_count = symfile->ResolveSymbolContext(hspec, 0, true, lldb::eSymbolContextCompUnit, sc_list); - EXPECT_EQ(2u, result_count); - EXPECT_TRUE(ContainsCompileUnit(sc_list, main_cpp_spec)); - EXPECT_TRUE(ContainsCompileUnit(sc_list, alt_cpp_spec)); - } +TEST_F(SymbolFilePDBTests, TestLineTablesMatchAll) { + // Test that when calling ResolveSymbolContext with a line number of 0, all + // line entries from + // the specified files are returned. + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFile *symfile = plugin->GetSymbolFile(); + + FileSpec source_file("test-pdb.cpp", false); + FileSpec header1("test-pdb.h", false); + FileSpec header2("test-pdb-nested.h", false); + uint32_t cus = symfile->GetNumCompileUnits(); + EXPECT_EQ(2u, cus); + + SymbolContextList sc_list; + uint32_t scope = lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry; + + uint32_t count = + symfile->ResolveSymbolContext(source_file, 0, true, scope, sc_list); + EXPECT_EQ(1u, count); + SymbolContext sc; + EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc)); + + LineTable *lt = sc.comp_unit->GetLineTable(); + EXPECT_NE(nullptr, lt); + count = lt->GetSize(); + // We expect one extra entry for termination (per function) + EXPECT_EQ(16u, count); + + VerifyLineEntry(module, sc, source_file, *lt, 7, 0x401040); + VerifyLineEntry(module, sc, source_file, *lt, 8, 0x401043); + VerifyLineEntry(module, sc, source_file, *lt, 9, 0x401045); + + VerifyLineEntry(module, sc, source_file, *lt, 13, 0x401050); + VerifyLineEntry(module, sc, source_file, *lt, 14, 0x401054); + VerifyLineEntry(module, sc, source_file, *lt, 15, 0x401070); + + VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090); + VerifyLineEntry(module, sc, header1, *lt, 10, 0x401093); + VerifyLineEntry(module, sc, header1, *lt, 11, 0x4010a2); + + VerifyLineEntry(module, sc, header2, *lt, 5, 0x401080); + VerifyLineEntry(module, sc, header2, *lt, 6, 0x401083); + VerifyLineEntry(module, sc, header2, *lt, 7, 0x401089); } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLookupOfHeaderFileWithNoInlines)) -{ - // Test that when looking up a header file via ResolveSymbolContext (i.e. a file that was not by itself - // compiled, but only contributes to the combined code of other source files), that if check_inlines - // is false, no SymbolContexts are returned. - FileSpec fspec(m_pdb_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - EXPECT_NE(nullptr, plugin); - SymbolFile *symfile = plugin->GetSymbolFile(); - - FileSpec header_specs[] = {FileSpec("test-pdb.h", false), FileSpec("test-pdb-nested.h", false)}; - for (const auto &hspec : header_specs) - { - SymbolContextList sc_list; - uint32_t result_count = symfile->ResolveSymbolContext(hspec, 0, false, lldb::eSymbolContextCompUnit, sc_list); - EXPECT_EQ(0u, result_count); - } +TEST_F(SymbolFilePDBTests, TestLineTablesMatchSpecific) { + // Test that when calling ResolveSymbolContext with a specific line number, + // only line entries + // which match the requested line are returned. + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFile *symfile = plugin->GetSymbolFile(); + + FileSpec source_file("test-pdb.cpp", false); + FileSpec header1("test-pdb.h", false); + FileSpec header2("test-pdb-nested.h", false); + uint32_t cus = symfile->GetNumCompileUnits(); + EXPECT_EQ(2u, cus); + + SymbolContextList sc_list; + uint32_t scope = lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry; + + // First test with line 7, and verify that only line 7 entries are added. + uint32_t count = + symfile->ResolveSymbolContext(source_file, 7, true, scope, sc_list); + EXPECT_EQ(1u, count); + SymbolContext sc; + EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc)); + + LineTable *lt = sc.comp_unit->GetLineTable(); + EXPECT_NE(nullptr, lt); + count = lt->GetSize(); + // We expect one extra entry for termination + EXPECT_EQ(3u, count); + + VerifyLineEntry(module, sc, source_file, *lt, 7, 0x401040); + VerifyLineEntry(module, sc, header2, *lt, 7, 0x401089); + + sc_list.Clear(); + // Then test with line 9, and verify that only line 9 entries are added. + count = symfile->ResolveSymbolContext(source_file, 9, true, scope, sc_list); + EXPECT_EQ(1u, count); + EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc)); + + lt = sc.comp_unit->GetLineTable(); + EXPECT_NE(nullptr, lt); + count = lt->GetSize(); + // We expect one extra entry for termination + EXPECT_EQ(3u, count); + + VerifyLineEntry(module, sc, source_file, *lt, 9, 0x401045); + VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090); } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLineTablesMatchAll)) -{ - // Test that when calling ResolveSymbolContext with a line number of 0, all line entries from - // the specified files are returned. - FileSpec fspec(m_pdb_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - SymbolFile *symfile = plugin->GetSymbolFile(); - - FileSpec source_file("test-pdb.cpp", false); - FileSpec header1("test-pdb.h", false); - FileSpec header2("test-pdb-nested.h", false); - uint32_t cus = symfile->GetNumCompileUnits(); - EXPECT_EQ(2u, cus); - - SymbolContextList sc_list; - uint32_t scope = lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry; - - uint32_t count = symfile->ResolveSymbolContext(source_file, 0, true, scope, sc_list); - EXPECT_EQ(1u, count); - SymbolContext sc; - EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc)); - - LineTable *lt = sc.comp_unit->GetLineTable(); - EXPECT_NE(nullptr, lt); - count = lt->GetSize(); - // We expect one extra entry for termination (per function) - EXPECT_EQ(16u, count); - - VerifyLineEntry(module, sc, source_file, *lt, 7, 0x401040); - VerifyLineEntry(module, sc, source_file, *lt, 8, 0x401043); - VerifyLineEntry(module, sc, source_file, *lt, 9, 0x401045); - - VerifyLineEntry(module, sc, source_file, *lt, 13, 0x401050); - VerifyLineEntry(module, sc, source_file, *lt, 14, 0x401054); - VerifyLineEntry(module, sc, source_file, *lt, 15, 0x401070); - - VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090); - VerifyLineEntry(module, sc, header1, *lt, 10, 0x401093); - VerifyLineEntry(module, sc, header1, *lt, 11, 0x4010a2); - - VerifyLineEntry(module, sc, header2, *lt, 5, 0x401080); - VerifyLineEntry(module, sc, header2, *lt, 6, 0x401083); - VerifyLineEntry(module, sc, header2, *lt, 7, 0x401089); +TEST_F(SymbolFilePDBTests, TestSimpleClassTypes) { + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = + static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString("Class"), nullptr, false, 0, + searched_files, results)); + EXPECT_EQ(1u, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("Class"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); + EXPECT_EQ(GetGlobalConstantInteger(session, "sizeof_Class"), + udt_type->GetByteSize()); } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLineTablesMatchSpecific)) -{ - // Test that when calling ResolveSymbolContext with a specific line number, only line entries - // which match the requested line are returned. - FileSpec fspec(m_pdb_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - SymbolFile *symfile = plugin->GetSymbolFile(); - - FileSpec source_file("test-pdb.cpp", false); - FileSpec header1("test-pdb.h", false); - FileSpec header2("test-pdb-nested.h", false); - uint32_t cus = symfile->GetNumCompileUnits(); - EXPECT_EQ(2u, cus); - - SymbolContextList sc_list; - uint32_t scope = lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry; - - // First test with line 7, and verify that only line 7 entries are added. - uint32_t count = symfile->ResolveSymbolContext(source_file, 7, true, scope, sc_list); - EXPECT_EQ(1u, count); - SymbolContext sc; - EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc)); - - LineTable *lt = sc.comp_unit->GetLineTable(); - EXPECT_NE(nullptr, lt); - count = lt->GetSize(); - // We expect one extra entry for termination - EXPECT_EQ(3u, count); - - VerifyLineEntry(module, sc, source_file, *lt, 7, 0x401040); - VerifyLineEntry(module, sc, header2, *lt, 7, 0x401089); - - sc_list.Clear(); - // Then test with line 9, and verify that only line 9 entries are added. - count = symfile->ResolveSymbolContext(source_file, 9, true, scope, sc_list); - EXPECT_EQ(1u, count); - EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc)); - - lt = sc.comp_unit->GetLineTable(); - EXPECT_NE(nullptr, lt); - count = lt->GetSize(); - // We expect one extra entry for termination - EXPECT_EQ(3u, count); - - VerifyLineEntry(module, sc, source_file, *lt, 9, 0x401045); - VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090); +TEST_F(SymbolFilePDBTests, TestNestedClassTypes) { + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = + static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString("Class::NestedClass"), + nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1u, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("Class::NestedClass"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); + EXPECT_EQ(GetGlobalConstantInteger(session, "sizeof_NestedClass"), + udt_type->GetByteSize()); } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestSimpleClassTypes)) -{ - FileSpec fspec(m_types_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); - const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); - SymbolContext sc; - llvm::DenseSet<SymbolFile *> searched_files; - TypeMap results; - EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString("Class"), nullptr, false, 0, searched_files, results)); - EXPECT_EQ(1u, results.GetSize()); - lldb::TypeSP udt_type = results.GetTypeAtIndex(0); - EXPECT_EQ(ConstString("Class"), udt_type->GetName()); - CompilerType compiler_type = udt_type->GetForwardCompilerType(); - EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); - EXPECT_EQ(uint64_t(GetGlobalConstantInteger(session, "sizeof_Class")), udt_type->GetByteSize()); +TEST_F(SymbolFilePDBTests, TestClassInNamespace) { + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = + static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString("NS::NSClass"), nullptr, + false, 0, searched_files, results)); + EXPECT_EQ(1u, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("NS::NSClass"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); + EXPECT_EQ(GetGlobalConstantInteger(session, "sizeof_NSClass"), + udt_type->GetByteSize()); } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestNestedClassTypes)) -{ - FileSpec fspec(m_types_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); - const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); - SymbolContext sc; - llvm::DenseSet<SymbolFile *> searched_files; +TEST_F(SymbolFilePDBTests, TestEnumTypes) { + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = + static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + const char *EnumsToCheck[] = {"Enum", "ShortEnum"}; + for (auto Enum : EnumsToCheck) { TypeMap results; - EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString("Class::NestedClass"), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString(Enum), nullptr, false, 0, + searched_files, results)); EXPECT_EQ(1u, results.GetSize()); - lldb::TypeSP udt_type = results.GetTypeAtIndex(0); - EXPECT_EQ(ConstString("Class::NestedClass"), udt_type->GetName()); - CompilerType compiler_type = udt_type->GetForwardCompilerType(); - EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); - EXPECT_EQ(uint64_t(GetGlobalConstantInteger(session, "sizeof_NestedClass")), udt_type->GetByteSize()); -} - -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestClassInNamespace)) -{ - FileSpec fspec(m_types_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); - const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); - SymbolContext sc; - llvm::DenseSet<SymbolFile *> searched_files; - TypeMap results; - EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString("NS::NSClass"), nullptr, false, 0, searched_files, results)); - EXPECT_EQ(1u, results.GetSize()); - lldb::TypeSP udt_type = results.GetTypeAtIndex(0); - EXPECT_EQ(ConstString("NS::NSClass"), udt_type->GetName()); - CompilerType compiler_type = udt_type->GetForwardCompilerType(); - EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); - EXPECT_EQ(GetGlobalConstantInteger(session, "sizeof_NSClass"), udt_type->GetByteSize()); -} - -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestEnumTypes)) -{ - FileSpec fspec(m_types_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); - const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); - SymbolContext sc; - llvm::DenseSet<SymbolFile *> searched_files; - const char *EnumsToCheck[] = {"Enum", "ShortEnum"}; - for (auto Enum : EnumsToCheck) - { - TypeMap results; - EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString(Enum), nullptr, false, 0, searched_files, results)); - EXPECT_EQ(1u, results.GetSize()); - lldb::TypeSP enum_type = results.GetTypeAtIndex(0); - EXPECT_EQ(ConstString(Enum), enum_type->GetName()); - CompilerType compiler_type = enum_type->GetFullCompilerType(); - EXPECT_TRUE(ClangASTContext::IsEnumType(compiler_type.GetOpaqueQualType())); - clang::EnumDecl *enum_decl = ClangASTContext::GetAsEnumDecl(compiler_type); - EXPECT_NE(nullptr, enum_decl); - EXPECT_EQ(2, std::distance(enum_decl->enumerator_begin(), enum_decl->enumerator_end())); - - std::string sizeof_var = "sizeof_"; - sizeof_var.append(Enum); - EXPECT_EQ(GetGlobalConstantInteger(session, sizeof_var.c_str()), enum_type->GetByteSize()); - } + lldb::TypeSP enum_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString(Enum), enum_type->GetName()); + CompilerType compiler_type = enum_type->GetFullCompilerType(); + EXPECT_TRUE(ClangASTContext::IsEnumType(compiler_type.GetOpaqueQualType())); + clang::EnumDecl *enum_decl = ClangASTContext::GetAsEnumDecl(compiler_type); + EXPECT_NE(nullptr, enum_decl); + EXPECT_EQ(2, std::distance(enum_decl->enumerator_begin(), + enum_decl->enumerator_end())); + + std::string sizeof_var = "sizeof_"; + sizeof_var.append(Enum); + EXPECT_EQ(GetGlobalConstantInteger(session, sizeof_var), + enum_type->GetByteSize()); + } } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestArrayTypes)) -{ - // In order to get this test working, we need to support lookup by symbol name. Because array - // types themselves do not have names, only the symbols have names (i.e. the name of the array). +TEST_F(SymbolFilePDBTests, TestArrayTypes) { + // In order to get this test working, we need to support lookup by symbol + // name. Because array + // types themselves do not have names, only the symbols have names (i.e. the + // name of the array). } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestFunctionTypes)) -{ - // In order to get this test working, we need to support lookup by symbol name. Because array - // types themselves do not have names, only the symbols have names (i.e. the name of the array). +TEST_F(SymbolFilePDBTests, TestFunctionTypes) { + // In order to get this test working, we need to support lookup by symbol + // name. Because array + // types themselves do not have names, only the symbols have names (i.e. the + // name of the array). } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestTypedefs)) -{ - FileSpec fspec(m_types_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); - const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); - SymbolContext sc; - llvm::DenseSet<SymbolFile *> searched_files; +TEST_F(SymbolFilePDBTests, TestTypedefs) { + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = + static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + + const char *TypedefsToCheck[] = {"ClassTypedef", "NSClassTypedef"}; + for (auto Typedef : TypedefsToCheck) { TypeMap results; - - const char *TypedefsToCheck[] = {"ClassTypedef", "NSClassTypedef"}; - for (auto Typedef : TypedefsToCheck) - { - TypeMap results; - EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString(Typedef), nullptr, false, 0, searched_files, results)); - EXPECT_EQ(1u, results.GetSize()); - lldb::TypeSP typedef_type = results.GetTypeAtIndex(0); - EXPECT_EQ(ConstString(Typedef), typedef_type->GetName()); - CompilerType compiler_type = typedef_type->GetFullCompilerType(); - ClangASTContext *clang_type_system = llvm::dyn_cast_or_null<ClangASTContext>(compiler_type.GetTypeSystem()); - EXPECT_TRUE(clang_type_system->IsTypedefType(compiler_type.GetOpaqueQualType())); - - std::string sizeof_var = "sizeof_"; - sizeof_var.append(Typedef); - EXPECT_EQ(GetGlobalConstantInteger(session, sizeof_var.c_str()), typedef_type->GetByteSize()); - } + EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString(Typedef), nullptr, false, + 0, searched_files, results)); + EXPECT_EQ(1u, results.GetSize()); + lldb::TypeSP typedef_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString(Typedef), typedef_type->GetName()); + CompilerType compiler_type = typedef_type->GetFullCompilerType(); + ClangASTContext *clang_type_system = + llvm::dyn_cast_or_null<ClangASTContext>(compiler_type.GetTypeSystem()); + EXPECT_TRUE( + clang_type_system->IsTypedefType(compiler_type.GetOpaqueQualType())); + + std::string sizeof_var = "sizeof_"; + sizeof_var.append(Typedef); + EXPECT_EQ(GetGlobalConstantInteger(session, sizeof_var), + typedef_type->GetByteSize()); + } } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestRegexNameMatch)) -{ - FileSpec fspec(m_types_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); - SymbolContext sc; - llvm::DenseSet<SymbolFile *> searched_files; - TypeMap results; - uint32_t num_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, false, 0, searched_files, results); - EXPECT_GT(num_results, 1u); - EXPECT_EQ(num_results, results.GetSize()); +TEST_F(SymbolFilePDBTests, TestRegexNameMatch) { + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = + static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + uint32_t num_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, + false, 0, searched_files, results); + EXPECT_GT(num_results, 1u); + EXPECT_EQ(num_results, results.GetSize()); } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestMaxMatches)) -{ - FileSpec fspec(m_types_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); - SymbolContext sc; - llvm::DenseSet<SymbolFile *> searched_files; - TypeMap results; - uint32_t num_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, false, 0, searched_files, results); - // Try to limit ourselves from 1 to 10 results, otherwise we could be doing this thousands of times. - // The idea is just to make sure that for a variety of values, the number of limited results always - // comes out to the number we are expecting. - uint32_t iterations = std::min(num_results, 10u); - for (uint32_t i = 1; i <= iterations; ++i) - { - uint32_t num_limited_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, false, i, searched_files, results); - EXPECT_EQ(i, num_limited_results); - EXPECT_EQ(num_limited_results, results.GetSize()); - } +TEST_F(SymbolFilePDBTests, TestMaxMatches) { + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = + static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + uint32_t num_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, + false, 0, searched_files, results); + // Try to limit ourselves from 1 to 10 results, otherwise we could be doing + // this thousands of times. + // The idea is just to make sure that for a variety of values, the number of + // limited results always + // comes out to the number we are expecting. + uint32_t iterations = std::min(num_results, 10u); + for (uint32_t i = 1; i <= iterations; ++i) { + uint32_t num_limited_results = symfile->FindTypes( + sc, ConstString(".*"), nullptr, false, i, searched_files, results); + EXPECT_EQ(i, num_limited_results); + EXPECT_EQ(num_limited_results, results.GetSize()); + } } -TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestNullName)) -{ - FileSpec fspec(m_types_test_exe.c_str(), false); - ArchSpec aspec("i686-pc-windows"); - lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); - - SymbolVendor *plugin = module->GetSymbolVendor(); - SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); - SymbolContext sc; - llvm::DenseSet<SymbolFile *> searched_files; - TypeMap results; - uint32_t num_results = symfile->FindTypes(sc, ConstString(), nullptr, false, 0, searched_files, results); - EXPECT_EQ(0u, num_results); - EXPECT_EQ(0u, results.GetSize()); +TEST_F(SymbolFilePDBTests, TestNullName) { + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = + static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + uint32_t num_results = symfile->FindTypes(sc, ConstString(), nullptr, false, + 0, searched_files, results); + EXPECT_EQ(0u, num_results); + EXPECT_EQ(0u, results.GetSize()); } diff --git a/unittests/UnwindAssembly/CMakeLists.txt b/unittests/UnwindAssembly/CMakeLists.txt new file mode 100644 index 000000000000..0326011fda84 --- /dev/null +++ b/unittests/UnwindAssembly/CMakeLists.txt @@ -0,0 +1,5 @@ +if ("X86" IN_LIST LLVM_TARGETS_TO_BUILD) + add_subdirectory(x86) +endif() + +add_subdirectory(InstEmulation) diff --git a/unittests/UnwindAssembly/InstEmulation/CMakeLists.txt b/unittests/UnwindAssembly/InstEmulation/CMakeLists.txt new file mode 100644 index 000000000000..5e274a229919 --- /dev/null +++ b/unittests/UnwindAssembly/InstEmulation/CMakeLists.txt @@ -0,0 +1,3 @@ +if ("AArch64" IN_LIST LLVM_TARGETS_TO_BUILD) + add_lldb_unittest(InstEmulationTests TestArm64InstEmulation.cpp) +endif() diff --git a/unittests/UnwindAssembly/InstEmulation/TestArm64InstEmulation.cpp b/unittests/UnwindAssembly/InstEmulation/TestArm64InstEmulation.cpp new file mode 100644 index 000000000000..d7caf4281d5d --- /dev/null +++ b/unittests/UnwindAssembly/InstEmulation/TestArm64InstEmulation.cpp @@ -0,0 +1,672 @@ +//===-- TestArm64InstEmulation.cpp ------------------------------------*- C++ +//-*-===// + +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include <vector> + +#include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/UnwindAssembly.h" + +#include "Plugins/Disassembler/llvm/DisassemblerLLVMC.h" +#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h" +#include "Plugins/Process/Utility/lldb-arm64-register-enums.h" +#include "llvm/Support/TargetSelect.h" + +using namespace lldb; +using namespace lldb_private; + +class TestArm64InstEmulation : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + + // virtual void SetUp() override { } + // virtual void TearDown() override { } + +protected: +}; + +void TestArm64InstEmulation::SetUpTestCase() { + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + DisassemblerLLVMC::Initialize(); + EmulateInstructionARM64::Initialize(); +} + +void TestArm64InstEmulation::TearDownTestCase() { + DisassemblerLLVMC::Terminate(); + EmulateInstructionARM64::Terminate(); +} + +TEST_F(TestArm64InstEmulation, TestSimpleDarwinFunction) { + ArchSpec arch("arm64-apple-ios10", nullptr); + UnwindAssemblyInstEmulation *engine = + static_cast<UnwindAssemblyInstEmulation *>( + UnwindAssemblyInstEmulation::CreateInstance(arch)); + ASSERT_NE(nullptr, engine); + + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + UnwindPlan::Row::RegisterLocation regloc; + + // 'int main() { }' compiled for arm64-apple-ios with clang + uint8_t data[] = { + 0xfd, 0x7b, 0xbf, 0xa9, // 0xa9bf7bfd : stp x29, x30, [sp, #-0x10]! + 0xfd, 0x03, 0x00, 0x91, // 0x910003fd : mov x29, sp + 0xff, 0x43, 0x00, 0xd1, // 0xd10043ff : sub sp, sp, #0x10 + + 0xbf, 0x03, 0x00, 0x91, // 0x910003bf : mov sp, x29 + 0xfd, 0x7b, 0xc1, 0xa8, // 0xa8c17bfd : ldp x29, x30, [sp], #16 + 0xc0, 0x03, 0x5f, 0xd6, // 0xd65f03c0 : ret + }; + + // UnwindPlan we expect: + + // row[0]: 0: CFA=sp +0 => + // row[1]: 4: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8] + // row[2]: 8: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] + // row[2]: 16: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8] + // row[3]: 20: CFA=sp +0 => fp= <same> lr= <same> + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + sample_range, data, sizeof(data), unwind_plan)); + + // CFA=sp +0 + row_sp = unwind_plan.GetRowForFunctionOffset(0); + EXPECT_EQ(0ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + // CFA=sp+16 => fp=[CFA-16] lr=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(8); + EXPECT_EQ(8ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // CFA=sp+16 => fp=[CFA-16] lr=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(16); + EXPECT_EQ(16ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // CFA=sp +0 => fp= <same> lr= <same> + row_sp = unwind_plan.GetRowForFunctionOffset(20); + EXPECT_EQ(20ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); +} + +TEST_F(TestArm64InstEmulation, TestMediumDarwinFunction) { + ArchSpec arch("arm64-apple-ios10", nullptr); + UnwindAssemblyInstEmulation *engine = + static_cast<UnwindAssemblyInstEmulation *>( + UnwindAssemblyInstEmulation::CreateInstance(arch)); + ASSERT_NE(nullptr, engine); + + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + UnwindPlan::Row::RegisterLocation regloc; + + // disassembly of -[NSPlaceholderString initWithBytes:length:encoding:] + // from Foundation for iOS. + uint8_t data[] = { + 0xf6, 0x57, 0xbd, 0xa9, // 0: 0xa9bd57f6 stp x22, x21, [sp, #-48]! + 0xf4, 0x4f, 0x01, 0xa9, // 4: 0xa9014ff4 stp x20, x19, [sp, #16] + 0xfd, 0x7b, 0x02, 0xa9, // 8: 0xa9027bfd stp x29, x30, [sp, #32] + 0xfd, 0x83, 0x00, 0x91, // 12: 0x910083fd add x29, sp, #32 + 0xff, 0x43, 0x00, 0xd1, // 16: 0xd10043ff sub sp, sp, #16 + + // [... function body ...] + 0x1f, 0x20, 0x03, 0xd5, // 20: 0xd503201f nop + + 0xbf, 0x83, 0x00, 0xd1, // 24: 0xd10083bf sub sp, x29, #32 + 0xfd, 0x7b, 0x42, 0xa9, // 28: 0xa9427bfd ldp x29, x30, [sp, #32] + 0xf4, 0x4f, 0x41, 0xa9, // 32: 0xa9414ff4 ldp x20, x19, [sp, #16] + 0xf6, 0x57, 0xc3, 0xa8, // 36: 0xa8c357f6 ldp x22, x21, [sp], #48 + 0x01, 0x16, 0x09, 0x14, // 40: 0x14091601 b 0x18f640524 ; symbol stub + // for: CFStringCreateWithBytes + }; + + // UnwindPlan we expect: + // 0: CFA=sp +0 => + // 4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48] + // 8: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // 12: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // fp=[CFA-16] lr=[CFA-8] + // 16: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // fp=[CFA-16] lr=[CFA-8] + + // [... function body ...] + + // 28: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // fp=[CFA-16] lr=[CFA-8] + // 32: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] fp= + // <same> lr= <same> + // 36: CFA=sp+48 => x19= <same> x20= <same> x21=[CFA-40] x22=[CFA-48] fp= + // <same> lr= <same> + // 40: CFA=sp +0 => x19= <same> x20= <same> x21= <same> x22= <same> fp= <same> + // lr= <same> + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + sample_range, data, sizeof(data), unwind_plan)); + + // 0: CFA=sp +0 => + row_sp = unwind_plan.GetRowForFunctionOffset(0); + EXPECT_EQ(0ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + // 4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48] + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-40, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-48, regloc.GetOffset()); + + // 8: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + row_sp = unwind_plan.GetRowForFunctionOffset(8); + EXPECT_EQ(8ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-24, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-32, regloc.GetOffset()); + + // 12: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // fp=[CFA-16] lr=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(12); + EXPECT_EQ(12ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // 16: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // fp=[CFA-16] lr=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(16); + EXPECT_EQ(16ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + // 28: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // fp=[CFA-16] lr=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(28); + EXPECT_EQ(28ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset()); + + // 32: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] fp= + // <same> lr= <same> + row_sp = unwind_plan.GetRowForFunctionOffset(32); + EXPECT_EQ(32ull, row_sp->GetOffset()); + + // I'd prefer if these restored registers were cleared entirely instead of set + // to IsSame... + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); + EXPECT_TRUE(regloc.IsSame()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); + EXPECT_TRUE(regloc.IsSame()); + + // 36: CFA=sp+48 => x19= <same> x20= <same> x21=[CFA-40] x22=[CFA-48] fp= + // <same> lr= <same> + row_sp = unwind_plan.GetRowForFunctionOffset(36); + EXPECT_EQ(36ull, row_sp->GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc)); + EXPECT_TRUE(regloc.IsSame()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); + EXPECT_TRUE(regloc.IsSame()); + + // 40: CFA=sp +0 => x19= <same> x20= <same> x21= <same> x22= <same> fp= <same> + // lr= <same> + row_sp = unwind_plan.GetRowForFunctionOffset(40); + EXPECT_EQ(40ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc)); + EXPECT_TRUE(regloc.IsSame()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc)); + EXPECT_TRUE(regloc.IsSame()); +} + +TEST_F(TestArm64InstEmulation, TestFramelessThreeEpilogueFunction) { + ArchSpec arch("arm64-apple-ios10", nullptr); + UnwindAssemblyInstEmulation *engine = + static_cast<UnwindAssemblyInstEmulation *>( + UnwindAssemblyInstEmulation::CreateInstance(arch)); + ASSERT_NE(nullptr, engine); + + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + UnwindPlan::Row::RegisterLocation regloc; + + // disassembly of JSC::ARM64LogicalImmediate::findBitRange<16u> + // from JavaScriptcore for iOS. + uint8_t data[] = { + 0x08, 0x3c, 0x0f, 0x53, // 0: 0x530f3c08 ubfx w8, w0, #15, #1 + 0x68, 0x00, 0x00, 0x39, // 4: 0x39000068 strb w8, [x3] + 0x08, 0x3c, 0x40, 0xd2, // 8: 0xd2403c08 eor x8, x0, #0xffff + 0x1f, 0x00, 0x71, 0xf2, // 12: 0xf271001f tst x0, #0x8000 + + // [...] + + 0x3f, 0x01, 0x0c, 0xeb, // 16: 0xeb0c013f cmp x9, x12 + 0x81, 0x00, 0x00, 0x54, // 20: 0x54000081 b.ne +34 + 0x5f, 0x00, 0x00, 0xb9, // 24: 0xb900005f str wzr, [x2] + 0xe0, 0x03, 0x00, 0x32, // 28: 0x320003e0 orr w0, wzr, #0x1 + 0xc0, 0x03, 0x5f, 0xd6, // 32: 0xd65f03c0 ret + 0x89, 0x01, 0x09, 0xca, // 36: 0xca090189 eor x9, x12, x9 + + // [...] + + 0x08, 0x05, 0x00, 0x11, // 40: 0x11000508 add w8, w8, #0x1 + 0x48, 0x00, 0x00, 0xb9, // 44: 0xb9000048 str w8, [x2] + 0xe0, 0x03, 0x00, 0x32, // 48: 0x320003e0 orr w0, wzr, #0x1 + 0xc0, 0x03, 0x5f, 0xd6, // 52: 0xd65f03c0 ret + 0x00, 0x00, 0x80, 0x52, // 56: 0x52800000 mov w0, #0x0 + 0xc0, 0x03, 0x5f, 0xd6, // 60: 0xd65f03c0 ret + + }; + + // UnwindPlan we expect: + // 0: CFA=sp +0 => + // (possibly with additional rows at offsets 36 and 56 saying the same thing) + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + sample_range, data, sizeof(data), unwind_plan)); + + // 0: CFA=sp +0 => + row_sp = unwind_plan.GetRowForFunctionOffset(0); + EXPECT_EQ(0ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(32); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x23_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x24_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x25_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x26_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x27_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x28_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); + + row_sp = unwind_plan.GetRowForFunctionOffset(36); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(52); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(56); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(60); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); +} + +TEST_F(TestArm64InstEmulation, TestRegisterSavedTwice) { + ArchSpec arch("arm64-apple-ios10", nullptr); + UnwindAssemblyInstEmulation *engine = + static_cast<UnwindAssemblyInstEmulation *>( + UnwindAssemblyInstEmulation::CreateInstance(arch)); + ASSERT_NE(nullptr, engine); + + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + UnwindPlan::Row::RegisterLocation regloc; + + // disassembly of mach_msg_sever_once from libsystem_kernel.dylib for iOS. + uint8_t data[] = { + + 0xfc, 0x6f, 0xba, 0xa9, // 0: 0xa9ba6ffc stp x28, x27, [sp, #-0x60]! + 0xfa, 0x67, 0x01, 0xa9, // 4: 0xa90167fa stp x26, x25, [sp, #0x10] + 0xf8, 0x5f, 0x02, 0xa9, // 8: 0xa9025ff8 stp x24, x23, [sp, #0x20] + 0xf6, 0x57, 0x03, 0xa9, // 12: 0xa90357f6 stp x22, x21, [sp, #0x30] + 0xf4, 0x4f, 0x04, 0xa9, // 16: 0xa9044ff4 stp x20, x19, [sp, #0x40] + 0xfd, 0x7b, 0x05, 0xa9, // 20: 0xa9057bfd stp x29, x30, [sp, #0x50] + 0xfd, 0x43, 0x01, 0x91, // 24: 0x910143fd add x29, sp, #0x50 + 0xff, 0xc3, 0x00, 0xd1, // 28: 0xd100c3ff sub sp, sp, #0x30 + + // mid-function, store x20 & x24 on the stack at a different location. + // this should not show up in the unwind plan; caller's values are not + // being saved to stack. + 0xf8, 0x53, 0x01, 0xa9, // 32: 0xa90153f8 stp x24, x20, [sp, #0x10] + + // mid-function, copy x20 and x19 off of the stack -- but not from + // their original locations. unwind plan should ignore this. + 0xf4, 0x4f, 0x41, 0xa9, // 36: 0xa9414ff4 ldp x20, x19, [sp, #0x10] + + // epilogue + 0xbf, 0x43, 0x01, 0xd1, // 40: 0xd10143bf sub sp, x29, #0x50 + 0xfd, 0x7b, 0x45, 0xa9, // 44: 0xa9457bfd ldp x29, x30, [sp, #0x50] + 0xf4, 0x4f, 0x44, 0xa9, // 48: 0xa9444ff4 ldp x20, x19, [sp, #0x40] + 0xf6, 0x57, 0x43, 0xa9, // 52: 0xa94357f6 ldp x22, x21, [sp, #0x30] + 0xf8, 0x5f, 0x42, 0xa9, // 56: 0xa9425ff8 ldp x24, x23, [sp, #0x20] + 0xfa, 0x67, 0x41, 0xa9, // 60: 0xa94167fa ldp x26, x25, [sp, #0x10] + 0xfc, 0x6f, 0xc6, 0xa8, // 64: 0xa8c66ffc ldp x28, x27, [sp], #0x60 + 0xc0, 0x03, 0x5f, 0xd6, // 68: 0xd65f03c0 ret + }; + + // UnwindPlan we expect: + // 0: CFA=sp +0 => + // 4: CFA=sp+96 => x27=[CFA-88] x28=[CFA-96] + // 8: CFA=sp+96 => x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96] + // 12: CFA=sp+96 => x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] + // x27=[CFA-88] x28=[CFA-96] + // 16: CFA=sp+96 => x21=[CFA-40] x22=[CFA-48] x23=[CFA-56] x24=[CFA-64] + // x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96] + // 20: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] + // x28=[CFA-96] + // 24: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] + // x28=[CFA-96] fp=[CFA-16] lr=[CFA-8] + // 28: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] + // x28=[CFA-96] fp=[CFA-16] lr=[CFA-8] + + // 44: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] + // x28=[CFA-96] fp=[CFA-16] lr=[CFA-8] + // 48: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] + // x28=[CFA-96] + // 52: CFA=sp+96 => x21=[CFA-40] x22=[CFA-48] x23=[CFA-56] x24=[CFA-64] + // x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96] + // 56: CFA=sp+96 => x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] + // x27=[CFA-88] x28=[CFA-96] + // 60: CFA=sp+96 => x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96] + // 64: CFA=sp+96 => x27=[CFA-88] x28=[CFA-96] + // 68: CFA=sp +0 => + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + sample_range, data, sizeof(data), unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(36); + EXPECT_EQ(28ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-32, regloc.GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(40); + EXPECT_EQ(28ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-32, regloc.GetOffset()); +} + +TEST_F(TestArm64InstEmulation, TestRegisterDoubleSpills) { + ArchSpec arch("arm64-apple-ios10", nullptr); + UnwindAssemblyInstEmulation *engine = + static_cast<UnwindAssemblyInstEmulation *>( + UnwindAssemblyInstEmulation::CreateInstance(arch)); + ASSERT_NE(nullptr, engine); + + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + UnwindPlan::Row::RegisterLocation regloc; + + // this file built with clang for iOS arch arm64 optimization -Os + // #include <stdio.h> + // double foo(double in) { + // double arr[32]; + // for (int i = 0; i < 32; i++) + // arr[i] = in + i; + // for (int i = 2; i < 30; i++) + // arr[i] = ((((arr[i - 1] * arr[i - 2] * 0.2) + (0.7 * arr[i])) / + // ((((arr[i] * 0.73) + 0.65) * (arr[i - 1] + 0.2)) - ((arr[i + 1] + (arr[i] + // * 0.32) + 0.52) / 0.3) + (0.531 * arr[i - 2]))) + ((arr[i - 1] + 5) / + // ((arr[i + 2] + 0.4) / arr[i])) + (arr[5] * (0.17 + arr[7] * arr[i])) + + // ((i > 5 ? (arr[i - 3]) : arr[i - 1]) * 0.263) + (((arr[i - 2] + arr[i - + // 1]) * 0.3252) + 3.56) - (arr[i + 1] * 0.852311)) * ((arr[i] * 85234.1345) + // + (77342.451324 / (arr[i - 2] + arr[i - 1] - 73425341.33455))) + (arr[i] + // * 875712013.55) - (arr[i - 1] * 0.5555) - ((arr[i] * (arr[i + 1] + + // 17342834.44) / 8688200123.555)) + (arr[i - 2] + 8888.888); + // return arr[16]; + //} + // int main(int argc, char **argv) { printf("%g\n", foo(argc)); } + + // so function foo() uses enough registers that it spills the callee-saved + // floating point registers. + uint8_t data[] = { + // prologue + 0xef, 0x3b, 0xba, 0x6d, // 0: 0x6dba3bef stp d15, d14, [sp, #-0x60]! + 0xed, 0x33, 0x01, 0x6d, // 4: 0x6d0133ed stp d13, d12, [sp, #0x10] + 0xeb, 0x2b, 0x02, 0x6d, // 8: 0x6d022beb stp d11, d10, [sp, #0x20] + 0xe9, 0x23, 0x03, 0x6d, // 12: 0x6d0323e9 stp d9, d8, [sp, #0x30] + 0xfc, 0x6f, 0x04, 0xa9, // 16: 0xa9046ffc stp x28, x27, [sp, #0x40] + 0xfd, 0x7b, 0x05, 0xa9, // 20: 0xa9057bfd stp x29, x30, [sp, #0x50] + 0xfd, 0x43, 0x01, 0x91, // 24: 0x910143fd add x29, sp, #0x50 + 0xff, 0x43, 0x04, 0xd1, // 28: 0xd10443ff sub sp, sp, #0x110 + + // epilogue + 0xbf, 0x43, 0x01, 0xd1, // 32: 0xd10143bf sub sp, x29, #0x50 + 0xfd, 0x7b, 0x45, 0xa9, // 36: 0xa9457bfd ldp x29, x30, [sp, #0x50] + 0xfc, 0x6f, 0x44, 0xa9, // 40: 0xa9446ffc ldp x28, x27, [sp, #0x40] + 0xe9, 0x23, 0x43, 0x6d, // 44: 0x6d4323e9 ldp d9, d8, [sp, #0x30] + 0xeb, 0x2b, 0x42, 0x6d, // 48: 0x6d422beb ldp d11, d10, [sp, #0x20] + 0xed, 0x33, 0x41, 0x6d, // 52: 0x6d4133ed ldp d13, d12, [sp, #0x10] + 0xef, 0x3b, 0xc6, 0x6c, // 56: 0x6cc63bef ldp d15, d14, [sp], #0x60 + 0xc0, 0x03, 0x5f, 0xd6, // 60: 0xd65f03c0 ret + }; + + // UnwindPlan we expect: + // 0: CFA=sp +0 => + // 4: CFA=sp+96 => d14=[CFA-88] d15=[CFA-96] + // 8: CFA=sp+96 => d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 12: CFA=sp+96 => d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] + // d14=[CFA-88] d15=[CFA-96] + // 16: CFA=sp+96 => d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] + // d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 20: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] d8=[CFA-40] d9=[CFA-48] + // d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] + // d15=[CFA-96] + // 24: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8] + // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] + // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 28: CFA=fp+16 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8] + // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] + // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 36: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8] + // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] + // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 40: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] d8=[CFA-40] d9=[CFA-48] + // d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] + // d15=[CFA-96] + // 44: CFA=sp+96 => d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] + // d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 48: CFA=sp+96 => d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] + // d14=[CFA-88] d15=[CFA-96] + // 52: CFA=sp+96 => d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 56: CFA=sp+96 => d14=[CFA-88] d15=[CFA-96] + // 60: CFA=sp +0 => + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + sample_range, data, sizeof(data), unwind_plan)); + + // 28: CFA=fp+16 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8] + // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] + // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + row_sp = unwind_plan.GetRowForFunctionOffset(28); + EXPECT_EQ(28ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d15_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-96, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d14_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-88, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d13_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-80, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d12_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-72, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d11_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-64, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d10_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-56, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d9_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-48, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d8_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-40, regloc.GetOffset()); + + // 60: CFA=sp +0 => + row_sp = unwind_plan.GetRowForFunctionOffset(60); + EXPECT_EQ(60ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + if (row_sp->GetRegisterInfo(fpu_d8_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d9_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d10_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d11_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d12_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d13_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d14_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d15_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(gpr_x27_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(gpr_x28_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); +} diff --git a/unittests/UnwindAssembly/x86/CMakeLists.txt b/unittests/UnwindAssembly/x86/CMakeLists.txt new file mode 100644 index 000000000000..7112467b3975 --- /dev/null +++ b/unittests/UnwindAssembly/x86/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_unittest(UnwindAssemblyx86Tests + Testx86AssemblyInspectionEngine.cpp + ) diff --git a/unittests/UnwindAssembly/x86/Testx86AssemblyInspectionEngine.cpp b/unittests/UnwindAssembly/x86/Testx86AssemblyInspectionEngine.cpp new file mode 100644 index 000000000000..e216d4be16fd --- /dev/null +++ b/unittests/UnwindAssembly/x86/Testx86AssemblyInspectionEngine.cpp @@ -0,0 +1,2339 @@ +//===-- Testx86AssemblyInspectionEngine.cpp ---------------------------*- C++ +//-*-===// + +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include <vector> + +#include "Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Symbol/UnwindPlan.h" + +#include "llvm/Support/TargetSelect.h" + +using namespace lldb; +using namespace lldb_private; + +class Testx86AssemblyInspectionEngine : public testing::Test { +public: + static void SetUpTestCase(); + + // static void TearDownTestCase() { } + + // virtual void SetUp() override { } + + // virtual void TearDown() override { } + +protected: +}; + +void Testx86AssemblyInspectionEngine::SetUpTestCase() { + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); +} + +// only defining the register names / numbers that the unwinder is actually +// using today + +// names should match the constants below. These will be the eRegisterKindLLDB +// register numbers. + +const char *x86_64_reg_names[] = {"rax", "rbx", "rcx", "rdx", "rsp", "rbp", + "rsi", "rdi", "r8", "r9", "r10", "r11", + "r12", "r13", "r14", "r15", "rip"}; + +enum x86_64_regs { + k_rax = 0, + k_rbx = 1, + k_rcx = 2, + k_rdx = 3, + k_rsp = 4, + k_rbp = 5, + k_rsi = 6, + k_rdi = 7, + k_r8 = 8, + k_r9 = 9, + k_r10 = 10, + k_r11 = 11, + k_r12 = 12, + k_r13 = 13, + k_r14 = 14, + k_r15 = 15, + k_rip = 16 +}; + +// names should match the constants below. These will be the eRegisterKindLLDB +// register numbers. + +const char *i386_reg_names[] = {"eax", "ecx", "edx", "ebx", "esp", + "ebp", "esi", "edi", "eip"}; + +enum i386_regs { + k_eax = 0, + k_ecx = 1, + k_edx = 2, + k_ebx = 3, + k_esp = 4, + k_ebp = 5, + k_esi = 6, + k_edi = 7, + k_eip = 8 +}; + +std::unique_ptr<x86AssemblyInspectionEngine> Getx86_64Inspector() { + + ArchSpec arch("x86_64-apple-macosx", nullptr); + std::unique_ptr<x86AssemblyInspectionEngine> engine( + new x86AssemblyInspectionEngine(arch)); + + std::vector<x86AssemblyInspectionEngine::lldb_reg_info> lldb_regnums; + int i = 0; + for (const auto &name : x86_64_reg_names) { + x86AssemblyInspectionEngine::lldb_reg_info ri; + ri.name = name; + ri.lldb_regnum = i++; + lldb_regnums.push_back(ri); + } + + engine->Initialize(lldb_regnums); + return engine; +} + +std::unique_ptr<x86AssemblyInspectionEngine> Geti386Inspector() { + + ArchSpec arch("i386-apple-macosx", nullptr); + std::unique_ptr<x86AssemblyInspectionEngine> engine( + new x86AssemblyInspectionEngine(arch)); + + std::vector<x86AssemblyInspectionEngine::lldb_reg_info> lldb_regnums; + int i = 0; + for (const auto &name : i386_reg_names) { + x86AssemblyInspectionEngine::lldb_reg_info ri; + ri.name = name; + ri.lldb_regnum = i++; + lldb_regnums.push_back(ri); + } + + engine->Initialize(lldb_regnums); + return engine; +} + +TEST_F(Testx86AssemblyInspectionEngine, TestSimple64bitFrameFunction) { + std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector(); + + // 'int main() { }' compiled for x86_64-apple-macosx with clang + uint8_t data[] = { + 0x55, // offset 0 -- pushq %rbp + 0x48, 0x89, 0xe5, // offset 1 -- movq %rsp, %rbp + 0x31, 0xc0, // offset 4 -- xorl %eax, %eax + 0x5d, // offset 6 -- popq %rbp + 0xc3 // offset 7 -- retq + }; + + AddressRange sample_range(0x1000, sizeof(data)); + + UnwindPlan unwind_plan(eRegisterKindLLDB); + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + // Expect four unwind rows: + // 0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] + // 1: CFA=rsp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] + // 4: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] + // 7: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] + + EXPECT_TRUE(unwind_plan.GetInitialCFARegister() == k_rsp); + EXPECT_TRUE(unwind_plan.GetUnwindPlanValidAtAllInstructions() == + eLazyBoolYes); + EXPECT_TRUE(unwind_plan.GetSourcedFromCompiler() == eLazyBoolNo); + + UnwindPlan::Row::RegisterLocation regloc; + + // 0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] + UnwindPlan::RowSP row_sp = unwind_plan.GetRowForFunctionOffset(0); + EXPECT_EQ(0ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // 1: CFA=rsp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(1); + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // 4: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // 7: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(7); + EXPECT_EQ(7ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestSimple32bitFrameFunction) { + std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector(); + + // 'int main() { }' compiled for i386-apple-macosx with clang + uint8_t data[] = { + 0x55, // offset 0 -- pushl %ebp + 0x89, 0xe5, // offset 1 -- movl %esp, %ebp + 0x31, 0xc0, // offset 3 -- xorl %eax, %eax + 0x5d, // offset 5 -- popl %ebp + 0xc3 // offset 6 -- retl + }; + + AddressRange sample_range(0x1000, sizeof(data)); + + UnwindPlan unwind_plan(eRegisterKindLLDB); + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + // Expect four unwind rows: + // 0: CFA=esp +4 => esp=CFA+0 eip=[CFA-4] + // 1: CFA=esp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4] + // 3: CFA=ebp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4] + // 6: CFA=esp +4 => esp=CFA+0 eip=[CFA-4] + + EXPECT_TRUE(unwind_plan.GetInitialCFARegister() == k_esp); + EXPECT_TRUE(unwind_plan.GetUnwindPlanValidAtAllInstructions() == + eLazyBoolYes); + EXPECT_TRUE(unwind_plan.GetSourcedFromCompiler() == eLazyBoolNo); + + UnwindPlan::Row::RegisterLocation regloc; + + // offset 0 -- pushl %ebp + UnwindPlan::RowSP row_sp = unwind_plan.GetRowForFunctionOffset(0); + EXPECT_EQ(0ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_TRUE(regloc.GetOffset() == -4); + + // 1: CFA=esp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4] + row_sp = unwind_plan.GetRowForFunctionOffset(1); + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-4, regloc.GetOffset()); + + // 3: CFA=ebp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4] + row_sp = unwind_plan.GetRowForFunctionOffset(3); + EXPECT_EQ(3ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_ebp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-4, regloc.GetOffset()); + + // 6: CFA=esp +4 => esp=CFA+0 eip=[CFA-4] + row_sp = unwind_plan.GetRowForFunctionOffset(6); + EXPECT_EQ(6ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-4, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, Test64bitFramelessBigStackFrame) { + std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector(); + + // this source file: + // + // #include <stdio.h> + // int main (int argc, char **argv) + // { + // + // const int arrsize = 60; + // int buf[arrsize * arrsize]; + // int accum = argc; + // for (int i = 0; i < arrsize; i++) + // for (int j = 0; j < arrsize; j++) + // { + // if (i > 0 && j > 0) + // { + // int n = buf[(i-1) * (j-1)] * 2; + // int m = buf[(i-1) * (j-1)] / 2; + // int j = buf[(i-1) * (j-1)] + 2; + // int k = buf[(i-1) * (j-1)] - 2; + // printf ("%d ", n + m + j + k); + // buf[(i-1) * (j-1)] += n - m + j - k; + // } + // buf[i*j] = accum++; + // } + // + // return buf[(arrsize * arrsize) - 2] + printf ("%d\n", buf[(arrsize * + // arrsize) - 3]); + // } + // + // compiled 'clang -fomit-frame-pointer -Os' for x86_64-apple-macosx + + uint8_t data[] = { + 0x55, // offset 0 -- pushq %rbp + 0x41, 0x57, // offset 1 -- pushq %r15 + 0x41, 0x56, // offset 3 -- pushq %r14 + 0x41, 0x55, // offset 5 -- pushq %r13 + 0x41, 0x54, // offset 7 -- pushq %r12 + 0x53, // offset 9 -- pushq %rbx + 0x48, 0x81, 0xec, 0x68, 0x38, 0x00, + 0x00, // offset 10 -- subq $0x3868, %rsp + + // .... + + 0x48, 0x81, 0xc4, 0x68, 0x38, 0x00, + 0x00, // offset 17 -- addq $0x3868, %rsp + 0x5b, // offset 24 -- popq %rbx + 0x41, 0x5c, // offset 25 -- popq %r12 + 0x41, 0x5d, // offset 27 -- popq %r13 + 0x41, 0x5e, // offset 29 -- popq %r14 + 0x41, 0x5f, // offset 31 -- popq %r15 + 0x5d, // offset 33 -- popq %rbp + 0xc3, // offset 34 -- retq + 0xe8, 0x12, 0x34, 0x56, 0x78 // offset 35 -- callq whatever + }; + + AddressRange sample_range(0x1000, sizeof(data)); + + UnwindPlan unwind_plan(eRegisterKindLLDB); + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + // Unwind rules should look like + // 0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] + // 1: CFA=rsp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] + // 3: CFA=rsp+24 => rbp=[CFA-16] rsp=CFA+0 r15=[CFA-24] rip=[CFA-8] + // 5: CFA=rsp+32 => rbp=[CFA-16] rsp=CFA+0 r14=[CFA-32] r15=[CFA-24] + // rip=[CFA-8 + // 7: CFA=rsp+40 => rbp=[CFA-16] rsp=CFA+0 r13=[CFA-40] r14=[CFA-32] + // r15=[CFA-24] rip=[CFA-8] + // 9: CFA=rsp+48 => rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48] r13=[CFA-40] + // r14=[CFA-32] r15=[CFA-24] rip=[CFA-8] + // 10: CFA=rsp+56 => rbx=[CFA-56] rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48] + // r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] rip=[CFA-8] + // 17: CFA=rsp+14496 => rbx=[CFA-56] rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48] + // r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] rip=[CFA-8] + + // 24: CFA=rsp+56 => rbx=[CFA-56] rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48] + // r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] rip=[CFA-8] + // 25: CFA=rsp+48 => rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48] r13=[CFA-40] + // r14=[CFA-32] r15=[CFA-24] rip=[CFA-8] + // 27: CFA=rsp+40 => rbp=[CFA-16] rsp=CFA+0 r13=[CFA-40] r14=[CFA-32] + // r15=[CFA-24] rip=[CFA-8] + // 29: CFA=rsp+32 => rbp=[CFA-16] rsp=CFA+0 r14=[CFA-32] r15=[CFA-24] + // rip=[CFA-8] + // 31: CFA=rsp+24 => rbp=[CFA-16] rsp=CFA+0 r15=[CFA-24] rip=[CFA-8] + // 33: CFA=rsp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] + // 34: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] + + UnwindPlan::Row::RegisterLocation regloc; + + // grab the Row for when the prologue has finished executing: + // 17: CFA=rsp+14496 => rbx=[CFA-56] rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48] + // r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] rip=[CFA-8] + + UnwindPlan::RowSP row_sp = unwind_plan.GetRowForFunctionOffset(17); + + EXPECT_EQ(17ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(14496, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbp, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_r15, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-24, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_r14, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-32, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_r13, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-40, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_r12, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-48, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbx, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-56, regloc.GetOffset()); + + // grab the Row for when the epilogue has finished executing: + // 34: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] + + row_sp = unwind_plan.GetRowForFunctionOffset(34); + + EXPECT_EQ(34ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // these could be set to IsSame and be valid -- meaning that the + // register value is the same as the caller's -- but I'd rather + // they not be mentioned at all. + + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rax, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbx, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rcx, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rdx, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbp, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rsi, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rdi, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r8, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r9, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r10, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r11, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r12, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r13, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r14, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r15, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, Test32bitFramelessBigStackFrame) { + std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector(); + + // this source file: + // + // #include <stdio.h> + // int main (int argc, char **argv) + // { + // + // const int arrsize = 60; + // int buf[arrsize * arrsize]; + // int accum = argc; + // for (int i = 0; i < arrsize; i++) + // for (int j = 0; j < arrsize; j++) + // { + // if (i > 0 && j > 0) + // { + // int n = buf[(i-1) * (j-1)] * 2; + // int m = buf[(i-1) * (j-1)] / 2; + // int j = buf[(i-1) * (j-1)] + 2; + // int k = buf[(i-1) * (j-1)] - 2; + // printf ("%d ", n + m + j + k); + // buf[(i-1) * (j-1)] += n - m + j - k; + // } + // buf[i*j] = accum++; + // } + // + // return buf[(arrsize * arrsize) - 2] + printf ("%d\n", buf[(arrsize * + // arrsize) - 3]); + // } + // + // compiled 'clang -arch i386 -fomit-frame-pointer -Os' for i386-apple-macosx + + // simplified assembly version of the above function, which is used as the + // input + // data: + // + // .section __TEXT,__text,regular,pure_instructions + // .macosx_version_min 10, 12 + // .globl _main + // .align 4, 0x90 + // _main: ## @main + // ## BB#0: + // pushl %ebp + // pushl %ebx + // pushl %edi + // pushl %esi + // L0$pb: + // subl $0x386c, %esp + // calll L1 + // L1: + // popl %ecx + // movl %ecx, 0x8(%esp) + // subl $0x8, %esp + // pushl %eax + // pushl 0x20(%esp) + // calll _puts + // addl $0x10, %esp + // incl %ebx + // addl $0x386c, %esp + // popl %esi + // popl %edi + // popl %ebx + // popl %ebp + // retl + // + // .section __TEXT,__cstring,cstring_literals + // L_.str: ## @.str + // .asciz "HI" + // + // + // .subsections_via_symbols + + uint8_t data[] = { + 0x55, + // offset 0 -- pushl %ebp + + 0x53, + // offset 1 -- pushl %ebx + + 0x57, + // offset 2 -- pushl %edi + + 0x56, + // offset 3 -- pushl %esi + + 0x81, 0xec, 0x6c, 0x38, 0x00, 0x00, + // offset 4 -- subl $0x386c, %esp + + 0xe8, 0x00, 0x00, 0x00, 0x00, + // offset 10 -- calll 0 + // call the next instruction, to put the pc on the stack + + 0x59, + // offset 15 -- popl %ecx + // pop the saved pc address into ecx + + 0x89, 0x4c, 0x24, 0x08, + // offset 16 -- movl %ecx, 0x8(%esp) + + // .... + + 0x83, 0xec, 0x08, + // offset 20 -- subl $0x8, %esp + + 0x50, + // offset 23 -- pushl %eax + + 0xff, 0x74, 0x24, 0x20, + // offset 24 -- pushl 0x20(%esp) + + 0xe8, 0x8c, 0x00, 0x00, 0x00, + // offset 28 -- calll puts + + 0x83, 0xc4, 0x10, + // offset 33 -- addl $0x10, %esp + // get esp back to the value it was before the + // alignment & argument saves for the puts call + + 0x43, + // offset 36 -- incl %ebx + + // .... + + 0x81, 0xc4, 0x6c, 0x38, 0x00, 0x00, + // offset 37 -- addl $0x386c, %esp + + 0x5e, + // offset 43 -- popl %esi + + 0x5f, + // offset 44 -- popl %edi + + 0x5b, + // offset 45 -- popl %ebx + + 0x5d, + // offset 46 -- popl %ebp + + 0xc3, + // offset 47 -- retl + + 0xe8, 0x12, 0x34, 0x56, 0x78, + // offset 48 -- calll __stack_chk_fail + }; + + AddressRange sample_range(0x1000, sizeof(data)); + + UnwindPlan unwind_plan(eRegisterKindLLDB); + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + // Unwind rules should look like + // + // 0: CFA=esp +4 => esp=CFA+0 eip=[CFA-4] + // 1: CFA=esp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4] + // 2: CFA=esp+12 => ebx=[CFA-12] ebp=[CFA-8] esp=CFA+0 eip=[CFA-4] + // 3: CFA=esp+16 => ebx=[CFA-12] edi=[CFA-16] ebp=[CFA-8] esp=CFA+0 + // eip=[CFA-4] + // 4: CFA=esp+20 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + // 10: CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + // 15: CFA=esp+14468 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + // 16: CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + // + // .... + // + // 23: CFA=esp+14472 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + // 24: CFA=esp+14476 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + // 28: CFA=esp+14480 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + // 36: CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + // + // ..... + // + // 37: CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + // 43: CFA=esp+20 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + // 44: CFA=esp+16 => ebx=[CFA-12] edi=[CFA-16] ebp=[CFA-8] esp=CFA+0 + // eip=[CFA-4] + // 45: CFA=esp+12 => ebx=[CFA-12] ebp=[CFA-8] esp=CFA+0 eip=[CFA-4] + // 46: CFA=esp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4] + // 47: CFA=esp +4 => esp=CFA+0 eip=[CFA-4] + // 48: CFA=esp+14480 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + + // Check that we get the CFA correct for the pic base setup sequence + + // CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + row_sp = unwind_plan.GetRowForFunctionOffset(10); + EXPECT_EQ(10ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(14464, row_sp->GetCFAValue().GetOffset()); + + // 15: CFA=esp+14468 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + row_sp = unwind_plan.GetRowForFunctionOffset(15); + EXPECT_EQ(15ull, row_sp->GetOffset()); + EXPECT_EQ(14468, row_sp->GetCFAValue().GetOffset()); + + // 16: CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + row_sp = unwind_plan.GetRowForFunctionOffset(16); + EXPECT_EQ(16ull, row_sp->GetOffset()); + EXPECT_EQ(14464, row_sp->GetCFAValue().GetOffset()); + + // Check that the row for offset 16 has the registers saved that we expect + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-4, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_ebp, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_ebx, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-12, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_edi, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_esi, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-20, regloc.GetOffset()); + + // + // Check the pushing & popping around the call printf instruction + + // 23: CFA=esp+14472 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + row_sp = unwind_plan.GetRowForFunctionOffset(23); + EXPECT_EQ(23ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(14472, row_sp->GetCFAValue().GetOffset()); + + // 24: CFA=esp+14476 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + row_sp = unwind_plan.GetRowForFunctionOffset(24); + EXPECT_EQ(24ull, row_sp->GetOffset()); + EXPECT_EQ(14476, row_sp->GetCFAValue().GetOffset()); + + // 28: CFA=esp+14480 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + row_sp = unwind_plan.GetRowForFunctionOffset(28); + EXPECT_EQ(28ull, row_sp->GetOffset()); + EXPECT_EQ(14480, row_sp->GetCFAValue().GetOffset()); + + // 36: CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8] + // esp=CFA+0 eip=[CFA-4] + row_sp = unwind_plan.GetRowForFunctionOffset(36); + EXPECT_EQ(36ull, row_sp->GetOffset()); + EXPECT_EQ(14464, row_sp->GetCFAValue().GetOffset()); + + // Check that the epilogue gets us back to the original unwind state + + // 47: CFA=esp +4 => esp=CFA+0 eip=[CFA-4] + row_sp = unwind_plan.GetRowForFunctionOffset(47); + EXPECT_EQ(47ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-4, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_esp, regloc)); + EXPECT_TRUE(regloc.IsCFAPlusOffset()); + EXPECT_EQ(0, regloc.GetOffset()); + + // Check that no unexpected registers were saved + + EXPECT_FALSE(row_sp->GetRegisterInfo(k_eax, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebx, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_ecx, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_edx, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_esi, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_edi, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebp, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, Test64bitFramelessSmallStackFrame) { + std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector(); + + // this source file: + // #include <stdio.h> + // int main () { + // puts ("HI"); + // } + // + // compiled 'clang -fomit-frame-pointer' for x86_64-apple-macosx + + uint8_t data[] = { + 0x50, + // offset 0 -- pushq %rax + + 0x48, 0x8d, 0x3d, 0x32, 0x00, 0x00, 0x00, + // offset 1 -- leaq 0x32(%rip), %rdi ; "HI" + + 0xe8, 0x0b, 0x00, 0x00, 0x00, + // offset 8 -- callq 0x100000f58 ; puts + + 0x31, 0xc9, + // offset 13 -- xorl %ecx, %ecx + + 0x89, 0x44, 0x24, 0x04, + // offset 15 -- movl %eax, 0x4(%rsp) + + 0x89, 0xc8, + // offset 19 -- movl %ecx, %eax + + 0x59, + // offset 21 -- popq %rcx + + 0xc3 + // offset 22 -- retq + }; + + AddressRange sample_range(0x1000, sizeof(data)); + + UnwindPlan unwind_plan(eRegisterKindLLDB); + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + // Unwind rules should look like + // 0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] + // 1: CFA=rsp+16 => rsp=CFA+0 rip=[CFA-8] + // 22: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] + + UnwindPlan::Row::RegisterLocation regloc; + + // grab the Row for when the prologue has finished executing: + // 1: CFA=rsp+16 => rsp=CFA+0 rip=[CFA-8] + + UnwindPlan::RowSP row_sp = unwind_plan.GetRowForFunctionOffset(13); + + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // none of these were spilled + + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rax, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbx, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rcx, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rdx, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbp, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rsi, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rdi, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r8, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r9, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r10, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r11, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r12, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r13, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r14, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r15, regloc)); + + // grab the Row for when the epilogue has finished executing: + // 22: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] + + row_sp = unwind_plan.GetRowForFunctionOffset(22); + + EXPECT_EQ(22ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, Test32bitFramelessSmallStackFrame) { + std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector(); + + // this source file: + // #include <stdio.h> + // int main () { + // puts ("HI"); + // } + // + // compiled 'clang -arch i386 -fomit-frame-pointer' for i386-apple-macosx + + uint8_t data[] = { + 0x83, 0xec, 0x0c, + // offset 0 -- subl $0xc, %esp + + 0xe8, 0x00, 0x00, 0x00, 0x00, + // offset 3 -- calll 0 {call the next instruction, to put the pc on + // the stack} + + 0x58, + // offset 8 -- popl %eax {pop the saved pc value off stack, into eax} + + 0x8d, 0x80, 0x3a, 0x00, 0x00, 0x00, + // offset 9 -- leal 0x3a(%eax),%eax + + 0x89, 0x04, 0x24, + // offset 15 -- movl %eax, (%esp) + + 0xe8, 0x0d, 0x00, 0x00, 0x00, + // offset 18 -- calll 0x1f94 (puts) + + 0x31, 0xc9, + // offset 23 -- xorl %ecx, %ecx + + 0x89, 0x44, 0x24, 0x08, + // offset 25 -- movl %eax, 0x8(%esp) + + 0x89, 0xc8, + // offset 29 -- movl %ecx, %eax + + 0x83, 0xc4, 0x0c, + // offset 31 -- addl $0xc, %esp + + 0xc3 + // offset 34 -- retl + }; + + AddressRange sample_range(0x1000, sizeof(data)); + + UnwindPlan unwind_plan(eRegisterKindLLDB); + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + // Unwind rules should look like + // row[0]: 0: CFA=esp +4 => esp=CFA+0 eip=[CFA-4] + // row[1]: 3: CFA=esp+16 => esp=CFA+0 eip=[CFA-4] + // row[2]: 8: CFA=esp+20 => esp=CFA+0 eip=[CFA-4] + // row[3]: 9: CFA=esp+16 => esp=CFA+0 eip=[CFA-4] + // row[4]: 34: CFA=esp +4 => esp=CFA+0 eip=[CFA-4] + + UnwindPlan::Row::RegisterLocation regloc; + + // Check unwind state before we set up the picbase register + // 3: CFA=esp+16 => esp=CFA+0 eip=[CFA-4] + + UnwindPlan::RowSP row_sp = unwind_plan.GetRowForFunctionOffset(3); + + EXPECT_EQ(3ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + // Check unwind state after we call the next instruction + // 8: CFA=esp+20 => esp=CFA+0 eip=[CFA-4] + + row_sp = unwind_plan.GetRowForFunctionOffset(8); + EXPECT_EQ(8ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(20, row_sp->GetCFAValue().GetOffset()); + + // Check unwind state after we pop the pic base value off the stack + // row[3]: 9: CFA=esp+16 => esp=CFA+0 eip=[CFA-4] + + row_sp = unwind_plan.GetRowForFunctionOffset(9); + EXPECT_EQ(9ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + // Check that no unexpected registers were saved + + EXPECT_FALSE(row_sp->GetRegisterInfo(k_eax, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebx, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_ecx, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_edx, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_esi, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_edi, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebp, regloc)); + + // verify that we get back to the original unwind state before the ret + // 34: CFA=esp +4 => esp=CFA+0 eip=[CFA-4] + + row_sp = unwind_plan.GetRowForFunctionOffset(34); + EXPECT_EQ(34ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPushRBP) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + + uint8_t data[] = { + 0x55, // pushq %rbp + 0x90 // nop + }; + + AddressRange sample_range(0x1000, sizeof(data)); + UnwindPlan unwind_plan(eRegisterKindLLDB); + + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(1); + + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbp, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); + + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(1); + + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbp, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPushImm) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + + uint8_t data[] = { + 0x68, 0xff, 0xff, 0x01, 0x69, // pushq $0x6901ffff + 0x6a, 0x7d, // pushl $0x7d + 0x90 // nop + }; + + AddressRange sample_range(0x1000, sizeof(data)); + UnwindPlan unwind_plan(eRegisterKindLLDB); + + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(5); + EXPECT_EQ(5ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(7); + EXPECT_EQ(7ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(24, row_sp->GetCFAValue().GetOffset()); + + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(5); + EXPECT_EQ(5ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(7); + EXPECT_EQ(7ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(12, row_sp->GetCFAValue().GetOffset()); +} + +// We treat 'pushq $0' / 'pushl $0' specially - this shows up +// in the first function called in a new thread and it needs to +// put a 0 as the saved pc. We pretend it didn't change the CFA. +TEST_F(Testx86AssemblyInspectionEngine, TestPush0) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + + uint8_t data[] = { + 0x6a, 0x00, // pushq $0 + 0x90 // nop + }; + + AddressRange sample_range(0x1000, sizeof(data)); + UnwindPlan unwind_plan(eRegisterKindLLDB); + + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + + // We're verifying that no row was created for the 'pushq $0' + EXPECT_EQ(0ull, row_sp->GetOffset()); + + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + + // We're verifying that no row was created for the 'pushq $0' + EXPECT_EQ(0ull, row_sp->GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPushExtended) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + + uint8_t data[] = { + 0xff, 0x74, 0x24, 0x20, // pushl 0x20(%esp) + 0xff, 0xb6, 0xce, 0x01, 0xf0, 0x00, // pushl 0xf001ce(%esi) + 0xff, 0x30, // pushl (%eax) + 0x90 // nop + }; + + AddressRange sample_range(0x1000, sizeof(data)); + UnwindPlan unwind_plan(eRegisterKindLLDB); + + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(4); + + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(10); + EXPECT_EQ(10ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(12, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(12); + EXPECT_EQ(12ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPushR15) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + + uint8_t data[] = { + 0x41, 0x57, // pushq %r15 + 0x90 // nop + }; + + AddressRange sample_range(0x1000, sizeof(data)); + UnwindPlan unwind_plan(eRegisterKindLLDB); + + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_r15, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPushR14) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + + uint8_t data[] = { + 0x41, 0x56, // pushq %r14 + 0x90 // nop + }; + + AddressRange sample_range(0x1000, sizeof(data)); + UnwindPlan unwind_plan(eRegisterKindLLDB); + + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_r14, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPushR13) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + + uint8_t data[] = { + 0x41, 0x55, // pushq %r13 + 0x90 // nop + }; + + AddressRange sample_range(0x1000, sizeof(data)); + UnwindPlan unwind_plan(eRegisterKindLLDB); + + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_r13, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPushR12) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + + uint8_t data[] = { + 0x41, 0x54, // pushq %r13 + 0x90 // nop + }; + + AddressRange sample_range(0x1000, sizeof(data)); + UnwindPlan unwind_plan(eRegisterKindLLDB); + + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_r12, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPushRBX) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + + uint8_t data[] = { + 0x53, // pushq %rbx + 0x90 // nop + }; + + AddressRange sample_range(0x1000, sizeof(data)); + UnwindPlan unwind_plan(eRegisterKindLLDB); + + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(1); + + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbx, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); +} + +// The ABI is hardcoded in x86AssemblyInspectionEngine such that +// eax, ecx, edx are all considered volatile and push/pops of them are +// not tracked (except to keep track of stack pointer movement) +TEST_F(Testx86AssemblyInspectionEngine, TestPushEAX) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data[] = { + 0x50, // pushl %eax + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(1); + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_FALSE(row_sp->GetRegisterInfo(k_eax, regloc)); +} + +// The ABI is hardcoded in x86AssemblyInspectionEngine such that +// eax, ecx, edx are all considered volatile and push/pops of them are +// not tracked (except to keep track of stack pointer movement) +TEST_F(Testx86AssemblyInspectionEngine, TestPushECX) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data[] = { + 0x51, // pushl %ecx + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(1); + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_FALSE(row_sp->GetRegisterInfo(k_ecx, regloc)); +} + +// The ABI is hardcoded in x86AssemblyInspectionEngine such that +// eax, ecx, edx are all considered volatile and push/pops of them are +// not tracked (except to keep track of stack pointer movement) +TEST_F(Testx86AssemblyInspectionEngine, TestPushEDX) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data[] = { + 0x52, // pushl %edx + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(1); + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_FALSE(row_sp->GetRegisterInfo(k_edx, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPushEBX) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data[] = { + 0x53, // pushl %ebx + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(1); + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_ebx, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPushEBP) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data[] = { + 0x55, // pushl %ebp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(1); + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_ebp, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPushESI) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data[] = { + 0x56, // pushl %esi + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(1); + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_esi, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPushEDI) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data[] = { + 0x57, // pushl %edi + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(1); + EXPECT_EQ(1ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_edi, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestMovRSPtoRBP) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + + uint8_t data64_1[] = { + 0x48, 0x8b, 0xec, // movq %rsp, %rbp + 0x90 // nop + }; + + AddressRange sample_range(0x1000, sizeof(data64_1)); + UnwindPlan unwind_plan(eRegisterKindLLDB); + + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data64_1, sizeof(data64_1), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(3); + + EXPECT_EQ(3ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + uint8_t data64_2[] = { + 0x48, 0x89, 0xe5, // movq %rsp, %rbp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data64_2)); + unwind_plan.Clear(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data64_2, sizeof(data64_2), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(3); + EXPECT_EQ(3ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + uint8_t data32_1[] = { + 0x8b, 0xec, // movl %rsp, %rbp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data32_1)); + unwind_plan.Clear(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data32_1, sizeof(data32_1), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_ebp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + uint8_t data32_2[] = { + 0x89, 0xe5, // movl %rsp, %rbp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data32_2)); + unwind_plan.Clear(); + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data32_2, sizeof(data32_2), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_ebp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestSubRSP) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + + uint8_t data1[] = { + 0x48, 0x81, 0xec, 0x00, 0x01, 0x00, 0x00, // subq $0x100, $rsp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data1)); + + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data1, sizeof(data1), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(7); + EXPECT_EQ(7ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(264, row_sp->GetCFAValue().GetOffset()); + + uint8_t data2[] = { + 0x48, 0x83, 0xec, 0x10, // subq $0x10, %rsp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data2)); + + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data2, sizeof(data2), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(24, row_sp->GetCFAValue().GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestSubESP) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data1[] = { + 0x81, 0xec, 0x00, 0x01, 0x00, 0x00, // subl $0x100, %esp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data1)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data1, sizeof(data1), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(6); + EXPECT_EQ(6ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(260, row_sp->GetCFAValue().GetOffset()); + + uint8_t data2[] = { + 0x83, 0xec, 0x10, // subq $0x10, %esp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data2)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data2, sizeof(data2), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(3); + EXPECT_EQ(3ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(20, row_sp->GetCFAValue().GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestAddRSP) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + + uint8_t data1[] = { + 0x48, 0x81, 0xc4, 0x00, 0x01, 0x00, 0x00, // addq $0x100, %rsp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data1)); + + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data1, sizeof(data1), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(7); + EXPECT_EQ(7ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8 - 256, row_sp->GetCFAValue().GetOffset()); + + uint8_t data2[] = { + 0x48, 0x83, 0xc4, 0x10, // addq $0x10, %rsp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data2)); + + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data2, sizeof(data2), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8 - 16, row_sp->GetCFAValue().GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestAddESP) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data1[] = { + 0x81, 0xc4, 0x00, 0x01, 0x00, 0x00, // addl $0x100, %esp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data1)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data1, sizeof(data1), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(6); + EXPECT_EQ(6ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(4 - 256, row_sp->GetCFAValue().GetOffset()); + + uint8_t data2[] = { + 0x83, 0xc4, 0x10, // addq $0x10, %esp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data2)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data2, sizeof(data2), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(3); + EXPECT_EQ(3ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(4 - 16, row_sp->GetCFAValue().GetOffset()); +} + +// FIXME add test for lea_rsp_pattern_p + +TEST_F(Testx86AssemblyInspectionEngine, TestPopRBX) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector(); + + uint8_t data[] = { + 0x53, // pushq %rbx + 0x5b, // popq %rbx + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbx, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPopRBP) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector(); + + uint8_t data[] = { + 0x55, // pushq %rbp + 0x5d, // popq %rbp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbp, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPopR12) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector(); + + uint8_t data[] = { + 0x41, 0x54, // pushq %r12 + 0x41, 0x5c, // popq %r12 + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r12, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPopR13) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector(); + + uint8_t data[] = { + 0x41, 0x55, // pushq %r13 + 0x41, 0x5d, // popq %r13 + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r13, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPopR14) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector(); + + uint8_t data[] = { + 0x41, 0x56, // pushq %r14 + 0x41, 0x5e, // popq %r14 + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r14, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPopR15) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector(); + + uint8_t data[] = { + 0x41, 0x57, // pushq %r15 + 0x41, 0x5f, // popq %r15 + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_r15, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPopEBX) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector(); + + uint8_t data[] = { + 0x53, // pushl %ebx + 0x5b, // popl %ebx + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebx, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPopEBP) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector(); + + uint8_t data[] = { + 0x55, // pushl %ebp + 0x5d, // popl %ebp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebp, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPopESI) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector(); + + uint8_t data[] = { + 0x56, // pushl %esi + 0x5e, // popl %esi + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_esi, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestPopEDI) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector(); + + uint8_t data[] = { + 0x57, // pushl %edi + 0x5f, // popl %edi + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_edi, regloc)); +} + +// We don't track these registers, but make sure the CFA address is updated +// if we're defining the CFA in term of esp. +TEST_F(Testx86AssemblyInspectionEngine, Testi386IgnoredRegisters) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector(); + + uint8_t data[] = { + 0x0e, // push cs + 0x16, // push ss + 0x1e, // push ds + 0x06, // push es + + 0x07, // pop es + 0x1f, // pop ds + 0x17, // pop ss + + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(20, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(7); + EXPECT_EQ(7ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestLEAVE) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data[] = { + 0x55, // push %rbp/ebp + 0xc9, // leave + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbp, regloc)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebp, regloc)); +} + +// In i386, which lacks pc-relative addressing, a common code sequence +// is to call the next instruction (i.e. call imm32, value of 0) which +// pushes the addr of the next insn on the stack, and then pop that value +// into a register (the "pic base" register). +TEST_F(Testx86AssemblyInspectionEngine, TestCALLNextInsn) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data[] = { + 0xe8, 0x00, 0x00, 0x00, 0x00, // call 0 + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(5); + EXPECT_EQ(5ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebp, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestSpillRegToStackViaMOVx86_64) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + + uint8_t data[] = { + 0x55, // pushq %rbp + 0x48, 0x89, 0xe5, // movq %rsp, %rbp + 0x4c, 0x89, 0x75, 0xc0, // movq %r14, -0x40(%rbp) + 0x4c, 0x89, 0xbd, 0x28, 0xfa, 0xff, 0xff, // movq %r15, -0x5d8(%rbp) + 0x48, 0x89, 0x5d, 0xb8, // movq %rbx, -0x48(%rbp) + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(19); + EXPECT_EQ(19ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_r14, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-80, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_r15, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-1512, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbx, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-88, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestSpillRegToStackViaMOVi386) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data[] = { + 0x55, // pushl %ebp + 0x89, 0xe5, // movl %esp, %ebp + 0x89, 0x9d, 0xb0, 0xfe, 0xff, 0xff, // movl %ebx, -0x150(%ebp) + 0x89, 0x75, 0xe0, // movl %esi, -0x20(%ebp) + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(12); + EXPECT_EQ(12ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_ebx, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-344, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_esi, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-40, regloc.GetOffset()); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestSimplex86_64Augmented) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + + uint8_t data[] = { + 0x55, // pushq %rbp + 0x48, 0x89, 0xe5, // movq %rsp, %rbp + + // x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite + // has a bug where it can't augment a function that is just + // prologue+epilogue - it needs at least one other instruction + // in between. + 0x90, // nop + + 0x5d, // popq %rbp + 0xc3 // retq + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + unwind_plan.SetSourceName("unit testing hand-created unwind plan"); + unwind_plan.SetPlanValidAddressRange(sample_range); + unwind_plan.SetRegisterKind(eRegisterKindLLDB); + + row_sp.reset(new UnwindPlan::Row); + + // Describe offset 0 + row_sp->SetOffset(0); + row_sp->GetCFAValue().SetIsRegisterPlusOffset(k_rsp, 8); + + regloc.SetAtCFAPlusOffset(-8); + row_sp->SetRegisterInfo(k_rip, regloc); + + unwind_plan.AppendRow(row_sp); + + // Allocate a new Row, populate it with the existing Row contents. + UnwindPlan::Row *new_row = new UnwindPlan::Row; + *new_row = *row_sp.get(); + row_sp.reset(new_row); + + // Describe offset 1 + row_sp->SetOffset(1); + row_sp->GetCFAValue().SetIsRegisterPlusOffset(k_rsp, 16); + regloc.SetAtCFAPlusOffset(-16); + row_sp->SetRegisterInfo(k_rbp, regloc); + unwind_plan.AppendRow(row_sp); + + // Allocate a new Row, populate it with the existing Row contents. + new_row = new UnwindPlan::Row; + *new_row = *row_sp.get(); + row_sp.reset(new_row); + + // Describe offset 4 + row_sp->SetOffset(4); + row_sp->GetCFAValue().SetIsRegisterPlusOffset(k_rbp, 16); + unwind_plan.AppendRow(row_sp); + + RegisterContextSP reg_ctx_sp; + EXPECT_TRUE(engine64->AugmentUnwindPlanFromCallSite( + data, sizeof(data), sample_range, unwind_plan, reg_ctx_sp)); + + row_sp = unwind_plan.GetRowForFunctionOffset(6); + EXPECT_EQ(6ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + // x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite + // doesn't track register restores (pop'ing a reg value back from + // the stack) - it was just written to make stepping work correctly. + // Technically we should be able to do the following test, but it + // won't work today - the unwind plan will still say that the caller's + // rbp is on the stack. + // EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbp, regloc)); +} + +TEST_F(Testx86AssemblyInspectionEngine, TestSimplei386ugmented) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + + uint8_t data[] = { + 0x55, // pushl %ebp + 0x89, 0xe5, // movl %esp, %ebp + + // x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite + // has a bug where it can't augment a function that is just + // prologue+epilogue - it needs at least one other instruction + // in between. + 0x90, // nop + + 0x5d, // popl %ebp + 0xc3 // retl + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + unwind_plan.SetSourceName("unit testing hand-created unwind plan"); + unwind_plan.SetPlanValidAddressRange(sample_range); + unwind_plan.SetRegisterKind(eRegisterKindLLDB); + + row_sp.reset(new UnwindPlan::Row); + + // Describe offset 0 + row_sp->SetOffset(0); + row_sp->GetCFAValue().SetIsRegisterPlusOffset(k_esp, 4); + + regloc.SetAtCFAPlusOffset(-4); + row_sp->SetRegisterInfo(k_eip, regloc); + + unwind_plan.AppendRow(row_sp); + + // Allocate a new Row, populate it with the existing Row contents. + UnwindPlan::Row *new_row = new UnwindPlan::Row; + *new_row = *row_sp.get(); + row_sp.reset(new_row); + + // Describe offset 1 + row_sp->SetOffset(1); + row_sp->GetCFAValue().SetIsRegisterPlusOffset(k_esp, 8); + regloc.SetAtCFAPlusOffset(-8); + row_sp->SetRegisterInfo(k_ebp, regloc); + unwind_plan.AppendRow(row_sp); + + // Allocate a new Row, populate it with the existing Row contents. + new_row = new UnwindPlan::Row; + *new_row = *row_sp.get(); + row_sp.reset(new_row); + + // Describe offset 3 + row_sp->SetOffset(3); + row_sp->GetCFAValue().SetIsRegisterPlusOffset(k_ebp, 8); + unwind_plan.AppendRow(row_sp); + + RegisterContextSP reg_ctx_sp; + EXPECT_TRUE(engine32->AugmentUnwindPlanFromCallSite( + data, sizeof(data), sample_range, unwind_plan, reg_ctx_sp)); + + row_sp = unwind_plan.GetRowForFunctionOffset(5); + EXPECT_EQ(5ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset()); + + // x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite + // doesn't track register restores (pop'ing a reg value back from + // the stack) - it was just written to make stepping work correctly. + // Technically we should be able to do the following test, but it + // won't work today - the unwind plan will still say that the caller's + // ebp is on the stack. + // EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebp, regloc)); +} + +// Check that the i386 disassembler disassembles past an opcode that +// is only valid in 32-bit mode (non-long mode), and the x86_64 disassembler +// stops +// disassembling at that point (long-mode). +TEST_F(Testx86AssemblyInspectionEngine, Test32BitOnlyInstruction) { + UnwindPlan::Row::RegisterLocation regloc; + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector(); + std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector(); + + uint8_t data[] = { + 0x43, // incl $ebx --- an invalid opcode in 64-bit mode + 0x55, // pushl %ebp + 0x90 // nop + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + EXPECT_EQ(2ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(k_ebp, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + unwind_plan.Clear(); + + EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly( + data, sizeof(data), sample_range, unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(2); + EXPECT_EQ(0ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset()); + + EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbp, regloc)); +} diff --git a/unittests/Utility/CMakeLists.txt b/unittests/Utility/CMakeLists.txt index 99677a47d7cc..15a29825f1da 100644 --- a/unittests/Utility/CMakeLists.txt +++ b/unittests/Utility/CMakeLists.txt @@ -2,6 +2,7 @@ add_lldb_unittest(UtilityTests ModuleCacheTest.cpp StringExtractorTest.cpp TaskPoolTest.cpp + TimeoutTest.cpp UriParserTest.cpp ) diff --git a/unittests/Utility/Inputs/TestModule.c b/unittests/Utility/Inputs/TestModule.c index 12374e1f7c65..6347f7264944 100644 --- a/unittests/Utility/Inputs/TestModule.c +++ b/unittests/Utility/Inputs/TestModule.c @@ -1,10 +1,9 @@ // Compile with $CC -nostdlib -shared TestModule.c -o TestModule.so -// The actual contents of the test module is not important here. I am using this because it +// The actual contents of the test module is not important here. I am using this +// because it // produces an extremely tiny (but still perfectly valid) module. -void -boom(void) -{ - char *BOOM; - *BOOM = 47; +void boom(void) { + char *BOOM; + *BOOM = 47; } diff --git a/unittests/Utility/ModuleCacheTest.cpp b/unittests/Utility/ModuleCacheTest.cpp index 53bfc882f232..911c278c1626 100644 --- a/unittests/Utility/ModuleCacheTest.cpp +++ b/unittests/Utility/ModuleCacheTest.cpp @@ -16,24 +16,20 @@ extern const char *TestMainArgv0; using namespace lldb_private; using namespace lldb; -namespace -{ +namespace { -class ModuleCacheTest : public testing::Test -{ +class ModuleCacheTest : public testing::Test { public: - static void - SetUpTestCase(); + static void SetUpTestCase(); - static void - TearDownTestCase(); + static void TearDownTestCase(); protected: - static FileSpec s_cache_dir; - static llvm::SmallString<128> s_test_executable; + static FileSpec s_cache_dir; + static llvm::SmallString<128> s_test_executable; - void - TryGetAndPut(const FileSpec &cache_dir, const char *hostname, bool expect_download); + void TryGetAndPut(const FileSpec &cache_dir, const char *hostname, + bool expect_download); }; } @@ -43,137 +39,131 @@ llvm::SmallString<128> ModuleCacheTest::s_test_executable; static const char dummy_hostname[] = "dummy_hostname"; static const char dummy_remote_dir[] = "bin"; static const char module_name[] = "TestModule.so"; -static const char module_uuid[] = "F4E7E991-9B61-6AD4-0073-561AC3D9FA10-C043A476"; +static const char module_uuid[] = + "F4E7E991-9B61-6AD4-0073-561AC3D9FA10-C043A476"; static const uint32_t uuid_bytes = 20; static const size_t module_size = 5602; -static FileSpec -GetDummyRemotePath() -{ - FileSpec fs("/", false, FileSpec::ePathSyntaxPosix); - fs.AppendPathComponent(dummy_remote_dir); - fs.AppendPathComponent(module_name); - return fs; +static FileSpec GetDummyRemotePath() { + FileSpec fs("/", false, FileSpec::ePathSyntaxPosix); + fs.AppendPathComponent(dummy_remote_dir); + fs.AppendPathComponent(module_name); + return fs; } -static FileSpec -GetUuidView(FileSpec spec) -{ - spec.AppendPathComponent(".cache"); - spec.AppendPathComponent(module_uuid); - spec.AppendPathComponent(module_name); - return spec; +static FileSpec GetUuidView(FileSpec spec) { + spec.AppendPathComponent(".cache"); + spec.AppendPathComponent(module_uuid); + spec.AppendPathComponent(module_name); + return spec; } -static FileSpec -GetSysrootView(FileSpec spec, const char *hostname) -{ - spec.AppendPathComponent(hostname); - spec.AppendPathComponent(dummy_remote_dir); - spec.AppendPathComponent(module_name); - return spec; +static FileSpec GetSysrootView(FileSpec spec, const char *hostname) { + spec.AppendPathComponent(hostname); + spec.AppendPathComponent(dummy_remote_dir); + spec.AppendPathComponent(module_name); + return spec; } -void -ModuleCacheTest::SetUpTestCase() -{ - HostInfo::Initialize(); - ObjectFileELF::Initialize(); +void ModuleCacheTest::SetUpTestCase() { + HostInfo::Initialize(); + ObjectFileELF::Initialize(); - FileSpec tmpdir_spec; - HostInfo::GetLLDBPath(lldb::ePathTypeLLDBTempSystemDir, s_cache_dir); + FileSpec tmpdir_spec; + HostInfo::GetLLDBPath(lldb::ePathTypeLLDBTempSystemDir, s_cache_dir); - llvm::StringRef exe_folder = llvm::sys::path::parent_path(TestMainArgv0); - s_test_executable = exe_folder; - llvm::sys::path::append(s_test_executable, "Inputs", module_name); + llvm::StringRef exe_folder = llvm::sys::path::parent_path(TestMainArgv0); + s_test_executable = exe_folder; + llvm::sys::path::append(s_test_executable, "Inputs", module_name); } -void -ModuleCacheTest::TearDownTestCase() -{ - ObjectFileELF::Terminate(); - HostInfo::Terminate(); +void ModuleCacheTest::TearDownTestCase() { + ObjectFileELF::Terminate(); + HostInfo::Terminate(); } -static void -VerifyDiskState(const FileSpec &cache_dir, const char *hostname) -{ - FileSpec uuid_view = GetUuidView(cache_dir); - EXPECT_TRUE(uuid_view.Exists()) << "uuid_view is: " << uuid_view.GetCString(); - EXPECT_EQ(module_size, uuid_view.GetByteSize()); +static void VerifyDiskState(const FileSpec &cache_dir, const char *hostname) { + FileSpec uuid_view = GetUuidView(cache_dir); + EXPECT_TRUE(uuid_view.Exists()) << "uuid_view is: " << uuid_view.GetCString(); + EXPECT_EQ(module_size, uuid_view.GetByteSize()); - FileSpec sysroot_view = GetSysrootView(cache_dir, hostname); - EXPECT_TRUE(sysroot_view.Exists()) << "sysroot_view is: " << sysroot_view.GetCString(); - EXPECT_EQ(module_size, sysroot_view.GetByteSize()); + FileSpec sysroot_view = GetSysrootView(cache_dir, hostname); + EXPECT_TRUE(sysroot_view.Exists()) << "sysroot_view is: " + << sysroot_view.GetCString(); + EXPECT_EQ(module_size, sysroot_view.GetByteSize()); } -void -ModuleCacheTest::TryGetAndPut(const FileSpec &cache_dir, const char *hostname, bool expect_download) -{ - ModuleCache mc; - ModuleSpec module_spec; - module_spec.GetFileSpec() = GetDummyRemotePath(); - module_spec.GetUUID().SetFromCString(module_uuid, uuid_bytes); - module_spec.SetObjectSize(module_size); - ModuleSP module_sp; - bool did_create; - bool download_called = false; - - Error error = mc.GetAndPut( - cache_dir, hostname, module_spec, - [this, &download_called](const ModuleSpec &module_spec, const FileSpec &tmp_download_file_spec) { - download_called = true; - EXPECT_STREQ(GetDummyRemotePath().GetCString(), module_spec.GetFileSpec().GetCString()); - std::error_code ec = llvm::sys::fs::copy_file(s_test_executable, tmp_download_file_spec.GetCString()); - EXPECT_FALSE(ec); - return Error(); - }, - [](const ModuleSP &module_sp, const FileSpec &tmp_download_file_spec) { return Error("Not supported."); }, - module_sp, &did_create); - EXPECT_EQ(expect_download, download_called); - - EXPECT_TRUE(error.Success()) << "Error was: " << error.AsCString(); - EXPECT_TRUE(did_create); - ASSERT_TRUE(bool(module_sp)); - - SymbolContextList sc_list; - EXPECT_EQ(1u, module_sp->FindFunctionSymbols(ConstString("boom"), eFunctionNameTypeFull, sc_list)); - EXPECT_STREQ(GetDummyRemotePath().GetCString(), module_sp->GetPlatformFileSpec().GetCString()); - EXPECT_STREQ(module_uuid, module_sp->GetUUID().GetAsString().c_str()); +void ModuleCacheTest::TryGetAndPut(const FileSpec &cache_dir, + const char *hostname, bool expect_download) { + ModuleCache mc; + ModuleSpec module_spec; + module_spec.GetFileSpec() = GetDummyRemotePath(); + module_spec.GetUUID().SetFromCString(module_uuid, uuid_bytes); + module_spec.SetObjectSize(module_size); + ModuleSP module_sp; + bool did_create; + bool download_called = false; + + Error error = mc.GetAndPut( + cache_dir, hostname, module_spec, + [this, &download_called](const ModuleSpec &module_spec, + const FileSpec &tmp_download_file_spec) { + download_called = true; + EXPECT_STREQ(GetDummyRemotePath().GetCString(), + module_spec.GetFileSpec().GetCString()); + std::error_code ec = llvm::sys::fs::copy_file( + s_test_executable, tmp_download_file_spec.GetCString()); + EXPECT_FALSE(ec); + return Error(); + }, + [](const ModuleSP &module_sp, const FileSpec &tmp_download_file_spec) { + return Error("Not supported."); + }, + module_sp, &did_create); + EXPECT_EQ(expect_download, download_called); + + EXPECT_TRUE(error.Success()) << "Error was: " << error.AsCString(); + EXPECT_TRUE(did_create); + ASSERT_TRUE(bool(module_sp)); + + SymbolContextList sc_list; + EXPECT_EQ(1u, module_sp->FindFunctionSymbols(ConstString("boom"), + eFunctionNameTypeFull, sc_list)); + EXPECT_STREQ(GetDummyRemotePath().GetCString(), + module_sp->GetPlatformFileSpec().GetCString()); + EXPECT_STREQ(module_uuid, module_sp->GetUUID().GetAsString().c_str()); } -TEST_F(ModuleCacheTest, GetAndPut) -{ - FileSpec test_cache_dir = s_cache_dir; - test_cache_dir.AppendPathComponent("GetAndPut"); +TEST_F(ModuleCacheTest, GetAndPut) { + FileSpec test_cache_dir = s_cache_dir; + test_cache_dir.AppendPathComponent("GetAndPut"); - const bool expect_download = true; - TryGetAndPut(test_cache_dir, dummy_hostname, expect_download); - VerifyDiskState(test_cache_dir, dummy_hostname); + const bool expect_download = true; + TryGetAndPut(test_cache_dir, dummy_hostname, expect_download); + VerifyDiskState(test_cache_dir, dummy_hostname); } -TEST_F(ModuleCacheTest, GetAndPutUuidExists) -{ - FileSpec test_cache_dir = s_cache_dir; - test_cache_dir.AppendPathComponent("GetAndPutUuidExists"); +TEST_F(ModuleCacheTest, GetAndPutUuidExists) { + FileSpec test_cache_dir = s_cache_dir; + test_cache_dir.AppendPathComponent("GetAndPutUuidExists"); - FileSpec uuid_view = GetUuidView(test_cache_dir); - std::error_code ec = llvm::sys::fs::create_directories(uuid_view.GetDirectory().GetCString()); - ASSERT_FALSE(ec); - ec = llvm::sys::fs::copy_file(s_test_executable, uuid_view.GetCString()); - ASSERT_FALSE(ec); + FileSpec uuid_view = GetUuidView(test_cache_dir); + std::error_code ec = + llvm::sys::fs::create_directories(uuid_view.GetDirectory().GetCString()); + ASSERT_FALSE(ec); + ec = llvm::sys::fs::copy_file(s_test_executable, uuid_view.GetCString()); + ASSERT_FALSE(ec); - const bool expect_download = false; - TryGetAndPut(test_cache_dir, dummy_hostname, expect_download); - VerifyDiskState(test_cache_dir, dummy_hostname); + const bool expect_download = false; + TryGetAndPut(test_cache_dir, dummy_hostname, expect_download); + VerifyDiskState(test_cache_dir, dummy_hostname); } -TEST_F(ModuleCacheTest, GetAndPutStrangeHostname) -{ - FileSpec test_cache_dir = s_cache_dir; - test_cache_dir.AppendPathComponent("GetAndPutStrangeHostname"); +TEST_F(ModuleCacheTest, GetAndPutStrangeHostname) { + FileSpec test_cache_dir = s_cache_dir; + test_cache_dir.AppendPathComponent("GetAndPutStrangeHostname"); - const bool expect_download = true; - TryGetAndPut(test_cache_dir, "tab\tcolon:asterisk*", expect_download); - VerifyDiskState(test_cache_dir, "tab_colon_asterisk_"); + const bool expect_download = true; + TryGetAndPut(test_cache_dir, "tab\tcolon:asterisk*", expect_download); + VerifyDiskState(test_cache_dir, "tab_colon_asterisk_"); } diff --git a/unittests/Utility/StringExtractorTest.cpp b/unittests/Utility/StringExtractorTest.cpp index 0eb6d1bda8bb..6a67a8c3a150 100644 --- a/unittests/Utility/StringExtractorTest.cpp +++ b/unittests/Utility/StringExtractorTest.cpp @@ -1,406 +1,698 @@ -#include <limits.h> #include "gtest/gtest.h" +#include <limits.h> #include "lldb/Utility/StringExtractor.h" -namespace -{ - class StringExtractorTest: public ::testing::Test - { - }; +namespace { +class StringExtractorTest : public ::testing::Test {}; +} + +TEST_F(StringExtractorTest, InitEmpty) { + const char kEmptyString[] = ""; + StringExtractor ex(kEmptyString); + + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(0u, ex.GetFilePos()); + ASSERT_STREQ(kEmptyString, ex.GetStringRef().c_str()); + ASSERT_EQ(true, ex.Empty()); + ASSERT_EQ(0u, ex.GetBytesLeft()); + ASSERT_EQ(nullptr, ex.Peek()); +} + +TEST_F(StringExtractorTest, InitMisc) { + const char kInitMiscString[] = "Hello, StringExtractor!"; + StringExtractor ex(kInitMiscString); + + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(0u, ex.GetFilePos()); + ASSERT_STREQ(kInitMiscString, ex.GetStringRef().c_str()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(sizeof(kInitMiscString) - 1, ex.GetBytesLeft()); + ASSERT_EQ(kInitMiscString[0], *ex.Peek()); +} + +TEST_F(StringExtractorTest, DecodeHexU8_Underflow) { + const char kEmptyString[] = ""; + StringExtractor ex(kEmptyString); + + ASSERT_EQ(-1, ex.DecodeHexU8()); + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(0u, ex.GetFilePos()); + ASSERT_EQ(true, ex.Empty()); + ASSERT_EQ(0u, ex.GetBytesLeft()); + ASSERT_EQ(nullptr, ex.Peek()); +} + +TEST_F(StringExtractorTest, DecodeHexU8_Underflow2) { + const char kEmptyString[] = "1"; + StringExtractor ex(kEmptyString); + + ASSERT_EQ(-1, ex.DecodeHexU8()); + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(0u, ex.GetFilePos()); + ASSERT_EQ(1u, ex.GetBytesLeft()); + ASSERT_EQ('1', *ex.Peek()); +} + +TEST_F(StringExtractorTest, DecodeHexU8_InvalidHex) { + const char kInvalidHex[] = "xa"; + StringExtractor ex(kInvalidHex); + + ASSERT_EQ(-1, ex.DecodeHexU8()); + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(0u, ex.GetFilePos()); + ASSERT_EQ(2u, ex.GetBytesLeft()); + ASSERT_EQ('x', *ex.Peek()); +} + +TEST_F(StringExtractorTest, DecodeHexU8_InvalidHex2) { + const char kInvalidHex[] = "ax"; + StringExtractor ex(kInvalidHex); + + ASSERT_EQ(-1, ex.DecodeHexU8()); + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(0u, ex.GetFilePos()); + ASSERT_EQ(2u, ex.GetBytesLeft()); + ASSERT_EQ('a', *ex.Peek()); +} + +TEST_F(StringExtractorTest, DecodeHexU8_Exact) { + const char kValidHexPair[] = "12"; + StringExtractor ex(kValidHexPair); + + ASSERT_EQ(0x12, ex.DecodeHexU8()); + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(2u, ex.GetFilePos()); + ASSERT_EQ(0u, ex.GetBytesLeft()); + ASSERT_EQ(nullptr, ex.Peek()); +} + +TEST_F(StringExtractorTest, DecodeHexU8_Extra) { + const char kValidHexPair[] = "1234"; + StringExtractor ex(kValidHexPair); + + ASSERT_EQ(0x12, ex.DecodeHexU8()); + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(2u, ex.GetFilePos()); + ASSERT_EQ(2u, ex.GetBytesLeft()); + ASSERT_EQ('3', *ex.Peek()); +} + +TEST_F(StringExtractorTest, GetHexU8_Underflow) { + const char kEmptyString[] = ""; + StringExtractor ex(kEmptyString); + + ASSERT_EQ(0xab, ex.GetHexU8(0xab)); + ASSERT_EQ(false, ex.IsGood()); + ASSERT_EQ(UINT64_MAX, ex.GetFilePos()); + ASSERT_EQ(true, ex.Empty()); + ASSERT_EQ(0u, ex.GetBytesLeft()); + ASSERT_EQ(nullptr, ex.Peek()); } -TEST_F (StringExtractorTest, InitEmpty) -{ - const char kEmptyString[] = ""; - StringExtractor ex (kEmptyString); +TEST_F(StringExtractorTest, GetHexU8_Underflow2) { + const char kOneNibble[] = "1"; + StringExtractor ex(kOneNibble); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (0u, ex.GetFilePos()); - ASSERT_STREQ (kEmptyString, ex.GetStringRef().c_str()); - ASSERT_EQ (true, ex.Empty()); - ASSERT_EQ (0u, ex.GetBytesLeft()); - ASSERT_EQ (nullptr, ex.Peek()); + ASSERT_EQ(0xbc, ex.GetHexU8(0xbc)); + ASSERT_EQ(false, ex.IsGood()); + ASSERT_EQ(UINT64_MAX, ex.GetFilePos()); + ASSERT_EQ(0u, ex.GetBytesLeft()); + ASSERT_EQ(nullptr, ex.Peek()); } -TEST_F (StringExtractorTest, InitMisc) -{ - const char kInitMiscString[] = "Hello, StringExtractor!"; - StringExtractor ex (kInitMiscString); +TEST_F(StringExtractorTest, GetHexU8_InvalidHex) { + const char kInvalidHex[] = "xx"; + StringExtractor ex(kInvalidHex); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (0u, ex.GetFilePos()); - ASSERT_STREQ (kInitMiscString, ex.GetStringRef().c_str()); - ASSERT_EQ (false, ex.Empty()); - ASSERT_EQ (sizeof(kInitMiscString)-1, ex.GetBytesLeft()); - ASSERT_EQ (kInitMiscString[0], *ex.Peek()); + ASSERT_EQ(0xcd, ex.GetHexU8(0xcd)); + ASSERT_EQ(false, ex.IsGood()); + ASSERT_EQ(UINT64_MAX, ex.GetFilePos()); + ASSERT_EQ(0u, ex.GetBytesLeft()); + ASSERT_EQ(nullptr, ex.Peek()); } -TEST_F (StringExtractorTest, DecodeHexU8_Underflow) -{ - const char kEmptyString[] = ""; - StringExtractor ex (kEmptyString); +TEST_F(StringExtractorTest, GetHexU8_Exact) { + const char kValidHexPair[] = "12"; + StringExtractor ex(kValidHexPair); - ASSERT_EQ (-1, ex.DecodeHexU8()); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (0u, ex.GetFilePos()); - ASSERT_EQ (true, ex.Empty()); - ASSERT_EQ (0u, ex.GetBytesLeft()); - ASSERT_EQ (nullptr, ex.Peek()); + ASSERT_EQ(0x12, ex.GetHexU8(0x12)); + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(2u, ex.GetFilePos()); + ASSERT_EQ(0u, ex.GetBytesLeft()); + ASSERT_EQ(nullptr, ex.Peek()); } -TEST_F (StringExtractorTest, DecodeHexU8_Underflow2) -{ - const char kEmptyString[] = "1"; - StringExtractor ex (kEmptyString); - - ASSERT_EQ (-1, ex.DecodeHexU8()); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (0u, ex.GetFilePos()); - ASSERT_EQ (1u, ex.GetBytesLeft()); - ASSERT_EQ ('1', *ex.Peek()); +TEST_F(StringExtractorTest, GetHexU8_Extra) { + const char kValidHexPair[] = "1234"; + StringExtractor ex(kValidHexPair); + + ASSERT_EQ(0x12, ex.GetHexU8(0x12)); + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(2u, ex.GetFilePos()); + ASSERT_EQ(2u, ex.GetBytesLeft()); + ASSERT_EQ('3', *ex.Peek()); } -TEST_F (StringExtractorTest, DecodeHexU8_InvalidHex) -{ - const char kInvalidHex[] = "xa"; - StringExtractor ex (kInvalidHex); - - ASSERT_EQ (-1, ex.DecodeHexU8()); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (0u, ex.GetFilePos()); - ASSERT_EQ (2u, ex.GetBytesLeft()); - ASSERT_EQ ('x', *ex.Peek()); +TEST_F(StringExtractorTest, GetHexU8_Underflow_NoEof) { + const char kEmptyString[] = ""; + StringExtractor ex(kEmptyString); + const bool kSetEofOnFail = false; + + ASSERT_EQ(0xab, ex.GetHexU8(0xab, kSetEofOnFail)); + ASSERT_EQ(false, ex.IsGood()); // this result seems inconsistent with + // kSetEofOnFail == false + ASSERT_EQ(UINT64_MAX, ex.GetFilePos()); + ASSERT_EQ(true, ex.Empty()); + ASSERT_EQ(0u, ex.GetBytesLeft()); + ASSERT_EQ(nullptr, ex.Peek()); } -TEST_F (StringExtractorTest, DecodeHexU8_InvalidHex2) -{ - const char kInvalidHex[] = "ax"; - StringExtractor ex (kInvalidHex); - - ASSERT_EQ (-1, ex.DecodeHexU8()); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (0u, ex.GetFilePos()); - ASSERT_EQ (2u, ex.GetBytesLeft()); - ASSERT_EQ ('a', *ex.Peek()); +TEST_F(StringExtractorTest, GetHexU8_Underflow2_NoEof) { + const char kOneNibble[] = "1"; + StringExtractor ex(kOneNibble); + const bool kSetEofOnFail = false; + + ASSERT_EQ(0xbc, ex.GetHexU8(0xbc, kSetEofOnFail)); + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(0u, ex.GetFilePos()); + ASSERT_EQ(1u, ex.GetBytesLeft()); + ASSERT_EQ('1', *ex.Peek()); } -TEST_F (StringExtractorTest, DecodeHexU8_Exact) -{ - const char kValidHexPair[] = "12"; - StringExtractor ex (kValidHexPair); - - ASSERT_EQ (0x12, ex.DecodeHexU8()); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (2u, ex.GetFilePos()); - ASSERT_EQ (0u, ex.GetBytesLeft()); - ASSERT_EQ (nullptr, ex.Peek()); -} +TEST_F(StringExtractorTest, GetHexU8_InvalidHex_NoEof) { + const char kInvalidHex[] = "xx"; + StringExtractor ex(kInvalidHex); + const bool kSetEofOnFail = false; -TEST_F (StringExtractorTest, DecodeHexU8_Extra) -{ - const char kValidHexPair[] = "1234"; - StringExtractor ex (kValidHexPair); - - ASSERT_EQ (0x12, ex.DecodeHexU8()); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (2u, ex.GetFilePos()); - ASSERT_EQ (2u, ex.GetBytesLeft()); - ASSERT_EQ ('3', *ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexU8_Underflow) -{ - const char kEmptyString[] = ""; - StringExtractor ex (kEmptyString); - - ASSERT_EQ (0xab, ex.GetHexU8(0xab)); - ASSERT_EQ (false, ex.IsGood()); - ASSERT_EQ (UINT64_MAX, ex.GetFilePos()); - ASSERT_EQ (true, ex.Empty()); - ASSERT_EQ (0u, ex.GetBytesLeft()); - ASSERT_EQ (nullptr, ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexU8_Underflow2) -{ - const char kOneNibble[] = "1"; - StringExtractor ex (kOneNibble); - - ASSERT_EQ (0xbc, ex.GetHexU8(0xbc)); - ASSERT_EQ (false, ex.IsGood()); - ASSERT_EQ (UINT64_MAX, ex.GetFilePos()); - ASSERT_EQ (0u, ex.GetBytesLeft()); - ASSERT_EQ (nullptr, ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexU8_InvalidHex) -{ - const char kInvalidHex[] = "xx"; - StringExtractor ex (kInvalidHex); - - ASSERT_EQ (0xcd, ex.GetHexU8(0xcd)); - ASSERT_EQ (false, ex.IsGood()); - ASSERT_EQ (UINT64_MAX, ex.GetFilePos()); - ASSERT_EQ (0u, ex.GetBytesLeft()); - ASSERT_EQ (nullptr, ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexU8_Exact) -{ - const char kValidHexPair[] = "12"; - StringExtractor ex (kValidHexPair); - - ASSERT_EQ (0x12, ex.GetHexU8(0x12)); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (2u, ex.GetFilePos()); - ASSERT_EQ (0u, ex.GetBytesLeft()); - ASSERT_EQ (nullptr, ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexU8_Extra) -{ - const char kValidHexPair[] = "1234"; - StringExtractor ex (kValidHexPair); - - ASSERT_EQ (0x12, ex.GetHexU8(0x12)); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (2u, ex.GetFilePos()); - ASSERT_EQ (2u, ex.GetBytesLeft()); - ASSERT_EQ ('3', *ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexU8_Underflow_NoEof) -{ - const char kEmptyString[] = ""; - StringExtractor ex (kEmptyString); - const bool kSetEofOnFail = false; - - ASSERT_EQ (0xab, ex.GetHexU8(0xab, kSetEofOnFail)); - ASSERT_EQ (false, ex.IsGood()); // this result seems inconsistent with kSetEofOnFail == false - ASSERT_EQ (UINT64_MAX, ex.GetFilePos()); - ASSERT_EQ (true, ex.Empty()); - ASSERT_EQ (0u, ex.GetBytesLeft()); - ASSERT_EQ (nullptr, ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexU8_Underflow2_NoEof) -{ - const char kOneNibble[] = "1"; - StringExtractor ex (kOneNibble); - const bool kSetEofOnFail = false; - - ASSERT_EQ (0xbc, ex.GetHexU8(0xbc, kSetEofOnFail)); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (0u, ex.GetFilePos()); - ASSERT_EQ (1u, ex.GetBytesLeft()); - ASSERT_EQ ('1', *ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexU8_InvalidHex_NoEof) -{ - const char kInvalidHex[] = "xx"; - StringExtractor ex (kInvalidHex); - const bool kSetEofOnFail = false; - - ASSERT_EQ (0xcd, ex.GetHexU8(0xcd, kSetEofOnFail)); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (0u, ex.GetFilePos()); - ASSERT_EQ (2u, ex.GetBytesLeft()); - ASSERT_EQ ('x', *ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexU8_Exact_NoEof) -{ - const char kValidHexPair[] = "12"; - StringExtractor ex (kValidHexPair); - const bool kSetEofOnFail = false; - - ASSERT_EQ (0x12, ex.GetHexU8(0x12, kSetEofOnFail)); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (2u, ex.GetFilePos()); - ASSERT_EQ (0u, ex.GetBytesLeft()); - ASSERT_EQ (nullptr, ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexU8_Extra_NoEof) -{ - const char kValidHexPair[] = "1234"; - StringExtractor ex (kValidHexPair); - const bool kSetEofOnFail = false; - - ASSERT_EQ (0x12, ex.GetHexU8(0x12, kSetEofOnFail)); - ASSERT_EQ (true, ex.IsGood()); - ASSERT_EQ (2u, ex.GetFilePos()); - ASSERT_EQ (2u, ex.GetBytesLeft()); - ASSERT_EQ ('3', *ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexBytes) -{ - const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; - const size_t kValidHexPairs = 8; - StringExtractor ex(kHexEncodedBytes); - - uint8_t dst[kValidHexPairs]; - ASSERT_EQ(kValidHexPairs, ex.GetHexBytes (dst, kValidHexPairs, 0xde)); - EXPECT_EQ(0xab,dst[0]); - EXPECT_EQ(0xcd,dst[1]); - EXPECT_EQ(0xef,dst[2]); - EXPECT_EQ(0x01,dst[3]); - EXPECT_EQ(0x23,dst[4]); - EXPECT_EQ(0x45,dst[5]); - EXPECT_EQ(0x67,dst[6]); - EXPECT_EQ(0x89,dst[7]); - - ASSERT_EQ(true, ex.IsGood()); - ASSERT_EQ(2*kValidHexPairs, ex.GetFilePos()); - ASSERT_EQ(false, ex.Empty()); - ASSERT_EQ(4u, ex.GetBytesLeft()); - ASSERT_EQ('x', *ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexBytes_Underflow) -{ - const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; - const size_t kValidHexPairs = 8; - StringExtractor ex(kHexEncodedBytes); - - uint8_t dst[12]; - ASSERT_EQ(kValidHexPairs, ex.GetHexBytes (dst, sizeof(dst), 0xde)); - EXPECT_EQ(0xab,dst[0]); - EXPECT_EQ(0xcd,dst[1]); - EXPECT_EQ(0xef,dst[2]); - EXPECT_EQ(0x01,dst[3]); - EXPECT_EQ(0x23,dst[4]); - EXPECT_EQ(0x45,dst[5]); - EXPECT_EQ(0x67,dst[6]); - EXPECT_EQ(0x89,dst[7]); - // these bytes should be filled with fail_fill_value 0xde - EXPECT_EQ(0xde,dst[8]); - EXPECT_EQ(0xde,dst[9]); - EXPECT_EQ(0xde,dst[10]); - EXPECT_EQ(0xde,dst[11]); - - ASSERT_EQ(false, ex.IsGood()); - ASSERT_EQ(UINT64_MAX, ex.GetFilePos()); - ASSERT_EQ(false, ex.Empty()); - ASSERT_EQ(0u, ex.GetBytesLeft()); - ASSERT_EQ(0, ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexBytes_Partial) -{ - const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; - const size_t kReadBytes = 4; - StringExtractor ex(kHexEncodedBytes); - - uint8_t dst[12]; - memset(dst, 0xab, sizeof(dst)); - ASSERT_EQ(kReadBytes, ex.GetHexBytes (dst, kReadBytes, 0xde)); - EXPECT_EQ(0xab,dst[0]); - EXPECT_EQ(0xcd,dst[1]); - EXPECT_EQ(0xef,dst[2]); - EXPECT_EQ(0x01,dst[3]); - // these bytes should be unchanged - EXPECT_EQ(0xab,dst[4]); - EXPECT_EQ(0xab,dst[5]); - EXPECT_EQ(0xab,dst[6]); - EXPECT_EQ(0xab,dst[7]); - EXPECT_EQ(0xab,dst[8]); - EXPECT_EQ(0xab,dst[9]); - EXPECT_EQ(0xab,dst[10]); - EXPECT_EQ(0xab,dst[11]); - - ASSERT_EQ(true, ex.IsGood()); - ASSERT_EQ(kReadBytes*2, ex.GetFilePos()); - ASSERT_EQ(false, ex.Empty()); - ASSERT_EQ(12u, ex.GetBytesLeft()); - ASSERT_EQ('2', *ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexBytesAvail) -{ - const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; - const size_t kValidHexPairs = 8; - StringExtractor ex(kHexEncodedBytes); - - uint8_t dst[kValidHexPairs]; - ASSERT_EQ(kValidHexPairs, ex.GetHexBytesAvail (dst, kValidHexPairs)); - EXPECT_EQ(0xab,dst[0]); - EXPECT_EQ(0xcd,dst[1]); - EXPECT_EQ(0xef,dst[2]); - EXPECT_EQ(0x01,dst[3]); - EXPECT_EQ(0x23,dst[4]); - EXPECT_EQ(0x45,dst[5]); - EXPECT_EQ(0x67,dst[6]); - EXPECT_EQ(0x89,dst[7]); - - ASSERT_EQ(true, ex.IsGood()); - ASSERT_EQ(2*kValidHexPairs, ex.GetFilePos()); - ASSERT_EQ(false, ex.Empty()); - ASSERT_EQ(4u, ex.GetBytesLeft()); - ASSERT_EQ('x', *ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexBytesAvail_Underflow) -{ - const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; - const size_t kValidHexPairs = 8; - StringExtractor ex(kHexEncodedBytes); - - uint8_t dst[12]; - memset(dst, 0xef, sizeof(dst)); - ASSERT_EQ(kValidHexPairs, ex.GetHexBytesAvail (dst, sizeof(dst))); - EXPECT_EQ(0xab,dst[0]); - EXPECT_EQ(0xcd,dst[1]); - EXPECT_EQ(0xef,dst[2]); - EXPECT_EQ(0x01,dst[3]); - EXPECT_EQ(0x23,dst[4]); - EXPECT_EQ(0x45,dst[5]); - EXPECT_EQ(0x67,dst[6]); - EXPECT_EQ(0x89,dst[7]); - // these bytes should be unchanged - EXPECT_EQ(0xef,dst[8]); - EXPECT_EQ(0xef,dst[9]); - EXPECT_EQ(0xef,dst[10]); - EXPECT_EQ(0xef,dst[11]); - - ASSERT_EQ(true, ex.IsGood()); - ASSERT_EQ(kValidHexPairs*2, ex.GetFilePos()); - ASSERT_EQ(false, ex.Empty()); - ASSERT_EQ(4u, ex.GetBytesLeft()); - ASSERT_EQ('x', *ex.Peek()); -} - -TEST_F (StringExtractorTest, GetHexBytesAvail_Partial) -{ - const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; - const size_t kReadBytes = 4; - StringExtractor ex(kHexEncodedBytes); - - uint8_t dst[12]; - memset(dst, 0xab, sizeof(dst)); - ASSERT_EQ(kReadBytes, ex.GetHexBytesAvail (dst, kReadBytes)); - EXPECT_EQ(0xab,dst[0]); - EXPECT_EQ(0xcd,dst[1]); - EXPECT_EQ(0xef,dst[2]); - EXPECT_EQ(0x01,dst[3]); - // these bytes should be unchanged - EXPECT_EQ(0xab,dst[4]); - EXPECT_EQ(0xab,dst[5]); - EXPECT_EQ(0xab,dst[6]); - EXPECT_EQ(0xab,dst[7]); - EXPECT_EQ(0xab,dst[8]); - EXPECT_EQ(0xab,dst[9]); - EXPECT_EQ(0xab,dst[10]); - EXPECT_EQ(0xab,dst[11]); - - ASSERT_EQ(true, ex.IsGood()); - ASSERT_EQ(kReadBytes*2, ex.GetFilePos()); - ASSERT_EQ(false, ex.Empty()); - ASSERT_EQ(12u, ex.GetBytesLeft()); - ASSERT_EQ('2', *ex.Peek()); + ASSERT_EQ(0xcd, ex.GetHexU8(0xcd, kSetEofOnFail)); + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(0u, ex.GetFilePos()); + ASSERT_EQ(2u, ex.GetBytesLeft()); + ASSERT_EQ('x', *ex.Peek()); } +TEST_F(StringExtractorTest, GetHexU8_Exact_NoEof) { + const char kValidHexPair[] = "12"; + StringExtractor ex(kValidHexPair); + const bool kSetEofOnFail = false; + ASSERT_EQ(0x12, ex.GetHexU8(0x12, kSetEofOnFail)); + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(2u, ex.GetFilePos()); + ASSERT_EQ(0u, ex.GetBytesLeft()); + ASSERT_EQ(nullptr, ex.Peek()); +} + +TEST_F(StringExtractorTest, GetHexU8_Extra_NoEof) { + const char kValidHexPair[] = "1234"; + StringExtractor ex(kValidHexPair); + const bool kSetEofOnFail = false; + + ASSERT_EQ(0x12, ex.GetHexU8(0x12, kSetEofOnFail)); + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(2u, ex.GetFilePos()); + ASSERT_EQ(2u, ex.GetBytesLeft()); + ASSERT_EQ('3', *ex.Peek()); +} + +TEST_F(StringExtractorTest, GetHexBytes) { + const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; + const size_t kValidHexPairs = 8; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[kValidHexPairs]; + ASSERT_EQ(kValidHexPairs, ex.GetHexBytes(dst, 0xde)); + EXPECT_EQ(0xab, dst[0]); + EXPECT_EQ(0xcd, dst[1]); + EXPECT_EQ(0xef, dst[2]); + EXPECT_EQ(0x01, dst[3]); + EXPECT_EQ(0x23, dst[4]); + EXPECT_EQ(0x45, dst[5]); + EXPECT_EQ(0x67, dst[6]); + EXPECT_EQ(0x89, dst[7]); + + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(2 * kValidHexPairs, ex.GetFilePos()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(4u, ex.GetBytesLeft()); + ASSERT_EQ('x', *ex.Peek()); +} + +TEST_F(StringExtractorTest, GetHexBytes_FullString) { + const char kHexEncodedBytes[] = "abcdef0123456789"; + const size_t kValidHexPairs = 8; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[kValidHexPairs]; + ASSERT_EQ(kValidHexPairs, ex.GetHexBytes(dst, 0xde)); + EXPECT_EQ(0xab, dst[0]); + EXPECT_EQ(0xcd, dst[1]); + EXPECT_EQ(0xef, dst[2]); + EXPECT_EQ(0x01, dst[3]); + EXPECT_EQ(0x23, dst[4]); + EXPECT_EQ(0x45, dst[5]); + EXPECT_EQ(0x67, dst[6]); + EXPECT_EQ(0x89, dst[7]); +} + +TEST_F(StringExtractorTest, GetHexBytes_OddPair) { + const char kHexEncodedBytes[] = "abcdef012345678w"; + const size_t kValidHexPairs = 7; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[8]; + ASSERT_EQ(kValidHexPairs, ex.GetHexBytes(dst, 0xde)); + EXPECT_EQ(0xab, dst[0]); + EXPECT_EQ(0xcd, dst[1]); + EXPECT_EQ(0xef, dst[2]); + EXPECT_EQ(0x01, dst[3]); + EXPECT_EQ(0x23, dst[4]); + EXPECT_EQ(0x45, dst[5]); + EXPECT_EQ(0x67, dst[6]); + + // This one should be invalid + EXPECT_EQ(0xde, dst[7]); +} + +TEST_F(StringExtractorTest, GetHexBytes_OddPair2) { + const char kHexEncodedBytes[] = "abcdef012345678"; + const size_t kValidHexPairs = 7; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[8]; + ASSERT_EQ(kValidHexPairs, ex.GetHexBytes(dst, 0xde)); + EXPECT_EQ(0xab, dst[0]); + EXPECT_EQ(0xcd, dst[1]); + EXPECT_EQ(0xef, dst[2]); + EXPECT_EQ(0x01, dst[3]); + EXPECT_EQ(0x23, dst[4]); + EXPECT_EQ(0x45, dst[5]); + EXPECT_EQ(0x67, dst[6]); + + EXPECT_EQ(0xde, dst[7]); +} + +TEST_F(StringExtractorTest, GetHexBytes_Underflow) { + const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; + const size_t kValidHexPairs = 8; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[12]; + ASSERT_EQ(kValidHexPairs, ex.GetHexBytes(dst, 0xde)); + EXPECT_EQ(0xab, dst[0]); + EXPECT_EQ(0xcd, dst[1]); + EXPECT_EQ(0xef, dst[2]); + EXPECT_EQ(0x01, dst[3]); + EXPECT_EQ(0x23, dst[4]); + EXPECT_EQ(0x45, dst[5]); + EXPECT_EQ(0x67, dst[6]); + EXPECT_EQ(0x89, dst[7]); + // these bytes should be filled with fail_fill_value 0xde + EXPECT_EQ(0xde, dst[8]); + EXPECT_EQ(0xde, dst[9]); + EXPECT_EQ(0xde, dst[10]); + EXPECT_EQ(0xde, dst[11]); + + ASSERT_EQ(false, ex.IsGood()); + ASSERT_EQ(UINT64_MAX, ex.GetFilePos()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(0u, ex.GetBytesLeft()); + ASSERT_EQ(0, ex.Peek()); +} + +TEST_F(StringExtractorTest, GetHexBytes_Partial) { + const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; + const size_t kReadBytes = 4; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[12]; + memset(dst, 0xab, sizeof(dst)); + ASSERT_EQ( + kReadBytes, + ex.GetHexBytes(llvm::MutableArrayRef<uint8_t>(dst, kReadBytes), 0xde)); + EXPECT_EQ(0xab, dst[0]); + EXPECT_EQ(0xcd, dst[1]); + EXPECT_EQ(0xef, dst[2]); + EXPECT_EQ(0x01, dst[3]); + // these bytes should be unchanged + EXPECT_EQ(0xab, dst[4]); + EXPECT_EQ(0xab, dst[5]); + EXPECT_EQ(0xab, dst[6]); + EXPECT_EQ(0xab, dst[7]); + EXPECT_EQ(0xab, dst[8]); + EXPECT_EQ(0xab, dst[9]); + EXPECT_EQ(0xab, dst[10]); + EXPECT_EQ(0xab, dst[11]); + + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(kReadBytes * 2, ex.GetFilePos()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(12u, ex.GetBytesLeft()); + ASSERT_EQ('2', *ex.Peek()); +} + +TEST_F(StringExtractorTest, GetHexBytesAvail) { + const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; + const size_t kValidHexPairs = 8; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[kValidHexPairs]; + ASSERT_EQ(kValidHexPairs, ex.GetHexBytesAvail(dst)); + EXPECT_EQ(0xab, dst[0]); + EXPECT_EQ(0xcd, dst[1]); + EXPECT_EQ(0xef, dst[2]); + EXPECT_EQ(0x01, dst[3]); + EXPECT_EQ(0x23, dst[4]); + EXPECT_EQ(0x45, dst[5]); + EXPECT_EQ(0x67, dst[6]); + EXPECT_EQ(0x89, dst[7]); + + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(2 * kValidHexPairs, ex.GetFilePos()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(4u, ex.GetBytesLeft()); + ASSERT_EQ('x', *ex.Peek()); +} + +TEST_F(StringExtractorTest, GetHexBytesAvail_FullString) { + const char kHexEncodedBytes[] = "abcdef0123456789"; + const size_t kValidHexPairs = 8; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[kValidHexPairs]; + ASSERT_EQ(kValidHexPairs, ex.GetHexBytesAvail(dst)); + EXPECT_EQ(0xab, dst[0]); + EXPECT_EQ(0xcd, dst[1]); + EXPECT_EQ(0xef, dst[2]); + EXPECT_EQ(0x01, dst[3]); + EXPECT_EQ(0x23, dst[4]); + EXPECT_EQ(0x45, dst[5]); + EXPECT_EQ(0x67, dst[6]); + EXPECT_EQ(0x89, dst[7]); +} + +TEST_F(StringExtractorTest, GetHexBytesAvail_OddPair) { + const char kHexEncodedBytes[] = "abcdef012345678w"; + const size_t kValidHexPairs = 7; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[8]; + ASSERT_EQ(kValidHexPairs, ex.GetHexBytesAvail(dst)); + EXPECT_EQ(0xab, dst[0]); + EXPECT_EQ(0xcd, dst[1]); + EXPECT_EQ(0xef, dst[2]); + EXPECT_EQ(0x01, dst[3]); + EXPECT_EQ(0x23, dst[4]); + EXPECT_EQ(0x45, dst[5]); + EXPECT_EQ(0x67, dst[6]); +} + +TEST_F(StringExtractorTest, GetHexBytesAvail_OddPair2) { + const char kHexEncodedBytes[] = "abcdef012345678"; + const size_t kValidHexPairs = 7; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[8]; + ASSERT_EQ(kValidHexPairs, ex.GetHexBytesAvail(dst)); + EXPECT_EQ(0xab, dst[0]); + EXPECT_EQ(0xcd, dst[1]); + EXPECT_EQ(0xef, dst[2]); + EXPECT_EQ(0x01, dst[3]); + EXPECT_EQ(0x23, dst[4]); + EXPECT_EQ(0x45, dst[5]); + EXPECT_EQ(0x67, dst[6]); +} + +TEST_F(StringExtractorTest, GetHexBytesAvail_Underflow) { + const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; + const size_t kValidHexPairs = 8; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[12]; + memset(dst, 0xef, sizeof(dst)); + ASSERT_EQ(kValidHexPairs, ex.GetHexBytesAvail(dst)); + EXPECT_EQ(0xab, dst[0]); + EXPECT_EQ(0xcd, dst[1]); + EXPECT_EQ(0xef, dst[2]); + EXPECT_EQ(0x01, dst[3]); + EXPECT_EQ(0x23, dst[4]); + EXPECT_EQ(0x45, dst[5]); + EXPECT_EQ(0x67, dst[6]); + EXPECT_EQ(0x89, dst[7]); + // these bytes should be unchanged + EXPECT_EQ(0xef, dst[8]); + EXPECT_EQ(0xef, dst[9]); + EXPECT_EQ(0xef, dst[10]); + EXPECT_EQ(0xef, dst[11]); + + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(kValidHexPairs * 2, ex.GetFilePos()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(4u, ex.GetBytesLeft()); + ASSERT_EQ('x', *ex.Peek()); +} + +TEST_F(StringExtractorTest, GetHexBytesAvail_Partial) { + const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; + const size_t kReadBytes = 4; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[12]; + memset(dst, 0xab, sizeof(dst)); + ASSERT_EQ(kReadBytes, ex.GetHexBytesAvail( + llvm::MutableArrayRef<uint8_t>(dst, kReadBytes))); + EXPECT_EQ(0xab, dst[0]); + EXPECT_EQ(0xcd, dst[1]); + EXPECT_EQ(0xef, dst[2]); + EXPECT_EQ(0x01, dst[3]); + // these bytes should be unchanged + EXPECT_EQ(0xab, dst[4]); + EXPECT_EQ(0xab, dst[5]); + EXPECT_EQ(0xab, dst[6]); + EXPECT_EQ(0xab, dst[7]); + EXPECT_EQ(0xab, dst[8]); + EXPECT_EQ(0xab, dst[9]); + EXPECT_EQ(0xab, dst[10]); + EXPECT_EQ(0xab, dst[11]); + + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(kReadBytes * 2, ex.GetFilePos()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(12u, ex.GetBytesLeft()); + ASSERT_EQ('2', *ex.Peek()); +} + +TEST_F(StringExtractorTest, GetNameColonValueSuccess) { + const char kNameColonPairs[] = "key1:value1;key2:value2;"; + StringExtractor ex(kNameColonPairs); + + llvm::StringRef name; + llvm::StringRef value; + EXPECT_TRUE(ex.GetNameColonValue(name, value)); + EXPECT_EQ("key1", name); + EXPECT_EQ("value1", value); + EXPECT_TRUE(ex.GetNameColonValue(name, value)); + EXPECT_EQ("key2", name); + EXPECT_EQ("value2", value); + EXPECT_EQ(0u, ex.GetBytesLeft()); +} + +TEST_F(StringExtractorTest, GetNameColonValueContainsColon) { + const char kNameColonPairs[] = "key1:value1:value2;key2:value3;"; + StringExtractor ex(kNameColonPairs); + + llvm::StringRef name; + llvm::StringRef value; + EXPECT_TRUE(ex.GetNameColonValue(name, value)); + EXPECT_EQ("key1", name); + EXPECT_EQ("value1:value2", value); + EXPECT_TRUE(ex.GetNameColonValue(name, value)); + EXPECT_EQ("key2", name); + EXPECT_EQ("value3", value); + EXPECT_EQ(0u, ex.GetBytesLeft()); +} + +TEST_F(StringExtractorTest, GetNameColonValueNoSemicolon) { + const char kNameColonPairs[] = "key1:value1"; + StringExtractor ex(kNameColonPairs); + + llvm::StringRef name; + llvm::StringRef value; + EXPECT_FALSE(ex.GetNameColonValue(name, value)); + EXPECT_EQ(0u, ex.GetBytesLeft()); +} + +TEST_F(StringExtractorTest, GetNameColonValueNoColon) { + const char kNameColonPairs[] = "key1value1;"; + StringExtractor ex(kNameColonPairs); + + llvm::StringRef name; + llvm::StringRef value; + EXPECT_FALSE(ex.GetNameColonValue(name, value)); + EXPECT_EQ(0u, ex.GetBytesLeft()); +} + +TEST_F(StringExtractorTest, GetU32LittleEndian) { + StringExtractor ex(""); + EXPECT_EQ(0x0ull, ex.GetHexMaxU32(true, 0)); + + ex.Reset("0"); + EXPECT_EQ(0x0ull, ex.GetHexMaxU32(true, 1)); + + ex.Reset("1"); + EXPECT_EQ(0x1ull, ex.GetHexMaxU32(true, 0)); + + ex.Reset("01"); + EXPECT_EQ(0x1ull, ex.GetHexMaxU32(true, 0)); + + ex.Reset("001"); + EXPECT_EQ(0x100ull, ex.GetHexMaxU32(true, 0)); + + ex.Reset("12"); + EXPECT_EQ(0x12ull, ex.GetHexMaxU32(true, 0)); + + ex.Reset("123"); + EXPECT_EQ(0x312ull, ex.GetHexMaxU32(true, 0)); + + ex.Reset("1203"); + EXPECT_EQ(0x312ull, ex.GetHexMaxU32(true, 0)); + + ex.Reset("1234"); + EXPECT_EQ(0x3412ull, ex.GetHexMaxU32(true, 0)); + + ex.Reset("12340"); + EXPECT_EQ(0x3412ull, ex.GetHexMaxU32(true, 0)); + + ex.Reset("123400"); + EXPECT_EQ(0x3412ull, ex.GetHexMaxU32(true, 0)); + + ex.Reset("12345670"); + EXPECT_EQ(0x70563412ull, ex.GetHexMaxU32(true, 0)); + + ex.Reset("123456701"); + EXPECT_EQ(0ull, ex.GetHexMaxU32(true, 0)); +} + +TEST_F(StringExtractorTest, GetU32BigEndian) { + StringExtractor ex(""); + EXPECT_EQ(0x0ull, ex.GetHexMaxU32(false, 0)); + + ex.Reset("0"); + EXPECT_EQ(0x0ull, ex.GetHexMaxU32(false, 1)); + + ex.Reset("1"); + EXPECT_EQ(0x1ull, ex.GetHexMaxU32(false, 0)); + + ex.Reset("01"); + EXPECT_EQ(0x1ull, ex.GetHexMaxU32(false, 0)); + + ex.Reset("001"); + EXPECT_EQ(0x1ull, ex.GetHexMaxU32(false, 0)); + + ex.Reset("12"); + EXPECT_EQ(0x12ull, ex.GetHexMaxU32(false, 0)); + + ex.Reset("123"); + EXPECT_EQ(0x123ull, ex.GetHexMaxU32(false, 0)); + + ex.Reset("1203"); + EXPECT_EQ(0x1203ull, ex.GetHexMaxU32(false, 0)); + + ex.Reset("1234"); + EXPECT_EQ(0x1234ull, ex.GetHexMaxU32(false, 0)); + + ex.Reset("12340"); + EXPECT_EQ(0x12340ull, ex.GetHexMaxU32(false, 0)); + + ex.Reset("123400"); + EXPECT_EQ(0x123400ull, ex.GetHexMaxU32(false, 0)); + + ex.Reset("12345670"); + EXPECT_EQ(0x12345670ull, ex.GetHexMaxU32(false, 0)); + + ex.Reset("123456700"); + EXPECT_EQ(0ull, ex.GetHexMaxU32(false, 0)); +} + +TEST_F(StringExtractorTest, GetU64LittleEndian) { + StringExtractor ex(""); + EXPECT_EQ(0x0ull, ex.GetHexMaxU64(true, 0)); + + ex.Reset("0"); + EXPECT_EQ(0x0ull, ex.GetHexMaxU64(true, 1)); + + ex.Reset("1"); + EXPECT_EQ(0x1ull, ex.GetHexMaxU64(true, 0)); + + ex.Reset("01"); + EXPECT_EQ(0x1ull, ex.GetHexMaxU64(true, 0)); + + ex.Reset("001"); + EXPECT_EQ(0x100ull, ex.GetHexMaxU64(true, 0)); + + ex.Reset("12"); + EXPECT_EQ(0x12ull, ex.GetHexMaxU64(true, 0)); + + ex.Reset("123"); + EXPECT_EQ(0x312ull, ex.GetHexMaxU64(true, 0)); + + ex.Reset("1203"); + EXPECT_EQ(0x312ull, ex.GetHexMaxU64(true, 0)); + + ex.Reset("1234"); + EXPECT_EQ(0x3412ull, ex.GetHexMaxU64(true, 0)); + + ex.Reset("12340"); + EXPECT_EQ(0x3412ull, ex.GetHexMaxU64(true, 0)); + + ex.Reset("123400"); + EXPECT_EQ(0x3412ull, ex.GetHexMaxU64(true, 0)); + + ex.Reset("123456789ABCDEF0"); + EXPECT_EQ(0xF0DEBC9A78563412ULL, ex.GetHexMaxU64(true, 0)); + + ex.Reset("123456789ABCDEF01"); + EXPECT_EQ(0ull, ex.GetHexMaxU64(true, 0)); +} + +TEST_F(StringExtractorTest, GetU64BigEndian) { + StringExtractor ex(""); + EXPECT_EQ(0x0ull, ex.GetHexMaxU64(false, 0)); + + ex.Reset("0"); + EXPECT_EQ(0x0ull, ex.GetHexMaxU64(false, 1)); + + ex.Reset("1"); + EXPECT_EQ(0x1ull, ex.GetHexMaxU64(false, 0)); + + ex.Reset("01"); + EXPECT_EQ(0x1ull, ex.GetHexMaxU64(false, 0)); + + ex.Reset("001"); + EXPECT_EQ(0x1ull, ex.GetHexMaxU64(false, 0)); + + ex.Reset("12"); + EXPECT_EQ(0x12ull, ex.GetHexMaxU64(false, 0)); + + ex.Reset("123"); + EXPECT_EQ(0x123ull, ex.GetHexMaxU64(false, 0)); + + ex.Reset("1203"); + EXPECT_EQ(0x1203ull, ex.GetHexMaxU64(false, 0)); + + ex.Reset("1234"); + EXPECT_EQ(0x1234ull, ex.GetHexMaxU64(false, 0)); + + ex.Reset("12340"); + EXPECT_EQ(0x12340ull, ex.GetHexMaxU64(false, 0)); + + ex.Reset("123400"); + EXPECT_EQ(0x123400ull, ex.GetHexMaxU64(false, 0)); + + ex.Reset("123456789ABCDEF0"); + EXPECT_EQ(0x123456789ABCDEF0ULL, ex.GetHexMaxU64(false, 0)); + + ex.Reset("123456789ABCDEF000"); + EXPECT_EQ(0ull, ex.GetHexMaxU64(false, 0)); +} diff --git a/unittests/Utility/TaskPoolTest.cpp b/unittests/Utility/TaskPoolTest.cpp index 24431e2c7896..172e32a9c6c0 100644 --- a/unittests/Utility/TaskPoolTest.cpp +++ b/unittests/Utility/TaskPoolTest.cpp @@ -2,61 +2,53 @@ #include "lldb/Utility/TaskPool.h" -TEST (TaskPoolTest, AddTask) -{ - auto fn = [](int x) { return x * x + 1; }; - - auto f1 = TaskPool::AddTask(fn, 1); - auto f2 = TaskPool::AddTask(fn, 2); - auto f3 = TaskPool::AddTask(fn, 3); - auto f4 = TaskPool::AddTask(fn, 4); - - ASSERT_EQ (10, f3.get()); - ASSERT_EQ ( 2, f1.get()); - ASSERT_EQ (17, f4.get()); - ASSERT_EQ ( 5, f2.get()); +TEST(TaskPoolTest, AddTask) { + auto fn = [](int x) { return x * x + 1; }; + + auto f1 = TaskPool::AddTask(fn, 1); + auto f2 = TaskPool::AddTask(fn, 2); + auto f3 = TaskPool::AddTask(fn, 3); + auto f4 = TaskPool::AddTask(fn, 4); + + ASSERT_EQ(10, f3.get()); + ASSERT_EQ(2, f1.get()); + ASSERT_EQ(17, f4.get()); + ASSERT_EQ(5, f2.get()); } -TEST (TaskPoolTest, RunTasks) -{ - std::vector<int> r(4); - - auto fn = [](int x, int& y) { y = x * x + 1; }; - - TaskPool::RunTasks( - [fn, &r]() { fn(1, r[0]); }, - [fn, &r]() { fn(2, r[1]); }, - [fn, &r]() { fn(3, r[2]); }, - [fn, &r]() { fn(4, r[3]); } - ); - - ASSERT_EQ ( 2, r[0]); - ASSERT_EQ ( 5, r[1]); - ASSERT_EQ (10, r[2]); - ASSERT_EQ (17, r[3]); +TEST(TaskPoolTest, RunTasks) { + std::vector<int> r(4); + + auto fn = [](int x, int &y) { y = x * x + 1; }; + + TaskPool::RunTasks([fn, &r]() { fn(1, r[0]); }, [fn, &r]() { fn(2, r[1]); }, + [fn, &r]() { fn(3, r[2]); }, [fn, &r]() { fn(4, r[3]); }); + + ASSERT_EQ(2, r[0]); + ASSERT_EQ(5, r[1]); + ASSERT_EQ(10, r[2]); + ASSERT_EQ(17, r[3]); } -TEST (TaskPoolTest, TaskRunner) -{ - auto fn = [](int x) { return std::make_pair(x, x * x); }; - - TaskRunner<std::pair<int, int>> tr; - tr.AddTask(fn, 1); - tr.AddTask(fn, 2); - tr.AddTask(fn, 3); - tr.AddTask(fn, 4); - - int count = 0; - while (true) - { - auto f = tr.WaitForNextCompletedTask(); - if (!f.valid()) - break; - - ++count; - std::pair<int, int> v = f.get(); - ASSERT_EQ (v.first * v.first, v.second); - } - - ASSERT_EQ(4, count); +TEST(TaskPoolTest, TaskRunner) { + auto fn = [](int x) { return std::make_pair(x, x * x); }; + + TaskRunner<std::pair<int, int>> tr; + tr.AddTask(fn, 1); + tr.AddTask(fn, 2); + tr.AddTask(fn, 3); + tr.AddTask(fn, 4); + + int count = 0; + while (true) { + auto f = tr.WaitForNextCompletedTask(); + if (!f.valid()) + break; + + ++count; + std::pair<int, int> v = f.get(); + ASSERT_EQ(v.first * v.first, v.second); + } + + ASSERT_EQ(4, count); } diff --git a/unittests/Utility/TimeoutTest.cpp b/unittests/Utility/TimeoutTest.cpp new file mode 100644 index 000000000000..a30c616d411b --- /dev/null +++ b/unittests/Utility/TimeoutTest.cpp @@ -0,0 +1,22 @@ +//===-- TimeoutTest.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/Timeout.h" +#include "gtest/gtest.h" + +using namespace lldb_private; +using namespace std::chrono; + +TEST(TimeoutTest, Construction) { + ASSERT_FALSE(Timeout<std::micro>(llvm::None)); + ASSERT_TRUE(bool(Timeout<std::micro>(seconds(0)))); + ASSERT_EQ(seconds(0), *Timeout<std::micro>(seconds(0))); + ASSERT_EQ(seconds(3), *Timeout<std::micro>(seconds(3))); + ASSERT_TRUE(bool(Timeout<std::micro>(Timeout<std::milli>(seconds(0))))); +} diff --git a/unittests/Utility/UriParserTest.cpp b/unittests/Utility/UriParserTest.cpp index fe0a6a70f212..8b08e63eb8c7 100644 --- a/unittests/Utility/UriParserTest.cpp +++ b/unittests/Utility/UriParserTest.cpp @@ -1,159 +1,155 @@ -#include "gtest/gtest.h" #include "Utility/UriParser.h" +#include "gtest/gtest.h" -namespace -{ - class UriParserTest: public ::testing::Test - { - }; +namespace { +class UriParserTest : public ::testing::Test {}; } // result strings (scheme/hostname/port/path) passed into UriParser::Parse -// are initialized to kAsdf so we can verify that they are unmodified if the +// are initialized to kAsdf so we can verify that they are unmodified if the // URI is invalid -static const char* kAsdf = "asdf"; +static const char *kAsdf = "asdf"; -class UriTestCase -{ +class UriTestCase { public: - UriTestCase(const char* uri, const char* scheme, const char* hostname, int port, const char* path) : - m_uri(uri), - m_result(true), - m_scheme(scheme), - m_hostname(hostname), - m_port(port), - m_path(path) - { - } - - UriTestCase(const char* uri) : - m_uri(uri), - m_result(false), - m_scheme(kAsdf), - m_hostname(kAsdf), - m_port(1138), - m_path(kAsdf) - { - } - - const char* m_uri; - bool m_result; - const char* m_scheme; - const char* m_hostname; - int m_port; - const char* m_path; + UriTestCase(const char *uri, const char *scheme, const char *hostname, + int port, const char *path) + : m_uri(uri), m_result(true), m_scheme(scheme), m_hostname(hostname), + m_port(port), m_path(path) {} + + UriTestCase(const char *uri) + : m_uri(uri), m_result(false), m_scheme(kAsdf), m_hostname(kAsdf), + m_port(1138), m_path(kAsdf) {} + + const char *m_uri; + bool m_result; + const char *m_scheme; + const char *m_hostname; + int m_port; + const char *m_path; }; -#define VALIDATE \ - std::string scheme(kAsdf); \ - std::string hostname(kAsdf); \ - int port(1138); \ - std::string path(kAsdf); \ - EXPECT_EQ (testCase.m_result, UriParser::Parse(testCase.m_uri, scheme, hostname, port, path)); \ - EXPECT_STREQ (testCase.m_scheme, scheme.c_str()); \ - EXPECT_STREQ (testCase.m_hostname, hostname.c_str()); \ - EXPECT_EQ (testCase.m_port, port); \ - EXPECT_STREQ (testCase.m_path, path.c_str()); +#define VALIDATE \ + llvm::StringRef scheme(kAsdf); \ + llvm::StringRef hostname(kAsdf); \ + int port(1138); \ + llvm::StringRef path(kAsdf); \ + EXPECT_EQ(testCase.m_result, \ + UriParser::Parse(testCase.m_uri, scheme, hostname, port, path)); \ + EXPECT_STREQ(testCase.m_scheme, scheme.str().c_str()); \ + EXPECT_STREQ(testCase.m_hostname, hostname.str().c_str()); \ + EXPECT_EQ(testCase.m_port, port); \ + EXPECT_STREQ(testCase.m_path, path.str().c_str()); -TEST_F (UriParserTest, Minimal) -{ - const UriTestCase testCase("x://y", "x", "y", -1, "/"); - VALIDATE +TEST_F(UriParserTest, Minimal) { + const UriTestCase testCase("x://y", "x", "y", -1, "/"); + VALIDATE } -TEST_F (UriParserTest, MinimalPort) -{ - const UriTestCase testCase("x://y:1", "x", "y", 1, "/"); - VALIDATE -} +TEST_F(UriParserTest, MinimalPort) { + const UriTestCase testCase("x://y:1", "x", "y", 1, "/"); + llvm::StringRef scheme(kAsdf); + llvm::StringRef hostname(kAsdf); + int port(1138); + llvm::StringRef path(kAsdf); + bool result = UriParser::Parse(testCase.m_uri, scheme, hostname, port, path); + EXPECT_EQ(testCase.m_result, result); -TEST_F (UriParserTest, MinimalPath) -{ - const UriTestCase testCase("x://y/", "x", "y", -1, "/"); - VALIDATE + EXPECT_STREQ(testCase.m_scheme, scheme.str().c_str()); + EXPECT_STREQ(testCase.m_hostname, hostname.str().c_str()); + EXPECT_EQ(testCase.m_port, port); + EXPECT_STREQ(testCase.m_path, path.str().c_str()); } -TEST_F (UriParserTest, MinimalPortPath) -{ - const UriTestCase testCase("x://y:1/", "x", "y", 1, "/"); - VALIDATE +TEST_F(UriParserTest, MinimalPath) { + const UriTestCase testCase("x://y/", "x", "y", -1, "/"); + VALIDATE } -TEST_F (UriParserTest, LongPath) -{ - const UriTestCase testCase("x://y/abc/def/xyz", "x", "y", -1, "/abc/def/xyz"); - VALIDATE +TEST_F(UriParserTest, MinimalPortPath) { + const UriTestCase testCase("x://y:1/", "x", "y", 1, "/"); + VALIDATE } -TEST_F (UriParserTest, TypicalPortPath) -{ - const UriTestCase testCase("connect://192.168.100.132:5432/", "connect", "192.168.100.132", 5432, "/"); - VALIDATE +TEST_F(UriParserTest, LongPath) { + const UriTestCase testCase("x://y/abc/def/xyz", "x", "y", -1, "/abc/def/xyz"); + VALIDATE } -TEST_F (UriParserTest, BracketedHostnamePort) -{ - const UriTestCase testCase("connect://[192.168.100.132]:5432/", "connect", "192.168.100.132", 5432, "/"); - VALIDATE +TEST_F(UriParserTest, TypicalPortPath) { + const UriTestCase testCase("connect://192.168.100.132:5432/", "connect", + "192.168.100.132", 5432, "/"); + VALIDATE; } -TEST_F (UriParserTest, BracketedHostname) -{ - const UriTestCase testCase("connect://[192.168.100.132]", "connect", "192.168.100.132", -1, "/"); - VALIDATE +TEST_F(UriParserTest, BracketedHostnamePort) { + const UriTestCase testCase("connect://[192.168.100.132]:5432/", "connect", + "192.168.100.132", 5432, "/"); + llvm::StringRef scheme(kAsdf); + llvm::StringRef hostname(kAsdf); + int port(1138); + llvm::StringRef path(kAsdf); + bool result = UriParser::Parse(testCase.m_uri, scheme, hostname, port, path); + EXPECT_EQ(testCase.m_result, result); + + EXPECT_STREQ(testCase.m_scheme, scheme.str().c_str()); + EXPECT_STREQ(testCase.m_hostname, hostname.str().c_str()); + EXPECT_EQ(testCase.m_port, port); + EXPECT_STREQ(testCase.m_path, path.str().c_str()); } -TEST_F (UriParserTest, BracketedHostnameWithColon) -{ - const UriTestCase testCase("connect://[192.168.100.132:5555]:1234", "connect", "192.168.100.132:5555", 1234, "/"); - VALIDATE +TEST_F(UriParserTest, BracketedHostname) { + const UriTestCase testCase("connect://[192.168.100.132]", "connect", + "192.168.100.132", -1, "/"); + VALIDATE } -TEST_F (UriParserTest, SchemeHostSeparator) -{ - const UriTestCase testCase("x:/y"); - VALIDATE +TEST_F(UriParserTest, BracketedHostnameWithColon) { + const UriTestCase testCase("connect://[192.168.100.132:5555]:1234", "connect", + "192.168.100.132:5555", 1234, "/"); + VALIDATE } -TEST_F (UriParserTest, SchemeHostSeparator2) -{ - const UriTestCase testCase("x:y"); - VALIDATE +TEST_F(UriParserTest, SchemeHostSeparator) { + const UriTestCase testCase("x:/y"); + VALIDATE } -TEST_F (UriParserTest, SchemeHostSeparator3) -{ - const UriTestCase testCase("x//y"); - VALIDATE +TEST_F(UriParserTest, SchemeHostSeparator2) { + const UriTestCase testCase("x:y"); + VALIDATE } -TEST_F (UriParserTest, SchemeHostSeparator4) -{ - const UriTestCase testCase("x/y"); - VALIDATE +TEST_F(UriParserTest, SchemeHostSeparator3) { + const UriTestCase testCase("x//y"); + VALIDATE } -TEST_F (UriParserTest, BadPort) -{ - const UriTestCase testCase("x://y:a/"); - VALIDATE +TEST_F(UriParserTest, SchemeHostSeparator4) { + const UriTestCase testCase("x/y"); + VALIDATE } -TEST_F (UriParserTest, BadPort2) -{ - const UriTestCase testCase("x://y:5432a/"); - VALIDATE +TEST_F(UriParserTest, BadPort) { + const UriTestCase testCase("x://y:a/"); + VALIDATE } -TEST_F (UriParserTest, Empty) -{ - const UriTestCase testCase(""); - VALIDATE +TEST_F(UriParserTest, BadPort2) { + const UriTestCase testCase("x://y:5432a/"); + VALIDATE } -TEST_F (UriParserTest, PortOverflow) -{ - const UriTestCase testCase("x://y:0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/"); - VALIDATE +TEST_F(UriParserTest, Empty) { + const UriTestCase testCase(""); + VALIDATE } +TEST_F(UriParserTest, PortOverflow) { + const UriTestCase testCase("x://" + "y:" + "0123456789012345678901234567890123456789012345678" + "9012345678901234567890123456789012345678901234567" + "89/"); + VALIDATE +} diff --git a/unittests/gtest_common.h b/unittests/gtest_common.h index 006c9596ca4f..c8fe92575941 100644 --- a/unittests/gtest_common.h +++ b/unittests/gtest_common.h @@ -17,8 +17,10 @@ // units. Be very leary about putting anything in this file. #if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) -// Due to a bug in <thread>, when _HAS_EXCEPTIONS == 0 the header will try to call -// uncaught_exception() without having a declaration for it. The fix for this is +// Due to a bug in <thread>, when _HAS_EXCEPTIONS == 0 the header will try to +// call +// uncaught_exception() without having a declaration for it. The fix for this +// is // to manually #include <eh.h>, which contains this declaration. #include <eh.h> #endif |