blob: 69a13888c5c48c051b9d962e4819ed550c3d7922 [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 "message_decoder.h"
#include <src/lib/fxl/logging.h>
#include <ostream>
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "tools/fidlcat/lib/library_loader.h"
#include "tools/fidlcat/lib/wire_object.h"
#include "tools/fidlcat/lib/wire_parser.h"
#include "tools/fidlcat/lib/wire_types.h"
namespace fidlcat {
MessageDecoder::MessageDecoder(const fidl::Message& message, bool output_errors)
: start_byte_pos_(message.bytes().begin()),
start_handle_pos_(message.handles().begin()),
end_byte_pos_(message.bytes().end()),
end_handle_pos_(message.handles().end()),
byte_pos_(message.bytes().begin()),
handle_pos_(message.handles().begin()),
output_errors_(output_errors) {}
MessageDecoder::MessageDecoder(const MessageDecoder* container,
uint64_t num_bytes, uint64_t num_handles)
: start_byte_pos_(container->byte_pos_),
start_handle_pos_(container->handle_pos_),
end_byte_pos_(container->byte_pos_ + num_bytes),
end_handle_pos_(container->handle_pos_ + num_handles),
byte_pos_(container->byte_pos_),
handle_pos_(container->handle_pos_),
output_errors_(container->output_errors_) {}
void MessageDecoder::ProcessSecondaryObjects() {
for (size_t i = 0; i < secondary_objects_.size(); ++i) {
MessageDecoder decoder(this, end_byte_pos_ - byte_pos_,
end_handle_pos_ - handle_pos_);
secondary_objects_[i]->DecodeContent(&decoder);
decoder.ProcessSecondaryObjects();
GotoNextObjectOffset(decoder.byte_pos() - byte_pos_);
SkipHandles(decoder.handle_pos() - handle_pos_);
}
}
std::unique_ptr<Object> MessageDecoder::DecodeMessage(
const Struct& message_format) {
std::unique_ptr<Object> result =
message_format.DecodeObject(this, /*name=*/"", /*type=*/nullptr,
/*offset=*/0, /*nullable=*/false);
GotoNextObjectOffset(message_format.size());
ProcessSecondaryObjects();
return result;
}
std::unique_ptr<Field> MessageDecoder::DecodeField(std::string_view name,
const Type* type) {
std::unique_ptr<Field> result = type->Decode(this, name, 0);
GotoNextObjectOffset(type->InlineSize());
ProcessSecondaryObjects();
return result;
}
std::string DocumentToString(rapidjson::Document& document) {
rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
document.Accept(writer);
return output.GetString();
}
void DecodeMessage(
LibraryLoader* loader,
std::map<std::tuple<zx_handle_t, uint64_t>, Direction>* handle_directions,
const DisplayOptions& options, uint64_t process_koid, zx_handle_t handle,
const fidl::Message& message, bool read, std::ostream& os) {
fidl_message_header_t header = message.header();
const InterfaceMethod* method = loader->GetByOrdinal(header.ordinal);
if (method == nullptr) {
// Probably should print out raw bytes here instead.
FXL_LOG(WARNING) << "Protocol method with ordinal " << header.ordinal
<< " not found";
return;
}
std::unique_ptr<Object> decoded_request;
bool matched_request = DecodeRequest(method, message, &decoded_request);
std::unique_ptr<Object> decoded_response;
bool matched_response = DecodeResponse(method, message, &decoded_response);
Direction direction = Direction::kUnknown;
auto handle_direction =
handle_directions->find(std::make_tuple(handle, process_koid));
if (handle_direction != handle_directions->end()) {
direction = handle_direction->second;
} else {
// This is the first read or write we intercept for this handle/koid. If we
// launched the process, we suppose we intercepted the very first read or
// write.
// If this is not an event (which would mean method->request() is null), a
// write means that we are watching a client (a client starts by writing a
// request) and a read means that we are watching a server (a server starts
// by reading the first client request).
// If we attached to a running process, we can only determine correctly if
// we are watching a client or a server if we have only one matched_request
// or one matched_response.
if ((process_koid == ULLONG_MAX) || (matched_request != matched_response)) {
// We lanched the process or exactly one of request and response are
// valid => we can determine the direction.
// Currently, a process_koid of ULLONG_MAX means that we launched the
// process.
if (read) {
(*handle_directions)[std::make_tuple(handle, process_koid)] =
(method->request() != nullptr) ? Direction::kServer
: Direction::kClient;
} else {
(*handle_directions)[std::make_tuple(handle, process_koid)] =
(method->request() != nullptr) ? Direction::kClient
: Direction::kServer;
}
direction = (*handle_directions)[std::make_tuple(handle, process_koid)];
}
}
bool is_request = false;
if (read == (direction == Direction::kServer)) {
is_request = true;
}
if (direction != Direction::kUnknown) {
if ((is_request && !matched_request) ||
(!is_request && !matched_response)) {
if ((is_request && matched_response) ||
(!is_request && matched_request)) {
// The first determination seems to be wrong. That is, we are expecting
// a request but only a response has been successfully decoded or we are
// expecting a response but only a request has been successfully
// decoded.
// Invert the deduction which should now be the right one.
(*handle_directions)[std::make_tuple(handle, process_koid)] =
(direction == Direction::kClient) ? Direction::kServer
: Direction::kClient;
is_request = !is_request;
}
}
}
rapidjson::Document actual_request;
rapidjson::Document actual_response;
if (!options.pretty_print) {
if (decoded_request != nullptr) {
decoded_request->ExtractJson(actual_request.GetAllocator(),
actual_request);
}
if (decoded_response != nullptr) {
decoded_response->ExtractJson(actual_response.GetAllocator(),
actual_response);
}
}
const Colors& colors = options.needs_colors ? WithColors : WithoutColors;
int tabs = 0;
if (direction == Direction::kUnknown) {
os << colors.red << "Can't determine request/response." << colors.reset
<< " it can be:\n";
++tabs;
}
if (matched_request && (is_request || (direction == Direction::kUnknown))) {
os << std::string(tabs * kTabSize, ' ') << colors.white_on_magenta
<< "request" << colors.reset << ' ' << colors.green
<< method->enclosing_interface().name() << '.' << method->name()
<< colors.reset << " = ";
if (options.pretty_print) {
decoded_request->PrettyPrint(os, colors, tabs, tabs * kTabSize,
options.columns);
} else {
os << DocumentToString(actual_request);
}
os << '\n';
}
if (matched_response && (!is_request || (direction == Direction::kUnknown))) {
os << std::string(tabs * kTabSize, ' ') << colors.white_on_magenta
<< "response" << colors.reset << ' ' << colors.green
<< method->enclosing_interface().name() << '.' << method->name()
<< colors.reset << " = ";
if (options.pretty_print) {
decoded_response->PrettyPrint(os, colors, tabs, tabs * kTabSize,
options.columns);
} else {
os << DocumentToString(actual_response);
}
os << '\n';
}
}
} // namespace fidlcat