blob: 7b2e8fbc7ffb3b6e50cb2e896828efae4190c9e8 [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;
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::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::kInt32:
return std::make_unique<fidl_codec::Int32Type>();
case SyscallType::kInt64:
return std::make_unique<fidl_codec::Int64Type>();
case SyscallType::kKoid:
return std::make_unique<fidl_codec::Uint64Type>(fidl_codec::Uint64Type::Kind::kKoid);
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::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::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::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::kStatus:
return std::make_unique<fidl_codec::Uint32Type>(fidl_codec::Uint32Type::Kind::kStatus);
case SyscallType::kTime:
return std::make_unique<fidl_codec::Int64Type>(fidl_codec::Int64Type::Kind::kTime);
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:
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 std::make_unique<fidl_codec::StructType>(*uint128_struct_definition, 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);
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(SyscallDecoder* decoder,
Stage stage) const {
return std::make_unique<fidl_codec::InvalidValue>();
}
void SyscallInputOutputStringBuffer::DisplayOutline(SyscallDecoder* decoder, Stage stage,
fidl_codec::PrettyPrinter& printer) const {
printer << name();
printer << ": " << fidl_codec::Green << "string" << fidl_codec::ResetColor << " = ";
const char* const* buffer = buffer_->Content(decoder, stage);
if (buffer == nullptr) {
printer << fidl_codec::Red << "nullptr" << fidl_codec::ResetColor;
} else {
uint32_t count = count_->Value(decoder, stage);
if (count == 0) {
printer << "empty\n";
return;
}
const char* separator = "";
for (uint32_t i = 0; i < count; ++i) {
if (buffer[i] != nullptr) {
printer << 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_);
printer.DisplayString(std::string_view(string, string_size));
separator = ", ";
}
}
}
printer << '\n';
}
const char* SyscallInputOutputFixedSizeString::DisplayInline(
SyscallDecoder* decoder, Stage stage, const char* separator,
fidl_codec::PrettyPrinter& printer) const {
printer << separator;
printer << name() << ": " << fidl_codec::Green << "string" << fidl_codec::ResetColor << " = ";
const char* string = string_->Content(decoder, stage);
size_t string_size = (string == nullptr) ? 0 : strnlen(string, string_size_);
printer.DisplayString(std::string_view(string, string_size));
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_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, bytes_value,
num_bytes_value, handle_dispositions_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_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(
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);
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, bytes_value,
num_bytes_value, handle_dispositions_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_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(
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_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, bytes_value,
num_bytes_value, handle_dispositions_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_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;
}
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), field->id()));
} else {
outline_members->emplace_back(
std::make_unique<fidl_codec::StructMember>(field->name(), std::move(type), field->id()));
}
}
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;
}
}
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);
}
}
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 = CreateDecoder(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 = CreateDecoder(workflow, 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();
}
}
std::unique_ptr<SyscallDecoder> SyscallDisplayDispatcher::CreateDecoder(
InterceptingThreadObserver* thread_observer, zxdb::Thread* thread, const Syscall* syscall,
uint64_t timestamp) {
return std::make_unique<SyscallDecoder>(this, thread_observer, thread, syscall,
std::make_unique<SyscallDisplay>(this, os_), timestamp);
}
std::unique_ptr<ExceptionDecoder> SyscallDisplayDispatcher::CreateDecoder(
InterceptionWorkflow* workflow, zxdb::Thread* thread, uint64_t timestamp) {
return std::make_unique<ExceptionDecoder>(
workflow, this, thread, std::make_unique<ExceptionDisplay>(this, os_), timestamp);
}
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;
}
last_displayed_syscall_ = nullptr;
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) {
last_displayed_syscall_ = nullptr;
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) {
last_displayed_syscall_ = nullptr;
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::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::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_syscall_ = nullptr;
last_displayed_event_ = 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());
}
if (decode_options().output_mode == OutputMode::kStandard) {
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_syscall_ = nullptr;
last_displayed_event_ = output_event.get();
}
}
SaveEvent(std::move(output_event));
}
void SyscallDisplayDispatcher::AddExceptionEvent(std::shared_ptr<ExceptionEvent> exception_event) {
if (!exception_event->thread()->displayed()) {
return;
}
if (decode_options().output_mode == OutputMode::kStandard) {
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);
}
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;
}
}
}
}
}
}
std::unique_ptr<SyscallDecoder> SyscallCompareDispatcher::CreateDecoder(
InterceptingThreadObserver* thread_observer, zxdb::Thread* thread, const Syscall* syscall,
uint64_t timestamp) {
return std::make_unique<SyscallDecoder>(this, thread_observer, thread, syscall,
std::make_unique<SyscallCompare>(this, comparator_, os_),
timestamp);
}
void SyscallDisplayDispatcher::GenerateTests(const std::string& output_directory) {
auto test_generator = TestGenerator(this, output_directory);
test_generator.GenerateTests();
}
} // namespace fidlcat