diff options
-rw-r--r-- | cmdline/cmdline_parser.h | 18 | ||||
-rw-r--r-- | cmdline/cmdline_types.h | 17 | ||||
-rw-r--r-- | libartbase/Android.bp | 2 | ||||
-rw-r--r-- | libartbase/base/flags.cc | 147 | ||||
-rw-r--r-- | libartbase/base/flags.h | 234 | ||||
-rw-r--r-- | libartbase/base/flags_test.cc | 123 | ||||
-rw-r--r-- | runtime/native/dalvik_system_ZygoteHooks.cc | 7 | ||||
-rw-r--r-- | runtime/parsed_options.cc | 8 | ||||
-rw-r--r-- | runtime/runtime.cc | 8 | ||||
-rw-r--r-- | runtime/runtime.h | 11 |
10 files changed, 572 insertions, 3 deletions
diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h index 22eb44c211..7e343f8ef2 100644 --- a/cmdline/cmdline_parser.h +++ b/cmdline/cmdline_parser.h @@ -210,6 +210,24 @@ struct CmdlineParser { return parent_; } + // Write the results of this argument into a variable pointed to by destination. + // An optional is used to tell whether the command line argument was present. + CmdlineParser::Builder& IntoLocation(std::optional<TArg>* destination) { + save_value_ = [destination](TArg& value) { + *destination = value; + }; + + load_value_ = [destination]() -> TArg& { + return destination->value(); + }; + + save_value_specified_ = true; + load_value_specified_ = true; + + CompleteArgument(); + return parent_; + } + // Ensure we always move this when returning a new builder. ArgumentBuilder(ArgumentBuilder&&) = default; diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 43d30222c7..964e2385b6 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -21,6 +21,7 @@ #include <list> #include <ostream> +#include "android-base/parsebool.h" #include "android-base/stringprintf.h" #include "cmdline_type_parser.h" #include "detail/cmdline_debug_detail.h" @@ -67,6 +68,22 @@ struct CmdlineType<Unit> : CmdlineTypeParser<Unit> { }; template <> +struct CmdlineType<bool> : CmdlineTypeParser<bool> { + Result Parse(const std::string& args) { + switch (::android::base::ParseBool(args)) { + case ::android::base::ParseBoolResult::kError: + return Result::Failure("Could not parse '" + args + "' as boolean"); + case ::android::base::ParseBoolResult::kTrue: + return Result::Success(true); + case ::android::base::ParseBoolResult::kFalse: + return Result::Success(false); + } + } + + static const char* DescribeType() { return "true|false|1|0|y|n|yes|no|on|off"; } +}; + +template <> struct CmdlineType<JdwpProvider> : CmdlineTypeParser<JdwpProvider> { /* * Handle a single JDWP provider name. Must be either 'internal', 'default', or the file name of diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 1b021195d6..9dcba9977c 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -37,6 +37,7 @@ cc_defaults { "base/enums.cc", "base/file_magic.cc", "base/file_utils.cc", + "base/flags.cc", "base/hex_dump.cc", "base/hiddenapi_flags.cc", "base/logging.cc", @@ -260,6 +261,7 @@ art_cc_test { "base/bit_vector_test.cc", "base/compiler_filter_test.cc", "base/file_utils_test.cc", + "base/flags_test.cc", "base/hash_map_test.cc", "base/hash_set_test.cc", "base/hex_dump_test.cc", diff --git a/libartbase/base/flags.cc b/libartbase/base/flags.cc new file mode 100644 index 0000000000..46de9c0821 --- /dev/null +++ b/libartbase/base/flags.cc @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "flags.h" + +#include <algorithm> + +#include "android-base/parsebool.h" +#include "android-base/parseint.h" +#include "android-base/properties.h" + +#include "base/utils.h" + +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wconversion" + +namespace { +constexpr const char* kPhenotypeFlagPrefix = "persist.device_config.runtime_native."; +constexpr const char* kSysPropertyFlagPrefix = "dalvik.vm."; +constexpr const char* kUndefinedValue = ""; + +// The various ParseValue functions store the parsed value into *destination. If parsing fails for +// some reason, ParseValue makes no changes to *destination. + +void ParseValue(const std::string_view value, std::optional<bool>* destination) { + switch (::android::base::ParseBool(value)) { + case ::android::base::ParseBoolResult::kError: + return; + case ::android::base::ParseBoolResult::kTrue: + *destination = true; + return; + case ::android::base::ParseBoolResult::kFalse: + *destination = false; + return; + } +} + +void ParseValue(const std::string_view value, std::optional<int>* destination) { + int parsed_value = 0; + if (::android::base::ParseInt(std::string{value}, &parsed_value)) { + *destination = parsed_value; + } +} + +void ParseValue(const std::string_view value, std::optional<std::string>* destination) { + *destination = value; +} + +} // namespace + +namespace art { + +template <> +std::forward_list<FlagBase*> FlagBase::ALL_FLAGS{}; + +// gFlags must be defined after FlagBase::ALL_FLAGS so the constructors run in the right order. +Flags gFlags; + +static std::string GenerateCmdLineArgName(const std::string& name) { + std::string result = "-X" + name + ":_"; + std::replace(result.begin(), result.end(), '.', '-'); + return result; +} + +static std::string GenerateSysPropName(const std::string& name) { + return kSysPropertyFlagPrefix + name; +} + +static std::string GeneratePhenotypeName(const std::string& name) { + return kPhenotypeFlagPrefix + name; +} + +template <typename Value> +Flag<Value>::Flag(const std::string& name, Value default_value) : + FlagBase(GenerateCmdLineArgName(name), + GenerateSysPropName(name), + GeneratePhenotypeName(name)), + initialized_{false}, + default_{default_value} { + ALL_FLAGS.push_front(this); +} + +template <typename Value> +void Flag<Value>::Reload() { + // The cmdline flags are loaded by the parsed_options infra. + + // Load system properties. + from_system_property_ = std::nullopt; + const std::string sysprop = ::android::base::GetProperty(system_property_name_, kUndefinedValue); + if (sysprop != kUndefinedValue) { + ParseValue(sysprop, &from_system_property_); + } + + // Load the server-side configuration. + from_server_setting_ = std::nullopt; + const std::string server_config = + ::android::base::GetProperty(server_setting_name_, kUndefinedValue); + if (server_config != kUndefinedValue) { + ParseValue(server_config, &from_server_setting_); + } + + initialized_ = true; +} + +template <typename Value> +void DumpValue(std::ostream& oss, const std::optional<Value>& val) { + if (val.has_value()) { + oss << val.value(); + } else { + oss << kUndefinedValue; + } +} + +template <typename Value> +void Flag<Value>::Dump(std::ostream& oss) const { + std::pair<Value, std::string> valueLoc = GetValueLocation(); + oss << "value: " << std::get<0>(valueLoc) << " (from " << std::get<1>(valueLoc) << ")"; + + oss << "\n default: " << default_; + oss << "\n " << command_line_argument_name_ << ": "; + DumpValue(oss, from_command_line_); + oss << "\n " << system_property_name_ << ": "; + DumpValue(oss, from_system_property_); + oss << "\n " << server_setting_name_ << ": "; + DumpValue(oss, from_server_setting_); +} + +template class Flag<bool>; +template class Flag<int>; +template class Flag<std::string>; + +} // namespace art + +#pragma clang diagnostic pop // -Wconversion diff --git a/libartbase/base/flags.h b/libartbase/base/flags.h new file mode 100644 index 0000000000..ae166e64a6 --- /dev/null +++ b/libartbase/base/flags.h @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBARTBASE_BASE_FLAGS_H_ +#define ART_LIBARTBASE_BASE_FLAGS_H_ + +#include <forward_list> +#include <optional> +#include <string> +#include <variant> + +#include "logging.h" + +// This file defines a set of flags that can be used to enable/disable features within ART or +// otherwise tune ART's behavior. Flags can be set through command line options, server side +// configuration, system properties, or default values. This flexibility enables easier development +// and also larger experiments. +// +// The value is retrieved in the following oder: +// 1) server side (device config) property +// 2) system property +// 3) cmdline flag +// 4) default value +// +// The flags are defined in the Flags struct near the bottom of the file. To define a new flag, add +// a Flag field to the struct. Then to read the value of the flag, use gFlag.MyNewFlag(). + +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wconversion" + +namespace art { + +// FlagMetaBase handles automatically adding flags to the command line parser. It is parameterized +// by all supported flag types. In general, this should be treated as though it does not exist and +// FlagBase, which is already specialized to the types we support, should be used instead. +template <typename... T> +class FlagMetaBase { + public: + FlagMetaBase(const std::string&& command_line_argument_name, + const std::string&& system_property_name, + const std::string&& server_setting_name) : + command_line_argument_name_(command_line_argument_name), + system_property_name_(system_property_name), + server_setting_name_(server_setting_name) {} + virtual ~FlagMetaBase() {} + + template <typename Builder> + static void AddFlagsToCmdlineParser(Builder* builder) { + for (auto* flag : ALL_FLAGS) { + // Each flag can return a pointer to where its command line value is stored. Because these can + // be different types, the return value comes as a variant. The cases list below contains a + // lambda that is specialized to handle each branch of the variant and call the correct + // methods on the command line parser builder. + FlagValuePointer location = flag->GetCmdLineLocation(); + auto cases = {std::function<void()>([&]() { + if (std::holds_alternative<std::optional<T>*>(location)) { + builder = &builder->Define(flag->command_line_argument_name_.c_str()) + .template WithType<T>() + .IntoLocation(std::get<std::optional<T>*>(location)); + } + })...}; + for (auto c : cases) { + c(); + } + } + } + + // Reload the value of the flags. + // + // DO NOT CALL this outside Runtime Init or Zygote post fork. + // This is a convention, as we should strive to have a constant view + // of the flags and not change the runtime behaviour midway during execution. + static void ReloadAllFlags(const std::string& caller) { + // Check the caller. This is a simple workaround to attract the attention + // to a possible dangerous call to ReloadAllFlags, while avoid building + // a lot of infra for it or having a complex friend definition. + DCHECK(caller == "Init" + || caller == "ZygoteHooks_nativePostForkChild" + || caller == "ZygoteHooks_nativePostForkSystemServer" + || caller == "test") << caller; + for (auto* flag : ALL_FLAGS) { + flag->Reload(); + } + + if (VLOG_IS_ON(startup)) { + VLOG_STREAM(startup) << "Dumping flags for " << caller; + DumpFlags(VLOG_STREAM(startup)); + } + } + + // Dump all the flags info to the given stream. + static void DumpFlags(std::ostream& oss) { + for (auto* flag : ALL_FLAGS) { + oss << "\n{\n"; + flag->Dump(oss); + oss << "\n}"; + } + } + + protected: + using FlagValuePointer = std::variant<std::optional<T>*...>; + // Return the pointer to the value holder associated with the cmd line location. + virtual FlagValuePointer GetCmdLineLocation() = 0; + // Reloads the flag values. + virtual void Reload() = 0; + // Dumps the flags info to the given stream. + virtual void Dump(std::ostream& oss) const = 0; + + static std::forward_list<FlagMetaBase<T...>*> ALL_FLAGS; + + const std::string command_line_argument_name_; + const std::string system_property_name_; + const std::string server_setting_name_; +}; + +using FlagBase = FlagMetaBase<bool, int, std::string>; + +template <> +std::forward_list<FlagBase*> FlagBase::ALL_FLAGS; + +class FlagsTests; + +// This class defines a flag with a value of a particular type. +template <typename Value> +class Flag : public FlagBase { + public: + // Create a new Flag. The name parameter is used to generate the names from the various parameter + // sources. See the documentation on the Flags struct for an example. + explicit Flag(const std::string& name, Value default_value = {}); + virtual ~Flag() {} + + + // Returns the flag value. + // + // The value is retrieved in the following oder: + // 1) server side (device config) property + // 2) system property + // 3) cmdline flag + // 4) default value + ALWAYS_INLINE Value GetValue() const { + return std::get<0>(GetValueLocation()); + } + + ALWAYS_INLINE Value operator()() const { + return GetValue(); + } + + // Returns the value and the location of that value for the given flag. + ALWAYS_INLINE std::pair<Value, std::string> GetValueLocation() const { + DCHECK(initialized_); + if (from_server_setting_.has_value()) { + return std::pair{from_server_setting_.value(), server_setting_name_}; + } + if (from_system_property_.has_value()) { + return std::pair{from_system_property_.value(), system_property_name_}; + } + if (from_command_line_.has_value()) { + return std::pair{from_command_line_.value(), command_line_argument_name_}; + } + return std::pair{default_, "default_value"}; + } + + void Dump(std::ostream& oss) const override; + + protected: + FlagValuePointer GetCmdLineLocation() override { return &from_command_line_; } + + + // Reload the server-configured value and system property values. In general this should not be + // used directly, but it can be used to support reloading the value without restarting the device. + void Reload() override; + + private: + bool initialized_; + const Value default_; + std::optional<Value> from_command_line_; + std::optional<Value> from_system_property_; + std::optional<Value> from_server_setting_; + + friend class FlagsTests; +}; + +// This struct contains the list of ART flags. Flags are parameterized by the type of value they +// support (bool, int, string, etc.). In addition to field name, flags have a name for the parameter +// as well. +// +// Example: +// +// Flag<int> WriteMetricsToLog{"my-feature-test.flag", 42}; +// +// This creates a boolean flag that can be read through gFlags.WriteMetricsToLog(). The default +// value is false. Note that the default value can be left unspecified, in which the value of the +// type's default constructor will be used. +// +// The flag can be set through the following generated means: +// +// Command Line: +// +// -Xmy-feature-test-flag=1 +// +// Server Side (Phenotype) Configuration: +// +// persist.device_config.runtime_native.my-feature-test.flag +// +// System Property: +// +// setprop dalvik.vm.metrics.my-feature-test.flag 2 +struct Flags { + // Flag used to test the infra. + // TODO: can be removed once we add real flags. + Flag<int> MyFeatureTestFlag{"my-feature-test.flag", /*default_value=*/ 42}; +}; + +// This is the actual instance of all the flags. +extern Flags gFlags; + +} // namespace art + +#pragma clang diagnostic pop // -Wconversion + +#endif // ART_LIBARTBASE_BASE_FLAGS_H_ diff --git a/libartbase/base/flags_test.cc b/libartbase/base/flags_test.cc new file mode 100644 index 0000000000..cfbf2a9f1c --- /dev/null +++ b/libartbase/base/flags_test.cc @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base/flags.h" + +#include <optional> + +#include "android-base/properties.h" +#include "common_runtime_test.h" + + +namespace art { + +class FlagsTests : public CommonRuntimeTest { + protected: + void assertCmdlineValue(bool has_value, int expected) { + ASSERT_EQ(gFlags.MyFeatureTestFlag.from_command_line_.has_value(), has_value); + if (has_value) { + ASSERT_EQ(gFlags.MyFeatureTestFlag.from_command_line_.value(), expected); + } + } + + void assertSysPropValue(bool has_value, int expected) { + ASSERT_EQ(gFlags.MyFeatureTestFlag.from_system_property_.has_value(), has_value); + if (has_value) { + ASSERT_EQ(gFlags.MyFeatureTestFlag.from_system_property_.value(), expected); + } + } + + void assertServerSettingValue(bool has_value, int expected) { + ASSERT_EQ(gFlags.MyFeatureTestFlag.from_server_setting_.has_value(), has_value); + if (has_value) { + ASSERT_EQ(gFlags.MyFeatureTestFlag.from_server_setting_.value(), expected); + } + } + + void assertDefaultValue(int expected) { + ASSERT_EQ(gFlags.MyFeatureTestFlag.default_, expected); + } +}; + +class FlagsTestsWithCmdLine : public FlagsTests { + public: + ~FlagsTestsWithCmdLine() { + android::base::SetProperty("dalvik.vm.my-feature-test.flag", ""); + android::base::SetProperty("persist.device_config.runtime_native.my-feature-test.flag", ""); + } + + protected: + void SetUpRuntimeOptions(RuntimeOptions* options) override { + // Disable implicit dex2oat invocations when loading image spaces. + options->emplace_back("-Xmy-feature-test-flag:1", nullptr); + } +}; + +// Validate that when no flag is set, the default is taken and none of the other +// locations are populated +TEST_F(FlagsTests, ValidateDefaultValue) { + FlagBase::ReloadAllFlags("test"); + + assertCmdlineValue(false, 1); + assertSysPropValue(false, 2); + assertServerSettingValue(false, 3); + assertDefaultValue(42); + + ASSERT_EQ(gFlags.MyFeatureTestFlag(), 42); +} + +// Validate that the server side config is picked when it is set. +TEST_F(FlagsTestsWithCmdLine, FlagsTestsGetValueServerSetting) { + android::base::SetProperty("dalvik.vm.my-feature-test.flag", "2"); + android::base::SetProperty("persist.device_config.runtime_native.my-feature-test.flag", "3"); + + FlagBase::ReloadAllFlags("test"); + + assertCmdlineValue(true, 1); + assertSysPropValue(true, 2); + assertServerSettingValue(true, 3); + assertDefaultValue(42); + + ASSERT_EQ(gFlags.MyFeatureTestFlag(), 3); +} + +// Validate that the system property value is picked when the server one is not set. +TEST_F(FlagsTestsWithCmdLine, FlagsTestsGetValueSysProperty) { + android::base::SetProperty("dalvik.vm.my-feature-test.flag", "2"); + + FlagBase::ReloadAllFlags("test"); + + assertCmdlineValue(true, 1); + assertSysPropValue(true, 2); + assertServerSettingValue(false, 3); + assertDefaultValue(42); + + ASSERT_EQ(gFlags.MyFeatureTestFlag(), 2); +} + +// Validate that the cmdline value is picked when no properties are set. +TEST_F(FlagsTestsWithCmdLine, FlagsTestsGetValueCmdline) { + FlagBase::ReloadAllFlags("test"); + + assertCmdlineValue(true, 1); + assertSysPropValue(false, 2); + assertServerSettingValue(false, 3); + assertDefaultValue(42); + + ASSERT_EQ(gFlags.MyFeatureTestFlag(), 1); +} + +} // namespace art diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 9f75a88159..cdb63f31cb 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -269,6 +269,9 @@ static void ZygoteHooks_nativePostZygoteFork(JNIEnv*, jclass) { static void ZygoteHooks_nativePostForkSystemServer(JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jint runtime_flags) { + // Reload the current flags first. In case we need to take actions based on them. + Runtime::Current()->ReloadAllFlags(__FUNCTION__); + // Set the runtime state as the first thing, in case JIT and other services // start querying it. Runtime::Current()->SetAsSystemServer(); @@ -296,7 +299,9 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, jboolean is_zygote, jstring instruction_set) { DCHECK(!(is_system_server && is_zygote)); - // Set the runtime state as the first thing, in case JIT and other services + // Reload the current flags first. In case we need to take any updated actions. + Runtime::Current()->ReloadAllFlags(__FUNCTION__); + // Then, set the runtime state, in case JIT and other services // start querying it. Runtime::Current()->SetAsZygoteChild(is_system_server, is_zygote); diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 471a3fafd6..e5e7c4f9d0 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -23,6 +23,7 @@ #include <android-base/strings.h> #include "base/file_utils.h" +#include "base/flags.h" #include "base/indenter.h" #include "base/macros.h" #include "base/utils.h" @@ -464,8 +465,11 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-XX:PerfettoJavaHeapStackProf=_") .WithType<bool>() .WithValueMap({{"false", false}, {"true", true}}) - .IntoKey(M::PerfettoJavaHeapStackProf) - .Ignore({ + .IntoKey(M::PerfettoJavaHeapStackProf); + + FlagBase::AddFlagsToCmdlineParser(parser_builder.get()); + + parser_builder->Ignore({ "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa", "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_", "-Xdexopt:_", "-Xnoquithandler", "-Xjnigreflimit:_", "-Xgenregmap", "-Xnogenregmap", diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 97c1d6dea7..52a4ab3ee5 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -56,6 +56,7 @@ #include "base/dumpable.h" #include "base/enums.h" #include "base/file_utils.h" +#include "base/flags.h" #include "base/malloc_arena_pool.h" #include "base/mem_map_arena_pool.h" #include "base/memory_tool.h" @@ -1284,6 +1285,10 @@ void Runtime::InitializeApexVersions() { apex_versions_ = result; } +void Runtime::ReloadAllFlags(const std::string& caller) { + FlagBase::ReloadAllFlags(caller); +} + bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc. // Take a snapshot of the environment at the time the runtime was created, for use by Exec, etc. @@ -1294,6 +1299,9 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { ScopedTrace trace(__FUNCTION__); CHECK_EQ(static_cast<size_t>(sysconf(_SC_PAGE_SIZE)), kPageSize); + // Reload all the flags value (from system properties and device configs). + ReloadAllFlags(__FUNCTION__); + // Early override for logging output. if (runtime_options.Exists(Opt::UseStderrLogger)) { android::base::SetLogger(android::base::StderrLogger); diff --git a/runtime/runtime.h b/runtime/runtime.h index 9664977577..1436a5f353 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -1005,6 +1005,17 @@ class Runtime { return apex_versions_; } + // Trigger a flag reload from system properties or device congfigs. + // + // Should only be called from runtime init and zygote post fork as + // we don't want to change the runtime config midway during execution. + // + // The caller argument should be the name of the function making this call + // and will be used to enforce the appropriate names. + // + // See Flags::ReloadAllFlags as well. + static void ReloadAllFlags(const std::string& caller); + private: static void InitPlatformSignalHandlers(); |