| /* | |
| * 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 { | |
| // Enum representing the type of the ART flag. | |
| enum class FlagType { | |
| // A flag that only looks at the cmdline argument to retrieve its value. | |
| kCmdlineOnly, | |
| // A flag that also looks at system properties and device config | |
| // (phenotype properties) when retrieving its value. | |
| kDeviceConfig, | |
| }; | |
| // 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, | |
| FlagType type) : | |
| command_line_argument_name_(command_line_argument_name), | |
| system_property_name_(system_property_name), | |
| server_setting_name_(server_setting_name), | |
| type_(type) {} | |
| 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_; | |
| FlagType type_; | |
| }; | |
| using FlagBase = FlagMetaBase<bool, int32_t, uint32_t, std::string>; | |
| template <> | |
| std::forward_list<FlagBase*> FlagBase::ALL_FLAGS; | |
| class FlagsTests; | |
| // Describes the possible origins of a flag value. | |
| enum class FlagOrigin { | |
| kDefaultValue, | |
| kCmdlineArg, | |
| kSystemProperty, | |
| kServerSetting, | |
| }; | |
| // 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. | |
| Flag(const std::string& name, Value default_value, FlagType type); | |
| 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>(GetValueAndOrigin()); | |
| } | |
| ALWAYS_INLINE Value operator()() const { | |
| return GetValue(); | |
| } | |
| // Return the value of the flag as optional. | |
| // | |
| // Returns the value of the flag if and only if the flag is set via | |
| // a server side setting, system property or a cmdline arg. | |
| // Otherwise it returns nullopt (meaning this never returns the default value). | |
| // | |
| // This is useful for properties that do not have a good default natural value | |
| // (e.g. file path arguments). | |
| ALWAYS_INLINE std::optional<Value> GetValueOptional() const { | |
| std::pair<Value, FlagOrigin> result = GetValueAndOrigin(); | |
| return std::get<1>(result) == FlagOrigin::kDefaultValue | |
| ? std::nullopt | |
| : std::make_optional(std::get<0>(result)); | |
| } | |
| // Returns the value and the origin of that value for the given flag. | |
| ALWAYS_INLINE std::pair<Value, FlagOrigin> GetValueAndOrigin() const { | |
| DCHECK(initialized_); | |
| if (from_server_setting_.has_value()) { | |
| return std::pair{from_server_setting_.value(), FlagOrigin::kServerSetting}; | |
| } | |
| if (from_system_property_.has_value()) { | |
| return std::pair{from_system_property_.value(), FlagOrigin::kSystemProperty}; | |
| } | |
| if (from_command_line_.has_value()) { | |
| return std::pair{from_command_line_.value(), FlagOrigin::kCmdlineArg}; | |
| } | |
| return std::pair{default_, FlagOrigin::kDefaultValue}; | |
| } | |
| 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 TestFlag; | |
| }; | |
| // 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, FlagType::kDeviceConfig}; | |
| // | |
| // This creates an integer flag that can be read through gFlags.WriteMetricsToLog(). The default | |
| // value is 42. Note that the default value can be left unspecified, in which case 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<int32_t> MyFeatureTestFlag{"my-feature-test.flag", 42, FlagType::kDeviceConfig}; | |
| // Metric infra flags. | |
| // The reporting spec for regular apps. An example of valid value is "S,1,2,4,*". | |
| // See metrics::ReportingPeriodSpec for complete docs. | |
| Flag<std::string> MetricsReportingSpec{ | |
| "metrics.reporting-spec", "1,5,30,60,600", FlagType::kDeviceConfig}; | |
| // The reporting spec for the system server. See MetricsReportingSpec as well. | |
| Flag<std::string> MetricsReportingSpecSystemServer{ | |
| "metrics.reporting-spec-server", "1,10,60,3600,*", FlagType::kDeviceConfig}; | |
| // The mods that should report metrics. Together with MetricsReportingNumMods, they | |
| // dictate what percentage of the runtime execution will report metrics. | |
| // If the `session_id (a random number) % MetricsReportingNumMods < MetricsReportingMods` | |
| // then the runtime session will report metrics. | |
| // | |
| // By default, the mods are 2, which means that 2 out of #{reporting-num-mods} of Android sessions | |
| // will be reported (with the default values this is 2/100 = 2%). | |
| Flag<uint32_t> MetricsReportingMods{"metrics.reporting-mods", 2, FlagType::kDeviceConfig}; | |
| Flag<uint32_t> MetricsReportingModsServer{ | |
| "metrics.reporting-mods-server", 2, FlagType::kDeviceConfig}; | |
| // See MetricsReportingMods docs. | |
| // | |
| // By default the number of mods is 100, so MetricsReportingMods will naturally | |
| // read as the percent of runtime sessions that will report metrics. If a finer | |
| // grain unit is needed (e.g. a tenth of a percent), the num-mods can be increased. | |
| Flag<uint32_t> MetricsReportingNumMods{"metrics.reporting-num-mods", 100, | |
| FlagType::kDeviceConfig}; | |
| Flag<uint32_t> MetricsReportingNumModsServer{"metrics.reporting-num-mods-server", 100, | |
| FlagType::kDeviceConfig}; | |
| // Whether or not we should write metrics to statsd. | |
| // Note that the actual write is still controlled by | |
| // MetricsReportingMods and MetricsReportingNumMods. | |
| Flag<bool> MetricsWriteToStatsd{"metrics.write-to-statsd", true, FlagType::kDeviceConfig}; | |
| // Whether or not we should write metrics to logcat. | |
| // Note that the actual write is still controlled by | |
| // MetricsReportingMods and MetricsReportingNumMods. | |
| Flag<bool> MetricsWriteToLogcat{ "metrics.write-to-logcat", false, FlagType::kCmdlineOnly}; | |
| // Whether or not we should write metrics to a file. | |
| // Note that the actual write is still controlled by | |
| // MetricsReportingMods and MetricsReportingNumMods. | |
| Flag<std::string> MetricsWriteToFile{"metrics.write-to-file", "", FlagType::kCmdlineOnly}; | |
| // The output format for metrics. This is only used | |
| // when writing metrics to a file; metrics written | |
| // to logcat will be in human-readable text format. | |
| // Supported values are "text" and "xml". | |
| Flag<std::string> MetricsFormat{"metrics.format", "text", FlagType::kCmdlineOnly}; | |
| // Whether or not to force the metrics initialization. | |
| Flag<bool> MetricsForceEnable{"metrics.force-enable", false, FlagType::kCmdlineOnly}; | |
| }; | |
| // 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_ |