diff options
author | 2022-05-11 09:46:58 +0000 | |
---|---|---|
committer | 2022-05-18 09:51:20 +0000 | |
commit | e22579cc46fc9deab2d2ae2fb78f5e116f54cde2 (patch) | |
tree | c6f40d279f4c186a2fbe15e8194ea8bb0d125804 | |
parent | 2201364058cb49a3298619cd8001a3e7df68106a (diff) |
Allow multiple output formats for runtime metrics
* Add base abstract class MetricsFormatter
* Extract current formatting into TextFormatter
* Add XmlFormatter for XML output format
* Add command line flag for xml format
Test: test.py (gTests)
Test: atest art_standalone_libartbase_tests
Test: art/build/apex/runtests.sh
Bug: 175048705
Size of APEX files:
* before the changes:
* com.android.art.apex: 61,001,728 bytes (59 MB)
* com.android.art.capex: 22,757,376 bytes (22 MB)
* after the changes:
* com.android.art.apex: 60,928,000 bytes (-73,728 bytes)
* com.android.art.capex: 22,728,704 bytes (-28,672 bytes)
Change-Id: Ie80abaf67ffc721d7da59b8115f655630b21cae4
-rw-r--r-- | libartbase/Android.bp | 5 | ||||
-rw-r--r-- | libartbase/base/flags.h | 6 | ||||
-rw-r--r-- | libartbase/base/metrics/metrics.h | 84 | ||||
-rw-r--r-- | libartbase/base/metrics/metrics_common.cc | 148 | ||||
-rw-r--r-- | libartbase/base/metrics/metrics_test.cc | 263 | ||||
-rw-r--r-- | odrefresh/Android.bp | 1 | ||||
-rw-r--r-- | runtime/Android.bp | 1 | ||||
-rw-r--r-- | runtime/metrics/reporter.cc | 12 | ||||
-rw-r--r-- | runtime/metrics/reporter.h | 3 |
9 files changed, 484 insertions, 39 deletions
diff --git a/libartbase/Android.bp b/libartbase/Android.bp index f9375239e4..80c63c6cfb 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -70,6 +70,7 @@ cc_defaults { // ZipArchive support, the order matters here to get all symbols. "libziparchive", ], + whole_static_libs: ["libtinyxml2"], shared_libs: [ "libz", "liblog", @@ -88,6 +89,7 @@ cc_defaults { static: { cflags: ["-DART_STATIC_LIBARTBASE"], }, + whole_static_libs: ["libtinyxml2"], shared_libs: [ "libziparchive", "libz", @@ -112,6 +114,7 @@ cc_defaults { // For common macros. "libbase", ], + whole_static_libs: ["libtinyxml2"], export_static_lib_headers: ["libbase"], // ART's macros.h depends on libbase's macros.h. cflags: ["-Wno-thread-safety"], @@ -391,6 +394,8 @@ cc_library_headers { shared_libs: ["libbase"], export_shared_lib_headers: ["libbase"], + whole_static_libs: ["libtinyxml2"], + apex_available: [ "com.android.art", "com.android.art.debug", diff --git a/libartbase/base/flags.h b/libartbase/base/flags.h index d1e1ca6243..fc568b2be2 100644 --- a/libartbase/base/flags.h +++ b/libartbase/base/flags.h @@ -304,6 +304,12 @@ struct Flags { // Note that the actual write is still controlled by // MetricsReportingMods and MetricsReportingNumMods. Flag<std::string> MetricsWriteToFile{"metrics.write-to-file", "", FlagType::kCmdlineOnly}; + + // The output format for metrics. This is only used + // when writing metrics to a file; metrics written + // to logcat will be in human-readable text format. + // Supported values are "text" and "xml". + Flag<std::string> MetricsFormat{"metrics.format", "text", FlagType::kCmdlineOnly}; }; // This is the actual instance of all the flags. diff --git a/libartbase/base/metrics/metrics.h b/libartbase/base/metrics/metrics.h index 701054e93b..4a1664ad24 100644 --- a/libartbase/base/metrics/metrics.h +++ b/libartbase/base/metrics/metrics.h @@ -30,6 +30,7 @@ #include "android-base/logging.h" #include "base/bit_utils.h" #include "base/time_utils.h" +#include "tinyxml2.h" #pragma clang diagnostic push #pragma clang diagnostic error "-Wconversion" @@ -433,12 +434,80 @@ class MetricsAccumulator final : MetricsBase<T> { friend class ArtMetrics; }; -// A backend that writes metrics in a human-readable format to a string. +// Base class for formatting metrics into different formats +// (human-readable text, JSON, etc.) +class MetricsFormatter { + public: + virtual ~MetricsFormatter() = default; + + virtual void FormatBeginReport(uint64_t timestamp_since_start_ms, + const std::optional<SessionData>& session_data) = 0; + virtual void FormatEndReport() = 0; + virtual void FormatReportCounter(DatumId counter_type, uint64_t value) = 0; + virtual void FormatReportHistogram(DatumId histogram_type, + int64_t low_value, + int64_t high_value, + const std::vector<uint32_t>& buckets) = 0; + virtual std::string GetAndResetBuffer() = 0; + + protected: + const std::string version = "1.0"; +}; + +// Formatter outputting metrics in human-readable text format +class TextFormatter : public MetricsFormatter { + public: + TextFormatter() = default; + + void FormatBeginReport(uint64_t timestamp_millis, + const std::optional<SessionData>& session_data) override; + + void FormatReportCounter(DatumId counter_type, uint64_t value) override; + + void FormatReportHistogram(DatumId histogram_type, + int64_t low_value, + int64_t high_value, + const std::vector<uint32_t>& buckets) override; + + void FormatEndReport() override; + + std::string GetAndResetBuffer() override; + + private: + std::ostringstream os_; +}; + +// Formatter outputting metrics in XML format +class XmlFormatter : public MetricsFormatter { + public: + XmlFormatter() = default; + + void FormatBeginReport(uint64_t timestamp_millis, + const std::optional<SessionData>& session_data) override; + + void FormatReportCounter(DatumId counter_type, uint64_t value) override; + + void FormatReportHistogram(DatumId histogram_type, + int64_t low_value, + int64_t high_value, + const std::vector<uint32_t>& buckets) override; + + void FormatEndReport() override; + + std::string GetAndResetBuffer() override; + + private: + tinyxml2::XMLDocument document_; +}; + +// A backend that writes metrics to a string. +// The format of the metrics' output is delegated +// to the MetricsFormatter class. // // This is used as a base for LogBackend and FileBackend. class StringBackend : public MetricsBackend { public: - StringBackend(); + explicit StringBackend(std::unique_ptr<MetricsFormatter> formatter); void BeginOrUpdateSession(const SessionData& session_data) override; @@ -456,14 +525,15 @@ class StringBackend : public MetricsBackend { std::string GetAndResetBuffer(); private: - std::ostringstream os_; + std::unique_ptr<MetricsFormatter> formatter_; std::optional<SessionData> session_data_; }; // A backend that writes metrics in human-readable format to the log (i.e. logcat). class LogBackend : public StringBackend { public: - explicit LogBackend(android::base::LogSeverity level); + explicit LogBackend(std::unique_ptr<MetricsFormatter> formatter, + android::base::LogSeverity level); void BeginReport(uint64_t timestamp_millis) override; void EndReport() override; @@ -473,12 +543,10 @@ class LogBackend : public StringBackend { }; // A backend that writes metrics to a file. -// -// These are currently written in the same human-readable format used by StringBackend and -// LogBackend, but we will probably want a more machine-readable format in the future. class FileBackend : public StringBackend { public: - explicit FileBackend(const std::string& filename); + explicit FileBackend(std::unique_ptr<MetricsFormatter> formatter, + const std::string& filename); void BeginReport(uint64_t timestamp_millis) override; void EndReport() override; diff --git a/libartbase/base/metrics/metrics_common.cc b/libartbase/base/metrics/metrics_common.cc index f09987ba8e..025f5eb79e 100644 --- a/libartbase/base/metrics/metrics_common.cc +++ b/libartbase/base/metrics/metrics_common.cc @@ -76,7 +76,7 @@ void ArtMetrics::ReportAllMetrics(MetricsBackend* backend) const { } void ArtMetrics::DumpForSigQuit(std::ostream& os) const { - StringBackend backend; + StringBackend backend(std::make_unique<TextFormatter>()); ReportAllMetrics(&backend); os << backend.GetAndResetBuffer(); } @@ -88,13 +88,12 @@ void ArtMetrics::Reset() { #undef ART_METRIC } -StringBackend::StringBackend() {} +StringBackend::StringBackend(std::unique_ptr<MetricsFormatter> formatter) + : formatter_(std::move(formatter)) +{} std::string StringBackend::GetAndResetBuffer() { - std::string result = os_.str(); - os_.clear(); - os_.str(""); - return result; + return formatter_->GetAndResetBuffer(); } void StringBackend::BeginOrUpdateSession(const SessionData& session_data) { @@ -102,32 +101,51 @@ void StringBackend::BeginOrUpdateSession(const SessionData& session_data) { } void StringBackend::BeginReport(uint64_t timestamp_since_start_ms) { + formatter_->FormatBeginReport(timestamp_since_start_ms, session_data_); +} + +void StringBackend::EndReport() { + formatter_->FormatEndReport(); +} + +void StringBackend::ReportCounter(DatumId counter_type, uint64_t value) { + formatter_->FormatReportCounter(counter_type, value); +} + +void StringBackend::ReportHistogram(DatumId histogram_type, + int64_t minimum_value_, + int64_t maximum_value_, + const std::vector<uint32_t>& buckets) { + formatter_->FormatReportHistogram(histogram_type, minimum_value_, maximum_value_, buckets); +} + +void TextFormatter::FormatBeginReport(uint64_t timestamp_since_start_ms, + const std::optional<SessionData>& session_data) { os_ << "\n*** ART internal metrics ***\n"; os_ << " Metadata:\n"; os_ << " timestamp_since_start_ms: " << timestamp_since_start_ms << "\n"; - if (session_data_.has_value()) { - os_ << " session_id: " << session_data_->session_id << "\n"; - os_ << " uid: " << session_data_->uid << "\n"; - os_ << " compilation_reason: " << CompilationReasonName(session_data_->compilation_reason) + if (session_data.has_value()) { + os_ << " session_id: " << session_data->session_id << "\n"; + os_ << " uid: " << session_data->uid << "\n"; + os_ << " compilation_reason: " << CompilationReasonName(session_data->compilation_reason) << "\n"; - os_ << " compiler_filter: " << CompilerFilterReportingName(session_data_->compiler_filter) + os_ << " compiler_filter: " << CompilerFilterReportingName(session_data->compiler_filter) << "\n"; } os_ << " Metrics:\n"; } -void StringBackend::EndReport() { os_ << "*** Done dumping ART internal metrics ***\n"; } - -void StringBackend::ReportCounter(DatumId counter_type, uint64_t value) { +void TextFormatter::FormatReportCounter(DatumId counter_type, uint64_t value) { os_ << " " << DatumName(counter_type) << ": count = " << value << "\n"; } -void StringBackend::ReportHistogram(DatumId histogram_type, - int64_t minimum_value_, - int64_t maximum_value_, - const std::vector<uint32_t>& buckets) { - os_ << " " << DatumName(histogram_type) << ": range = " << minimum_value_ << "..." << maximum_value_; - if (buckets.size() > 0) { +void TextFormatter::FormatReportHistogram(DatumId histogram_type, + int64_t minimum_value_, + int64_t maximum_value_, + const std::vector<uint32_t>& buckets) { + os_ << " " << DatumName(histogram_type) << ": range = " + << minimum_value_ << "..." << maximum_value_; + if (!buckets.empty()) { os_ << ", buckets: "; bool first = true; for (const auto& count : buckets) { @@ -143,22 +161,100 @@ void StringBackend::ReportHistogram(DatumId histogram_type, } } -LogBackend::LogBackend(android::base::LogSeverity level) : level_{level} {} +void TextFormatter::FormatEndReport() { + os_ << "*** Done dumping ART internal metrics ***\n"; +} + +std::string TextFormatter::GetAndResetBuffer() { + std::string result = os_.str(); + os_.clear(); + os_.str(""); + return result; +} + +void XmlFormatter::FormatBeginReport(uint64_t timestamp_millis, + const std::optional<SessionData>& session_data) { + tinyxml2::XMLElement* art_runtime_metrics = document_.NewElement("art_runtime_metrics"); + document_.InsertEndChild(art_runtime_metrics); + + art_runtime_metrics->InsertNewChildElement("version")->SetText(version.data()); + + tinyxml2::XMLElement* metadata = art_runtime_metrics->InsertNewChildElement("metadata"); + metadata->InsertNewChildElement("timestamp_since_start_ms")->SetText(timestamp_millis); + + if (session_data.has_value()) { + metadata->InsertNewChildElement("session_id")->SetText(session_data->session_id); + metadata->InsertNewChildElement("uid")->SetText(session_data->uid); + metadata + ->InsertNewChildElement("compilation_reason") + ->SetText(CompilationReasonName(session_data->compilation_reason)); + metadata + ->InsertNewChildElement("compiler_filter") + ->SetText(CompilerFilterReportingName(session_data->compiler_filter)); + } + + art_runtime_metrics->InsertNewChildElement("metrics"); +} + +void XmlFormatter::FormatReportCounter(DatumId counter_type, uint64_t value) { + tinyxml2::XMLElement* metrics = document_.RootElement()->FirstChildElement("metrics"); + + tinyxml2::XMLElement* counter = metrics->InsertNewChildElement(DatumName(counter_type).data()); + counter->InsertNewChildElement("counter_type")->SetText("count"); + counter->InsertNewChildElement("value")->SetText(value); +} + +void XmlFormatter::FormatReportHistogram(DatumId histogram_type, + int64_t low_value, + int64_t high_value, + const std::vector<uint32_t>& buckets) { + tinyxml2::XMLElement* metrics = document_.RootElement()->FirstChildElement("metrics"); + + tinyxml2::XMLElement* histogram = + metrics->InsertNewChildElement(DatumName(histogram_type).data()); + histogram->InsertNewChildElement("counter_type")->SetText("histogram"); + histogram->InsertNewChildElement("minimum_value")->SetText(low_value); + histogram->InsertNewChildElement("maximum_value")->SetText(high_value); + + tinyxml2::XMLElement* buckets_element = histogram->InsertNewChildElement("buckets"); + for (const auto& count : buckets) { + buckets_element->InsertNewChildElement("bucket")->SetText(count); + } +} + +void XmlFormatter::FormatEndReport() {} + +std::string XmlFormatter::GetAndResetBuffer() { + tinyxml2::XMLPrinter printer(/*file=*/nullptr, /*compact=*/true); + document_.Print(&printer); + std::string result = printer.CStr(); + document_.Clear(); + + return result; +} + +LogBackend::LogBackend(std::unique_ptr<MetricsFormatter> formatter, + android::base::LogSeverity level) + : StringBackend{std::move(formatter)}, level_{level} +{} void LogBackend::BeginReport(uint64_t timestamp_since_start_ms) { - GetAndResetBuffer(); + StringBackend::GetAndResetBuffer(); StringBackend::BeginReport(timestamp_since_start_ms); } void LogBackend::EndReport() { StringBackend::EndReport(); - LOG_STREAM(level_) << GetAndResetBuffer(); + LOG_STREAM(level_) << StringBackend::GetAndResetBuffer(); } -FileBackend::FileBackend(const std::string& filename) : filename_{filename} {} +FileBackend::FileBackend(std::unique_ptr<MetricsFormatter> formatter, + const std::string& filename) + : StringBackend{std::move(formatter)}, filename_{filename} +{} void FileBackend::BeginReport(uint64_t timestamp_since_start_ms) { - GetAndResetBuffer(); + StringBackend::GetAndResetBuffer(); StringBackend::BeginReport(timestamp_since_start_ms); } @@ -170,7 +266,7 @@ void FileBackend::EndReport() { if (file.get() == nullptr) { LOG(WARNING) << "Could open metrics file '" << filename_ << "': " << error_message; } else { - if (!android::base::WriteStringToFd(GetAndResetBuffer(), file.get()->Fd())) { + if (!android::base::WriteStringToFd(StringBackend::GetAndResetBuffer(), file.get()->Fd())) { PLOG(WARNING) << "Error writing metrics to file"; } } diff --git a/libartbase/base/metrics/metrics_test.cc b/libartbase/base/metrics/metrics_test.cc index ed6f88d709..282029012b 100644 --- a/libartbase/base/metrics/metrics_test.cc +++ b/libartbase/base/metrics/metrics_test.cc @@ -16,6 +16,7 @@ #include "metrics.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "metrics_test.h" @@ -248,7 +249,7 @@ TEST_F(MetricsTest, HistogramTimer) { // Makes sure all defined metrics are included when dumping through StreamBackend. TEST_F(MetricsTest, StreamBackendDumpAllMetrics) { ArtMetrics metrics; - StringBackend backend; + StringBackend backend(std::make_unique<TextFormatter>()); metrics.ReportAllMetrics(&backend); @@ -305,6 +306,266 @@ TEST_F(MetricsTest, ResetMetrics) { metrics.ReportAllMetrics(&zero_backend); } +TEST(TextFormatterTest, ReportMetrics_WithBuckets) { + TextFormatter text_formatter; + SessionData session_data { + .session_id = 1000, + .uid = 50, + .compilation_reason = CompilationReason::kInstall, + .compiler_filter = CompilerFilterReporting::kSpeed, + }; + + text_formatter.FormatBeginReport(200, session_data); + text_formatter.FormatReportCounter(DatumId::kFullGcCount, 1u); + text_formatter.FormatReportHistogram(DatumId::kFullGcCollectionTime, + 50, + 200, + {2, 4, 7, 1}); + text_formatter.FormatEndReport(); + + const std::string result = text_formatter.GetAndResetBuffer(); + ASSERT_EQ(result, + "\n*** ART internal metrics ***\n" + " Metadata:\n" + " timestamp_since_start_ms: 200\n" + " session_id: 1000\n" + " uid: 50\n" + " compilation_reason: install\n" + " compiler_filter: speed\n" + " Metrics:\n" + " FullGcCount: count = 1\n" + " FullGcCollectionTime: range = 50...200, buckets: 2,4,7,1\n" + "*** Done dumping ART internal metrics ***\n"); +} + +TEST(TextFormatterTest, ReportMetrics_NoBuckets) { + TextFormatter text_formatter; + SessionData session_data { + .session_id = 500, + .uid = 15, + .compilation_reason = CompilationReason::kCmdLine, + .compiler_filter = CompilerFilterReporting::kExtract, + }; + + text_formatter.FormatBeginReport(400, session_data); + text_formatter.FormatReportHistogram(DatumId::kFullGcCollectionTime, 10, 20, {}); + text_formatter.FormatEndReport(); + + std::string result = text_formatter.GetAndResetBuffer(); + ASSERT_EQ(result, + "\n*** ART internal metrics ***\n" + " Metadata:\n" + " timestamp_since_start_ms: 400\n" + " session_id: 500\n" + " uid: 15\n" + " compilation_reason: cmdline\n" + " compiler_filter: extract\n" + " Metrics:\n" + " FullGcCollectionTime: range = 10...20, no buckets\n" + "*** Done dumping ART internal metrics ***\n"); +} + +TEST(TextFormatterTest, BeginReport_NoSessionData) { + TextFormatter text_formatter; + std::optional<SessionData> empty_session_data; + + text_formatter.FormatBeginReport(100, empty_session_data); + text_formatter.FormatEndReport(); + + std::string result = text_formatter.GetAndResetBuffer(); + ASSERT_EQ(result, + "\n*** ART internal metrics ***\n" + " Metadata:\n" + " timestamp_since_start_ms: 100\n" + " Metrics:\n" + "*** Done dumping ART internal metrics ***\n"); +} + +TEST(TextFormatterTest, GetAndResetBuffer_ActuallyResetsBuffer) { + TextFormatter text_formatter; + std::optional<SessionData> empty_session_data; + + text_formatter.FormatBeginReport(200, empty_session_data); + text_formatter.FormatReportCounter(DatumId::kFullGcCount, 1u); + text_formatter.FormatEndReport(); + + std::string result = text_formatter.GetAndResetBuffer(); + ASSERT_EQ(result, + "\n*** ART internal metrics ***\n" + " Metadata:\n" + " timestamp_since_start_ms: 200\n" + " Metrics:\n" + " FullGcCount: count = 1\n" + "*** Done dumping ART internal metrics ***\n"); + + text_formatter.FormatBeginReport(300, empty_session_data); + text_formatter.FormatReportCounter(DatumId::kFullGcCount, 5u); + text_formatter.FormatEndReport(); + + result = text_formatter.GetAndResetBuffer(); + ASSERT_EQ(result, + "\n*** ART internal metrics ***\n" + " Metadata:\n" + " timestamp_since_start_ms: 300\n" + " Metrics:\n" + " FullGcCount: count = 5\n" + "*** Done dumping ART internal metrics ***\n"); +} + +TEST(XmlFormatterTest, ReportMetrics_WithBuckets) { + XmlFormatter xml_formatter; + SessionData session_data { + .session_id = 123, + .uid = 456, + .compilation_reason = CompilationReason::kFirstBoot, + .compiler_filter = CompilerFilterReporting::kSpace, + }; + + xml_formatter.FormatBeginReport(250, session_data); + xml_formatter.FormatReportCounter(DatumId::kYoungGcCount, 3u); + xml_formatter.FormatReportHistogram(DatumId::kYoungGcCollectionTime, + 300, + 600, + {1, 5, 3}); + xml_formatter.FormatEndReport(); + + const std::string result = xml_formatter.GetAndResetBuffer(); + ASSERT_EQ(result, + "<art_runtime_metrics>" + "<version>1.0</version>" + "<metadata>" + "<timestamp_since_start_ms>250</timestamp_since_start_ms>" + "<session_id>123</session_id>" + "<uid>456</uid>" + "<compilation_reason>first-boot</compilation_reason>" + "<compiler_filter>space</compiler_filter>" + "</metadata>" + "<metrics>" + "<YoungGcCount>" + "<counter_type>count</counter_type>" + "<value>3</value>" + "</YoungGcCount>" + "<YoungGcCollectionTime>" + "<counter_type>histogram</counter_type>" + "<minimum_value>300</minimum_value>" + "<maximum_value>600</maximum_value>" + "<buckets>" + "<bucket>1</bucket>" + "<bucket>5</bucket>" + "<bucket>3</bucket>" + "</buckets>" + "</YoungGcCollectionTime>" + "</metrics>" + "</art_runtime_metrics>"); +} + +TEST(XmlFormatterTest, ReportMetrics_NoBuckets) { + XmlFormatter xml_formatter; + SessionData session_data { + .session_id = 234, + .uid = 345, + .compilation_reason = CompilationReason::kFirstBoot, + .compiler_filter = CompilerFilterReporting::kSpace, + }; + + xml_formatter.FormatBeginReport(160, session_data); + xml_formatter.FormatReportCounter(DatumId::kYoungGcCount, 4u); + xml_formatter.FormatReportHistogram(DatumId::kYoungGcCollectionTime, 20, 40, {}); + xml_formatter.FormatEndReport(); + + const std::string result = xml_formatter.GetAndResetBuffer(); + ASSERT_EQ(result, + "<art_runtime_metrics>" + "<version>1.0</version>" + "<metadata>" + "<timestamp_since_start_ms>160</timestamp_since_start_ms>" + "<session_id>234</session_id>" + "<uid>345</uid>" + "<compilation_reason>first-boot</compilation_reason>" + "<compiler_filter>space</compiler_filter>" + "</metadata>" + "<metrics>" + "<YoungGcCount>" + "<counter_type>count</counter_type>" + "<value>4</value>" + "</YoungGcCount>" + "<YoungGcCollectionTime>" + "<counter_type>histogram</counter_type>" + "<minimum_value>20</minimum_value>" + "<maximum_value>40</maximum_value>" + "<buckets/>" + "</YoungGcCollectionTime>" + "</metrics>" + "</art_runtime_metrics>"); +} + +TEST(XmlFormatterTest, BeginReport_NoSessionData) { + XmlFormatter xml_formatter; + std::optional<SessionData> empty_session_data; + + xml_formatter.FormatBeginReport(100, empty_session_data); + xml_formatter.FormatReportCounter(DatumId::kYoungGcCount, 3u); + xml_formatter.FormatEndReport(); + + std::string result = xml_formatter.GetAndResetBuffer(); + ASSERT_EQ(result, + "<art_runtime_metrics>" + "<version>1.0</version>" + "<metadata>" + "<timestamp_since_start_ms>100</timestamp_since_start_ms>" + "</metadata>" + "<metrics>" + "<YoungGcCount>" + "<counter_type>count</counter_type>" + "<value>3</value>" + "</YoungGcCount>" + "</metrics>" + "</art_runtime_metrics>"); +} + +TEST(XmlFormatterTest, GetAndResetBuffer_ActuallyResetsBuffer) { + XmlFormatter xml_formatter; + std::optional<SessionData> empty_session_data; + + xml_formatter.FormatBeginReport(200, empty_session_data); + xml_formatter.FormatReportCounter(DatumId::kFullGcCount, 1u); + xml_formatter.FormatEndReport(); + + std::string result = xml_formatter.GetAndResetBuffer(); + ASSERT_EQ(result, + "<art_runtime_metrics>" + "<version>1.0</version>" + "<metadata>" + "<timestamp_since_start_ms>200</timestamp_since_start_ms>" + "</metadata>" + "<metrics>" + "<FullGcCount>" + "<counter_type>count</counter_type>" + "<value>1</value>" + "</FullGcCount>" + "</metrics>" + "</art_runtime_metrics>"); + + xml_formatter.FormatBeginReport(300, empty_session_data); + xml_formatter.FormatReportCounter(DatumId::kFullGcCount, 5u); + xml_formatter.FormatEndReport(); + + result = xml_formatter.GetAndResetBuffer(); + ASSERT_EQ(result, + "<art_runtime_metrics>" + "<version>1.0</version>" + "<metadata>" + "<timestamp_since_start_ms>300</timestamp_since_start_ms>" + "</metadata>" + "<metrics>" + "<FullGcCount>" + "<counter_type>count</counter_type>" + "<value>5</value>" + "</FullGcCount>" + "</metrics>" + "</art_runtime_metrics>"); +} + TEST(CompilerFilterReportingTest, FromName) { ASSERT_EQ(CompilerFilterReportingFromName("error"), CompilerFilterReporting::kError); diff --git a/odrefresh/Android.bp b/odrefresh/Android.bp index 8fee83a38a..55a1fdea2c 100644 --- a/odrefresh/Android.bp +++ b/odrefresh/Android.bp @@ -47,7 +47,6 @@ cc_defaults { ], static_libs: [ "libc++fs", - "libtinyxml2", ], tidy: true, tidy_disabled_srcs: [":art-apex-cache-info"], diff --git a/runtime/Android.bp b/runtime/Android.bp index 9e0e78ebdb..98ddbb7210 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -415,7 +415,6 @@ libart_cc_defaults { ], static_libs: [ "libstatslog_art", - "libtinyxml2", ], generated_sources: [ "apex-info-list-tinyxml", diff --git a/runtime/metrics/reporter.cc b/runtime/metrics/reporter.cc index a44066e487..28ca997cec 100644 --- a/runtime/metrics/reporter.cc +++ b/runtime/metrics/reporter.cc @@ -126,10 +126,17 @@ void MetricsReporter::BackgroundThreadRun() { // Configure the backends if (config_.dump_to_logcat) { - backends_.emplace_back(new LogBackend(LogSeverity::INFO)); + backends_.emplace_back(new LogBackend(std::make_unique<TextFormatter>(), LogSeverity::INFO)); } if (config_.dump_to_file.has_value()) { - backends_.emplace_back(new FileBackend(config_.dump_to_file.value())); + std::unique_ptr<MetricsFormatter> formatter; + if (config_.metrics_format == "xml") { + formatter = std::make_unique<XmlFormatter>(); + } else { + formatter = std::make_unique<TextFormatter>(); + } + + backends_.emplace_back(new FileBackend(std::move(formatter), config_.dump_to_file.value())); } if (config_.dump_to_statsd) { auto backend = CreateStatsdBackend(); @@ -291,6 +298,7 @@ ReportingConfig ReportingConfig::FromFlags(bool is_system_server) { .dump_to_logcat = gFlags.MetricsWriteToLogcat(), .dump_to_file = gFlags.MetricsWriteToFile.GetValueOptional(), .dump_to_statsd = gFlags.MetricsWriteToStatsd(), + .metrics_format = gFlags.MetricsFormat(), .period_spec = period_spec, .reporting_num_mods = reporting_num_mods, .reporting_mods = reporting_mods, diff --git a/runtime/metrics/reporter.h b/runtime/metrics/reporter.h index daeaf1fa18..af9e0ca151 100644 --- a/runtime/metrics/reporter.h +++ b/runtime/metrics/reporter.h @@ -78,6 +78,9 @@ struct ReportingConfig { // If set, provides a file name to enable metrics logging to a file. std::optional<std::string> dump_to_file; + // Provides the desired output format for metrics written to a file. + std::string metrics_format; + // The reporting period configuration. std::optional<ReportingPeriodSpec> period_spec; |