//===-- ReproducerInstrumentationTest.cpp ---------------------------------===// // // 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.1f; 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 std::unique_ptr g_registry; static llvm::Optional g_serializer; static llvm::Optional g_deserializer; class TestInstrumentationData : public InstrumentationData { public: TestInstrumentationData() : InstrumentationData() {} TestInstrumentationData(Serializer &serializer, Registry ®istry) : InstrumentationData(serializer, registry) {} TestInstrumentationData(Deserializer &deserializer, Registry ®istry) : InstrumentationData(deserializer, registry) {} }; inline TestInstrumentationData GetTestInstrumentationData() { assert(!(g_serializer && g_deserializer)); if (g_serializer) return TestInstrumentationData(*g_serializer, *g_registry); if (g_deserializer) return TestInstrumentationData(*g_deserializer, *g_registry); return TestInstrumentationData(); } class TestInstrumentationDataRAII { public: TestInstrumentationDataRAII(llvm::raw_string_ostream &os) { g_registry = std::make_unique(); g_serializer.emplace(os); g_deserializer.reset(); } TestInstrumentationDataRAII(llvm::StringRef buffer) { g_registry = std::make_unique(); g_serializer.reset(); g_deserializer.emplace(buffer); } ~TestInstrumentationDataRAII() { Reset(); } void Reset() { g_registry.reset(); g_serializer.reset(); g_deserializer.reset(); } static std::unique_ptr GetRecordingData(llvm::raw_string_ostream &os) { return std::make_unique(os); } static std::unique_ptr GetReplayData(llvm::StringRef buffer) { return std::make_unique(buffer); } }; #define LLDB_GET_INSTRUMENTATION_DATA() GetTestInstrumentationData() enum class Class { Foo, Bar, }; class Instrumented { public: virtual ~Instrumented() = default; virtual void Validate() = 0; virtual bool IsA(Class c) = 0; }; class InstrumentedFoo : public Instrumented { public: InstrumentedFoo() = default; /// Instrumented methods. /// { InstrumentedFoo(int i); InstrumentedFoo(const InstrumentedFoo &foo); InstrumentedFoo &operator=(const InstrumentedFoo &foo); void A(int a); int GetA(); void B(int &b) const; int &GetB(); int C(float *c); float GetC(); int D(const char *d) const; size_t GetD(char *buffer, size_t length); static void E(double e); double GetE(); static int F(); bool GetF(); void Validate() override; //// } virtual bool IsA(Class c) override { return c == Class::Foo; } 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 { public: /// Instrumented methods. /// { InstrumentedBar(); InstrumentedFoo GetInstrumentedFoo(); InstrumentedFoo &GetInstrumentedFooRef(); InstrumentedFoo *GetInstrumentedFooPtr(); void SetInstrumentedFoo(InstrumentedFoo *foo); void SetInstrumentedFoo(InstrumentedFoo &foo); void Validate() override; /// } virtual bool IsA(Class c) override { return c == Class::Bar; } 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; struct Validator { enum Validation { valid, invalid }; Validator(Class clazz, Validation validation) : clazz(clazz), validation(validation) {} Class clazz; Validation validation; }; void ValidateObjects(std::vector objects, std::vector validators) { ASSERT_EQ(validators.size(), objects.size()); for (size_t i = 0; i < validators.size(); ++i) { Validator &validator = validators[i]; Instrumented *instrumented = static_cast(objects[i]); EXPECT_TRUE(instrumented->IsA(validator.clazz)); switch (validator.validation) { case Validator::valid: instrumented->Validate(); break; case Validator::invalid: break; } } } InstrumentedFoo::InstrumentedFoo(int i) { LLDB_RECORD_CONSTRUCTOR(InstrumentedFoo, (int), i); } InstrumentedFoo::InstrumentedFoo(const InstrumentedFoo &foo) { LLDB_RECORD_CONSTRUCTOR(InstrumentedFoo, (const InstrumentedFoo &), foo); } InstrumentedFoo &InstrumentedFoo::operator=(const InstrumentedFoo &foo) { LLDB_RECORD_METHOD(InstrumentedFoo &, InstrumentedFoo, operator=,(const InstrumentedFoo &), foo); return *this; } void InstrumentedFoo::A(int a) { LLDB_RECORD_METHOD(void, InstrumentedFoo, A, (int), a); B(a); m_a = a; } int InstrumentedFoo::GetA() { LLDB_RECORD_METHOD_NO_ARGS(int, InstrumentedFoo, GetA); return m_a; } void InstrumentedFoo::B(int &b) const { LLDB_RECORD_METHOD_CONST(void, InstrumentedFoo, B, (int &), b); m_called++; m_b = b; } int &InstrumentedFoo::GetB() { LLDB_RECORD_METHOD_NO_ARGS(int &, InstrumentedFoo, GetB); return m_b; } int InstrumentedFoo::C(float *c) { LLDB_RECORD_METHOD(int, InstrumentedFoo, C, (float *), c); m_c = *c; return 1; } float InstrumentedFoo::GetC() { LLDB_RECORD_METHOD_NO_ARGS(float, InstrumentedFoo, GetC); return m_c; } int InstrumentedFoo::D(const char *d) const { LLDB_RECORD_METHOD_CONST(int, InstrumentedFoo, D, (const char *), d); m_d = std::string(d); return 2; } size_t InstrumentedFoo::GetD(char *buffer, size_t length) { LLDB_RECORD_CHAR_PTR_METHOD(size_t, InstrumentedFoo, GetD, (char *, size_t), buffer, "", length); ::snprintf(buffer, length, "%s", m_d.c_str()); return m_d.size(); } void InstrumentedFoo::E(double e) { LLDB_RECORD_STATIC_METHOD(void, InstrumentedFoo, E, (double), e); g_e = e; } double InstrumentedFoo::GetE() { LLDB_RECORD_METHOD_NO_ARGS(double, InstrumentedFoo, GetE); return g_e; } int InstrumentedFoo::F() { LLDB_RECORD_STATIC_METHOD_NO_ARGS(int, InstrumentedFoo, F); g_f = true; return 3; } bool InstrumentedFoo::GetF() { LLDB_RECORD_METHOD_NO_ARGS(bool, InstrumentedFoo, GetF); return g_f; } 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); } 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, ()); LLDB_REGISTER_METHOD(int, InstrumentedFoo, GetA, ()); LLDB_REGISTER_METHOD(int &, InstrumentedFoo, GetB, ()); LLDB_REGISTER_METHOD(float, InstrumentedFoo, GetC, ()); LLDB_REGISTER_METHOD(size_t, InstrumentedFoo, GetD, (char *, size_t)); LLDB_REGISTER_METHOD(double, InstrumentedFoo, GetE, ()); LLDB_REGISTER_METHOD(bool, InstrumentedFoo, GetF, ()); } 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, SerializeDeserializeCStringNull) { const char *cstr = nullptr; std::string str; llvm::raw_string_ostream os(str); Serializer serializer(os); serializer.SerializeAll(cstr); llvm::StringRef buffer(os.str()); Deserializer deserializer(buffer); EXPECT_EQ(nullptr, deserializer.Deserialize()); } TEST(SerializationRountripTest, SerializeDeserializeCStringArray) { const char *foo = "foo"; const char *bar = "bar"; const char *baz = "baz"; const char *arr[4] = {foo, bar, baz, nullptr}; std::string str; llvm::raw_string_ostream os(str); Serializer serializer(os); serializer.SerializeAll(static_cast(arr)); llvm::StringRef buffer(os.str()); Deserializer deserializer(buffer); const char **deserialized = deserializer.Deserialize(); EXPECT_STREQ("foo", deserialized[0]); EXPECT_STREQ("bar", deserialized[1]); EXPECT_STREQ("baz", deserialized[2]); } TEST(SerializationRountripTest, SerializeDeserializeCStringArrayNullptrElem) { const char *arr[1] = {nullptr}; std::string str; llvm::raw_string_ostream os(str); Serializer serializer(os); serializer.SerializeAll(static_cast(arr)); llvm::StringRef buffer(os.str()); Deserializer deserializer(buffer); const char **deserialized = deserializer.Deserialize(); EXPECT_EQ(nullptr, deserialized); } TEST(SerializationRountripTest, SerializeDeserializeCStringArrayNullptr) { std::string str; llvm::raw_string_ostream os(str); Serializer serializer(os); serializer.SerializeAll(static_cast(nullptr)); llvm::StringRef buffer(os.str()); Deserializer deserializer(buffer); const char **deserialized = deserializer.Deserialize(); EXPECT_EQ(nullptr, deserialized); } TEST(SerializationRountripTest, SerializeDeserializeObjectPointer) { Foo foo; Bar bar; std::string str; llvm::raw_string_ostream os(str); unsigned sequence = 123; Serializer serializer(os); serializer.SerializeAll(sequence, static_cast(1)); serializer.SerializeAll(sequence, 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); unsigned sequence = 123; Serializer serializer(os); serializer.SerializeAll(sequence, static_cast(1)); serializer.SerializeAll(sequence, 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); { auto data = TestInstrumentationDataRAII::GetRecordingData(os); int b = 200; float c = 300.3f; 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(); } TestingRegistry registry; Deserializer deserializer(os.str()); registry.Replay(deserializer); ValidateObjects(deserializer.GetAllObjects(), {{Class::Foo, Validator::valid}}); } TEST(RecordReplayTest, InstrumentedFooSameThis) { std::string str; llvm::raw_string_ostream os(str); { auto data = TestInstrumentationDataRAII::GetRecordingData(os); int b = 200; float c = 300.3f; 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; } TestingRegistry registry; Deserializer deserializer(os.str()); registry.Replay(deserializer); ValidateObjects(deserializer.GetAllObjects(), {{Class::Foo, Validator::valid}}); } TEST(RecordReplayTest, InstrumentedBar) { std::string str; llvm::raw_string_ostream os(str); { auto data = TestInstrumentationDataRAII::GetRecordingData(os); InstrumentedBar bar; InstrumentedFoo foo = bar.GetInstrumentedFoo(); int b = 200; float c = 300.3f; 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(); } TestingRegistry registry; Deserializer deserializer(os.str()); registry.Replay(deserializer); ValidateObjects( deserializer.GetAllObjects(), { {Class::Bar, Validator::valid}, // bar {Class::Foo, Validator::invalid}, // bar.GetInstrumentedFoo() {Class::Foo, Validator::valid}, // foo }); } TEST(RecordReplayTest, InstrumentedBarRef) { std::string str; llvm::raw_string_ostream os(str); { auto data = TestInstrumentationDataRAII::GetRecordingData(os); InstrumentedBar bar; InstrumentedFoo &foo = bar.GetInstrumentedFooRef(); int b = 200; float c = 300.3f; 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(); } TestingRegistry registry; Deserializer deserializer(os.str()); registry.Replay(deserializer); ValidateObjects( deserializer.GetAllObjects(), {{Class::Bar, Validator::valid}, {Class::Foo, Validator::valid}}); } TEST(RecordReplayTest, InstrumentedBarPtr) { std::string str; llvm::raw_string_ostream os(str); { auto data = TestInstrumentationDataRAII::GetRecordingData(os); InstrumentedBar bar; InstrumentedFoo &foo = *(bar.GetInstrumentedFooPtr()); int b = 200; float c = 300.3f; 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(); } TestingRegistry registry; Deserializer deserializer(os.str()); registry.Replay(deserializer); ValidateObjects( deserializer.GetAllObjects(), {{Class::Bar, Validator::valid}, {Class::Foo, Validator::valid}}); } TEST(PassiveReplayTest, InstrumentedFoo) { std::string str; llvm::raw_string_ostream os(str); { auto data = TestInstrumentationDataRAII::GetRecordingData(os); int b = 200; float c = 300.3f; 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(); EXPECT_EQ(foo.GetA(), 100); EXPECT_EQ(foo.GetB(), 200); EXPECT_NEAR(foo.GetC(), 300.3, 0.01); char buffer[100]; foo.GetD(buffer, 100); EXPECT_STREQ(buffer, "bar"); EXPECT_NEAR(foo.GetE(), 400.4, 0.01); EXPECT_EQ(foo.GetF(), true); } std::string buffer = os.str(); { auto data = TestInstrumentationDataRAII::GetReplayData(buffer); int b = 999; float c = 999.9f; double e = 999.9; InstrumentedFoo foo(9); foo.A(999); foo.B(b); foo.C(&c); foo.D("999"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo.Validate(); EXPECT_EQ(foo.GetA(), 100); EXPECT_EQ(foo.GetB(), 200); EXPECT_NEAR(foo.GetC(), 300.3, 0.01); char buffer[100]; foo.GetD(buffer, 100); EXPECT_STREQ(buffer, "bar"); EXPECT_NEAR(foo.GetE(), 400.4, 0.01); EXPECT_EQ(foo.GetF(), true); } } TEST(PassiveReplayTest, InstrumentedFooInvalid) { std::string str; llvm::raw_string_ostream os(str); { auto data = TestInstrumentationDataRAII::GetRecordingData(os); int b = 200; float c = 300.3f; 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(); EXPECT_EQ(foo.GetA(), 100); EXPECT_EQ(foo.GetB(), 200); EXPECT_NEAR(foo.GetC(), 300.3, 0.01); EXPECT_NEAR(foo.GetE(), 400.4, 0.01); EXPECT_EQ(foo.GetF(), true); } std::string buffer = os.str(); { auto data = TestInstrumentationDataRAII::GetReplayData(buffer); int b = 999; float c = 999.9f; double e = 999.9; InstrumentedFoo foo(9); foo.A(999); foo.B(b); foo.C(&c); foo.D("999"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo.Validate(); EXPECT_EQ(foo.GetA(), 100); // Detect divergence. EXPECT_DEATH(foo.GetA(), ""); } } TEST(PassiveReplayTest, InstrumentedBar) { std::string str; llvm::raw_string_ostream os(str); { auto data = TestInstrumentationDataRAII::GetRecordingData(os); InstrumentedBar bar; InstrumentedFoo foo = bar.GetInstrumentedFoo(); int b = 200; float c = 300.3f; double e = 400.4; foo.A(100); foo.B(b); foo.C(&c); foo.D("bar"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo.Validate(); EXPECT_EQ(foo.GetA(), 100); EXPECT_EQ(foo.GetB(), 200); EXPECT_NEAR(foo.GetC(), 300.3, 0.01); char buffer[100]; foo.GetD(buffer, 100); EXPECT_STREQ(buffer, "bar"); EXPECT_NEAR(foo.GetE(), 400.4, 0.01); EXPECT_EQ(foo.GetF(), true); bar.SetInstrumentedFoo(foo); bar.SetInstrumentedFoo(&foo); bar.Validate(); } std::string buffer = os.str(); { auto data = TestInstrumentationDataRAII::GetReplayData(buffer); InstrumentedBar bar; InstrumentedFoo foo = bar.GetInstrumentedFoo(); int b = 99; float c = 999.9f; double e = 999.9; foo.A(999); foo.B(b); foo.C(&c); foo.D("999"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo.Validate(); EXPECT_EQ(foo.GetA(), 100); EXPECT_EQ(foo.GetB(), 200); EXPECT_NEAR(foo.GetC(), 300.3, 0.01); char buffer[100]; foo.GetD(buffer, 100); EXPECT_STREQ(buffer, "bar"); EXPECT_NEAR(foo.GetE(), 400.4, 0.01); EXPECT_EQ(foo.GetF(), true); bar.SetInstrumentedFoo(foo); bar.SetInstrumentedFoo(&foo); bar.Validate(); } } TEST(PassiveReplayTest, InstrumentedBarRef) { std::string str; llvm::raw_string_ostream os(str); { auto data = TestInstrumentationDataRAII::GetRecordingData(os); InstrumentedBar bar; InstrumentedFoo &foo = bar.GetInstrumentedFooRef(); int b = 200; float c = 300.3f; double e = 400.4; foo.A(100); foo.B(b); foo.C(&c); foo.D("bar"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo.Validate(); EXPECT_EQ(foo.GetA(), 100); EXPECT_EQ(foo.GetB(), 200); EXPECT_NEAR(foo.GetC(), 300.3, 0.01); char buffer[100]; foo.GetD(buffer, 100); EXPECT_STREQ(buffer, "bar"); EXPECT_NEAR(foo.GetE(), 400.4, 0.01); EXPECT_EQ(foo.GetF(), true); bar.SetInstrumentedFoo(foo); bar.SetInstrumentedFoo(&foo); bar.Validate(); } std::string buffer = os.str(); { auto data = TestInstrumentationDataRAII::GetReplayData(buffer); InstrumentedBar bar; InstrumentedFoo &foo = bar.GetInstrumentedFooRef(); int b = 99; float c = 999.9f; double e = 999.9; foo.A(999); foo.B(b); foo.C(&c); foo.D("999"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo.Validate(); EXPECT_EQ(foo.GetA(), 100); EXPECT_EQ(foo.GetB(), 200); EXPECT_NEAR(foo.GetC(), 300.3, 0.01); char buffer[100]; foo.GetD(buffer, 100); EXPECT_STREQ(buffer, "bar"); EXPECT_NEAR(foo.GetE(), 400.4, 0.01); EXPECT_EQ(foo.GetF(), true); bar.SetInstrumentedFoo(foo); bar.SetInstrumentedFoo(&foo); bar.Validate(); } } TEST(PassiveReplayTest, InstrumentedBarPtr) { std::string str; llvm::raw_string_ostream os(str); { auto data = TestInstrumentationDataRAII::GetRecordingData(os); InstrumentedBar bar; InstrumentedFoo &foo = *(bar.GetInstrumentedFooPtr()); int b = 200; float c = 300.3f; double e = 400.4; foo.A(100); foo.B(b); foo.C(&c); foo.D("bar"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo.Validate(); EXPECT_EQ(foo.GetA(), 100); EXPECT_EQ(foo.GetB(), 200); EXPECT_NEAR(foo.GetC(), 300.3, 0.01); char buffer[100]; foo.GetD(buffer, 100); EXPECT_STREQ(buffer, "bar"); EXPECT_NEAR(foo.GetE(), 400.4, 0.01); EXPECT_EQ(foo.GetF(), true); bar.SetInstrumentedFoo(foo); bar.SetInstrumentedFoo(&foo); bar.Validate(); } std::string buffer = os.str(); { auto data = TestInstrumentationDataRAII::GetReplayData(buffer); InstrumentedBar bar; InstrumentedFoo &foo = *(bar.GetInstrumentedFooPtr()); int b = 99; float c = 999.9f; double e = 999.9; foo.A(999); foo.B(b); foo.C(&c); foo.D("999"); InstrumentedFoo::E(e); InstrumentedFoo::F(); foo.Validate(); EXPECT_EQ(foo.GetA(), 100); EXPECT_EQ(foo.GetB(), 200); EXPECT_NEAR(foo.GetC(), 300.3, 0.01); char buffer[100]; foo.GetD(buffer, 100); EXPECT_STREQ(buffer, "bar"); EXPECT_NEAR(foo.GetE(), 400.4, 0.01); EXPECT_EQ(foo.GetF(), true); bar.SetInstrumentedFoo(foo); bar.SetInstrumentedFoo(&foo); bar.Validate(); } } TEST(RecordReplayTest, ValidSequence) { std::string str; llvm::raw_string_ostream os(str); { auto data = TestInstrumentationDataRAII::GetRecordingData(os); unsigned sequence = 1; int (*f)() = &lldb_private::repro::invoke::method< InstrumentedFoo::F>::record; unsigned id = g_registry->GetID(uintptr_t(f)); g_serializer->SerializeAll(sequence, id); unsigned result = 0; g_serializer->SerializeAll(sequence, result); } TestingRegistry registry; Deserializer deserializer(os.str()); registry.Replay(deserializer); } TEST(RecordReplayTest, InvalidSequence) { std::string str; llvm::raw_string_ostream os(str); { auto data = TestInstrumentationDataRAII::GetRecordingData(os); unsigned sequence = 1; int (*f)() = &lldb_private::repro::invoke::method< InstrumentedFoo::F>::record; unsigned id = g_registry->GetID(uintptr_t(f)); g_serializer->SerializeAll(sequence, id); unsigned result = 0; unsigned invalid_sequence = 2; g_serializer->SerializeAll(invalid_sequence, result); } TestingRegistry registry; Deserializer deserializer(os.str()); EXPECT_DEATH(registry.Replay(deserializer), ""); }