blob: 748d2e9128acc957a7ead5d94fc514217bcccd77 [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 <sys/time.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <cstdint>
#include <fstream>
#include <memory>
#include <sstream>
#include "src/developer/debug/zxdb/client/process.h"
#include "src/developer/debug/zxdb/client/thread.h"
#include "src/lib/fidl_codec/semantic.h"
#include "tools/fidlcat/lib/code_generator/test_generator.h"
#include "tools/fidlcat/lib/inference.h"
#include "tools/fidlcat/lib/interception_workflow.h"
#include "tools/fidlcat/lib/syscall_decoder.h"
#include "tools/fidlcat/lib/top.h"
#include "tools/fidlcat/proto/session.pb.h"
namespace fidlcat {
std::unique_ptr<fidl_codec::Struct> uint128_struct_definition = nullptr;
const fidl_codec::Struct& GetUint128StructDefinition() {
if (uint128_struct_definition == nullptr) {
uint128_struct_definition = std::make_unique<fidl_codec::Struct>("zx.uint128");
uint128_struct_definition->AddMember("low",
SyscallTypeToFidlCodecType(SyscallType::kUint64Hexa));
uint128_struct_definition->AddMember("high",
SyscallTypeToFidlCodecType(SyscallType::kUint64Hexa));
}
return *uint128_struct_definition;
}
std::unique_ptr<fidl_codec::Type> SyscallTypeToFidlCodecType(fidlcat::SyscallType syscall_type) {
switch (syscall_type) {
case SyscallType::kBool:
return std::make_unique<fidl_codec::BoolType>();
case SyscallType::kBtiPerm:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kBtiPerm);
case SyscallType::kCachePolicy:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kCachePolicy);
case SyscallType::kChannelOption:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kChannelOption);
case SyscallType::kChar:
return std::make_unique<fidl_codec::Int8Type>(fidl_codec::Int8Type::Kind::kChar);
case SyscallType::kClock:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kClock);
case SyscallType::kDuration:
return std::make_unique<fidl_codec::Int64Type>(fidl_codec::Int64Type::Kind::kDuration);
case SyscallType::kExceptionChannelType:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kExceptionChannelType);
case SyscallType::kExceptionState:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kExceptionState);
case SyscallType::kFeatureKind:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kFeatureKind);
case SyscallType::kFutex:
return std::make_unique<fidl_codec::Int32Type>(fidl_codec::Int32Type::Kind::kFutex);
case SyscallType::kGpAddr:
return std::make_unique<fidl_codec::Uint64Type>(fidl_codec::Uint64Type::Kind::kGpAddr);
case SyscallType::kGuestTrap:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kGuestTrap);
case SyscallType::kHandle:
return std::make_unique<fidl_codec::HandleType>();
case SyscallType::kInfoMapsType:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kInfoMapsType);
case SyscallType::kInt32:
return std::make_unique<fidl_codec::Int32Type>();
case SyscallType::kInt64:
return std::make_unique<fidl_codec::Int64Type>();
case SyscallType::kInterruptFlags:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kInterruptFlags);
case SyscallType::kIommuType:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kIommuType);
case SyscallType::kKoid:
return std::make_unique<fidl_codec::Uint64Type>(fidl_codec::Uint64Type::Kind::kKoid);
case SyscallType::kKtraceControlAction:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kKtraceControlAction);
case SyscallType::kMonotonicTime:
return std::make_unique<fidl_codec::Int64Type>(fidl_codec::Int64Type::Kind::kMonotonicTime);
case SyscallType::kObjectInfoTopic:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kObjectInfoTopic);
case SyscallType::kObjType:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kObjType);
case SyscallType::kPacketGuestVcpuType:
return std::make_unique<fidl_codec::Uint8Type>(
fidl_codec::Uint8Type::Kind::kPacketGuestVcpuType);
case SyscallType::kPacketPageRequestCommand:
return std::make_unique<fidl_codec::Uint16Type>(
fidl_codec::Uint16Type::Kind::kPacketPageRequestCommand);
case SyscallType::kPaddr:
return std::make_unique<fidl_codec::Uint64Type>(fidl_codec::Uint64Type::Kind::kPaddr);
case SyscallType::kPciBarType:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kPciBarType);
case SyscallType::kPolicyAction:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kPolicyAction);
case SyscallType::kPolicyCondition:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kPolicyCondition);
case SyscallType::kPolicyTopic:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kPolicyTopic);
case SyscallType::kPortPacketType:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kPortPacketType);
case SyscallType::kProfileInfoFlags:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kProfileInfoFlags);
case SyscallType::kPropType:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kPropType);
case SyscallType::kRights:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kRights);
case SyscallType::kRsrcKind:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kRsrcKind);
case SyscallType::kSignals:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kSignals);
case SyscallType::kSize:
return std::make_unique<fidl_codec::Uint64Type>(fidl_codec::Uint64Type::Kind::kSize);
case SyscallType::kSocketCreateOptions:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kSocketCreateOptions);
case SyscallType::kSocketReadOptions:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kSocketReadOptions);
case SyscallType::kSocketDisposition:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kSocketDisposition);
case SyscallType::kStatus:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kStatus);
case SyscallType::kSystemEventType:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kSystemEventType);
case SyscallType::kSystemPowerctl:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kSystemPowerctl);
case SyscallType::kThreadState:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kThreadState);
case SyscallType::kThreadStateTopic:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kThreadStateTopic);
case SyscallType::kTime:
return std::make_unique<fidl_codec::Int64Type>(fidl_codec::Int64Type::Kind::kTime);
case SyscallType::kTimerOption:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kTimerOption);
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::kUint128Hexa:
return std::make_unique<fidl_codec::StructType>(GetUint128StructDefinition(), false);
case SyscallType::kUintptr:
return std::make_unique<fidl_codec::Uint64Type>(fidl_codec::Uint64Type::Kind::kUintptr);
case SyscallType::kVaddr:
return std::make_unique<fidl_codec::Uint64Type>(fidl_codec::Uint64Type::Kind::kVaddr);
case SyscallType::kVcpu:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kVcpu);
case SyscallType::kVmOption:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kVmOption);
case SyscallType::kVmoCreationOption:
return std::make_unique<fidl_codec::Uint32Type>(
fidl_codec::Uint32Type::Kind::kVmoCreationOption);
case SyscallType::kVmoOp:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kVmoOp);
case SyscallType::kVmoOption:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kVmoOption);
case SyscallType::kVmoType:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kVmoType);
default:
return nullptr;
}
}
std::unique_ptr<fidl_codec::Type> AccessBase::ComputeType() const {
return SyscallTypeToFidlCodecType(GetSyscallType());
}
std::unique_ptr<fidl_codec::Type> SyscallInputOutputBase::ComputeType() const { return nullptr; }
std::unique_ptr<fidl_codec::Value> SyscallInputOutputBase::GenerateValue(
SyscallDecoderInterface* decoder, Stage stage) const {
return std::make_unique<fidl_codec::InvalidValue>();
}
bool SyscallInputOutputBase::GetAutomationInstructions(
const std::vector<debug::RegisterID>& argument_indexes, bool is_invoked,
const std::vector<debug_ipc::AutomationCondition>& conditions, Syscall& syscall) {
return false;
}
void SyscallFidlMessageBase::LoadBytes(SyscallDecoderInterface* decoder, Stage stage) const {
handle_->Load(decoder, stage);
options_->Load(decoder, stage);
num_bytes_->Load(decoder, stage);
if (num_bytes_->Loaded(decoder, stage)) {
uint32_t count = num_bytes_->Value(decoder, stage);
bool use_iovec = false;
if ((type_ == fidl_codec::SyscallFidlType::kOutputMessage) ||
(type_ == fidl_codec::SyscallFidlType::kOutputRequest)) {
uint32_t options = options_->Value(decoder, stage);
if ((options & ZX_CHANNEL_WRITE_USE_IOVEC) != 0) {
use_iovec = true;
}
}
if (count > 0) {
if (use_iovec) {
bytes_->LoadArray(decoder, stage, count * sizeof(zx_channel_iovec_t));
if (bytes_->ArrayLoaded(decoder, stage, count * sizeof(zx_channel_iovec_t))) {
auto iovec = reinterpret_cast<const zx_channel_iovec_t*>(bytes_->Content(decoder, stage));
for (uint32_t buffer = 0; buffer < count; ++buffer) {
decoder->LoadBuffer(stage, reinterpret_cast<uint64_t>(iovec[buffer].buffer),
iovec[buffer].capacity);
}
}
} else {
bytes_->LoadArray(decoder, stage, count);
}
}
}
}
SyscallFidlMessageBase::ByteBuffer::ByteBuffer(SyscallDecoderInterface* decoder, Stage stage,
const SyscallFidlMessageBase* from) {
uint32_t options_value = from->options()->Value(decoder, stage);
if (((from->type() == fidl_codec::SyscallFidlType::kOutputMessage) ||
(from->type() == fidl_codec::SyscallFidlType::kOutputRequest)) &&
((options_value & ZX_CHANNEL_WRITE_USE_IOVEC) != 0)) {
// For the iovec case, we need to concatanate all the buffers into one.
const zx_channel_iovec_t* iovec =
reinterpret_cast<const zx_channel_iovec_t*>(from->bytes()->Content(decoder, stage));
uint32_t iovec_count = from->num_bytes()->Value(decoder, stage);
for (uint32_t i = 0; i < iovec_count; ++i) {
count_ += static_cast<uint32_t>(iovec[i].capacity);
}
buffer_ = new uint8_t[count_];
uint8_t* dst = buffer_;
for (uint32_t i = 0; i < iovec_count; ++i) {
const uint8_t* data =
decoder->BufferContent(stage, reinterpret_cast<uint64_t>(iovec[i].buffer));
memcpy(dst, data, iovec[i].capacity);
dst += iovec[i].capacity;
}
bytes_ = buffer_;
} else {
bytes_ = from->bytes()->Content(decoder, stage);
count_ = from->num_bytes()->Value(decoder, stage);
}
}
std::unique_ptr<fidl_codec::Type> SyscallFidlMessageHandle::ComputeType() const {
return std::make_unique<fidl_codec::FidlMessageType>();
}
std::unique_ptr<fidl_codec::Value> SyscallFidlMessageHandle::GenerateValue(
SyscallDecoderInterface* decoder, Stage stage) const {
zx_handle_t handle_value = handle()->Value(decoder, stage);
ByteBuffer buffer(decoder, stage, this);
const zx_handle_t* handles_value = handles()->Content(decoder, stage);
uint32_t num_handles_value = num_handles()->Value(decoder, stage);
zx_handle_disposition_t* handle_dispositions_value = nullptr;
if (num_handles_value > 0) {
handle_dispositions_value = new zx_handle_disposition_t[num_handles_value];
for (uint32_t i = 0; i < num_handles_value; ++i) {
handle_dispositions_value[i].operation = fidl_codec::kNoHandleDisposition;
handle_dispositions_value[i].handle = handles_value[i];
handle_dispositions_value[i].rights = 0;
handle_dispositions_value[i].type = ZX_OBJ_TYPE_NONE;
handle_dispositions_value[i].result = ZX_OK;
}
}
fidl_codec::DecodedMessage message;
std::stringstream error_stream;
message.DecodeMessage(decoder->dispatcher()->MessageDecoderDispatcher(),
decoder->fidlcat_thread()->process()->koid(), handle_value, buffer.bytes(),
buffer.count(), handle_dispositions_value, num_handles_value, type(),
error_stream);
auto result = std::make_unique<fidl_codec::FidlMessageValue>(
&message, error_stream.str(), buffer.bytes(), buffer.count(), handle_dispositions_value,
num_handles_value);
delete[] handle_dispositions_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;
}
std::unique_ptr<fidl_codec::Type> SyscallFidlMessageHandleInfo::ComputeType() const {
return std::make_unique<fidl_codec::FidlMessageType>();
}
std::unique_ptr<fidl_codec::Value> SyscallFidlMessageHandleInfo::GenerateValue(
SyscallDecoderInterface* decoder, Stage stage) const {
zx_handle_t handle_value = handle()->Value(decoder, stage);
ByteBuffer buffer(decoder, stage, this);
const zx_handle_info_t* handle_infos_value = handles()->Content(decoder, stage);
uint32_t num_handles_value = num_handles()->Value(decoder, stage);
zx_handle_disposition_t* handle_dispositions_value = nullptr;
if (num_handles_value > 0) {
handle_dispositions_value = new zx_handle_disposition_t[num_handles_value];
for (uint32_t i = 0; i < num_handles_value; ++i) {
handle_dispositions_value[i].operation = fidl_codec::kNoHandleDisposition;
handle_dispositions_value[i].handle = handle_infos_value[i].handle;
handle_dispositions_value[i].type = handle_infos_value[i].type;
handle_dispositions_value[i].rights = handle_infos_value[i].rights;
handle_dispositions_value[i].result = ZX_OK;
}
}
fidl_codec::DecodedMessage message;
std::stringstream error_stream;
message.DecodeMessage(decoder->dispatcher()->MessageDecoderDispatcher(),
decoder->fidlcat_thread()->process()->koid(), handle_value, buffer.bytes(),
buffer.count(), handle_dispositions_value, num_handles_value, type(),
error_stream);
auto result = std::make_unique<fidl_codec::FidlMessageValue>(
&message, error_stream.str(), buffer.bytes(), buffer.count(), handle_dispositions_value,
num_handles_value);
delete[] handle_dispositions_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;
}
std::unique_ptr<fidl_codec::Type> SyscallFidlMessageHandleDisposition::ComputeType() const {
return std::make_unique<fidl_codec::FidlMessageType>();
}
std::unique_ptr<fidl_codec::Value> SyscallFidlMessageHandleDisposition::GenerateValue(
SyscallDecoderInterface* decoder, Stage stage) const {
zx_handle_t handle_value = handle()->Value(decoder, stage);
ByteBuffer buffer(decoder, stage, this);
const zx_handle_disposition_t* handle_dispositions_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->fidlcat_thread()->process()->koid(), handle_value, buffer.bytes(),
buffer.count(), handle_dispositions_value, num_handles_value, type(),
error_stream);
auto result = std::make_unique<fidl_codec::FidlMessageValue>(
&message, error_stream.str(), buffer.bytes(), buffer.count(), handle_dispositions_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 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 (field->InlineValue()) {
inline_members->emplace_back(
std::make_unique<fidl_codec::StructMember>(field->name(), std::move(type), field->id()));
} else {
outline_members->emplace_back(
std::make_unique<fidl_codec::StructMember>(field->name(), std::move(type), field->id()));
}
}
}
void Syscall::ComputeTypes() {
fidlcat::ComputeTypes(inputs_, &input_inline_members_, &input_outline_members_);
fidlcat::ComputeTypes(outputs_, &output_inline_members_, &output_outline_members_);
}
const fidl_codec::StructMember* Syscall::SearchInlineMember(const std::string& name,
bool invoked) const {
if (invoked) {
for (const auto& member : input_inline_members_) {
if (member->name() == name) {
return member.get();
}
}
} else {
for (const auto& member : output_inline_members_) {
if (member->name() == name) {
return member.get();
}
}
}
return nullptr;
}
const fidl_codec::StructMember* Syscall::SearchInlineMember(uint32_t id, bool invoked) const {
if (invoked) {
for (const auto& member : input_inline_members_) {
if (member->id() == id) {
return member.get();
}
}
} else {
for (const auto& member : output_inline_members_) {
if (member->id() == id) {
return member.get();
}
}
}
return nullptr;
}
const fidl_codec::StructMember* Syscall::SearchOutlineMember(const std::string& name,
bool invoked) const {
if (invoked) {
for (const auto& member : input_outline_members_) {
if (member->name() == name) {
return member.get();
}
}
} else {
for (const auto& member : output_outline_members_) {
if (member->name() == name) {
return member.get();
}
}
}
return nullptr;
}
const fidl_codec::StructMember* Syscall::SearchOutlineMember(uint32_t id, bool invoked) const {
if (invoked) {
for (const auto& member : input_outline_members_) {
if (member->id() == id) {
return member.get();
}
}
} else {
for (const auto& member : output_outline_members_) {
if (member->id() == id) {
return member.get();
}
}
}
return nullptr;
}
void Syscall::ComputeStatistics(const OutputEvent* event) const {
if (compute_statistics_ != nullptr) {
(*compute_statistics_)(event);
}
}
bool ComputeAutomation(const std::vector<debug::RegisterID>& argument_indexes, debug::Arch arch,
const std::vector<std::unique_ptr<SyscallInputOutputBase>>& fields,
bool is_invoked, Syscall& syscall) {
bool fully_automated = true;
for (const auto& field : fields) {
std::vector<debug_ipc::AutomationCondition> automation_conditions;
for (const auto& condition : *(field->conditions())) {
if (!condition->ComputeAutomationCondition(argument_indexes, is_invoked, arch, syscall,
automation_conditions)) {
continue;
}
}
if (!field->GetAutomationInstructions(argument_indexes, is_invoked, automation_conditions,
syscall)) {
fully_automated = false;
}
}
return fully_automated;
}
void Syscall::ComputeAutomation(debug::Arch arch) {
if (invoked_bp_instructions_.size() + exit_bp_instructions_.size() > 0) {
return;
}
static const std::vector<debug::RegisterID> amd64_argument_indexes = {
debug::RegisterID::kX64_rdi, debug::RegisterID::kX64_rsi, debug::RegisterID::kX64_rdx,
debug::RegisterID::kX64_rcx, debug::RegisterID::kX64_r8, debug::RegisterID::kX64_r9};
static const std::vector<debug::RegisterID> arm64_argument_indexes = {
debug::RegisterID::kARMv8_x0, debug::RegisterID::kARMv8_x1, debug::RegisterID::kARMv8_x2,
debug::RegisterID::kARMv8_x3, debug::RegisterID::kARMv8_x4, debug::RegisterID::kARMv8_x5,
debug::RegisterID::kARMv8_x6, debug::RegisterID::kARMv8_x7};
const std::vector<debug::RegisterID>* arg_index;
if (arch == debug::Arch::kX64) {
arg_index = &amd64_argument_indexes;
} else if (arch == debug::Arch::kArm64) {
arg_index = &arm64_argument_indexes;
} else {
FX_LOGS(ERROR) << "Unknown architecture";
return;
}
bool initial_automated = fidlcat::ComputeAutomation(*arg_index, arch, inputs_, true, *this);
bool exit_automated = fidlcat::ComputeAutomation(*arg_index, arch, outputs_, false, *this);
fully_automated_ = initial_automated && exit_automated;
debug_ipc::AutomationInstruction clear_instr;
if (invoked_bp_instructions_.size() + exit_bp_instructions_.size() > 0) {
clear_instr.InitClearStoredValues(std::vector<debug_ipc::AutomationCondition>());
exit_bp_instructions_.emplace_back(clear_instr);
}
}
SyscallDecoderDispatcher::SyscallDecoderDispatcher(const DecodeOptions& decode_options)
: decode_options_(decode_options), inference_(this) {
Populate();
ComputeTypes();
if (!decode_options.trigger_filters.empty()) {
// We have at least one trigger => wait for a message satisfying the trigger before displaying
// any syscall.
display_started_ = false;
}
if (!decode_options.message_filters.empty() || !decode_options.exclude_message_filters.empty()) {
has_filter_ = true;
}
if ((decode_options.stack_level != kNoStack) || !decode_options_.save.empty()) {
needs_stack_frame_ = true;
}
if (!decode_options_.save.empty()) {
needs_to_save_events_ = true;
} else {
switch (decode_options_.output_mode) {
case OutputMode::kNone:
case OutputMode::kStandard:
break;
case OutputMode::kTextProtobuf:
needs_to_save_events_ = true;
break;
}
}
}
HandleInfo* SyscallDecoderDispatcher::CreateHandleInfo(Thread* thread, uint32_t handle,
int64_t creation_time, bool startup) {
auto old_value = thread->process()->SearchHandleInfo(handle);
if (old_value != nullptr) {
return old_value;
}
auto result = std::make_unique<HandleInfo>(thread, handle, creation_time, startup);
auto returned_value = result.get();
thread->process()->handle_infos().emplace_back(result.get());
thread->process()->handle_info_map().emplace(std::make_pair(handle, result.get()));
handle_infos_.emplace_back(std::move(result));
return returned_value;
}
void SyscallDecoderDispatcher::DecodeSyscall(InterceptingThreadObserver* thread_observer,
zxdb::Thread* thread, Syscall* syscall,
uint64_t timestamp) {
uint64_t thread_id = thread->GetKoid();
auto current = syscall_decoders_.find(thread_id);
if (current != syscall_decoders_.end()) {
FX_LOGS(ERROR) << thread->GetProcess()->GetName() << ' ' << thread->GetProcess()->GetKoid()
<< ':' << thread_id << ": Internal error: already decoding the thread";
return;
}
auto decoder =
std::make_unique<SyscallDecoder>(this, thread_observer, thread, syscall, timestamp);
auto tmp = decoder.get();
syscall_decoders_[thread_id] = std::move(decoder);
tmp->Decode();
}
void SyscallDecoderDispatcher::DecodeException(InterceptionWorkflow* workflow, zxdb::Thread* thread,
uint64_t timestamp) {
uint64_t thread_id = thread->GetKoid();
auto current = exception_decoders_.find(thread_id);
if (current != exception_decoders_.end()) {
FX_LOGS(ERROR) << thread->GetProcess()->GetName() << ' ' << thread->GetProcess()->GetKoid()
<< ':' << thread_id
<< ": Internal error: already decoding an exception for the thread";
return;
}
auto decoder = std::make_unique<ExceptionDecoder>(workflow, this, thread, timestamp);
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(false);
}
}
syscall_decoders_.erase(decoder->fidlcat_thread()->koid());
}
void SyscallDecoderDispatcher::DeleteDecoder(ExceptionDecoder* decoder) {
zxdb::Thread* thread = decoder->get_thread();
if (thread != nullptr) {
thread->Continue(false);
}
exception_decoders_.erase(decoder->thread_id());
}
void SyscallDecoderDispatcher::AddStopMonitoringEvent(std::shared_ptr<StopMonitoringEvent> event) {
for (const auto& decoder : syscall_decoders_) {
if (decoder.second->fidlcat_thread()->process() == event->process()) {
decoder.second->set_aborted();
}
}
}
void SyscallDecoderDispatcher::SaveEvent(std::shared_ptr<Event> event) {
if (needs_to_save_events()) {
decoded_events_.emplace_back(std::move(event));
}
}
void SyscallDecoderDispatcher::SessionEnded() {
bool generate_proto_session = false;
if (!decode_options_.save.empty()) {
generate_proto_session = true;
} else {
switch (decode_options_.output_mode) {
case OutputMode::kNone:
case OutputMode::kStandard:
break;
case OutputMode::kTextProtobuf:
generate_proto_session = true;
break;
}
}
if (generate_proto_session) {
proto::Session session;
GenerateProtoSession(&session);
if (!decode_options_.save.empty()) {
std::fstream output(decode_options_.save, std::ios::out | std::ios::trunc | std::ios::binary);
if (output.fail()) {
FX_LOGS(ERROR) << "Can't open <" << decode_options_.save << "> for writing.";
} else if (!session.SerializeToOstream(&output)) {
FX_LOGS(ERROR) << "Failed to write session to protobuf file <" << decode_options_.save
<< ">.";
}
}
switch (decode_options_.output_mode) {
case OutputMode::kNone:
case OutputMode::kStandard:
break;
case OutputMode::kTextProtobuf:
std::cout << session.DebugString();
break;
}
}
}
void SyscallDecoderDispatcher::GenerateProtoSession(proto::Session* session) {
for (const auto& process : processes_) {
proto::Process* proto_process = session->add_process();
proto_process->set_koid(process.second->koid());
proto_process->set_name(process.second->name());
auto process_semantic = inference().GetProcessSemantic(process.second->koid());
if (process_semantic != nullptr) {
for (const auto& linked_handles : process_semantic->linked_handles) {
if (linked_handles.first < linked_handles.second) {
proto::LinkedHandles* proto_linked_handles = proto_process->add_linked_handles();
proto_linked_handles->set_handle_0(linked_handles.first);
proto_linked_handles->set_handle_1(linked_handles.second);
}
}
}
}
for (const auto& thread : threads_) {
proto::Thread* proto_thread = session->add_thread();
proto_thread->set_koid(thread.second->koid());
proto_thread->set_process_koid(thread.second->process()->koid());
}
for (const auto& handle_info : handle_infos_) {
fidl_codec::semantic::InferredHandleInfo* inferred_handle_info =
inference().GetInferredHandleInfo(handle_info->thread()->process()->koid(),
handle_info->handle());
proto::HandleDescription* proto_handle_description = session->add_handle_description();
proto_handle_description->set_handle(handle_info->handle());
proto_handle_description->set_thread_koid(handle_info->thread()->koid());
proto_handle_description->set_creation_time(handle_info->creation_time());
proto_handle_description->set_startup(handle_info->startup());
if (inferred_handle_info != nullptr) {
proto_handle_description->set_type(inferred_handle_info->type());
proto_handle_description->set_fd(inferred_handle_info->fd());
proto_handle_description->set_path(inferred_handle_info->path());
proto_handle_description->set_attributes(inferred_handle_info->attributes());
}
proto_handle_description->set_koid(handle_info->koid());
proto_handle_description->set_object_type(handle_info->object_type());
}
for (const auto& linked_koids : inference().linked_koids()) {
if (linked_koids.first < linked_koids.second) {
proto::LinkedKoids* proto_linked_koids = session->add_linked_koids();
proto_linked_koids->set_koid_0(linked_koids.first);
proto_linked_koids->set_koid_1(linked_koids.second);
}
}
for (const auto& event : decoded_events_) {
event->Write(session->add_event());
}
}
void SyscallDecoderDispatcher::ComputeTypes() {
for (const auto& syscall : syscalls_) {
syscall.second->ComputeTypes();
}
}
double SyscallDisplayDispatcher::GetTime(int64_t timestamp) {
return static_cast<double>(timestamp) / 1000000000;
}
void SyscallDisplayDispatcher::AddProcessLaunchedEvent(
std::shared_ptr<ProcessLaunchedEvent> event) {
if (decode_options().output_mode == OutputMode::kStandard) {
if (!decode_options().thread_filters.empty()) {
return;
}
os_ << '\n' << colors().green << GetTime(event->timestamp()) << colors().reset << ' ';
if (event->error_message().empty()) {
os_ << colors().green << "Launched " << colors().blue << event->command() << colors().reset
<< '\n';
} else {
os_ << colors().red << "Can't launch " << colors().blue << event->command() << colors().reset
<< " : " << colors().red << event->error_message() << colors().reset << '\n';
}
}
SaveEvent(std::move(event));
}
void SyscallDisplayDispatcher::AddProcessMonitoredEvent(
std::shared_ptr<ProcessMonitoredEvent> event) {
if (!decode_options().thread_filters.empty()) {
return;
}
if (decode_options().output_mode == OutputMode::kStandard) {
os_ << '\n' << colors().green << GetTime(event->timestamp()) << colors().reset << ' ';
if (event->error_message().empty()) {
os_ << colors().green << "Monitoring ";
} else {
os_ << colors().red << "Can't monitor ";
}
if (event->process()->name().empty()) {
os_ << colors().reset << "process with koid ";
} else {
os_ << colors().blue << event->process()->name() << colors().reset << " koid=";
}
os_ << colors().red << event->process()->koid() << colors().reset;
if (!event->error_message().empty()) {
os_ << " : " << colors().red << event->error_message() << colors().reset;
}
os_ << '\n';
}
SaveEvent(std::move(event));
}
void SyscallDisplayDispatcher::AddStopMonitoringEvent(std::shared_ptr<StopMonitoringEvent> event) {
if (!decode_options().thread_filters.empty()) {
return;
}
if (decode_options().output_mode == OutputMode::kStandard) {
os_ << '\n' << colors().green << GetTime(event->timestamp()) << colors().reset << ' ';
if (event->process()->name().empty()) {
os_ << colors().green << "Stop monitoring process with koid" << colors().reset;
} else {
os_ << colors().green << "Stop monitoring" << colors().reset << ' ' << colors().blue
<< event->process()->name() << colors().reset << " koid";
}
os_ << ' ' << colors().red << event->process()->koid() << colors().reset << '\n';
}
SaveEvent(event);
SyscallDecoderDispatcher::AddStopMonitoringEvent(std::move(event));
}
void SyscallDisplayDispatcher::SyscallDecodingError(const fidlcat::Thread* fidlcat_thread,
const Syscall* syscall,
const DecoderError& error) {
std::string message = error.message();
size_t pos = 0;
for (;;) {
size_t end = message.find('\n', pos);
os_ << fidlcat_thread->process()->name() << ' ' << colors().red
<< fidlcat_thread->process()->koid() << colors().reset << ':' << colors().red
<< fidlcat_thread->koid() << colors().reset << ' ' << syscall->name() << ": "
<< colors().red << error.message().substr(pos, end) << colors().reset << '\n';
if (end == std::string::npos) {
break;
}
pos = end + 1;
}
os_ << '\n';
}
void SyscallDisplayDispatcher::AddInvokedEvent(std::shared_ptr<InvokedEvent> invoked_event) {
invoked_event->set_id(GetNextInvokedEventId());
if (!extra_generation().empty()) {
invoked_event->ComputeHandleInfo(this);
}
if (!invoked_event->thread()->displayed()) {
return;
}
if (!display_started()) {
// The user specified a trigger. Check if this is a message which satisfies one of the triggers.
const fidl_codec::FidlMessageValue* message = invoked_event->GetMessage();
if ((message == nullptr) || (message->method() == nullptr) ||
!decode_options().IsTrigger(message->method()->fully_qualified_name())) {
return;
}
// We found a trigger => allow the display.
set_display_started();
}
if (has_filter() && invoked_event->syscall()->has_fidl_message()) {
// We have filters and this is a syscalls with a FIDL message.
// Only display the syscall if the message satifies the conditions.
const fidl_codec::FidlMessageValue* message = invoked_event->GetMessage();
if ((message == nullptr) || (message->method() == nullptr) ||
!decode_options().SatisfiesMessageFilters(message->method()->fully_qualified_name())) {
return;
}
}
invoked_event->set_displayed();
DisplayInvokedEvent(invoked_event.get());
SaveEvent(std::move(invoked_event));
}
void SyscallDisplayDispatcher::AddOutputEvent(std::shared_ptr<OutputEvent> output_event) {
if (!output_event->thread()->displayed()) {
return;
}
if (!extra_generation().empty()) {
if (output_event->invoked_event()->handle_info() != nullptr) {
output_event->invoked_event()->handle_info()->AddEvent(output_event.get());
}
output_event->syscall()->ComputeStatistics(output_event.get());
}
if (!output_event->invoked_event()->displayed()) {
// The display of the syscall wasn't allowed by the input arguments. Check if the output
// arguments allows its display.
if (!display_started()) {
// The user specified a trigger. Check if this is a message which satisfies one of the
// triggers.
const fidl_codec::FidlMessageValue* message = output_event->GetMessage();
if ((message == nullptr) || (message->method() == nullptr) ||
!decode_options().IsTrigger(message->method()->fully_qualified_name())) {
return;
}
set_display_started();
}
if (has_filter() && output_event->syscall()->has_fidl_message()) {
// We have filters and this is a syscalls with a FIDL message.
// Only display the syscall if the message satifies the conditions.
const fidl_codec::FidlMessageValue* message = output_event->GetMessage();
if ((message == nullptr) || (message->method() == nullptr) ||
!decode_options().SatisfiesMessageFilters(message->method()->fully_qualified_name())) {
return;
}
}
// We can display the syscall but the inputs have not been displayed => display the inputs
// before displaying the outputs.
DisplayInvokedEvent(output_event->invoked_event());
}
DisplayOutputEvent(output_event.get());
SaveEvent(std::move(output_event));
}
void SyscallDisplayDispatcher::AddExceptionEvent(std::shared_ptr<ExceptionEvent> exception_event) {
if (!exception_event->thread()->displayed()) {
return;
}
DisplayExceptionEvent(exception_event.get());
SaveEvent(std::move(exception_event));
}
void SyscallDisplayDispatcher::SessionEnded() {
SyscallDecoderDispatcher::SessionEnded();
if (!decoded_events().empty()) {
// Uses the first event for the timestamp reference.
GetTime(decoded_events().front()->timestamp());
}
const char* separator = "";
for (const auto& extra_generation : extra_generation()) {
if (extra_generation.path.empty()) {
os_ << separator;
switch (extra_generation.kind) {
case ExtraGeneration::Kind::kSummary:
DisplaySummary(os_);
break;
case ExtraGeneration::Kind::kTop: {
Top top(this);
top.Display(os_);
break;
}
case ExtraGeneration::Kind::kThreads:
DisplayThreads(os_);
break;
case ExtraGeneration::Kind::kCpp:
GenerateTests("/tmp/fidlcat-generated-tests/" + std::to_string(std::time(0)));
break;
}
separator = "\n";
} else {
if (extra_generation.kind == ExtraGeneration::Kind::kCpp) {
GenerateTests(extra_generation.path);
} else {
std::fstream output(extra_generation.path, std::ios::out | std::ios::trunc);
if (output.fail()) {
FX_LOGS(ERROR) << "Can't open <" << extra_generation.path << "> for writing.";
} else {
switch (extra_generation.kind) {
case ExtraGeneration::Kind::kSummary:
DisplaySummary(output);
break;
case ExtraGeneration::Kind::kTop: {
Top top(this);
top.Display(output);
break;
}
case ExtraGeneration::Kind::kThreads:
DisplayThreads(output);
break;
case ExtraGeneration::Kind::kCpp:
break;
}
}
}
}
}
}
void SyscallDisplayDispatcher::DisplayInvokedEvent(const InvokedEvent* invoked_event) {
if (decode_options().output_mode != OutputMode::kStandard) {
return;
}
std::string line_header =
colors().green + std::to_string(GetTime(invoked_event->timestamp())) + colors().reset + ' ' +
invoked_event->thread()->process()->name() + ' ' + colors().red +
std::to_string(invoked_event->thread()->process()->koid()) + colors().reset + ':' +
colors().red + std::to_string(invoked_event->thread()->koid()) + colors().reset + ' ';
if (with_process_info()) {
os_ << line_header;
}
os_ << '\n';
FidlcatPrinter printer(this, invoked_event->thread()->process(), os_, line_header);
// We have been able to create values from the syscall => print them.
invoked_event->PrettyPrint(printer);
last_displayed_event_ = invoked_event;
}
void SyscallDisplayDispatcher::DisplayOutputEvent(const OutputEvent* output_event) {
if (decode_options().output_mode != OutputMode::kStandard) {
return;
}
if (output_event->syscall()->return_type() != SyscallReturnType::kNoReturn) {
if (last_displayed_event_ != output_event->invoked_event()) {
// Add a blank line to tell the user that this display is not linked to the
// previous displayed lines.
os_ << "\n";
}
std::string line_header;
if (with_process_info() || (last_displayed_event_ != output_event->invoked_event())) {
line_header = colors().green + std::to_string(GetTime(output_event->timestamp())) +
colors().reset + ' ' + output_event->thread()->process()->name() + ' ' +
colors().red + std::to_string(output_event->thread()->process()->koid()) +
colors().reset + ':' + colors().red +
std::to_string(output_event->thread()->koid()) + colors().reset + ' ';
} else {
line_header = colors().green + std::to_string(GetTime(output_event->timestamp())) +
colors().reset + ' ';
}
FidlcatPrinter printer(this, output_event->thread()->process(), os_, line_header);
// We have been able to create values from the syscall => print them.
output_event->PrettyPrint(printer);
last_displayed_event_ = output_event;
}
}
void SyscallDisplayDispatcher::DisplayExceptionEvent(const ExceptionEvent* exception_event) {
if (decode_options().output_mode != OutputMode::kStandard) {
return;
}
os_ << '\n';
std::string line_header =
colors().green + std::to_string(GetTime(exception_event->timestamp())) + colors().reset +
' ' + exception_event->thread()->process()->name() + ' ' + colors().red +
std::to_string(exception_event->thread()->process()->koid()) + colors().reset + ':' +
colors().red + std::to_string(exception_event->thread()->koid()) + colors().reset + ' ';
FidlcatPrinter printer(this, exception_event->thread()->process(), os_, line_header);
exception_event->PrettyPrint(printer);
}
void SyscallCompareDispatcher::SyscallDecodingError(const fidlcat::Thread* fidlcat_thread,
const Syscall* syscall,
const DecoderError& error) {
os_.clear();
os_.str("");
SyscallDisplayDispatcher::SyscallDecodingError(fidlcat_thread, syscall, error);
comparator_->DecodingError(os_.str());
}
void SyscallCompareDispatcher::DisplayInvokedEvent(const InvokedEvent* invoked_event) {
os_.clear();
os_.str("");
SyscallDisplayDispatcher::DisplayInvokedEvent(invoked_event);
comparator_->CompareInput(os_.str(), invoked_event->thread()->process()->name(),
invoked_event->thread()->process()->koid(),
invoked_event->thread()->koid());
}
void SyscallCompareDispatcher::DisplayOutputEvent(const OutputEvent* output_event) {
os_.clear();
os_.str("");
SyscallDisplayDispatcher::DisplayOutputEvent(output_event);
if (output_event->syscall()->return_type() != SyscallReturnType::kNoReturn) {
comparator_->CompareOutput(os_.str(), output_event->thread()->process()->name(),
output_event->thread()->process()->koid(),
output_event->thread()->koid());
}
}
void SyscallDisplayDispatcher::GenerateTests(const std::string& output_directory) {
auto test_generator = TestGenerator(this, output_directory);
test_generator.GenerateTests();
}
} // namespace fidlcat