blob: 7ef1fe5747667c74042dafb782153188f6d77662 [file] [log] [blame]
//===--- 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 <string.h>
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_<type> 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(__OpenBSD__) || 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_<type> above. An unknown type will
// produce an error that parse_<unknown-type> 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
SWIFT_RUNTIME_EXPORT
bool swift_COWChecksEnabled() {
return runtime::environment::SWIFT_DEBUG_ENABLE_COW_CHECKS();
}