• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <stdio.h>
12 #include <stdlib.h>
13 #include <thread>
14 
15 using namespace lldb_private;
16 using namespace lldb_private::repro;
17 
GetObjectForIndexImpl(unsigned idx)18 void *IndexToObject::GetObjectForIndexImpl(unsigned idx) {
19   return m_mapping.lookup(idx);
20 }
21 
AddObjectForIndexImpl(unsigned idx,void * object)22 void IndexToObject::AddObjectForIndexImpl(unsigned idx, void *object) {
23   assert(idx != 0 && "Cannot add object for sentinel");
24   m_mapping[idx] = object;
25 }
26 
GetAllObjects() const27 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 
Deserialize()46 template <> const uint8_t *Deserializer::Deserialize<const uint8_t *>() {
47   return Deserialize<uint8_t *>();
48 }
49 
Deserialize()50 template <> void *Deserializer::Deserialize<void *>() {
51   return const_cast<void *>(Deserialize<const void *>());
52 }
53 
Deserialize()54 template <> const void *Deserializer::Deserialize<const void *>() {
55   return nullptr;
56 }
57 
Deserialize()58 template <> char *Deserializer::Deserialize<char *>() {
59   return const_cast<char *>(Deserialize<const char *>());
60 }
61 
Deserialize()62 template <> const char *Deserializer::Deserialize<const char *>() {
63   const size_t size = Deserialize<size_t>();
64   if (size == std::numeric_limits<size_t>::max())
65     return nullptr;
66   assert(HasData(size + 1));
67   const char *str = m_buffer.data();
68   m_buffer = m_buffer.drop_front(size + 1);
69 #ifdef LLDB_REPRO_INSTR_TRACE
70   llvm::errs() << "Deserializing with " << LLVM_PRETTY_FUNCTION << " -> \""
71                << str << "\"\n";
72 #endif
73   return str;
74 }
75 
Deserialize()76 template <> const char **Deserializer::Deserialize<const char **>() {
77   const size_t size = Deserialize<size_t>();
78   if (size == 0)
79     return nullptr;
80   const char **r =
81       reinterpret_cast<const char **>(calloc(size + 1, sizeof(char *)));
82   for (size_t i = 0; i < size; ++i)
83     r[i] = Deserialize<const char *>();
84   return r;
85 }
86 
Replay(const FileSpec & file)87 bool Registry::Replay(const FileSpec &file) {
88   auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
89   if (auto err = error_or_file.getError())
90     return false;
91 
92   return Replay((*error_or_file)->getBuffer());
93 }
94 
Replay(llvm::StringRef buffer)95 bool Registry::Replay(llvm::StringRef buffer) {
96   Deserializer deserializer(buffer);
97   return Replay(deserializer);
98 }
99 
Replay(Deserializer & deserializer)100 bool Registry::Replay(Deserializer &deserializer) {
101 #ifndef LLDB_REPRO_INSTR_TRACE
102   Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API);
103 #endif
104 
105   // Disable buffering stdout so that we approximate the way things get flushed
106   // during an interactive session.
107   setvbuf(stdout, nullptr, _IONBF, 0);
108 
109   while (deserializer.HasData(1)) {
110     unsigned id = deserializer.Deserialize<unsigned>();
111 
112 #ifndef LLDB_REPRO_INSTR_TRACE
113     LLDB_LOG(log, "Replaying {0}: {1}", id, GetSignature(id));
114 #else
115     llvm::errs() << "Replaying " << id << ": " << GetSignature(id) << "\n";
116 #endif
117 
118     GetReplayer(id)->operator()(deserializer);
119   }
120 
121   // Add a small artificial delay to ensure that all asynchronous events have
122   // completed before we exit.
123   std::this_thread::sleep_for(std::chrono::milliseconds(100));
124 
125   return true;
126 }
127 
DoRegister(uintptr_t RunID,std::unique_ptr<Replayer> replayer,SignatureStr signature)128 void Registry::DoRegister(uintptr_t RunID, std::unique_ptr<Replayer> replayer,
129                           SignatureStr signature) {
130   const unsigned id = m_replayers.size() + 1;
131   assert(m_replayers.find(RunID) == m_replayers.end());
132   m_replayers[RunID] = std::make_pair(std::move(replayer), id);
133   m_ids[id] =
134       std::make_pair(m_replayers[RunID].first.get(), std::move(signature));
135 }
136 
GetID(uintptr_t addr)137 unsigned Registry::GetID(uintptr_t addr) {
138   unsigned id = m_replayers[addr].second;
139   assert(id != 0 && "Forgot to add function to registry?");
140   return id;
141 }
142 
GetSignature(unsigned id)143 std::string Registry::GetSignature(unsigned id) {
144   assert(m_ids.count(id) != 0 && "ID not in registry");
145   return m_ids[id].second.ToString();
146 }
147 
CheckID(unsigned expected,unsigned actual)148 void Registry::CheckID(unsigned expected, unsigned actual) {
149   if (expected != actual) {
150     llvm::errs() << "Reproducer expected signature " << expected << ": '"
151                  << GetSignature(expected) << "'\n";
152     llvm::errs() << "Reproducer actual signature " << actual << ": '"
153                  << GetSignature(actual) << "'\n";
154     llvm::report_fatal_error(
155         "Detected reproducer replay divergence. Refusing to continue.");
156   }
157 
158 #ifdef LLDB_REPRO_INSTR_TRACE
159   llvm::errs() << "Replaying " << actual << ": " << GetSignature(actual)
160                << "\n";
161 #endif
162 }
163 
GetReplayer(unsigned id)164 Replayer *Registry::GetReplayer(unsigned id) {
165   assert(m_ids.count(id) != 0 && "ID not in registry");
166   return m_ids[id].first;
167 }
168 
ToString() const169 std::string Registry::SignatureStr::ToString() const {
170   return (result + (result.empty() ? "" : " ") + scope + "::" + name + args)
171       .str();
172 }
173 
GetIndexForObjectImpl(const void * object)174 unsigned ObjectToIndex::GetIndexForObjectImpl(const void *object) {
175   unsigned index = m_mapping.size() + 1;
176   auto it = m_mapping.find(object);
177   if (it == m_mapping.end())
178     m_mapping[object] = index;
179   return m_mapping[object];
180 }
181 
Recorder()182 Recorder::Recorder()
183     : m_serializer(nullptr), m_pretty_func(), m_pretty_args(),
184       m_local_boundary(false), m_result_recorded(true) {
185   if (!g_global_boundary) {
186     g_global_boundary = true;
187     m_local_boundary = true;
188   }
189 }
190 
Recorder(llvm::StringRef pretty_func,std::string && pretty_args)191 Recorder::Recorder(llvm::StringRef pretty_func, std::string &&pretty_args)
192     : m_serializer(nullptr), m_pretty_func(pretty_func),
193       m_pretty_args(pretty_args), m_local_boundary(false),
194       m_result_recorded(true) {
195   if (!g_global_boundary) {
196     g_global_boundary = true;
197     m_local_boundary = true;
198 
199     LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), "{0} ({1})",
200              m_pretty_func, m_pretty_args);
201   }
202 }
203 
~Recorder()204 Recorder::~Recorder() {
205   assert(m_result_recorded && "Did you forget LLDB_RECORD_RESULT?");
206   UpdateBoundary();
207 }
208 
Initialize(Serializer & serializer,Registry & registry)209 void InstrumentationData::Initialize(Serializer &serializer,
210                                      Registry &registry) {
211   InstanceImpl().emplace(serializer, registry);
212 }
213 
Initialize(Deserializer & deserializer,Registry & registry)214 void InstrumentationData::Initialize(Deserializer &deserializer,
215                                      Registry &registry) {
216   InstanceImpl().emplace(deserializer, registry);
217 }
218 
Instance()219 InstrumentationData &InstrumentationData::Instance() {
220   if (!InstanceImpl())
221     InstanceImpl().emplace();
222   return *InstanceImpl();
223 }
224 
InstanceImpl()225 llvm::Optional<InstrumentationData> &InstrumentationData::InstanceImpl() {
226   static llvm::Optional<InstrumentationData> g_instrumentation_data;
227   return g_instrumentation_data;
228 }
229 
230 thread_local bool lldb_private::repro::Recorder::g_global_boundary = false;
231