blob: 7a766967a8390b88899673c059598f1a22d7dd7a [file] [log] [blame]
// Copyright 2020 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 "src/lib/fidl_codec/printer.h"
#include <lib/syslog/cpp/macros.h>
#include "src/lib/fidl_codec/display_handle.h"
#include "src/lib/fidl_codec/status.h"
namespace fidl_codec {
constexpr int kCharactersPerByte = 2;
const Colors WithoutColors("", "", "", "", "", "");
const Colors WithColors(/*new_reset=*/"\u001b[0m", /*new_red=*/"\u001b[31m",
/*new_green=*/"\u001b[32m", /*new_blue=*/"\u001b[34m",
/*new_white_on_magenta=*/"\u001b[45m\u001b[37m",
/*new_yellow_background=*/"\u001b[103m");
PrettyPrinter::PrettyPrinter(std::ostream& os, const Colors& colors, bool pretty_print,
std::string_view line_header, int max_line_size,
bool header_on_every_line, int tabulations)
: os_(os),
colors_(colors),
pretty_print_(pretty_print),
line_header_(line_header),
max_line_size_(max_line_size),
header_on_every_line_(header_on_every_line),
tabulations_(tabulations),
remaining_size_(max_line_size - tabulations * kTabSize) {
// Computes the displayed size of the header. The header can contain escape sequences (to add some
// colors) which don't count as displayed characters. Here we count the number of characters in
// the line header skiping everything between escape ('\u001b') and 'm'.
size_t i = 0;
while (i < line_header.size()) {
if (line_header[i] == '\u001b') {
i = line_header.find_first_of('m', i + 1);
if (i == std::string_view::npos) {
break;
}
++i;
} else {
++i;
++line_header_size_;
}
}
}
void PrettyPrinter::DisplayHandle(const zx_handle_disposition_t& handle) {
fidl_codec::DisplayHandle(handle, *this);
}
#define BtiPermNameCase(name) \
if ((perm & (name)) == (name)) { \
*this << separator << #name; \
separator = " | "; \
}
void PrettyPrinter::DisplayBtiPerm(uint32_t perm) {
if (perm == 0) {
*this << Red << "0" << ResetColor;
return;
}
*this << Blue;
const char* separator = "";
BtiPermNameCase(ZX_BTI_PERM_READ);
BtiPermNameCase(ZX_BTI_PERM_WRITE);
BtiPermNameCase(ZX_BTI_PERM_EXECUTE);
BtiPermNameCase(ZX_BTI_COMPRESS);
BtiPermNameCase(ZX_BTI_CONTIGUOUS);
*this << ResetColor;
}
#define CachePolicyNameCase(name) \
case name: \
*this << Blue << #name << ResetColor; \
return
void PrettyPrinter::DisplayCachePolicy(uint32_t cache_policy) {
switch (cache_policy) {
CachePolicyNameCase(ZX_CACHE_POLICY_CACHED);
CachePolicyNameCase(ZX_CACHE_POLICY_UNCACHED);
CachePolicyNameCase(ZX_CACHE_POLICY_UNCACHED_DEVICE);
CachePolicyNameCase(ZX_CACHE_POLICY_WRITE_COMBINING);
default:
*this << Red << cache_policy << ResetColor;
return;
}
}
#define ClockNameCase(name) \
case name: \
*this << Blue << #name << ResetColor; \
return
void PrettyPrinter::DisplayClock(zx_clock_t clock) {
switch (clock) {
ClockNameCase(ZX_CLOCK_MONOTONIC);
ClockNameCase(ZX_CLOCK_UTC);
ClockNameCase(ZX_CLOCK_THREAD);
default:
*this << Red << clock << ResetColor;
return;
}
}
void PrettyPrinter::DisplayDuration(zx_duration_t duration_ns) {
if (duration_ns == ZX_TIME_INFINITE) {
*this << Blue << "ZX_TIME_INFINITE" << ResetColor;
return;
}
if (duration_ns == ZX_TIME_INFINITE_PAST) {
*this << Blue << "ZX_TIME_INFINITE_PAST" << ResetColor;
return;
}
*this << Blue;
if (duration_ns < 0) {
*this << '-';
duration_ns = -duration_ns;
}
const char* separator = "";
int64_t nanoseconds = duration_ns % kOneBillion;
int64_t seconds = duration_ns / kOneBillion;
if (seconds != 0) {
int64_t minutes = seconds / kSecondsPerMinute;
if (minutes != 0) {
int64_t hours = minutes / kMinutesPerHour;
if (hours != 0) {
int64_t days = hours / kHoursPerDay;
if (days != 0) {
*this << days << " days";
separator = ", ";
}
*this << separator << (hours % kHoursPerDay) << " hours";
separator = ", ";
}
*this << separator << (minutes % kMinutesPerHour) << " minutes";
separator = ", ";
}
*this << separator << (seconds % kSecondsPerMinute) << " seconds";
if (nanoseconds != 0) {
*this << " and " << nanoseconds << " nano seconds";
}
} else if (nanoseconds != 0) {
*this << nanoseconds << " nano seconds";
} else {
*this << "0 seconds";
}
*this << ResetColor;
}
#define ExceptionStateNameCase(name) \
case name: \
*this << #name << ResetColor; \
return
void PrettyPrinter::DisplayExceptionState(uint32_t state) {
*this << Blue;
switch (state) {
ExceptionStateNameCase(ZX_EXCEPTION_STATE_TRY_NEXT);
ExceptionStateNameCase(ZX_EXCEPTION_STATE_HANDLED);
default:
*this << static_cast<uint32_t>(state) << ResetColor;
return;
}
}
void PrettyPrinter::DisplayHexa8(uint8_t value) {
std::vector<char> buffer(sizeof(uint8_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%02" PRIx8, value);
*this << Blue << buffer.data() << ResetColor;
}
void PrettyPrinter::DisplayHexa16(uint16_t value) {
std::vector<char> buffer(sizeof(uint16_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%04" PRIx16, value);
*this << Blue << buffer.data() << ResetColor;
}
void PrettyPrinter::DisplayHexa32(uint32_t value) {
std::vector<char> buffer(sizeof(uint32_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%08" PRIx32, value);
*this << Blue << buffer.data() << ResetColor;
}
void PrettyPrinter::DisplayHexa64(uint64_t value) {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIx64, value);
*this << Blue << buffer.data() << ResetColor;
}
#define TopicNameCase(name) \
case name: \
*this << #name << ResetColor; \
return
void PrettyPrinter::DisplayObjectInfoTopic(uint32_t topic) {
*this << Blue;
switch (topic) {
TopicNameCase(ZX_INFO_NONE);
TopicNameCase(ZX_INFO_HANDLE_VALID);
TopicNameCase(ZX_INFO_HANDLE_BASIC);
TopicNameCase(ZX_INFO_PROCESS);
TopicNameCase(ZX_INFO_PROCESS_THREADS);
TopicNameCase(ZX_INFO_VMAR);
TopicNameCase(ZX_INFO_JOB_CHILDREN);
TopicNameCase(ZX_INFO_JOB_PROCESSES);
TopicNameCase(ZX_INFO_THREAD);
TopicNameCase(ZX_INFO_THREAD_EXCEPTION_REPORT);
TopicNameCase(ZX_INFO_TASK_STATS);
TopicNameCase(ZX_INFO_PROCESS_MAPS);
TopicNameCase(ZX_INFO_PROCESS_VMOS);
TopicNameCase(ZX_INFO_THREAD_STATS);
TopicNameCase(ZX_INFO_CPU_STATS);
TopicNameCase(ZX_INFO_KMEM_STATS);
TopicNameCase(ZX_INFO_RESOURCE);
TopicNameCase(ZX_INFO_HANDLE_COUNT);
TopicNameCase(ZX_INFO_BTI);
TopicNameCase(ZX_INFO_PROCESS_HANDLE_STATS);
TopicNameCase(ZX_INFO_SOCKET);
TopicNameCase(ZX_INFO_VMO);
TopicNameCase(ZX_INFO_JOB);
default:
*this << "topic=" << topic << ResetColor;
return;
}
}
void PrettyPrinter::DisplayPaddr(zx_paddr_t addr) {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
#ifdef __MACH__
snprintf(buffer.data(), buffer.size(), "%016" PRIxPTR, addr);
#else
snprintf(buffer.data(), buffer.size(), "%016" PRIx64, addr);
#endif
*this << Blue << buffer.data() << ResetColor;
}
#define PacketGuestVcpuTypeNameCase(name) \
case name: \
*this << #name << ResetColor; \
return
void PrettyPrinter::DisplayPacketGuestVcpuType(uint8_t type) {
*this << Blue;
switch (type) {
PacketGuestVcpuTypeNameCase(ZX_PKT_GUEST_VCPU_INTERRUPT);
PacketGuestVcpuTypeNameCase(ZX_PKT_GUEST_VCPU_STARTUP);
default:
*this << static_cast<uint32_t>(type) << ResetColor;
return;
}
}
#define PacketPageRequestCommandNameCase(name) \
case name: \
*this << #name << ResetColor; \
return
void PrettyPrinter::DisplayPacketPageRequestCommand(uint16_t command) {
*this << Blue;
switch (command) {
PacketPageRequestCommandNameCase(ZX_PAGER_VMO_READ);
PacketPageRequestCommandNameCase(ZX_PAGER_VMO_COMPLETE);
default:
*this << static_cast<uint32_t>(command) << ResetColor;
return;
}
}
#define PciBarTypeNameCase(name) \
case name: \
*this << #name << ResetColor; \
return
void PrettyPrinter::DisplayPciBarType(uint32_t type) {
*this << Blue;
switch (type) {
PciBarTypeNameCase(ZX_PCI_BAR_TYPE_UNUSED);
PciBarTypeNameCase(ZX_PCI_BAR_TYPE_MMIO);
PciBarTypeNameCase(ZX_PCI_BAR_TYPE_PIO);
default:
*this << static_cast<uint32_t>(type) << ResetColor;
return;
}
}
#define ProfileInfoFlagsNameCase(name) \
if ((flags & (name)) == (name)) { \
*this << separator << #name; \
separator = " | "; \
}
void PrettyPrinter::DisplayProfileInfoFlags(uint32_t flags) {
*this << Blue;
if (flags == 0) {
*this << "0" << ResetColor;
return;
}
const char* separator = "";
ProfileInfoFlagsNameCase(ZX_PROFILE_INFO_FLAG_PRIORITY);
ProfileInfoFlagsNameCase(ZX_PROFILE_INFO_FLAG_CPU_MASK);
*this << ResetColor;
}
#define PortPacketTypeNameCase(name) \
case name: \
*this << #name << ResetColor; \
return
void PrettyPrinter::DisplayPortPacketType(uint32_t type) {
*this << Blue;
switch (type) {
PortPacketTypeNameCase(ZX_PKT_TYPE_USER);
PortPacketTypeNameCase(ZX_PKT_TYPE_SIGNAL_ONE);
PortPacketTypeNameCase(ZX_PKT_TYPE_GUEST_BELL);
PortPacketTypeNameCase(ZX_PKT_TYPE_GUEST_MEM);
PortPacketTypeNameCase(ZX_PKT_TYPE_GUEST_IO);
PortPacketTypeNameCase(ZX_PKT_TYPE_GUEST_VCPU);
PortPacketTypeNameCase(ZX_PKT_TYPE_INTERRUPT);
PortPacketTypeNameCase(ZX_PKT_TYPE_PAGE_REQUEST);
default:
*this << "port_packet_type=" << type << ResetColor;
return;
}
}
// ZX_PROP_REGISTER_GS and ZX_PROP_REGISTER_FS are defined in
// <zircon/system/public/zircon/syscalls/object.h>
// but only available for amd64.
// We need these values in all the environments.
#ifndef ZX_PROP_REGISTER_GS
#define ZX_PROP_REGISTER_GS ((uint32_t)2u)
#endif
#ifndef ZX_PROP_REGISTER_FS
#define ZX_PROP_REGISTER_FS ((uint32_t)4u)
#endif
#define PropTypeNameCase(name) \
case name: \
*this << #name; \
*this << ResetColor; \
return
void PrettyPrinter::DisplayPropType(uint32_t type) {
*this << Blue;
switch (type) {
PropTypeNameCase(ZX_PROP_NAME);
PropTypeNameCase(ZX_PROP_REGISTER_FS);
PropTypeNameCase(ZX_PROP_REGISTER_GS);
PropTypeNameCase(ZX_PROP_PROCESS_DEBUG_ADDR);
PropTypeNameCase(ZX_PROP_PROCESS_VDSO_BASE_ADDRESS);
PropTypeNameCase(ZX_PROP_SOCKET_RX_THRESHOLD);
PropTypeNameCase(ZX_PROP_SOCKET_TX_THRESHOLD);
PropTypeNameCase(ZX_PROP_JOB_KILL_ON_OOM);
PropTypeNameCase(ZX_PROP_EXCEPTION_STATE);
default:
*this << type << ResetColor;
return;
}
}
#define RightsNameCase(name) \
if ((rights & (name)) != 0) { \
*this << separator << #name; \
separator = " | "; \
}
void PrettyPrinter::DisplayRights(uint32_t rights) {
*this << Blue;
if (rights == 0) {
*this << "ZX_RIGHT_NONE" << ResetColor;
return;
}
const char* separator = "";
RightsNameCase(ZX_RIGHT_DUPLICATE);
RightsNameCase(ZX_RIGHT_TRANSFER);
RightsNameCase(ZX_RIGHT_READ);
RightsNameCase(ZX_RIGHT_WRITE);
RightsNameCase(ZX_RIGHT_EXECUTE);
RightsNameCase(ZX_RIGHT_MAP);
RightsNameCase(ZX_RIGHT_GET_PROPERTY);
RightsNameCase(ZX_RIGHT_SET_PROPERTY);
RightsNameCase(ZX_RIGHT_ENUMERATE);
RightsNameCase(ZX_RIGHT_DESTROY);
RightsNameCase(ZX_RIGHT_SET_POLICY);
RightsNameCase(ZX_RIGHT_GET_POLICY);
RightsNameCase(ZX_RIGHT_SIGNAL);
RightsNameCase(ZX_RIGHT_SIGNAL_PEER);
RightsNameCase(ZX_RIGHT_WAIT);
RightsNameCase(ZX_RIGHT_INSPECT);
RightsNameCase(ZX_RIGHT_MANAGE_JOB);
RightsNameCase(ZX_RIGHT_MANAGE_PROCESS);
RightsNameCase(ZX_RIGHT_MANAGE_THREAD);
RightsNameCase(ZX_RIGHT_APPLY_PROFILE);
RightsNameCase(ZX_RIGHT_SAME_RIGHTS);
*this << ResetColor;
}
#define SignalNameCase(name) \
if ((signals & (name)) == (name)) { \
*this << separator << #name; \
separator = " | "; \
}
void PrettyPrinter::DisplaySignals(zx_signals_t signals) {
*this << Blue;
if (signals == 0) {
*this << "0" << ResetColor;
return;
}
if (signals == __ZX_OBJECT_SIGNAL_ALL) {
*this << "__ZX_OBJECT_SIGNAL_ALL" << ResetColor;
return;
}
const char* separator = "";
SignalNameCase(__ZX_OBJECT_READABLE);
SignalNameCase(__ZX_OBJECT_WRITABLE);
SignalNameCase(__ZX_OBJECT_PEER_CLOSED);
SignalNameCase(__ZX_OBJECT_SIGNALED);
SignalNameCase(__ZX_OBJECT_SIGNAL_4);
SignalNameCase(__ZX_OBJECT_SIGNAL_5);
SignalNameCase(__ZX_OBJECT_SIGNAL_6);
SignalNameCase(__ZX_OBJECT_SIGNAL_7);
SignalNameCase(__ZX_OBJECT_SIGNAL_8);
SignalNameCase(__ZX_OBJECT_SIGNAL_9);
SignalNameCase(__ZX_OBJECT_SIGNAL_10);
SignalNameCase(__ZX_OBJECT_SIGNAL_11);
SignalNameCase(__ZX_OBJECT_SIGNAL_12);
SignalNameCase(__ZX_OBJECT_SIGNAL_13);
SignalNameCase(__ZX_OBJECT_SIGNAL_14);
SignalNameCase(__ZX_OBJECT_SIGNAL_15);
SignalNameCase(__ZX_OBJECT_SIGNAL_16);
SignalNameCase(__ZX_OBJECT_SIGNAL_17);
SignalNameCase(__ZX_OBJECT_SIGNAL_18);
SignalNameCase(__ZX_OBJECT_SIGNAL_19);
SignalNameCase(__ZX_OBJECT_SIGNAL_20);
SignalNameCase(__ZX_OBJECT_SIGNAL_21);
SignalNameCase(__ZX_OBJECT_SIGNAL_22);
SignalNameCase(__ZX_OBJECT_HANDLE_CLOSED);
SignalNameCase(ZX_USER_SIGNAL_0);
SignalNameCase(ZX_USER_SIGNAL_1);
SignalNameCase(ZX_USER_SIGNAL_2);
SignalNameCase(ZX_USER_SIGNAL_3);
SignalNameCase(ZX_USER_SIGNAL_4);
SignalNameCase(ZX_USER_SIGNAL_5);
SignalNameCase(ZX_USER_SIGNAL_6);
SignalNameCase(ZX_USER_SIGNAL_7);
*this << ResetColor;
}
void PrettyPrinter::DisplayStatus(zx_status_t status) {
if (status == ZX_OK) {
*this << Green;
} else {
*this << Red;
}
*this << StatusName(status) << ResetColor;
}
void PrettyPrinter::DisplayString(std::string_view string) {
if (string.data() == nullptr) {
*this << "nullptr\n";
} else {
*this << Red << '"';
for (char value : string) {
switch (value) {
case 0:
break;
case '\\':
*this << "\\\\";
break;
case '\n':
*this << "\\n";
break;
default:
*this << value;
break;
}
}
*this << '"' << ResetColor;
}
}
void PrettyPrinter::DisplayTime(zx_time_t time_ns) {
if (time_ns == ZX_TIME_INFINITE) {
(*this) << Blue << "ZX_TIME_INFINITE" << ResetColor;
} else if (time_ns == ZX_TIME_INFINITE_PAST) {
(*this) << Blue << "ZX_TIME_INFINITE_PAST" << ResetColor;
} else {
// Gets the time in seconds.
time_t value = time_ns / kOneBillion;
struct tm tm;
if (localtime_r(&value, &tm) == &tm) {
char buffer[100];
strftime(buffer, sizeof(buffer), "%c", &tm);
// And now, displays the nano seconds.
(*this) << Blue << buffer << " and ";
snprintf(buffer, sizeof(buffer), "%09" PRId64, time_ns % kOneBillion);
(*this) << buffer << " ns" << ResetColor;
} else {
(*this) << Red << "unknown time" << ResetColor;
}
}
}
#ifdef __MACH__
void PrettyPrinter::DisplayUintptr(uintptr_t ptr) {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIxPTR, ptr);
*this << Blue << buffer.data() << ResetColor;
}
#else
void PrettyPrinter::DisplayUintptr(uint64_t ptr) {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIx64, ptr);
*this << Blue << buffer.data() << ResetColor;
}
#endif
void PrettyPrinter::DisplayVaddr(zx_vaddr_t addr) {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
#ifdef __MACH__
snprintf(buffer.data(), buffer.size(), "%016" PRIxPTR, addr);
#else
snprintf(buffer.data(), buffer.size(), "%016" PRIx64, addr);
#endif
*this << Blue << buffer.data() << ResetColor;
}
void PrettyPrinter::DisplayGpAddr(zx_gpaddr_t addr) {
#ifdef __MACH__
std::vector<char> buffer(sizeof(uintptr_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIxPTR, addr);
#else
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIx64, addr);
#endif
*this << Blue << buffer.data() << ResetColor;
}
void PrettyPrinter::IncrementTabulations() {
++tabulations_;
if (need_to_print_header_) {
remaining_size_ -= kTabSize;
}
}
void PrettyPrinter::DecrementTabulations() {
--tabulations_;
if (need_to_print_header_) {
remaining_size_ += kTabSize;
}
}
void PrettyPrinter::NeedHeader() {
remaining_size_ = max_line_size_ - line_header_size_ - tabulations_ * kTabSize;
need_to_print_header_ = true;
}
void PrettyPrinter::PrintHeader(char first_character) {
FX_DCHECK(need_to_print_header_);
need_to_print_header_ = false;
if (line_header_size_ > 0) {
os_ << line_header_;
if (!header_on_every_line_) {
line_header_size_ = 0;
}
}
if (first_character != '\n') {
for (int tab = tabulations_ * kTabSize; tab > 0; --tab) {
os_ << ' ';
}
}
}
PrettyPrinter& PrettyPrinter::operator<<(std::string_view data) {
if (data.empty()) {
return *this;
}
if (need_to_print_header_) {
PrintHeader(data[0]);
}
size_t end_of_line = data.find('\n', 0);
if (end_of_line == std::string_view::npos) {
os_ << data;
remaining_size_ -= data.size();
return *this;
}
size_t current = 0;
for (;;) {
std::string_view tmp = data.substr(current, end_of_line - current + 1);
os_ << tmp;
NeedHeader();
current = end_of_line + 1;
if (current >= data.size()) {
return *this;
}
end_of_line = data.find('\n', current);
if (end_of_line == std::string_view::npos) {
os_ << data;
remaining_size_ -= data.size();
return *this;
}
PrintHeader(data[current]);
}
}
} // namespace fidl_codec