blob: f5f707bee6c6f3125c95dda47703358da1885f4d [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 "tools/fidlcat/lib/syscall_decoder_dispatcher.h"
#include <zircon/system/public/zircon/errors.h>
#include <zircon/system/public/zircon/types.h>
#include <cstdint>
#include <memory>
#include <sstream>
#include "src/developer/debug/zxdb/client/process.h"
#include "src/developer/debug/zxdb/client/thread.h"
#include "tools/fidlcat/lib/inference.h"
#include "tools/fidlcat/lib/syscall_decoder.h"
namespace fidlcat {
constexpr int kPatternColorSize = 4;
constexpr int kPatternSize = 8;
constexpr int kLineSize = 16;
constexpr int kLineHandleSize = 4;
void DumpMessage(bool error, const uint8_t* bytes, uint32_t num_bytes,
const zx_handle_info_t* handles, uint32_t num_handles,
SyscallDisplayDispatcher* dispatcher, std::string_view line_header, int tabs,
std::ostream& os) {
os << line_header << std::string(tabs * fidl_codec::kTabSize, ' ');
if (error) {
os << dispatcher->colors().red << "Can't decode message: ";
} else {
os << "Message: ";
}
os << "num_bytes=" << num_bytes << " num_handles=" << num_handles;
if ((bytes != nullptr) && (num_bytes >= sizeof(fidl_message_header_t))) {
auto header = reinterpret_cast<const fidl_message_header_t*>(bytes);
os << " ordinal=" << std::hex << header->ordinal << std::dec;
if (dispatcher->message_decoder_dispatcher().loader() != nullptr) {
const std::vector<const fidl_codec::InterfaceMethod*>* methods =
dispatcher->message_decoder_dispatcher().loader()->GetByOrdinal(header->ordinal);
if ((methods != nullptr) && !methods->empty()) {
const fidl_codec::InterfaceMethod* method = (*methods)[0];
os << '(' << method->enclosing_interface().name() << '.' << method->name() << ')';
}
}
}
os << dispatcher->colors().reset << '\n';
os << line_header << std::string((tabs + 1) * fidl_codec::kTabSize, ' ') << "data=";
const char* separator = "";
for (uint32_t i = 0; i < num_bytes; ++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);
os << separator << "\n"
<< line_header << std::string((tabs + 1) * fidl_codec::kTabSize, ' ') << " "
<< buffer.data() << ": ";
separator = "";
}
// Display 4 bytes in red then four bytes in black ...
if (i % kPatternSize == 0) {
os << dispatcher->colors().red;
} else if (i % kPatternColorSize == 0) {
os << dispatcher->colors().reset;
}
std::vector<char> buffer(sizeof(uint8_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%02x", bytes[i]);
os << separator << buffer.data();
separator = ", ";
}
os << dispatcher->colors().reset << '\n';
if (num_handles > 0) {
os << line_header << std::string((tabs + 1) * fidl_codec::kTabSize, ' ') << "handles=";
const char* separator = "";
for (uint32_t i = 0; i < num_handles; ++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);
os << separator << "\n"
<< line_header << std::string((tabs + 1) * fidl_codec::kTabSize, ' ') << " "
<< buffer.data() << ": ";
separator = "";
}
std::vector<char> buffer(sizeof(zx_handle_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%08x", handles[i].handle);
os << separator << buffer.data();
separator = ", ";
}
os << '\n';
}
}
void DisplayString(const fidl_codec::Colors& colors, const char* string, size_t size,
std::ostream& os) {
if (string == nullptr) {
os << "nullptr\n";
} else {
if (size == 0) {
os << "empty\n";
} else {
os << colors.red << '"';
for (size_t i = 0; i < size; ++i) {
char value = string[i];
switch (value) {
case 0:
break;
case '\\':
os << "\\\\";
break;
case '\n':
os << "\\n";
break;
default:
os << value;
break;
}
}
os << '"' << colors.reset;
}
}
}
std::unique_ptr<fidl_codec::Type> AccessBase::ComputeType() const {
switch (GetSyscallType()) {
case SyscallType::kBool:
return std::make_unique<fidl_codec::BoolType>();
case SyscallType::kInt32:
return std::make_unique<fidl_codec::Int32Type>();
case SyscallType::kInt64:
return std::make_unique<fidl_codec::Int64Type>();
case SyscallType::kUint8:
return std::make_unique<fidl_codec::Uint8Type>();
case SyscallType::kUint8Hexa:
return std::make_unique<fidl_codec::Uint8Type>(fidl_codec::Uint8Type::Kind::kHexaDecimal);
case SyscallType::kUint16:
return std::make_unique<fidl_codec::Uint16Type>();
case SyscallType::kUint16Hexa:
return std::make_unique<fidl_codec::Uint16Type>(fidl_codec::Uint16Type::Kind::kHexaDecimal);
case SyscallType::kUint32:
return std::make_unique<fidl_codec::Uint32Type>();
case SyscallType::kUint32Hexa:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kHexaDecimal);
case SyscallType::kUint64:
return std::make_unique<fidl_codec::Uint64Type>();
case SyscallType::kUint64Hexa:
return std::make_unique<fidl_codec::Uint64Type>(fidl_codec::Uint64Type::Kind::kHexaDecimal);
case SyscallType::kHandle:
return std::make_unique<fidl_codec::HandleType>();
case SyscallType::kTime:
return std::make_unique<fidl_codec::Int64Type>(fidl_codec::Int64Type::Kind::kTime);
default:
return nullptr;
}
}
std::unique_ptr<fidl_codec::Type> SyscallInputOutputBase::ComputeType() const { return nullptr; }
std::unique_ptr<fidl_codec::Value> SyscallInputOutputBase::GenerateValue(SyscallDecoder* decoder,
Stage stage) const {
return std::make_unique<fidl_codec::InvalidValue>();
}
void SyscallInputOutputStringBuffer::DisplayOutline(SyscallDisplayDispatcher* dispatcher,
SyscallDecoder* decoder, Stage stage,
std::string_view line_header, int tabs,
std::ostream& os) const {
os << line_header << std::string((tabs + 1) * fidl_codec::kTabSize, ' ') << name();
const fidl_codec::Colors& colors = dispatcher->colors();
os << ':' << colors.green << "string" << colors.reset << ": ";
const char* const* buffer = buffer_->Content(decoder, stage);
if (buffer == nullptr) {
os << colors.red << "nullptr" << colors.reset;
} else {
uint32_t count = count_->Value(decoder, stage);
if (count == 0) {
os << "empty\n";
return;
}
const char* separator = "";
for (uint32_t i = 0; i < count; ++i) {
if (buffer[i] != nullptr) {
os << separator;
const char* string = reinterpret_cast<const char*>(
decoder->BufferContent(stage, reinterpret_cast<uint64_t>(buffer[i])));
size_t string_size = (string == nullptr) ? 0 : strnlen(string, max_size_);
DisplayString(colors, string, string_size, os);
separator = ", ";
}
}
}
os << '\n';
}
const char* SyscallInputOutputFixedSizeString::DisplayInline(SyscallDisplayDispatcher* dispatcher,
SyscallDecoder* decoder, Stage stage,
const char* separator,
std::ostream& os) const {
const fidl_codec::Colors& colors = dispatcher->colors();
os << separator;
os << name() << ':' << colors.green << "string" << colors.reset << ": ";
const char* string = string_->Content(decoder, stage);
size_t string_size = (string == nullptr) ? 0 : strnlen(string, string_size_);
DisplayString(colors, string, string_size, os);
return ", ";
}
std::unique_ptr<fidl_codec::Type> SyscallFidlMessageHandle::ComputeType() const {
return std::make_unique<fidl_codec::FidlMessageType>();
}
std::unique_ptr<fidl_codec::Value> SyscallFidlMessageHandle::GenerateValue(SyscallDecoder* decoder,
Stage stage) const {
zx_handle_t handle_value = handle()->Value(decoder, stage);
const uint8_t* bytes_value = bytes()->Content(decoder, stage);
uint32_t num_bytes_value = num_bytes()->Value(decoder, stage);
const zx_handle_t* handles_value = handles()->Content(decoder, stage);
uint32_t num_handles_value = num_handles()->Value(decoder, stage);
zx_handle_info_t* handle_infos_value = nullptr;
if (num_handles_value > 0) {
handle_infos_value = new zx_handle_info_t[num_handles_value];
for (uint32_t i = 0; i < num_handles_value; ++i) {
handle_infos_value[i].handle = handles_value[i];
handle_infos_value[i].type = ZX_OBJ_TYPE_NONE;
handle_infos_value[i].rights = 0;
}
}
fidl_codec::DecodedMessage message;
std::stringstream error_stream;
message.DecodeMessage(decoder->dispatcher()->MessageDecoderDispatcher(), decoder->process_id(),
handle_value, bytes_value, num_bytes_value, handle_infos_value,
num_handles_value, type(), error_stream);
auto result = std::make_unique<fidl_codec::FidlMessageValue>(
&message, error_stream.str(), bytes_value, num_bytes_value, handle_infos_value,
num_handles_value);
delete[] handle_infos_value;
if (result->is_request()) {
if (result->matched_request()) {
decoder->set_semantic(result->method()->semantic());
decoder->set_decoded_request(result->decoded_request());
}
if (result->matched_response()) {
decoder->set_semantic(result->method()->semantic());
decoder->set_decoded_response(result->decoded_response());
}
}
return result;
}
void SyscallFidlMessageHandle::DisplayOutline(SyscallDisplayDispatcher* dispatcher,
SyscallDecoder* decoder, Stage stage,
std::string_view line_header, int tabs,
std::ostream& os) const {
zx_handle_t handle_value = handle()->Value(decoder, stage);
const uint8_t* bytes_value = bytes()->Content(decoder, stage);
uint32_t num_bytes_value = num_bytes()->Value(decoder, stage);
const zx_handle_t* handles_value = handles()->Content(decoder, stage);
uint32_t num_handles_value = num_handles()->Value(decoder, stage);
zx_handle_info_t* handle_infos_value = nullptr;
if (num_handles_value > 0) {
handle_infos_value = new zx_handle_info_t[num_handles_value];
for (uint32_t i = 0; i < num_handles_value; ++i) {
handle_infos_value[i].handle = handles_value[i];
handle_infos_value[i].type = ZX_OBJ_TYPE_NONE;
handle_infos_value[i].rights = 0;
}
}
if (!dispatcher->message_decoder_dispatcher().DecodeAndDisplayMessage(
decoder->process_id(), handle_value, bytes_value, num_bytes_value, handle_infos_value,
num_handles_value, type(), os, line_header, tabs)) {
DumpMessage(/*error=*/true, bytes_value, num_bytes_value, handle_infos_value, num_handles_value,
dispatcher, line_header, tabs, os);
} else if (dispatcher->dump_messages()) {
DumpMessage(/*error=*/false, bytes_value, num_bytes_value, handle_infos_value,
num_handles_value, dispatcher, line_header, tabs, os);
}
delete[] handle_infos_value;
}
std::unique_ptr<fidl_codec::Type> SyscallFidlMessageHandleInfo::ComputeType() const {
return std::make_unique<fidl_codec::FidlMessageType>();
}
std::unique_ptr<fidl_codec::Value> SyscallFidlMessageHandleInfo::GenerateValue(
SyscallDecoder* decoder, Stage stage) const {
zx_handle_t handle_value = handle()->Value(decoder, stage);
const uint8_t* bytes_value = bytes()->Content(decoder, stage);
uint32_t num_bytes_value = num_bytes()->Value(decoder, stage);
const zx_handle_info_t* handle_infos_value = handles()->Content(decoder, stage);
uint32_t num_handles_value = num_handles()->Value(decoder, stage);
fidl_codec::DecodedMessage message;
std::stringstream error_stream;
message.DecodeMessage(decoder->dispatcher()->MessageDecoderDispatcher(), decoder->process_id(),
handle_value, bytes_value, num_bytes_value, handle_infos_value,
num_handles_value, type(), error_stream);
auto result = std::make_unique<fidl_codec::FidlMessageValue>(
&message, error_stream.str(), bytes_value, num_bytes_value, handle_infos_value,
num_handles_value);
if (result->is_request()) {
if (result->matched_request()) {
decoder->set_semantic(result->method()->semantic());
decoder->set_decoded_request(result->decoded_request());
}
if (result->matched_response()) {
decoder->set_semantic(result->method()->semantic());
decoder->set_decoded_response(result->decoded_response());
}
}
return result;
}
void SyscallFidlMessageHandleInfo::DisplayOutline(SyscallDisplayDispatcher* dispatcher,
SyscallDecoder* decoder, Stage stage,
std::string_view line_header, int tabs,
std::ostream& os) const {
zx_handle_t handle_value = handle()->Value(decoder, stage);
const uint8_t* bytes_value = bytes()->Content(decoder, stage);
uint32_t num_bytes_value = num_bytes()->Value(decoder, stage);
const zx_handle_info_t* handle_infos_value = handles()->Content(decoder, stage);
uint32_t num_handles_value = num_handles()->Value(decoder, stage);
if (!dispatcher->message_decoder_dispatcher().DecodeAndDisplayMessage(
decoder->process_id(), handle_value, bytes_value, num_bytes_value, handle_infos_value,
num_handles_value, type(), os, line_header, tabs)) {
DumpMessage(/*error=*/true, bytes_value, num_bytes_value, handle_infos_value, num_handles_value,
dispatcher, line_header, tabs, os);
} else if (dispatcher->dump_messages()) {
DumpMessage(/*error=*/false, bytes_value, num_bytes_value, handle_infos_value,
num_handles_value, dispatcher, line_header, tabs, os);
}
}
bool ComputeTypes(const std::vector<std::unique_ptr<SyscallInputOutputBase>>& fields,
std::vector<std::unique_ptr<fidl_codec::StructMember>>* inline_members,
std::vector<std::unique_ptr<fidl_codec::StructMember>>* outline_members) {
for (const auto& field : fields) {
std::unique_ptr<fidl_codec::Type> type = field->ComputeType();
if (type == nullptr) {
return false;
}
if (field->InlineValue()) {
inline_members->emplace_back(
std::make_unique<fidl_codec::StructMember>(field->name(), std::move(type)));
} else {
outline_members->emplace_back(
std::make_unique<fidl_codec::StructMember>(field->name(), std::move(type)));
}
}
return true;
}
void Syscall::ComputeTypes() {
fidl_codec_values_ready_ = true;
if (!fidlcat::ComputeTypes(inputs_, &input_inline_members_, &input_outline_members_)) {
fidl_codec_values_ready_ = false;
return;
}
if (!fidlcat::ComputeTypes(outputs_, &output_inline_members_, &output_outline_members_)) {
fidl_codec_values_ready_ = false;
return;
}
}
void SyscallDecoderDispatcher::DecodeSyscall(InterceptingThreadObserver* thread_observer,
zxdb::Thread* thread, Syscall* syscall) {
uint64_t thread_id = thread->GetKoid();
auto current = syscall_decoders_.find(thread_id);
if (current != syscall_decoders_.end()) {
FXL_LOG(ERROR) << thread->GetProcess()->GetName() << ' ' << thread->GetProcess()->GetKoid()
<< ':' << thread_id << ": Internal error: already decoding the thread";
return;
}
auto decoder = CreateDecoder(thread_observer, thread, syscall);
auto tmp = decoder.get();
syscall_decoders_[thread_id] = std::move(decoder);
tmp->Decode();
}
void SyscallDecoderDispatcher::DecodeException(InterceptionWorkflow* workflow,
zxdb::Thread* thread) {
uint64_t thread_id = thread->GetKoid();
auto current = exception_decoders_.find(thread_id);
if (current != exception_decoders_.end()) {
FXL_LOG(ERROR) << thread->GetProcess()->GetName() << ' ' << thread->GetProcess()->GetKoid()
<< ':' << thread_id
<< ": Internal error: already decoding an exception for the thread";
return;
}
auto decoder = CreateDecoder(workflow, thread);
auto tmp = decoder.get();
exception_decoders_[thread_id] = std::move(decoder);
tmp->Decode();
}
void SyscallDecoderDispatcher::DeleteDecoder(SyscallDecoder* decoder) {
if (!decoder->aborted()) {
zxdb::Thread* thread = decoder->get_thread();
if (thread != nullptr) {
thread->Continue();
}
}
syscall_decoders_.erase(decoder->thread_id());
}
void SyscallDecoderDispatcher::DeleteDecoder(ExceptionDecoder* decoder) {
zxdb::Thread* thread = decoder->get_thread();
if (thread != nullptr) {
thread->Continue();
}
exception_decoders_.erase(decoder->thread_id());
}
void SyscallDecoderDispatcher::ProcessMonitored(std::string_view name, zx_koid_t koid,
std::string_view error_message) {
if (!error_message.empty()) {
return;
}
auto process = processes_.find(koid);
if (process == processes_.end()) {
processes_.emplace(std::make_pair(koid, std::make_unique<Process>(name, koid)));
}
}
void SyscallDecoderDispatcher::StopMonitoring(zx_koid_t koid) {
for (const auto& decoder : syscall_decoders_) {
if (decoder.second->process_id() == koid) {
decoder.second->set_aborted();
}
}
}
void SyscallDecoderDispatcher::ComputeTypes() {
for (const auto& syscall : syscalls_) {
syscall->ComputeTypes();
}
}
std::unique_ptr<SyscallDecoder> SyscallDisplayDispatcher::CreateDecoder(
InterceptingThreadObserver* thread_observer, zxdb::Thread* thread, const Syscall* syscall) {
return std::make_unique<SyscallDecoder>(this, thread_observer, thread, syscall,
std::make_unique<SyscallDisplay>(this, os_));
}
std::unique_ptr<ExceptionDecoder> SyscallDisplayDispatcher::CreateDecoder(
InterceptionWorkflow* workflow, zxdb::Thread* thread) {
return std::make_unique<ExceptionDecoder>(workflow, this, thread,
std::make_unique<ExceptionDisplay>(this, os_));
}
void SyscallDisplayDispatcher::ProcessLaunched(const std::string& command,
std::string_view error_message) {
last_displayed_syscall_ = nullptr;
if (error_message.empty()) {
os_ << colors().green << "\nLaunched " << colors().blue << command << colors().reset << '\n';
} else {
os_ << colors().red << "\nCan't launch " << colors().blue << command << colors().reset << " : "
<< colors().red << error_message << colors().reset << '\n';
}
}
void SyscallDisplayDispatcher::ProcessMonitored(std::string_view name, zx_koid_t koid,
std::string_view error_message) {
last_displayed_syscall_ = nullptr;
if (error_message.empty()) {
auto process = processes().find(koid);
if (process == processes().end()) {
os_ << colors().green << "\nMonitoring ";
} else {
os_ << colors().red << "\nAlready monitoring ";
}
} else {
os_ << colors().red << "\nCan't monitor ";
}
if (name.empty()) {
os_ << colors().reset << "process with koid ";
} else {
os_ << colors().blue << name << colors().reset << " koid=";
}
os_ << colors().red << koid << colors().reset;
if (!error_message.empty()) {
os_ << " : " << colors().red << error_message << colors().reset;
}
os_ << '\n';
SyscallDecoderDispatcher::ProcessMonitored(name, koid, error_message);
}
void SyscallDisplayDispatcher::StopMonitoring(zx_koid_t koid) {
last_displayed_syscall_ = nullptr;
os_ << colors().green << "\nStop monitoring process with koid ";
os_ << colors().red << koid << colors().reset << '\n';
SyscallDecoderDispatcher::StopMonitoring(koid);
}
std::unique_ptr<SyscallDecoder> SyscallCompareDispatcher::CreateDecoder(
InterceptingThreadObserver* thread_observer, zxdb::Thread* thread, const Syscall* syscall) {
return std::make_unique<SyscallDecoder>(this, thread_observer, thread, syscall,
std::make_unique<SyscallCompare>(this, comparator_, os_));
}
} // namespace fidlcat