1 //===-- ReproducerInstrumentation.h -----------------------------*- C++ -*-===//
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //===----------------------------------------------------------------------===//
8 #ifndef LLDB_UTILITY_REPRODUCER_INSTRUMENTATION_H
9 #define LLDB_UTILITY_REPRODUCER_INSTRUMENTATION_H
11 #include "lldb/Utility/FileSpec.h"
12 #include "lldb/Utility/Log.h"
13 #include "lldb/Utility/Logging.h"
15 #include "llvm/ADT/DenseMap.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/Support/ErrorHandling.h"
21 #include <type_traits>
24 typename std::enable_if<std::is_fundamental<T>::value, int>::type = 0>
25 inline void stringify_append(llvm::raw_string_ostream &ss, const T &t) {
29 template <typename T, typename std::enable_if<!std::is_fundamental<T>::value,
31 inline void stringify_append(llvm::raw_string_ostream &ss, const T &t) {
36 inline void stringify_append(llvm::raw_string_ostream &ss, const T *t) {
37 ss << reinterpret_cast<const void *>(t);
41 inline void stringify_append<char>(llvm::raw_string_ostream &ss,
43 ss << '\"' << t << '\"';
46 template <typename Head>
47 inline void stringify_helper(llvm::raw_string_ostream &ss, const Head &head) {
48 stringify_append(ss, head);
51 template <typename Head, typename... Tail>
52 inline void stringify_helper(llvm::raw_string_ostream &ss, const Head &head,
53 const Tail &... tail) {
54 stringify_append(ss, head);
56 stringify_helper(ss, tail...);
59 template <typename... Ts> inline std::string stringify_args(const Ts &... ts) {
61 llvm::raw_string_ostream ss(buffer);
62 stringify_helper(ss, ts...);
66 // Define LLDB_REPRO_INSTR_TRACE to trace to stderr instead of LLDB's log
67 // infrastructure. This is useful when you need to see traces before the logger
68 // is initialized or enabled.
69 // #define LLDB_REPRO_INSTR_TRACE
71 #define LLDB_REGISTER_CONSTRUCTOR(Class, Signature) \
72 R.Register<Class * Signature>(&construct<Class Signature>::doit, "", #Class, \
74 #define LLDB_REGISTER_METHOD(Result, Class, Method, Signature) \
76 &invoke<Result(Class::*) Signature>::method<(&Class::Method)>::doit, \
77 #Result, #Class, #Method, #Signature)
78 #define LLDB_REGISTER_METHOD_CONST(Result, Class, Method, Signature) \
79 R.Register(&invoke<Result(Class::*) Signature const>::method_const<( \
80 &Class::Method)>::doit, \
81 #Result, #Class, #Method, #Signature)
82 #define LLDB_REGISTER_STATIC_METHOD(Result, Class, Method, Signature) \
83 R.Register<Result Signature>( \
84 static_cast<Result(*) Signature>(&Class::Method), #Result, #Class, \
87 #define LLDB_RECORD_CONSTRUCTOR(Class, Signature, ...) \
88 lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION, \
89 stringify_args(__VA_ARGS__)); \
90 if (lldb_private::repro::InstrumentationData data = \
91 LLDB_GET_INSTRUMENTATION_DATA()) { \
92 sb_recorder.Record(data.GetSerializer(), data.GetRegistry(), \
93 &lldb_private::repro::construct<Class Signature>::doit, \
95 sb_recorder.RecordResult(this); \
98 #define LLDB_RECORD_CONSTRUCTOR_NO_ARGS(Class) \
99 lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION); \
100 if (lldb_private::repro::InstrumentationData data = \
101 LLDB_GET_INSTRUMENTATION_DATA()) { \
102 sb_recorder.Record(data.GetSerializer(), data.GetRegistry(), \
103 &lldb_private::repro::construct<Class()>::doit); \
104 sb_recorder.RecordResult(this); \
107 #define LLDB_RECORD_METHOD(Result, Class, Method, Signature, ...) \
108 lldb_private::repro::Recorder sb_recorder( \
109 LLVM_PRETTY_FUNCTION, stringify_args(*this, __VA_ARGS__)); \
110 if (lldb_private::repro::InstrumentationData data = \
111 LLDB_GET_INSTRUMENTATION_DATA()) { \
112 sb_recorder.Record( \
113 data.GetSerializer(), data.GetRegistry(), \
114 &lldb_private::repro::invoke<Result(Class::*) Signature>::method<( \
115 &Class::Method)>::doit, \
116 this, __VA_ARGS__); \
119 #define LLDB_RECORD_METHOD_CONST(Result, Class, Method, Signature, ...) \
120 lldb_private::repro::Recorder sb_recorder( \
121 LLVM_PRETTY_FUNCTION, stringify_args(*this, __VA_ARGS__)); \
122 if (lldb_private::repro::InstrumentationData data = \
123 LLDB_GET_INSTRUMENTATION_DATA()) { \
124 sb_recorder.Record( \
125 data.GetSerializer(), data.GetRegistry(), \
126 &lldb_private::repro::invoke<Result( \
127 Class::*) Signature const>::method_const<(&Class::Method)>::doit, \
128 this, __VA_ARGS__); \
131 #define LLDB_RECORD_METHOD_NO_ARGS(Result, Class, Method) \
132 lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION, \
133 stringify_args(*this)); \
134 if (lldb_private::repro::InstrumentationData data = \
135 LLDB_GET_INSTRUMENTATION_DATA()) { \
136 sb_recorder.Record(data.GetSerializer(), data.GetRegistry(), \
137 &lldb_private::repro::invoke<Result ( \
138 Class::*)()>::method<(&Class::Method)>::doit, \
142 #define LLDB_RECORD_METHOD_CONST_NO_ARGS(Result, Class, Method) \
143 lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION, \
144 stringify_args(*this)); \
145 if (lldb_private::repro::InstrumentationData data = \
146 LLDB_GET_INSTRUMENTATION_DATA()) { \
147 sb_recorder.Record( \
148 data.GetSerializer(), data.GetRegistry(), \
149 &lldb_private::repro::invoke<Result ( \
150 Class::*)() const>::method_const<(&Class::Method)>::doit, \
154 #define LLDB_RECORD_STATIC_METHOD(Result, Class, Method, Signature, ...) \
155 lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION, \
156 stringify_args(__VA_ARGS__)); \
157 if (lldb_private::repro::InstrumentationData data = \
158 LLDB_GET_INSTRUMENTATION_DATA()) { \
159 sb_recorder.Record(data.GetSerializer(), data.GetRegistry(), \
160 static_cast<Result(*) Signature>(&Class::Method), \
164 #define LLDB_RECORD_STATIC_METHOD_NO_ARGS(Result, Class, Method) \
165 lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION); \
166 if (lldb_private::repro::InstrumentationData data = \
167 LLDB_GET_INSTRUMENTATION_DATA()) { \
168 sb_recorder.Record(data.GetSerializer(), data.GetRegistry(), \
169 static_cast<Result (*)()>(&Class::Method)); \
172 #define LLDB_RECORD_RESULT(Result) sb_recorder.RecordResult(Result);
174 /// The LLDB_RECORD_DUMMY macro is special because it doesn't actually record
175 /// anything. It's used to track API boundaries when we cannot record for
176 /// technical reasons.
177 #define LLDB_RECORD_DUMMY(Result, Class, Method, Signature, ...) \
178 lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION, \
179 stringify_args(__VA_ARGS__));
180 #define LLDB_RECORD_DUMMY_NO_ARGS(Result, Class, Method) \
181 lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION);
183 namespace lldb_private {
186 /// Mapping between serialized indices and their corresponding objects.
188 /// This class is used during replay to map indices back to in-memory objects.
190 /// When objects are constructed, they are added to this mapping using
191 /// AddObjectForIndex.
193 /// When an object is passed to a function, its index is deserialized and
194 /// AddObjectForIndex returns the corresponding object. If there is no object
195 /// for the given index, a nullptr is returend. The latter is valid when custom
196 /// replay code is in place and the actual object is ignored.
197 class IndexToObject {
199 /// Returns an object as a pointer for the given index or nullptr if not
200 /// present in the map.
201 template <typename T> T *GetObjectForIndex(unsigned idx) {
202 assert(idx != 0 && "Cannot get object for sentinel");
203 void *object = GetObjectForIndexImpl(idx);
204 return static_cast<T *>(object);
207 /// Adds a pointer to an object to the mapping for the given index.
208 template <typename T> void AddObjectForIndex(unsigned idx, T *object) {
209 AddObjectForIndexImpl(
210 idx, static_cast<void *>(
211 const_cast<typename std::remove_const<T>::type *>(object)));
214 /// Adds a reference to an object to the mapping for the given index.
215 template <typename T> void AddObjectForIndex(unsigned idx, T &object) {
216 AddObjectForIndexImpl(
217 idx, static_cast<void *>(
218 const_cast<typename std::remove_const<T>::type *>(&object)));
222 /// Helper method that does the actual lookup. The void* result is later cast
224 void *GetObjectForIndexImpl(unsigned idx);
226 /// Helper method that does the actual insertion.
227 void AddObjectForIndexImpl(unsigned idx, void *object);
229 /// Keeps a mapping between indices and their corresponding object.
230 llvm::DenseMap<unsigned, void *> m_mapping;
233 /// We need to differentiate between pointers to fundamental and
234 /// non-fundamental types. See the corresponding Deserializer::Read method
235 /// for the reason why.
236 struct PointerTag {};
237 struct ReferenceTag {};
239 struct FundamentalPointerTag {};
240 struct FundamentalReferenceTag {};
241 struct NotImplementedTag {};
243 /// Return the deserialization tag for the given type T.
244 template <class T> struct serializer_tag {
245 typedef typename std::conditional<std::is_trivially_copyable<T>::value, ValueTag, NotImplementedTag>::type type;
247 template <class T> struct serializer_tag<T *> {
249 typename std::conditional<std::is_fundamental<T>::value,
250 FundamentalPointerTag, PointerTag>::type type;
252 template <class T> struct serializer_tag<T &> {
253 typedef typename std::conditional<std::is_fundamental<T>::value,
254 FundamentalReferenceTag, ReferenceTag>::type
258 /// Deserializes data from a buffer. It is used to deserialize function indices
259 /// to replay, their arguments and return values.
261 /// Fundamental types and strings are read by value. Objects are read by their
262 /// index, which get translated by the IndexToObject mapping maintained in
265 /// Additional bookkeeping with regards to the IndexToObject is required to
266 /// deserialize objects. When a constructor is run or an object is returned by
267 /// value, we need to capture the object and add it to the index together with
268 /// its index. This is the job of HandleReplayResult(Void).
271 Deserializer(llvm::StringRef buffer) : m_buffer(buffer) {}
273 /// Returns true when the buffer has unread data.
274 bool HasData(unsigned size) { return size <= m_buffer.size(); }
276 /// Deserialize and interpret value as T.
277 template <typename T> T Deserialize() {
278 #ifdef LLDB_REPRO_INSTR_TRACE
279 llvm::errs() << "Deserializing with " << LLVM_PRETTY_FUNCTION << "\n";
281 return Read<T>(typename serializer_tag<T>::type());
284 /// Store the returned value in the index-to-object mapping.
285 template <typename T> void HandleReplayResult(const T &t) {
286 unsigned result = Deserialize<unsigned>();
287 if (std::is_fundamental<T>::value)
289 // We need to make a copy as the original object might go out of scope.
290 m_index_to_object.AddObjectForIndex(result, new T(t));
293 /// Store the returned value in the index-to-object mapping.
294 template <typename T> void HandleReplayResult(T *t) {
295 unsigned result = Deserialize<unsigned>();
296 if (std::is_fundamental<T>::value)
298 m_index_to_object.AddObjectForIndex(result, t);
301 /// All returned types are recorded, even when the function returns a void.
302 /// The latter requires special handling.
303 void HandleReplayResultVoid() {
304 unsigned result = Deserialize<unsigned>();
310 template <typename T> T Read(NotImplementedTag) {
311 m_buffer = m_buffer.drop_front(sizeof(T));
315 template <typename T> T Read(ValueTag) {
316 assert(HasData(sizeof(T)));
318 std::memcpy(reinterpret_cast<char *>(&t), m_buffer.data(), sizeof(T));
319 m_buffer = m_buffer.drop_front(sizeof(T));
323 template <typename T> T Read(PointerTag) {
324 typedef typename std::remove_pointer<T>::type UnderlyingT;
325 return m_index_to_object.template GetObjectForIndex<UnderlyingT>(
326 Deserialize<unsigned>());
329 template <typename T> T Read(ReferenceTag) {
330 typedef typename std::remove_reference<T>::type UnderlyingT;
331 // If this is a reference to a fundamental type we just read its value.
332 return *m_index_to_object.template GetObjectForIndex<UnderlyingT>(
333 Deserialize<unsigned>());
336 /// This method is used to parse references to fundamental types. Because
337 /// they're not recorded in the object table we have serialized their value.
338 /// We read its value, allocate a copy on the heap, and return a pointer to
340 template <typename T> T Read(FundamentalPointerTag) {
341 typedef typename std::remove_pointer<T>::type UnderlyingT;
342 return new UnderlyingT(Deserialize<UnderlyingT>());
345 /// This method is used to parse references to fundamental types. Because
346 /// they're not recorded in the object table we have serialized their value.
347 /// We read its value, allocate a copy on the heap, and return a reference to
349 template <typename T> T Read(FundamentalReferenceTag) {
350 // If this is a reference to a fundamental type we just read its value.
351 typedef typename std::remove_reference<T>::type UnderlyingT;
352 return *(new UnderlyingT(Deserialize<UnderlyingT>()));
355 /// Mapping of indices to objects.
356 IndexToObject m_index_to_object;
358 /// Buffer containing the serialized data.
359 llvm::StringRef m_buffer;
362 /// Partial specialization for C-style strings. We read the string value
363 /// instead of treating it as pointer.
364 template <> const char *Deserializer::Deserialize<const char *>();
365 template <> char *Deserializer::Deserialize<char *>();
367 /// Helpers to auto-synthesize function replay code. It deserializes the replay
368 /// function's arguments one by one and finally calls the corresponding
370 template <typename... Remaining> struct DeserializationHelper;
372 template <typename Head, typename... Tail>
373 struct DeserializationHelper<Head, Tail...> {
374 template <typename Result, typename... Deserialized> struct deserialized {
375 static Result doit(Deserializer &deserializer,
376 Result (*f)(Deserialized..., Head, Tail...),
378 return DeserializationHelper<Tail...>::
379 template deserialized<Result, Deserialized..., Head>::doit(
380 deserializer, f, d..., deserializer.Deserialize<Head>());
385 template <> struct DeserializationHelper<> {
386 template <typename Result, typename... Deserialized> struct deserialized {
387 static Result doit(Deserializer &deserializer, Result (*f)(Deserialized...),
394 /// The replayer interface.
396 virtual ~Replayer() {}
397 virtual void operator()(Deserializer &deserializer) const = 0;
400 /// The default replayer deserializes the arguments and calls the function.
401 template <typename Signature> struct DefaultReplayer;
402 template <typename Result, typename... Args>
403 struct DefaultReplayer<Result(Args...)> : public Replayer {
404 DefaultReplayer(Result (*f)(Args...)) : Replayer(), f(f) {}
406 void operator()(Deserializer &deserializer) const override {
407 deserializer.HandleReplayResult(
408 DeserializationHelper<Args...>::template deserialized<Result>::doit(
412 Result (*f)(Args...);
415 /// Partial specialization for function returning a void type. It ignores the
416 /// (absent) return value.
417 template <typename... Args>
418 struct DefaultReplayer<void(Args...)> : public Replayer {
419 DefaultReplayer(void (*f)(Args...)) : Replayer(), f(f) {}
421 void operator()(Deserializer &deserializer) const override {
422 DeserializationHelper<Args...>::template deserialized<void>::doit(
424 deserializer.HandleReplayResultVoid();
430 /// The registry contains a unique mapping between functions and their ID. The
431 /// IDs can be serialized and deserialized to replay a function. Functions need
432 /// to be registered with the registry for this to work.
435 struct SignatureStr {
436 SignatureStr(llvm::StringRef result = {}, llvm::StringRef scope = {},
437 llvm::StringRef name = {}, llvm::StringRef args = {})
438 : result(result), scope(scope), name(name), args(args) {}
440 std::string ToString() const;
442 llvm::StringRef result;
443 llvm::StringRef scope;
444 llvm::StringRef name;
445 llvm::StringRef args;
449 Registry() = default;
450 virtual ~Registry() = default;
452 /// Register a default replayer for a function.
453 template <typename Signature>
454 void Register(Signature *f, llvm::StringRef result = {},
455 llvm::StringRef scope = {}, llvm::StringRef name = {},
456 llvm::StringRef args = {}) {
457 DoRegister(uintptr_t(f), std::make_unique<DefaultReplayer<Signature>>(f),
458 SignatureStr(result, scope, name, args));
461 /// Register a replayer that invokes a custom function with the same
462 /// signature as the replayed function.
463 template <typename Signature>
464 void Register(Signature *f, Signature *g, llvm::StringRef result = {},
465 llvm::StringRef scope = {}, llvm::StringRef name = {},
466 llvm::StringRef args = {}) {
467 DoRegister(uintptr_t(f), std::make_unique<DefaultReplayer<Signature>>(g),
468 SignatureStr(result, scope, name, args));
471 /// Replay functions from a file.
472 bool Replay(const FileSpec &file);
474 /// Replay functions from a buffer.
475 bool Replay(llvm::StringRef buffer);
477 /// Returns the ID for a given function address.
478 unsigned GetID(uintptr_t addr);
481 /// Register the given replayer for a function (and the ID mapping).
482 void DoRegister(uintptr_t RunID, std::unique_ptr<Replayer> replayer,
483 SignatureStr signature);
486 std::string GetSignature(unsigned id);
487 Replayer *GetReplayer(unsigned id);
489 /// Mapping of function addresses to replayers and their ID.
490 std::map<uintptr_t, std::pair<std::unique_ptr<Replayer>, unsigned>>
493 /// Mapping of IDs to replayer instances.
494 std::map<unsigned, std::pair<Replayer *, SignatureStr>> m_ids;
497 /// To be used as the "Runtime ID" of a constructor. It also invokes the
498 /// constructor when called.
499 template <typename Signature> struct construct;
500 template <typename Class, typename... Args> struct construct<Class(Args...)> {
501 static Class *doit(Args... args) { return new Class(args...); }
504 /// To be used as the "Runtime ID" of a member function. It also invokes the
505 /// member function when called.
506 template <typename Signature> struct invoke;
507 template <typename Result, typename Class, typename... Args>
508 struct invoke<Result (Class::*)(Args...)> {
509 template <Result (Class::*m)(Args...)> struct method {
510 static Result doit(Class *c, Args... args) { return (c->*m)(args...); }
514 template <typename Result, typename Class, typename... Args>
515 struct invoke<Result (Class::*)(Args...) const> {
516 template <Result (Class::*m)(Args...) const> struct method_const {
517 static Result doit(Class *c, Args... args) { return (c->*m)(args...); }
521 template <typename Class, typename... Args>
522 struct invoke<void (Class::*)(Args...)> {
523 template <void (Class::*m)(Args...)> struct method {
524 static void doit(Class *c, Args... args) { (c->*m)(args...); }
528 /// Maps an object to an index for serialization. Indices are unique and
529 /// incremented for every new object.
531 /// Indices start at 1 in order to differentiate with an invalid index (0) in
532 /// the serialized buffer.
533 class ObjectToIndex {
535 template <typename T> unsigned GetIndexForObject(T *t) {
536 return GetIndexForObjectImpl(static_cast<const void *>(t));
540 unsigned GetIndexForObjectImpl(const void *object);
542 llvm::DenseMap<const void *, unsigned> m_mapping;
545 /// Serializes functions, their arguments and their return type to a stream.
548 Serializer(llvm::raw_ostream &stream = llvm::outs()) : m_stream(stream) {}
550 /// Recursively serialize all the given arguments.
551 template <typename Head, typename... Tail>
552 void SerializeAll(const Head &head, const Tail &... tail) {
554 SerializeAll(tail...);
557 void SerializeAll() { m_stream.flush(); }
560 /// Serialize pointers. We need to differentiate between pointers to
561 /// fundamental types (in which case we serialize its value) and pointer to
562 /// objects (in which case we serialize their index).
563 template <typename T> void Serialize(T *t) {
564 if (std::is_fundamental<T>::value) {
567 unsigned idx = m_tracker.GetIndexForObject(t);
572 /// Serialize references. We need to differentiate between references to
573 /// fundamental types (in which case we serialize its value) and references
574 /// to objects (in which case we serialize their index).
575 template <typename T> void Serialize(T &t) {
576 if (std::is_fundamental<T>::value) {
577 m_stream.write(reinterpret_cast<const char *>(&t), sizeof(T));
579 unsigned idx = m_tracker.GetIndexForObject(&t);
584 void Serialize(void *v) {
585 // FIXME: Support void*
586 llvm_unreachable("void* is currently unsupported.");
589 void Serialize(const char *t) {
594 /// Serialization stream.
595 llvm::raw_ostream &m_stream;
597 /// Mapping of objects to indices.
598 ObjectToIndex m_tracker;
601 class InstrumentationData {
603 InstrumentationData() : m_serializer(nullptr), m_registry(nullptr){};
604 InstrumentationData(Serializer &serializer, Registry ®istry)
605 : m_serializer(&serializer), m_registry(®istry){};
607 Serializer &GetSerializer() { return *m_serializer; }
608 Registry &GetRegistry() { return *m_registry; }
610 operator bool() { return m_serializer != nullptr && m_registry != nullptr; }
613 Serializer *m_serializer;
614 Registry *m_registry;
617 /// RAII object that records function invocations and their return value.
619 /// API calls are only captured when the API boundary is crossed. Once we're in
620 /// the API layer, and another API function is called, it doesn't need to be
623 /// When a call is recored, its result is always recorded as well, even if the
624 /// function returns a void. For functions that return by value, RecordResult
625 /// should be used. Otherwise a sentinel value (0) will be serialized.
627 /// Because of the functional overlap between logging and recording API calls,
628 /// this class is also used for logging.
631 Recorder(llvm::StringRef pretty_func = {}, std::string &&pretty_args = {});
634 /// Records a single function call.
635 template <typename Result, typename... FArgs, typename... RArgs>
636 void Record(Serializer &serializer, Registry ®istry, Result (*f)(FArgs...),
637 const RArgs &... args) {
638 m_serializer = &serializer;
639 if (!ShouldCapture())
642 unsigned id = registry.GetID(uintptr_t(f));
644 #ifdef LLDB_REPRO_INSTR_TRACE
648 serializer.SerializeAll(id);
649 serializer.SerializeAll(args...);
651 if (std::is_class<typename std::remove_pointer<
652 typename std::remove_reference<Result>::type>::type>::value) {
653 m_result_recorded = false;
655 serializer.SerializeAll(0);
656 m_result_recorded = true;
660 /// Records a single function call.
661 template <typename... Args>
662 void Record(Serializer &serializer, Registry ®istry, void (*f)(Args...),
663 const Args &... args) {
664 m_serializer = &serializer;
665 if (!ShouldCapture())
668 unsigned id = registry.GetID(uintptr_t(f));
670 #ifdef LLDB_REPRO_INSTR_TRACE
674 serializer.SerializeAll(id);
675 serializer.SerializeAll(args...);
678 serializer.SerializeAll(0);
679 m_result_recorded = true;
682 /// Record the result of a function call.
683 template <typename Result> Result RecordResult(Result &&r) {
685 if (m_serializer && ShouldCapture()) {
686 assert(!m_result_recorded);
687 m_serializer->SerializeAll(r);
688 m_result_recorded = true;
690 return std::forward<Result>(r);
694 void UpdateBoundary() {
695 if (m_local_boundary)
696 g_global_boundary = false;
699 bool ShouldCapture() { return m_local_boundary; }
701 #ifdef LLDB_REPRO_INSTR_TRACE
702 void Log(unsigned id) {
703 llvm::errs() << "Recording " << id << ": " << m_pretty_func << " ("
704 << m_pretty_args << ")\n";
708 Serializer *m_serializer;
710 /// Pretty function for logging.
711 llvm::StringRef m_pretty_func;
712 std::string m_pretty_args;
714 /// Whether this function call was the one crossing the API boundary.
715 bool m_local_boundary;
717 /// Whether the return value was recorded explicitly.
718 bool m_result_recorded;
720 /// Whether we're currently across the API boundary.
721 static bool g_global_boundary;
725 } // namespace lldb_private
727 #endif // LLDB_UTILITY_REPRODUCER_INSTRUMENTATION_H