aboutsummaryrefslogtreecommitdiff
path: root/cpp/watchdog
diff options
context:
space:
mode:
authorTreeHugger Robot <treehugger-gerrit@google.com>2021-06-22 17:52:26 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2021-06-22 17:52:26 +0000
commitcd48bd670e349e6f56d552bf905983933eb049b7 (patch)
treee7552c5ab7ceb4daefff55bd712eae6d543373df /cpp/watchdog
parent4878af06abd0249657fd348a74bfe997640d2019 (diff)
parent0a022dc3c90971174efec1bbe82417ad2ad08039 (diff)
downloadCar-cd48bd670e349e6f56d552bf905983933eb049b7.tar.gz
Merge "Implement resource overuse configuration XML writing." into sc-dev
Diffstat (limited to 'cpp/watchdog')
-rw-r--r--cpp/watchdog/server/src/OveruseConfigurationXmlHelper.cpp297
-rw-r--r--cpp/watchdog/server/tests/OveruseConfigurationXmlHelperTest.cpp132
2 files changed, 426 insertions, 3 deletions
diff --git a/cpp/watchdog/server/src/OveruseConfigurationXmlHelper.cpp b/cpp/watchdog/server/src/OveruseConfigurationXmlHelper.cpp
index 308bb8eba3..a3bf4a96f9 100644
--- a/cpp/watchdog/server/src/OveruseConfigurationXmlHelper.cpp
+++ b/cpp/watchdog/server/src/OveruseConfigurationXmlHelper.cpp
@@ -59,6 +59,7 @@ using ::android::base::StringPrintf;
using ::android::base::Trim;
using ::android::binder::Status;
using ::tinyxml2::XML_SUCCESS;
+using ::tinyxml2::XMLDeclaration;
using ::tinyxml2::XMLDocument;
using ::tinyxml2::XMLElement;
@@ -95,6 +96,8 @@ constexpr int kNumParams = 2;
constexpr const char kAttrId[] = "id";
constexpr const char kAttrType[] = "type";
+constexpr const char kAttrVersion[] = "version";
+constexpr const char kVersionNumber[] = "1.0";
Result<const XMLElement*> readExactlyOneElement(const char* tag, const XMLElement* rootElement) {
const XMLElement* element = rootElement->FirstChildElement(tag);
@@ -432,6 +435,249 @@ Result<IoOveruseConfiguration> readIoOveruseConfiguration(ComponentType componen
return configuration;
}
+Result<void> writeComponentType(ComponentType componentType, XMLElement* rootElement) {
+ XMLElement* childElement = rootElement->InsertNewChildElement(kTagComponentType);
+ if (!childElement) {
+ return Error() << "Failed to insert new child element with tag '" << kTagComponentType
+ << "'";
+ }
+ childElement->SetText(toString(componentType).c_str());
+ return {};
+}
+
+Result<void> writeSafeToKillPackages(const std::vector<std::string>& safeToKillPackages,
+ XMLElement* rootElement) {
+ if (safeToKillPackages.empty()) {
+ return {};
+ }
+ XMLElement* outerElement = rootElement->InsertNewChildElement(kTagSafeToKillPackages);
+ if (!outerElement) {
+ return Error() << "Failed to insert new child element with tag '" << kTagSafeToKillPackages
+ << "'";
+ }
+ for (const auto& package : safeToKillPackages) {
+ XMLElement* innerElement = outerElement->InsertNewChildElement(kTagPackage);
+ if (!innerElement) {
+ return Error() << "Failed to insert new child element with tag '" << kTagPackage << "'";
+ }
+ innerElement->SetText(package.c_str());
+ }
+ return {};
+}
+
+Result<void> writeVendorPackagePrefixes(const std::vector<std::string>& vendorPackagePrefixes,
+ XMLElement* rootElement) {
+ if (vendorPackagePrefixes.empty()) {
+ return {};
+ }
+ XMLElement* outerElement = rootElement->InsertNewChildElement(kTagVendorPackagePrefixes);
+ if (!outerElement) {
+ return Error() << "Failed to insert new child element with tag '"
+ << kTagVendorPackagePrefixes << "'";
+ }
+ for (const auto& packagePrefix : vendorPackagePrefixes) {
+ XMLElement* innerElement = outerElement->InsertNewChildElement(kTagPackagePrefix);
+ if (!innerElement) {
+ return Error() << "Failed to insert new child element with tag '" << kTagPackagePrefix
+ << "'";
+ }
+ innerElement->SetText(packagePrefix.c_str());
+ }
+ return {};
+}
+
+Result<void> writePackageToAppCategoryTypes(const std::vector<PackageMetadata>& packageMetadata,
+ XMLElement* rootElement) {
+ if (packageMetadata.empty()) {
+ return {};
+ }
+ XMLElement* outerElement = rootElement->InsertNewChildElement(kTagPackageToAppCategoryTypes);
+ if (!outerElement) {
+ return Error() << "Failed to insert new child element with tag '"
+ << kTagPackageToAppCategoryTypes << "'";
+ }
+ for (const auto& meta : packageMetadata) {
+ XMLElement* innerElement = outerElement->InsertNewChildElement(kTagPackageAppCategory);
+ if (!innerElement) {
+ return Error() << "Failed to insert new child element with tag '"
+ << kTagPackageAppCategory << "'";
+ }
+ innerElement->SetAttribute(kAttrType, toString(meta.appCategoryType).c_str());
+ innerElement->SetText(meta.packageName.c_str());
+ }
+ return {};
+}
+
+Result<void> writePerStateBytes(const PerStateBytes& perStateBytes, XMLElement* rootElement) {
+ const auto writeStateElement = [&](const char* state, int64_t value) -> Result<void> {
+ XMLElement* childElement = rootElement->InsertNewChildElement(kTagState);
+ if (!childElement) {
+ return Error() << "Failed to insert new child element with tag '" << kTagState << "'";
+ }
+ childElement->SetAttribute(kAttrId, state);
+ childElement->SetText(value);
+ return {};
+ };
+ if (const auto result = writeStateElement(kStateIdForegroundMode,
+ perStateBytes.foregroundBytes / kOneMegaByte);
+ !result.ok()) {
+ return Error() << "Failed to write bytes for state '" << kStateIdForegroundMode
+ << "': " << result.error();
+ }
+ if (const auto result = writeStateElement(kStateIdBackgroundMode,
+ perStateBytes.backgroundBytes / kOneMegaByte);
+ !result.ok()) {
+ return Error() << "Failed to write bytes for state '" << kStateIdBackgroundMode
+ << "': " << result.error();
+ }
+ if (const auto result =
+ writeStateElement(kStateIdGarageMode, perStateBytes.garageModeBytes / kOneMegaByte);
+ !result.ok()) {
+ return Error() << "Failed to write bytes for state '" << kStateIdGarageMode
+ << "': " << result.error();
+ }
+ return {};
+}
+
+Result<void> writeComponentLevelThresholds(const PerStateIoOveruseThreshold& thresholds,
+ XMLElement* rootElement) {
+ XMLElement* childElement = rootElement->InsertNewChildElement(kTagComponentLevelThresholds);
+ if (!childElement) {
+ return Error() << "Failed to insert new child element with tag '"
+ << kTagComponentLevelThresholds << "'";
+ }
+ if (const auto result = writePerStateBytes(thresholds.perStateWriteBytes, childElement);
+ !result.ok()) {
+ return Error() << "Failed to write per-state bytes: " << result.error();
+ }
+ return {};
+}
+
+Result<void> writePerStateThresholds(const PerStateIoOveruseThreshold& thresholds,
+ XMLElement* rootElement) {
+ XMLElement* childElement = rootElement->InsertNewChildElement(kTagPerStateThreshold);
+ if (!childElement) {
+ return Error() << "Failed to insert new child element with tag '" << kTagPerStateThreshold
+ << "'";
+ }
+ childElement->SetAttribute(kAttrId, thresholds.name.c_str());
+ if (const auto result = writePerStateBytes(thresholds.perStateWriteBytes, childElement);
+ !result.ok()) {
+ return Error() << "Failed to write per-state bytes: " << result.error();
+ }
+ return {};
+}
+
+Result<void> writePackageSpecificThresholds(
+ const std::vector<PerStateIoOveruseThreshold>& thresholds, XMLElement* rootElement) {
+ XMLElement* childElement = rootElement->InsertNewChildElement(kTagPackageSpecificThresholds);
+ if (!childElement) {
+ return Error() << "Failed to insert new child element with tag '"
+ << kTagPackageSpecificThresholds << "'";
+ }
+ for (const auto threshold : thresholds) {
+ if (const auto result = writePerStateThresholds(threshold, childElement); !result.ok()) {
+ return Error() << "Failed to write per-state thresholds for '" << threshold.name
+ << "': " << result.error();
+ }
+ }
+ return {};
+}
+
+Result<void> writeAppCategorySpecificThresholds(
+ const std::vector<PerStateIoOveruseThreshold>& thresholds, XMLElement* rootElement) {
+ XMLElement* childElement =
+ rootElement->InsertNewChildElement(kTagAppCategorySpecificThresholds);
+ if (!childElement) {
+ return Error() << "Failed to insert new child element with tag '"
+ << kTagAppCategorySpecificThresholds << "'";
+ }
+ for (const auto threshold : thresholds) {
+ if (const auto result = writePerStateThresholds(threshold, childElement); !result.ok()) {
+ return Error() << "Failed to write per-state thresholds for '" << threshold.name
+ << "': " << result.error();
+ }
+ }
+ return {};
+}
+
+Result<void> writeAlertThresholds(const IoOveruseAlertThreshold& alertThresholds,
+ XMLElement* rootElement) {
+ XMLElement* outerElement = rootElement->InsertNewChildElement(kTagAlertThreshold);
+ if (!outerElement) {
+ return Error() << "Failed to insert new child element with tag '" << kTagAlertThreshold
+ << "'";
+ }
+ const auto writeParamElement = [&](const char* param, int64_t value) -> Result<void> {
+ XMLElement* innerElement = outerElement->InsertNewChildElement(kTagParam);
+ if (!innerElement) {
+ return Error() << "Failed to insert new child element with tag '" << kTagParam << "'";
+ }
+ innerElement->SetAttribute(kAttrId, param);
+ innerElement->SetText(value);
+ return {};
+ };
+ if (const auto result =
+ writeParamElement(kParamIdDurationSeconds, alertThresholds.durationInSeconds);
+ !result.ok()) {
+ return Error() << "Failed to write duration for param '" << kParamIdDurationSeconds
+ << "': " << result.error();
+ }
+ if (const auto result = writeParamElement(kParamIdWrittenBytesPerSecond,
+ alertThresholds.writtenBytesPerSecond);
+ !result.ok()) {
+ return Error() << "Failed to write bps for param '" << kParamIdWrittenBytesPerSecond
+ << "': " << result.error();
+ }
+ return {};
+}
+
+Result<void> writeSystemWideThresholds(const std::vector<IoOveruseAlertThreshold>& thresholds,
+ XMLElement* rootElement) {
+ XMLElement* childElement = rootElement->InsertNewChildElement(kTagSystemWideThresholds);
+ if (!childElement) {
+ return Error() << "Failed to insert new child element with tag '"
+ << kTagSystemWideThresholds << "'";
+ }
+ for (const auto threshold : thresholds) {
+ if (const auto result = writeAlertThresholds(threshold, childElement); !result.ok()) {
+ return Error() << "Failed to write I/O overuse alert thresholds:" << result.error();
+ }
+ }
+ return {};
+}
+
+Result<void> writeIoOveruseConfiguration(const IoOveruseConfiguration& configuration,
+ XMLElement* rootElement) {
+ XMLElement* childElement = rootElement->InsertNewChildElement(kTagIoOveruseConfiguration);
+ if (!childElement) {
+ return Error() << "Failed to insert new child element with tag '"
+ << kTagIoOveruseConfiguration << "'";
+ }
+ if (const auto result =
+ writeComponentLevelThresholds(configuration.componentLevelThresholds, childElement);
+ !result.ok()) {
+ return Error() << "Failed to write component-wide thresholds: " << result.error();
+ }
+ if (const auto result = writePackageSpecificThresholds(configuration.packageSpecificThresholds,
+ childElement);
+ !result.ok()) {
+ return Error() << "Failed to write package specific thresholds: " << result.error();
+ }
+ if (const auto result =
+ writeAppCategorySpecificThresholds(configuration.categorySpecificThresholds,
+ childElement);
+ !result.ok()) {
+ return Error() << "Failed to write app category specific thresholds: " << result.error();
+ }
+ if (const auto result =
+ writeSystemWideThresholds(configuration.systemWideThresholds, childElement);
+ !result.ok()) {
+ return Error() << "Failed to write system-wide thresholds: " << result.error();
+ }
+ return {};
+}
+
} // namespace
Result<ResourceOveruseConfiguration> OveruseConfigurationXmlHelper::parseXmlFile(
@@ -478,9 +724,54 @@ Result<ResourceOveruseConfiguration> OveruseConfigurationXmlHelper::parseXmlFile
}
Result<void> OveruseConfigurationXmlHelper::writeXmlFile(
- [[maybe_unused]] const ResourceOveruseConfiguration& configuration,
- [[maybe_unused]] const char* filePath) {
- // TODO(b/185287136): Write the configuration to file.
+ const ResourceOveruseConfiguration& configuration, const char* filePath) {
+ XMLDocument xmlDoc;
+ if (XMLDeclaration* declaration = xmlDoc.NewDeclaration(); declaration) {
+ xmlDoc.InsertEndChild(declaration);
+ } else {
+ return Error() << "Failed to create new xml declaration";
+ }
+ XMLElement* rootElement = xmlDoc.NewElement(kTagResourceOveruseConfiguration);
+ if (!rootElement) {
+ return Error() << "Failed to create new xml element for tag '"
+ << kTagResourceOveruseConfiguration << "'";
+ }
+ rootElement->SetAttribute(kAttrVersion, kVersionNumber);
+ xmlDoc.InsertEndChild(rootElement);
+ if (const auto result = writeComponentType(configuration.componentType, rootElement);
+ !result.ok()) {
+ return Error() << "Failed to write component type: " << result.error();
+ }
+ if (const auto result = writeSafeToKillPackages(configuration.safeToKillPackages, rootElement);
+ !result.ok()) {
+ return Error() << "Failed to write safe-to-kill packages: " << result.error();
+ }
+ if (const auto result =
+ writeVendorPackagePrefixes(configuration.vendorPackagePrefixes, rootElement);
+ !result.ok()) {
+ return Error() << "Failed to write vendor package prefixes: " << result.error();
+ }
+ if (const auto result =
+ writePackageToAppCategoryTypes(configuration.packageMetadata, rootElement);
+ !result.ok()) {
+ return Error() << "Failed to write package to app category types: " << result.error();
+ }
+ if (configuration.resourceSpecificConfigurations.size() != 1 ||
+ configuration.resourceSpecificConfigurations[0].getTag() !=
+ ResourceSpecificConfiguration::ioOveruseConfiguration) {
+ return Error() << "Must provide exactly one I/O overuse configuration";
+ }
+ IoOveruseConfiguration ioOveruseConfig =
+ configuration.resourceSpecificConfigurations[0]
+ .get<ResourceSpecificConfiguration::ioOveruseConfiguration>();
+ if (const auto result = writeIoOveruseConfiguration(ioOveruseConfig, rootElement);
+ !result.ok()) {
+ return Error() << "Failed to write I/O overuse configuration: " << result.error();
+ }
+ if (const auto xmlError = xmlDoc.SaveFile(filePath); xmlError != XML_SUCCESS) {
+ return Error() << "Failed to write XML configuration to file '" << filePath
+ << "': " << XMLDocument::ErrorIDToName(xmlError);
+ }
return {};
}
diff --git a/cpp/watchdog/server/tests/OveruseConfigurationXmlHelperTest.cpp b/cpp/watchdog/server/tests/OveruseConfigurationXmlHelperTest.cpp
index 88006d7424..9e45fd06b9 100644
--- a/cpp/watchdog/server/tests/OveruseConfigurationXmlHelperTest.cpp
+++ b/cpp/watchdog/server/tests/OveruseConfigurationXmlHelperTest.cpp
@@ -165,6 +165,138 @@ TEST(OveruseConfigurationXmlHelperTest, TestInvalidOveruseConfigurations) {
}
}
+TEST(OveruseConfigurationXmlHelperTest, TestWriteXmlFileWithSystemConfiguration) {
+ auto ioConfig = constructIoOveruseConfig(
+ /*componentLevel=*/toPerStateIoOveruseThreshold(ComponentType::SYSTEM,
+ 300 * kOneMegaByte, 150 * kOneMegaByte,
+ 500 * kOneMegaByte),
+ /*packageSpecific=*/
+ {toPerStateIoOveruseThreshold("system.package.C", 400 * kOneMegaByte,
+ 100 * kOneMegaByte, 200 * kOneMegaByte),
+ toPerStateIoOveruseThreshold("system.package.D", 1024 * kOneMegaByte,
+ 500 * kOneMegaByte, 2048 * kOneMegaByte)},
+ /*categorySpecific=*/{},
+ /*systemWide=*/{toIoOveruseAlertThreshold(10, 200), toIoOveruseAlertThreshold(5, 50)});
+ ResourceOveruseConfiguration expected =
+ constructResourceOveruseConfig(ComponentType::SYSTEM,
+ /*safeToKill=*/{"system.package.A", "system.package.B"},
+ /*vendorPrefixes=*/{},
+ /*packageMetadata=*/
+ {toPackageMetadata("system.package.A",
+ ApplicationCategoryType::MEDIA),
+ toPackageMetadata("system.package.B",
+ ApplicationCategoryType::MAPS)},
+ ioConfig);
+ TemporaryFile temporaryFile;
+ ASSERT_NE(temporaryFile.fd, -1);
+
+ ASSERT_RESULT_OK(OveruseConfigurationXmlHelper::writeXmlFile(expected, temporaryFile.path));
+
+ ALOGW("Wrote to file: %s", temporaryFile.path);
+
+ auto actual = OveruseConfigurationXmlHelper::parseXmlFile(temporaryFile.path);
+
+ ASSERT_RESULT_OK(actual);
+
+ EXPECT_THAT(*actual, ResourceOveruseConfigurationMatcher(expected))
+ << "Expected: " << expected.toString() << "\nActual: " << actual->toString();
+
+ temporaryFile.release();
+}
+
+TEST(OveruseConfigurationXmlHelperTest, TestWriteXmlFileWithVendorConfiguration) {
+ auto ioConfig = constructIoOveruseConfig(
+ /*componentLevel=*/toPerStateIoOveruseThreshold(ComponentType::VENDOR,
+ 1024 * kOneMegaByte, 512 * kOneMegaByte,
+ 3072 * kOneMegaByte),
+ /*packageSpecific=*/
+ {toPerStateIoOveruseThreshold("com.vendor.package.C", 400 * kOneMegaByte,
+ 100 * kOneMegaByte, 200 * kOneMegaByte),
+ toPerStateIoOveruseThreshold("com.vendor.package.D", 1024 * kOneMegaByte,
+ 500 * kOneMegaByte, 2048 * kOneMegaByte)},
+ /*categorySpecific=*/
+ {toPerStateIoOveruseThreshold("MAPS", 800 * kOneMegaByte, 900 * kOneMegaByte,
+ 2048 * kOneMegaByte),
+ toPerStateIoOveruseThreshold("MEDIA", 600 * kOneMegaByte, 700 * kOneMegaByte,
+ 1024 * kOneMegaByte)},
+ /*systemWide=*/{});
+ ResourceOveruseConfiguration expected =
+ constructResourceOveruseConfig(ComponentType::VENDOR,
+ /*safeToKill=*/
+ {"com.vendor.package.A", "com.vendor.package.B"},
+ /*vendorPrefixes=*/{"com.vendor.package"},
+ /*packageMetadata=*/
+ {toPackageMetadata("com.vendor.package.A",
+ ApplicationCategoryType::MEDIA),
+ toPackageMetadata("com.vendor.package.B",
+ ApplicationCategoryType::MAPS),
+ toPackageMetadata("com.third.party.package.C",
+ ApplicationCategoryType::MEDIA),
+ toPackageMetadata("system.package.D",
+ ApplicationCategoryType::MAPS)},
+ ioConfig);
+ TemporaryFile temporaryFile;
+ ASSERT_NE(temporaryFile.fd, -1);
+
+ ASSERT_RESULT_OK(OveruseConfigurationXmlHelper::writeXmlFile(expected, temporaryFile.path));
+
+ ALOGW("Wrote to file: %s", temporaryFile.path);
+
+ auto actual = OveruseConfigurationXmlHelper::parseXmlFile(temporaryFile.path);
+
+ ASSERT_RESULT_OK(actual);
+
+ EXPECT_THAT(*actual, ResourceOveruseConfigurationMatcher(expected))
+ << "Expected: " << expected.toString() << "\nActual: " << actual->toString();
+
+ temporaryFile.release();
+}
+
+TEST(OveruseConfigurationXmlHelperTest, TestWriteXmlFileWithThirdPartyConfiguration) {
+ auto ioConfig = constructIoOveruseConfig(
+ /*componentLevel=*/toPerStateIoOveruseThreshold(ComponentType::THIRD_PARTY,
+ 300 * kOneMegaByte, 150 * kOneMegaByte,
+ 500 * kOneMegaByte),
+ /*packageSpecific=*/{},
+ /*categorySpecific=*/{},
+ /*systemWide=*/{});
+ ResourceOveruseConfiguration expected =
+ constructResourceOveruseConfig(ComponentType::THIRD_PARTY,
+ /*safeToKill=*/{},
+ /*vendorPrefixes=*/{},
+ /*packageMetadata=*/{}, ioConfig);
+ TemporaryFile temporaryFile;
+ ASSERT_NE(temporaryFile.fd, -1);
+
+ ASSERT_RESULT_OK(OveruseConfigurationXmlHelper::writeXmlFile(expected, temporaryFile.path));
+
+ ALOGW("Wrote to file: %s", temporaryFile.path);
+
+ auto actual = OveruseConfigurationXmlHelper::parseXmlFile(temporaryFile.path);
+
+ ASSERT_RESULT_OK(actual);
+
+ EXPECT_THAT(*actual, ResourceOveruseConfigurationMatcher(expected))
+ << "Expected: " << expected.toString() << "\nActual: " << actual->toString();
+
+ temporaryFile.release();
+}
+
+TEST(OveruseConfigurationXmlHelperTest, TestFailsWriteXmlFileWithInvalidConfig) {
+ ResourceOveruseConfiguration resourceOveruseConfig;
+ resourceOveruseConfig.componentType = ComponentType::THIRD_PARTY;
+
+ TemporaryFile temporaryFile;
+ ASSERT_NE(temporaryFile.fd, -1);
+
+ ASSERT_FALSE(
+ OveruseConfigurationXmlHelper::writeXmlFile(resourceOveruseConfig, temporaryFile.path)
+ .ok())
+ << "Should fail to write invalid config";
+
+ temporaryFile.release();
+}
+
} // namespace watchdog
} // namespace automotive
} // namespace android