//===--- EnvironmentVariables.h - Debug variables. --------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2020 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 // //===----------------------------------------------------------------------===// // // Debug behavior conditionally enabled using environment variables. // //===----------------------------------------------------------------------===// #include "swift/Runtime/Debug.h" #include "swift/Runtime/EnvironmentVariables.h" #include using namespace swift; namespace { // Require all environment variable names to start with SWIFT_ static constexpr bool hasSwiftPrefix(const char *str) { const char prefix[] = "SWIFT_"; for (unsigned i = 0; i < sizeof(prefix) - 1; i++) if (str[i] != prefix[i]) return false; return true; } #define VARIABLE(name, type, defaultValue, help) \ static_assert(hasSwiftPrefix(#name), "Names must start with SWIFT"); #include "EnvironmentVariables.def" #if SWIFT_STDLIB_HAS_ENVIRON // Value parsers. Add new functions named parse_ to accommodate more // debug variable types. static bool parse_bool(const char *name, const char *value, bool defaultValue) { if (!value) return defaultValue; switch (value[0]) { case 'Y': case 'y': case 'T': case 't': case '1': return true; case 'N': case 'n': case 'F': case 'f': case '0': return false; default: swift::warning(RuntimeErrorFlagNone, "Warning: cannot parse value %s=%s, defaulting to %s.\n", name, value, defaultValue ? "true" : "false"); return defaultValue; } } static uint8_t parse_uint8_t(const char *name, const char *value, uint8_t defaultValue) { if (!value) return defaultValue; char *end; long n = strtol(value, &end, 0); if (*end != '\0') { swift::warning(RuntimeErrorFlagNone, "Warning: cannot parse value %s=%s, defaulting to %u.\n", name, value, defaultValue); return defaultValue; } if (n < 0) { swift::warning(RuntimeErrorFlagNone, "Warning: %s=%s out of bounds, clamping to 0.\n", name, value); return 0; } if (n > UINT8_MAX) { swift::warning(RuntimeErrorFlagNone, "Warning: %s=%s out of bounds, clamping to %d.\n", name, value, UINT8_MAX); return UINT8_MAX; } return n; } // Print a list of all the environment variables. Lazy initialization makes // this a bit odd, but the use of these variables in the metadata system means // it's almost certain to run early. // // The "extra" parameter is printed after the header and before the list of // variables. void printHelp(const char *extra) { swift::warning(RuntimeErrorFlagNone, "Swift runtime debugging:\n"); if (extra) swift::warning(RuntimeErrorFlagNone, "%s\n", extra); #define VARIABLE(name, type, defaultValue, help) \ swift::warning(RuntimeErrorFlagNone, "%7s %s [default: %s] - %s\n", \ #type, #name, #defaultValue, help); #include "EnvironmentVariables.def" swift::warning(RuntimeErrorFlagNone, "SWIFT_DEBUG_HELP=YES - Print this help."); } #endif // SWIFT_STDLIB_HAS_ENVIRON } // end anonymous namespace // Define backing variables. #define VARIABLE(name, type, defaultValue, help) \ type swift::runtime::environment::name ## _variable = defaultValue; #include "EnvironmentVariables.def" // Initialization code. OnceToken_t swift::runtime::environment::initializeToken; #if SWIFT_STDLIB_HAS_ENVIRON && (defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux__)) extern "C" char **environ; #define ENVIRON environ #elif defined(_WIN32) // `_environ` is DLL-imported unless we are linking against the static C runtime // (via `/MT` or `/MTd`). #if defined(_DLL) extern "C" __declspec(dllimport) char **_environ; #else extern "C" char **_environ; #endif // `_environ` is unavailable in the Windows Runtime environment. // https://docs.microsoft.com/en-us/cpp/c-runtime-library/environ-wenviron?view=msvc-160 #if !defined(_WINRT_DLL) #define ENVIRON _environ #endif #endif #ifdef ENVIRON void swift::runtime::environment::initialize(void *context) { // On platforms where we have an environment variable array available, scan it // directly. This optimizes for the common case where no variables are set, // since we only need to perform one scan to set all variables. It also allows // us to detect some spelling mistakes by warning on unknown SWIFT_ variables. bool SWIFT_DEBUG_HELP_variable = false; for (char **var = ENVIRON; *var; var++) { // Immediately skip anything without a SWIFT_ prefix. if (strncmp(*var, "SWIFT_", 6) != 0) continue; bool foundVariable = false; // Check each defined variable in turn, plus SWIFT_DEBUG_HELP. Variables are // parsed by functions named parse_ above. An unknown type will // produce an error that parse_ doesn't exist. Add new parsers // above. #define VARIABLE(name, type, defaultValue, help) \ if (strncmp(*var, #name "=", strlen(#name "=")) == 0) { \ name ## _variable = \ parse_ ## type(#name, *var + strlen(#name "="), defaultValue); \ foundVariable = true; \ } // SWIFT_DEBUG_HELP is not in the variables list. Parse it like the other // variables. VARIABLE(SWIFT_DEBUG_HELP, bool, false, ) #include "EnvironmentVariables.def" // Flag unknown SWIFT_DEBUG_ variables to catch misspellings. We don't flag // all unknown SWIFT_ variables, because there are a bunch of other SWIFT_ // variables used for other purposes, such as SWIFT_SOURCE_ROOT and // SWIFT_INSTALL_DIR, and we don't want to warn for all of those. const char *swiftDebugPrefix = "SWIFT_DEBUG_"; if (!foundVariable && strncmp(*var, swiftDebugPrefix, strlen(swiftDebugPrefix)) == 0) { const char *equals = strchr(*var, '='); if (!equals) equals = *var + strlen(*var); swift::warning(RuntimeErrorFlagNone, "Warning: unknown environment variable %.*s\n", (int)(equals - *var), *var); } } if (SWIFT_DEBUG_HELP_variable) printHelp(nullptr); } #elif SWIFT_STDLIB_HAS_ENVIRON void swift::runtime::environment::initialize(void *context) { // Emit a getenv call for each variable. This is less efficient but works // everywhere. #define VARIABLE(name, type, defaultValue, help) \ name ## _variable = parse_ ## type(#name, getenv(#name), defaultValue); #include "EnvironmentVariables.def" // Print help if requested. if (parse_bool("SWIFT_DEBUG_HELP", getenv("SWIFT_DEBUG_HELP"), false)) printHelp("Using getenv to read variables. Unknown SWIFT_DEBUG_ variables " "will not be flagged."); } #else void swift::runtime::environment::initialize(void *context) { (void)context; } #endif SWIFT_RUNTIME_EXPORT bool swift_COWChecksEnabled() { return runtime::environment::SWIFT_DEBUG_ENABLE_COW_CHECKS(); } SWIFT_RUNTIME_STDLIB_SPI bool concurrencyEnableJobDispatchIntegration() { return runtime::environment:: SWIFT_ENABLE_ASYNC_JOB_DISPATCH_INTEGRATION(); }