blob: 785a852f7d2aa415b722d5cdc896c952c3984f9d [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 "src/lib/fidl_codec/wire_object.h"
#include <lib/syslog/cpp/macros.h>
#include <iostream>
#include <memory>
#include <sstream>
#include <vector>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include "src/lib/fidl_codec/json_visitor.h"
#include "src/lib/fidl_codec/library_loader.h"
#include "src/lib/fidl_codec/printer.h"
#include "src/lib/fidl_codec/status.h"
#include "src/lib/fidl_codec/visitor.h"
#include "src/lib/fidl_codec/wire_types.h"
namespace fidl_codec {
void Value::ExtractJson(rapidjson::Document::AllocatorType& allocator,
rapidjson::Value& result) const {
JsonVisitor visitor(&result, &allocator);
Visit(&visitor, nullptr);
}
std::string DocumentToString(rapidjson::Document* document) {
rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
document->Accept(writer);
return output.GetString();
}
void InvalidValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitInvalidValue(this, for_type);
}
void EmptyPayloadValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitEmptyPayloadValue(this, for_type);
}
void NullValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitNullValue(this, for_type);
}
size_t RawValue::DisplaySize(const Type* /*for_type*/, size_t /*remaining_size*/) const {
return data_.empty() ? 0 : data_.size() * 3 - 1;
}
void RawValue::PrettyPrint(const Type* for_type, PrettyPrinter& printer) const {
if (data_.empty()) {
return;
}
size_t buffer_size = data_.size() * 3;
std::vector<char> buffer(buffer_size);
for (size_t i = 0; i < data_.size(); ++i) {
if (i != 0) {
buffer[i * 3 - 1] = ' ';
}
snprintf(buffer.data() + (i * 3), 4, "%02x", data_[i]);
}
printer << buffer.data();
}
void RawValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitRawValue(this, for_type);
}
size_t BoolValue::DisplaySize(const Type* /*for_type*/, size_t /*remaining_size*/) const {
constexpr int kTrueSize = 4;
constexpr int kFalseSize = 5;
return value_ ? kTrueSize : kFalseSize;
}
void BoolValue::PrettyPrint(const Type* for_type, PrettyPrinter& printer) const {
printer << Blue << (value_ ? "true" : "false") << ResetColor;
}
void BoolValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitBoolValue(this, for_type);
}
uint8_t IntegerValue::GetUint8Value() const {
return (!negative_ && (absolute_value_ < 0x100)) ? static_cast<uint8_t>(absolute_value_) : 0;
}
size_t IntegerValue::DisplaySize(const Type* for_type, size_t /*remaining_size*/) const {
std::stringstream dummyStream;
auto dummyPrinter = PrettyPrinter(dummyStream, WithoutColors, true, "", INT_MAX, false);
for_type->PrettyPrint(this, dummyPrinter);
return dummyStream.str().length();
}
void IntegerValue::PrettyPrint(const Type* for_type, PrettyPrinter& printer) const {
FX_DCHECK(for_type != nullptr);
for_type->PrettyPrint(this, printer);
}
void IntegerValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitIntegerValue(this, for_type);
}
size_t ActualAndRequestedValue::DisplaySize(const Type* for_type, size_t /*remaining_size*/) const {
std::stringstream dummyStream;
auto dummyPrinter = PrettyPrinter(dummyStream, WithoutColors, true, "", INT_MAX, false);
for_type->PrettyPrint(this, dummyPrinter);
return dummyStream.str().length();
}
void ActualAndRequestedValue::PrettyPrint(const Type* for_type, PrettyPrinter& printer) const {
FX_DCHECK(for_type != nullptr);
for_type->PrettyPrint(this, printer);
}
void ActualAndRequestedValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitActualAndRequestedValue(this, for_type);
}
size_t DoubleValue::DisplaySize(const Type* for_type, size_t /*remaining_size*/) const {
return std::to_string(value_).size();
}
void DoubleValue::PrettyPrint(const Type* for_type, PrettyPrinter& printer) const {
FX_DCHECK(for_type != nullptr);
for_type->PrettyPrint(this, printer);
}
void DoubleValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitDoubleValue(this, for_type);
}
size_t StringValue::DisplaySize(const Type* /*for_type*/, size_t /*remaining_size*/) const {
return string_.size() + 2; // The two quotes.
}
void StringValue::PrettyPrint(const Type* for_type, PrettyPrinter& printer) const {
printer.DisplayString(string_);
}
void StringValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitStringValue(this, for_type);
}
bool HandleValue::NeedsToLoadHandleInfo(int64_t timestamp, zx_koid_t tid,
semantic::HandleSemantic* handle_semantic) const {
if (handle_.handle == ZX_HANDLE_INVALID) {
return false;
}
return handle_semantic->NeedsToLoadHandleInfo(timestamp, tid, handle_.handle);
}
size_t HandleValue::DisplaySize(const Type* /*for_type*/, size_t /*remaining_size*/) const {
return std::to_string(handle_.handle).size();
}
void HandleValue::PrettyPrint(const Type* /*for_type*/, PrettyPrinter& printer) const {
printer.DisplayHandle(handle_);
}
void HandleValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitHandleValue(this, for_type);
}
bool UnionValue::NeedsToLoadHandleInfo(int64_t timestamp, zx_koid_t tid,
semantic::HandleSemantic* handle_semantic) const {
return value_->NeedsToLoadHandleInfo(timestamp, tid, handle_semantic);
}
size_t UnionValue::DisplaySize(const Type* for_type, size_t remaining_size) const {
// Two characters for the opening brace ("{ ") + three characters for equal
// (" = ") and two characters for the closing brace (" }").
constexpr int kExtraSize = 7;
size_t size = member_.name().size() + kExtraSize;
// Two characters for ": ".
size += member_.type()->Name().size() + 2;
size += value_->DisplaySize(member_.type(), remaining_size - size);
return size;
}
void UnionValue::PrettyPrint(const Type* for_type, PrettyPrinter& printer) const {
if (DisplaySize(for_type, printer.remaining_size()) <= printer.remaining_size()) {
printer << "{ " << member_.name() << ": ";
member_.type()->PrettyPrint(printer);
printer << " = ";
value_->PrettyPrint(member_.type(), printer);
printer << " }";
} else {
printer << "{\n";
{
Indent indent(printer);
printer << member_.name() << ": ";
member_.type()->PrettyPrint(printer);
printer << " = ";
value_->PrettyPrint(member_.type(), printer);
printer << '\n';
}
printer << "}";
}
}
void UnionValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitUnionValue(this, for_type);
}
std::pair<const Type*, const Value*> StructValue::GetFieldValue(std::string_view field_name) const {
for (const auto& field : fields_) {
if (field.first->name() == field_name) {
return std::make_pair(field.first->type(), field.second.get());
}
}
return std::make_pair(nullptr, nullptr);
}
void StructValue::AddField(std::string_view name, uint32_t id, std::unique_ptr<Value> value) {
StructMember* member = struct_definition_.SearchMember(name, id);
if (member != nullptr) {
AddField(member, std::move(value));
}
}
bool StructValue::NeedsToLoadHandleInfo(int64_t timestamp, zx_koid_t tid,
semantic::HandleSemantic* handle_semantic) const {
for (const auto& field : fields_) {
if (field.second->NeedsToLoadHandleInfo(timestamp, tid, handle_semantic)) {
return true;
}
}
return false;
}
size_t StructValue::DisplaySize(const Type* for_type, size_t remaining_size) const {
size_t size = 0;
for (const auto& member : struct_definition_.members()) {
auto it = fields_.find(member.get());
if (it == fields_.end())
continue;
// Two characters for the separator ("{ " or ", ") and three characters for
// equal (" = ").
constexpr int kExtraSize = 5;
size += member->name().size() + kExtraSize;
// Two characters for ": ".
size += member->type()->Name().size() + 2;
size += it->second->DisplaySize(member->type(), remaining_size - size);
if (size > remaining_size) {
return size;
}
}
// Two characters for the closing brace (" }").
size += 2;
return size;
}
void StructValue::PrettyPrint(const Type* for_type, PrettyPrinter& printer) const {
if (fields_.empty()) {
printer << "{}";
} else if (DisplaySize(for_type, printer.remaining_size()) <= printer.remaining_size()) {
const char* separator = "{ ";
for (const auto& member : struct_definition_.members()) {
auto it = fields_.find(member.get());
if (it == fields_.end())
continue;
printer << separator << member->name() << ": ";
member->type()->PrettyPrint(printer);
printer << " = ";
it->second->PrettyPrint(member->type(), printer);
separator = ", ";
}
printer << " }";
} else {
printer << "{\n";
{
Indent indent(printer);
for (const auto& member : struct_definition_.members()) {
auto it = fields_.find(member.get());
if (it == fields_.end())
continue;
std::string type_name = member->type()->Name();
printer << member->name() << ": ";
member->type()->PrettyPrint(printer);
printer << " = ";
it->second->PrettyPrint(member->type(), printer);
printer << "\n";
}
}
printer << '}';
}
}
void StructValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitStructValue(this, for_type);
}
bool VectorValue::NeedsToLoadHandleInfo(int64_t timestamp, zx_koid_t tid,
semantic::HandleSemantic* handle_semantic) const {
for (const auto& value : values_) {
if (value->NeedsToLoadHandleInfo(timestamp, tid, handle_semantic)) {
return true;
}
}
return false;
}
size_t VectorValue::DisplaySize(const Type* for_type, size_t remaining_size) const {
FX_DCHECK(for_type != nullptr);
if (values_.empty()) {
return 2; // The two brackets.
}
if (is_string_) {
return values_.size() + 2; // The string and the two quotes.
}
const Type* component_type = for_type->GetComponentType();
FX_DCHECK(component_type != nullptr);
size_t size = 0;
for (const auto& value : values_) {
// Two characters for the separator ("[ " or ", ").
size += value->DisplaySize(component_type, remaining_size - size) + 2;
if (size > remaining_size) {
return size;
}
}
// Two characters for the closing bracket (" ]").
size += 2;
return size;
}
void VectorValue::PrettyPrint(const Type* for_type, PrettyPrinter& printer) const {
FX_DCHECK(for_type != nullptr);
if (values_.empty()) {
printer << "[]";
} else if (is_string_) {
if (has_new_line_) {
printer << "[\n";
{
Indent indent(printer);
printer << Red;
for (const auto& value : values_) {
printer << static_cast<char>(value->GetUint8Value());
}
printer << ResetColor;
}
printer << '\n';
printer << ']';
} else {
printer << Red << '"';
for (const auto& value : values_) {
printer << static_cast<char>(value->GetUint8Value());
}
printer << '"' << ResetColor;
}
} else if (DisplaySize(for_type, printer.remaining_size()) <= printer.remaining_size()) {
const Type* component_type = for_type->GetComponentType();
FX_DCHECK(component_type != nullptr);
const char* separator = "[ ";
for (const auto& value : values_) {
printer << separator;
separator = ", ";
value->PrettyPrint(component_type, printer);
}
printer << " ]";
} else {
const Type* component_type = for_type->GetComponentType();
FX_DCHECK(component_type != nullptr);
printer << "[\n";
{
Indent indent(printer);
for (const auto& value : values_) {
size_t value_size = value->DisplaySize(component_type, printer.remaining_size());
if (!printer.LineEmpty()) {
if (value_size + 3 > printer.remaining_size()) {
printer << "\n";
} else {
printer << ", ";
}
}
value->PrettyPrint(component_type, printer);
}
printer << '\n';
}
printer << ']';
}
}
void VectorValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitVectorValue(this, for_type);
}
bool TableValue::AddMember(std::string_view name, std::unique_ptr<Value> value) {
const TableMember* member = table_definition_.SearchMember(name);
if (member == nullptr) {
return false;
}
AddMember(member, std::move(value));
return true;
}
bool TableValue::NeedsToLoadHandleInfo(int64_t timestamp, zx_koid_t tid,
semantic::HandleSemantic* handle_semantic) const {
for (const auto& member : members_) {
if (member.second->NeedsToLoadHandleInfo(timestamp, tid, handle_semantic)) {
return true;
}
}
return false;
}
size_t TableValue::DisplaySize(const Type* for_type, size_t remaining_size) const {
size_t size = 0;
for (const auto& member : table_definition_.members()) {
if (member != nullptr) {
auto it = members_.find(member.get());
if ((it == members_.end()) || (it->second == nullptr) || it->second->IsNull())
continue;
// Two characters for the separator ("{ " or ", "), three characters for " = ".
size += member->name().size() + 2 + 3;
// Two characters for ": ".
size += member->type()->Name().size() + 2;
size += it->second->DisplaySize(member->type(), remaining_size - size);
if (size > remaining_size) {
return size;
}
}
}
// Two characters for the closing brace (" }").
size += 2;
return size;
}
void TableValue::PrettyPrint(const Type* for_type, PrettyPrinter& printer) const {
size_t display_size = DisplaySize(for_type, printer.remaining_size());
if (display_size == 2) {
printer << "{}";
} else if (display_size <= printer.remaining_size()) {
const char* separator = "{ ";
for (const auto& member : table_definition_.members()) {
if (member != nullptr) {
auto it = members_.find(member.get());
if ((it == members_.end()) || (it->second == nullptr) || it->second->IsNull())
continue;
printer << separator << member->name() << ": " << Green << member->type()->Name()
<< ResetColor << " = ";
separator = ", ";
it->second->PrettyPrint(member->type(), printer);
}
}
printer << " }";
} else {
printer << "{\n";
{
Indent indent(printer);
for (const auto& member : table_definition_.members()) {
if (member != nullptr) {
auto it = members_.find(member.get());
if ((it == members_.end()) || (it->second == nullptr) || it->second->IsNull())
continue;
std::string type_name = member->type()->Name();
printer << member->name() << ": " << Green << type_name << ResetColor << " = ";
it->second->PrettyPrint(member->type(), printer);
printer << "\n";
}
}
}
printer << '}';
}
}
void TableValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitTableValue(this, for_type);
}
FidlMessageValue::FidlMessageValue(fidl_codec::DecodedMessage* message, std::string global_errors,
const uint8_t* bytes, uint32_t num_bytes,
const zx_handle_disposition_t* handles, uint32_t num_handles)
: txid_(message->txid()),
ordinal_(message->ordinal()),
global_errors_(std::move(global_errors)),
epitaph_error_(
(message->ordinal() == kFidlOrdinalEpitaph) ? StatusName(message->epitaph_error()) : ""),
received_(message->received()),
is_request_(message->is_request()),
unknown_direction_(message->direction() == Direction::kUnknown),
method_(message->method()),
bytes_(bytes, bytes + num_bytes),
handles_(handles, handles + num_handles),
decoded_request_((unknown_direction_ || is_request_) ? std::move(message->decoded_request())
: nullptr),
request_errors_((unknown_direction_ || is_request_) ? message->request_error_stream().str()
: ""),
decoded_response_(
(unknown_direction_ || !is_request_) ? std::move(message->decoded_response()) : nullptr),
response_errors_((unknown_direction_ || !is_request_) ? message->response_error_stream().str()
: "") {}
bool FidlMessageValue::NeedsToLoadHandleInfo(int64_t timestamp, zx_koid_t tid,
semantic::HandleSemantic* handle_semantic) const {
for (const auto& handle : handles_) {
auto inferred_handle_info = handle_semantic->GetInferredHandleInfo(tid, handle.handle);
if (inferred_handle_info == nullptr) {
return true;
}
}
return false;
}
size_t FidlMessageValue::DisplaySize(const Type* for_type, size_t remaining_size) const {
return 0;
}
void FidlMessageValue::PrettyPrint(const Type* for_type, PrettyPrinter& printer) const {
PrintMessage(printer);
DumpMessage(printer);
}
void FidlMessageValue::PrintMessage(PrettyPrinter& printer) const {
if (!global_errors_.empty()) {
printer << global_errors_;
return;
}
if (ordinal_ == kFidlOrdinalEpitaph) {
printer << fidl_codec::WhiteOnMagenta << (received_ ? "received" : "sent") << " epitaph"
<< fidl_codec::ResetColor << ' ' << fidl_codec::Red << epitaph_error_
<< fidl_codec::ResetColor << "\n";
return;
}
if (unknown_direction_) {
bool matched_request = (decoded_request_ != nullptr) && request_errors_.empty();
bool matched_response = (decoded_response_ != nullptr) && response_errors_.empty();
if (matched_request || matched_response) {
printer << fidl_codec::Red << "Can't determine request/response." << fidl_codec::ResetColor
<< " it can be:\n";
fidl_codec::Indent indent(printer);
PrintMessageBody(printer);
return;
}
}
PrintMessageBody(printer);
}
void FidlMessageValue::PrintMessageBody(PrettyPrinter& printer) const {
if (matched_request() && (is_request_ || unknown_direction_)) {
printer << fidl_codec::WhiteOnMagenta << (received_ ? "received" : "sent") << " request"
<< fidl_codec::ResetColor << ' ' << fidl_codec::Green
<< method_->enclosing_protocol().name() << '.' << method_->name()
<< fidl_codec::ResetColor << " = ";
if (printer.pretty_print()) {
decoded_request_->PrettyPrint(nullptr, printer);
printer << '\n';
} else {
rapidjson::Document actual_request;
if (decoded_request_ != nullptr) {
decoded_request_->ExtractJson(actual_request.GetAllocator(), actual_request);
}
printer << DocumentToString(&actual_request) << '\n';
}
}
if (matched_response() && (!is_request_ || unknown_direction_)) {
printer << fidl_codec::WhiteOnMagenta << (received_ ? "received" : "sent")
<< (method_->has_request() ? " response" : " event") << fidl_codec::ResetColor << ' '
<< fidl_codec::Green << method_->enclosing_protocol().name() << '.' << method_->name()
<< fidl_codec::ResetColor << " = ";
if (printer.pretty_print()) {
decoded_response_->PrettyPrint(nullptr, printer);
printer << '\n';
} else {
rapidjson::Document actual_response;
if (decoded_response_ != nullptr) {
decoded_response_->ExtractJson(actual_response.GetAllocator(), actual_response);
}
printer << DocumentToString(&actual_response) << '\n';
}
}
if (matched_request() || matched_response()) {
return;
}
if (!request_errors_.empty()) {
printer << fidl_codec::Red << (received_ ? "received" : "sent") << " request errors"
<< fidl_codec::ResetColor << ":\n";
{
Indent indent(printer);
printer << request_errors_;
}
if (decoded_request_ != nullptr) {
printer << fidl_codec::WhiteOnMagenta << (received_ ? "received" : "sent") << " request"
<< fidl_codec::ResetColor << ' ' << fidl_codec::Green
<< method_->enclosing_protocol().name() << '.' << method_->name()
<< fidl_codec::ResetColor << " = ";
decoded_request_->PrettyPrint(nullptr, printer);
printer << '\n';
}
}
if (!response_errors_.empty()) {
printer << fidl_codec::Red << (received_ ? "received" : "sent")
<< (method_->has_request() ? " response errors" : " event errors")
<< fidl_codec::ResetColor << ":\n";
{
Indent indent(printer);
printer << response_errors_;
}
if (decoded_response_ != nullptr) {
printer << fidl_codec::WhiteOnMagenta << (received_ ? "received" : "sent")
<< (method_->has_request() ? " response" : " event") << fidl_codec::ResetColor << ' '
<< fidl_codec::Green << method_->enclosing_protocol().name() << '.' << method_->name()
<< fidl_codec::ResetColor << " = ";
decoded_response_->PrettyPrint(nullptr, printer);
printer << '\n';
}
}
}
void FidlMessageValue::DumpMessage(PrettyPrinter& printer) const {
constexpr int kPatternColorSize = 4;
constexpr int kPatternSize = 8;
constexpr int kLineSize = 16;
constexpr int kLineHandleSize = 4;
constexpr int kCharactersPerByte = 2;
if ((decoded_request_ == nullptr) && (decoded_response_ == nullptr) &&
(ordinal_ != kFidlOrdinalEpitaph)) {
printer << fidl_codec::Red << "Can't decode message: ";
} else if (printer.DumpMessages()) {
printer << "Message: ";
} else {
return;
}
printer << "num_bytes=" << bytes_.size() << " num_handles=" << handles_.size();
if (txid_ != 0) {
printer << " txid=" << std::hex << txid_ << std::dec;
}
if (ordinal_ != 0) {
printer << " ordinal=" << std::hex << ordinal_ << std::dec;
if (method_ != nullptr) {
printer << '(' << method_->enclosing_protocol().name() << '.' << method_->name() << ')';
}
}
printer << fidl_codec::ResetColor << '\n';
{
Indent indent_data(printer);
printer << "data=";
const char* separator = "";
Indent indent_dump(printer);
for (uint32_t i = 0; i < bytes_.size(); ++i) {
// Display 16 bytes per line.
if (i % kLineSize == 0) {
std::vector<char> buffer(sizeof(uint32_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%04x", i);
printer << separator << "\n";
printer << buffer.data() << ": ";
separator = "";
}
// Display 4 bytes in red then four bytes in black ...
if (i % kPatternSize == 0) {
printer << fidl_codec::Red;
} else if (i % kPatternColorSize == 0) {
printer << fidl_codec::ResetColor;
}
std::vector<char> buffer(sizeof(uint8_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%02x", bytes_[i]);
printer << separator << buffer.data();
separator = ", ";
}
printer << fidl_codec::ResetColor << '\n';
}
if (handles_.size() > 0) {
Indent indent_data(printer);
printer << "handles=";
Indent indent_dump(printer);
const char* separator = "";
for (uint32_t i = 0; i < handles_.size(); ++i) {
// Display 4 bytes per line.
if (i % kLineHandleSize == 0) {
std::vector<char> buffer(sizeof(uint32_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%04x", i);
printer << separator << "\n";
printer << buffer.data() << ": ";
separator = "";
}
std::vector<char> buffer(sizeof(zx_handle_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%08x", handles_[i].handle);
printer << separator << buffer.data();
separator = ", ";
}
printer << '\n';
}
}
void FidlMessageValue::Visit(Visitor* visitor, const Type* for_type) const {
visitor->VisitFidlMessageValue(this, for_type);
}
} // namespace fidl_codec