1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ART_LIBARTBASE_BASE_FLAGS_H_ 18 #define ART_LIBARTBASE_BASE_FLAGS_H_ 19 20 #include <forward_list> 21 #include <optional> 22 #include <string> 23 #include <variant> 24 25 #include "logging.h" 26 27 // This file defines a set of flags that can be used to enable/disable features within ART or 28 // otherwise tune ART's behavior. Flags can be set through command line options, server side 29 // configuration, system properties, or default values. This flexibility enables easier development 30 // and also larger experiments. 31 // 32 // The value is retrieved in the following oder: 33 // 1) server side (device config) property 34 // 2) system property 35 // 3) cmdline flag 36 // 4) default value 37 // 38 // The flags are defined in the Flags struct near the bottom of the file. To define a new flag, add 39 // a Flag field to the struct. Then to read the value of the flag, use gFlag.MyNewFlag(). 40 41 #pragma clang diagnostic push 42 #pragma clang diagnostic error "-Wconversion" 43 44 namespace art { 45 46 // Enum representing the type of the ART flag. 47 enum class FlagType { 48 // A flag that only looks at the cmdline argument to retrieve its value. 49 kCmdlineOnly, 50 // A flag that also looks at system properties and device config 51 // (phenotype properties) when retrieving its value. 52 kDeviceConfig, 53 }; 54 55 // FlagMetaBase handles automatically adding flags to the command line parser. It is parameterized 56 // by all supported flag types. In general, this should be treated as though it does not exist and 57 // FlagBase, which is already specialized to the types we support, should be used instead. 58 template <typename... T> 59 class FlagMetaBase { 60 public: FlagMetaBase(const std::string && command_line_argument_name,const std::string && system_property_name,const std::string && server_setting_name,FlagType type)61 FlagMetaBase(const std::string&& command_line_argument_name, 62 const std::string&& system_property_name, 63 const std::string&& server_setting_name, 64 FlagType type) : 65 command_line_argument_name_(command_line_argument_name), 66 system_property_name_(system_property_name), 67 server_setting_name_(server_setting_name), 68 type_(type) {} ~FlagMetaBase()69 virtual ~FlagMetaBase() {} 70 71 template <typename Builder> AddFlagsToCmdlineParser(Builder * builder)72 static void AddFlagsToCmdlineParser(Builder* builder) { 73 for (auto* flag : ALL_FLAGS) { 74 // Each flag can return a pointer to where its command line value is stored. Because these can 75 // be different types, the return value comes as a variant. The cases list below contains a 76 // lambda that is specialized to handle each branch of the variant and call the correct 77 // methods on the command line parser builder. 78 FlagValuePointer location = flag->GetCmdLineLocation(); 79 auto cases = {std::function<void()>([&]() { 80 if (std::holds_alternative<std::optional<T>*>(location)) { 81 builder = &builder->Define(flag->command_line_argument_name_.c_str()) 82 .template WithType<T>() 83 .IntoLocation(std::get<std::optional<T>*>(location)); 84 } 85 })...}; 86 for (auto c : cases) { 87 c(); 88 } 89 } 90 } 91 92 // Reload the value of the flags. 93 // 94 // DO NOT CALL this outside Runtime Init or Zygote post fork. 95 // This is a convention, as we should strive to have a constant view 96 // of the flags and not change the runtime behaviour midway during execution. ReloadAllFlags(const std::string & caller)97 static void ReloadAllFlags(const std::string& caller) { 98 // Check the caller. This is a simple workaround to attract the attention 99 // to a possible dangerous call to ReloadAllFlags, while avoid building 100 // a lot of infra for it or having a complex friend definition. 101 DCHECK(caller == "Init" 102 || caller == "ZygoteHooks_nativePostForkChild" 103 || caller == "ZygoteHooks_nativePostForkSystemServer" 104 || caller == "test") << caller; 105 for (auto* flag : ALL_FLAGS) { 106 flag->Reload(); 107 } 108 109 if (VLOG_IS_ON(startup)) { 110 VLOG_STREAM(startup) << "Dumping flags for " << caller; 111 DumpFlags(VLOG_STREAM(startup)); 112 } 113 } 114 115 // Dump all the flags info to the given stream. DumpFlags(std::ostream & oss)116 static void DumpFlags(std::ostream& oss) { 117 for (auto* flag : ALL_FLAGS) { 118 oss << "\n{\n"; 119 flag->Dump(oss); 120 oss << "\n}"; 121 } 122 } 123 124 protected: 125 using FlagValuePointer = std::variant<std::optional<T>*...>; 126 // Return the pointer to the value holder associated with the cmd line location. 127 virtual FlagValuePointer GetCmdLineLocation() = 0; 128 // Reloads the flag values. 129 virtual void Reload() = 0; 130 // Dumps the flags info to the given stream. 131 virtual void Dump(std::ostream& oss) const = 0; 132 133 static std::forward_list<FlagMetaBase<T...>*> ALL_FLAGS; 134 135 const std::string command_line_argument_name_; 136 const std::string system_property_name_; 137 const std::string server_setting_name_; 138 FlagType type_; 139 }; 140 141 using FlagBase = FlagMetaBase<bool, int32_t, uint32_t, std::string>; 142 143 template <> 144 std::forward_list<FlagBase*> FlagBase::ALL_FLAGS; 145 146 class FlagsTests; 147 148 // Describes the possible origins of a flag value. 149 enum class FlagOrigin { 150 kDefaultValue, 151 kCmdlineArg, 152 kSystemProperty, 153 kServerSetting, 154 }; 155 156 // This class defines a flag with a value of a particular type. 157 template <typename Value> 158 class Flag : public FlagBase { 159 public: 160 // Create a new Flag. The name parameter is used to generate the names from the various parameter 161 // sources. See the documentation on the Flags struct for an example. 162 Flag(const std::string& name, Value default_value, FlagType type); 163 virtual ~Flag(); 164 165 166 // Returns the flag value. 167 // 168 // The value is retrieved in the following oder: 169 // 1) server side (device config) property 170 // 2) system property 171 // 3) cmdline flag 172 // 4) default value GetValue()173 ALWAYS_INLINE Value GetValue() const { 174 return std::get<0>(GetValueAndOrigin()); 175 } 176 operator()177 ALWAYS_INLINE Value operator()() const { 178 return GetValue(); 179 } 180 181 // Return the value of the flag as optional. 182 // 183 // Returns the value of the flag if and only if the flag is set via 184 // a server side setting, system property or a cmdline arg. 185 // Otherwise it returns nullopt (meaning this never returns the default value). 186 // 187 // This is useful for properties that do not have a good default natural value 188 // (e.g. file path arguments). GetValueOptional()189 ALWAYS_INLINE std::optional<Value> GetValueOptional() const { 190 std::pair<Value, FlagOrigin> result = GetValueAndOrigin(); 191 return std::get<1>(result) == FlagOrigin::kDefaultValue 192 ? std::nullopt 193 : std::make_optional(std::get<0>(result)); 194 } 195 196 // Returns the value and the origin of that value for the given flag. GetValueAndOrigin()197 ALWAYS_INLINE std::pair<Value, FlagOrigin> GetValueAndOrigin() const { 198 DCHECK(initialized_); 199 if (from_server_setting_.has_value()) { 200 return std::pair{from_server_setting_.value(), FlagOrigin::kServerSetting}; 201 } 202 if (from_system_property_.has_value()) { 203 return std::pair{from_system_property_.value(), FlagOrigin::kSystemProperty}; 204 } 205 if (from_command_line_.has_value()) { 206 return std::pair{from_command_line_.value(), FlagOrigin::kCmdlineArg}; 207 } 208 return std::pair{default_, FlagOrigin::kDefaultValue}; 209 } 210 211 void Dump(std::ostream& oss) const override; 212 213 protected: GetCmdLineLocation()214 FlagValuePointer GetCmdLineLocation() override { return &from_command_line_; } 215 216 217 // Reload the server-configured value and system property values. In general this should not be 218 // used directly, but it can be used to support reloading the value without restarting the device. 219 void Reload() override; 220 221 private: 222 bool initialized_; 223 const Value default_; 224 std::optional<Value> from_command_line_; 225 std::optional<Value> from_system_property_; 226 std::optional<Value> from_server_setting_; 227 228 friend class TestFlag; 229 }; 230 231 // This struct contains the list of ART flags. Flags are parameterized by the type of value they 232 // support (bool, int, string, etc.). In addition to field name, flags have a name for the parameter 233 // as well. 234 // 235 // Example: 236 // 237 // Flag<int> WriteMetricsToLog{"my-feature-test.flag", 42, FlagType::kDeviceConfig}; 238 // 239 // This creates a boolean flag that can be read through gFlags.WriteMetricsToLog(). The default 240 // value is false. Note that the default value can be left unspecified, in which the value of the 241 // type's default constructor will be used. 242 // 243 // The flag can be set through the following generated means: 244 // 245 // Command Line: 246 // 247 // -Xmy-feature-test-flag=1 248 // 249 // Server Side (Phenotype) Configuration: 250 // 251 // persist.device_config.runtime_native.my-feature-test.flag 252 // 253 // System Property: 254 // 255 // setprop dalvik.vm.metrics.my-feature-test.flag 2 256 struct Flags { 257 // Flag used to test the infra. 258 // TODO: can be removed once we add real flags. 259 Flag<int32_t> MyFeatureTestFlag{"my-feature-test.flag", 42, FlagType::kDeviceConfig}; 260 261 262 // Metric infra flags. 263 264 // The reporting spec for regular apps. An example of valid value is "S,1,2,4,*". 265 // See metrics::ReportingPeriodSpec for complete docs. 266 Flag<std::string> MetricsReportingSpec{"metrics.reporting-spec", "", FlagType::kDeviceConfig}; 267 268 // The reporting spec for the system server. See MetricsReportingSpec as well. 269 Flag<std::string> MetricsReportingSpecSystemServer{"metrics.reporting-spec-server", "", 270 FlagType::kDeviceConfig}; 271 272 // The mods that should report metrics. Together with MetricsReportingNumMods, they 273 // dictate what percentage of the runtime execution will report metrics. 274 // If the `session_id (a random number) % MetricsReportingNumMods < MetricsReportingMods` 275 // then the runtime session will report metrics. 276 // 277 // By default, the mods are 0, which means the reporting is disabled. 278 Flag<uint32_t> MetricsReportingMods{"metrics.reporting-mods", 0, 279 FlagType::kDeviceConfig}; 280 Flag<uint32_t> MetricsReportingModsServer{"metrics.reporting-mods-server", 0, 281 FlagType::kDeviceConfig}; 282 283 // See MetricsReportingMods docs. 284 // 285 // By default the number of mods is 100, so MetricsReportingMods will naturally 286 // read as the percent of runtime sessions that will report metrics. If a finer 287 // grain unit is needed (e.g. a tenth of a percent), the num-mods can be increased. 288 Flag<uint32_t> MetricsReportingNumMods{"metrics.reporting-num-mods", 100, 289 FlagType::kDeviceConfig}; 290 Flag<uint32_t> MetricsReportingNumModsServer{"metrics.reporting-num-mods-server", 100, 291 FlagType::kDeviceConfig}; 292 293 // Whether or not we should write metrics to statsd. 294 // Note that the actual write is still controlled by 295 // MetricsReportingMods and MetricsReportingNumMods. 296 Flag<bool> MetricsWriteToStatsd{ "metrics.write-to-statsd", false, FlagType::kDeviceConfig}; 297 298 // Whether or not we should write metrics to logcat. 299 // Note that the actual write is still controlled by 300 // MetricsReportingMods and MetricsReportingNumMods. 301 Flag<bool> MetricsWriteToLogcat{ "metrics.write-to-logcat", false, FlagType::kCmdlineOnly}; 302 303 // Whether or not we should write metrics to a file. 304 // Note that the actual write is still controlled by 305 // MetricsReportingMods and MetricsReportingNumMods. 306 Flag<std::string> MetricsWriteToFile{"metrics.write-to-file", "", FlagType::kCmdlineOnly}; 307 }; 308 309 // This is the actual instance of all the flags. 310 extern Flags gFlags; 311 312 } // namespace art 313 314 #pragma clang diagnostic pop // -Wconversion 315 316 #endif // ART_LIBARTBASE_BASE_FLAGS_H_ 317