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
|