blob: cf9f257cadf4f4f3546ad7c46a075ff61cb4d162 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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 {
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_ipc::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_ipc::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_ipc::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_ipc::Arch arch) : arch_(arch) {}
bool True(const ClassType* object, debug_ipc::Arch /*arch*/) override;
private:
// The architecture we check.
const debug_ipc::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_ipc::Arch arch) {
conditions_.push_back(std::make_unique<ArchCondition<ClassType, uint8_t>>(arch));
return this;
}
bool ConditionsAreTrue(const ClassType* object, debug_ipc::Arch arch) {
for (const auto& condition : conditions_) {
if (!condition->True(object, arch)) {
return false;
}
}
return true;
}
virtual void Display(const ClassType* object, debug_ipc::Arch arch,
fidl_codec::PrettyPrinter& printer) const = 0;
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_ipc::Arch arch) const {
return std::make_unique<fidl_codec::InvalidValue>();
}
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_; }
void Display(const ClassType* object, debug_ipc::Arch arch,
fidl_codec::PrettyPrinter& printer) const override;
std::unique_ptr<fidl_codec::Type> ComputeType() const override;
std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object,
debug_ipc::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);
}
void Display(const ClassType* object, debug_ipc::Arch arch,
fidl_codec::PrettyPrinter& printer) const override;
std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object,
debug_ipc::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));
}
void Display(const ClassType* object, debug_ipc::Arch arch,
fidl_codec::PrettyPrinter& printer) const override;
std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object,
debug_ipc::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) {}
void Display(const ClassType* object, debug_ipc::Arch arch,
fidl_codec::PrettyPrinter& printer) const override;
std::unique_ptr<fidl_codec::Type> ComputeType() const override {
return field_class_->ComputeType();
}
std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object,
debug_ipc::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) {}
void Display(const ClassType* object, debug_ipc::Arch arch,
fidl_codec::PrettyPrinter& printer) 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));
}
void Display(const ClassType* object, debug_ipc::Arch arch,
fidl_codec::PrettyPrinter& printer) const override;
std::unique_ptr<fidl_codec::Value> GenerateValue(const ClassType* object,
debug_ipc::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_; }
void DisplayObject(const ClassType* object, debug_ipc::Arch arch,
fidl_codec::PrettyPrinter& printer) const {
printer << "{\n";
{
fidl_codec::Indent indent(printer);
for (const auto& field : fields_) {
if (field->ConditionsAreTrue(object, arch)) {
field->Display(object, arch, printer);
}
}
}
printer << '}';
}
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_ipc::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(SyscallDecoder* /*decoder*/, Stage /*stage*/) const {}
// True if the argument data is available.
virtual bool Loaded(SyscallDecoder* /*decoder*/, Stage /*stage*/) const { return false; }
// True if the argument data is valid (not a null pointer).
virtual bool ValueValid(SyscallDecoder* /*decoder*/, Stage /*stage*/) const { return false; }
// The data for the argument.
virtual Type Value(SyscallDecoder* /*decoder*/, Stage /*stage*/) const { return Type(); }
// For buffers, ensures that the buffer will be in memory.
virtual void LoadArray(SyscallDecoder* /*decoder*/, Stage /*stage*/, size_t /*size*/) const {}
// For buffers, true if the buffer is available.
virtual bool ArrayLoaded(SyscallDecoder* /*decoder*/, Stage /*stage*/, size_t /*size*/) const {
return false;
}
// For buffers, get a pointer on the buffer data.
virtual Type* Content(SyscallDecoder* /*decoder*/, Stage /*stage*/) const { return nullptr; }
};
// 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(SyscallDecoder* /*decoder*/, Stage /*stage*/) const override { return true; }
bool ValueValid(SyscallDecoder* /*decoder*/, Stage /*stage*/) const override { return true; }
Type Value(SyscallDecoder* decoder, Stage /*stage*/) const override {
return Type(decoder->ArgumentValue(index()));
}
};
// 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(SyscallDecoder* decoder, Stage stage) const override {
decoder->LoadArgument(stage, index(), sizeof(Type));
}
bool Loaded(SyscallDecoder* decoder, Stage stage) const override {
return decoder->ArgumentLoaded(stage, index(), sizeof(Type));
}
bool ValueValid(SyscallDecoder* decoder, Stage stage) const override {
return decoder->ArgumentContent(stage, index()) != nullptr;
}
Type Value(SyscallDecoder* decoder, Stage stage) const override {
uint8_t* content = decoder->ArgumentContent(stage, index());
if (content == nullptr) {
return Type();
}
return *reinterpret_cast<Type*>(content);
}
void LoadArray(SyscallDecoder* decoder, Stage stage, size_t size) const override {
decoder->LoadArgument(stage, index(), size);
}
bool ArrayLoaded(SyscallDecoder* decoder, Stage stage, size_t size) const override {
return decoder->ArgumentLoaded(stage, index(), size);
}
Type* Content(SyscallDecoder* decoder, Stage stage) const override {
return reinterpret_cast<Type*>(decoder->ArgumentContent(stage, index()));
}
};
// 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(SyscallDecoder* decoder, Stage stage, size_t size) = 0;
// For buffers, true if the buffer is available.
virtual bool ArrayLoaded(SyscallDecoder* decoder, Stage stage, size_t size) const = 0;
// For buffers, get a pointer on the buffer data.
virtual const uint8_t* Uint8Content(SyscallDecoder* decoder, Stage stage) 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(SyscallDecoder* decoder, Stage stage) const = 0;
// True if the data is available.
virtual bool Loaded(SyscallDecoder* decoder, Stage stage) const = 0;
// True if the data is valid (not a null pointer).
virtual bool ValueValid(SyscallDecoder* decoder, Stage stage) const = 0;
// The data.
virtual Type Value(SyscallDecoder* decoder, Stage stage) const = 0;
// For buffers, get a pointer on the buffer data.
virtual const Type* Content(SyscallDecoder* decoder, Stage stage) const = 0;
const uint8_t* Uint8Content(SyscallDecoder* 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(SyscallDecoder* decoder, Stage stage) const;
// Display the data on a stream (with name and type).
void Display(SyscallDecoder* decoder, Stage stage, std::string_view name,
fidl_codec::PrettyPrinter& printer) 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(SyscallDecoder* decoder, Stage stage) const override {
argument_->Load(decoder, stage);
}
bool Loaded(SyscallDecoder* decoder, Stage stage) const override {
return argument_->Loaded(decoder, stage);
}
bool ValueValid(SyscallDecoder* decoder, Stage stage) const override {
return argument_->ValueValid(decoder, stage);
}
Type Value(SyscallDecoder* decoder, Stage stage) const override {
return argument_->Value(decoder, stage);
}
void LoadArray(SyscallDecoder* decoder, Stage stage, size_t size) override {
argument_->LoadArray(decoder, stage, size);
}
bool ArrayLoaded(SyscallDecoder* decoder, Stage stage, size_t size) const override {
return argument_->ArrayLoaded(decoder, stage, size);
}
const Type* Content(SyscallDecoder* decoder, Stage stage) const override {
return argument_->Content(decoder, stage);
}
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(SyscallDecoder* decoder, Stage stage) const override {
argument_->LoadArray(decoder, stage, sizeof(ClassType));
}
bool Loaded(SyscallDecoder* decoder, Stage stage) const override {
return argument_->ArrayLoaded(decoder, stage, sizeof(ClassType));
}
bool ValueValid(SyscallDecoder* decoder, Stage stage) const override {
return argument_->Content(decoder, stage) != nullptr;
}
Type Value(SyscallDecoder* decoder, Stage stage) const override {
return get_(argument_->Content(decoder, stage));
}
void LoadArray(SyscallDecoder* /*decoder*/, Stage /*stage*/, size_t /*size*/) override {}
bool ArrayLoaded(SyscallDecoder* /*decoder*/, Stage /*stage*/, size_t /*size*/) const override {
return false;
}
const Type* Content(SyscallDecoder* /*decoder*/, Stage /*stage*/) const override {
return nullptr;
}
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(SyscallDecoder* /*decoder*/, Stage /*stage*/) const override {}
bool Loaded(SyscallDecoder* /*decoder*/, Stage /*stage*/) const override { return false; }
bool ValueValid(SyscallDecoder* /*decoder*/, Stage /*stage*/) const override { return false; }
Type Value(SyscallDecoder* /*decoder*/, Stage /*stage*/) const override { return {}; }
void LoadArray(SyscallDecoder* 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(SyscallDecoder* 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(SyscallDecoder* 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))));
}
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(SyscallDecoder* decoder, Stage stage) const = 0;
// True if the data is valid (not a null pointer).
virtual bool ValueValid(SyscallDecoder* decoder, Stage stage) const = 0;
// True if the condition is satisfied.
virtual bool True(SyscallDecoder* decoder, Stage stage) 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(SyscallDecoder* decoder, Stage stage) const override { access_->Load(decoder, stage); }
bool ValueValid(SyscallDecoder* decoder, Stage stage) const override {
return access_->ValueValid(decoder, stage);
}
bool True(SyscallDecoder* decoder, Stage stage) const override {
return access_->Value(decoder, stage) == value_;
}
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_ipc::Arch arch) : arch_(arch) {}
void Load(SyscallDecoder* /*decoder*/, Stage /*stage*/) const override {}
bool ValueValid(SyscallDecoder* /*decoder*/, Stage /*stage*/) const override { return true; }
bool True(SyscallDecoder* decoder, Stage /*stage*/) const override {
return decoder->arch() == arch_;
}
private:
// The architecture we check.
const debug_ipc::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_; }
// 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_ipc::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(SyscallDecoder* 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(SyscallDecoder* decoder,
Stage stage) const;
// Displays small inputs or outputs.
virtual const char* DisplayInline(SyscallDecoder* /*decoder*/, Stage /*stage*/,
const char* separator,
fidl_codec::PrettyPrinter& /*printer*/) const {
return separator;
}
// Displays large (multi lines) inputs or outputs.
virtual void DisplayOutline(SyscallDecoder* /*decoder*/, Stage /*stage*/,
fidl_codec::PrettyPrinter& /*printer*/) const {}
// True if all the conditions are met.
bool ConditionsAreTrue(SyscallDecoder* decoder, Stage stage) {
for (const auto& condition : conditions_) {
if (!condition->True(decoder, stage)) {
return false;
}
}
return true;
}
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(SyscallDecoder* decoder, Stage stage) const override {
SyscallInputOutputBase::Load(decoder, stage);
access_->Load(decoder, stage);
}
std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoder* decoder,
Stage stage) const override {
return access_->GenerateValue(decoder, stage);
}
const char* DisplayInline(SyscallDecoder* decoder, Stage stage, const char* separator,
fidl_codec::PrettyPrinter& printer) const override {
printer << separator;
access_->Display(decoder, stage, name(), printer);
return ", ";
}
private:
const std::unique_ptr<Access<Type>> access_;
};
// An input/output which displays actual/asked. 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>> asked)
: SyscallInputOutputBase(error_code, name),
actual_(std::move(actual)),
asked_(std::move(asked)) {}
void Load(SyscallDecoder* decoder, Stage stage) const override {
SyscallInputOutputBase::Load(decoder, stage);
actual_->Load(decoder, stage);
asked_->Load(decoder, stage);
}
const char* DisplayInline(SyscallDecoder* decoder, Stage stage, const char* separator,
fidl_codec::PrettyPrinter& printer) const override;
private:
// Current value.
const std::unique_ptr<Access<Type>> actual_;
// Value which has been asked or value that should have been asked.
const std::unique_ptr<Access<Type>> asked_;
};
// 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(SyscallDecoder* decoder, Stage stage) const override {
SyscallInputOutputBase::Load(decoder, stage);
buffer_->LoadArray(decoder, stage, sizeof(Type));
}
std::unique_ptr<fidl_codec::Value> GenerateValue(SyscallDecoder* decoder,
Stage stage) const override;
const char* DisplayInline(SyscallDecoder* decoder, Stage stage, const char* separator,
fidl_codec::PrettyPrinter& printer) const 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(SyscallDecoder* decoder,
Stage stage) const override;
bool InlineValue() const override { return false; }
void Load(SyscallDecoder* 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));
}
}
}
void DisplayOutline(SyscallDecoder* decoder, Stage stage,
fidl_codec::PrettyPrinter& printer) const 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(SyscallDecoder* 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_);
}
}
}
}
}
}
}
void DisplayOutline(SyscallDecoder* decoder, Stage stage,
fidl_codec::PrettyPrinter& printer) const override;
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(SyscallDecoder* 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(SyscallDecoder* 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);
}
}
}
const char* DisplayInline(SyscallDecoder* decoder, Stage stage, const char* separator,
fidl_codec::PrettyPrinter& printer) const 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) {}
void Load(SyscallDecoder* decoder, Stage stage) const override {
SyscallInputOutputBase::Load(decoder, stage);
string_->LoadArray(decoder, stage, string_size_);
}
const char* DisplayInline(SyscallDecoder* decoder, Stage stage, const char* separator,
fidl_codec::PrettyPrinter& printer) const override;
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(SyscallDecoder* 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(SyscallDecoder* 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));
}
}
void DisplayOutline(SyscallDecoder* decoder, Stage stage,
fidl_codec::PrettyPrinter& printer) const override;
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; }
void Load(SyscallDecoder* 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);
}
}
}
void DisplayOutline(SyscallDecoder* decoder, Stage stage,
fidl_codec::PrettyPrinter& printer) 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_;
};
// An input/output which is a FIDL message. This is always displayed outline.
template <typename HandleType>
class SyscallFidlMessage : public SyscallInputOutputBase {
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<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)
: SyscallInputOutputBase(error_code, name),
type_(type),
handle_(std::move(handle)),
bytes_(std::move(bytes)),
num_bytes_(std::move(num_bytes)),
handles_(std::move(handles)),
num_handles_(std::move(num_handles)) {}
fidl_codec::SyscallFidlType type() const { return type_; }
const Access<zx_handle_t>* handle() const { return handle_.get(); }
const Access<uint8_t>* bytes() const { return bytes_.get(); }
const Access<uint32_t>* num_bytes() const { return num_bytes_.get(); }
const Access<HandleType>* handles() const { return handles_.get(); }
const Access<uint32_t>* num_handles() const { return num_handles_.get(); }
void Load(SyscallDecoder* decoder, Stage stage) const override {
SyscallInputOutputBase::Load(decoder, stage);
handle_->Load(decoder, stage);
num_bytes_->Load(decoder, stage);
num_handles_->Load(decoder, stage);
if (num_bytes_->Loaded(decoder, stage)) {
uint32_t value = num_bytes_->Value(decoder, stage);
if (value > 0) {
bytes_->LoadArray(decoder, stage, value);
}
}
if (num_handles_->Loaded(decoder, stage)) {
uint32_t value = num_handles_->Value(decoder, stage);
if (value > 0) {
handles_->LoadArray(decoder, stage, value * sizeof(HandleType));
}
}
}
private:
const fidl_codec::SyscallFidlType type_;
const std::unique_ptr<Access<zx_handle_t>> handle_;
const std::unique_ptr<Access<uint8_t>> bytes_;
const std::unique_ptr<Access<uint32_t>> num_bytes_;
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<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(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(SyscallDecoder* 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<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(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(SyscallDecoder* 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<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(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(SyscallDecoder* 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_;
}
bool fidl_codec_values_ready() const { return fidl_codec_values_ready_; }
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<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(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<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(bytes), std::move(num_bytes),
std::move(handles), std::move(num_handles)));
}
// Adds an inline output to display.
template <typename Type>
SyscallInputOutput<Type>* Output(int64_t error_code, std::string_view name,
std::unique_ptr<Access<Type>> access) {
auto object = std::make_unique<SyscallInputOutput<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/asked.
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>> asked) {
auto object = std::make_unique<SyscallInputOutputActualAndRequested<Type>>(
error_code, name, std::move(actual), std::move(asked));
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<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(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<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(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;
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_;
bool fidl_codec_values_ready_ = false;
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;
};
// 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;
}
// 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) {}
// Create the object which will decode the syscall.
virtual std::unique_ptr<SyscallDecoder> CreateDecoder(InterceptingThreadObserver* thread_observer,
zxdb::Thread* thread,
const Syscall* syscall,
uint64_t timestamp) = 0;
// 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);
// Create the object which will decode the exception.
virtual std::unique_ptr<ExceptionDecoder> CreateDecoder(InterceptionWorkflow* workflow,
zxdb::Thread* thread,
uint64_t timestamp) = 0;
// 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);
// 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 SyscallDisplay* last_displayed_syscall() const { return last_displayed_syscall_; }
void set_last_displayed_syscall(const SyscallDisplay* last_displayed_syscall) {
last_displayed_syscall_ = last_displayed_syscall;
}
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);
}
std::unique_ptr<SyscallDecoder> CreateDecoder(InterceptingThreadObserver* thread_observer,
zxdb::Thread* thread, const Syscall* syscall,
uint64_t timestamp) override;
std::unique_ptr<ExceptionDecoder> CreateDecoder(InterceptionWorkflow* workflow,
zxdb::Thread* thread,
uint64_t timestamp) override;
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 AddInvokedEvent(std::shared_ptr<InvokedEvent> invoked_event) override;
// Displays an invoked event.
void DisplayInvokedEvent(const InvokedEvent* invoked_event);
void AddOutputEvent(std::shared_ptr<OutputEvent> output_event) override;
void AddExceptionEvent(std::shared_ptr<ExceptionEvent> exception_event) override;
void SessionEnded() override;
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 syscall we displayed the inputs on the stream.
const SyscallDisplay* last_displayed_syscall_ = nullptr;
// 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) {}
std::unique_ptr<SyscallDecoder> CreateDecoder(InterceptingThreadObserver* thread_observer,
zxdb::Thread* thread, const Syscall* syscall,
uint64_t timestamp) 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 <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);
}
// Display a value on a stream.
template <typename ValueType>
void DisplayValue(SyscallType type, ValueType /*value*/, fidl_codec::PrettyPrinter& printer) {
printer << "unimplemented generic value " << static_cast<uint32_t>(type);
}
template <>
inline void DisplayValue<bool>(SyscallType type, bool value, fidl_codec::PrettyPrinter& printer) {
switch (type) {
case SyscallType::kBool:
printer << fidl_codec::Blue << (value ? "true" : "false") << fidl_codec::ResetColor;
break;
default:
printer << "unimplemented bool value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<int32_t>(SyscallType type, int32_t value,
fidl_codec::PrettyPrinter& printer) {
switch (type) {
case SyscallType::kInt32:
printer << fidl_codec::Blue << value << fidl_codec::ResetColor;
break;
case SyscallType::kFutex:
printer << fidl_codec::Red << value << fidl_codec::ResetColor;
break;
case SyscallType::kStatus:
printer.DisplayStatus(value);
break;
default:
printer << "unimplemented int32_t value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<int64_t>(SyscallType type, int64_t value,
fidl_codec::PrettyPrinter& printer) {
switch (type) {
case SyscallType::kInt64:
printer << fidl_codec::Blue << value << fidl_codec::ResetColor;
break;
case SyscallType::kDuration:
printer << DisplayDuration(value);
break;
case SyscallType::kFutex:
printer << fidl_codec::Red << value << fidl_codec::ResetColor;
break;
case SyscallType::kMonotonicTime:
printer << DisplayDuration(value);
break;
case SyscallType::kTime:
printer << DisplayTime(value);
break;
default:
printer << "unimplemented int64_t value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<uint8_t>(SyscallType type, uint8_t value,
fidl_codec::PrettyPrinter& printer) {
switch (type) {
case SyscallType::kUint8:
printer << fidl_codec::Blue << static_cast<uint32_t>(value) << fidl_codec::ResetColor;
break;
case SyscallType::kUint8Hexa:
printer.DisplayHexa8(value);
break;
case SyscallType::kPacketGuestVcpuType:
printer.DisplayPacketGuestVcpuType(value);
break;
default:
printer << "unimplemented uint8_t value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<uint16_t>(SyscallType type, uint16_t value,
fidl_codec::PrettyPrinter& printer) {
switch (type) {
case SyscallType::kUint16:
printer << fidl_codec::Blue << value << fidl_codec::ResetColor;
break;
case SyscallType::kUint16Hexa:
printer.DisplayHexa16(value);
break;
case SyscallType::kPacketPageRequestCommand:
printer.DisplayPacketPageRequestCommand(value);
break;
default:
printer << "unimplemented uint16_t value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<uint32_t>(SyscallType type, uint32_t value,
fidl_codec::PrettyPrinter& printer) {
switch (type) {
case SyscallType::kUint32:
printer <<