blob: 973a5da0e9a4de622bddec220cf6254423774f0a [file] [log] [blame]
Calin Juravle09eacd92021-05-19 17:03:55 -07001/*
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
44namespace art {
45
46// FlagMetaBase handles automatically adding flags to the command line parser. It is parameterized
47// by all supported flag types. In general, this should be treated as though it does not exist and
48// FlagBase, which is already specialized to the types we support, should be used instead.
49template <typename... T>
50class FlagMetaBase {
51 public:
52 FlagMetaBase(const std::string&& command_line_argument_name,
53 const std::string&& system_property_name,
54 const std::string&& server_setting_name) :
55 command_line_argument_name_(command_line_argument_name),
56 system_property_name_(system_property_name),
57 server_setting_name_(server_setting_name) {}
58 virtual ~FlagMetaBase() {}
59
60 template <typename Builder>
61 static void AddFlagsToCmdlineParser(Builder* builder) {
62 for (auto* flag : ALL_FLAGS) {
63 // Each flag can return a pointer to where its command line value is stored. Because these can
64 // be different types, the return value comes as a variant. The cases list below contains a
65 // lambda that is specialized to handle each branch of the variant and call the correct
66 // methods on the command line parser builder.
67 FlagValuePointer location = flag->GetCmdLineLocation();
68 auto cases = {std::function<void()>([&]() {
69 if (std::holds_alternative<std::optional<T>*>(location)) {
70 builder = &builder->Define(flag->command_line_argument_name_.c_str())
71 .template WithType<T>()
72 .IntoLocation(std::get<std::optional<T>*>(location));
73 }
74 })...};
75 for (auto c : cases) {
76 c();
77 }
78 }
79 }
80
81 // Reload the value of the flags.
82 //
83 // DO NOT CALL this outside Runtime Init or Zygote post fork.
84 // This is a convention, as we should strive to have a constant view
85 // of the flags and not change the runtime behaviour midway during execution.
86 static void ReloadAllFlags(const std::string& caller) {
87 // Check the caller. This is a simple workaround to attract the attention
88 // to a possible dangerous call to ReloadAllFlags, while avoid building
89 // a lot of infra for it or having a complex friend definition.
90 DCHECK(caller == "Init"
91 || caller == "ZygoteHooks_nativePostForkChild"
92 || caller == "ZygoteHooks_nativePostForkSystemServer"
93 || caller == "test") << caller;
94 for (auto* flag : ALL_FLAGS) {
95 flag->Reload();
96 }
97
98 if (VLOG_IS_ON(startup)) {
99 VLOG_STREAM(startup) << "Dumping flags for " << caller;
100 DumpFlags(VLOG_STREAM(startup));
101 }
102 }
103
104 // Dump all the flags info to the given stream.
105 static void DumpFlags(std::ostream& oss) {
106 for (auto* flag : ALL_FLAGS) {
107 oss << "\n{\n";
108 flag->Dump(oss);
109 oss << "\n}";
110 }
111 }
112
113 protected:
114 using FlagValuePointer = std::variant<std::optional<T>*...>;
115 // Return the pointer to the value holder associated with the cmd line location.
116 virtual FlagValuePointer GetCmdLineLocation() = 0;
117 // Reloads the flag values.
118 virtual void Reload() = 0;
119 // Dumps the flags info to the given stream.
120 virtual void Dump(std::ostream& oss) const = 0;
121
122 static std::forward_list<FlagMetaBase<T...>*> ALL_FLAGS;
123
124 const std::string command_line_argument_name_;
125 const std::string system_property_name_;
126 const std::string server_setting_name_;
127};
128
129using FlagBase = FlagMetaBase<bool, int, std::string>;
130
131template <>
132std::forward_list<FlagBase*> FlagBase::ALL_FLAGS;
133
134class FlagsTests;
135
136// This class defines a flag with a value of a particular type.
137template <typename Value>
138class Flag : public FlagBase {
139 public:
140 // Create a new Flag. The name parameter is used to generate the names from the various parameter
141 // sources. See the documentation on the Flags struct for an example.
142 explicit Flag(const std::string& name, Value default_value = {});
Calin Juravle14a51512021-06-04 12:57:16 -0700143 virtual ~Flag();
Calin Juravle09eacd92021-05-19 17:03:55 -0700144
145
146 // Returns the flag value.
147 //
148 // The value is retrieved in the following oder:
149 // 1) server side (device config) property
150 // 2) system property
151 // 3) cmdline flag
152 // 4) default value
153 ALWAYS_INLINE Value GetValue() const {
154 return std::get<0>(GetValueLocation());
155 }
156
157 ALWAYS_INLINE Value operator()() const {
158 return GetValue();
159 }
160
161 // Returns the value and the location of that value for the given flag.
162 ALWAYS_INLINE std::pair<Value, std::string> GetValueLocation() const {
163 DCHECK(initialized_);
164 if (from_server_setting_.has_value()) {
165 return std::pair{from_server_setting_.value(), server_setting_name_};
166 }
167 if (from_system_property_.has_value()) {
168 return std::pair{from_system_property_.value(), system_property_name_};
169 }
170 if (from_command_line_.has_value()) {
171 return std::pair{from_command_line_.value(), command_line_argument_name_};
172 }
173 return std::pair{default_, "default_value"};
174 }
175
176 void Dump(std::ostream& oss) const override;
177
178 protected:
179 FlagValuePointer GetCmdLineLocation() override { return &from_command_line_; }
180
181
182 // Reload the server-configured value and system property values. In general this should not be
183 // used directly, but it can be used to support reloading the value without restarting the device.
184 void Reload() override;
185
186 private:
187 bool initialized_;
188 const Value default_;
189 std::optional<Value> from_command_line_;
190 std::optional<Value> from_system_property_;
191 std::optional<Value> from_server_setting_;
192
Calin Juravle14a51512021-06-04 12:57:16 -0700193 friend class TestFlag;
Calin Juravle09eacd92021-05-19 17:03:55 -0700194};
195
196// This struct contains the list of ART flags. Flags are parameterized by the type of value they
197// support (bool, int, string, etc.). In addition to field name, flags have a name for the parameter
198// as well.
199//
200// Example:
201//
202// Flag<int> WriteMetricsToLog{"my-feature-test.flag", 42};
203//
204// This creates a boolean flag that can be read through gFlags.WriteMetricsToLog(). The default
205// value is false. Note that the default value can be left unspecified, in which the value of the
206// type's default constructor will be used.
207//
208// The flag can be set through the following generated means:
209//
210// Command Line:
211//
212// -Xmy-feature-test-flag=1
213//
214// Server Side (Phenotype) Configuration:
215//
216// persist.device_config.runtime_native.my-feature-test.flag
217//
218// System Property:
219//
220// setprop dalvik.vm.metrics.my-feature-test.flag 2
221struct Flags {
222 // Flag used to test the infra.
223 // TODO: can be removed once we add real flags.
224 Flag<int> MyFeatureTestFlag{"my-feature-test.flag", /*default_value=*/ 42};
225};
226
227// This is the actual instance of all the flags.
228extern Flags gFlags;
229
230} // namespace art
231
232#pragma clang diagnostic pop // -Wconversion
233
234#endif // ART_LIBARTBASE_BASE_FLAGS_H_