summaryrefslogtreecommitdiff
path: root/lldb/include/lldb/Utility/ReproducerInstrumentation.h
blob: ddeacb1b72286f1411d10d627b2175f5faa80a75 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//===-- ReproducerInstrumentation.h -----------------------------*- C++ -*-===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_UTILITY_REPRODUCERINSTRUMENTATION_H
#define LLDB_UTILITY_REPRODUCERINSTRUMENTATION_H

#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/Logging.h"

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorHandling.h"

#include <map>
#include <thread>
#include <type_traits>

template <typename T,
          typename std::enable_if<std::is_fundamental<T>::value, int>::type = 0>
inline void stringify_append(llvm::raw_string_ostream &ss, const T &t) {
  ss << t;
}

template <typename T, typename std::enable_if<!std::is_fundamental<T>::value,
                                              int>::type = 0>
inline void stringify_append(llvm::raw_string_ostream &ss, const T &t) {
  ss << &t;
}

template <typename T>
inline void stringify_append(llvm::raw_string_ostream &ss, T *t) {
  ss << reinterpret_cast<void *>(t);
}

template <typename T>
inline void stringify_append(llvm::raw_string_ostream &ss, const T *t) {
  ss << reinterpret_cast<const void *>(t);
}

template <>
inline void stringify_append<char>(llvm::raw_string_ostream &ss,
                                   const char *t) {
  ss << '\"' << t << '\"';
}

template <>
inline void stringify_append<std::nullptr_t>(llvm::raw_string_ostream &ss,
                                             const std::nullptr_t &t) {
  ss << "\"nullptr\"";
}

template <typename Head>
inline void stringify_helper(llvm::raw_string_ostream &ss, const Head &head) {
  stringify_append(ss, head);
}

template <typename Head, typename... Tail>
inline void stringify_helper(llvm::raw_string_ostream &ss, const Head &head,
                             const Tail &... tail) {
  stringify_append(ss, head);
  ss << ", ";
  stringify_helper(ss, tail...);
}

template <typename... Ts> inline std::string stringify_args(const Ts &... ts) {
  std::string buffer;
  llvm::raw_string_ostream ss(buffer);
  stringify_helper(ss, ts...);
  return ss.str();
}

#define LLDB_CONSTRUCT_(T, Class, ...)                                         \
  lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION);

#define LLDB_RECORD_CONSTRUCTOR(Class, Signature, ...)                         \
  LLDB_CONSTRUCT_(Class Signature, this, __VA_ARGS__)

#define LLDB_RECORD_CONSTRUCTOR_NO_ARGS(Class)                                 \
  LLDB_CONSTRUCT_(Class(), this, lldb_private::repro::EmptyArg())

#define LLDB_RECORD_(T1, T2, ...)                                              \
  lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION,                \
                                          stringify_args(__VA_ARGS__));

#define LLDB_RECORD_METHOD(Result, Class, Method, Signature, ...)              \
  LLDB_RECORD_(Result(Class::*) Signature, (&Class::Method), this, __VA_ARGS__)

#define LLDB_RECORD_METHOD_CONST(Result, Class, Method, Signature, ...)        \
  LLDB_RECORD_(Result(Class::*) Signature const, (&Class::Method), this,       \
               __VA_ARGS__)

#define LLDB_RECORD_METHOD_NO_ARGS(Result, Class, Method)                      \
  LLDB_RECORD_(Result (Class::*)(), (&Class::Method), this)

#define LLDB_RECORD_METHOD_CONST_NO_ARGS(Result, Class, Method)                \
  LLDB_RECORD_(Result (Class::*)() const, (&Class::Method), this)

#define LLDB_RECORD_STATIC_METHOD(Result, Class, Method, Signature, ...)       \
  LLDB_RECORD_(Result(*) Signature, (&Class::Method), __VA_ARGS__)

#define LLDB_RECORD_STATIC_METHOD_NO_ARGS(Result, Class, Method)               \
  LLDB_RECORD_(Result (*)(), (&Class::Method), lldb_private::repro::EmptyArg())

#define LLDB_RECORD_CHAR_PTR_(T1, T2, StrOut, ...)                             \
  lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION,                \
                                          stringify_args(__VA_ARGS__));

#define LLDB_RECORD_CHAR_PTR_METHOD(Result, Class, Method, Signature, StrOut,  \
                                    ...)                                       \
  LLDB_RECORD_CHAR_PTR_(Result(Class::*) Signature, (&Class::Method), StrOut,  \
                        this, __VA_ARGS__)

#define LLDB_RECORD_CHAR_PTR_METHOD_CONST(Result, Class, Method, Signature,    \
                                          StrOut, ...)                         \
  LLDB_RECORD_CHAR_PTR_(Result(Class::*) Signature const, (&Class::Method),    \
                        StrOut, this, __VA_ARGS__)

#define LLDB_RECORD_CHAR_PTR_STATIC_METHOD(Result, Class, Method, Signature,   \
                                           StrOut, ...)                        \
  LLDB_RECORD_CHAR_PTR_(Result(*) Signature, (&Class::Method), StrOut,         \
                        __VA_ARGS__)

/// The LLDB_RECORD_DUMMY macro is special because it doesn't actually record
/// anything. It's used to track API boundaries when we cannot record for
/// technical reasons.
#define LLDB_RECORD_DUMMY(Result, Class, Method, Signature, ...)               \
  lldb_private::repro::Recorder _recorder;

#define LLDB_RECORD_DUMMY_NO_ARGS(Result, Class, Method)                       \
  lldb_private::repro::Recorder _recorder;

namespace lldb_private {
namespace repro {

struct EmptyArg {};

/// RAII object that records function invocations and their return value.
///
/// API calls are only captured when the API boundary is crossed. Once we're in
/// the API layer, and another API function is called, it doesn't need to be
/// recorded.
///
/// When a call is recored, its result is always recorded as well, even if the
/// function returns a void. For functions that return by value, RecordResult
/// should be used. Otherwise a sentinel value (0) will be serialized.
///
/// Because of the functional overlap between logging and recording API calls,
/// this class is also used for logging.
class Recorder {
public:
  Recorder();
  Recorder(llvm::StringRef pretty_func, std::string &&pretty_args = {});
  ~Recorder();

private:
  void UpdateBoundary();

  /// Whether this function call was the one crossing the API boundary.
  bool m_local_boundary = false;
};

} // namespace repro
} // namespace lldb_private

#endif // LLDB_UTILITY_REPRODUCERINSTRUMENTATION_H