Skip to content

Commit a1dbd36

Browse files
committed
[lldb] Deal gracefully with concurrency in the API instrumentation.
Prevent lldb from crashing when multiple threads are concurrently accessing the SB API with reproducer capture enabled. The API instrumentation records both the input arguments and the return value, but it cannot block for the duration of the API call. Therefore we introduce a sequence number that allows to to correlate the function with its result and add locking to ensure those two parts are emitted atomically. Using the sequence number, we can detect situations where the return value does not succeed the function call, in which case we print an error saying that concurrency is not (currently) supported. In the future we might attempt to be smarter and read ahead until we've found the return value matching the current call. Differential revision: https://reviews.llvm.org/D92820 (cherry picked from commit ac25e86)
1 parent 4adb81a commit a1dbd36

File tree

3 files changed

+120
-5
lines changed

3 files changed

+120
-5
lines changed

lldb/include/lldb/Utility/ReproducerInstrumentation.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ class Deserializer {
333333
}
334334

335335
template <typename T> const T &HandleReplayResult(const T &t) {
336+
CheckSequence(Deserialize<unsigned>());
336337
unsigned result = Deserialize<unsigned>();
337338
if (is_trivially_serializable<T>::value)
338339
return t;
@@ -342,6 +343,7 @@ class Deserializer {
342343

343344
/// Store the returned value in the index-to-object mapping.
344345
template <typename T> T &HandleReplayResult(T &t) {
346+
CheckSequence(Deserialize<unsigned>());
345347
unsigned result = Deserialize<unsigned>();
346348
if (is_trivially_serializable<T>::value)
347349
return t;
@@ -351,6 +353,7 @@ class Deserializer {
351353

352354
/// Store the returned value in the index-to-object mapping.
353355
template <typename T> T *HandleReplayResult(T *t) {
356+
CheckSequence(Deserialize<unsigned>());
354357
unsigned result = Deserialize<unsigned>();
355358
if (is_trivially_serializable<T>::value)
356359
return t;
@@ -360,6 +363,7 @@ class Deserializer {
360363
/// All returned types are recorded, even when the function returns a void.
361364
/// The latter requires special handling.
362365
void HandleReplayResultVoid() {
366+
CheckSequence(Deserialize<unsigned>());
363367
unsigned result = Deserialize<unsigned>();
364368
assert(result == 0);
365369
(void)result;
@@ -369,6 +373,10 @@ class Deserializer {
369373
return m_index_to_object.GetAllObjects();
370374
}
371375

376+
void SetExpectedSequence(unsigned sequence) {
377+
m_expected_sequence = sequence;
378+
}
379+
372380
private:
373381
template <typename T> T Read(ValueTag) {
374382
assert(HasData(sizeof(T)));
@@ -410,11 +418,17 @@ class Deserializer {
410418
return *(new UnderlyingT(Deserialize<UnderlyingT>()));
411419
}
412420

421+
/// Verify that the given sequence number matches what we expect.
422+
void CheckSequence(unsigned sequence);
423+
413424
/// Mapping of indices to objects.
414425
IndexToObject m_index_to_object;
415426

416427
/// Buffer containing the serialized data.
417428
llvm::StringRef m_buffer;
429+
430+
/// The result's expected sequence number.
431+
llvm::Optional<unsigned> m_expected_sequence;
418432
};
419433

420434
/// Partial specialization for C-style strings. We read the string value
@@ -745,19 +759,23 @@ class Recorder {
745759
if (!ShouldCapture())
746760
return;
747761

762+
std::lock_guard<std::mutex> lock(g_mutex);
763+
unsigned sequence = GetSequenceNumber();
748764
unsigned id = registry.GetID(uintptr_t(f));
749765

750766
#ifdef LLDB_REPRO_INSTR_TRACE
751767
Log(id);
752768
#endif
753769

770+
serializer.SerializeAll(sequence);
754771
serializer.SerializeAll(id);
755772
serializer.SerializeAll(args...);
756773

757774
if (std::is_class<typename std::remove_pointer<
758775
typename std::remove_reference<Result>::type>::type>::value) {
759776
m_result_recorded = false;
760777
} else {
778+
serializer.SerializeAll(sequence);
761779
serializer.SerializeAll(0);
762780
m_result_recorded = true;
763781
}
@@ -771,16 +789,20 @@ class Recorder {
771789
if (!ShouldCapture())
772790
return;
773791

792+
std::lock_guard<std::mutex> lock(g_mutex);
793+
unsigned sequence = GetSequenceNumber();
774794
unsigned id = registry.GetID(uintptr_t(f));
775795

776796
#ifdef LLDB_REPRO_INSTR_TRACE
777797
Log(id);
778798
#endif
779799

800+
serializer.SerializeAll(sequence);
780801
serializer.SerializeAll(id);
781802
serializer.SerializeAll(args...);
782803

783804
// Record result.
805+
serializer.SerializeAll(sequence);
784806
serializer.SerializeAll(0);
785807
m_result_recorded = true;
786808
}
@@ -806,7 +828,9 @@ class Recorder {
806828
if (update_boundary)
807829
UpdateBoundary();
808830
if (m_serializer && ShouldCapture()) {
831+
std::lock_guard<std::mutex> lock(g_mutex);
809832
assert(!m_result_recorded);
833+
m_serializer->SerializeAll(GetSequenceNumber());
810834
m_serializer->SerializeAll(r);
811835
m_result_recorded = true;
812836
}
@@ -816,6 +840,7 @@ class Recorder {
816840
template <typename Result, typename T>
817841
Result Replay(Deserializer &deserializer, Registry &registry, uintptr_t addr,
818842
bool update_boundary) {
843+
deserializer.SetExpectedSequence(deserializer.Deserialize<unsigned>());
819844
unsigned actual_id = registry.GetID(addr);
820845
unsigned id = deserializer.Deserialize<unsigned>();
821846
registry.CheckID(id, actual_id);
@@ -826,6 +851,7 @@ class Recorder {
826851
}
827852

828853
void Replay(Deserializer &deserializer, Registry &registry, uintptr_t addr) {
854+
deserializer.SetExpectedSequence(deserializer.Deserialize<unsigned>());
829855
unsigned actual_id = registry.GetID(addr);
830856
unsigned id = deserializer.Deserialize<unsigned>();
831857
registry.CheckID(id, actual_id);
@@ -846,6 +872,9 @@ class Recorder {
846872
static void PrivateThread() { g_global_boundary = true; }
847873

848874
private:
875+
static unsigned GetNextSequenceNumber() { return g_sequence++; }
876+
unsigned GetSequenceNumber() const;
877+
849878
template <typename T> friend struct replay;
850879
void UpdateBoundary() {
851880
if (m_local_boundary)
@@ -871,8 +900,17 @@ class Recorder {
871900
/// Whether the return value was recorded explicitly.
872901
bool m_result_recorded;
873902

903+
/// The sequence number for this pair of function and result.
904+
unsigned m_sequence;
905+
874906
/// Whether we're currently across the API boundary.
875907
static thread_local bool g_global_boundary;
908+
909+
/// Global mutex to protect concurrent access.
910+
static std::mutex g_mutex;
911+
912+
/// Unique, monotonically increasing sequence number.
913+
static std::atomic<unsigned> g_sequence;
876914
};
877915

878916
/// To be used as the "Runtime ID" of a constructor. It also invokes the
@@ -1014,6 +1052,7 @@ struct invoke_char_ptr<Result (Class::*)(Args...) const> {
10141052

10151053
static Result replay(Recorder &recorder, Deserializer &deserializer,
10161054
Registry &registry, char *str) {
1055+
deserializer.SetExpectedSequence(deserializer.Deserialize<unsigned>());
10171056
deserializer.Deserialize<unsigned>();
10181057
Class *c = deserializer.Deserialize<Class *>();
10191058
deserializer.Deserialize<const char *>();
@@ -1035,6 +1074,7 @@ struct invoke_char_ptr<Result (Class::*)(Args...)> {
10351074

10361075
static Result replay(Recorder &recorder, Deserializer &deserializer,
10371076
Registry &registry, char *str) {
1077+
deserializer.SetExpectedSequence(deserializer.Deserialize<unsigned>());
10381078
deserializer.Deserialize<unsigned>();
10391079
Class *c = deserializer.Deserialize<Class *>();
10401080
deserializer.Deserialize<const char *>();
@@ -1055,6 +1095,7 @@ struct invoke_char_ptr<Result (*)(Args...)> {
10551095

10561096
static Result replay(Recorder &recorder, Deserializer &deserializer,
10571097
Registry &registry, char *str) {
1098+
deserializer.SetExpectedSequence(deserializer.Deserialize<unsigned>());
10581099
deserializer.Deserialize<unsigned>();
10591100
deserializer.Deserialize<const char *>();
10601101
size_t l = deserializer.Deserialize<size_t>();

lldb/source/Utility/ReproducerInstrumentation.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "lldb/Utility/ReproducerInstrumentation.h"
1010
#include "lldb/Utility/Reproducer.h"
11+
#include <limits>
1112
#include <stdio.h>
1213
#include <stdlib.h>
1314
#include <thread>
@@ -84,6 +85,16 @@ template <> const char **Deserializer::Deserialize<const char **>() {
8485
return r;
8586
}
8687

88+
void Deserializer::CheckSequence(unsigned sequence) {
89+
if (m_expected_sequence && *m_expected_sequence != sequence)
90+
llvm::report_fatal_error(
91+
"The result does not match the preceding "
92+
"function. This is probably the result of concurrent "
93+
"use of the SB API during capture, which is currently not "
94+
"supported.");
95+
m_expected_sequence.reset();
96+
}
97+
8798
bool Registry::Replay(const FileSpec &file) {
8899
auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
89100
if (auto err = error_or_file.getError())
@@ -107,6 +118,7 @@ bool Registry::Replay(Deserializer &deserializer) {
107118
setvbuf(stdout, nullptr, _IONBF, 0);
108119

109120
while (deserializer.HasData(1)) {
121+
unsigned sequence = deserializer.Deserialize<unsigned>();
110122
unsigned id = deserializer.Deserialize<unsigned>();
111123

112124
#ifndef LLDB_REPRO_INSTR_TRACE
@@ -115,6 +127,7 @@ bool Registry::Replay(Deserializer &deserializer) {
115127
llvm::errs() << "Replaying " << id << ": " << GetSignature(id) << "\n";
116128
#endif
117129

130+
deserializer.SetExpectedSequence(sequence);
118131
GetReplayer(id)->operator()(deserializer);
119132
}
120133

@@ -181,21 +194,24 @@ unsigned ObjectToIndex::GetIndexForObjectImpl(const void *object) {
181194

182195
Recorder::Recorder()
183196
: m_serializer(nullptr), m_pretty_func(), m_pretty_args(),
184-
m_local_boundary(false), m_result_recorded(true) {
197+
m_local_boundary(false), m_result_recorded(true),
198+
m_sequence(std::numeric_limits<unsigned>::max()) {
185199
if (!g_global_boundary) {
186200
g_global_boundary = true;
187201
m_local_boundary = true;
202+
m_sequence = GetNextSequenceNumber();
188203
}
189204
}
190205

191206
Recorder::Recorder(llvm::StringRef pretty_func, std::string &&pretty_args)
192207
: m_serializer(nullptr), m_pretty_func(pretty_func),
193208
m_pretty_args(pretty_args), m_local_boundary(false),
194-
m_result_recorded(true) {
209+
m_result_recorded(true),
210+
m_sequence(std::numeric_limits<unsigned>::max()) {
195211
if (!g_global_boundary) {
196212
g_global_boundary = true;
197213
m_local_boundary = true;
198-
214+
m_sequence = GetNextSequenceNumber();
199215
LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), "{0} ({1})",
200216
m_pretty_func, m_pretty_args);
201217
}
@@ -206,6 +222,11 @@ Recorder::~Recorder() {
206222
UpdateBoundary();
207223
}
208224

225+
unsigned Recorder::GetSequenceNumber() const {
226+
assert(m_sequence != std::numeric_limits<unsigned>::max());
227+
return m_sequence;
228+
}
229+
209230
void InstrumentationData::Initialize(Serializer &serializer,
210231
Registry &registry) {
211232
InstanceImpl().emplace(serializer, registry);
@@ -228,3 +249,5 @@ llvm::Optional<InstrumentationData> &InstrumentationData::InstanceImpl() {
228249
}
229250

230251
thread_local bool lldb_private::repro::Recorder::g_global_boundary = false;
252+
std::atomic<unsigned> lldb_private::repro::Recorder::g_sequence;
253+
std::mutex lldb_private::repro::Recorder::g_mutex;

lldb/unittests/Utility/ReproducerInstrumentationTest.cpp

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -576,8 +576,11 @@ TEST(SerializationRountripTest, SerializeDeserializeObjectPointer) {
576576
std::string str;
577577
llvm::raw_string_ostream os(str);
578578

579+
unsigned sequence = 123;
580+
579581
Serializer serializer(os);
580-
serializer.SerializeAll(static_cast<unsigned>(1), static_cast<unsigned>(2));
582+
serializer.SerializeAll(sequence, static_cast<unsigned>(1));
583+
serializer.SerializeAll(sequence, static_cast<unsigned>(2));
581584
serializer.SerializeAll(&foo, &bar);
582585

583586
llvm::StringRef buffer(os.str());
@@ -597,8 +600,11 @@ TEST(SerializationRountripTest, SerializeDeserializeObjectReference) {
597600
std::string str;
598601
llvm::raw_string_ostream os(str);
599602

603+
unsigned sequence = 123;
604+
600605
Serializer serializer(os);
601-
serializer.SerializeAll(static_cast<unsigned>(1), static_cast<unsigned>(2));
606+
serializer.SerializeAll(sequence, static_cast<unsigned>(1));
607+
serializer.SerializeAll(sequence, static_cast<unsigned>(2));
602608
serializer.SerializeAll(foo, bar);
603609

604610
llvm::StringRef buffer(os.str());
@@ -1114,3 +1120,48 @@ TEST(PassiveReplayTest, InstrumentedBarPtr) {
11141120
bar.Validate();
11151121
}
11161122
}
1123+
1124+
TEST(RecordReplayTest, ValidSequence) {
1125+
std::string str;
1126+
llvm::raw_string_ostream os(str);
1127+
1128+
{
1129+
auto data = TestInstrumentationDataRAII::GetRecordingData(os);
1130+
1131+
unsigned sequence = 1;
1132+
int (*f)() = &lldb_private::repro::invoke<int (*)()>::method<
1133+
InstrumentedFoo::F>::record;
1134+
unsigned id = g_registry->GetID(uintptr_t(f));
1135+
g_serializer->SerializeAll(sequence, id);
1136+
1137+
unsigned result = 0;
1138+
g_serializer->SerializeAll(sequence, result);
1139+
}
1140+
1141+
TestingRegistry registry;
1142+
Deserializer deserializer(os.str());
1143+
registry.Replay(deserializer);
1144+
}
1145+
1146+
TEST(RecordReplayTest, InvalidSequence) {
1147+
std::string str;
1148+
llvm::raw_string_ostream os(str);
1149+
1150+
{
1151+
auto data = TestInstrumentationDataRAII::GetRecordingData(os);
1152+
1153+
unsigned sequence = 1;
1154+
int (*f)() = &lldb_private::repro::invoke<int (*)()>::method<
1155+
InstrumentedFoo::F>::record;
1156+
unsigned id = g_registry->GetID(uintptr_t(f));
1157+
g_serializer->SerializeAll(sequence, id);
1158+
1159+
unsigned result = 0;
1160+
unsigned invalid_sequence = 2;
1161+
g_serializer->SerializeAll(invalid_sequence, result);
1162+
}
1163+
1164+
TestingRegistry registry;
1165+
Deserializer deserializer(os.str());
1166+
EXPECT_DEATH(registry.Replay(deserializer), "");
1167+
}

0 commit comments

Comments
 (0)