diff options
-rw-r--r-- | cmdline/cmdline_parser.h | 18 | ||||
-rw-r--r-- | cmdline/cmdline_types.h | 17 | ||||
-rw-r--r-- | libartbase/Android.bp | 1 | ||||
-rw-r--r-- | libartbase/base/flags.cc | 115 | ||||
-rw-r--r-- | libartbase/base/flags.h | 150 | ||||
-rw-r--r-- | runtime/metrics/reporter.cc | 3 | ||||
-rw-r--r-- | runtime/parsed_options.cc | 11 | ||||
-rw-r--r-- | runtime/runtime_options.def | 1 | ||||
-rwxr-xr-x | test/2232-write-metrics-to-log/run | 2 |
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 |