Skip to content

Commit b25dab2

Browse files
authored
Merge pull request #32137 from mikeash/debug-environment-variables
[Runtime] Unify debug variable parsing from the environment and avoid getenv when possible.
2 parents a99d7ea + f2fb539 commit b25dab2

8 files changed

+311
-36
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===--- EnvironmentVariables.h - Debug variables. --------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Debug behavior conditionally enabled using environment variables.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "../Basic/Lazy.h"
18+
19+
namespace swift {
20+
namespace runtime {
21+
namespace environment {
22+
23+
void initialize(void *);
24+
25+
extern OnceToken_t initializeToken;
26+
27+
// Declare backing variables.
28+
#define VARIABLE(name, type, defaultValue, help) extern type name ## _variable;
29+
#include "../../../stdlib/public/runtime/EnvironmentVariables.def"
30+
31+
// Define getter functions.
32+
#define VARIABLE(name, type, defaultValue, help) \
33+
inline type name() { \
34+
SWIFT_ONCE_F(initializeToken, initialize, nullptr); \
35+
return name ## _variable; \
36+
}
37+
#include "../../../stdlib/public/runtime/EnvironmentVariables.def"
38+
39+
} // end namespace environment
40+
} // end namespace runtime
41+
} // end namespace Swift

stdlib/public/runtime/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ set(swift_runtime_sources
3434
CygwinPort.cpp
3535
Demangle.cpp
3636
Enum.cpp
37+
EnvironmentVariables.cpp
3738
ErrorObjectCommon.cpp
3839
ErrorObjectConstants.cpp
3940
ErrorObjectNative.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
//===--- EnvironmentVariables.h - Debug variables. --------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Debug behavior conditionally enabled using environment variables.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "swift/Runtime/Debug.h"
18+
#include "swift/Runtime/EnvironmentVariables.h"
19+
20+
#include <string.h>
21+
22+
using namespace swift;
23+
24+
namespace {
25+
26+
// Require all environment variable names to start with SWIFT_
27+
static constexpr bool hasSwiftPrefix(const char *str) {
28+
const char prefix[] = "SWIFT_";
29+
for (unsigned i = 0; i < sizeof(prefix) - 1; i++)
30+
if (str[i] != prefix[i])
31+
return false;
32+
return true;
33+
}
34+
#define VARIABLE(name, type, defaultValue, help) \
35+
static_assert(hasSwiftPrefix(#name), "Names must start with SWIFT");
36+
#include "EnvironmentVariables.def"
37+
38+
// Value parsers. Add new functions named parse_<type> to accommodate more
39+
// debug variable types.
40+
static bool parse_bool(const char *name, const char *value, bool defaultValue) {
41+
if (!value)
42+
return defaultValue;
43+
switch (value[0]) {
44+
case 'Y':
45+
case 'y':
46+
case 'T':
47+
case 't':
48+
case '1':
49+
return true;
50+
case 'N':
51+
case 'n':
52+
case 'F':
53+
case 'f':
54+
case '0':
55+
return false;
56+
default:
57+
swift::warning(RuntimeErrorFlagNone,
58+
"Warning: cannot parse value %s=%s, defaulting to %s.\n",
59+
name, value, defaultValue ? "true" : "false");
60+
return defaultValue;
61+
}
62+
}
63+
64+
static uint8_t parse_uint8_t(const char *name,
65+
const char *value,
66+
uint8_t defaultValue) {
67+
if (!value)
68+
return defaultValue;
69+
char *end;
70+
long n = strtol(value, &end, 0);
71+
if (*end != '\0') {
72+
swift::warning(RuntimeErrorFlagNone,
73+
"Warning: cannot parse value %s=%s, defaulting to %u.\n",
74+
name, value, defaultValue);
75+
return defaultValue;
76+
}
77+
78+
if (n < 0) {
79+
swift::warning(RuntimeErrorFlagNone,
80+
"Warning: %s=%s out of bounds, clamping to 0.\n",
81+
name, value);
82+
return 0;
83+
}
84+
if (n > UINT8_MAX) {
85+
swift::warning(RuntimeErrorFlagNone,
86+
"Warning: %s=%s out of bounds, clamping to %d.\n",
87+
name, value, UINT8_MAX);
88+
return UINT8_MAX;
89+
}
90+
91+
return n;
92+
}
93+
94+
// Print a list of all the environment variables. Lazy initialization makes
95+
// this a bit odd, but the use of these variables in the metadata system means
96+
// it's almost certain to run early.
97+
//
98+
// The "extra" parameter is printed after the header and before the list of
99+
// variables.
100+
void printHelp(const char *extra) {
101+
swift::warning(RuntimeErrorFlagNone, "Swift runtime debugging:\n");
102+
if (extra)
103+
swift::warning(RuntimeErrorFlagNone, "%s\n", extra);
104+
#define VARIABLE(name, type, defaultValue, help) \
105+
swift::warning(RuntimeErrorFlagNone, "%7s %s [default: %s] - %s\n", \
106+
#type, #name, #defaultValue, help);
107+
#include "EnvironmentVariables.def"
108+
swift::warning(RuntimeErrorFlagNone, "SWIFT_DEBUG_HELP=YES - Print this help.");
109+
}
110+
111+
} // end anonymous namespace
112+
113+
// Define backing variables.
114+
#define VARIABLE(name, type, defaultValue, help) \
115+
type swift::runtime::environment::name ## _variable = defaultValue;
116+
#include "EnvironmentVariables.def"
117+
118+
// Initialization code.
119+
OnceToken_t swift::runtime::environment::initializeToken;
120+
121+
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
122+
extern "C" char **environ;
123+
#define ENVIRON environ
124+
#elif defined(_WIN32)
125+
extern "C" char **_environ;
126+
#define ENVIRON _environ
127+
#endif
128+
129+
#ifdef ENVIRON
130+
void swift::runtime::environment::initialize(void *context) {
131+
// On platforms where we have an environment variable array available, scan it
132+
// directly. This optimizes for the common case where no variables are set,
133+
// since we only need to perform one scan to set all variables. It also allows
134+
// us to detect some spelling mistakes by warning on unknown SWIFT_ variables.
135+
136+
bool SWIFT_DEBUG_HELP_variable = false;
137+
for (char **var = ENVIRON; *var; var++) {
138+
// Immediately skip anything without a SWIFT_ prefix.
139+
if (strncmp(*var, "SWIFT_", 6) != 0)
140+
continue;
141+
142+
bool foundVariable = false;
143+
// Check each defined variable in turn, plus SWIFT_DEBUG_HELP. Variables are
144+
// parsed by functions named parse_<type> above. An unknown type will
145+
// produce an error that parse_<unknown-type> doesn't exist. Add new parsers
146+
// above.
147+
#define VARIABLE(name, type, defaultValue, help) \
148+
if (strncmp(*var, #name "=", strlen(#name "=")) == 0) { \
149+
name ## _variable = \
150+
parse_ ## type(#name, *var + strlen(#name "="), defaultValue); \
151+
foundVariable = true; \
152+
}
153+
// SWIFT_DEBUG_HELP is not in the variables list. Parse it like the other
154+
// variables.
155+
VARIABLE(SWIFT_DEBUG_HELP, bool, false, )
156+
#include "EnvironmentVariables.def"
157+
158+
// Flag unknown SWIFT_DEBUG_ variables to catch misspellings. We don't flag
159+
// all unknown SWIFT_ variables, because there are a bunch of other SWIFT_
160+
// variables used for other purposes, such as SWIFT_SOURCE_ROOT and
161+
// SWIFT_INSTALL_DIR, and we don't want to warn for all of those.
162+
const char *swiftDebugPrefix = "SWIFT_DEBUG_";
163+
if (!foundVariable &&
164+
strncmp(*var, swiftDebugPrefix, strlen(swiftDebugPrefix)) == 0) {
165+
const char *equals = strchr(*var, '=');
166+
if (!equals)
167+
equals = *var + strlen(*var);
168+
swift::warning(RuntimeErrorFlagNone,
169+
"Warning: unknown environment variable %.*s\n",
170+
(int)(equals - *var), *var);
171+
}
172+
}
173+
174+
if (SWIFT_DEBUG_HELP_variable)
175+
printHelp(nullptr);
176+
}
177+
#else
178+
void swift::runtime::environment::initialize(void *context) {
179+
// Emit a getenv call for each variable. This is less efficient but works
180+
// everywhere.
181+
#define VARIABLE(name, type, defaultValue, help) \
182+
name ## _variable = parse_ ## type(#name, getenv(#name), defaultValue);
183+
#include "EnvironmentVariables.def"
184+
185+
// Print help if requested.
186+
if (parse_bool("SWIFT_DEBUG_HELP", getenv("SWIFT_DEBUG_HELP"), false))
187+
printHelp("Using getenv to read variables. Unknown SWIFT_DEBUG_ variables "
188+
"will not be flagged.");
189+
}
190+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===--- EnvironmentVariables.def - Debug variables. ------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines x-macros used for metaprogramming with the set of
14+
// environment variables used for configuring or enabling debug features in the
15+
// runtime.
16+
//
17+
//===----------------------------------------------------------------------===//
18+
19+
// #define VARIABLE(name, type, defaultValue, help)
20+
21+
#ifndef VARIABLE
22+
#error "Must define VARIABLE to include EnvironmentVariables.def"
23+
#endif
24+
25+
VARIABLE(SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION, bool, false,
26+
"Enable additional metadata allocation tracking for swift-inspect to "
27+
"use.")
28+
29+
VARIABLE(SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT, uint8_t, 2,
30+
"Print warnings when using implicit @objc entrypoints. Set to "
31+
"desired reporting level, 0-3.")
32+
33+
VARIABLE(SWIFT_DETERMINISTIC_HASHING, bool, false,
34+
"Disable randomized hash seeding.")
35+
36+
VARIABLE(SWIFT_ENABLE_MANGLED_NAME_VERIFICATION, bool, false,
37+
"Enable verification that metadata can roundtrip through a mangled "
38+
"name each time metadata is instantiated.")
39+
40+
#undef VARIABLE

stdlib/public/runtime/Metadata.cpp

+18-19
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/Demangling/Demangler.h"
2222
#include "swift/ABI/TypeIdentity.h"
2323
#include "swift/Runtime/Casting.h"
24+
#include "swift/Runtime/EnvironmentVariables.h"
2425
#include "swift/Runtime/ExistentialContainer.h"
2526
#include "swift/Runtime/HeapObject.h"
2627
#include "swift/Runtime/Mutex.h"
@@ -5504,21 +5505,21 @@ bool swift::_swift_debug_metadataAllocationIterationEnabled = false;
55045505
const void * const swift::_swift_debug_allocationPoolPointer = &AllocationPool;
55055506

55065507
static void checkAllocatorDebugEnvironmentVariable(void *context) {
5507-
const char *value =
5508-
getenv("SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION");
5509-
if (value && (value[0] == '1' || value[0] == 'y' || value[0] == 'Y')) {
5510-
_swift_debug_metadataAllocationIterationEnabled = true;
5511-
// Write a PoolTrailer to the end of InitialAllocationPool and shrink
5512-
// the pool accordingly.
5513-
auto poolCopy = AllocationPool.load(std::memory_order_relaxed);
5514-
assert(poolCopy.Begin == InitialAllocationPool.Pool);
5515-
size_t newPoolSize = InitialPoolSize - sizeof(PoolTrailer);
5516-
PoolTrailer trailer = { nullptr, newPoolSize };
5517-
memcpy(InitialAllocationPool.Pool + newPoolSize, &trailer,
5518-
sizeof(trailer));
5519-
poolCopy.Remaining = newPoolSize;
5520-
AllocationPool.store(poolCopy, std::memory_order_relaxed);
5521-
}
5508+
_swift_debug_metadataAllocationIterationEnabled
5509+
= runtime::environment::SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION();
5510+
if (!_swift_debug_metadataAllocationIterationEnabled)
5511+
return;
5512+
5513+
// Write a PoolTrailer to the end of InitialAllocationPool and shrink
5514+
// the pool accordingly.
5515+
auto poolCopy = AllocationPool.load(std::memory_order_relaxed);
5516+
assert(poolCopy.Begin == InitialAllocationPool.Pool);
5517+
size_t newPoolSize = InitialPoolSize - sizeof(PoolTrailer);
5518+
PoolTrailer trailer = { nullptr, newPoolSize };
5519+
memcpy(InitialAllocationPool.Pool + newPoolSize, &trailer,
5520+
sizeof(trailer));
5521+
poolCopy.Remaining = newPoolSize;
5522+
AllocationPool.store(poolCopy, std::memory_order_relaxed);
55225523
}
55235524

55245525
void *MetadataAllocator::Allocate(size_t size, size_t alignment) {
@@ -5654,10 +5655,8 @@ void swift::verifyMangledNameRoundtrip(const Metadata *metadata) {
56545655
// variable lets us easily turn on verification to find and fix these
56555656
// bugs. Remove this and leave it permanently on once everything works
56565657
// with it enabled.
5657-
bool verificationEnabled =
5658-
SWIFT_LAZY_CONSTANT((bool)getenv("SWIFT_ENABLE_MANGLED_NAME_VERIFICATION"));
5659-
5660-
if (!verificationEnabled) return;
5658+
if (!swift::runtime::environment::SWIFT_ENABLE_MANGLED_NAME_VERIFICATION())
5659+
return;
56615660

56625661
Demangle::StackAllocatedDemangler<1024> Dem;
56635662
auto node = _swift_buildDemanglingForMetadata(metadata, Dem);

stdlib/public/runtime/SwiftObject.mm

+3-15
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/ADT/StringRef.h"
2727
#include "swift/Basic/Lazy.h"
2828
#include "swift/Runtime/Casting.h"
29+
#include "swift/Runtime/EnvironmentVariables.h"
2930
#include "swift/Runtime/Heap.h"
3031
#include "swift/Runtime/HeapObject.h"
3132
#include "swift/Runtime/Metadata.h"
@@ -1478,22 +1479,9 @@ void swift_objc_swift3ImplicitObjCEntrypoint(id self, SEL selector,
14781479
// if possible.
14791480
// 3: Complain about uses of implicit @objc entrypoints, then abort().
14801481
//
1481-
// The actual reportLevel is stored as the above values +1, so that
1482-
// 0 indicates we have not yet checked. It's fine to race through here.
1483-
//
14841482
// The default, if SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT is not set, is 2.
1485-
static int storedReportLevel = 0;
1486-
if (storedReportLevel == 0) {
1487-
auto reportLevelStr = getenv("SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT");
1488-
if (reportLevelStr &&
1489-
reportLevelStr[0] >= '0' && reportLevelStr[0] <= '3' &&
1490-
reportLevelStr[1] == 0)
1491-
storedReportLevel = (reportLevelStr[0] - '0') + 1;
1492-
else
1493-
storedReportLevel = 3;
1494-
}
1495-
1496-
int reportLevel = storedReportLevel - 1;
1483+
uint8_t reportLevel =
1484+
runtime::environment::SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT();
14971485
if (reportLevel < 1) return;
14981486

14991487
// Report the error.

stdlib/public/stubs/GlobalObjects.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "../SwiftShims/Random.h"
2121
#include "swift/Runtime/Metadata.h"
2222
#include "swift/Runtime/Debug.h"
23+
#include "swift/Runtime/EnvironmentVariables.h"
2324
#include <stdlib.h>
2425

2526
namespace swift {
@@ -113,8 +114,7 @@ static swift::_SwiftHashingParameters initializeHashingParameters() {
113114
// results are repeatable, e.g., in certain test environments. (Note that
114115
// even if the seed override is enabled, hash values aren't guaranteed to
115116
// remain stable across even minor stdlib releases.)
116-
auto determinism = getenv("SWIFT_DETERMINISTIC_HASHING");
117-
if (determinism && 0 == strcmp(determinism, "1")) {
117+
if (swift::runtime::environment::SWIFT_DETERMINISTIC_HASHING()) {
118118
return { 0, 0, true };
119119
}
120120
__swift_uint64_t seed0 = 0, seed1 = 0;

0 commit comments

Comments
 (0)