JDevlieghere created this revision.
JDevlieghere added a reviewer: labath.
Herald added a subscriber: abidh.
JDevlieghere added a parent revision: D77602: [lldb/Reproducers] Support inline 
replay.

Several SB API functions return strings a `(char*, size_t)` pair. During 
capture, we serialize an empty string for the `char*` because the memory can be 
uninitialized.  During replay, we have custom replay redirects that ensure that 
we don't override the buffer from which we're reading, but rather a buffer on 
the heap with the given length. This is sufficient for the "regular" reproducer 
use case, where we only care about the side effects of the API calls, not the 
actual return values.

This is not sufficient for inline replay. For inline replay, we ignore all the 
incoming arguments, and re-execute the current function with the arguments 
serialized in the reproducer. This means that these function will update the 
deserialized copy of the arguments, rather than whatever was passed in by the 
SWIG wrapper. To solve this problem, I've extended the reproducer 
instrumentation with special case replayers and corresponding macros. They 
ignore the replayer in the registry and the incoming char pointer, and instead 
reinvoke the current method on the deserialized class, and populate the output 
argument. It's unfortunate that this needs to be special cased, but I don't see 
a better solution.


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D77759

Files:
  lldb/include/lldb/Utility/ReproducerInstrumentation.h
  lldb/source/API/SBDebugger.cpp
  lldb/source/API/SBFileSpec.cpp
  lldb/source/API/SBProcess.cpp
  lldb/source/API/SBStructuredData.cpp
  lldb/source/API/SBThread.cpp

Index: lldb/source/API/SBThread.cpp
===================================================================
--- lldb/source/API/SBThread.cpp
+++ lldb/source/API/SBThread.cpp
@@ -312,8 +312,8 @@
 }
 
 size_t SBThread::GetStopDescription(char *dst, size_t dst_len) {
-  LLDB_RECORD_METHOD(size_t, SBThread, GetStopDescription, (char *, size_t), "",
-                     dst_len);
+  LLDB_RECORD_CHAR_PTR_METHOD(size_t, SBThread, GetStopDescription,
+                              (char *, size_t), dst, "", dst_len);
 
   std::unique_lock<std::recursive_mutex> lock;
   ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
Index: lldb/source/API/SBStructuredData.cpp
===================================================================
--- lldb/source/API/SBStructuredData.cpp
+++ lldb/source/API/SBStructuredData.cpp
@@ -196,8 +196,8 @@
 }
 
 size_t SBStructuredData::GetStringValue(char *dst, size_t dst_len) const {
-  LLDB_RECORD_METHOD_CONST(size_t, SBStructuredData, GetStringValue,
-                           (char *, size_t), "", dst_len);
+  LLDB_RECORD_CHAR_PTR_METHOD_CONST(size_t, SBStructuredData, GetStringValue,
+                                    (char *, size_t), dst, "", dst_len);
 
   return (m_impl_up ? m_impl_up->GetStringValue(dst, dst_len) : 0);
 }
Index: lldb/source/API/SBProcess.cpp
===================================================================
--- lldb/source/API/SBProcess.cpp
+++ lldb/source/API/SBProcess.cpp
@@ -271,8 +271,8 @@
 }
 
 size_t SBProcess::GetSTDOUT(char *dst, size_t dst_len) const {
-  LLDB_RECORD_METHOD_CONST(size_t, SBProcess, GetSTDOUT, (char *, size_t), "",
-                           dst_len);
+  LLDB_RECORD_CHAR_PTR_METHOD_CONST(size_t, SBProcess, GetSTDOUT,
+                                    (char *, size_t), dst, "", dst_len);
 
   size_t bytes_read = 0;
   ProcessSP process_sp(GetSP());
@@ -285,8 +285,8 @@
 }
 
 size_t SBProcess::GetSTDERR(char *dst, size_t dst_len) const {
-  LLDB_RECORD_METHOD_CONST(size_t, SBProcess, GetSTDERR, (char *, size_t), "",
-                           dst_len);
+  LLDB_RECORD_CHAR_PTR_METHOD_CONST(size_t, SBProcess, GetSTDERR,
+                                    (char *, size_t), dst, "", dst_len);
 
   size_t bytes_read = 0;
   ProcessSP process_sp(GetSP());
@@ -299,8 +299,8 @@
 }
 
 size_t SBProcess::GetAsyncProfileData(char *dst, size_t dst_len) const {
-  LLDB_RECORD_METHOD_CONST(size_t, SBProcess, GetAsyncProfileData,
-                           (char *, size_t), "", dst_len);
+  LLDB_RECORD_CHAR_PTR_METHOD_CONST(size_t, SBProcess, GetAsyncProfileData,
+                                    (char *, size_t), dst, "", dst_len);
 
   size_t bytes_read = 0;
   ProcessSP process_sp(GetSP());
Index: lldb/source/API/SBFileSpec.cpp
===================================================================
--- lldb/source/API/SBFileSpec.cpp
+++ lldb/source/API/SBFileSpec.cpp
@@ -143,8 +143,8 @@
 }
 
 uint32_t SBFileSpec::GetPath(char *dst_path, size_t dst_len) const {
-  LLDB_RECORD_METHOD_CONST(uint32_t, SBFileSpec, GetPath, (char *, size_t), "",
-                           dst_len);
+  LLDB_RECORD_CHAR_PTR_METHOD_CONST(uint32_t, SBFileSpec, GetPath,
+                                    (char *, size_t), dst_path, "", dst_len);
 
   uint32_t result = m_opaque_up->GetPath(dst_path, dst_len);
 
Index: lldb/source/API/SBDebugger.cpp
===================================================================
--- lldb/source/API/SBDebugger.cpp
+++ lldb/source/API/SBDebugger.cpp
@@ -596,8 +596,9 @@
 }
 
 bool SBDebugger::GetDefaultArchitecture(char *arch_name, size_t arch_name_len) {
-  LLDB_RECORD_STATIC_METHOD(bool, SBDebugger, GetDefaultArchitecture,
-                            (char *, size_t), "", arch_name_len);
+  LLDB_RECORD_CHAR_PTR_STATIC_METHOD(bool, SBDebugger, GetDefaultArchitecture,
+                                     (char *, size_t), arch_name, "",
+                                     arch_name_len);
 
   if (arch_name && arch_name_len) {
     ArchSpec default_arch = Target::GetDefaultArchitecture();
Index: lldb/include/lldb/Utility/ReproducerInstrumentation.h
===================================================================
--- lldb/include/lldb/Utility/ReproducerInstrumentation.h
+++ lldb/include/lldb/Utility/ReproducerInstrumentation.h
@@ -286,6 +286,79 @@
     }                                                                          \
   }
 
+#define LLDB_RECORD_CHAR_PTR_METHOD(Result, Class, Method, Signature, StrOut,  \
+                                    ...)                                       \
+  lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION,                \
+                                          stringify_args(*this, __VA_ARGS__)); \
+  if (lldb_private::repro::InstrumentationData _data =                         \
+          LLDB_GET_INSTRUMENTATION_DATA()) {                                   \
+    if (lldb_private::repro::Serializer *_serializer =                         \
+            _data.GetSerializer()) {                                           \
+      _recorder.Record(                                                        \
+          *_serializer, _data.GetRegistry(),                                   \
+          &lldb_private::repro::invoke<Result(Class::*) Signature>::method<(   \
+              &Class::Method)>::doit,                                          \
+          this, __VA_ARGS__);                                                  \
+    } else if (lldb_private::repro::Deserializer *_deserializer =              \
+                   _data.GetDeserializer()) {                                  \
+      if (_recorder.ShouldCapture()) {                                         \
+        return lldb_private::repro::replay_char_ptr<                           \
+            Result(Class::*)                                                   \
+                Signature>::method<&Class::Method>::doit(_recorder,            \
+                                                         *_deserializer,       \
+                                                         _data.GetRegistry(),  \
+                                                         LLVM_PRETTY_FUNCTION, \
+                                                         StrOut);              \
+      }                                                                        \
+    }                                                                          \
+  }
+
+#define LLDB_RECORD_CHAR_PTR_METHOD_CONST(Result, Class, Method, Signature,    \
+                                          StrOut, ...)                         \
+  lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION,                \
+                                          stringify_args(*this, __VA_ARGS__)); \
+  if (lldb_private::repro::InstrumentationData _data =                         \
+          LLDB_GET_INSTRUMENTATION_DATA()) {                                   \
+    if (lldb_private::repro::Serializer *_serializer =                         \
+            _data.GetSerializer()) {                                           \
+      _recorder.Record(                                                        \
+          *_serializer, _data.GetRegistry(),                                   \
+          &lldb_private::repro::invoke<Result(Class::*) Signature const>::     \
+              method_const<(&Class::Method)>::doit,                            \
+          this, __VA_ARGS__);                                                  \
+    } else if (lldb_private::repro::Deserializer *_deserializer =              \
+                   _data.GetDeserializer()) {                                  \
+      if (_recorder.ShouldCapture()) {                                         \
+        return lldb_private::repro::replay_char_ptr<Result(                    \
+            Class::*) Signature const>::method_const<&Class::Method>::         \
+            doit(_recorder, *_deserializer, _data.GetRegistry(),               \
+                 LLVM_PRETTY_FUNCTION, StrOut);                                \
+      }                                                                        \
+    }                                                                          \
+  }
+
+#define LLDB_RECORD_CHAR_PTR_STATIC_METHOD(Result, Class, Method, Signature,   \
+                                           StrOut, ...)                        \
+  lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION,                \
+                                          stringify_args(__VA_ARGS__));        \
+  if (lldb_private::repro::InstrumentationData _data =                         \
+          LLDB_GET_INSTRUMENTATION_DATA()) {                                   \
+    if (lldb_private::repro::Serializer *_serializer =                         \
+            _data.GetSerializer()) {                                           \
+      _recorder.Record(*_serializer, _data.GetRegistry(),                      \
+                       static_cast<Result(*) Signature>(&Class::Method),       \
+                       __VA_ARGS__);                                           \
+    } else if (lldb_private::repro::Deserializer *_deserializer =              \
+                   _data.GetDeserializer()) {                                  \
+      if (_recorder.ShouldCapture()) {                                         \
+        return lldb_private::repro::replay_char_ptr<Result(*) Signature>::     \
+            method_static<(&Class::Method)>::doit(                             \
+                _recorder, *_deserializer, _data.GetRegistry(),                \
+                LLVM_PRETTY_FUNCTION, StrOut);                                 \
+      }                                                                        \
+    }                                                                          \
+  }
+
 #define LLDB_RECORD_RESULT(Result) _recorder.RecordResult(Result, true);
 
 /// The LLDB_RECORD_DUMMY macro is special because it doesn't actually record
@@ -1022,6 +1095,74 @@
   };
 };
 
+/// Special handling for functions returning strings as (char*, size_t).
+/// {
+
+/// For inline replay, we ignore the arguments and use the ones from the
+/// serializer instead. This doesn't work for methods that use a char* and a
+/// size to return a string. For one these functions have a custom replayer to
+/// prevent override the input buffer. Furthermore, the template-generated
+/// deserialization is not easy to hook into.
+///
+/// The specializations below hand-implement the serialization logic for the
+/// inline replay. Instead of using the function from the registry, it uses the
+/// one passed into the macro.
+template <typename Signature> struct replay_char_ptr;
+template <typename Result, typename Class, typename... Args>
+struct replay_char_ptr<Result (Class::*)(Args...) const> {
+  template <Result (Class::*m)(Args...) const> struct method_const {
+    static Result doit(Recorder &recorder, Deserializer &deserializer,
+                       Registry &registry, llvm::StringRef signature,
+                       char *str) {
+      unsigned id = deserializer.Deserialize<unsigned>();
+      registry.CheckSignature(signature, id);
+
+      Class *c = deserializer.Deserialize<Class *>();
+      deserializer.Deserialize<const char *>();
+      size_t l = deserializer.Deserialize<size_t>();
+      return recorder.ReplayResult<Result>(
+          std::move(deserializer.HandleReplayResult<Result>((c->*m)(str, l))),
+          true);
+    }
+  };
+};
+template <typename Signature> struct replay_char_ptr;
+template <typename Result, typename Class, typename... Args>
+struct replay_char_ptr<Result (Class::*)(Args...)> {
+  template <Result (Class::*m)(Args...)> struct method {
+    static Result doit(Recorder &recorder, Deserializer &deserializer,
+                       Registry &registry, llvm::StringRef signature,
+                       char *str) {
+      unsigned id = deserializer.Deserialize<unsigned>();
+      registry.CheckSignature(signature, id);
+
+      Class *c = deserializer.Deserialize<Class *>();
+      deserializer.Deserialize<const char *>();
+      size_t l = deserializer.Deserialize<size_t>();
+      return recorder.ReplayResult<Result>(
+          std::move(deserializer.HandleReplayResult<Result>((c->*m)(str, l))),
+          true);
+    }
+  };
+};
+template <typename Result, typename... Args>
+struct replay_char_ptr<Result (*)(Args...)> {
+  template <Result (*m)(Args...)> struct method_static {
+    static Result doit(Recorder &recorder, Deserializer &deserializer,
+                       Registry &registry, llvm::StringRef signature,
+                       char *str) {
+      unsigned id = deserializer.Deserialize<unsigned>();
+      registry.CheckSignature(signature, id);
+
+      deserializer.Deserialize<const char *>();
+      size_t l = deserializer.Deserialize<size_t>();
+      return recorder.ReplayResult<Result>(
+          std::move(deserializer.HandleReplayResult<Result>((*m)(str, l))),
+          true);
+    }
+  };
+};
+
 template <typename Signature> struct char_ptr_redirect;
 template <typename Result, typename Class>
 struct char_ptr_redirect<Result (Class::*)(char *, size_t) const> {
@@ -1048,6 +1189,7 @@
   char *buffer = reinterpret_cast<char *>(calloc(l, sizeof(char)));
   return f(buffer, l);
 }
+/// }
 
 } // namespace repro
 } // namespace lldb_private
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to