| // 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. |
| |
| #ifndef TOOLS_FIDLCAT_LIB_SYSCALL_DECODER_DISPATCHER_H_ |
| #define TOOLS_FIDLCAT_LIB_SYSCALL_DECODER_DISPATCHER_H_ |
| |
| #include <lib/syslog/cpp/macros.h> |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include <cinttypes> |
| #include <cstddef> |
| #include <cstdint> |
| #include <ctime> |
| #include <fstream> |
| #include <functional> |
| #include <map> |
| #include <memory> |
| #include <ostream> |
| #include <sstream> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| #include "src/developer/debug/zxdb/client/thread.h" |
| #include "src/lib/fidl_codec/display_handle.h" |
| #include "src/lib/fidl_codec/display_options.h" |
| #include "src/lib/fidl_codec/message_decoder.h" |
| #include "src/lib/fidl_codec/wire_types.h" |
| #include "tools/fidlcat/lib/comparator.h" |
| #include "tools/fidlcat/lib/decode_options.h" |
| #include "tools/fidlcat/lib/event.h" |
| #include "tools/fidlcat/lib/exception_decoder.h" |
| #include "tools/fidlcat/lib/inference.h" |
| #include "tools/fidlcat/lib/syscall_decoder.h" |
| #include "tools/fidlcat/lib/type_decoder.h" |
| #include "tools/fidlcat/proto/session.pb.h" |
| |
| namespace fidlcat { |
| |
| const fidl_codec::Struct& GetUint128StructDefinition(); |
| std::unique_ptr<fidl_codec::Type> SyscallTypeToFidlCodecType(fidlcat::SyscallType); |
| |
| template <typename ClassType, typename Type> |
| class ClassField; |
| |
| template <typename ClassType> |
| class Class; |
| |
| // Base class for all conditions on fields. |
| template <typename ClassType> |
| class ClassFieldConditionBase { |
| public: |
| ClassFieldConditionBase() = default; |
| virtual ~ClassFieldConditionBase() = default; |
| |
| // Returns true if the condition is true. |
| virtual bool True(const ClassType* object, debug::Arch /*arch*/) = 0; |
| }; |
| |
| // Condition which checks that the field has an expected value. |
| template <typename ClassType, typename Type> |
| class ClassFieldCondition : public ClassFieldConditionBase<ClassType> { |
| public: |
| ClassFieldCondition(const ClassField<ClassType, Type>* field, Type value) |
| : field_(field), value_(value) {} |
| |
| bool True(const ClassType* object, debug::Arch /*arch*/) override; |
| |
| private: |
| // The field we check. |
| const ClassField<ClassType, Type>* const field_; |
| // The value we expect. |
| const Type value_; |
| }; |
| |
| // Condition which checks that the masked field has an expected value. |
| template <typename ClassType, typename Type> |
| class ClassFieldMaskedCondition : public ClassFieldConditionBase<ClassType> { |
| public: |
| ClassFieldMaskedCondition(const ClassField<ClassType, Type>* field, Type mask, Type value) |
| : field_(field), mask_(mask), value_(value) {} |
| |
| bool True(const ClassType* object, debug::Arch /*arch*/) override; |
| |
| private: |
| // The field we check. |
| const ClassField<ClassType, Type>* const field_; |
| // The mask to apply to the field. |
| const Type mask_; |
| // The value we expect. |
| const Type value_; |
| }; |
| |
| // Condition which checks that the architecture has an expected value. |
| template <typename ClassType, typename Type> |
| class ArchCondition : public ClassFieldConditionBase<ClassType> { |
| public: |
| explicit ArchCondition(debug::Arch arch) : arch_(arch) {} |
| |
| bool True(const ClassType* object, debug::Arch /*arch*/) override; |
| |
| private: |
| // The architecture we check. |
| const debug::Arch arch_; |
| }; |
| |
| // Base class for all class fields. |
| template <typename ClassType> |
| class ClassFieldBase { |
| public: |
| ClassFieldBase(std::string_view name, SyscallType syscall_type) |
| : name_(name), syscall_type_(syscall_type) {} |
| virtual ~ClassFieldBase() = default; |
| |
| const std::string& name() const { return name_; } |
| SyscallType syscall_type() const { return syscall_type_; } |
| |
| // Add a condition which must be true to display the input/output. |
| template <typename Type> |
| ClassFieldBase<ClassType>* DisplayIfEqual(const ClassField<ClassType, Type>* field, Type value) { |
| conditions_.push_back(std::make_unique<ClassFieldCondition<ClassType, Type>>(field, value)); |
| return this; |
| } |
| |
| // Add a condition which must be true to display the input/output. |
| template <typename Type> |
| ClassFieldBase<ClassType>* DisplayIfMaskedEqual(const ClassField<ClassType, Type>* field, |
| Type mask, Type value) { |
| conditions_.push_back( |
| std::make_unique<ClassFieldMaskedCondition<ClassType, Type>>(field, mask, value)); |
| return this; |
| } |
| |
| // Define the architecture needed to display the input/output. |
| ClassFieldBase<ClassType>* DisplayIfArch(debug::Arch arch) { |
| conditions_.push_back(std::make_unique<ArchCondition<ClassType, uint8_t>>(arch)); |
| return this; |
| } |
| |
| bool ConditionsAreTrue(const ClassType* object, debug::Arch arch) { |
| for (const auto& condition : conditions_) { |
| if (!condition->True(object, arch)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| virtual std::unique_ptr<fidl_codec::Type> ComputeType() const { |
| return std::make_unique<fidl_codec::InvalidType>(); |
| } |
| |
| virtual std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object, |
| debug::Arch arch) const = 0; |
| |
| uint8_t id() const { return id_; } |
| |
| ClassFieldBase<ClassType>* SetId(uint8_t id) { |
| id_ = id; |
| return this; |
| } |
| |
| private: |
| std::string name_; |
| const SyscallType syscall_type_; |
| std::vector<std::unique_ptr<ClassFieldConditionBase<ClassType>>> conditions_; |
| uint8_t id_ = 0; |
| }; |
| |
| // Define a class field for basic types. |
| template <typename ClassType, typename Type> |
| class ClassField : public ClassFieldBase<ClassType> { |
| public: |
| ClassField(std::string_view name, SyscallType syscall_type, Type (*get)(const ClassType* from)) |
| : ClassFieldBase<ClassType>(name, syscall_type), get_(get) {} |
| |
| Type (*get() const)(const ClassType* from) { return get_; } |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override; |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object, |
| debug::Arch arch) const override; |
| |
| private: |
| // Function which can extract the value of the field for a given object. |
| Type (*get_)(const ClassType* from); |
| }; |
| |
| // Define a class field which is an array of base type items. |
| template <typename ClassType, typename Type> |
| class ArrayField : public ClassFieldBase<ClassType> { |
| public: |
| ArrayField(std::string_view name, SyscallType syscall_type, |
| std::pair<const Type*, int> (*get)(const ClassType* from)) |
| : ClassFieldBase<ClassType>(name, syscall_type), get_(get) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| auto type = SyscallTypeToFidlCodecType(this->syscall_type()); |
| ClassType dummy; |
| std::pair<const Type*, int> result = (*get_)(&dummy); |
| int fixed_size = result.second; |
| return std::make_unique<fidl_codec::ArrayType>(std::move(type), fixed_size); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object, |
| debug::Arch arch) const override; |
| |
| private: |
| // Function which can extract the address of the field for a given object. |
| std::pair<const Type*, int> (*get_)(const ClassType* from); |
| }; |
| |
| // Define a class field which is an array of base type items. The size of the array is dynamic. |
| template <typename ClassType, typename Type, typename SizeType> |
| class DynamicArrayField : public ClassFieldBase<ClassType> { |
| public: |
| DynamicArrayField(std::string_view name, SyscallType syscall_type, |
| std::pair<const Type*, SizeType> (*get)(const ClassType* from)) |
| : ClassFieldBase<ClassType>(name, syscall_type), get_(get) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| auto type = SyscallTypeToFidlCodecType(this->syscall_type()); |
| return std::make_unique<fidl_codec::VectorType>(std::move(type)); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object, |
| debug::Arch arch) const override; |
| |
| private: |
| // Function which can extract the address of the field for a given object. |
| std::pair<const Type*, SizeType> (*get_)(const ClassType* from); |
| }; |
| |
| // Define a class field which is a class. |
| template <typename ClassType, typename Type> |
| class ClassClassField : public ClassFieldBase<ClassType> { |
| public: |
| ClassClassField(std::string_view name, const Type* (*get)(const ClassType* from), |
| const Class<Type>* field_class) |
| : ClassFieldBase<ClassType>(name, SyscallType::kStruct), |
| get_(get), |
| field_class_(field_class) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| return field_class_->ComputeType(); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object, |
| debug::Arch arch) const override { |
| return field_class_->GenerateValue(get_(object), arch); |
| } |
| |
| private: |
| // Function which can extract the address of the field for a given object. |
| const Type* (*get_)(const ClassType* from); |
| // Definition of the field's class. |
| const Class<Type>* const field_class_; |
| }; |
| |
| // Define a class field which is an array of objects. |
| template <typename ClassType, typename Type> |
| class ArrayClassField : public ClassFieldBase<ClassType> { |
| public: |
| ArrayClassField(std::string_view name, std::pair<const Type*, int> (*get)(const ClassType* from), |
| const Class<Type>* sub_class) |
| : ClassFieldBase<ClassType>(name, SyscallType::kStruct), get_(get), sub_class_(sub_class) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| auto type = sub_class_->ComputeType(); |
| ClassType dummy; |
| std::pair<const Type*, int> result = (*get_)(&dummy); |
| int fixed_size = result.second; |
| return std::make_unique<fidl_codec::ArrayType>(std::move(type), fixed_size); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object, |
| debug::Arch arch) const override; |
| |
| private: |
| // Function which can extract the address of the field for a given object. |
| std::pair<const Type*, int> (*get_)(const ClassType* from); |
| // Definition of the array items' class. |
| const Class<Type>* const sub_class_; |
| }; |
| |
| // Define a class field which is an array of objects. The size of the array is dynamic. |
| template <typename ClassType, typename Type> |
| class DynamicArrayClassField : public ClassFieldBase<ClassType> { |
| public: |
| DynamicArrayClassField(std::string_view name, const Type* (*get)(const ClassType* from), |
| uint32_t (*get_size)(const ClassType* from), const Class<Type>* sub_class) |
| : ClassFieldBase<ClassType>(name, SyscallType::kStruct), |
| get_(get), |
| get_size_(get_size), |
| sub_class_(sub_class) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| auto type = sub_class_->ComputeType(); |
| return std::make_unique<fidl_codec::VectorType>(std::move(type)); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object, |
| debug::Arch arch) const override; |
| |
| private: |
| // Function which can extract the address of the field for a given object. |
| const Type* (*get_)(const ClassType* from); |
| // Function which can extract the size of the array for a given object. |
| uint32_t (*get_size_)(const ClassType* from); |
| // Definition of the array items' class. |
| const Class<Type>* const sub_class_; |
| }; |
| |
| // Define a class. |
| template <typename ClassType> |
| class Class { |
| public: |
| const std::string& name() const { return name_; } |
| |
| const std::vector<std::unique_ptr<ClassFieldBase<ClassType>>>& fields() const { return fields_; } |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const { |
| return std::make_unique<fidl_codec::StructType>(struct_definition_, true); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object, |
| debug::Arch arch) const { |
| if (object == nullptr) { |
| return std::make_unique<fidl_codec::NullValue>(); |
| } |
| |
| auto struct_value = std::make_unique<fidl_codec::StructValue>(struct_definition_); |
| for (const auto& field : fields()) { |
| if (field->ConditionsAreTrue(object, arch)) { |
| std::unique_ptr<fidl_codec::Value> val = field->GenerateValue(object, arch); |
| struct_value->AddField(field->name(), field->id(), std::move(val)); |
| } |
| } |
| return std::move(struct_value); |
| } |
| |
| template <typename Type> |
| ClassField<ClassType, Type>* AddField(std::unique_ptr<ClassField<ClassType, Type>> field, |
| uint8_t id = 0) { |
| auto result = field.get(); |
| result->SetId(id); |
| AddFieldToStructDefinition(*field, id); |
| fields_.push_back(std::move(field)); |
| return result; |
| } |
| |
| template <typename Type> |
| ArrayField<ClassType, Type>* AddField(std::unique_ptr<ArrayField<ClassType, Type>> field, |
| uint8_t id = 0) { |
| auto result = field.get(); |
| result->SetId(id); |
| AddFieldToStructDefinition(*field, id); |
| fields_.push_back(std::move(field)); |
| return result; |
| } |
| |
| template <typename Type, typename SizeType> |
| DynamicArrayField<ClassType, Type, SizeType>* AddField( |
| std::unique_ptr<DynamicArrayField<ClassType, Type, SizeType>> field, uint8_t id = 0) { |
| auto result = field.get(); |
| result->SetId(id); |
| AddFieldToStructDefinition(*field, id); |
| fields_.push_back(std::move(field)); |
| return result; |
| } |
| |
| template <typename Type> |
| ClassClassField<ClassType, Type>* AddField( |
| std::unique_ptr<ClassClassField<ClassType, Type>> field, uint8_t id = 0) { |
| auto result = field.get(); |
| result->SetId(id); |
| AddFieldToStructDefinition(*field, id); |
| fields_.push_back(std::move(field)); |
| return result; |
| } |
| |
| template <typename Type> |
| ArrayClassField<ClassType, Type>* AddField( |
| std::unique_ptr<ArrayClassField<ClassType, Type>> field, uint8_t id = 0) { |
| auto result = field.get(); |
| result->SetId(id); |
| AddFieldToStructDefinition(*field, id); |
| fields_.push_back(std::move(field)); |
| return result; |
| } |
| |
| template <typename Type> |
| DynamicArrayClassField<ClassType, Type>* AddField( |
| std::unique_ptr<DynamicArrayClassField<ClassType, Type>> field, uint8_t id = 0) { |
| auto result = field.get(); |
| result->SetId(id); |
| AddFieldToStructDefinition(*field, id); |
| fields_.push_back(std::move(field)); |
| return result; |
| } |
| |
| protected: |
| explicit Class(std::string_view name) : name_(name), struct_definition_(name) {} |
| Class(const Class&) = delete; |
| Class& operator=(const Class&) = delete; |
| |
| private: |
| void AddFieldToStructDefinition(ClassFieldBase<ClassType>& field, uint8_t id) { |
| auto type = field.ComputeType(); |
| struct_definition_.AddMember(field.name(), std::move(type), id); |
| } |
| // Name of the class. |
| std::string name_; |
| // List of all fields in the class. Some fields can be specified several times |
| // with different conditions. |
| std::vector<std::unique_ptr<ClassFieldBase<ClassType>>> fields_; |
| // Struct definition for the generated value. |
| fidl_codec::Struct struct_definition_; |
| }; |
| |
| // Base class (not templated) for system call arguments. |
| class SyscallArgumentBase { |
| public: |
| SyscallArgumentBase(int index, SyscallType syscall_type) |
| : index_(index), syscall_type_(syscall_type) {} |
| virtual ~SyscallArgumentBase() = default; |
| |
| int index() const { return index_; } |
| SyscallType syscall_type() const { return syscall_type_; } |
| |
| private: |
| const int index_; |
| const SyscallType syscall_type_; |
| }; |
| |
| template <typename Type> |
| class SyscallArgumentBaseTyped : public SyscallArgumentBase { |
| public: |
| SyscallArgumentBaseTyped(int index, SyscallType syscall_type) |
| : SyscallArgumentBase(index, syscall_type) {} |
| |
| // Ensures that the argument data will be in memory. |
| virtual void Load(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const {} |
| |
| // True if the argument data is available. |
| virtual bool Loaded(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const { return false; } |
| |
| // True if the argument data is valid (not a null pointer). |
| virtual bool ValueValid(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const { |
| return false; |
| } |
| |
| // The data for the argument. |
| virtual Type Value(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const { return Type(); } |
| |
| // For buffers, ensures that the buffer will be in memory. |
| virtual void LoadArray(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/, |
| size_t /*size*/) const {} |
| |
| // For buffers, true if the buffer is available. |
| virtual bool ArrayLoaded(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/, |
| size_t /*size*/) const { |
| return false; |
| } |
| |
| // For buffers, get a pointer on the buffer data. |
| virtual Type* Content(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const { |
| return nullptr; |
| } |
| |
| virtual debug_ipc::AutomationOperand ComputeAutomationOperand( |
| const std::vector<debug::RegisterID>& argument_indexes) const { |
| return debug_ipc::AutomationOperand(); |
| } |
| }; |
| |
| // Defines an basic type argument for a system call. |
| // A basic type argument can be stored in a 64 bit register. |
| template <typename Type> |
| class SyscallArgument : public SyscallArgumentBaseTyped<Type> { |
| public: |
| SyscallArgument(int index, SyscallType syscall_type) |
| : SyscallArgumentBaseTyped<Type>(index, syscall_type) {} |
| |
| // Redefine index within the class to avoid a compiler error. |
| int index() const { return SyscallArgumentBase::index(); } |
| |
| bool Loaded(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const override { return true; } |
| |
| bool ValueValid(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const override { |
| return true; |
| } |
| |
| Type Value(SyscallDecoderInterface* decoder, Stage /*stage*/) const override { |
| return Type(decoder->ArgumentValue(index())); |
| } |
| |
| debug_ipc::AutomationOperand ComputeAutomationOperand( |
| const std::vector<debug::RegisterID>& argument_indexes) const override { |
| debug_ipc::AutomationOperand operand; |
| if (static_cast<uint64_t>(index()) < argument_indexes.size()) { |
| operand.InitRegister(argument_indexes[index()]); |
| } else { |
| // This will only happen on X64 when we have more than 6 arguments. The last two arguments are |
| // placed on the stack. |
| operand.InitStackSlot((index() - argument_indexes.size()) * 8); |
| } |
| return operand; |
| } |
| }; |
| |
| // Defines a buffer argument for a system call. |
| // A buffer argument is defined by a pointer which can be stored in a 64 bit |
| // register. The data for the buffer stays in memory (referenced by the |
| // pointer). |
| template <typename Type> |
| class SyscallPointerArgument : public SyscallArgumentBaseTyped<Type> { |
| public: |
| SyscallPointerArgument(int index, SyscallType syscall_type) |
| : SyscallArgumentBaseTyped<Type>(index, syscall_type) {} |
| |
| int index() const { return SyscallArgumentBase::index(); } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| decoder->LoadArgument(stage, index(), sizeof(Type)); |
| } |
| |
| bool Loaded(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return decoder->ArgumentLoaded(stage, index(), sizeof(Type)); |
| } |
| |
| bool ValueValid(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return decoder->ArgumentContent(stage, index()) != nullptr; |
| } |
| |
| Type Value(SyscallDecoderInterface* decoder, Stage stage) const override { |
| uint8_t* content = decoder->ArgumentContent(stage, index()); |
| if (content == nullptr) { |
| return Type(); |
| } |
| return *reinterpret_cast<Type*>(content); |
| } |
| |
| void LoadArray(SyscallDecoderInterface* decoder, Stage stage, size_t size) const override { |
| decoder->LoadArgument(stage, index(), size); |
| } |
| |
| bool ArrayLoaded(SyscallDecoderInterface* decoder, Stage stage, size_t size) const override { |
| return decoder->ArgumentLoaded(stage, index(), size); |
| } |
| |
| Type* Content(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return reinterpret_cast<Type*>(decoder->ArgumentContent(stage, index())); |
| } |
| |
| debug_ipc::AutomationOperand ComputeAutomationOperand( |
| const std::vector<debug::RegisterID>& argument_indexes) const override { |
| debug_ipc::AutomationOperand operand; |
| if (static_cast<uint64_t>(index()) < argument_indexes.size()) { |
| operand.InitRegister(argument_indexes[index()]); |
| } else { |
| // This will only happen on X64 when we have more than 6 arguments. The last two arguments are |
| // placed on the stack. |
| operand.InitStackSlot((index() - argument_indexes.size()) * 8); |
| } |
| return operand; |
| } |
| }; |
| |
| // Base class for all data accesses. |
| class AccessBase { |
| public: |
| AccessBase() = default; |
| virtual ~AccessBase() = default; |
| |
| // Returns the real type of the data (because, for example, handles are |
| // implemented as uint32_t). |
| virtual SyscallType GetSyscallType() const = 0; |
| |
| // Computes the fidl codec type for this access. Currently, we are not able to compute it for all |
| // the cases. When we are not able to compute it, this method returns null. |
| std::unique_ptr<fidl_codec::Type> ComputeType() const; |
| |
| // For buffers, ensures that the buffer will be in memory. |
| virtual void LoadArray(SyscallDecoderInterface* decoder, Stage stage, size_t size) = 0; |
| |
| // For buffers, true if the buffer is available. |
| virtual bool ArrayLoaded(SyscallDecoderInterface* decoder, Stage stage, size_t size) const = 0; |
| |
| // For buffers, get a pointer on the buffer data. |
| virtual const uint8_t* Uint8Content(SyscallDecoderInterface* decoder, Stage stage) const = 0; |
| |
| // Returns the automation operand that will load the value of this access. |
| virtual debug_ipc::AutomationOperand ComputeAutomationOperand( |
| const std::vector<debug::RegisterID>& argument_indexes) const = 0; |
| }; |
| |
| // Use to access data for an input or an output. |
| template <typename Type> |
| class Access : public AccessBase { |
| public: |
| Access() = default; |
| |
| // Ensures that the data will be in memory. |
| virtual void Load(SyscallDecoderInterface* decoder, Stage stage) const = 0; |
| |
| // True if the data is available. |
| virtual bool Loaded(SyscallDecoderInterface* decoder, Stage stage) const = 0; |
| |
| // True if the data is valid (not a null pointer). |
| virtual bool ValueValid(SyscallDecoderInterface* decoder, Stage stage) const = 0; |
| |
| // The data. |
| virtual Type Value(SyscallDecoderInterface* decoder, Stage stage) const = 0; |
| |
| // For buffers, get a pointer on the buffer data. |
| virtual const Type* Content(SyscallDecoderInterface* decoder, Stage stage) const = 0; |
| |
| const uint8_t* Uint8Content(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return reinterpret_cast<const uint8_t*>(Content(decoder, stage)); |
| } |
| |
| // Generates the fidl codec value for this access. |
| std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const; |
| }; |
| |
| // Access to a system call argument. There is a direct access to the value |
| // given when the system call is called. For struct or buffer input arguments |
| // and for output arguments (all are pointers), we need to load the referenced |
| // data to be able to access the actual content. This is done within LoadInputs |
| // at the system call entry for input arguments. This is done within LoadOutputs |
| // after the system call returns for output arguments. |
| // All the basic types values and the pointer values are read at the system call |
| // entry. |
| template <typename Type> |
| class ArgumentAccess : public Access<Type> { |
| public: |
| explicit ArgumentAccess(const SyscallArgumentBaseTyped<Type>* argument) : argument_(argument) {} |
| |
| SyscallType GetSyscallType() const override { return argument_->syscall_type(); } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| argument_->Load(decoder, stage); |
| } |
| |
| bool Loaded(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return argument_->Loaded(decoder, stage); |
| } |
| |
| bool ValueValid(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return argument_->ValueValid(decoder, stage); |
| } |
| |
| Type Value(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return argument_->Value(decoder, stage); |
| } |
| |
| void LoadArray(SyscallDecoderInterface* decoder, Stage stage, size_t size) override { |
| argument_->LoadArray(decoder, stage, size); |
| } |
| |
| bool ArrayLoaded(SyscallDecoderInterface* decoder, Stage stage, size_t size) const override { |
| return argument_->ArrayLoaded(decoder, stage, size); |
| } |
| |
| const Type* Content(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return argument_->Content(decoder, stage); |
| } |
| |
| debug_ipc::AutomationOperand ComputeAutomationOperand( |
| const std::vector<debug::RegisterID>& argument_indexes) const override { |
| return argument_->ComputeAutomationOperand(argument_indexes); |
| } |
| |
| private: |
| const SyscallArgumentBaseTyped<Type>* const argument_; |
| }; |
| |
| // Access to a field of a system call argument. |
| template <typename ClassType, typename Type> |
| class FieldAccess : public Access<Type> { |
| public: |
| explicit FieldAccess(const SyscallPointerArgument<ClassType>* argument, |
| Type (*get)(const ClassType* from), SyscallType syscall_type) |
| : argument_(argument), get_(get), syscall_type_(syscall_type) {} |
| |
| SyscallType GetSyscallType() const override { return syscall_type_; } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| argument_->LoadArray(decoder, stage, sizeof(ClassType)); |
| } |
| |
| bool Loaded(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return argument_->ArrayLoaded(decoder, stage, sizeof(ClassType)); |
| } |
| |
| bool ValueValid(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return argument_->Content(decoder, stage) != nullptr; |
| } |
| |
| Type Value(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return get_(argument_->Content(decoder, stage)); |
| } |
| |
| void LoadArray(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/, size_t /*size*/) override {} |
| |
| bool ArrayLoaded(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/, |
| size_t /*size*/) const override { |
| return false; |
| } |
| |
| const Type* Content(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const override { |
| return nullptr; |
| } |
| |
| debug_ipc::AutomationOperand ComputeAutomationOperand( |
| const std::vector<debug::RegisterID>& argument_indexes) const override { |
| return argument_->ComputeAutomationOperand(argument_indexes); |
| } |
| |
| private: |
| const SyscallPointerArgument<ClassType>* const argument_; |
| Type (*get_)(const ClassType* from); |
| const SyscallType syscall_type_; |
| }; |
| |
| // Access to a field of a system call argument. |
| template <typename ClassType, typename Type> |
| class PointerFieldAccess : public Access<Type> { |
| public: |
| explicit PointerFieldAccess(const SyscallPointerArgument<ClassType>* argument, |
| const Type* (*get)(const ClassType* from), SyscallType syscall_type) |
| : argument_(argument), get_(get), syscall_type_(syscall_type) {} |
| |
| SyscallType GetSyscallType() const override { return syscall_type_; } |
| |
| void Load(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const override {} |
| |
| bool Loaded(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const override { |
| return false; |
| } |
| |
| bool ValueValid(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const override { |
| return false; |
| } |
| |
| Type Value(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const override { return {}; } |
| |
| void LoadArray(SyscallDecoderInterface* decoder, Stage stage, size_t size) override { |
| argument_->LoadArray(decoder, stage, sizeof(ClassType)); |
| ClassType* object = argument_->Content(decoder, stage); |
| if (object != nullptr) { |
| decoder->LoadBuffer(stage, reinterpret_cast<uint64_t>(get_(object)), size); |
| } |
| } |
| |
| bool ArrayLoaded(SyscallDecoderInterface* decoder, Stage stage, size_t size) const override { |
| ClassType* object = argument_->Content(decoder, stage); |
| return (object == nullptr) || |
| decoder->BufferLoaded(stage, reinterpret_cast<uint64_t>(get_(object)), size); |
| } |
| |
| const Type* Content(SyscallDecoderInterface* decoder, Stage stage) const override { |
| ClassType* object = argument_->Content(decoder, stage); |
| return reinterpret_cast<const Type*>( |
| decoder->BufferContent(stage, reinterpret_cast<uint64_t>(get_(object)))); |
| } |
| |
| debug_ipc::AutomationOperand ComputeAutomationOperand( |
| const std::vector<debug::RegisterID>& argument_indexes) const override { |
| return argument_->ComputeAutomationOperand(argument_indexes); |
| } |
| |
| private: |
| const SyscallPointerArgument<ClassType>* const argument_; |
| const Type* (*get_)(const ClassType* from); |
| const SyscallType syscall_type_; |
| }; |
| |
| // Base class for the syscall arguments' conditions. |
| class SyscallInputOutputConditionBase { |
| public: |
| SyscallInputOutputConditionBase() = default; |
| virtual ~SyscallInputOutputConditionBase() = default; |
| |
| // Ensures that the data will be in memory. |
| virtual void Load(SyscallDecoderInterface* decoder, Stage stage) const = 0; |
| |
| // True if the data is valid (not a null pointer). |
| virtual bool ValueValid(SyscallDecoderInterface* decoder, Stage stage) const = 0; |
| |
| // True if the condition is satisfied. |
| virtual bool True(SyscallDecoderInterface* decoder, Stage stage) const = 0; |
| |
| virtual bool ComputeAutomationCondition( |
| const std::vector<debug::RegisterID>& argument_indexes, bool is_invoked, debug::Arch arch, |
| Syscall& syscall, std::vector<debug_ipc::AutomationCondition>& condition_vect) const = 0; |
| }; |
| |
| // Condition that a syscall argument must meet. |
| template <typename Type> |
| class SyscallInputOutputCondition : public SyscallInputOutputConditionBase { |
| public: |
| SyscallInputOutputCondition(std::unique_ptr<Access<Type>> access, Type value) |
| : access_(std::move(access)), value_(value) {} |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| access_->Load(decoder, stage); |
| } |
| |
| bool ValueValid(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return access_->ValueValid(decoder, stage); |
| } |
| |
| bool True(SyscallDecoderInterface* decoder, Stage stage) const override { |
| return access_->Value(decoder, stage) == value_; |
| } |
| |
| bool ComputeAutomationCondition( |
| const std::vector<debug::RegisterID>& argument_indexes, bool is_invoked, debug::Arch arch, |
| Syscall& syscall, std::vector<debug_ipc::AutomationCondition>& condition_vect) const override; |
| |
| private: |
| // Access to the syscall argument. |
| const std::unique_ptr<Access<Type>> access_; |
| // Value which is expected. |
| Type value_; |
| }; |
| |
| // Condition which checks that the architecture has an expected value. |
| class SyscallInputOutputArchCondition : public SyscallInputOutputConditionBase { |
| public: |
| explicit SyscallInputOutputArchCondition(debug::Arch arch) : arch_(arch) {} |
| |
| void Load(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const override {} |
| |
| bool ValueValid(SyscallDecoderInterface* /*decoder*/, Stage /*stage*/) const override { |
| return true; |
| } |
| |
| bool True(SyscallDecoderInterface* decoder, Stage /*stage*/) const override { |
| return decoder->arch() == arch_; |
| } |
| |
| bool ComputeAutomationCondition( |
| const std::vector<debug::RegisterID>& argument_indexes, bool is_invoked, debug::Arch arch, |
| Syscall& syscall, |
| std::vector<debug_ipc::AutomationCondition>& condition_vect) const override { |
| return arch_ == arch; |
| } |
| |
| private: |
| // The architecture we check. |
| const debug::Arch arch_; |
| }; |
| |
| // Base class for the inputs/outputs we want to display for a system call. |
| class SyscallInputOutputBase { |
| public: |
| explicit SyscallInputOutputBase(int64_t error_code, std::string_view name) |
| : error_code_(error_code), name_(name) {} |
| virtual ~SyscallInputOutputBase() = default; |
| |
| // For outputs, error code which must have been returned to be able to display |
| // the ouput. |
| int64_t error_code() const { return error_code_; } |
| |
| // Name of the input/output. |
| const std::string& name() const { return name_; } |
| |
| // Id of the input/output |
| uint8_t id() const { return id_; } |
| |
| const std::vector<std::unique_ptr<SyscallInputOutputConditionBase>>* conditions() const { |
| return &conditions_; |
| } |
| |
| // Returns true if this value is displayed inline. |
| virtual bool InlineValue() const { return true; } |
| |
| // Computes the fidl codec type for this input/output. |
| virtual std::unique_ptr<fidl_codec::Type> ComputeType() const; |
| |
| // Adds a condition which must be true to display the input/output. |
| template <typename Type> |
| SyscallInputOutputBase* DisplayIfEqual(std::unique_ptr<Access<Type>> access, Type value) { |
| conditions_.push_back( |
| std::make_unique<SyscallInputOutputCondition<Type>>(std::move(access), value)); |
| return this; |
| } |
| |
| // Sets a unique id to distinguish the input/output from other conditional input/outputs with the |
| // same name |
| SyscallInputOutputBase* SetId(uint8_t id) { |
| id_ = id; |
| return this; |
| } |
| |
| // Defines the architecture needed to display the input/output. |
| SyscallInputOutputBase* DisplayIfArch(debug::Arch arch) { |
| conditions_.push_back(std::make_unique<SyscallInputOutputArchCondition>(arch)); |
| return this; |
| } |
| |
| // Ensures that all the data needed to display the input/output is available. |
| virtual void Load(SyscallDecoderInterface* decoder, Stage stage) const { |
| for (const auto& condition : conditions_) { |
| condition->Load(decoder, stage); |
| } |
| } |
| |
| // Generates the fidl codec value for this input/output. |
| virtual std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const; |
| |
| // True if all the conditions are met. |
| bool ConditionsAreTrue(SyscallDecoderInterface* decoder, Stage stage) { |
| for (const auto& condition : conditions_) { |
| if (!condition->True(decoder, stage)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Returns true if everything which needs memory has generated automation instructions. |
| virtual bool GetAutomationInstructions( |
| const std::vector<debug::RegisterID>& argument_indexes, bool is_invoked, |
| const std::vector<debug_ipc::AutomationCondition>& conditions, Syscall& syscall); |
| |
| private: |
| // For ouput arguments, condition the error code must meet. |
| const int64_t error_code_; |
| // Name of the displayed value. |
| const std::string name_; |
| // Conditions which must be met to display this input/output. |
| std::vector<std::unique_ptr<SyscallInputOutputConditionBase>> conditions_; |
| // A unique id to distinguish input/output from other conditional intput/output with the same name |
| uint8_t id_ = 0; |
| }; |
| |
| // An input/output which only displays an expression (for example, the value of |
| // an argument). This is always displayed inline. |
| template <typename Type> |
| class SyscallInputOutput : public SyscallInputOutputBase { |
| public: |
| SyscallInputOutput(int64_t error_code, std::string_view name, |
| std::unique_ptr<Access<Type>> access) |
| : SyscallInputOutputBase(error_code, name), access_(std::move(access)) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { return access_->ComputeType(); } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| SyscallInputOutputBase::Load(decoder, stage); |
| access_->Load(decoder, stage); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const override { |
| return access_->GenerateValue(decoder, stage); |
| } |
| |
| bool GetAutomationInstructions(const std::vector<debug::RegisterID>& argument_indexes, |
| bool is_invoked, |
| const std::vector<debug_ipc::AutomationCondition>& conditions, |
| Syscall& syscall) override { |
| return true; |
| } |
| |
| const Access<Type>* access_ptr() const { return access_.get(); } |
| |
| private: |
| const std::unique_ptr<Access<Type>> access_; |
| }; |
| |
| // An input/output which only displays a pointer. This is always displayed inline. |
| template <typename Type> |
| class SyscallInputOutputPointer : public SyscallInputOutput<Type> { |
| public: |
| SyscallInputOutputPointer(int64_t error_code, std::string_view name, |
| std::unique_ptr<Access<Type>> access) |
| : SyscallInputOutput<Type>(error_code, name, std::move(access)) {} |
| |
| bool GetAutomationInstructions(const std::vector<debug::RegisterID>& argument_indexes, |
| bool is_invoked, |
| const std::vector<debug_ipc::AutomationCondition>& conditions, |
| Syscall& syscall) override; |
| }; |
| |
| // An input/output which displays actual/requested. This is always displayed inline. |
| template <typename Type> |
| class SyscallInputOutputActualAndRequested : public SyscallInputOutputBase { |
| public: |
| SyscallInputOutputActualAndRequested(int64_t error_code, std::string_view name, |
| std::unique_ptr<Access<Type>> actual, |
| std::unique_ptr<Access<Type>> requested) |
| : SyscallInputOutputBase(error_code, name), |
| actual_(std::move(actual)), |
| requested_(std::move(requested)) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| return std::make_unique<fidl_codec::ActualAndRequestedType>(); |
| } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| SyscallInputOutputBase::Load(decoder, stage); |
| actual_->Load(decoder, stage); |
| requested_->Load(decoder, stage); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const override { |
| auto actual = actual_->GenerateValue(decoder, stage); |
| uint64_t actual_absolute; |
| bool actual_negative; |
| if (!actual->GetIntegerValue(&actual_absolute, &actual_negative) || actual_negative) { |
| return nullptr; |
| } |
| auto requested = requested_->GenerateValue(decoder, stage); |
| uint64_t requested_absolute; |
| bool requested_negative; |
| if (!requested->GetIntegerValue(&requested_absolute, &requested_negative) || |
| requested_negative) { |
| return nullptr; |
| } |
| return std::make_unique<fidl_codec::ActualAndRequestedValue>(actual_absolute, |
| requested_absolute); |
| } |
| |
| private: |
| // Current value. |
| const std::unique_ptr<Access<Type>> actual_; |
| // Value which has been requested or value that should have been requested. |
| const std::unique_ptr<Access<Type>> requested_; |
| }; |
| |
| // An input/output which is one indirect value (access via a pointer). |
| // This is always displayed inline. |
| template <typename Type, typename FromType> |
| class SyscallInputOutputIndirect : public SyscallInputOutputBase { |
| public: |
| SyscallInputOutputIndirect(int64_t error_code, std::string_view name, SyscallType syscall_type, |
| std::unique_ptr<Access<FromType>> buffer) |
| : SyscallInputOutputBase(error_code, name), |
| syscall_type_(syscall_type), |
| buffer_(std::move(buffer)) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| return SyscallTypeToFidlCodecType(syscall_type_); |
| } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| SyscallInputOutputBase::Load(decoder, stage); |
| buffer_->LoadArray(decoder, stage, sizeof(Type)); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const override; |
| |
| bool GetAutomationInstructions(const std::vector<debug::RegisterID>& argument_indexes, |
| bool is_invoked, |
| const std::vector<debug_ipc::AutomationCondition>& conditions, |
| Syscall& syscall) override; |
| |
| private: |
| // Type of the value. |
| SyscallType syscall_type_; |
| // Access to the buffer which contains all the items. |
| const std::unique_ptr<Access<FromType>> buffer_; |
| // Item count in the buffer. |
| const std::unique_ptr<Access<size_t>> buffer_size_; |
| }; |
| |
| // An input/output which is a composed of several items of the same type. |
| // This is always displayed outline. |
| template <typename Type, typename FromType, typename SizeType> |
| class SyscallInputOutputBuffer : public SyscallInputOutputBase { |
| public: |
| SyscallInputOutputBuffer(int64_t error_code, std::string_view name, SyscallType syscall_type, |
| std::unique_ptr<Access<FromType>> buffer, |
| std::unique_ptr<Access<SizeType>> elem_size, |
| std::unique_ptr<Access<SizeType>> elem_count) |
| : SyscallInputOutputBase(error_code, name), |
| syscall_type_(syscall_type), |
| buffer_(std::move(buffer)), |
| elem_size_(std::move(elem_size)), |
| elem_count_(std::move(elem_count)) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| std::unique_ptr<fidl_codec::Type> elem_type = SyscallTypeToFidlCodecType(syscall_type_); |
| return std::make_unique<fidl_codec::VectorType>(std::move(elem_type)); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const override; |
| |
| bool InlineValue() const override { return false; } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| SyscallInputOutputBase::Load(decoder, stage); |
| elem_size_->Load(decoder, stage); |
| if (elem_count_ != nullptr) { |
| elem_count_->Load(decoder, stage); |
| } |
| |
| if (elem_size_->Loaded(decoder, stage) && |
| ((elem_count_ == nullptr) || elem_count_->Loaded(decoder, stage))) { |
| SizeType value = elem_size_->Value(decoder, stage); |
| if (elem_count_ != nullptr) { |
| value *= elem_count_->Value(decoder, stage); |
| } |
| if (value > 0) { |
| buffer_->LoadArray(decoder, stage, value * sizeof(Type)); |
| } |
| } |
| } |
| |
| bool GetAutomationInstructions(const std::vector<debug::RegisterID>& argument_indexes, |
| bool is_invoked, |
| const std::vector<debug_ipc::AutomationCondition>& conditions, |
| Syscall& syscall) override; |
| |
| private: |
| // Type of one buffer item. |
| SyscallType syscall_type_; |
| // Access to the buffer which contains all the items. |
| const std::unique_ptr<Access<FromType>> buffer_; |
| // Size in bytes of one element in the buffer. |
| const std::unique_ptr<Access<SizeType>> elem_size_; |
| // Element count in the buffer. If null, we have exactly one element. |
| const std::unique_ptr<Access<SizeType>> elem_count_; |
| }; |
| |
| // An input/output which is a buffer. Each item in this buffer is a pointer to a C string. |
| class SyscallInputOutputStringBuffer : public SyscallInputOutputBase { |
| public: |
| SyscallInputOutputStringBuffer(int64_t error_code, std::string_view name, |
| std::unique_ptr<Access<char*>> buffer, |
| std::unique_ptr<Access<uint32_t>> count, size_t max_size) |
| : SyscallInputOutputBase(error_code, name), |
| buffer_(std::move(buffer)), |
| count_(std::move(count)), |
| max_size_(max_size) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| return std::make_unique<fidl_codec::StringType>(); |
| } |
| |
| bool InlineValue() const override { return false; } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| SyscallInputOutputBase::Load(decoder, stage); |
| count_->Load(decoder, stage); |
| |
| if (count_->Loaded(decoder, stage)) { |
| uint32_t count = count_->Value(decoder, stage); |
| if (count > 0) { |
| buffer_->LoadArray(decoder, stage, count * sizeof(char*)); |
| if (buffer_->ArrayLoaded(decoder, stage, count * sizeof(char*))) { |
| const char* const* buffer = buffer_->Content(decoder, stage); |
| if (buffer != nullptr) { |
| for (uint32_t i = 0; i < count; ++i) { |
| if (buffer[i] != nullptr) { |
| decoder->LoadBuffer(stage, reinterpret_cast<uint64_t>(buffer[i]), max_size_); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private: |
| // Access to the buffer which contains all the items. |
| const std::unique_ptr<Access<char*>> buffer_; |
| // Element count in the buffer. |
| const std::unique_ptr<Access<uint32_t>> count_; |
| // Maximum size of a string. |
| size_t max_size_; |
| }; |
| |
| // An input/output which is a string. This is always displayed inline. |
| template <typename FromType> |
| class SyscallInputOutputString : public SyscallInputOutputBase { |
| public: |
| SyscallInputOutputString(int64_t error_code, std::string_view name, |
| std::unique_ptr<Access<FromType>> string, |
| std::unique_ptr<Access<size_t>> string_size) |
| : SyscallInputOutputBase(error_code, name), |
| string_(std::move(string)), |
| string_size_(std::move(string_size)) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| return std::make_unique<fidl_codec::StringType>(); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const override { |
| const char* string = reinterpret_cast<const char*>(string_->Content(decoder, stage)); |
| size_t string_size = string_size_->Value(decoder, stage); |
| if (string == nullptr) { |
| return std::make_unique<fidl_codec::NullValue>(); |
| } else { |
| return std::make_unique<fidl_codec::StringValue>( |
| fidl_codec::StringValue(std::string_view(string, string_size))); |
| } |
| } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| SyscallInputOutputBase::Load(decoder, stage); |
| string_size_->Load(decoder, stage); |
| |
| if (string_size_->Loaded(decoder, stage)) { |
| size_t value = string_size_->Value(decoder, stage); |
| if (value > 0) { |
| string_->LoadArray(decoder, stage, value); |
| } |
| } |
| } |
| |
| bool GetAutomationInstructions(const std::vector<debug::RegisterID>& argument_indexes, |
| bool is_invoked, |
| const std::vector<debug_ipc::AutomationCondition>& conditions, |
| Syscall& syscall) override; |
| |
| private: |
| const std::unique_ptr<Access<FromType>> string_; |
| const std::unique_ptr<Access<size_t>> string_size_; |
| }; |
| |
| // An input/output which is a string of fixed size. This is always displayed inline. |
| class SyscallInputOutputFixedSizeString : public SyscallInputOutputBase { |
| public: |
| SyscallInputOutputFixedSizeString(int64_t error_code, std::string_view name, |
| std::unique_ptr<Access<char>> string, size_t string_size) |
| : SyscallInputOutputBase(error_code, name), |
| string_(std::move(string)), |
| string_size_(string_size) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| return std::make_unique<fidl_codec::StringType>(); |
| } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| SyscallInputOutputBase::Load(decoder, stage); |
| string_->LoadArray(decoder, stage, string_size_); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const override { |
| const char* string = reinterpret_cast<const char*>(string_->Content(decoder, stage)); |
| if (string == nullptr) { |
| return std::make_unique<fidl_codec::NullValue>(); |
| } |
| size_t size = strnlen(string, string_size_); |
| return std::make_unique<fidl_codec::StringValue>( |
| fidl_codec::StringValue(std::string_view(string, size))); |
| } |
| |
| private: |
| const std::unique_ptr<Access<char>> string_; |
| size_t string_size_; |
| }; |
| |
| // An input/output which is an object. This is always displayed outline. |
| template <typename ClassType, typename SizeType> |
| class SyscallInputOutputObject : public SyscallInputOutputBase { |
| public: |
| SyscallInputOutputObject(int64_t error_code, std::string_view name, |
| std::unique_ptr<AccessBase> buffer, |
| std::unique_ptr<Access<SizeType>> buffer_size, |
| const Class<ClassType>* class_definition) |
| : SyscallInputOutputBase(error_code, name), |
| buffer_(std::move(buffer)), |
| buffer_size_(std::move(buffer_size)), |
| class_definition_(class_definition) {} |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| return class_definition_->ComputeType(); |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const override { |
| const auto object = reinterpret_cast<const ClassType*>(buffer_->Uint8Content(decoder, stage)); |
| return class_definition_->GenerateValue(object, decoder->arch()); |
| } |
| |
| bool InlineValue() const override { return false; } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| SyscallInputOutputBase::Load(decoder, stage); |
| if (buffer_size_ != nullptr) { |
| buffer_size_->Load(decoder, stage); |
| if (buffer_size_->Loaded(decoder, stage)) { |
| size_t value = buffer_size_->Value(decoder, stage); |
| buffer_->LoadArray(decoder, stage, value); |
| } |
| } else { |
| buffer_->LoadArray(decoder, stage, sizeof(ClassType)); |
| } |
| } |
| |
| private: |
| // Access to the buffer (raw data) which contains the object. |
| const std::unique_ptr<AccessBase> buffer_; |
| // Access to the buffer size. If nul, the size of the buffer is the size of ClassType. |
| const std::unique_ptr<Access<SizeType>> buffer_size_; |
| // Class definition for the displayed object. |
| const Class<ClassType>* class_definition_; |
| }; |
| |
| // An input/output which is an array of objects. This is always displayed outline. |
| template <typename ClassType, typename SizeType> |
| class SyscallInputOutputObjectArray : public SyscallInputOutputBase { |
| public: |
| SyscallInputOutputObjectArray(int64_t error_code, std::string_view name, |
| std::unique_ptr<AccessBase> buffer, |
| std::unique_ptr<Access<SizeType>> buffer_size, |
| const Class<ClassType>* class_definition) |
| : SyscallInputOutputBase(error_code, name), |
| buffer_(std::move(buffer)), |
| buffer_size_(std::move(buffer_size)), |
| class_definition_(class_definition) {} |
| |
| bool InlineValue() const override { return false; } |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override { |
| std::unique_ptr<fidl_codec::Type> elem_type = class_definition_->ComputeType(); |
| return std::make_unique<fidl_codec::VectorType>(std::move(elem_type)); |
| } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| SyscallInputOutputBase::Load(decoder, stage); |
| buffer_size_->Load(decoder, stage); |
| |
| if (buffer_size_->Loaded(decoder, stage)) { |
| size_t value = buffer_size_->Value(decoder, stage); |
| if (value > 0) { |
| buffer_->LoadArray(decoder, stage, sizeof(ClassType) * value); |
| } |
| } |
| } |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const override; |
| |
| private: |
| // Access to the buffer (raw data) which contains the object. |
| const std::unique_ptr<AccessBase> buffer_; |
| // Access to the buffer size. |
| const std::unique_ptr<Access<SizeType>> buffer_size_; |
| // Class definition for the displayed object. |
| const Class<ClassType>* class_definition_; |
| }; |
| |
| class SyscallFidlMessageBase : public SyscallInputOutputBase { |
| public: |
| SyscallFidlMessageBase(int64_t error_code, std::string_view name, |
| fidl_codec::SyscallFidlType type, |
| std::unique_ptr<Access<zx_handle_t>> handle, |
| std::unique_ptr<Access<uint32_t>> options, |
| std::unique_ptr<Access<uint8_t>> bytes, |
| std::unique_ptr<Access<uint32_t>> num_bytes) |
| : SyscallInputOutputBase(error_code, name), |
| type_(type), |
| handle_(std::move(handle)), |
| options_(std::move(options)), |
| bytes_(std::move(bytes)), |
| num_bytes_(std::move(num_bytes)) {} |
| |
| fidl_codec::SyscallFidlType type() const { return type_; } |
| const Access<zx_handle_t>* handle() const { return handle_.get(); } |
| const Access<uint32_t>* options() const { return options_.get(); } |
| const Access<uint8_t>* bytes() const { return bytes_.get(); } |
| const Access<uint32_t>* num_bytes() const { return num_bytes_.get(); } |
| |
| void LoadBytes(SyscallDecoderInterface* decoder, Stage stage) const; |
| |
| class ByteBuffer { |
| public: |
| ByteBuffer(SyscallDecoderInterface* decoder, Stage stage, const SyscallFidlMessageBase* from); |
| ~ByteBuffer() { delete[] buffer_; } |
| |
| const uint8_t* bytes() const { return bytes_; } |
| uint32_t count() const { return count_; } |
| |
| private: |
| uint8_t* buffer_ = nullptr; |
| const uint8_t* bytes_ = nullptr; |
| uint32_t count_ = 0; |
| }; |
| |
| private: |
| const fidl_codec::SyscallFidlType type_; |
| const std::unique_ptr<Access<zx_handle_t>> handle_; |
| const std::unique_ptr<Access<uint32_t>> options_; |
| const std::unique_ptr<Access<uint8_t>> bytes_; |
| const std::unique_ptr<Access<uint32_t>> num_bytes_; |
| }; |
| |
| // An input/output which is a FIDL message. This is always displayed outline. |
| template <typename HandleType> |
| class SyscallFidlMessage : public SyscallFidlMessageBase { |
| public: |
| SyscallFidlMessage(int64_t error_code, std::string_view name, fidl_codec::SyscallFidlType type, |
| std::unique_ptr<Access<zx_handle_t>> handle, |
| std::unique_ptr<Access<uint32_t>> options, |
| std::unique_ptr<Access<uint8_t>> bytes, |
| std::unique_ptr<Access<uint32_t>> num_bytes, |
| std::unique_ptr<Access<HandleType>> handles, |
| std::unique_ptr<Access<uint32_t>> num_handles) |
| : SyscallFidlMessageBase(error_code, name, type, std::move(handle), std::move(options), |
| std::move(bytes), std::move(num_bytes)), |
| handles_(std::move(handles)), |
| num_handles_(std::move(num_handles)) {} |
| |
| const Access<HandleType>* handles() const { return handles_.get(); } |
| const Access<uint32_t>* num_handles() const { return num_handles_.get(); } |
| |
| void Load(SyscallDecoderInterface* decoder, Stage stage) const override { |
| SyscallInputOutputBase::Load(decoder, stage); |
| LoadBytes(decoder, stage); |
| |
| num_handles_->Load(decoder, stage); |
| if (num_handles_->Loaded(decoder, stage)) { |
| uint32_t value = num_handles_->Value(decoder, stage); |
| if (value > 0) { |
| handles_->LoadArray(decoder, stage, value * sizeof(HandleType)); |
| } |
| } |
| } |
| |
| bool GetAutomationInstructions(const std::vector<debug::RegisterID>& argument_indexes, |
| bool is_invoked, |
| const std::vector<debug_ipc::AutomationCondition>& conditions, |
| Syscall& syscall) override; |
| |
| private: |
| const std::unique_ptr<Access<HandleType>> handles_; |
| const std::unique_ptr<Access<uint32_t>> num_handles_; |
| }; |
| |
| class SyscallFidlMessageHandle : public SyscallFidlMessage<zx_handle_t> { |
| public: |
| SyscallFidlMessageHandle( |
| int64_t error_code, std::string_view name, fidl_codec::SyscallFidlType type, |
| std::unique_ptr<Access<zx_handle_t>> handle, std::unique_ptr<Access<uint32_t>> options, |
| std::unique_ptr<Access<uint8_t>> bytes, std::unique_ptr<Access<uint32_t>> num_bytes, |
| std::unique_ptr<Access<zx_handle_t>> handles, std::unique_ptr<Access<uint32_t>> num_handles) |
| : SyscallFidlMessage<zx_handle_t>(error_code, name, type, std::move(handle), |
| std::move(options), std::move(bytes), std::move(num_bytes), |
| std::move(handles), std::move(num_handles)) {} |
| |
| bool InlineValue() const override { return false; } |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override; |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const override; |
| }; |
| |
| class SyscallFidlMessageHandleInfo : public SyscallFidlMessage<zx_handle_info_t> { |
| public: |
| SyscallFidlMessageHandleInfo(int64_t error_code, std::string_view name, |
| fidl_codec::SyscallFidlType type, |
| std::unique_ptr<Access<zx_handle_t>> handle, |
| std::unique_ptr<Access<uint32_t>> options, |
| std::unique_ptr<Access<uint8_t>> bytes, |
| std::unique_ptr<Access<uint32_t>> num_bytes, |
| std::unique_ptr<Access<zx_handle_info_t>> handles, |
| std::unique_ptr<Access<uint32_t>> num_handles) |
| : SyscallFidlMessage<zx_handle_info_t>( |
| error_code, name, type, std::move(handle), std::move(options), std::move(bytes), |
| std::move(num_bytes), std::move(handles), std::move(num_handles)) {} |
| |
| bool InlineValue() const override { return false; } |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override; |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const override; |
| }; |
| |
| class SyscallFidlMessageHandleDisposition : public SyscallFidlMessage<zx_handle_disposition_t> { |
| public: |
| SyscallFidlMessageHandleDisposition(int64_t error_code, std::string_view name, |
| fidl_codec::SyscallFidlType type, |
| std::unique_ptr<Access<zx_handle_t>> handle, |
| std::unique_ptr<Access<uint32_t>> options, |
| std::unique_ptr<Access<uint8_t>> bytes, |
| std::unique_ptr<Access<uint32_t>> num_bytes, |
| std::unique_ptr<Access<zx_handle_disposition_t>> handles, |
| std::unique_ptr<Access<uint32_t>> num_handles) |
| : SyscallFidlMessage<zx_handle_disposition_t>( |
| error_code, name, type, std::move(handle), std::move(options), std::move(bytes), |
| std::move(num_bytes), std::move(handles), std::move(num_handles)) {} |
| |
| bool InlineValue() const override { return false; } |
| |
| std::unique_ptr<fidl_codec::Type> ComputeType() const override; |
| |
| std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const override; |
| }; |
| |
| enum class SyscallKind { |
| // Describes a function (like startup functions). |
| kFunction, |
| // Describes a regular syscall (with no special handling). |
| kRegularSyscall, |
| // zx_channel_read and zx_channel_read_etc syscalls. |
| kChannelRead, |
| // zx_channel_write and zx_channel_write_etc syscalls. |
| kChannelWrite, |
| // zx_channel_call syscall. |
| kChannelCall |
| }; |
| |
| // Defines a syscall we want to decode/display. |
| class Syscall { |
| public: |
| Syscall(std::string_view name, SyscallReturnType return_type, SyscallKind kind) |
| : name_(name), |
| return_type_(return_type), |
| kind_(kind), |
| breakpoint_name_((kind == SyscallKind::kFunction) ? name_ : "$plt(" + name_ + ")") {} |
| |
| // Name of the syscall. |
| [[nodiscard]] const std::string& name() const { return name_; } |
| |
| // Type of the syscall returned value. |
| [[nodiscard]] SyscallReturnType return_type() const { return return_type_; } |
| |
| // Kind of the syscall. |
| SyscallKind kind() const { return kind_; } |
| |
| // True if this class describes a regular function and not a syscall. |
| bool is_function() const { return kind_ == SyscallKind::kFunction; } |
| |
| // True if this class describes a zx_channel_read or zx_channel_read_etc. |
| bool is_channel_read() const { return kind_ == SyscallKind::kChannelRead; } |
| |
| // True if this class describes a zx_channel_write or zx_channel_write_etc. |
| bool is_channel_write() const { return kind_ == SyscallKind::kChannelWrite; } |
| |
| // True if this class describes a zx_channel_call. |
| bool is_channel_call() const { return kind_ == SyscallKind::kChannelCall; } |
| |
| // True if the syscall exchanges at least one FIDL message. |
| bool has_fidl_message() const { |
| return is_channel_read() || is_channel_write() || is_channel_call(); |
| } |
| |
| // Name of the breakpoint used to watch the syscall. |
| [[nodiscard]] const std::string& breakpoint_name() const { return breakpoint_name_; } |
| |
| // All arguments for the syscall. |
| [[nodiscard]] const std::vector<std::unique_ptr<SyscallArgumentBase>>& arguments() const { |
| return arguments_; |
| } |
| |
| // All the data we want to display at the syscall entry. |
| [[nodiscard]] const std::vector<std::unique_ptr<SyscallInputOutputBase>>& inputs() const { |
| return inputs_; |
| } |
| |
| // All the data we want to display at the syscall exit. These data are |
| // conditionally displayed depending on the syscall error code. |
| [[nodiscard]] const std::vector<std::unique_ptr<SyscallInputOutputBase>>& outputs() const { |
| return outputs_; |
| } |
| |
| const std::vector<std::unique_ptr<fidl_codec::StructMember>>& input_inline_members() const { |
| return input_inline_members_; |
| } |
| const std::vector<std::unique_ptr<fidl_codec::StructMember>>& input_outline_members() const { |
| return input_outline_members_; |
| } |
| const std::vector<std::unique_ptr<fidl_codec::StructMember>>& output_inline_members() const { |
| return output_inline_members_; |
| } |
| const std::vector<std::unique_ptr<fidl_codec::StructMember>>& output_outline_members() const { |
| return output_outline_members_; |
| } |
| |
| // The code to execute when the input is decoded and before the input is displayed. |
| // If it exists and returns false, the input is not displayed. |
| [[nodiscard]] bool (SyscallDecoderDispatcher::*inputs_decoded_action() const)(int64_t, |
| SyscallDecoder*) { |
| return inputs_decoded_action_; |
| } |
| void set_inputs_decoded_action(bool (SyscallDecoderDispatcher::*inputs_decoded_action)( |
| int64_t timestamp, SyscallDecoder* decoder)) { |
| inputs_decoded_action_ = inputs_decoded_action; |
| } |
| |
| [[nodiscard]] void (SyscallDecoderDispatcher::*inference() |
| const)(const OutputEvent*, const fidl_codec::semantic::MethodSemantic*) { |
| return inference_; |
| } |
| void set_inference(void (SyscallDecoderDispatcher::*inference)( |
| const OutputEvent*, const fidl_codec::semantic::MethodSemantic*)) { |
| inference_ = inference; |
| } |
| void set_compute_statistics(void (*compute_statistics)(const OutputEvent* event)) { |
| compute_statistics_ = compute_statistics; |
| } |
| |
| // Adds an argument definition to the syscall. |
| template <typename Type> |
| SyscallArgument<Type>* Argument(SyscallType syscall_type) { |
| auto argument = std::make_unique<SyscallArgument<Type>>(arguments_.size(), syscall_type); |
| auto result = argument.get(); |
| arguments_.push_back(std::move(argument)); |
| return result; |
| } |
| |
| // Adds a pointer argument definition to the syscall (the actual type of the |
| // argument is Type*). |
| template <typename Type> |
| SyscallPointerArgument<Type>* PointerArgument(SyscallType syscall_type) { |
| auto argument = std::make_unique<SyscallPointerArgument<Type>>(arguments_.size(), syscall_type); |
| auto result = argument.get(); |
| arguments_.push_back(std::move(argument)); |
| return result; |
| } |
| |
| // Adds an inline input to display. |
| template <typename Type> |
| SyscallInputOutput<Type>* Input(std::string_view name, std::unique_ptr<Access<Type>> access) { |
| auto object = std::make_unique<SyscallInputOutput<Type>>(0, name, std::move(access)); |
| auto result = object.get(); |
| inputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds an indirect input to display. |
| template <typename Type, typename FromType> |
| SyscallInputOutputIndirect<Type, FromType>* InputIndirect( |
| std::string_view name, SyscallType syscall_type, std::unique_ptr<Access<FromType>> buffer) { |
| auto object = std::make_unique<SyscallInputOutputIndirect<Type, FromType>>( |
| 0, name, syscall_type, std::move(buffer)); |
| auto result = object.get(); |
| inputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds an input buffer to display. |
| template <typename Type, typename FromType, typename SizeType> |
| void InputBuffer(std::string_view name, SyscallType syscall_type, |
| std::unique_ptr<Access<Type>> buffer, |
| std::unique_ptr<Access<SizeType>> elem_size, |
| std::unique_ptr<Access<SizeType>> elem_count = nullptr) { |
| inputs_.push_back(std::make_unique<SyscallInputOutputBuffer<Type, FromType, SizeType>>( |
| 0, name, syscall_type, std::move(buffer), std::move(elem_size), std::move(elem_count))); |
| } |
| |
| // Adds an input buffer. Each element of the buffer if a pointer to a C string. |
| void InputStringBuffer(std::string_view name, std::unique_ptr<Access<char*>> buffer, |
| std::unique_ptr<Access<uint32_t>> count, size_t max_size) { |
| inputs_.push_back(std::make_unique<SyscallInputOutputStringBuffer>(0, name, std::move(buffer), |
| std::move(count), max_size)); |
| } |
| |
| // Adds an input string to display. |
| template <typename FromType> |
| SyscallInputOutputString<FromType>* InputString(std::string_view name, |
| std::unique_ptr<Access<FromType>> string, |
| std::unique_ptr<Access<size_t>> string_size) { |
| auto object = std::make_unique<SyscallInputOutputString<FromType>>(0, name, std::move(string), |
| std::move(string_size)); |
| auto result = object.get(); |
| inputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds a fixed size input string to display. |
| SyscallInputOutputFixedSizeString* InputFixedSizeString(std::string_view name, |
| std::unique_ptr<Access<char>> string, |
| size_t string_size) { |
| auto object = std::make_unique<SyscallInputOutputFixedSizeString>(0, name, std::move(string), |
| string_size); |
| auto result = object.get(); |
| inputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds an object input to display. |
| template <typename ClassType> |
| SyscallInputOutputObject<ClassType, size_t>* InputObject( |
| std::string_view name, std::unique_ptr<AccessBase> buffer, |
| const Class<ClassType>* class_definition) { |
| auto object = std::make_unique<SyscallInputOutputObject<ClassType, size_t>>( |
| 0, name, std::move(buffer), nullptr, class_definition); |
| auto result = object.get(); |
| inputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds an object input with a dynamic size to display. |
| template <typename ClassType, typename SizeType> |
| SyscallInputOutputObject<ClassType, SizeType>* InputObject( |
| std::string_view name, std::unique_ptr<AccessBase> buffer, |
| std::unique_ptr<Access<SizeType>> buffer_size, const Class<ClassType>* class_definition) { |
| auto object = std::make_unique<SyscallInputOutputObject<ClassType, SizeType>>( |
| 0, name, std::move(buffer), std::move(buffer_size), class_definition); |
| auto result = object.get(); |
| inputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds an object array input to display. |
| template <typename ClassType, typename SizeType> |
| SyscallInputOutputObjectArray<ClassType, SizeType>* InputObjectArray( |
| std::string_view name, std::unique_ptr<AccessBase> buffer, |
| std::unique_ptr<Access<SizeType>> buffer_size, const Class<ClassType>* class_definition) { |
| auto object = std::make_unique<SyscallInputOutputObjectArray<ClassType, SizeType>>( |
| 0, name, std::move(buffer), std::move(buffer_size), class_definition); |
| auto result = object.get(); |
| inputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds an input FIDL message to display. |
| void InputFidlMessageHandle(std::string_view name, fidl_codec::SyscallFidlType type, |
| std::unique_ptr<Access<zx_handle_t>> handle, |
| std::unique_ptr<Access<uint32_t>> options, |
| std::unique_ptr<Access<uint8_t>> bytes, |
| std::unique_ptr<Access<uint32_t>> num_bytes, |
| std::unique_ptr<Access<zx_handle_t>> handles, |
| std::unique_ptr<Access<uint32_t>> num_handles) { |
| inputs_.push_back(std::make_unique<SyscallFidlMessageHandle>( |
| 0, name, type, std::move(handle), std::move(options), std::move(bytes), |
| std::move(num_bytes), std::move(handles), std::move(num_handles))); |
| } |
| |
| // Adds an input FIDL message to display. |
| void InputFidlMessageHandleDisposition(std::string_view name, fidl_codec::SyscallFidlType type, |
| std::unique_ptr<Access<zx_handle_t>> handle, |
| std::unique_ptr<Access<uint32_t>> options, |
| std::unique_ptr<Access<uint8_t>> bytes, |
| std::unique_ptr<Access<uint32_t>> num_bytes, |
| std::unique_ptr<Access<zx_handle_disposition_t>> handles, |
| std::unique_ptr<Access<uint32_t>> num_handles) { |
| inputs_.push_back(std::make_unique<SyscallFidlMessageHandleDisposition>( |
| 0, name, type, std::move(handle), std::move(options), std::move(bytes), |
| std::move(num_bytes), std::move(handles), std::move(num_handles))); |
| } |
| |
| // Adds an inline output to display. |
| template <typename Type> |
| SyscallInputOutputPointer<Type>* Output(int64_t error_code, std::string_view name, |
| std::unique_ptr<Access<Type>> access) { |
| auto object = |
| std::make_unique<SyscallInputOutputPointer<Type>>(error_code, name, std::move(access)); |
| auto result = object.get(); |
| outputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds an inline output to display which is displayed like: actual/requested. |
| template <typename Type> |
| SyscallInputOutputActualAndRequested<Type>* OutputActualAndRequested( |
| int64_t error_code, std::string_view name, std::unique_ptr<Access<Type>> actual, |
| std::unique_ptr<Access<Type>> requested) { |
| auto object = std::make_unique<SyscallInputOutputActualAndRequested<Type>>( |
| error_code, name, std::move(actual), std::move(requested)); |
| auto result = object.get(); |
| outputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds an indirect output to display. |
| template <typename Type, typename FromType> |
| SyscallInputOutputIndirect<Type, FromType>* OutputIndirect( |
| int64_t error_code, std::string_view name, SyscallType syscall_type, |
| std::unique_ptr<Access<FromType>> buffer) { |
| auto object = std::make_unique<SyscallInputOutputIndirect<Type, FromType>>( |
| error_code, name, syscall_type, std::move(buffer)); |
| auto result = object.get(); |
| outputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds an output buffer to display. |
| template <typename Type, typename FromType, typename SizeType> |
| SyscallInputOutputBuffer<Type, FromType, SizeType>* OutputBuffer( |
| int64_t error_code, std::string_view name, SyscallType syscall_type, |
| std::unique_ptr<Access<FromType>> buffer, std::unique_ptr<Access<SizeType>> elem_size, |
| std::unique_ptr<Access<SizeType>> elem_count = nullptr) { |
| auto object = std::make_unique<SyscallInputOutputBuffer<Type, FromType, SizeType>>( |
| error_code, name, syscall_type, std::move(buffer), std::move(elem_size), |
| std::move(elem_count)); |
| auto result = object.get(); |
| outputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds an output string to display. |
| template <typename FromType> |
| SyscallInputOutputString<FromType>* OutputString(int64_t error_code, std::string_view name, |
| std::unique_ptr<Access<FromType>> string, |
| std::unique_ptr<Access<size_t>> string_size) { |
| auto object = std::make_unique<SyscallInputOutputString<FromType>>( |
| error_code, name, std::move(string), std::move(string_size)); |
| auto result = object.get(); |
| outputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds an object output to display. |
| template <typename ClassType> |
| SyscallInputOutputObject<ClassType, size_t>* OutputObject( |
| int64_t error_code, std::string_view name, std::unique_ptr<AccessBase> buffer, |
| const Class<ClassType>* class_definition) { |
| auto object = std::make_unique<SyscallInputOutputObject<ClassType, size_t>>( |
| error_code, name, std::move(buffer), nullptr, class_definition); |
| auto result = object.get(); |
| outputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Adds an object array output to display. |
| template <typename ClassType, typename SizeType> |
| SyscallInputOutputObjectArray<ClassType, SizeType>* OutputObjectArray( |
| int64_t error_code, std::string_view name, std::unique_ptr<AccessBase> buffer, |
| std::unique_ptr<Access<SizeType>> buffer_size, const Class<ClassType>* class_definition) { |
| auto object = std::make_unique<SyscallInputOutputObjectArray<ClassType, SizeType>>( |
| error_code, name, std::move(buffer), std::move(buffer_size), class_definition); |
| auto result = object.get(); |
| outputs_.push_back(std::move(object)); |
| return result; |
| } |
| |
| // Add an output FIDL message to display. |
| void OutputFidlMessageHandle( |
| int64_t error_code, std::string_view name, fidl_codec::SyscallFidlType type, |
| std::unique_ptr<Access<zx_handle_t>> handle, std::unique_ptr<Access<uint32_t>> options, |
| std::unique_ptr<Access<uint8_t>> bytes, std::unique_ptr<Access<uint32_t>> num_bytes, |
| std::unique_ptr<Access<zx_handle_t>> handles, std::unique_ptr<Access<uint32_t>> num_handles) { |
| outputs_.push_back(std::make_unique<SyscallFidlMessageHandle>( |
| error_code, name, type, std::move(handle), std::move(options), std::move(bytes), |
| std::move(num_bytes), std::move(handles), std::move(num_handles))); |
| } |
| void OutputFidlMessageHandleInfo(int64_t error_code, std::string_view name, |
| fidl_codec::SyscallFidlType type, |
| std::unique_ptr<Access<zx_handle_t>> handle, |
| std::unique_ptr<Access<uint32_t>> options, |
| std::unique_ptr<Access<uint8_t>> bytes, |
| std::unique_ptr<Access<uint32_t>> num_bytes, |
| std::unique_ptr<Access<zx_handle_info_t>> handles, |
| std::unique_ptr<Access<uint32_t>> num_handles) { |
| outputs_.push_back(std::make_unique<SyscallFidlMessageHandleInfo>( |
| error_code, name, type, std::move(handle), std::move(options), std::move(bytes), |
| std::move(num_bytes), std::move(handles), std::move(num_handles))); |
| } |
| |
| // Computes all the fidl codec types for this syscall. |
| void ComputeTypes(); |
| |
| const fidl_codec::StructMember* SearchInlineMember(const std::string& name, bool invoked) const; |
| const fidl_codec::StructMember* SearchInlineMember(uint32_t id, bool invoked) const; |
| const fidl_codec::StructMember* SearchOutlineMember(const std::string& name, bool invoked) const; |
| const fidl_codec::StructMember* SearchOutlineMember(uint32_t id, bool invoked) const; |
| |
| void ComputeStatistics(const OutputEvent* event) const; |
| |
| void ComputeAutomation(debug::Arch arch); |
| |
| // This function stores the value of the operand passed to it when the invoked breakpoint is hit. |
| // Then it modifies the operand so that when the exit breakpoint is hit it will load the stored |
| // value. |
| void EnsureOutputValue(debug_ipc::AutomationOperand* operand, |
| const std::vector<debug_ipc::AutomationCondition>& conditions) { |
| if (operand->kind() == debug_ipc::AutomationOperandKind::kRegister || |
| operand->kind() == debug_ipc::AutomationOperandKind::kStackSlot || |
| operand->kind() == debug_ipc::AutomationOperandKind::kRegisterTimesConstant) { |
| debug_ipc::AutomationInstruction store_instr; |
| store_instr.InitComputeAndStore(*operand, next_stored_value_index_, conditions); |
| AddAutomationInstruction(true, store_instr); |
| operand->InitStoredValue(next_stored_value_index_); |
| ++next_stored_value_index_; |
| } |
| } |
| |
| void AddAutomationInstruction(bool is_invoked, |
| const debug_ipc::AutomationInstruction& new_instr) { |
| if (is_invoked) { |
| invoked_bp_instructions_.emplace_back(new_instr); |
| } else { |
| exit_bp_instructions_.emplace_back(new_instr); |
| } |
| } |
| |
| std::vector<debug_ipc::AutomationInstruction> invoked_bp_instructions() const { |
| return invoked_bp_instructions_; |
| } |
| std::vector<debug_ipc::AutomationInstruction> exit_bp_instructions() const { |
| return exit_bp_instructions_; |
| } |
| |
| bool fully_automated() const { return fully_automated_; } |
| |
| private: |
| const std::string name_; |
| const SyscallReturnType return_type_; |
| const SyscallKind kind_; |
| const std::string breakpoint_name_; |
| std::vector<std::unique_ptr<SyscallArgumentBase>> arguments_; |
| std::vector<std::unique_ptr<SyscallInputOutputBase>> inputs_; |
| std::vector<std::unique_ptr<SyscallInputOutputBase>> outputs_; |
| std::vector<std::unique_ptr<fidl_codec::StructMember>> input_inline_members_; |
| std::vector<std::unique_ptr<fidl_codec::StructMember>> input_outline_members_; |
| std::vector<std::unique_ptr<fidl_codec::StructMember>> output_inline_members_; |
| std::vector<std::unique_ptr<fidl_codec::StructMember>> output_outline_members_; |
| bool (SyscallDecoderDispatcher::*inputs_decoded_action_)(int64_t timestamp, |
| SyscallDecoder* decoder) = nullptr; |
| void (SyscallDecoderDispatcher::*inference_)( |
| const OutputEvent* event, const fidl_codec::semantic::MethodSemantic* semantic) = nullptr; |
| // Method which can compute statistics for the syscall. |
| void (*compute_statistics_)(const OutputEvent* event) = nullptr; |
| |
| bool fully_automated_ = false; |
| std::vector<debug_ipc::AutomationInstruction> invoked_bp_instructions_; |
| std::vector<debug_ipc::AutomationInstruction> exit_bp_instructions_; |
| uint32_t next_stored_value_index_ = 0; |
| }; |
| |
| // Decoder for syscalls. This creates the breakpoints for all the syscalls we |
| // want to monitor. Then, each time a breakpoint is reached, it creates a |
| // SyscallDecoder object which will handle the decoding of one syscall. |
| class SyscallDecoderDispatcher { |
| public: |
| explicit SyscallDecoderDispatcher(const DecodeOptions& decode_options); |
| virtual ~SyscallDecoderDispatcher() = default; |
| |
| const DecodeOptions& decode_options() const { return decode_options_; } |
| |
| const std::map<std::string, std::unique_ptr<Syscall>>& syscalls() const { return syscalls_; } |
| |
| const std::map<zx_koid_t, std::unique_ptr<Process>>& processes() const { return processes_; } |
| std::map<zx_koid_t, std::unique_ptr<Process>>& processes() { return processes_; } |
| |
| const std::map<zx_koid_t, std::unique_ptr<Thread>>& threads() const { return threads_; } |
| std::map<zx_koid_t, std::unique_ptr<Thread>>& threads() { return threads_; } |
| |
| const Inference& inference() const { return inference_; } |
| Inference& inference() { return inference_; } |
| |
| bool display_started() const { return display_started_; } |
| void set_display_started() { display_started_ = true; } |
| |
| bool has_filter() const { return has_filter_; } |
| |
| bool needs_stack_frame() const { return needs_stack_frame_; } |
| |
| // True if we need to save the events in memory. |
| bool needs_to_save_events() const { return needs_to_save_events_; } |
| void set_needs_to_save_events() { needs_to_save_events_ = true; } |
| |
| const std::vector<std::shared_ptr<Event>>& decoded_events() const { return decoded_events_; } |
| |
| Syscall* SearchSyscall(const std::string& name) const { |
| auto result = syscalls_.find(name); |
| if (result == syscalls_.end()) { |
| return nullptr; |
| } |
| return result->second.get(); |
| } |
| |
| Process* SearchProcess(zx_koid_t koid) const { |
| auto process = processes_.find(koid); |
| if (process == processes_.end()) { |
| return nullptr; |
| } |
| return process->second.get(); |
| } |
| |
| Process* CreateProcess(std::string_view name, zx_koid_t koid, |
| fxl::WeakPtr<zxdb::Process> zxdb_process) { |
| FX_DCHECK(processes_.find(koid) == processes_.end()); |
| auto process = std::make_unique<Process>(name, koid, zxdb_process); |
| auto returned_value = process.get(); |
| processes_.emplace(std::make_pair(koid, std::move(process))); |
| return returned_value; |
| } |
| |
| Thread* SearchThread(zx_koid_t koid) const { |
| auto thread = threads_.find(koid); |
| if (thread == threads_.end()) { |
| return nullptr; |
| } |
| return thread->second.get(); |
| } |
| |
| Thread* CreateThread(zx_koid_t koid, Process* process) { |
| FX_DCHECK(threads_.find(koid) == threads_.end()); |
| bool displayed; |
| if (decode_options_.thread_filters.empty()) { |
| displayed = true; |
| } else { |
| displayed = false; |
| for (const auto thread_koid : decode_options_.thread_filters) { |
| if (thread_koid == koid) { |
| displayed = true; |
| break; |
| } |
| } |
| } |
| auto thread = std::make_unique<Thread>(process, koid, displayed); |
| auto returned_value = thread.get(); |
| threads_.emplace(std::make_pair(koid, std::move(thread))); |
| return returned_value; |
| } |
| |
| Thread* CreateThread(std::string_view process_name, zx_koid_t process_koid, zx_koid_t thread_koid, |
| fxl::WeakPtr<zxdb::Process> zxdb_process) { |
| Process* process = SearchProcess(process_koid); |
| if (process == nullptr) { |
| process = CreateProcess(process_name, process_koid, std::move(zxdb_process)); |
| } |
| Thread* thread = SearchThread(thread_koid); |
| if (thread != nullptr) { |
| return thread; |
| } |
| return CreateThread(thread_koid, process); |
| } |
| |
| // Ensures that the handle has been added to the process this thread belongs to. |
| // If this handle has not been added (this is the first time fidlcat monitors it), |
| // the handle is added to the process list of handle using |creation_time| and |startup|. |
| // |startup| is true if the handle has been given to the process during the process |
| // initialization. |
| // When created, the handle is first stored within the dispatcher. |
| HandleInfo* CreateHandleInfo(Thread* thread, uint32_t handle, int64_t creation_time, |
| bool startup); |
| |
| // Decode an intercepted system call. |
| // Called when a thread reached a breakpoint on a system call. |
| // This will only start the decoding. The display will be done when all the |
| // needed information will be gathered. |
| void DecodeSyscall(InterceptingThreadObserver* thread_observer, zxdb::Thread* thread, |
| Syscall* syscall, uint64_t timestamp); |
| |
| // Decode an exception received by a thread. |
| void DecodeException(InterceptionWorkflow* workflow, zxdb::Thread* thread, uint64_t timestamp); |
| |
| virtual fidl_codec::MessageDecoderDispatcher* MessageDecoderDispatcher() { return nullptr; } |
| |
| // Called when we are watching a process we launched. |
| virtual void AddLaunchedProcess(uint64_t process_koid) {} |
| |
| // Delete a decoder created by DecodeSyscall. Called when the syscall is |
| // fully decoded and displayed or the syscalls had an error. |
| virtual void DeleteDecoder(SyscallDecoder* decoder); |
| |
| // Delete a decoder created by DecodeException. Called when the exception is fully decoded and |
| // displayed or the exception had an error. |
| virtual void DeleteDecoder(ExceptionDecoder* decoder); |
| |
| // Called when a process is launched (by using the run option). If |event->error_message()| is not |
| // empty, the the process didn't launch and |event->error_message()| explains why. |
| virtual void AddProcessLaunchedEvent(std::shared_ptr<ProcessLaunchedEvent> event) {} |
| |
| // Called when a process is monitored. If |event->error_message()| is not empty, we haven't been |
| // able to monitor the process. |
| virtual void AddProcessMonitoredEvent(std::shared_ptr<ProcessMonitoredEvent> event) {} |
| |
| // Called when a process is no longer monitored. |
| virtual void AddStopMonitoringEvent(std::shared_ptr<StopMonitoringEvent> event); |
| |
| // Called when the decoder failed. |
| virtual void SyscallDecodingError(const fidlcat::Thread* fidlcat_thread, const Syscall* syscall, |
| const DecoderError& error) { |
| FX_LOGS(ERROR) << error.message(); |
| } |
| |
| // Adds an invoked event. |
| virtual void AddInvokedEvent(std::shared_ptr<InvokedEvent> invoked_event) {} |
| |
| // Adds an output event. |
| virtual void AddOutputEvent(std::shared_ptr<OutputEvent> output_event) {} |
| |
| // Adds an exception event. |
| virtual void AddExceptionEvent(std::shared_ptr<ExceptionEvent> exception_event) {} |
| |
| // Saves a decoded event. |
| void SaveEvent(std::shared_ptr<Event> event); |
| |
| // The session ended (we don't monitor anything more). Do global processing (like saving the |
| // events). |
| virtual void SessionEnded(); |
| |
| // Generate the serialized protobuf session. |
| void GenerateProtoSession(proto::Session* session); |
| |
| private: |
| // Feeds syscalls_ with all the syscalls we can decode. |
| void Populate(); |
| |
| // Computes all the fidl codec types for the syscalls. |
| void ComputeTypes(); |
| |
| // Add a function we want to put a breakpoint on. Used by Populate. |
| Syscall* AddFunction(std::string_view name, SyscallReturnType return_type) { |
| auto syscall = std::make_unique<Syscall>(name, return_type, SyscallKind::kFunction); |
| auto result = syscall.get(); |
| syscalls_.emplace(std::make_pair(syscall->name(), std::move(syscall))); |
| return result; |
| } |
| |
| // Add a syscall. Used by Populate. |
| Syscall* Add(std::string_view name, SyscallReturnType return_type, |
| SyscallKind kind = SyscallKind::kRegularSyscall) { |
| auto syscall = std::make_unique<Syscall>(name, return_type, kind); |
| auto result = syscall.get(); |
| syscalls_.emplace(std::make_pair(syscall->name(), std::move(syscall))); |
| return result; |
| } |
| |
| // Called when we intercept processargs_extract_handles. |
| bool ExtractHandleInfos(int64_t timestamp, SyscallDecoder* decoder) { |
| inference_.ExtractHandleInfos(timestamp, decoder); |
| return false; |
| } |
| |
| // Called when we intercept __libc_extensions_init. |
| bool LibcExtensionsInit(int64_t timestamp, SyscallDecoder* decoder) { |
| inference_.LibcExtensionsInit(timestamp, decoder); |
| return false; |
| } |
| |
| // Called when we intercept zx_channel_create. |
| void ZxChannelCreate(const OutputEvent* event, |
| const fidl_codec::semantic::MethodSemantic* semantic) { |
| inference_.ZxChannelCreate(event); |
| } |
| |
| // Called when we intercept zx_channel_read or a zx_channel_read_etc. |
| void ZxChannelRead(const OutputEvent* event, |
| const fidl_codec::semantic::MethodSemantic* semantic) { |
| inference_.InferMessage(event, semantic, fidl_codec::semantic::ContextType::kRead); |
| } |
| |
| // Called when we intercept zx_channel_write. |
| void ZxChannelWrite(const OutputEvent* event, |
| const fidl_codec::semantic::MethodSemantic* semantic) { |
| inference_.InferMessage(event, semantic, fidl_codec::semantic::ContextType::kWrite); |
| } |
| |
| // Called when we intercept zx_channel_call. |
| void ZxChannelCall(const OutputEvent* event, |
| const fidl_codec::semantic::MethodSemantic* semantic) { |
| inference_.InferMessage(event, semantic, fidl_codec::semantic::ContextType::kCall); |
| } |
| |
| // Called when we intercept zx_port_create. |
| void ZxPortCreate(const OutputEvent* event, |
| const fidl_codec::semantic::MethodSemantic* semantic) { |
| inference_.ZxPortCreate(event); |
| } |
| |
| // Called when we intercept zx_timer_create. |
| void ZxTimerCreate(const OutputEvent* event, |
| const fidl_codec::semantic::MethodSemantic* semantic) { |
| inference_.ZxTimerCreate(event); |
| } |
| |
| // Decoding options. |
| const DecodeOptions& decode_options_; |
| |
| // The definition of all the syscalls we can decode. |
| std::map<std::string, std::unique_ptr<Syscall>> syscalls_; |
| |
| // The intercepted syscalls we are currently decoding. |
| std::map<uint64_t, std::unique_ptr<SyscallDecoder>> syscall_decoders_; |
| |
| // The intercepted exceptions we are currently decoding. |
| std::map<uint64_t, std::unique_ptr<ExceptionDecoder>> exception_decoders_; |
| |
| // All the processes created by this dispatcher. |
| std::map<zx_koid_t, std::unique_ptr<Process>> processes_; |
| |
| // All the threads created by this dispatcher. |
| std::map<zx_koid_t, std::unique_ptr<Thread>> threads_; |
| |
| // All the handles created by this dispatcher. |
| std::vector<std::unique_ptr<HandleInfo>> handle_infos_; |
| |
| // All the handles for which we have some information. |
| Inference inference_; |
| |
| // True if we are now displaying messages and syscalls. If decode_options_.trigger_filters is not |
| // empty, it starts with a false value and switchs to true when a message that satisfies one of |
| // the filter is found. |
| bool display_started_ = true; |
| |
| // True if we are filtering messages. |
| bool has_filter_ = false; |
| |
| // True if we need the stack frame. |
| bool needs_stack_frame_ = false; |
| |
| // True if we need to save the events in memory. |
| bool needs_to_save_events_ = false; |
| |
| // All the events we have decoded (only filled if needs_to_save_events_ is true). |
| std::vector<std::shared_ptr<Event>> decoded_events_; |
| }; |
| |
| class SyscallDisplayDispatcher : public SyscallDecoderDispatcher { |
| public: |
| SyscallDisplayDispatcher(fidl_codec::LibraryLoader* loader, const DecodeOptions& decode_options, |
| const DisplayOptions& display_options, std::ostream& os) |
| : SyscallDecoderDispatcher(decode_options), |
| message_decoder_dispatcher_(loader, display_options), |
| os_(os), |
| dump_messages_(display_options.dump_messages) { |
| if (!display_options.extra_generation.empty()) { |
| set_needs_to_save_events(); |
| } |
| } |
| |
| fidl_codec::MessageDecoderDispatcher& message_decoder_dispatcher() { |
| return message_decoder_dispatcher_; |
| } |
| |
| const fidl_codec::Colors& colors() const { return message_decoder_dispatcher_.colors(); } |
| |
| int columns() const { return message_decoder_dispatcher_.columns(); } |
| |
| bool with_process_info() const { return message_decoder_dispatcher_.with_process_info(); } |
| |
| const std::vector<ExtraGeneration>& extra_generation() const { |
| return message_decoder_dispatcher_.display_options().extra_generation; |
| } |
| |
| bool extra_generation_needs_colors() const { |
| return message_decoder_dispatcher_.display_options().extra_generation_needs_colors; |
| } |
| |
| fidl_codec::LibraryLoader* loader() const { return message_decoder_dispatcher_.loader(); } |
| |
| const Event* last_displayed_event() const { return last_displayed_event_; } |
| void clear_last_displayed_event() { last_displayed_event_ = nullptr; } |
| |
| std::ostream& os() const { return os_; } |
| |
| bool dump_messages() const { return dump_messages_; } |
| |
| uint32_t GetNextInvokedEventId() { return next_invoked_event_id_++; } |
| |
| fidl_codec::MessageDecoderDispatcher* MessageDecoderDispatcher() override { |
| return &message_decoder_dispatcher_; |
| } |
| |
| void AddLaunchedProcess(uint64_t process_koid) override { |
| message_decoder_dispatcher_.AddLaunchedProcess(process_koid); |
| } |
| |
| double GetTime(int64_t timestamp); |
| |
| void AddProcessLaunchedEvent(std::shared_ptr<ProcessLaunchedEvent> event) override; |
| |
| void AddProcessMonitoredEvent(std::shared_ptr<ProcessMonitoredEvent> event) override; |
| |
| void AddStopMonitoringEvent(std::shared_ptr<StopMonitoringEvent> event) override; |
| |
| void SyscallDecodingError(const fidlcat::Thread* fidlcat_thread, const Syscall* syscall, |
| const DecoderError& error) override; |
| |
| void AddInvokedEvent(std::shared_ptr<InvokedEvent> invoked_event) override; |
| |
| void AddOutputEvent(std::shared_ptr<OutputEvent> output_event) override; |
| |
| void AddExceptionEvent(std::shared_ptr<ExceptionEvent> exception_event) override; |
| |
| void SessionEnded() override; |
| |
| // Displays an invoked event. |
| virtual void DisplayInvokedEvent(const InvokedEvent* invoked_event); |
| |
| // Displays an output event. |
| virtual void DisplayOutputEvent(const OutputEvent* output_event); |
| |
| // Displays an exception event. |
| virtual void DisplayExceptionEvent(const ExceptionEvent* exception_event); |
| |
| void DisplaySummary(std::ostream& os); |
| |
| void DisplayTop(std::ostream& os); |
| |
| void DisplayThreads(std::ostream& os); |
| |
| void GenerateTests(const std::string& output_directory); |
| |
| private: |
| // Class which can decode a FIDL message. |
| fidl_codec::MessageDecoderDispatcher message_decoder_dispatcher_; |
| // The last event we displayed. |
| const Event* last_displayed_event_ = nullptr; |
| // The stream which will receive the syscall decodings. |
| std::ostream& os_; |
| // True if we always display the binary dump of the messages. |
| const bool dump_messages_; |
| uint32_t next_invoked_event_id_ = 0; |
| }; |
| |
| class SyscallCompareDispatcher : public SyscallDisplayDispatcher { |
| public: |
| SyscallCompareDispatcher(fidl_codec::LibraryLoader* loader, const DecodeOptions& decode_options, |
| const DisplayOptions& display_options, |
| std::shared_ptr<Comparator> comparator) |
| : SyscallDisplayDispatcher(loader, decode_options, display_options, os_), |
| comparator_(comparator) {} |
| |
| void SyscallDecodingError(const fidlcat::Thread* fidlcat_thread, const Syscall* syscall, |
| const DecoderError& error) override; |
| |
| void DisplayInvokedEvent(const InvokedEvent* invoked_event) override; |
| |
| void DisplayOutputEvent(const OutputEvent* output_event) override; |
| |
| private: |
| std::shared_ptr<Comparator> comparator_; |
| std::ostringstream os_; |
| }; |
| |
| // Generates a fidl codec value for this syscall value. |
| template <typename ValueType> |
| inline std::unique_ptr<fidl_codec::Value> GenerateValue(ValueType /*value*/) { |
| return std::make_unique<fidl_codec::InvalidValue>(); |
| } |
| |
| template <> |
| inline std::unique_ptr<fidl_codec::Value> GenerateValue(bool value) { |
| return std::make_unique<fidl_codec::BoolValue>(value); |
| } |
| |
| template <> |
| inline std::unique_ptr<fidl_codec::Value> GenerateValue(int32_t value) { |
| if (value < 0) { |
| return std::make_unique<fidl_codec::IntegerValue>(-(static_cast<int64_t>(value)), true); |
| } |
| return std::make_unique<fidl_codec::IntegerValue>(value, false); |
| } |
| |
| template <> |
| inline std::unique_ptr<fidl_codec::Value> GenerateValue(int64_t value) { |
| if (value < 0) { |
| return std::make_unique<fidl_codec::IntegerValue>(-(static_cast<uint64_t>(value)), true); |
| } |
| return std::make_unique<fidl_codec::IntegerValue>(value, false); |
| } |
| |
| template <> |
| inline std::unique_ptr<fidl_codec::Value> GenerateValue(uint8_t value) { |
| return std::make_unique<fidl_codec::IntegerValue>(value, false); |
| } |
| |
| template <> |
| inline std::unique_ptr<fidl_codec::Value> GenerateValue(uint16_t value) { |
| return std::make_unique<fidl_codec::IntegerValue>(value, false); |
| } |
| |
| template <> |
| inline std::unique_ptr<fidl_codec::Value> GenerateValue(uint32_t value) { |
| return std::make_unique<fidl_codec::IntegerValue>(value, false); |
| } |
| |
| template <> |
| inline std::unique_ptr<fidl_codec::Value> GenerateValue(uint64_t value) { |
| return std::make_unique<fidl_codec::IntegerValue>(value, false); |
| } |
| |
| #ifdef __MACH__ |
| template <> |
| inline std::unique_ptr<fidl_codec::Value> GenerateValue(uintptr_t value) { |
| return std::make_unique<fidl_codec::IntegerValue>(value, false); |
| } |
| #endif |
| |
| template <> |
| inline std::unique_ptr<fidl_codec::Value> GenerateValue(zx_uint128_t value) { |
| auto struct_value = std::make_unique<fidl_codec::StructValue>(GetUint128StructDefinition()); |
| struct_value->AddField("low", 0, std::make_unique<fidl_codec::IntegerValue>(value.low, false)); |
| struct_value->AddField("high", 0, std::make_unique<fidl_codec::IntegerValue>(value.high, false)); |
| return struct_value; |
| } |
| |
| template <typename Type> |
| inline std::unique_ptr<fidl_codec::Value> GenerateHandleValue(Type handle) { |
| return std::make_unique<fidl_codec::InvalidValue>(); |
| } |
| |
| template <> |
| inline std::unique_ptr<fidl_codec::Value> GenerateHandleValue(zx_handle_t handle) { |
| zx_handle_disposition_t result; |
| result.operation = fidl_codec::kNoHandleDisposition; |
| result.handle = handle; |
| result.rights = 0; |
| result.type = ZX_OBJ_TYPE_NONE; |
| result.result = ZX_OK; |
| return std::make_unique<fidl_codec::HandleValue>(result); |
| } |
| |
| template <typename ClassType, typename Type> |
| bool ClassFieldCondition<ClassType, Type>::True(const ClassType* object, debug::Arch /*arch*/) { |
| return field_->get()(object) == value_; |
| } |
| |
| template <typename ClassType, typename Type> |
| bool ClassFieldMaskedCondition<ClassType, Type>::True(const ClassType* object, |
| debug::Arch /*arch*/) { |
| return (field_->get()(object) & mask_) == value_; |
| } |
| |
| template <typename ClassType, typename Type> |
| bool ArchCondition<ClassType, Type>::True(const ClassType* /*object*/, debug::Arch arch) { |
| return arch_ == arch; |
| } |
| |
| template <typename ClassType, typename Type> |
| std::unique_ptr<fidl_codec::Value> ClassField<ClassType, Type>::GenerateValue( |
| const ClassType* object, debug::Arch arch) const { |
| if (ClassFieldBase<ClassType>::syscall_type() == SyscallType::kHandle) { |
| return fidlcat::GenerateHandleValue<Type>(get_(object)); |
| } else { |
| return fidlcat::GenerateValue<Type>(get_(object)); |
| } |
| } |
| |
| template <typename ClassType, typename Type> |
| std::unique_ptr<fidl_codec::Type> ClassField<ClassType, Type>::ComputeType() const { |
| auto type = SyscallTypeToFidlCodecType(this->syscall_type()); |
| if (type == nullptr) { |
| type = std::make_unique<fidl_codec::InvalidType>(); |
| } |
| return type; |
| } |
| |
| template <typename ClassType, typename Type> |
| std::unique_ptr<fidl_codec::Value> ArrayField<ClassType, Type>::GenerateValue( |
| const ClassType* object, debug::Arch arch) const { |
| auto vector_value = std::make_unique<fidl_codec::VectorValue>(); |
| std::pair<const Type*, int> array = get_(object); |
| auto syscall_type_ = this->syscall_type(); |
| |
| for (int i = 0; i < array.second; ++i) { |
| if (syscall_type_ == SyscallType::kHandle) { |
| vector_value->AddValue(fidlcat::GenerateHandleValue<Type>(array.first[i])); |
| } else { |
| vector_value->AddValue(fidlcat::GenerateValue<Type>(array.first[i])); |
| } |
| } |
| |
| return vector_value; |
| } |
| |
| template <typename ClassType, typename Type, typename SizeType> |
| std::unique_ptr<fidl_codec::Value> DynamicArrayField<ClassType, Type, SizeType>::GenerateValue( |
| const ClassType* object, debug::Arch arch) const { |
| std::pair<const Type*, SizeType> vector = get_(object); |
| auto syscall_type_ = this->syscall_type(); |
| if (syscall_type_ == SyscallType::kChar) { |
| size_t size = strnlen(reinterpret_cast<const char*>(vector.first), vector.second); |
| return std::make_unique<fidl_codec::StringValue>( |
| std::string_view(reinterpret_cast<const char*>(vector.first), size)); |
| } |
| |
| auto vector_value = std::make_unique<fidl_codec::VectorValue>(); |
| for (SizeType i = 0; i < vector.second; ++i) { |
| if (syscall_type_ == SyscallType::kHandle) { |
| vector_value->AddValue(fidlcat::GenerateHandleValue<Type>(vector.first[i])); |
| } else { |
| vector_value->AddValue(fidlcat::GenerateValue<Type>(vector.first[i])); |
| } |
| } |
| |
| return vector_value; |
| } |
| |
| template <typename ClassType, typename Type> |
| std::unique_ptr<fidl_codec::Value> ArrayClassField<ClassType, Type>::GenerateValue( |
| const ClassType* object, debug::Arch arch) const { |
| auto vector_value = std::make_unique<fidl_codec::VectorValue>(); |
| std::pair<const Type*, int> array = get_(object); |
| |
| for (int i = 0; i < array.second; ++i) { |
| vector_value->AddValue(sub_class_->GenerateValue(array.first + i, arch)); |
| } |
| |
| return vector_value; |
| } |
| |
| template <typename ClassType, typename Type> |
| std::unique_ptr<fidl_codec::Value> DynamicArrayClassField<ClassType, Type>::GenerateValue( |
| const ClassType* object, debug::Arch arch) const { |
| auto vector_value = std::make_unique<fidl_codec::VectorValue>(); |
| const Type* array = get_(object); |
| uint32_t size = get_size_(object); |
| |
| for (uint32_t i = 0; i < size; ++i) { |
| vector_value->AddValue(sub_class_->GenerateValue(array + i, arch)); |
| } |
| |
| return vector_value; |
| } |
| |
| template <typename Type> |
| std::unique_ptr<fidl_codec::Value> Access<Type>::GenerateValue(SyscallDecoderInterface* decoder, |
| Stage stage) const { |
| if (ValueValid(decoder, stage)) { |
| if (GetSyscallType() |