diff --git a/include/swift/Runtime/EnvironmentVariables.h b/include/swift/Runtime/EnvironmentVariables.h new file mode 100644 index 0000000000000..affcbd07baff8 --- /dev/null +++ b/include/swift/Runtime/EnvironmentVariables.h @@ -0,0 +1,41 @@ +//===--- 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 "../Basic/Lazy.h" + +namespace swift { +namespace runtime { +namespace environment { + +void initialize(void *); + +extern OnceToken_t initializeToken; + +// Declare backing variables. +#define VARIABLE(name, type, defaultValue, help) extern type name ## _variable; +#include "../../../stdlib/public/runtime/EnvironmentVariables.def" + +// Define getter functions. +#define VARIABLE(name, type, defaultValue, help) \ + inline type name() { \ + SWIFT_ONCE_F(initializeToken, initialize, nullptr); \ + return name ## _variable; \ + } +#include "../../../stdlib/public/runtime/EnvironmentVariables.def" + +} // end namespace environment +} // end namespace runtime +} // end namespace Swift diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index c73961205828d..db829a67538b7 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -34,6 +34,7 @@ set(swift_runtime_sources CygwinPort.cpp Demangle.cpp Enum.cpp + EnvironmentVariables.cpp ErrorObjectCommon.cpp ErrorObjectConstants.cpp ErrorObjectNative.cpp diff --git a/stdlib/public/runtime/EnvironmentVariables.cpp b/stdlib/public/runtime/EnvironmentVariables.cpp new file mode 100644 index 0000000000000..dfaf099d8a16f --- /dev/null +++ b/stdlib/public/runtime/EnvironmentVariables.cpp @@ -0,0 +1,190 @@ +//===--- 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" + +// 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."); +} + +} // 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 defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) +extern "C" char **environ; +#define ENVIRON environ +#elif defined(_WIN32) +extern "C" char **_environ; +#define ENVIRON _environ +#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); +} +#else +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."); +} +#endif diff --git a/stdlib/public/runtime/EnvironmentVariables.def b/stdlib/public/runtime/EnvironmentVariables.def new file mode 100644 index 0000000000000..093c43f04f6fe --- /dev/null +++ b/stdlib/public/runtime/EnvironmentVariables.def @@ -0,0 +1,40 @@ +//===--- EnvironmentVariables.def - 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines x-macros used for metaprogramming with the set of +// environment variables used for configuring or enabling debug features in the +// runtime. +// +//===----------------------------------------------------------------------===// + +// #define VARIABLE(name, type, defaultValue, help) + +#ifndef VARIABLE +#error "Must define VARIABLE to include EnvironmentVariables.def" +#endif + +VARIABLE(SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION, bool, false, + "Enable additional metadata allocation tracking for swift-inspect to " + "use.") + +VARIABLE(SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT, uint8_t, 2, + "Print warnings when using implicit @objc entrypoints. Set to " + "desired reporting level, 0-3.") + +VARIABLE(SWIFT_DETERMINISTIC_HASHING, bool, false, + "Disable randomized hash seeding.") + +VARIABLE(SWIFT_ENABLE_MANGLED_NAME_VERIFICATION, bool, false, + "Enable verification that metadata can roundtrip through a mangled " + "name each time metadata is instantiated.") + +#undef VARIABLE diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index fbafd97872c12..5d8cabca493c7 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -21,6 +21,7 @@ #include "swift/Demangling/Demangler.h" #include "swift/ABI/TypeIdentity.h" #include "swift/Runtime/Casting.h" +#include "swift/Runtime/EnvironmentVariables.h" #include "swift/Runtime/ExistentialContainer.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Mutex.h" @@ -5505,21 +5506,21 @@ bool swift::_swift_debug_metadataAllocationIterationEnabled = false; const void * const swift::_swift_debug_allocationPoolPointer = &AllocationPool; static void checkAllocatorDebugEnvironmentVariable(void *context) { - const char *value = - getenv("SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION"); - if (value && (value[0] == '1' || value[0] == 'y' || value[0] == 'Y')) { - _swift_debug_metadataAllocationIterationEnabled = true; - // Write a PoolTrailer to the end of InitialAllocationPool and shrink - // the pool accordingly. - auto poolCopy = AllocationPool.load(std::memory_order_relaxed); - assert(poolCopy.Begin == InitialAllocationPool.Pool); - size_t newPoolSize = InitialPoolSize - sizeof(PoolTrailer); - PoolTrailer trailer = { nullptr, newPoolSize }; - memcpy(InitialAllocationPool.Pool + newPoolSize, &trailer, - sizeof(trailer)); - poolCopy.Remaining = newPoolSize; - AllocationPool.store(poolCopy, std::memory_order_relaxed); - } + _swift_debug_metadataAllocationIterationEnabled + = runtime::environment::SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION(); + if (!_swift_debug_metadataAllocationIterationEnabled) + return; + + // Write a PoolTrailer to the end of InitialAllocationPool and shrink + // the pool accordingly. + auto poolCopy = AllocationPool.load(std::memory_order_relaxed); + assert(poolCopy.Begin == InitialAllocationPool.Pool); + size_t newPoolSize = InitialPoolSize - sizeof(PoolTrailer); + PoolTrailer trailer = { nullptr, newPoolSize }; + memcpy(InitialAllocationPool.Pool + newPoolSize, &trailer, + sizeof(trailer)); + poolCopy.Remaining = newPoolSize; + AllocationPool.store(poolCopy, std::memory_order_relaxed); } void *MetadataAllocator::Allocate(size_t size, size_t alignment) { @@ -5655,10 +5656,8 @@ void swift::verifyMangledNameRoundtrip(const Metadata *metadata) { // variable lets us easily turn on verification to find and fix these // bugs. Remove this and leave it permanently on once everything works // with it enabled. - bool verificationEnabled = - SWIFT_LAZY_CONSTANT((bool)getenv("SWIFT_ENABLE_MANGLED_NAME_VERIFICATION")); - - if (!verificationEnabled) return; + if (!swift::runtime::environment::SWIFT_ENABLE_MANGLED_NAME_VERIFICATION()) + return; Demangle::StackAllocatedDemangler<1024> Dem; auto node = _swift_buildDemanglingForMetadata(metadata, Dem); diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index 34e87a9b1e99f..09cd6c312b3b2 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -26,6 +26,7 @@ #include "llvm/ADT/StringRef.h" #include "swift/Basic/Lazy.h" #include "swift/Runtime/Casting.h" +#include "swift/Runtime/EnvironmentVariables.h" #include "swift/Runtime/Heap.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" @@ -1478,22 +1479,9 @@ void swift_objc_swift3ImplicitObjCEntrypoint(id self, SEL selector, // if possible. // 3: Complain about uses of implicit @objc entrypoints, then abort(). // - // The actual reportLevel is stored as the above values +1, so that - // 0 indicates we have not yet checked. It's fine to race through here. - // // The default, if SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT is not set, is 2. - static int storedReportLevel = 0; - if (storedReportLevel == 0) { - auto reportLevelStr = getenv("SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT"); - if (reportLevelStr && - reportLevelStr[0] >= '0' && reportLevelStr[0] <= '3' && - reportLevelStr[1] == 0) - storedReportLevel = (reportLevelStr[0] - '0') + 1; - else - storedReportLevel = 3; - } - - int reportLevel = storedReportLevel - 1; + uint8_t reportLevel = + runtime::environment::SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT(); if (reportLevel < 1) return; // Report the error. diff --git a/stdlib/public/stubs/GlobalObjects.cpp b/stdlib/public/stubs/GlobalObjects.cpp index 201169a195287..8167fde149f79 100644 --- a/stdlib/public/stubs/GlobalObjects.cpp +++ b/stdlib/public/stubs/GlobalObjects.cpp @@ -20,6 +20,7 @@ #include "../SwiftShims/Random.h" #include "swift/Runtime/Metadata.h" #include "swift/Runtime/Debug.h" +#include "swift/Runtime/EnvironmentVariables.h" #include namespace swift { @@ -113,8 +114,7 @@ static swift::_SwiftHashingParameters initializeHashingParameters() { // results are repeatable, e.g., in certain test environments. (Note that // even if the seed override is enabled, hash values aren't guaranteed to // remain stable across even minor stdlib releases.) - auto determinism = getenv("SWIFT_DETERMINISTIC_HASHING"); - if (determinism && 0 == strcmp(determinism, "1")) { + if (swift::runtime::environment::SWIFT_DETERMINISTIC_HASHING()) { return { 0, 0, true }; } __swift_uint64_t seed0 = 0, seed1 = 0; diff --git a/test/Runtime/environment_variables.swift b/test/Runtime/environment_variables.swift new file mode 100644 index 0000000000000..8f772339d3011 --- /dev/null +++ b/test/Runtime/environment_variables.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -o %t/main +// RUN: %target-codesign %t/main +// RUN: env %env-SWIFT_DEBUG_HELP=YES %env-SWIFT_DEBUG_SOME_UNKNOWN_VARIABLE=42 %env-SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION=YES %env-SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT=abc %env-SWIFT_DETERMINISTIC_HASHING=whatever %env-SWIFT_ENABLE_MANGLED_NAME_VERIFICATION=YES %target-run %t/main 2>&1 | %FileCheck %s --dump-input fail + +// CHECK-DAG: {{Warning: unknown environment variable SWIFT_DEBUG_SOME_UNKNOWN_VARIABLE|Using getenv to read variables. Unknown variables will not be flagged.}} +// CHECK-DAG: Warning: cannot parse value SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT=abc, defaulting to 2. +// CHECK-DAG: Warning: cannot parse value SWIFT_DETERMINISTIC_HASHING=whatever, defaulting to false. +// CHECK-DAG: Swift runtime debugging: +// CHECK-DAG: bool SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION [default: false] - Enable additional metadata allocation tracking for swift-inspect to use. +// CHECK-DAG: uint8_t SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT [default: 2] - Print warnings when using implicit @objc entrypoints. Set to desired reporting level, 0-3. +// CHECK-DAG: bool SWIFT_DETERMINISTIC_HASHING [default: false] - Disable randomized hash seeding. +// CHECK-DAG: bool SWIFT_ENABLE_MANGLED_NAME_VERIFICATION [default: false] - Enable verification that metadata can roundtrip through a mangled name each time metadata is instantiated. + +print("Hello, world") +// CHECK: Hello, world