Skip to content

Commit f78fcd6

Browse files
committed
[lldb/Test] Rewrite ReproducerInstrumentationTest
The instrumentation unit tests' current implementation uses global variables to track constructor calls for the instrumented classes during replay. This is suboptimal because it indirectly relies on how the reproducer instrumentation is implemented. I found out when adding support for passive replay and the test broke because we made an extra (temporary) copy of the instrumented objects. Additionally, the old approach wasn't very self-explanatory. It took me a bit of time to understand why we were expecting the number of objects in the test. This patch rewrites the test and uses the index-to-object-mapping to verify the objects created during replay. You can now specify the expected objects, in order, and whether they should be valid or not. I find that it makes the tests much easier to understand. More importantly, this approach is resilient to implementation detail changes in the instrumentation.
1 parent 16206ee commit f78fcd6

File tree

3 files changed

+103
-41
lines changed

3 files changed

+103
-41
lines changed

lldb/include/lldb/Utility/ReproducerInstrumentation.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,9 @@ class IndexToObject {
255255
const_cast<typename std::remove_const<T>::type *>(&object)));
256256
}
257257

258+
/// Get all objects sorted by their index.
259+
std::vector<void *> GetAllObjects() const;
260+
258261
private:
259262
/// Helper method that does the actual lookup. The void* result is later cast
260263
/// by the caller.
@@ -345,6 +348,10 @@ class Deserializer {
345348
(void)result;
346349
}
347350

351+
std::vector<void *> GetAllObjects() const {
352+
return m_index_to_object.GetAllObjects();
353+
}
354+
348355
private:
349356
template <typename T> T Read(ValueTag) {
350357
assert(HasData(sizeof(T)));
@@ -512,6 +519,9 @@ class Registry {
512519
/// Replay functions from a buffer.
513520
bool Replay(llvm::StringRef buffer);
514521

522+
/// Replay functions from a deserializer.
523+
bool Replay(Deserializer &deserializer);
524+
515525
/// Returns the ID for a given function address.
516526
unsigned GetID(uintptr_t addr);
517527

lldb/source/Utility/ReproducerInstrumentation.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,25 @@ void IndexToObject::AddObjectForIndexImpl(unsigned idx, void *object) {
2424
m_mapping[idx] = object;
2525
}
2626

27+
std::vector<void *> IndexToObject::GetAllObjects() const {
28+
std::vector<std::pair<unsigned, void *>> pairs;
29+
for (auto &e : m_mapping) {
30+
pairs.emplace_back(e.first, e.second);
31+
}
32+
33+
// Sort based on index.
34+
std::sort(pairs.begin(), pairs.end(),
35+
[](auto &lhs, auto &rhs) { return lhs.first < rhs.first; });
36+
37+
std::vector<void *> objects;
38+
objects.reserve(pairs.size());
39+
for (auto &p : pairs) {
40+
objects.push_back(p.second);
41+
}
42+
43+
return objects;
44+
}
45+
2746
template <> const uint8_t *Deserializer::Deserialize<const uint8_t *>() {
2847
return Deserialize<uint8_t *>();
2948
}
@@ -74,6 +93,11 @@ bool Registry::Replay(const FileSpec &file) {
7493
}
7594

7695
bool Registry::Replay(llvm::StringRef buffer) {
96+
Deserializer deserializer(buffer);
97+
return Replay(deserializer);
98+
}
99+
100+
bool Registry::Replay(Deserializer &deserializer) {
77101
#ifndef LLDB_REPRO_INSTR_TRACE
78102
Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API);
79103
#endif
@@ -82,7 +106,6 @@ bool Registry::Replay(llvm::StringRef buffer) {
82106
// during an interactive session.
83107
setvbuf(stdout, nullptr, _IONBF, 0);
84108

85-
Deserializer deserializer(buffer);
86109
while (deserializer.HasData(1)) {
87110
unsigned id = deserializer.Deserialize<unsigned>();
88111

lldb/unittests/Utility/ReproducerInstrumentationTest.cpp

Lines changed: 69 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,22 @@ static llvm::Optional<Serializer> g_serializer;
5454
static llvm::Optional<TestingRegistry> g_registry;
5555

5656
#define LLDB_GET_INSTRUMENTATION_DATA() \
57-
g_serializer ? InstrumentationData(*g_serializer, *g_registry) : InstrumentationData()
57+
g_serializer ? InstrumentationData(*g_serializer, *g_registry) \
58+
: InstrumentationData()
5859

59-
class InstrumentedFoo {
60+
enum class Class {
61+
Foo,
62+
Bar,
63+
};
64+
65+
class Instrumented {
66+
public:
67+
virtual ~Instrumented() = default;
68+
virtual void Validate() = 0;
69+
virtual bool IsA(Class c) = 0;
70+
};
71+
72+
class InstrumentedFoo : public Instrumented {
6073
public:
6174
InstrumentedFoo() = default;
6275
/// Instrumented methods.
@@ -70,8 +83,9 @@ class InstrumentedFoo {
7083
int D(const char *d) const;
7184
static void E(double e);
7285
static int F();
73-
void Validate();
86+
void Validate() override;
7487
//// }
88+
virtual bool IsA(Class c) override { return c == Class::Foo; }
7589

7690
private:
7791
int m_a = 0;
@@ -83,7 +97,7 @@ class InstrumentedFoo {
8397
mutable int m_called = 0;
8498
};
8599

86-
class InstrumentedBar {
100+
class InstrumentedBar : public Instrumented {
87101
public:
88102
/// Instrumented methods.
89103
/// {
@@ -93,8 +107,9 @@ class InstrumentedBar {
93107
InstrumentedFoo *GetInstrumentedFooPtr();
94108
void SetInstrumentedFoo(InstrumentedFoo *foo);
95109
void SetInstrumentedFoo(InstrumentedFoo &foo);
96-
void Validate();
110+
void Validate() override;
97111
/// }
112+
virtual bool IsA(Class c) override { return c == Class::Bar; }
98113

99114
private:
100115
bool m_get_instrumend_foo_called = false;
@@ -105,45 +120,47 @@ class InstrumentedBar {
105120
double InstrumentedFoo::g_e = 0;
106121
bool InstrumentedFoo::g_f = false;
107122

108-
static std::vector<InstrumentedFoo *> g_foos;
109-
static std::vector<InstrumentedBar *> g_bars;
110-
111123
void ClearObjects() {
112124
g_registry.reset();
113125
g_serializer.reset();
114-
g_foos.clear();
115-
g_bars.clear();
116126
}
117127

118-
void ValidateObjects(size_t expected_foos, size_t expected_bars) {
119-
EXPECT_EQ(expected_foos, g_foos.size());
120-
EXPECT_EQ(expected_bars, g_bars.size());
121-
122-
for (auto *foo : g_foos) {
123-
foo->Validate();
124-
}
128+
struct Validator {
129+
enum Validation { valid, invalid };
130+
Validator(Class clazz, Validation validation)
131+
: clazz(clazz), validation(validation) {}
132+
Class clazz;
133+
Validation validation;
134+
};
125135

126-
for (auto *bar : g_bars) {
127-
bar->Validate();
136+
void ValidateObjects(std::vector<void *> objects,
137+
std::vector<Validator> validators) {
138+
ASSERT_EQ(validators.size(), objects.size());
139+
for (size_t i = 0; i < validators.size(); ++i) {
140+
Validator &validator = validators[i];
141+
Instrumented *instrumented = static_cast<Instrumented *>(objects[i]);
142+
EXPECT_TRUE(instrumented->IsA(validator.clazz));
143+
switch (validator.validation) {
144+
case Validator::valid:
145+
instrumented->Validate();
146+
break;
147+
case Validator::invalid:
148+
break;
149+
}
128150
}
129151
}
130152

131153
InstrumentedFoo::InstrumentedFoo(int i) {
132154
LLDB_RECORD_CONSTRUCTOR(InstrumentedFoo, (int), i);
133-
g_foos.push_back(this);
134155
}
135156

136157
InstrumentedFoo::InstrumentedFoo(const InstrumentedFoo &foo) {
137158
LLDB_RECORD_CONSTRUCTOR(InstrumentedFoo, (const InstrumentedFoo &), foo);
138-
g_foos.erase(std::remove(g_foos.begin(), g_foos.end(), &foo));
139-
g_foos.push_back(this);
140159
}
141160

142161
InstrumentedFoo &InstrumentedFoo::operator=(const InstrumentedFoo &foo) {
143162
LLDB_RECORD_METHOD(InstrumentedFoo &,
144163
InstrumentedFoo, operator=,(const InstrumentedFoo &), foo);
145-
g_foos.erase(std::remove(g_foos.begin(), g_foos.end(), &foo));
146-
g_foos.push_back(this);
147164
return *this;
148165
}
149166

@@ -195,7 +212,6 @@ void InstrumentedFoo::Validate() {
195212

196213
InstrumentedBar::InstrumentedBar() {
197214
LLDB_RECORD_CONSTRUCTOR_NO_ARGS(InstrumentedBar);
198-
g_bars.push_back(this);
199215
}
200216

201217
InstrumentedFoo InstrumentedBar::GetInstrumentedFoo() {
@@ -242,7 +258,7 @@ void InstrumentedBar::Validate() {
242258
}
243259

244260
TestingRegistry::TestingRegistry() {
245-
Registry& R = *this;
261+
Registry &R = *this;
246262

247263
LLDB_REGISTER_CONSTRUCTOR(InstrumentedFoo, (int i));
248264
LLDB_REGISTER_CONSTRUCTOR(InstrumentedFoo, (const InstrumentedFoo &));
@@ -525,9 +541,11 @@ TEST(RecordReplayTest, InstrumentedFoo) {
525541
ClearObjects();
526542

527543
TestingRegistry registry;
528-
registry.Replay(os.str());
544+
Deserializer deserializer(os.str());
545+
registry.Replay(deserializer);
529546

530-
ValidateObjects(1, 0);
547+
ValidateObjects(deserializer.GetAllObjects(),
548+
{{Class::Foo, Validator::valid}});
531549
}
532550

533551
TEST(RecordReplayTest, InstrumentedFooSameThis) {
@@ -563,9 +581,11 @@ TEST(RecordReplayTest, InstrumentedFooSameThis) {
563581
ClearObjects();
564582

565583
TestingRegistry registry;
566-
registry.Replay(os.str());
584+
Deserializer deserializer(os.str());
585+
registry.Replay(deserializer);
567586

568-
ValidateObjects(2, 0);
587+
ValidateObjects(deserializer.GetAllObjects(),
588+
{{Class::Foo, Validator::valid}});
569589
}
570590

571591
TEST(RecordReplayTest, InstrumentedBar) {
@@ -577,10 +597,6 @@ TEST(RecordReplayTest, InstrumentedBar) {
577597
{
578598
InstrumentedBar bar;
579599
InstrumentedFoo foo = bar.GetInstrumentedFoo();
580-
#if 0
581-
InstrumentedFoo& foo_ref = bar.GetInstrumentedFooRef();
582-
InstrumentedFoo* foo_ptr = bar.GetInstrumentedFooPtr();
583-
#endif
584600

585601
int b = 200;
586602
float c = 300.3f;
@@ -602,9 +618,16 @@ TEST(RecordReplayTest, InstrumentedBar) {
602618
ClearObjects();
603619

604620
TestingRegistry registry;
605-
registry.Replay(os.str());
621+
Deserializer deserializer(os.str());
622+
registry.Replay(deserializer);
606623

607-
ValidateObjects(1, 1);
624+
ValidateObjects(
625+
deserializer.GetAllObjects(),
626+
{
627+
{Class::Bar, Validator::valid}, // bar
628+
{Class::Foo, Validator::invalid}, // bar.GetInstrumentedFoo()
629+
{Class::Foo, Validator::valid}, // foo
630+
});
608631
}
609632

610633
TEST(RecordReplayTest, InstrumentedBarRef) {
@@ -637,9 +660,12 @@ TEST(RecordReplayTest, InstrumentedBarRef) {
637660
ClearObjects();
638661

639662
TestingRegistry registry;
640-
registry.Replay(os.str());
663+
Deserializer deserializer(os.str());
664+
registry.Replay(deserializer);
641665

642-
ValidateObjects(1, 1);
666+
ValidateObjects(
667+
deserializer.GetAllObjects(),
668+
{{Class::Bar, Validator::valid}, {Class::Foo, Validator::valid}});
643669
}
644670

645671
TEST(RecordReplayTest, InstrumentedBarPtr) {
@@ -672,7 +698,10 @@ TEST(RecordReplayTest, InstrumentedBarPtr) {
672698
ClearObjects();
673699

674700
TestingRegistry registry;
675-
registry.Replay(os.str());
701+
Deserializer deserializer(os.str());
702+
registry.Replay(deserializer);
676703

677-
ValidateObjects(1, 1);
704+
ValidateObjects(
705+
deserializer.GetAllObjects(),
706+
{{Class::Bar, Validator::valid}, {Class::Foo, Validator::valid}});
678707
}

0 commit comments

Comments
 (0)