| // 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 |