Index: lldb/include/lldb/Utility/ReproducerInstrumentation.h =================================================================== --- lldb/include/lldb/Utility/ReproducerInstrumentation.h +++ lldb/include/lldb/Utility/ReproducerInstrumentation.h @@ -333,6 +333,7 @@ } template const T &HandleReplayResult(const T &t) { + CheckIndex(Deserialize()); unsigned result = Deserialize(); if (is_trivially_serializable::value) return t; @@ -342,6 +343,7 @@ /// Store the returned value in the index-to-object mapping. template T &HandleReplayResult(T &t) { + CheckIndex(Deserialize()); unsigned result = Deserialize(); if (is_trivially_serializable::value) return t; @@ -351,6 +353,7 @@ /// Store the returned value in the index-to-object mapping. template T *HandleReplayResult(T *t) { + CheckIndex(Deserialize()); unsigned result = Deserialize(); if (is_trivially_serializable::value) return t; @@ -360,6 +363,7 @@ /// All returned types are recorded, even when the function returns a void. /// The latter requires special handling. void HandleReplayResultVoid() { + CheckIndex(Deserialize()); unsigned result = Deserialize(); assert(result == 0); (void)result; @@ -369,6 +373,8 @@ return m_index_to_object.GetAllObjects(); } + void SetExpectedIndex(unsigned idx) { m_expected_idx = idx; } + private: template T Read(ValueTag) { assert(HasData(sizeof(T))); @@ -410,11 +416,17 @@ return *(new UnderlyingT(Deserialize())); } + /// Verify that the given index matches the expected index if set. + void CheckIndex(unsigned idx); + /// Mapping of indices to objects. IndexToObject m_index_to_object; /// Buffer containing the serialized data. llvm::StringRef m_buffer; + + /// Expected index of the result. + llvm::Optional m_expected_idx; }; /// Partial specialization for C-style strings. We read the string value @@ -741,6 +753,7 @@ template void Record(Serializer &serializer, Registry ®istry, Result (*f)(FArgs...), const RArgs &... args) { + std::lock_guard lock(g_mutex); m_serializer = &serializer; if (!ShouldCapture()) return; @@ -751,6 +764,7 @@ Log(id); #endif + serializer.SerializeAll(m_index); serializer.SerializeAll(id); serializer.SerializeAll(args...); @@ -758,6 +772,7 @@ typename std::remove_reference::type>::type>::value) { m_result_recorded = false; } else { + serializer.SerializeAll(m_index); serializer.SerializeAll(0); m_result_recorded = true; } @@ -771,16 +786,19 @@ if (!ShouldCapture()) return; + std::lock_guard lock(g_mutex); unsigned id = registry.GetID(uintptr_t(f)); #ifdef LLDB_REPRO_INSTR_TRACE Log(id); #endif + serializer.SerializeAll(m_index); serializer.SerializeAll(id); serializer.SerializeAll(args...); // Record result. + serializer.SerializeAll(m_index); serializer.SerializeAll(0); m_result_recorded = true; } @@ -806,7 +824,9 @@ if (update_boundary) UpdateBoundary(); if (m_serializer && ShouldCapture()) { + std::lock_guard lock(g_mutex); assert(!m_result_recorded); + m_serializer->SerializeAll(m_index); m_serializer->SerializeAll(r); m_result_recorded = true; } @@ -816,6 +836,7 @@ template Result Replay(Deserializer &deserializer, Registry ®istry, uintptr_t addr, bool update_boundary) { + deserializer.SetExpectedIndex(deserializer.Deserialize()); unsigned actual_id = registry.GetID(addr); unsigned id = deserializer.Deserialize(); registry.CheckID(id, actual_id); @@ -826,6 +847,7 @@ } void Replay(Deserializer &deserializer, Registry ®istry, uintptr_t addr) { + deserializer.SetExpectedIndex(deserializer.Deserialize()); unsigned actual_id = registry.GetID(addr); unsigned id = deserializer.Deserialize(); registry.CheckID(id, actual_id); @@ -846,6 +868,8 @@ static void PrivateThread() { g_global_boundary = true; } private: + static uint64_t GetNextIndex() { return g_index++; } + template friend struct replay; void UpdateBoundary() { if (m_local_boundary) @@ -871,8 +895,17 @@ /// Whether the return value was recorded explicitly. bool m_result_recorded; + /// The current index. + unsigned m_index; + /// Whether we're currently across the API boundary. static thread_local bool g_global_boundary; + + /// Global mutex to protect concurrent access. + static std::mutex g_mutex; + + /// Global index. + static std::atomic g_index; }; /// To be used as the "Runtime ID" of a constructor. It also invokes the @@ -1014,6 +1047,7 @@ static Result replay(Recorder &recorder, Deserializer &deserializer, Registry ®istry, char *str) { + deserializer.SetExpectedIndex(deserializer.Deserialize()); deserializer.Deserialize(); Class *c = deserializer.Deserialize(); deserializer.Deserialize(); @@ -1035,6 +1069,7 @@ static Result replay(Recorder &recorder, Deserializer &deserializer, Registry ®istry, char *str) { + deserializer.SetExpectedIndex(deserializer.Deserialize()); deserializer.Deserialize(); Class *c = deserializer.Deserialize(); deserializer.Deserialize(); @@ -1055,6 +1090,7 @@ static Result replay(Recorder &recorder, Deserializer &deserializer, Registry ®istry, char *str) { + deserializer.SetExpectedIndex(deserializer.Deserialize()); deserializer.Deserialize(); deserializer.Deserialize(); size_t l = deserializer.Deserialize(); Index: lldb/source/Utility/ReproducerInstrumentation.cpp =================================================================== --- lldb/source/Utility/ReproducerInstrumentation.cpp +++ lldb/source/Utility/ReproducerInstrumentation.cpp @@ -8,6 +8,7 @@ #include "lldb/Utility/ReproducerInstrumentation.h" #include "lldb/Utility/Reproducer.h" +#include #include #include #include @@ -84,6 +85,16 @@ return r; } +void Deserializer::CheckIndex(unsigned idx) { + if (m_expected_idx && *m_expected_idx != idx) + llvm::report_fatal_error( + "The result does not match the preceding " + "function. This is probably the result of concurrent " + "use of the SB API during capture, which is currently not " + "supported."); + m_expected_idx.reset(); +} + bool Registry::Replay(const FileSpec &file) { auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath()); if (auto err = error_or_file.getError()) @@ -107,6 +118,7 @@ setvbuf(stdout, nullptr, _IONBF, 0); while (deserializer.HasData(1)) { + unsigned idx = deserializer.Deserialize(); unsigned id = deserializer.Deserialize(); #ifndef LLDB_REPRO_INSTR_TRACE @@ -115,6 +127,7 @@ llvm::errs() << "Replaying " << id << ": " << GetSignature(id) << "\n"; #endif + deserializer.SetExpectedIndex(idx); GetReplayer(id)->operator()(deserializer); } @@ -181,21 +194,23 @@ Recorder::Recorder() : m_serializer(nullptr), m_pretty_func(), m_pretty_args(), - m_local_boundary(false), m_result_recorded(true) { + m_local_boundary(false), m_result_recorded(true), + m_index(std::numeric_limits::max()) { if (!g_global_boundary) { g_global_boundary = true; m_local_boundary = true; + m_index = GetNextIndex(); } } Recorder::Recorder(llvm::StringRef pretty_func, std::string &&pretty_args) : m_serializer(nullptr), m_pretty_func(pretty_func), m_pretty_args(pretty_args), m_local_boundary(false), - m_result_recorded(true) { + m_result_recorded(true), m_index(std::numeric_limits::max()) { if (!g_global_boundary) { g_global_boundary = true; m_local_boundary = true; - + m_index = GetNextIndex(); LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), "{0} ({1})", m_pretty_func, m_pretty_args); } @@ -228,3 +243,5 @@ } thread_local bool lldb_private::repro::Recorder::g_global_boundary = false; +std::atomic lldb_private::repro::Recorder::g_index; +std::mutex lldb_private::repro::Recorder::g_mutex; Index: lldb/unittests/Utility/ReproducerInstrumentationTest.cpp =================================================================== --- lldb/unittests/Utility/ReproducerInstrumentationTest.cpp +++ lldb/unittests/Utility/ReproducerInstrumentationTest.cpp @@ -576,8 +576,11 @@ std::string str; llvm::raw_string_ostream os(str); + unsigned index = 0; + Serializer serializer(os); - serializer.SerializeAll(static_cast(1), static_cast(2)); + serializer.SerializeAll(index, static_cast(1)); + serializer.SerializeAll(index, static_cast(2)); serializer.SerializeAll(&foo, &bar); llvm::StringRef buffer(os.str()); @@ -597,8 +600,11 @@ std::string str; llvm::raw_string_ostream os(str); + unsigned index = 0; + Serializer serializer(os); - serializer.SerializeAll(static_cast(1), static_cast(2)); + serializer.SerializeAll(index, static_cast(1)); + serializer.SerializeAll(index, static_cast(2)); serializer.SerializeAll(foo, bar); llvm::StringRef buffer(os.str()); @@ -1114,3 +1120,48 @@ bar.Validate(); } } + +TEST(RecordReplayTest, ValidIndex) { + std::string str; + llvm::raw_string_ostream os(str); + + { + auto data = TestInstrumentationDataRAII::GetRecordingData(os); + + unsigned index = 1; + int (*f)() = &lldb_private::repro::invoke::method< + InstrumentedFoo::F>::record; + unsigned id = g_registry->GetID(uintptr_t(f)); + g_serializer->SerializeAll(index, id); + + unsigned result = 0; + g_serializer->SerializeAll(index, result); + } + + TestingRegistry registry; + Deserializer deserializer(os.str()); + registry.Replay(deserializer); +} + +TEST(RecordReplayTest, InvalidIndex) { + std::string str; + llvm::raw_string_ostream os(str); + + { + auto data = TestInstrumentationDataRAII::GetRecordingData(os); + + unsigned index = 1; + int (*f)() = &lldb_private::repro::invoke::method< + InstrumentedFoo::F>::record; + unsigned id = g_registry->GetID(uintptr_t(f)); + g_serializer->SerializeAll(index, id); + + unsigned result = 0; + unsigned invalid_index = 2; + g_serializer->SerializeAll(invalid_index, result); + } + + TestingRegistry registry; + Deserializer deserializer(os.str()); + EXPECT_DEATH(registry.Replay(deserializer), ""); +}