1 //===-- ReproducerInstrumentation.cpp -------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "lldb/Utility/ReproducerInstrumentation.h"
10 #include "lldb/Utility/Reproducer.h"
11 #include <cstdio>
12 #include <cstdlib>
13 #include <limits>
14 #include <thread>
15
16 using namespace lldb_private;
17 using namespace lldb_private::repro;
18
GetObjectForIndexImpl(unsigned idx)19 void *IndexToObject::GetObjectForIndexImpl(unsigned idx) {
20 return m_mapping.lookup(idx);
21 }
22
AddObjectForIndexImpl(unsigned idx,void * object)23 void IndexToObject::AddObjectForIndexImpl(unsigned idx, void *object) {
24 assert(idx != 0 && "Cannot add object for sentinel");
25 m_mapping[idx] = object;
26 }
27
GetAllObjects() const28 std::vector<void *> IndexToObject::GetAllObjects() const {
29 std::vector<std::pair<unsigned, void *>> pairs;
30 for (auto &e : m_mapping) {
31 pairs.emplace_back(e.first, e.second);
32 }
33
34 // Sort based on index.
35 std::sort(pairs.begin(), pairs.end(),
36 [](auto &lhs, auto &rhs) { return lhs.first < rhs.first; });
37
38 std::vector<void *> objects;
39 objects.reserve(pairs.size());
40 for (auto &p : pairs) {
41 objects.push_back(p.second);
42 }
43
44 return objects;
45 }
46
Deserialize()47 template <> const uint8_t *Deserializer::Deserialize<const uint8_t *>() {
48 return Deserialize<uint8_t *>();
49 }
50
Deserialize()51 template <> void *Deserializer::Deserialize<void *>() {
52 return const_cast<void *>(Deserialize<const void *>());
53 }
54
Deserialize()55 template <> const void *Deserializer::Deserialize<const void *>() {
56 return nullptr;
57 }
58
Deserialize()59 template <> char *Deserializer::Deserialize<char *>() {
60 return const_cast<char *>(Deserialize<const char *>());
61 }
62
Deserialize()63 template <> const char *Deserializer::Deserialize<const char *>() {
64 const size_t size = Deserialize<size_t>();
65 if (size == std::numeric_limits<size_t>::max())
66 return nullptr;
67 assert(HasData(size + 1));
68 const char *str = m_buffer.data();
69 m_buffer = m_buffer.drop_front(size + 1);
70 #ifdef LLDB_REPRO_INSTR_TRACE
71 llvm::errs() << "Deserializing with " << LLVM_PRETTY_FUNCTION << " -> \""
72 << str << "\"\n";
73 #endif
74 return str;
75 }
76
Deserialize()77 template <> const char **Deserializer::Deserialize<const char **>() {
78 const size_t size = Deserialize<size_t>();
79 if (size == 0)
80 return nullptr;
81 const char **r =
82 reinterpret_cast<const char **>(calloc(size + 1, sizeof(char *)));
83 for (size_t i = 0; i < size; ++i)
84 r[i] = Deserialize<const char *>();
85 return r;
86 }
87
CheckSequence(unsigned sequence)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
Replay(const FileSpec & file)98 bool Registry::Replay(const FileSpec &file) {
99 auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
100 if (auto err = error_or_file.getError())
101 return false;
102
103 return Replay((*error_or_file)->getBuffer());
104 }
105
Replay(llvm::StringRef buffer)106 bool Registry::Replay(llvm::StringRef buffer) {
107 Deserializer deserializer(buffer);
108 return Replay(deserializer);
109 }
110
Replay(Deserializer & deserializer)111 bool Registry::Replay(Deserializer &deserializer) {
112 #ifndef LLDB_REPRO_INSTR_TRACE
113 Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API);
114 #endif
115
116 // Disable buffering stdout so that we approximate the way things get flushed
117 // during an interactive session.
118 setvbuf(stdout, nullptr, _IONBF, 0);
119
120 while (deserializer.HasData(1)) {
121 unsigned sequence = deserializer.Deserialize<unsigned>();
122 unsigned id = deserializer.Deserialize<unsigned>();
123
124 #ifndef LLDB_REPRO_INSTR_TRACE
125 LLDB_LOG(log, "Replaying {0}: {1}", id, GetSignature(id));
126 #else
127 llvm::errs() << "Replaying " << id << ": " << GetSignature(id) << "\n";
128 #endif
129
130 deserializer.SetExpectedSequence(sequence);
131 GetReplayer(id)->operator()(deserializer);
132 }
133
134 // Add a small artificial delay to ensure that all asynchronous events have
135 // completed before we exit.
136 std::this_thread::sleep_for(std::chrono::milliseconds(100));
137
138 return true;
139 }
140
DoRegister(uintptr_t RunID,std::unique_ptr<Replayer> replayer,SignatureStr signature)141 void Registry::DoRegister(uintptr_t RunID, std::unique_ptr<Replayer> replayer,
142 SignatureStr signature) {
143 const unsigned id = m_replayers.size() + 1;
144 assert(m_replayers.find(RunID) == m_replayers.end());
145 m_replayers[RunID] = std::make_pair(std::move(replayer), id);
146 m_ids[id] =
147 std::make_pair(m_replayers[RunID].first.get(), std::move(signature));
148 }
149
GetID(uintptr_t addr)150 unsigned Registry::GetID(uintptr_t addr) {
151 unsigned id = m_replayers[addr].second;
152 assert(id != 0 && "Forgot to add function to registry?");
153 return id;
154 }
155
GetSignature(unsigned id)156 std::string Registry::GetSignature(unsigned id) {
157 assert(m_ids.count(id) != 0 && "ID not in registry");
158 return m_ids[id].second.ToString();
159 }
160
CheckID(unsigned expected,unsigned actual)161 void Registry::CheckID(unsigned expected, unsigned actual) {
162 if (expected != actual) {
163 llvm::errs() << "Reproducer expected signature " << expected << ": '"
164 << GetSignature(expected) << "'\n";
165 llvm::errs() << "Reproducer actual signature " << actual << ": '"
166 << GetSignature(actual) << "'\n";
167 llvm::report_fatal_error(
168 "Detected reproducer replay divergence. Refusing to continue.");
169 }
170
171 #ifdef LLDB_REPRO_INSTR_TRACE
172 llvm::errs() << "Replaying " << actual << ": " << GetSignature(actual)
173 << "\n";
174 #endif
175 }
176
GetReplayer(unsigned id)177 Replayer *Registry::GetReplayer(unsigned id) {
178 assert(m_ids.count(id) != 0 && "ID not in registry");
179 return m_ids[id].first;
180 }
181
ToString() const182 std::string Registry::SignatureStr::ToString() const {
183 return (result + (result.empty() ? "" : " ") + scope + "::" + name + args)
184 .str();
185 }
186
GetIndexForObjectImpl(const void * object)187 unsigned ObjectToIndex::GetIndexForObjectImpl(const void *object) {
188 unsigned index = m_mapping.size() + 1;
189 auto it = m_mapping.find(object);
190 if (it == m_mapping.end())
191 m_mapping[object] = index;
192 return m_mapping[object];
193 }
194
Recorder()195 Recorder::Recorder()
196 : m_pretty_func(), m_pretty_args(),
197
198 m_sequence(std::numeric_limits<unsigned>::max()) {
199 if (!g_global_boundary) {
200 g_global_boundary = true;
201 m_local_boundary = true;
202 m_sequence = GetNextSequenceNumber();
203 }
204 }
205
Recorder(llvm::StringRef pretty_func,std::string && pretty_args)206 Recorder::Recorder(llvm::StringRef pretty_func, std::string &&pretty_args)
207 : m_serializer(nullptr), m_pretty_func(pretty_func),
208 m_pretty_args(pretty_args), m_local_boundary(false),
209 m_result_recorded(true),
210 m_sequence(std::numeric_limits<unsigned>::max()) {
211 if (!g_global_boundary) {
212 g_global_boundary = true;
213 m_local_boundary = true;
214 m_sequence = GetNextSequenceNumber();
215 LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), "{0} ({1})",
216 m_pretty_func, m_pretty_args);
217 }
218 }
219
~Recorder()220 Recorder::~Recorder() {
221 assert(m_result_recorded && "Did you forget LLDB_RECORD_RESULT?");
222 UpdateBoundary();
223 }
224
GetSequenceNumber() const225 unsigned Recorder::GetSequenceNumber() const {
226 assert(m_sequence != std::numeric_limits<unsigned>::max());
227 return m_sequence;
228 }
229
Initialize(Serializer & serializer,Registry & registry)230 void InstrumentationData::Initialize(Serializer &serializer,
231 Registry ®istry) {
232 InstanceImpl().emplace(serializer, registry);
233 }
234
Initialize(Deserializer & deserializer,Registry & registry)235 void InstrumentationData::Initialize(Deserializer &deserializer,
236 Registry ®istry) {
237 InstanceImpl().emplace(deserializer, registry);
238 }
239
Instance()240 InstrumentationData &InstrumentationData::Instance() {
241 if (!InstanceImpl())
242 InstanceImpl().emplace();
243 return *InstanceImpl();
244 }
245
InstanceImpl()246 llvm::Optional<InstrumentationData> &InstrumentationData::InstanceImpl() {
247 static llvm::Optional<InstrumentationData> g_instrumentation_data;
248 return g_instrumentation_data;
249 }
250
251 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;
254