vintf: clean up output.
Output summarizes <hal> tags and omits kernel
configs now. If a user still wants these output, --verbose
can be used.
Test: adb shell vintf
Change-Id: Id7716d55d9a6a159df180d476609d95035ec0c15
diff --git a/main.cpp b/main.cpp
index f49e484..111b797 100644
--- a/main.cpp
+++ b/main.cpp
@@ -14,13 +14,21 @@
* limitations under the License.
*/
-#include <iostream>
-#include <vintf/parse_xml.h>
-#include <vintf/parse_string.h>
+#include <getopt.h>
+
+#include <android-base/strings.h>
#include <vintf/VintfObject.h>
+#include <vintf/parse_string.h>
+#include <vintf/parse_xml.h>
+#include <iomanip>
+#include <iostream>
+#include <string>
+#include <vector>
using namespace ::android::vintf;
+static const std::string kColumnSeperator = " ";
+
std::string existString(bool value) {
return value ? "GOOD" : "DOES NOT EXIST";
}
@@ -51,40 +59,241 @@
}
}
+enum Status : int {
+ OK = 0,
+ USAGE,
+};
+
+struct ParsedOptions {
+ bool verbose = false;
+};
+
+struct Option {
+ char shortOption = '\0';
+ std::string longOption;
+ std::string help;
+ std::function<Status(ParsedOptions*)> op;
+};
+
+std::string getShortOptions(const std::vector<Option>& options) {
+ std::stringstream ret;
+ for (const auto& e : options)
+ if (e.shortOption != '\0') ret << e.shortOption;
+ return ret.str();
+}
+
+std::unique_ptr<struct option[]> getLongOptions(const std::vector<Option>& options,
+ int* longOptFlag) {
+ std::unique_ptr<struct option[]> ret{new struct option[options.size() + 1]};
+ int i = 0;
+ for (const auto& e : options) {
+ ret[i].name = e.longOption.c_str();
+ ret[i].has_arg = no_argument;
+ ret[i].flag = longOptFlag;
+ ret[i].val = i;
+
+ i++;
+ }
+ // getopt_long last option has all zeros
+ ret[i].name = NULL;
+ ret[i].has_arg = 0;
+ ret[i].flag = NULL;
+ ret[i].val = 0;
+
+ return ret;
+}
+
+Status parseOptions(int argc, char** argv, const std::vector<Option>& options, ParsedOptions* out) {
+ int longOptFlag;
+ std::unique_ptr<struct option[]> longOptions = getLongOptions(options, &longOptFlag);
+ std::string shortOptions = getShortOptions(options);
+ int optionIndex;
+ for (;;) {
+ int c = getopt_long(argc, argv, shortOptions.c_str(), longOptions.get(), &optionIndex);
+ if (c == -1) {
+ break;
+ }
+ const Option* found = nullptr;
+ for (size_t i = 0; i < options.size(); ++i)
+ if ((c == 0 && longOptFlag == static_cast<int>(i)) ||
+ (c != 0 && c == options[i].shortOption))
+
+ found = &options[i];
+
+ if (found == nullptr) {
+ // see unrecognized options
+ std::cerr << "unrecognized option `" << argv[optind - 1] << "'" << std::endl;
+ return USAGE;
+ }
+
+ Status status = found->op(out);
+ if (status != OK) return status;
+ }
+ if (optind < argc) {
+ // see non option
+ std::cerr << "unrecognized option `" << argv[optind] << "'" << std::endl;
+ return USAGE;
+ }
+ return OK;
+}
+
+void usage(char* me, const std::vector<Option>& options) {
+ std::cerr << me << ": dump VINTF metadata via libvintf." << std::endl;
+ for (const auto& e : options) {
+ if (e.help.empty()) continue;
+ std::cerr << " ";
+ if (e.shortOption != '\0') std::cerr << "-" << e.shortOption;
+ if (e.shortOption != '\0' && !e.longOption.empty()) std::cerr << ", ";
+ if (!e.longOption.empty()) std::cerr << "--" << e.longOption;
+ std::cerr << ": "
+ << android::base::Join(android::base::Split(e.help, "\n"), "\n ")
+ << std::endl;
+ }
+}
+
+// android.hardware.foo@1.0-1::IFoo/default.
+// Note that the format is extended to support a range of versions.
+std::string fqInstanceName(const std::string& package, const VersionRange& range,
+ const std::string& interface, const std::string& instance) {
+ std::stringstream ss;
+ ss << package << "@" << range << "::" << interface << "/" << instance;
+ return ss.str();
+}
+
+struct TableRow {
+ // Whether the HAL version is in device manifest, framework manifest, device compatibility
+ // matrix, framework compatibility matrix, respectively.
+ bool dm = false;
+ bool fm = false;
+ bool dcm = false;
+ bool fcm = false;
+ // If the HAL version is in device / framework compatibility matrix, whether it is required
+ // or not.
+ bool required = false;
+
+ // Return true if:
+ // - not a required HAL version; OR
+ // - required in device matrix and framework manifest;
+ // - required in framework matrix and device manifest.
+ bool meetsReqeuirement() const {
+ if (!required) return true;
+ if (dcm && !fm) return false;
+ if (fcm && !dm) return false;
+ return true;
+ }
+};
+
+std::ostream& operator<<(std::ostream& out, const TableRow& row) {
+ return out << (row.required ? "R" : " ") << (row.meetsReqeuirement() ? " " : "!")
+ << kColumnSeperator << (row.dm ? "DM" : " ") << kColumnSeperator
+ << (row.fm ? "FM" : " ") << kColumnSeperator << (row.fcm ? "FCM" : " ")
+ << kColumnSeperator << (row.dcm ? "DCM" : " ");
+}
+
+using RowMutator = std::function<void(TableRow*)>;
+using Table = std::map<std::string, TableRow>;
+
+// Insert each fqInstanceName foo@x.y::IFoo/instance to the table by inserting the key
+// if it does not exist and setting the corresponding indicator (as specified by "mutate").
+void insert(const HalManifest* manifest, Table* table, const RowMutator& mutate) {
+ if (manifest == nullptr) return;
+ manifest->forEachInstance([&](const auto& package, const auto& version, const auto& interface,
+ const auto& instance, bool* /* stop */) {
+ std::string key = fqInstanceName(package, VersionRange{version.majorVer, version.minorVer},
+ interface, instance);
+ mutate(&(*table)[key]);
+ });
+}
+
+void insert(const CompatibilityMatrix* matrix, Table* table, const RowMutator& mutate) {
+ if (matrix == nullptr) return;
+ matrix->forEachInstance([&](const auto& package, const auto& range, const auto& interface,
+ const auto& instance, bool optional, bool* /* stop */) {
+ bool missed = false;
+ for (auto minorVer = range.minMinor; minorVer <= range.maxMinor; ++minorVer) {
+ std::string key = fqInstanceName(package, VersionRange{range.majorVer, minorVer},
+ interface, instance);
+ auto it = table->find(key);
+ if (it == table->end()) {
+ missed = true;
+ } else {
+ mutate(&it->second);
+ it->second.required = !optional;
+ }
+ }
+ if (missed) {
+ std::string key = fqInstanceName(package, range, interface, instance);
+ mutate(&(*table)[key]);
+ }
+ });
+}
+
+Table generateHalSummary(const HalManifest* vm, const HalManifest* fm,
+ const CompatibilityMatrix* vcm, const CompatibilityMatrix* fcm) {
+ Table table;
+ insert(vm, &table, [](auto* row) { row->dm = true; });
+ insert(fm, &table, [](auto* row) { row->fm = true; });
+ insert(vcm, &table, [](auto* row) { row->dcm = true; });
+ insert(fcm, &table, [](auto* row) { row->fcm = true; });
+
+ return table;
+}
+
+static const std::vector<Option> gAvailableOptions{
+ {'h', "help", "Print help message.", [](auto) { return USAGE; }},
+ {'v', "verbose", "Dump detailed and raw content, including kernel configurations", [](auto o) {
+ o->verbose = true;
+ return OK;
+ }}};
// A convenience binary to dump information available through libvintf.
-int main(int, char **) {
+int main(int argc, char** argv) {
+ ParsedOptions options;
+ Status status = parseOptions(argc, argv, gAvailableOptions, &options);
+ if (status == USAGE) usage(argv[0], gAvailableOptions);
+ if (status != OK) return status;
+ auto vm = VintfObject::GetDeviceHalManifest();
+ auto fm = VintfObject::GetFrameworkHalManifest();
+ auto vcm = VintfObject::GetDeviceCompatibilityMatrix();
+ auto fcm = VintfObject::GetFrameworkCompatibilityMatrix();
+ auto ki = VintfObject::GetRuntimeInfo();
+
+ if (!options.verbose) {
+ std::cout << "======== HALs =========" << std::endl
+ << "R: required. (empty): optional or missing from matrices. "
+ << "!: required and not in manifest." << std::endl
+ << "DM: device manifest. FM: framework manifest." << std::endl
+ << "FCM: framework compatibility matrix. DCM: device compatibility matrix."
+ << std::endl
+ << std::endl;
+ auto table = generateHalSummary(vm.get(), fm.get(), vcm.get(), fcm.get());
+
+ for (const auto& pair : table)
+ std::cout << pair.second << kColumnSeperator << pair.first << std::endl;
+
+ std::cout << std::endl;
+ }
+
+ SerializeFlags flags = SerializeFlag::EVERYTHING;
+ if (!options.verbose) {
+ flags |= SerializeFlag::NO_HALS;
+ flags |= SerializeFlag::NO_KERNEL;
+ }
std::cout << "======== Device HAL Manifest =========" << std::endl;
-
- std::shared_ptr<const HalManifest> vm = VintfObject::GetDeviceHalManifest();
- if (vm != nullptr)
- std::cout << gHalManifestConverter(*vm);
-
+ if (vm != nullptr) std::cout << gHalManifestConverter(*vm, flags);
std::cout << "======== Framework HAL Manifest =========" << std::endl;
-
- std::shared_ptr<const HalManifest> fm = VintfObject::GetFrameworkHalManifest();
- if (fm != nullptr)
- std::cout << gHalManifestConverter(*fm);
-
+ if (fm != nullptr) std::cout << gHalManifestConverter(*fm, flags);
std::cout << "======== Device Compatibility Matrix =========" << std::endl;
-
- std::shared_ptr<const CompatibilityMatrix> vcm = VintfObject::GetDeviceCompatibilityMatrix();
- if (vcm != nullptr)
- std::cout << gCompatibilityMatrixConverter(*vcm);
-
+ if (vcm != nullptr) std::cout << gCompatibilityMatrixConverter(*vcm, flags);
std::cout << "======== Framework Compatibility Matrix =========" << std::endl;
-
- std::shared_ptr<const CompatibilityMatrix> fcm = VintfObject::GetFrameworkCompatibilityMatrix();
- if (fcm != nullptr)
- std::cout << gCompatibilityMatrixConverter(*fcm);
+ if (fcm != nullptr) std::cout << gCompatibilityMatrixConverter(*fcm, flags);
std::cout << "======== Runtime Info =========" << std::endl;
+ if (ki != nullptr) std::cout << dump(*ki, options.verbose);
- std::shared_ptr<const RuntimeInfo> ki = VintfObject::GetRuntimeInfo();
- if (ki != nullptr) std::cout << dump(*ki);
std::cout << std::endl;
- std::cout << "======== Compatibility check =========" << std::endl;
+ std::cout << "======== Summary =========" << std::endl;
std::cout << "Device Manifest? " << existString(vm != nullptr) << std::endl
<< "Device Matrix? " << existString(vcm != nullptr) << std::endl
<< "Framework Manifest? " << existString(fm != nullptr) << std::endl