Skip to content

Commit 8429d0d

Browse files
committed
[exclusivity] Introduce an env variable to in asserts builds cause the exclusivity part of the runtime to emit logging information.
The specific environment variable is SWIFT_DEBUG_RUNTIME_EXCLUSIVITY_LOGGING. We only check the environment the first time we try to lookup the TLSContext, so it shouldn't impact exclusivity performance. My intention is to use this to write some FileCheck tests so that we can really be sure that the runtime is doing what we think it is. As such I also changed the small amount of logging routines meant for use in the debugger to use stdout. (cherry picked from commit f79108c)
1 parent 6247a7c commit 8429d0d

File tree

2 files changed

+133
-5
lines changed

2 files changed

+133
-5
lines changed

stdlib/public/runtime/EnvironmentVariables.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,11 @@ VARIABLE(SWIFT_DEBUG_ENABLE_SHARED_CACHE_PROTOCOL_CONFORMANCES, bool, true,
6262

6363
#endif
6464

65+
#ifndef NDEBUG
66+
67+
VARIABLE(SWIFT_DEBUG_RUNTIME_EXCLUSIVITY_LOGGING, bool, false,
68+
"Enable the an asserts runtime to emit logging as it works.")
69+
70+
#endif
71+
6572
#undef VARIABLE

stdlib/public/runtime/Exclusivity.cpp

Lines changed: 126 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@
2727
#include "swift/Basic/Lazy.h"
2828
#include "swift/Runtime/Config.h"
2929
#include "swift/Runtime/Debug.h"
30+
#include "swift/Runtime/EnvironmentVariables.h"
3031
#include "swift/Runtime/Metadata.h"
3132
#include "swift/Runtime/ThreadLocalStorage.h"
32-
#include <memory>
3333
#include <inttypes.h>
34+
#include <memory>
3435
#include <stdio.h>
3536

3637
// Pick a return-address strategy
@@ -56,6 +57,16 @@ static const char *getAccessName(ExclusivityFlags flags) {
5657
}
5758
}
5859

60+
// In asserts builds if the environment variable
61+
// SWIFT_DEBUG_RUNTIME_EXCLUSIVITY_LOGGING is set, emit logging information.
62+
#ifndef NDEBUG
63+
64+
static inline bool isExclusivityLoggingEnabled() {
65+
return runtime::environment::SWIFT_DEBUG_RUNTIME_EXCLUSIVITY_LOGGING();
66+
}
67+
68+
#endif
69+
5970
SWIFT_ALWAYS_INLINE
6071
static void reportExclusivityConflict(ExclusivityFlags oldAction, void *oldPC,
6172
ExclusivityFlags newFlags, void *newPC,
@@ -179,6 +190,10 @@ class AccessSet {
179190
constexpr bool isHead(Access *access) const { return Head == access; }
180191

181192
bool insert(Access *access, void *pc, void *pointer, ExclusivityFlags flags) {
193+
#ifndef NDEBUG
194+
if (isExclusivityLoggingEnabled())
195+
fprintf(stderr, "Inserting new access: %p\n", access);
196+
#endif
182197
auto action = getAccessAction(flags);
183198

184199
for (Access *cur = Head; cur != nullptr; cur = cur->getNext()) {
@@ -198,17 +213,33 @@ class AccessSet {
198213
// 0 means no backtrace will be printed.
199214
fatalError(0, "Fatal access conflict detected.\n");
200215
}
201-
if (!isTracking(flags))
216+
if (!isTracking(flags)) {
217+
#ifndef NDEBUG
218+
if (isExclusivityLoggingEnabled()) {
219+
fprintf(stderr, " Not tracking!\n");
220+
}
221+
#endif
202222
return false;
223+
}
203224

204225
// Insert to the front of the array so that remove tends to find it faster.
205226
access->initialize(pc, pointer, Head, action);
206227
Head = access;
228+
#ifndef NDEBUG
229+
if (isExclusivityLoggingEnabled()) {
230+
fprintf(stderr, " Tracking!\n");
231+
swift_dumpTrackedAccesses();
232+
}
233+
#endif
207234
return true;
208235
}
209236

210237
void remove(Access *access) {
211238
assert(Head && "removal from empty AccessSet");
239+
#ifndef NDEBUG
240+
if (isExclusivityLoggingEnabled())
241+
fprintf(stderr, "Removing access: %p\n", access);
242+
#endif
212243
auto cur = Head;
213244
// Fast path: stack discipline.
214245
if (cur == access) {
@@ -419,9 +450,14 @@ char *swift::swift_getOrigOfReplaceable(char **OrigFnPtr) {
419450
//
420451
// This is only intended to be used in the debugger.
421452
void swift::swift_dumpTrackedAccesses() {
422-
getTLSContext().accessSet.forEach([](Access *a) {
423-
fprintf(stderr, "Access. Pointer: %p. PC: %p. AccessAction: %s\n",
424-
a->Pointer, a->PC, getAccessName(a->getAccessAction()));
453+
auto &accessSet = getTLSContext().accessSet;
454+
if (!accessSet) {
455+
fprintf(stderr, " No Accesses.\n");
456+
return;
457+
}
458+
accessSet.forEach([](Access *a) {
459+
fprintf(stderr, " Access. Pointer: %p. PC: %p. AccessAction: %s\n",
460+
a->Pointer, a->PC, getAccessName(a->getAccessAction()));
425461
});
426462
}
427463

@@ -617,6 +653,15 @@ namespace {
617653
struct SwiftTaskThreadLocalContext {
618654
uintptr_t state[2];
619655

656+
#ifndef NDEBUG
657+
void dump() {
658+
fprintf(stderr,
659+
" SwiftTaskThreadLocalContext: (FirstAccess,LastAccess): "
660+
"(%p, %p)\n",
661+
(void *)state[0], (void *)state[1]);
662+
}
663+
#endif
664+
620665
bool hasInitialAccessSet() const {
621666
// If state[0] is nullptr, we have an initial access set.
622667
return bool(state[0]);
@@ -633,6 +678,25 @@ struct SwiftTaskThreadLocalContext {
633678
void setTaskAccessSetHead(Access *newHead) { state[0] = uintptr_t(newHead); }
634679

635680
void setTaskAccessSetTail(Access *newTail) { state[1] = uintptr_t(newTail); }
681+
682+
#ifndef NDEBUG
683+
const char *getTaskAddress() const {
684+
// Constant only used when we have an asserts compiler so that we can output
685+
// exactly the header location of the task for FileCheck purposes.
686+
//
687+
// WARNING: This test will fail if the Task ABI changes. When that happens,
688+
// update the offset!
689+
//
690+
// TODO: This probably will need 32 bit help.
691+
#if __POINTER_WIDTH__ == 64
692+
unsigned taskHeadOffsetFromTaskAccessSet = 128;
693+
#else
694+
unsigned taskHeadOffsetFromTaskAccessSet = 68;
695+
#endif
696+
auto *self = reinterpret_cast<const char *>(this);
697+
return self - taskHeadOffsetFromTaskAccessSet;
698+
}
699+
#endif
636700
};
637701

638702
} // end anonymous namespace
@@ -642,6 +706,29 @@ void swift::swift_task_enterThreadLocalContext(char *state) {
642706
auto &taskCtx = *reinterpret_cast<SwiftTaskThreadLocalContext *>(state);
643707
auto &tlsCtxAccessSet = getTLSContext().accessSet;
644708

709+
#ifndef NDEBUG
710+
if (isExclusivityLoggingEnabled()) {
711+
fprintf(stderr,
712+
"Entering Thread Local Context. Before Swizzle. Task: %p\n",
713+
taskCtx.getTaskAddress());
714+
taskCtx.dump();
715+
swift_dumpTrackedAccesses();
716+
}
717+
718+
auto logEndState = [&] {
719+
if (isExclusivityLoggingEnabled()) {
720+
fprintf(stderr,
721+
"Entering Thread Local Context. After Swizzle. Task: %p\n",
722+
taskCtx.getTaskAddress());
723+
taskCtx.dump();
724+
swift_dumpTrackedAccesses();
725+
}
726+
};
727+
#else
728+
// Just a no-op that should inline away.
729+
auto logEndState = [] {};
730+
#endif
731+
645732
// First handle all of the cases where our task does not start without an
646733
// initial access set.
647734
//
@@ -653,6 +740,7 @@ void swift::swift_task_enterThreadLocalContext(char *state) {
653740
//
654741
// Handles push cases 1-2.
655742
if (!tlsCtxAccessSet) {
743+
logEndState();
656744
return;
657745
}
658746

@@ -663,6 +751,7 @@ void swift::swift_task_enterThreadLocalContext(char *state) {
663751
//
664752
// Handles push cases 3-4.
665753
taskCtx.setTaskAccessSetHead(tlsCtxAccessSet.getHead());
754+
logEndState();
666755
return;
667756
}
668757

@@ -679,6 +768,7 @@ void swift::swift_task_enterThreadLocalContext(char *state) {
679768
tlsCtxAccessSet = taskCtx.getTaskAccessSetHead();
680769
taskCtx.setTaskAccessSetHead(nullptr);
681770
taskCtx.setTaskAccessSetTail(nullptr);
771+
logEndState();
682772
return;
683773
}
684774

@@ -694,13 +784,40 @@ void swift::swift_task_enterThreadLocalContext(char *state) {
694784
tail->setNext(oldHead);
695785
taskCtx.setTaskAccessSetHead(oldHead);
696786
taskCtx.setTaskAccessSetTail(nullptr);
787+
logEndState();
697788
}
698789

699790
// See algorithm description on SwiftTaskThreadLocalContext.
700791
void swift::swift_task_exitThreadLocalContext(char *state) {
701792
auto &taskCtx = *reinterpret_cast<SwiftTaskThreadLocalContext *>(state);
702793
auto &tlsCtxAccessSet = getTLSContext().accessSet;
703794

795+
#ifndef NDEBUG
796+
if (isExclusivityLoggingEnabled()) {
797+
fprintf(stderr,
798+
"Exiting Thread Local Context. Before Swizzle. Task: %p\n",
799+
taskCtx.getTaskAddress());
800+
taskCtx.dump();
801+
swift_dumpTrackedAccesses();
802+
}
803+
804+
auto logEndState = [&] {
805+
if (isExclusivityLoggingEnabled()) {
806+
fprintf(stderr,
807+
"Exiting Thread Local Context. After Swizzle. Task: %p\n",
808+
taskCtx.getTaskAddress());
809+
taskCtx.dump();
810+
swift_dumpTrackedAccesses();
811+
}
812+
};
813+
#else
814+
// If we are not compiling with asserts, just use a simple identity function
815+
// that should be inlined away.
816+
//
817+
// TODO: Can we use defer in the runtime?
818+
auto logEndState = [] {};
819+
#endif
820+
704821
// First check our ctx to see if we were tracking a previous synchronous
705822
// head. If we don't then we know that our synchronous thread was not
706823
// initially tracking any accesses.
@@ -719,6 +836,7 @@ void swift::swift_task_exitThreadLocalContext(char *state) {
719836
if (!tlsCtxAccessSet) {
720837
assert(taskCtx.getTaskAccessSetTail() == nullptr &&
721838
"Make sure we set this to nullptr when we pushed");
839+
logEndState();
722840
return;
723841
}
724842

@@ -735,6 +853,7 @@ void swift::swift_task_exitThreadLocalContext(char *state) {
735853
tlsCtxAccessSet = nullptr;
736854
taskCtx.setTaskAccessSetHead(newHead);
737855
taskCtx.setTaskAccessSetTail(newTail);
856+
logEndState();
738857
return;
739858
}
740859

@@ -752,6 +871,7 @@ void swift::swift_task_exitThreadLocalContext(char *state) {
752871
if (tlsCtxAccessSet.getHead() == oldHead) {
753872
taskCtx.setTaskAccessSetHead(nullptr);
754873
taskCtx.setTaskAccessSetTail(nullptr);
874+
logEndState();
755875
return;
756876
}
757877

@@ -770,4 +890,5 @@ void swift::swift_task_exitThreadLocalContext(char *state) {
770890
newEnd->setNext(nullptr);
771891
taskCtx.setTaskAccessSetHead(newHead);
772892
taskCtx.setTaskAccessSetTail(newEnd);
893+
logEndState();
773894
}

0 commit comments

Comments
 (0)