//===-- ReproducerInstrumentationTest.cpp -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include "lldb/Utility/ReproducerInstrumentation.h" using namespace lldb_private; using namespace lldb_private::repro; struct Foo { int m = 1; }; struct Bar { double m = 2; }; bool operator==(const Foo &LHS, const Foo &RHS) { return LHS.m == RHS.m; } bool operator==(const Bar &LHS, const Bar &RHS) { return LHS.m == RHS.m; } struct Pod { bool a = true; bool b = false; char c = 'a'; float d = 1.1; int e = 2; long long f = 3; long g = 4; short h = 5; unsigned char i = 'b'; unsigned int j = 6; unsigned long long k = 7; unsigned long l = 8; unsigned short m = 9; Pod() {} }; class TestingRegistry : public Registry { public: TestingRegistry(); }; static llvm::Optional g_serializer; static llvm::Optional g_registry; #define LLDB_GET_INSTRUMENTATION_DATA() \ InstrumentationData(*g_serializer, *g_registry) class InstrumentedFoo { public: InstrumentedFoo() = default; /// Instrumented methods. /// { InstrumentedFoo(int i); InstrumentedFoo(const InstrumentedFoo &foo); InstrumentedFoo &operator=(const InstrumentedFoo &foo); void A(int a); void B(int &b) const; int C(float *c); int D(const char *d) const; static void E(double e); static int F(); void Validate(); //// } private: int m_a = 0; mutable int m_b = 0; float m_c = 0; mutable std::string m_d = {}; static double g_e; static bool g_f; mutable int m_called = 0; }; class InstrumentedBar { public: /// Instrumented methods. /// { InstrumentedBar(); InstrumentedFoo GetInstrumentedFoo(); InstrumentedFoo &GetInstrumentedFooRef(); InstrumentedFoo *GetInstrumentedFooPtr(); void SetInstrumentedFoo(InstrumentedFoo *foo); void SetInstrumentedFoo(InstrumentedFoo &foo); void Validate(); /// } private: bool m_get_instrumend_foo_called = false; InstrumentedFoo *m_foo_set_by_ptr = nullptr; InstrumentedFoo *m_foo_set_by_ref = nullptr; }; double InstrumentedFoo::g_e = 0; bool InstrumentedFoo::g_f = false; static std::vector g_foos; static std::vector g_bars; void ClearObjects() { g_foos.clear(); g_bars.clear(); } void ValidateObjects(size_t expected_foos, size_t expected_bars) { EXPECT_EQ(expected_foos, g_foos.size()); EXPECT_EQ(expected_bars, g_bars.size()); for (auto *foo : g_foos) { foo->Validate(); } for (auto *bar : g_bars) { bar->Validate(); } } InstrumentedFoo::InstrumentedFoo(int i) { LLDB_RECORD_CONSTRUCTOR(InstrumentedFoo, (int), i); g_foos.push_back(this); } InstrumentedFoo::InstrumentedFoo(const InstrumentedFoo &foo) { LLDB_RECORD_CONSTRUCTOR(InstrumentedFoo, (const InstrumentedFoo &), foo); g_foos.erase(std::remove(g_foos.begin(), g_foos.end(), &foo)); g_foos.push_back(this); } InstrumentedFoo &InstrumentedFoo::operator=(const InstrumentedFoo &foo) { LLDB_RECORD_METHOD(InstrumentedFoo &, InstrumentedFoo, operator=,(const InstrumentedFoo &), foo); g_foos.erase(std::remove(g_foos.begin(), g_foos.end(), &foo)); g_foos.push_back(this); return *this; } void InstrumentedFoo::A(int a) { LLDB_RECORD_METHOD(void, InstrumentedFoo, A, (int), a); B(a); m_a = a; } void InstrumentedFoo::B(int &b) const { LLDB_RECORD_METHOD_CONST(void, InstrumentedFoo, B, (int &), b); m_called++; m_b = b; } int InstrumentedFoo::C(float *c) { LLDB_RECORD_METHOD(int, InstrumentedFoo, C, (float *), c); m_c = *c; return 1; } int InstrumentedFoo::D(const char *d) const { LLDB_RECORD_METHOD_CONST(int, InstrumentedFoo, D, (const char *), d); m_d = std::string(d); return 2; } void InstrumentedFoo::E(double e) { LLDB_RECORD_STATIC_METHOD(void, InstrumentedFoo, E, (double), e); g_e = e; } int InstrumentedFoo::F() { LLDB_RECORD_STATIC_METHOD_NO_ARGS(int, InstrumentedFoo, F); g_f = true; return 3; } void InstrumentedFoo::Validate() { LLDB_RECORD_METHOD_NO_ARGS(void, InstrumentedFoo, Validate); EXPECT_EQ(m_a, 100); EXPECT_EQ(m_b, 200); EXPECT_NEAR(m_c, 300.3, 0.01); EXPECT_EQ(m_d, "bar"); EXPECT_NEAR(g_e, 400.4, 0.01); EXPECT_EQ(g_f, true); EXPECT_EQ(2, m_called); } InstrumentedBar::InstrumentedBar() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(InstrumentedBar); g_bars.push_back(this); } InstrumentedFoo InstrumentedBar::GetInstrumentedFoo() { LLDB_RECORD_METHOD_NO_ARGS(InstrumentedFoo, InstrumentedBar, GetInstrumentedFoo); m_get_instrumend_foo_called = true; return LLDB_RECORD_RESULT(InstrumentedFoo(0)); } InstrumentedFoo &InstrumentedBar::GetInstrumentedFooRef() { LLDB_RECORD_METHOD_NO_ARGS(InstrumentedFoo &, InstrumentedBar, GetInstrumentedFooRef); InstrumentedFoo *foo = new InstrumentedFoo(0); m_get_instrumend_foo_called = true; return LLDB_RECORD_RESULT(*foo); } InstrumentedFoo *InstrumentedBar::GetInstrumentedFooPtr() { LLDB_RECORD_METHOD_NO_ARGS(InstrumentedFoo *, InstrumentedBar, GetInstrumentedFooPtr); InstrumentedFoo *foo = new InstrumentedFoo(0); m_get_instrumend_foo_called = true; return LLDB_RECORD_RESULT(foo); } void InstrumentedBar::SetInstrumentedFoo(InstrumentedFoo *foo) { LLDB_RECORD_METHOD(void, InstrumentedBar, SetInstrumentedFoo, (InstrumentedFoo *), foo); m_foo_set_by_ptr = foo; } void InstrumentedBar::SetInstrumentedFoo(InstrumentedFoo &foo) { LLDB_RECORD_METHOD(void, InstrumentedBar, SetInstrumentedFoo, (InstrumentedFoo &), foo); m_foo_set_by_ref = &foo; } void InstrumentedBar::Validate() { LLDB_RECORD_METHOD_NO_ARGS(void, InstrumentedBar, Validate); EXPECT_TRUE(m_get_instrumend_foo_called); EXPECT_NE(m_foo_set_by_ptr, nullptr); EXPECT_EQ(m_foo_set_by_ptr, m_foo_set_by_ref); } TestingRegistry::TestingRegistry() { Registry& R = *this; LLDB_REGISTER_CONSTRUCTOR(InstrumentedFoo, (int i)); LLDB_REGISTER_CONSTRUCTOR(InstrumentedFoo, (const InstrumentedFoo &)); LLDB_REGISTER_METHOD(InstrumentedFoo &, InstrumentedFoo, operator=,(const InstrumentedFoo &)); LLDB_REGISTER_METHOD(void, InstrumentedFoo, A, (int)); LLDB_REGISTER_METHOD_CONST(void, InstrumentedFoo, B, (int &)); LLDB_REGISTER_METHOD(int, InstrumentedFoo, C, (float *)); LLDB_REGISTER_METHOD_CONST(int, InstrumentedFoo, D, (const char *)); LLDB_REGISTER_STATIC_METHOD(void, InstrumentedFoo, E, (double)); LLDB_REGISTER_STATIC_METHOD(int, InstrumentedFoo, F, ()); LLDB_REGISTER_METHOD(void, InstrumentedFoo, Validate, ()); LLDB_REGISTER_CONSTRUCTOR(InstrumentedBar, ()); LLDB_REGISTER_METHOD(InstrumentedFoo, InstrumentedBar, GetInstrumentedFoo, ()); LLDB_REGISTER_METHOD(InstrumentedFoo &, InstrumentedBar, GetInstrumentedFooRef, ()); LLDB_REGISTER_METHOD(InstrumentedFoo *, InstrumentedBar, GetInstrumentedFooPtr, ()); LLDB_REGISTER_METHOD(void, InstrumentedBar, SetInstrumentedFoo, (InstrumentedFoo *)); LLDB_REGISTER_METHOD(void, InstrumentedBar, SetInstrumentedFoo, (InstrumentedFoo &)); LLDB_REGISTER_METHOD(void, InstrumentedBar, Validate, ()); } static const Pod p; TEST(IndexToObjectTest, ObjectForIndex) { IndexToObject index_to_object; Foo foo; Bar bar; EXPECT_EQ(nullptr, index_to_object.GetObjectForIndex(1)); EXPECT_EQ(nullptr, index_to_object.GetObjectForIndex(2)); index_to_object.AddObjectForIndex(1, foo); index_to_object.AddObjectForIndex(2, &bar); EXPECT_EQ(&foo, index_to_object.GetObjectForIndex(1)); EXPECT_EQ(&bar, index_to_object.GetObjectForIndex(2)); } TEST(DeserializerTest, HasData) { { Deserializer deserializer(""); EXPECT_FALSE(deserializer.HasData(1)); } { Deserializer deserializer("a"); EXPECT_TRUE(deserializer.HasData(1)); EXPECT_FALSE(deserializer.HasData(2)); } } TEST(SerializationRountripTest, SerializeDeserializePod) { std::string str; llvm::raw_string_ostream os(str); Serializer serializer(os); serializer.SerializeAll(p.a, p.b, p.c, p.d, p.e, p.f, p.g, p.h, p.i, p.j, p.k, p.l, p.m); llvm::StringRef buffer(os.str()); Deserializer deserializer(buffer); EXPECT_EQ(p.a, deserializer.Deserialize()); EXPECT_EQ(p.b, deserializer.Deserialize()); EXPECT_EQ(p.c, deserializer.Deserialize()); EXPECT_EQ(p.d, deserializer.Deserialize()); EXPECT_EQ(p.e, deserializer.Deserialize()); EXPECT_EQ(p.f, deserializer.Deserialize()); EXPECT_EQ(p.g, deserializer.Deserialize()); EXPECT_EQ(p.h, deserializer.Deserialize()); EXPECT_EQ(p.i, deserializer.Deserialize()); EXPECT_EQ(p.j, deserializer.Deserialize()); EXPECT_EQ(p.k, deserializer.Deserialize()); EXPECT_EQ(p.l, deserializer.Deserialize()); EXPECT_EQ(p.m, deserializer.Deserialize()); } TEST(SerializationRountripTest, SerializeDeserializePodPointers) { std::string str; llvm::raw_string_ostream os(str); Serializer serializer(os); serializer.SerializeAll(&p.a, &p.b, &p.c, &p.d, &p.e, &p.f, &p.g, &p.h, &p.i, &p.j, &p.k, &p.l, &p.m); llvm::StringRef buffer(os.str()); Deserializer deserializer(buffer); EXPECT_EQ(p.a, *deserializer.Deserialize()); EXPECT_EQ(p.b, *deserializer.Deserialize()); EXPECT_EQ(p.c, *deserializer.Deserialize()); EXPECT_EQ(p.d, *deserializer.Deserialize()); EXPECT_EQ(p.e, *deserializer.Deserialize()); EXPECT_EQ(p.f, *deserializer.Deserialize()); EXPECT_EQ(p.g, *deserializer.Deserialize()); EXPECT_EQ(p.h, *deserializer.Deserialize()); EXPECT_EQ(p.i, *deserializer.Deserialize()); EXPECT_EQ(p.j, *deserializer.Deserialize()); EXPECT_EQ(p.k, *deserializer.Deserialize()); EXPECT_EQ(p.l, *deserializer.Deserialize()); EXPECT_EQ(p.m, *deserializer.Deserialize()); } TEST(SerializationRountripTest, SerializeDeserializePodReferences) { std::string str; llvm::raw_string_ostream os(str); Serializer serializer(os); serializer.SerializeAll(p.a, p.b, p.c, p.d, p.e, p.f, p.g, p.h, p.i, p.j, p.k, p.l, p.m); llvm::StringRef buffer(os.str()); Deserializer deserializer(buffer); EXPECT_EQ(p.a, deserializer.Deserialize()); EXPECT_EQ(p.b, deserializer.Deserialize()); EXPECT_EQ(p.c, deserializer.Deserialize()); EXPECT_EQ(p.d, deserializer.Deserialize()); EXPECT_EQ(p.e, deserializer.Deserialize()); EXPECT_EQ(p.f, deserializer.Deserialize()); EXPECT_EQ(p.g, deserializer.Deserialize()); EXPECT_EQ(p.h, deserializer.Deserialize()); EXPECT_EQ(p.i, deserializer.Deserialize()); EXPECT_EQ(p.j, deserializer.Deserialize()); EXPECT_EQ(p.k, deserializer.Deserialize()); EXPECT_EQ(p.l, deserializer.Deserialize()); EXPECT_EQ(p.m, deserializer.Deserialize()); } TEST(SerializationRountripTest, SerializeDeserializeCString) { const char *cstr = "string"; std::string str; llvm::raw_string_ostream os(str); Serializer serializer(os); serializer.SerializeAll(cstr); llvm::StringRef buffer(os.str()); Deserializer deserializer(buffer); EXPECT_STREQ(cstr, deserializer.Deserialize()); } TEST(SerializationRountripTest, SerializeDeserializeObjectPointer) { Foo foo; Bar bar; std::string str; llvm::raw_string_ostream os(str); Serializer serializer(os); serializer.SerializeAll(static_cast(1), static_cast(2)); serializer.SerializeAll(&foo, &bar); llvm::StringRef buffer(os.str()); Deserializer deserializer(buffer); deserializer.HandleReplayResult(&foo); deserializer.HandleReplayResult(&bar); EXPECT_EQ(foo, *deserializer.Deserialize()); EXPECT_EQ(bar, *deserializer.Deserialize()); } TEST(SerializationRountripTest, SerializeDeserializeObjectReference) { Foo foo; Bar bar; std::string str; llvm::raw_string_ostream os(str); Serializer serializer(os); serializer.SerializeAll(static_cast(1), static_cast(2)); serializer.SerializeAll(foo, bar); llvm::StringRef buffer(os.str()); Deserializer deserializer(buffer); deserializer.HandleReplayResult(&foo); deserializer.HandleReplayResult(&bar); EXPECT_EQ(foo, deserializer.Deserialize()); EXPECT_EQ(bar, deserializer.Deserialize()); } TEST(RecordReplayTest, InstrumentedFoo) { std::string str; llvm::raw_string_ostream os(str); g_registry.emplace(); g_serializer.emplace(os); { int b = 200; float c = 300.3; double e = 400.4; InstrumentedFoo foo(0); foo.A(100); foo.B(b); foo.C(&c); foo.D("bar"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo.Validate(); } ClearObjects(); TestingRegistry registry; registry.Replay(os.str()); ValidateObjects(1, 0); } TEST(RecordReplayTest, InstrumentedFooSameThis) { std::string str; llvm::raw_string_ostream os(str); g_registry.emplace(); g_serializer.emplace(os); int b = 200; float c = 300.3; double e = 400.4; InstrumentedFoo *foo = new InstrumentedFoo(0); foo->A(100); foo->B(b); foo->C(&c); foo->D("bar"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo->Validate(); foo->~InstrumentedFoo(); InstrumentedFoo *foo2 = new (foo) InstrumentedFoo(0); foo2->A(100); foo2->B(b); foo2->C(&c); foo2->D("bar"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo2->Validate(); delete foo2; ClearObjects(); TestingRegistry registry; registry.Replay(os.str()); ValidateObjects(2, 0); } TEST(RecordReplayTest, InstrumentedBar) { std::string str; llvm::raw_string_ostream os(str); g_registry.emplace(); g_serializer.emplace(os); { InstrumentedBar bar; InstrumentedFoo foo = bar.GetInstrumentedFoo(); #if 0 InstrumentedFoo& foo_ref = bar.GetInstrumentedFooRef(); InstrumentedFoo* foo_ptr = bar.GetInstrumentedFooPtr(); #endif int b = 200; float c = 300.3; double e = 400.4; foo.A(100); foo.B(b); foo.C(&c); foo.D("bar"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo.Validate(); bar.SetInstrumentedFoo(foo); bar.SetInstrumentedFoo(&foo); bar.Validate(); } ClearObjects(); TestingRegistry registry; registry.Replay(os.str()); ValidateObjects(1, 1); } TEST(RecordReplayTest, InstrumentedBarRef) { std::string str; llvm::raw_string_ostream os(str); g_registry.emplace(); g_serializer.emplace(os); { InstrumentedBar bar; InstrumentedFoo &foo = bar.GetInstrumentedFooRef(); int b = 200; float c = 300.3; double e = 400.4; foo.A(100); foo.B(b); foo.C(&c); foo.D("bar"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo.Validate(); bar.SetInstrumentedFoo(foo); bar.SetInstrumentedFoo(&foo); bar.Validate(); } ClearObjects(); TestingRegistry registry; registry.Replay(os.str()); ValidateObjects(1, 1); } TEST(RecordReplayTest, InstrumentedBarPtr) { std::string str; llvm::raw_string_ostream os(str); g_registry.emplace(); g_serializer.emplace(os); { InstrumentedBar bar; InstrumentedFoo &foo = *(bar.GetInstrumentedFooPtr()); int b = 200; float c = 300.3; double e = 400.4; foo.A(100); foo.B(b); foo.C(&c); foo.D("bar"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo.Validate(); bar.SetInstrumentedFoo(foo); bar.SetInstrumentedFoo(&foo); bar.Validate(); } ClearObjects(); TestingRegistry registry; registry.Replay(os.str()); ValidateObjects(1, 1); }