-
Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathRuntimeInvocationsTracking.cpp
241 lines (209 loc) · 10.2 KB
/
RuntimeInvocationsTracking.cpp
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
//===--- RuntimeInvocationsTracking.cpp - Track runtime invocations -------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Track invocations of Swift runtime functions. This can be used for performance
// analysis.
//
//===----------------------------------------------------------------------===//
#include <cstdint>
#include "RuntimeInvocationsTracking.h"
#include "swift/Basic/Lazy.h"
#include "swift/Runtime/HeapObject.h"
#include "swift/Threading/Mutex.h"
#include "llvm/ADT/DenseMap.h"
#if defined(SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS)
#define SWIFT_RT_FUNCTION_INVOCATION_COUNTER_NAME(RT_FUNCTION) \
invocationCounter_##RT_FUNCTION
namespace swift {
// Define counters used for tracking the total number of invocations of runtime
// functions.
struct RuntimeFunctionCountersState {
#define FUNCTION_TO_TRACK(RT_FUNCTION) \
std::uint32_t SWIFT_RT_FUNCTION_INVOCATION_COUNTER_NAME(RT_FUNCTION) = 0;
// Provide one counter per runtime function being tracked.
#include "RuntimeInvocationsTracking.def"
};
} // end namespace swift
/// If set, global runtime function counters should be tracked.
static bool UpdatePerObjectRuntimeFunctionCounters = false;
/// If set, per object runtime function counters should be tracked.
static bool UpdateGlobalRuntimeFunctionCounters = false;
/// TODO: Add support for enabling/disabling counters on a per object basis?
/// Global set of counters tracking the total number of runtime invocations.
struct RuntimeFunctionCountersStateSentinel {
RuntimeFunctionCountersState State;
LazyMutex Lock;
};
static RuntimeFunctionCountersStateSentinel RuntimeGlobalFunctionCountersState;
/// The object state cache mapping objects to the collected state associated with
/// them.
struct RuntimeObjectCacheSentinel {
llvm::DenseMap<HeapObject *, RuntimeFunctionCountersState> Cache;
Mutex Lock;
};
static Lazy<RuntimeObjectCacheSentinel> RuntimeObjectStateCache;
static const char *RuntimeFunctionNames[] {
/// Define names of runtime functions.
#define FUNCTION_TO_TRACK(RT_FUNCTION) #RT_FUNCTION,
#include "RuntimeInvocationsTracking.def"
nullptr
};
#define RT_FUNCTION_ID(RT_FUNCTION) ID_##RT_FUNCTION
/// Define an enum where each enumerator corresponds to a runtime function being
/// tracked. Their order is the same as the order of the counters in the
/// RuntimeObjectState structure.
enum RuntimeFunctionNamesIDs : std::uint32_t {
/// Defines names of enum cases for each function being tracked.
#define FUNCTION_TO_TRACK(RT_FUNCTION) RT_FUNCTION_ID(RT_FUNCTION),
#include "RuntimeInvocationsTracking.def"
ID_LastRuntimeFunctionName,
};
/// The global handler to be invoked on runtime function counters updates.
static RuntimeFunctionCountersUpdateHandler
GlobalRuntimeFunctionCountersUpdateHandler;
/// The offsets of the runtime function counters being tracked inside the
/// RuntimeObjectState structure. The array is indexed by
/// the enumerators from RuntimeFunctionNamesIDs.
static std::uint16_t RuntimeFunctionCountersOffsets[] = {
/// Define offset for each function being tracked.
#define FUNCTION_TO_TRACK(RT_FUNCTION) \
(sizeof(std::uint16_t) * (unsigned)RT_FUNCTION_ID(RT_FUNCTION)),
#include "RuntimeInvocationsTracking.def"
};
/// Define implementations of tracking functions.
/// TODO: Track only objects that were registered for tracking?
/// TODO: Perform atomic increments?
#define FUNCTION_TO_TRACK(RT_FUNCTION) \
void SWIFT_RT_TRACK_INVOCATION_NAME(RT_FUNCTION)(HeapObject * object) { \
/* Update global counters. */ \
if (UpdateGlobalRuntimeFunctionCounters) { \
LazyMutex::ScopedLock lock(RuntimeGlobalFunctionCountersState.Lock); \
RuntimeGlobalFunctionCountersState.State \
.SWIFT_RT_FUNCTION_INVOCATION_COUNTER_NAME(RT_FUNCTION)++; \
if (GlobalRuntimeFunctionCountersUpdateHandler) { \
auto oldGlobalMode = _swift_setGlobalRuntimeFunctionCountersMode(0); \
auto oldPerObjectMode = \
_swift_setPerObjectRuntimeFunctionCountersMode(0); \
GlobalRuntimeFunctionCountersUpdateHandler( \
object, RT_FUNCTION_ID(RT_FUNCTION)); \
_swift_setGlobalRuntimeFunctionCountersMode(oldGlobalMode); \
_swift_setPerObjectRuntimeFunctionCountersMode(oldPerObjectMode); \
} \
} \
/* Update per object counters. */ \
if (UpdatePerObjectRuntimeFunctionCounters && object) { \
auto &theSentinel = RuntimeObjectStateCache.get(); \
Mutex::ScopedLock lock(theSentinel.Lock); \
theSentinel.Cache[object].SWIFT_RT_FUNCTION_INVOCATION_COUNTER_NAME( \
RT_FUNCTION)++; \
/* TODO: Remember the order/history of operations? */ \
} \
}
#include "RuntimeInvocationsTracking.def"
/// Public APIs
/// Get the runtime object state associated with an object.
void _swift_getObjectRuntimeFunctionCounters(
HeapObject *object, RuntimeFunctionCountersState *result) {
auto &theSentinel = RuntimeObjectStateCache.get();
Mutex::ScopedLock lock(theSentinel.Lock);
*result = theSentinel.Cache[object];
}
/// Set the runtime object state associated with an object from a provided
/// state.
void _swift_setObjectRuntimeFunctionCounters(
HeapObject *object, RuntimeFunctionCountersState *state) {
auto &theSentinel = RuntimeObjectStateCache.get();
Mutex::ScopedLock lock(theSentinel.Lock);
theSentinel.Cache[object] = *state;
}
/// Get the global runtime state containing the total numbers of invocations for
/// each runtime function of interest.
void _swift_getGlobalRuntimeFunctionCounters(
RuntimeFunctionCountersState *result) {
LazyMutex::ScopedLock lock(RuntimeGlobalFunctionCountersState.Lock);
*result = RuntimeGlobalFunctionCountersState.State;
}
/// Set the global runtime state of function pointers from a provided state.
void _swift_setGlobalRuntimeFunctionCounters(
RuntimeFunctionCountersState *state) {
LazyMutex::ScopedLock lock(RuntimeGlobalFunctionCountersState.Lock);
RuntimeGlobalFunctionCountersState.State = *state;
}
/// Return the names of the runtime functions being tracked.
/// Their order is the same as the order of the counters in the
/// RuntimeObjectState structure. All these strings are null terminated.
const char **_swift_getRuntimeFunctionNames() {
return RuntimeFunctionNames;
}
/// Return the offsets of the runtime function counters being tracked.
/// Their order is the same as the order of the counters in the
/// RuntimeObjectState structure.
const std::uint16_t *_swift_getRuntimeFunctionCountersOffsets() {
return RuntimeFunctionCountersOffsets;
}
/// Return the number of runtime functions being tracked.
std::uint64_t _swift_getNumRuntimeFunctionCounters() {
return ID_LastRuntimeFunctionName;
}
static void _swift_dumpRuntimeCounters(RuntimeFunctionCountersState *State) {
std::uint32_t tmp;
/// Define how to dump the counter for a given runtime function.
#define FUNCTION_TO_TRACK(RT_FUNCTION) \
tmp = State->SWIFT_RT_FUNCTION_INVOCATION_COUNTER_NAME(RT_FUNCTION); \
if (tmp != 0) \
printf("%s = %d\n", \
RuntimeFunctionNames[(int)RT_FUNCTION_ID(RT_FUNCTION)], tmp);
#include "RuntimeInvocationsTracking.def"
}
/// Dump all per-object runtime function pointers.
void _swift_dumpObjectsRuntimeFunctionPointers() {
auto &theSentinel = RuntimeObjectStateCache.get();
Mutex::ScopedLock lock(theSentinel.Lock);
for (auto &Pair : theSentinel.Cache) {
printf("\n\nRuntime counters for object at address %p:\n", Pair.getFirst());
_swift_dumpRuntimeCounters(&Pair.getSecond());
printf("\n");
}
}
/// Set mode for global runtime function counters.
/// Return the old value of this flag.
int _swift_setGlobalRuntimeFunctionCountersMode(int mode) {
int oldMode = UpdateGlobalRuntimeFunctionCounters;
UpdateGlobalRuntimeFunctionCounters = mode ? 1 : 0;
return oldMode;
}
/// Set mode for per object runtime function counters.
/// Return the old value of this flag.
int _swift_setPerObjectRuntimeFunctionCountersMode(int mode) {
int oldMode = UpdatePerObjectRuntimeFunctionCounters;
UpdatePerObjectRuntimeFunctionCounters = mode ? 1 : 0;
return oldMode;
}
/// Add the ability to call custom handlers when a counter
/// is being updated. The handler should take the object and may be
/// the name of the runtime function as parameters. And this handler
/// could e.g. check some conditions and stop the program under
/// a debugger if a certain condition is met, like a refcount has
/// reached a certain value.
/// We could allow for setting global handlers or even per-object
/// handlers.
RuntimeFunctionCountersUpdateHandler
_swift_setGlobalRuntimeFunctionCountersUpdateHandler(
RuntimeFunctionCountersUpdateHandler handler) {
auto oldHandler = GlobalRuntimeFunctionCountersUpdateHandler;
GlobalRuntimeFunctionCountersUpdateHandler = handler;
return oldHandler;
}
/// TODO: Provide an API to remove any counters related to a specific object
/// or all objects.
/// This is useful if you want to reset the stats for some/all objects.
#endif