summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Eric Holk <eholk@google.com> 2021-01-30 02:03:29 +0000
committer Treehugger Robot <treehugger-gerrit@google.com> 2021-02-17 15:05:02 +0000
commit3dba023d4fb47882fa215715c196cfa3be30c098 (patch)
treeda22f82b7cde87f37100f715a0e05f332b377f73
parent381d35c1b01401e193544f679bfaf4f5c197f7cd (diff)
Add a server-configurable flags API
Adds a new set of flags that can be set by the command line, server configuration, or system properties. These flags can be used to enable or disable certain features or otherwise change their behavior. The flexible configuration options facilitate both development and also experimentation. As an example of their use, this CL also moves the -Xwrite-metrics-to-log command line option to the flags system instead. Future work will migrate the rest of the metrics settings. Test: ./test/run-test --host --jit 2232-write-metrics-to-log Bug: 175050458 Change-Id: I1ef37e7d355204a3172b7aa5c0baa4cbd8c7076b
-rw-r--r--cmdline/cmdline_parser.h18
-rw-r--r--cmdline/cmdline_types.h17
-rw-r--r--libartbase/Android.bp1
-rw-r--r--libartbase/base/flags.cc115
-rw-r--r--libartbase/base/flags.h150
-rw-r--r--runtime/metrics/reporter.cc3
-rw-r--r--runtime/parsed_options.cc11
-rw-r--r--runtime/runtime_options.def1
-rwxr-xr-xtest/2232-write-metrics-to-log/run2
9 files changed, 310 insertions, 8 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 2d7d5f1b78..7f01be6472 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|0|1|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 c3a63643e4..77ff6b2a64 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -28,6 +28,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",
diff --git a/libartbase/base/flags.cc b/libartbase/base/flags.cc
new file mode 100644
index 0000000000..079694c835
--- /dev/null
+++ b/libartbase/base/flags.cc
@@ -0,0 +1,115 @@
+/*
+ * 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/properties.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wconversion"
+
+namespace {
+constexpr const char* kServerConfigurableFlagsPrefix = "persist.device_config.runtime_native_boot.";
+constexpr const char* kUndefinedValue = "UNSET";
+
+// 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;
+ }
+}
+
+} // 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;
+
+template <typename Value>
+Flag<Value>::Flag(const std::string& name, Value default_value) : default_{default_value} {
+ command_line_argument_name_ = "-X" + name + "=_";
+ std::replace(command_line_argument_name_.begin(), command_line_argument_name_.end(), '.', '-');
+ system_property_name_ = "dalvik.vm." + name;
+
+ std::string server_setting = name;
+ std::replace(server_setting.begin(), server_setting.end(), '.', '_');
+ std::replace(server_setting.begin(), server_setting.end(), '-', '_');
+ server_setting_name_ = kServerConfigurableFlagsPrefix + server_setting;
+
+ ALL_FLAGS.push_front(this);
+}
+
+template <typename Value>
+Value Flag<Value>::operator()() {
+ if (!initialized_) {
+ Reload();
+ }
+ if (from_command_line_.has_value()) {
+ return from_command_line_.value();
+ }
+ if (from_server_setting_.has_value()) {
+ return from_server_setting_.value();
+ }
+ if (from_system_property_.has_value()) {
+ return from_system_property_.value();
+ }
+ return default_;
+}
+
+template <typename Value>
+void Flag<Value>::Reload() {
+ // Check system properties and server configured value.
+ from_system_property_ = std::nullopt;
+ const std::string sysprop = ::android::base::GetProperty(system_property_name_, kUndefinedValue);
+ if (sysprop != kUndefinedValue) {
+ ParseValue(sysprop, &from_system_property_);
+ }
+
+ // Check the server-side configuration
+ from_server_setting_ = std::nullopt;
+ // Read the device_config setting directly to avoid a dependency on server_configurable_flags.
+ const std::string server_config =
+ ::android::base::GetProperty(server_setting_name_, kUndefinedValue);
+ if (server_config != kUndefinedValue) {
+ ParseValue(server_config, &from_server_setting_);
+ }
+
+ // Command line argument cannot be reloaded. It must be set during initial command line parsing.
+
+ initialized_ = true;
+}
+
+template class Flag<bool>;
+
+} // 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..5bd18e5328
--- /dev/null
+++ b/libartbase/base/flags.h
@@ -0,0 +1,150 @@
+/*
+ * 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>
+
+// 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 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:
+ 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->GetLocation();
+ auto cases = {[&]() {
+ 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();
+ }
+ }
+ }
+
+ protected:
+ using FlagValuePointer = std::variant<std::optional<T>*...>;
+ static std::forward_list<FlagMetaBase<T...>*> ALL_FLAGS;
+
+ std::string command_line_argument_name_;
+ std::string system_property_name_;
+ std::string server_setting_name_;
+
+ virtual FlagValuePointer GetLocation() = 0;
+};
+
+using FlagBase = FlagMetaBase<bool>;
+
+template <>
+std::forward_list<FlagBase*> FlagBase::ALL_FLAGS;
+
+// 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 value of the flag.
+ //
+ // The value returned will be the command line argument, if present, otherwise the
+ // server-configured value, if present, otherwise the system property value, if present, and
+ // finally, the default value.
+ Value operator()();
+
+ // 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();
+
+ protected:
+ FlagValuePointer GetLocation() override { return &from_command_line_; }
+
+ private:
+ bool initialized_{false};
+ const Value default_;
+ std::optional<Value> from_command_line_;
+ std::optional<Value> from_system_property_;
+ std::optional<Value> from_server_setting_;
+};
+
+// 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<bool> WriteMetricsToLog{"metrics.write-to-log", false};
+//
+// 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:
+//
+// -Xmetrics-write-to-log=true
+//
+// Server Side Configuration:
+//
+// runtime_native_boot.metrics_write_to_log
+//
+// System Property:
+//
+// setprop dalvik.vm.metrics.write-to-log true
+struct Flags {
+ Flag<bool> WriteMetricsToLog{"metrics.write-to-log", false};
+};
+
+// 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/runtime/metrics/reporter.cc b/runtime/metrics/reporter.cc
index 6e0106f4b4..700775b2fe 100644
--- a/runtime/metrics/reporter.cc
+++ b/runtime/metrics/reporter.cc
@@ -16,6 +16,7 @@
#include "reporter.h"
+#include "base/flags.h"
#include "runtime.h"
#include "runtime_options.h"
#include "statsd.h"
@@ -136,7 +137,7 @@ void MetricsReporter::ReportMetrics() const {
ReportingConfig ReportingConfig::FromRuntimeArguments(const RuntimeArgumentMap& args) {
using M = RuntimeArgumentMap;
return {
- .dump_to_logcat = args.Exists(M::WriteMetricsToLog),
+ .dump_to_logcat = gFlags.WriteMetricsToLog(),
.dump_to_file = args.GetOptional(M::WriteMetricsToFile),
.dump_to_statsd = args.Exists(M::WriteMetricsToStatsd),
.report_metrics_on_shutdown = !args.Exists(M::DisableFinalMetricsReport),
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index ec5f4869da..f082a5305d 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"
@@ -394,9 +395,6 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.IntoKey(M::CorePlatformApiPolicy)
.Define("-Xuse-stderr-logger")
.IntoKey(M::UseStderrLogger)
- .Define("-Xwrite-metrics-to-log")
- .WithHelp("Enables writing ART metrics to logcat")
- .IntoKey(M::WriteMetricsToLog)
.Define("-Xwrite-metrics-to-file=_")
.WithHelp("Enables writing ART metrics to the given file")
.WithType<std::string>()
@@ -445,8 +443,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_options.def b/runtime/runtime_options.def
index ad4d4a147a..7c8c56b3e7 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -187,7 +187,6 @@ RUNTIME_OPTIONS_KEY (bool, PerfettoHprof, false)
RUNTIME_OPTIONS_KEY (bool, PerfettoJavaHeapStackProf, false)
// Whether to dump ART metrics to logcat
-RUNTIME_OPTIONS_KEY (Unit, WriteMetricsToLog)
RUNTIME_OPTIONS_KEY (std::string, WriteMetricsToFile)
RUNTIME_OPTIONS_KEY (Unit, WriteMetricsToStatsd)
RUNTIME_OPTIONS_KEY (Unit, DisableFinalMetricsReport)
diff --git a/test/2232-write-metrics-to-log/run b/test/2232-write-metrics-to-log/run
index b170317971..4d357e045e 100755
--- a/test/2232-write-metrics-to-log/run
+++ b/test/2232-write-metrics-to-log/run
@@ -15,4 +15,4 @@
# limitations under the License.
export ANDROID_LOG_TAGS="*:i"
-exec ${RUN} $@ --external-log-tags --runtime-option -Xwrite-metrics-to-log
+exec ${RUN} $@ --external-log-tags --runtime-option -Xmetrics-write-to-log=true