summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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