[Lldb-commits] [lldb] r353195 - [Reproducers] Instrumentation Framework: Serialization

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Tue Feb 5 10:46:37 PST 2019


Author: jdevlieghere
Date: Tue Feb  5 10:46:36 2019
New Revision: 353195

URL: http://llvm.org/viewvc/llvm-project?rev=353195&view=rev
Log:
[Reproducers] Instrumentation Framework: Serialization

This is the is serialization/deserialization part of the reproducer
instrumentation framework.

For all the details refer to the RFC on the mailing list:
http://lists.llvm.org/pipermail/lldb-dev/2019-January/014530.html

Differential revision: https://reviews.llvm.org/D57714

Added:
    lldb/trunk/include/lldb/Utility/ReproducerInstrumentation.h
    lldb/trunk/source/Utility/ReproducerInstrumentation.cpp
    lldb/trunk/unittests/Utility/ReproducerInstrumentationTest.cpp
Modified:
    lldb/trunk/source/Utility/CMakeLists.txt
    lldb/trunk/unittests/Utility/CMakeLists.txt

Added: lldb/trunk/include/lldb/Utility/ReproducerInstrumentation.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Utility/ReproducerInstrumentation.h?rev=353195&view=auto
==============================================================================
--- lldb/trunk/include/lldb/Utility/ReproducerInstrumentation.h (added)
+++ lldb/trunk/include/lldb/Utility/ReproducerInstrumentation.h Tue Feb  5 10:46:36 2019
@@ -0,0 +1,302 @@
+//===-- ReproducerInstrumentation.h -----------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UTILITY_REPRODUCER_INSTRUMENTATION_H
+#define LLDB_UTILITY_REPRODUCER_INSTRUMENTATION_H
+
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <map>
+#include <mutex>
+
+namespace lldb_private {
+namespace repro {
+
+/// Mapping between serialized indices and their corresponding objects.
+///
+/// This class is used during replay to map indices back to in-memory objects.
+///
+/// When objects are constructed, they are added to this mapping using
+/// AddObjectForIndex.
+///
+/// When an object is passed to a function, its index is deserialized and
+/// AddObjectForIndex returns the corresponding object. If there is no object
+/// for the given index, a nullptr is returend. The latter is valid when custom
+/// replay code is in place and the actual object is ignored.
+class IndexToObject {
+public:
+  /// Returns an object as a pointer for the given index or nullptr if not
+  /// present in the map.
+  template <typename T> T *GetObjectForIndex(unsigned idx) {
+    assert(idx != 0 && "Cannot get object for sentinel");
+    void *object = GetObjectForIndexImpl(idx);
+    return static_cast<T *>(object);
+  }
+
+  /// Adds a pointer to an object to the mapping for the given index.
+  template <typename T> void AddObjectForIndex(unsigned idx, T *object) {
+    AddObjectForIndexImpl(
+        idx, static_cast<void *>(
+                 const_cast<typename std::remove_const<T>::type *>(object)));
+  }
+
+  /// Adds a reference to an object to the mapping for the given index.
+  template <typename T> void AddObjectForIndex(unsigned idx, T &object) {
+    AddObjectForIndexImpl(
+        idx, static_cast<void *>(
+                 const_cast<typename std::remove_const<T>::type *>(&object)));
+  }
+
+private:
+  /// Helper method that does the actual lookup. The void* result is later cast
+  /// by the caller.
+  void *GetObjectForIndexImpl(unsigned idx);
+
+  /// Helper method that does the actual insertion.
+  void AddObjectForIndexImpl(unsigned idx, void *object);
+
+  /// Keeps a mapping between indices and their corresponding object.
+  llvm::DenseMap<unsigned, void *> m_mapping;
+};
+
+/// We need to differentiate between pointers to fundamental and
+/// non-fundamental types. See the corresponding Deserializer::Read method
+/// for the reason why.
+struct PointerTag {};
+struct ReferenceTag {};
+struct ValueTag {};
+struct FundamentalPointerTag {};
+struct FundamentalReferenceTag {};
+
+/// Return the deserialization tag for the given type T.
+template <class T> struct serializer_tag { typedef ValueTag type; };
+template <class T> struct serializer_tag<T *> {
+  typedef
+      typename std::conditional<std::is_fundamental<T>::value,
+                                FundamentalPointerTag, PointerTag>::type type;
+};
+template <class T> struct serializer_tag<T &> {
+  typedef typename std::conditional<std::is_fundamental<T>::value,
+                                    FundamentalReferenceTag, ReferenceTag>::type
+      type;
+};
+
+/// Deserializes data from a buffer. It is used to deserialize function indices
+/// to replay, their arguments and return values.
+///
+/// Fundamental types and strings are read by value. Objects are read by their
+/// index, which get translated by the IndexToObject mapping maintained in
+/// this class.
+///
+/// Additional bookkeeping with regards to the IndexToObject is required to
+/// deserialize objects. When a constructor is run or an object is returned by
+/// value, we need to capture the object and add it to the index together with
+/// its index. This is the job of HandleReplayResult(Void).
+class Deserializer {
+public:
+  Deserializer(llvm::StringRef buffer) : m_buffer(buffer) {}
+
+  /// Returns true when the buffer has unread data.
+  bool HasData(unsigned size) { return size <= m_buffer.size(); }
+
+  /// Deserialize and interpret value as T.
+  template <typename T> T Deserialize() {
+    return Read<T>(typename serializer_tag<T>::type());
+  }
+
+  /// Store the returned value in the index-to-object mapping.
+  template <typename T> void HandleReplayResult(const T &t) {
+    unsigned result = Deserialize<unsigned>();
+    if (std::is_fundamental<T>::value)
+      return;
+    // We need to make a copy as the original object might go out of scope.
+    m_index_to_object.AddObjectForIndex(result, new T(t));
+  }
+
+  /// Store the returned value in the index-to-object mapping.
+  template <typename T> void HandleReplayResult(T *t) {
+    unsigned result = Deserialize<unsigned>();
+    if (std::is_fundamental<T>::value)
+      return;
+    m_index_to_object.AddObjectForIndex(result, t);
+  }
+
+  /// All returned types are recorded, even when the function returns a void.
+  /// The latter requires special handling.
+  void HandleReplayResultVoid() {
+    unsigned result = Deserialize<unsigned>();
+    assert(result == 0);
+  }
+
+protected:
+  IndexToObject &GetIndexToObject() { return m_index_to_object; }
+
+private:
+  template <typename T> T Read(ValueTag) {
+    assert(HasData(sizeof(T)));
+    T t;
+    std::memcpy(reinterpret_cast<char *>(&t), m_buffer.data(), sizeof(T));
+    m_buffer = m_buffer.drop_front(sizeof(T));
+    return t;
+  }
+
+  template <typename T> T Read(PointerTag) {
+    typedef typename std::remove_pointer<T>::type UnderlyingT;
+    return m_index_to_object.template GetObjectForIndex<UnderlyingT>(
+        Deserialize<unsigned>());
+  }
+
+  template <typename T> T Read(ReferenceTag) {
+    typedef typename std::remove_reference<T>::type UnderlyingT;
+    // If this is a reference to a fundamental type we just read its value.
+    return *m_index_to_object.template GetObjectForIndex<UnderlyingT>(
+        Deserialize<unsigned>());
+  }
+
+  /// This method is used to parse references to fundamental types. Because
+  /// they're not recorded in the object table we have serialized their value.
+  /// We read its value, allocate a copy on the heap, and return a pointer to
+  /// the copy.
+  template <typename T> T Read(FundamentalPointerTag) {
+    typedef typename std::remove_pointer<T>::type UnderlyingT;
+    return new UnderlyingT(Deserialize<UnderlyingT>());
+  }
+
+  /// This method is used to parse references to fundamental types. Because
+  /// they're not recorded in the object table we have serialized their value.
+  /// We read its value, allocate a copy on the heap, and return a reference to
+  /// the copy.
+  template <typename T> T Read(FundamentalReferenceTag) {
+    // If this is a reference to a fundamental type we just read its value.
+    typedef typename std::remove_reference<T>::type UnderlyingT;
+    return *(new UnderlyingT(Deserialize<UnderlyingT>()));
+  }
+
+  /// Mapping of indices to objects.
+  IndexToObject m_index_to_object;
+
+  /// Buffer containing the serialized data.
+  llvm::StringRef m_buffer;
+};
+
+/// Partial specialization for C-style strings. We read the string value
+/// instead of treating it as pointer.
+template <> const char *Deserializer::Deserialize<const char *>();
+template <> char *Deserializer::Deserialize<char *>();
+
+/// Helpers to auto-synthesize function replay code. It deserializes the replay
+/// function's arguments one by one and finally calls the corresponding
+/// function.
+template <typename... Remaining> struct DeserializationHelper;
+
+template <typename Head, typename... Tail>
+struct DeserializationHelper<Head, Tail...> {
+  template <typename Result, typename... Deserialized> struct deserialized {
+    static Result doit(Deserializer &deserializer,
+                       Result (*f)(Deserialized..., Head, Tail...),
+                       Deserialized... d) {
+      return DeserializationHelper<Tail...>::
+          template deserialized<Result, Deserialized..., Head>::doit(
+              deserializer, f, d..., deserializer.Deserialize<Head>());
+    }
+  };
+};
+
+template <> struct DeserializationHelper<> {
+  template <typename Result, typename... Deserialized> struct deserialized {
+    static Result doit(Deserializer &deserializer, Result (*f)(Deserialized...),
+                       Deserialized... d) {
+      return f(d...);
+    }
+  };
+};
+
+/// Maps an object to an index for serialization. Indices are unique and
+/// incremented for every new object.
+///
+/// Indices start at 1 in order to differentiate with an invalid index (0) in
+/// the serialized buffer.
+class ObjectToIndex {
+public:
+  template <typename T> unsigned GetIndexForObject(T *t) {
+    return GetIndexForObjectImpl((void *)t);
+  }
+
+private:
+  unsigned GetIndexForObjectImpl(void *object);
+
+  std::mutex m_mutex;
+  llvm::DenseMap<void *, unsigned> m_mapping;
+};
+
+/// Serializes functions, their arguments and their return type to a stream.
+class Serializer {
+public:
+  Serializer(llvm::raw_ostream &stream = llvm::outs()) : m_stream(stream) {}
+
+  /// Recursively serialize all the given arguments.
+  template <typename Head, typename... Tail>
+  void SerializeAll(const Head &head, const Tail &... tail) {
+    Serialize(head);
+    SerializeAll(tail...);
+  }
+
+  void SerializeAll() {}
+
+private:
+  /// Serialize pointers. We need to differentiate between pointers to
+  /// fundamental types (in which case we serialize its value) and pointer to
+  /// objects (in which case we serialize their index).
+  template <typename T> void Serialize(T *t) {
+    if (std::is_fundamental<T>::value) {
+      Serialize(*t);
+    } else {
+      unsigned idx = m_tracker.GetIndexForObject(t);
+      Serialize(idx);
+    }
+  }
+
+  /// Serialize references. We need to differentiate between references to
+  /// fundamental types (in which case we serialize its value) and references
+  /// to objects (in which case we serialize their index).
+  template <typename T> void Serialize(T &t) {
+    if (std::is_fundamental<T>::value) {
+      m_stream.write(reinterpret_cast<const char *>(&t), sizeof(T));
+    } else {
+      unsigned idx = m_tracker.GetIndexForObject(&t);
+      Serialize(idx);
+    }
+  }
+
+  void Serialize(void *v) {
+    // FIXME: Support void*
+    llvm_unreachable("void* is currently unsupported.");
+  }
+
+  void Serialize(const char *t) {
+    m_stream << t;
+    m_stream.write(0x0);
+  }
+
+  /// Serialization stream.
+  llvm::raw_ostream &m_stream;
+
+  /// Mapping of objects to indices.
+  ObjectToIndex m_tracker;
+};
+
+} // namespace repro
+} // namespace lldb_private
+
+#endif // LLDB_UTILITY_REPRODUCER_INSTRUMENTATION_H

Modified: lldb/trunk/source/Utility/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Utility/CMakeLists.txt?rev=353195&r1=353194&r2=353195&view=diff
==============================================================================
--- lldb/trunk/source/Utility/CMakeLists.txt (original)
+++ lldb/trunk/source/Utility/CMakeLists.txt Tue Feb  5 10:46:36 2019
@@ -67,6 +67,7 @@ add_lldb_library(lldbUtility
   RegisterValue.cpp
   RegularExpression.cpp
   Reproducer.cpp
+  ReproducerInstrumentation.cpp
   Scalar.cpp
   SelectHelper.cpp
   SharingPtr.cpp

Added: lldb/trunk/source/Utility/ReproducerInstrumentation.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Utility/ReproducerInstrumentation.cpp?rev=353195&view=auto
==============================================================================
--- lldb/trunk/source/Utility/ReproducerInstrumentation.cpp (added)
+++ lldb/trunk/source/Utility/ReproducerInstrumentation.cpp Tue Feb  5 10:46:36 2019
@@ -0,0 +1,44 @@
+//===-- ReproducerInstrumentation.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 "lldb/Utility/ReproducerInstrumentation.h"
+#include "lldb/Utility/Reproducer.h"
+
+using namespace lldb_private;
+using namespace lldb_private::repro;
+
+void *IndexToObject::GetObjectForIndexImpl(unsigned idx) {
+  return m_mapping.lookup(idx);
+}
+
+void IndexToObject::AddObjectForIndexImpl(unsigned idx, void *object) {
+  assert(idx != 0 && "Cannot add object for sentinel");
+  m_mapping[idx] = object;
+}
+
+template <> char *Deserializer::Deserialize<char *>() {
+  return const_cast<char *>(Deserialize<const char *>());
+}
+
+template <> const char *Deserializer::Deserialize<const char *>() {
+  auto pos = m_buffer.find('\0');
+  if (pos == llvm::StringRef::npos)
+    return nullptr;
+  const char *str = m_buffer.data();
+  m_buffer = m_buffer.drop_front(pos + 1);
+  return str;
+}
+
+unsigned ObjectToIndex::GetIndexForObjectImpl(void *object) {
+  std::lock_guard<std::mutex> guard(m_mutex);
+  unsigned index = m_mapping.size() + 1;
+  auto it = m_mapping.find(object);
+  if (it == m_mapping.end())
+    m_mapping[object] = index;
+  return m_mapping[object];
+}

Modified: lldb/trunk/unittests/Utility/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Utility/CMakeLists.txt?rev=353195&r1=353194&r2=353195&view=diff
==============================================================================
--- lldb/trunk/unittests/Utility/CMakeLists.txt (original)
+++ lldb/trunk/unittests/Utility/CMakeLists.txt Tue Feb  5 10:46:36 2019
@@ -20,6 +20,7 @@ add_lldb_unittest(UtilityTests
   PredicateTest.cpp
   RegisterValueTest.cpp
   ReproducerTest.cpp
+  ReproducerInstrumentationTest.cpp
   ScalarTest.cpp
   StateTest.cpp
   StatusTest.cpp

Added: lldb/trunk/unittests/Utility/ReproducerInstrumentationTest.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Utility/ReproducerInstrumentationTest.cpp?rev=353195&view=auto
==============================================================================
--- lldb/trunk/unittests/Utility/ReproducerInstrumentationTest.cpp (added)
+++ lldb/trunk/unittests/Utility/ReproducerInstrumentationTest.cpp Tue Feb  5 10:46:36 2019
@@ -0,0 +1,208 @@
+//===-- 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 "lldb/Utility/ReproducerInstrumentation.h"
+
+using namespace lldb_private;
+using namespace lldb_private::repro;
+
+namespace {
+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;
+};
+} // namespace
+
+static const Pod p;
+
+TEST(IndexToObjectTest, ObjectForIndex) {
+  IndexToObject index_to_object;
+  Foo foo;
+  Bar bar;
+
+  EXPECT_EQ(nullptr, index_to_object.GetObjectForIndex<Foo>(1));
+  EXPECT_EQ(nullptr, index_to_object.GetObjectForIndex<Bar>(2));
+
+  index_to_object.AddObjectForIndex<Foo>(1, foo);
+  index_to_object.AddObjectForIndex<Bar>(2, &bar);
+
+  EXPECT_EQ(&foo, index_to_object.GetObjectForIndex<Foo>(1));
+  EXPECT_EQ(&bar, index_to_object.GetObjectForIndex<Bar>(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<bool>());
+  EXPECT_EQ(p.b, deserializer.Deserialize<bool>());
+  EXPECT_EQ(p.c, deserializer.Deserialize<char>());
+  EXPECT_EQ(p.d, deserializer.Deserialize<float>());
+  EXPECT_EQ(p.e, deserializer.Deserialize<int>());
+  EXPECT_EQ(p.f, deserializer.Deserialize<long long>());
+  EXPECT_EQ(p.g, deserializer.Deserialize<long>());
+  EXPECT_EQ(p.h, deserializer.Deserialize<short>());
+  EXPECT_EQ(p.i, deserializer.Deserialize<unsigned char>());
+  EXPECT_EQ(p.j, deserializer.Deserialize<unsigned int>());
+  EXPECT_EQ(p.k, deserializer.Deserialize<unsigned long long>());
+  EXPECT_EQ(p.l, deserializer.Deserialize<unsigned long>());
+  EXPECT_EQ(p.m, deserializer.Deserialize<unsigned short>());
+}
+
+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<bool *>());
+  EXPECT_EQ(p.b, *deserializer.Deserialize<bool *>());
+  EXPECT_EQ(p.c, *deserializer.Deserialize<char *>());
+  EXPECT_EQ(p.d, *deserializer.Deserialize<float *>());
+  EXPECT_EQ(p.e, *deserializer.Deserialize<int *>());
+  EXPECT_EQ(p.f, *deserializer.Deserialize<long long *>());
+  EXPECT_EQ(p.g, *deserializer.Deserialize<long *>());
+  EXPECT_EQ(p.h, *deserializer.Deserialize<short *>());
+  EXPECT_EQ(p.i, *deserializer.Deserialize<unsigned char *>());
+  EXPECT_EQ(p.j, *deserializer.Deserialize<unsigned int *>());
+  EXPECT_EQ(p.k, *deserializer.Deserialize<unsigned long long *>());
+  EXPECT_EQ(p.l, *deserializer.Deserialize<unsigned long *>());
+  EXPECT_EQ(p.m, *deserializer.Deserialize<unsigned short *>());
+}
+
+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<bool &>());
+  EXPECT_EQ(p.b, deserializer.Deserialize<bool &>());
+  EXPECT_EQ(p.c, deserializer.Deserialize<char &>());
+  EXPECT_EQ(p.d, deserializer.Deserialize<float &>());
+  EXPECT_EQ(p.e, deserializer.Deserialize<int &>());
+  EXPECT_EQ(p.f, deserializer.Deserialize<long long &>());
+  EXPECT_EQ(p.g, deserializer.Deserialize<long &>());
+  EXPECT_EQ(p.h, deserializer.Deserialize<short &>());
+  EXPECT_EQ(p.i, deserializer.Deserialize<unsigned char &>());
+  EXPECT_EQ(p.j, deserializer.Deserialize<unsigned int &>());
+  EXPECT_EQ(p.k, deserializer.Deserialize<unsigned long long &>());
+  EXPECT_EQ(p.l, deserializer.Deserialize<unsigned long &>());
+  EXPECT_EQ(p.m, deserializer.Deserialize<unsigned short &>());
+}
+
+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<const char *>());
+}
+
+TEST(SerializationRountripTest, SerializeDeserializeObjectPointer) {
+  Foo foo;
+  Bar bar;
+
+  std::string str;
+  llvm::raw_string_ostream os(str);
+
+  Serializer serializer(os);
+  serializer.SerializeAll(static_cast<unsigned>(1), static_cast<unsigned>(2));
+  serializer.SerializeAll(&foo, &bar);
+
+  llvm::StringRef buffer(os.str());
+  Deserializer deserializer(buffer);
+
+  deserializer.HandleReplayResult(&foo);
+  deserializer.HandleReplayResult(&bar);
+
+  EXPECT_EQ(foo, *deserializer.Deserialize<Foo *>());
+  EXPECT_EQ(bar, *deserializer.Deserialize<Bar *>());
+}
+
+TEST(SerializationRountripTest, SerializeDeserializeObjectReference) {
+  Foo foo;
+  Bar bar;
+
+  std::string str;
+  llvm::raw_string_ostream os(str);
+
+  Serializer serializer(os);
+  serializer.SerializeAll(static_cast<unsigned>(1), static_cast<unsigned>(2));
+  serializer.SerializeAll(foo, bar);
+
+  llvm::StringRef buffer(os.str());
+  Deserializer deserializer(buffer);
+
+  deserializer.HandleReplayResult(&foo);
+  deserializer.HandleReplayResult(&bar);
+
+  EXPECT_EQ(foo, deserializer.Deserialize<Foo &>());
+  EXPECT_EQ(bar, deserializer.Deserialize<Bar &>());
+}




More information about the lldb-commits mailing list