blob: af90fad191f1f919705b1ce22ba94726c1a0f17b [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "text_formatter.h"
#include <lib/fostr/hex_dump.h>
#include <sstream>
#include "src/lib/inspect_deprecated/health/health.h"
#include "src/lib/inspect_deprecated/hierarchy.h"
namespace inspect_deprecated {
namespace {
void Indent(std::ostream& ss, int indent) { ss << std::string(indent, ' '); }
constexpr size_t kMaxHexSize = 256;
std::string HexDump(const std::vector<uint8_t>& contents) {
std::ostringstream out;
if (contents.size() > kMaxHexSize) {
out << "\nFirst " << kMaxHexSize << " bytes of " << contents.size() << ":";
}
out << fostr::HexDump(contents.data(), std::min(kMaxHexSize, contents.size()), 0x0);
return out.str();
}
template <typename ArrayType>
void FormatArray(std::ostream& ss, const ArrayType& array) {
auto buckets = array.GetBuckets();
if (!buckets.empty()) {
ss << "[";
bool first = true;
for (const auto& bucket : buckets) {
if (!first) {
ss << ", ";
}
first = false;
// Prevent very small negative numbers from appearing in the underflow
// bucket. Replace them with "<min>"
if (bucket.floor != 0 && bucket.floor == std::numeric_limits<decltype(bucket.floor)>::min()) {
ss << "[<min>," << bucket.upper_limit << ")=";
} else if (bucket.upper_limit != 0 &&
bucket.upper_limit == std::numeric_limits<decltype(bucket.upper_limit)>::max()) {
ss << "[" << bucket.floor << ",<max>)=";
}
else {
ss << "[" << bucket.floor << "," << bucket.upper_limit << ")=";
}
// Cast all counts to integers for display.
if (!std::numeric_limits<decltype(bucket.count)>::is_integer) {
ss << static_cast<uint64_t>(bucket.count);
} else {
ss << bucket.count;
}
}
ss << "]";
} else {
ss << "[";
bool first = true;
for (const auto& val : array.value()) {
if (!first) {
ss << ", ";
}
first = false;
ss << val;
}
ss << "]";
}
}
void FormatMetricValue(std::ostream& ss, const inspect_deprecated::hierarchy::Metric& metric) {
switch (metric.format()) {
case inspect_deprecated::hierarchy::MetricFormat::INT_ARRAY:
FormatArray(ss, metric.Get<inspect_deprecated::hierarchy::IntArray>());
break;
case inspect_deprecated::hierarchy::MetricFormat::UINT_ARRAY:
FormatArray(ss, metric.Get<inspect_deprecated::hierarchy::UIntArray>());
break;
case inspect_deprecated::hierarchy::MetricFormat::DOUBLE_ARRAY:
FormatArray(ss, metric.Get<inspect_deprecated::hierarchy::DoubleArray>());
break;
case inspect_deprecated::hierarchy::MetricFormat::INT:
ss << metric.Get<inspect_deprecated::hierarchy::IntMetric>().value();
break;
case inspect_deprecated::hierarchy::MetricFormat::UINT:
ss << metric.Get<inspect_deprecated::hierarchy::UIntMetric>().value();
break;
case inspect_deprecated::hierarchy::MetricFormat::DOUBLE:
ss << metric.Get<inspect_deprecated::hierarchy::DoubleMetric>().value();
break;
default:
ss << "<unknown metric type>";
break;
}
}
std::string FormatHealthForNode(const inspect_deprecated::ObjectHierarchy& node) {
std::string status;
std::string message;
for (auto& property : node.node().properties()) {
if (property.name() == "status")
status = property.Get<inspect_deprecated::hierarchy::StringProperty>().value();
if (property.name() == "message")
message = property.Get<inspect_deprecated::hierarchy::StringProperty>().value();
}
std::ostringstream ss;
ss << status;
if (!message.empty())
ss << " (" << message << ")";
ss << std::endl;
return ss.str();
}
} // namespace
std::string TextFormatter::FormatSourcesRecursive(
const std::vector<inspect_deprecated::Source>& sources) const {
std::ostringstream ss;
ss.precision(6);
ss << std::fixed;
for (const auto& entry_point : sources) {
entry_point.VisitObjectsInHierarchy([&](const Path path_to_node,
const ObjectHierarchy& hierarchy) {
const int name_indent = options_.indent * path_to_node.size();
const int value_indent = name_indent + options_.indent;
Indent(ss, name_indent);
ss << FormatPathOrName(entry_point.GetLocation(), path_to_node, hierarchy.node().name())
<< ":" << std::endl;
for (const auto& property : hierarchy.node().properties()) {
Indent(ss, value_indent);
ss << property.name() << " = ";
switch (property.format()) {
case inspect_deprecated::hierarchy::PropertyFormat::STRING:
ss << property.Get<inspect_deprecated::hierarchy::StringProperty>().value();
break;
case inspect_deprecated::hierarchy::PropertyFormat::BYTES:
ss << "Binary: "
<< HexDump(
property.Get<inspect_deprecated::hierarchy::ByteVectorProperty>().value());
break;
default:
ss << "<unknown property format>";
break;
}
ss << std::endl;
}
for (const auto& metric : hierarchy.node().metrics()) {
Indent(ss, value_indent);
ss << metric.name() << " = ";
FormatMetricValue(ss, metric);
ss << std::endl;
}
});
}
return ss.str();
}
std::string TextFormatter::FormatChildListing(
const std::vector<inspect_deprecated::Source>& sources) const {
std::stringstream ss;
for (const auto& source : sources) {
const auto& hierarchy = source.GetHierarchy();
for (const auto& child : hierarchy.children()) {
ss << FormatPathOrName(source.GetLocation(), {child.node().name()}, child.node().name())
<< std::endl;
}
}
return ss.str();
}
std::string TextFormatter::FormatSourceLocations(
const std::vector<inspect_deprecated::Source>& sources) const {
std::stringstream ss;
for (const auto& source : sources) {
source.VisitObjectsInHierarchy(
[&](const Path& path, const inspect_deprecated::ObjectHierarchy& hierarchy) {
ss << FormatPathOrName(source.GetLocation(), path, hierarchy.node().name()) << std::endl;
});
}
return ss.str();
}
std::string TextFormatter::FormatHealth(
const std::vector<inspect_deprecated::Source>& sources) const {
std::ostringstream ss;
for (const auto& entry_point : sources) {
entry_point.VisitObjectsInHierarchy(
[&](const Path path_to_node, const ObjectHierarchy& hierarchy) {
// GetByPath returns nullptr if not found.
const ObjectHierarchy* health_node =
hierarchy.GetByPath({inspect_deprecated::kHealthNodeName});
if (!health_node)
return;
ss << FormatPathOrName(entry_point.GetLocation(), path_to_node, hierarchy.node().name())
<< " = " << FormatHealthForNode(*health_node);
});
}
return ss.str();
}
} // namespace inspect_deprecated