blob: 222ee4083d2445cfb027c90359f1a64052ecd11b [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 <zircon/system/public/zircon/errors.h>
#include <zircon/system/public/zircon/types.h>
#include <cinttypes>
#include <cstddef>
#include <cstdint>
#include <ctime>
#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 "src/lib/fxl/logging.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"
namespace fidlcat {
template <typename ClassType, typename Type>
class ClassField;
template <typename ClassType>
class Class;
void DisplayString(const fidl_codec::Colors& colors, const char* string, size_t size,
std::ostream& os);
// 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, SyscallDecoder* decoder,
const fidl_codec::Colors& colors, std::string_view line_header, int tabs,
std::ostream& os) const = 0;
private:
std::string name_;
const SyscallType syscall_type_;
std::vector<std::unique_ptr<ClassFieldConditionBase<ClassType>>> conditions_;
};
// 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, SyscallDecoder* decoder,
const fidl_codec::Colors& colors, std::string_view line_header, int tabs,
std::ostream& os) 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) {}
void Display(const ClassType* object, debug_ipc::Arch arch, SyscallDecoder* decoder,
const fidl_codec::Colors& colors, std::string_view line_header, int tabs,
std::ostream& os) const;
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 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, SyscallDecoder* decoder,
const fidl_codec::Colors& colors, std::string_view line_header, int tabs,
std::ostream& os) const override;
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, SyscallDecoder* decoder,
const fidl_codec::Colors& colors, std::string_view line_header, int tabs,
std::ostream& os) 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) {}
void Display(const ClassType* object, debug_ipc::Arch arch, SyscallDecoder* decoder,
const fidl_codec::Colors& colors, std::string_view line_header, int tabs,
std::ostream& os) 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_; }
void DisplayObject(const ClassType* object, debug_ipc::Arch arch, SyscallDecoder* decoder,
const fidl_codec::Colors& colors, std::string_view line_header, int tabs,
std::ostream& os) const {
os << "{\n";
for (const auto& field : fields_) {
if (field->ConditionsAreTrue(object, arch)) {
field->Display(object, arch, decoder, colors, line_header, tabs + 1, os);
}
}
os << line_header << std::string(tabs * fidl_codec::kTabSize, ' ') << "}";
}
template <typename Type>
ClassField<ClassType, Type>* AddField(std::unique_ptr<ClassField<ClassType, Type>> field) {
auto result = field.get();
fields_.push_back(std::move(field));
return result;
}
template <typename Type>
ArrayField<ClassType, Type>* AddField(std::unique_ptr<ArrayField<ClassType, Type>> field) {
auto result = field.get();
fields_.push_back(std::move(field));
return result;
}
template <typename Type>
ClassClassField<ClassType, Type>* AddField(
std::unique_ptr<ClassClassField<ClassType, Type>> field) {
auto result = field.get();
fields_.push_back(std::move(field));
return result;
}
template <typename Type>
ArrayClassField<ClassType, Type>* AddField(
std::unique_ptr<ArrayClassField<ClassType, Type>> field) {
auto result = field.get();
fields_.push_back(std::move(field));
return result;
}
template <typename Type>
DynamicArrayClassField<ClassType, Type>* AddField(
std::unique_ptr<DynamicArrayClassField<ClassType, Type>> field) {
auto result = field.get();
fields_.push_back(std::move(field));
return result;
}
protected:
explicit Class(std::string_view name) : name_(name) {}
Class(const Class&) = delete;
Class& operator=(const Class&) = delete;
private:
// 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_;
};
// 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(SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder, Stage stage,
std::string_view name, std::ostream& os) 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_; }
// 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;
}
// 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(SyscallDisplayDispatcher* /*dispatcher*/,
SyscallDecoder* /*decoder*/, Stage /*stage*/,
const char* separator, std::ostream& /*os*/) const {
return separator;
}
// Displays large (multi lines) inputs or outputs.
virtual void DisplayOutline(SyscallDisplayDispatcher* /*dispatcher*/, SyscallDecoder* /*decoder*/,
Stage /*stage*/, std::string_view /*line_header*/, int /*tabs*/,
std::ostream& /*os*/) 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_;
};
// 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(SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder,
Stage stage, const char* separator, std::ostream& os) const override {
os << separator;
access_->Display(dispatcher, decoder, stage, name(), os);
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(SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder,
Stage stage, const char* separator, std::ostream& os) 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)) {}
void Load(SyscallDecoder* decoder, Stage stage) const override {
SyscallInputOutputBase::Load(decoder, stage);
buffer_->LoadArray(decoder, stage, sizeof(Type));
}
const char* DisplayInline(SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder,
Stage stage, const char* separator, std::ostream& os) 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)) {}
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(SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder, Stage stage,
std::string_view line_header, int tabs, std::ostream& os) 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) {}
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(SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder, Stage stage,
std::string_view line_header, int tabs, std::ostream& os) 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)) {}
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(SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder,
Stage stage, const char* separator, std::ostream& os) 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(SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder,
Stage stage, const char* separator, std::ostream& os) 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) {}
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(SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder, Stage stage,
std::string_view line_header, int tabs, std::ostream& os) 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(SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder, Stage stage,
std::string_view line_header, int tabs, std::ostream& os) 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;
};
// Defines a syscall we want to decode/display.
class Syscall {
public:
Syscall(std::string_view name, SyscallReturnType return_type, bool is_function)
: name_(name),
return_type_(return_type),
is_function_(is_function),
breakpoint_name_(is_function_ ? 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_; }
// True if this class describes a regular function and not a syscall.
bool is_function() const { return is_function_; }
// 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)(SyscallDecoder*) {
return inputs_decoded_action_;
}
void set_inputs_decoded_action(
bool (SyscallDecoderDispatcher::*inputs_decoded_action)(SyscallDecoder* decoder)) {
inputs_decoded_action_ = inputs_decoded_action;
}
[[nodiscard]] void (SyscallDecoderDispatcher::*displayed_action() const)(SyscallDecoder*) {
return displayed_action_;
}
void set_displayed_action(
void (SyscallDecoderDispatcher::*displayed_action)(SyscallDecoder* decoder)) {
displayed_action_ = displayed_action;
}
// 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 InputFidlMessage(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 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();
private:
const std::string name_;
const SyscallReturnType return_type_;
const bool is_function_;
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_)(SyscallDecoder* decoder) = nullptr;
void (SyscallDecoderDispatcher::*displayed_action_)(SyscallDecoder* decoder) = 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)
: decode_options_(decode_options) {
Populate();
ComputeTypes();
}
virtual ~SyscallDecoderDispatcher() = default;
const DecodeOptions& decode_options() const { return decode_options_; }
const std::vector<std::unique_ptr<Syscall>>& syscalls() const { return syscalls_; }
const std::map<zx_koid_t, std::unique_ptr<Process>>& processes() const { return processes_; }
const Inference& inference() const { return inference_; }
const Process* SearchProcess(zx_koid_t koid) const {
auto process = processes_.find(koid);
if (process == processes_.end()) {
return nullptr;
}
return process->second.get();
}
const Process* CreateProcess(std::string_view name, zx_koid_t koid) {
FXL_DCHECK(processes_.find(koid) == processes_.end());
auto process = std::make_unique<Process>(name, koid);
auto returned_value = process.get();
processes_.emplace(std::make_pair(koid, std::move(process)));
return returned_value;
}
const Thread* SearchThread(zx_koid_t koid) const {
auto thread = threads_.find(koid);
if (thread == threads_.end()) {
return nullptr;
}
return thread->second.get();
}
const Thread* CreateThread(zx_koid_t koid, const Process* process) {
FXL_DCHECK(threads_.find(koid) == threads_.end());
auto thread = std::make_unique<Thread>(process, koid);
auto returned_value = thread.get();
threads_.emplace(std::make_pair(koid, std::move(thread)));
return returned_value;
}
// 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);
// Decode an exception received by a thread.
void DecodeException(InterceptionWorkflow* workflow, zxdb::Thread* thread);
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) = 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) = 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 |error_message| is not
// empty, the the process didn't launch and |error_message| explains why.
virtual void ProcessLaunched(const std::string& command, std::string_view error_message) {}
// Called when a process is monitored. If |error_message| is not empty, we haven't been able
// to monitor the process.
virtual void ProcessMonitored(std::string_view name, zx_koid_t koid,
std::string_view error_message);
// Called when a process is no longer monitored.
virtual void StopMonitoring(zx_koid_t koid);
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, /*is_function=*/true);
auto result = syscall.get();
syscalls_.push_back(std::move(syscall));
return result;
}
// Add a syscall. Used by Populate.
Syscall* Add(std::string_view name, SyscallReturnType return_type) {
auto syscall = std::make_unique<Syscall>(name, return_type, /*is_function=*/false);
auto result = syscall.get();
syscalls_.push_back(std::move(syscall));
return result;
}
// Called when we intercept processargs_extract_handles.
bool ExtractHandles(SyscallDecoder* decoder) {
inference_.ExtractHandles(decoder);
return false;
}
// Called when we intercept __libc_extensions_init.
bool LibcExtensionsInit(SyscallDecoder* decoder) {
inference_.LibcExtensionsInit(decoder);
return false;
}
// Called when we intercept zx_channel_create.
void ZxChannelCreate(SyscallDecoder* decoder) { inference_.ZxChannelCreate(decoder); }
// Called when we intercept zx_channel_read or a zx_channel_read_etc.
void ZxChannelRead(SyscallDecoder* decoder) {
inference_.InferMessage(decoder, fidl_codec::semantic::ContextType::kRead);
}
// Called when we intercept zx_channel_write.
void ZxChannelWrite(SyscallDecoder* decoder) {
inference_.InferMessage(decoder, fidl_codec::semantic::ContextType::kWrite);
}
// Called when we intercept zx_channel_call.
void ZxChannelCall(SyscallDecoder* decoder) {
inference_.InferMessage(decoder, fidl_codec::semantic::ContextType::kCall);
}
// Called when we intercept zx_port_create.
void ZxPortCreate(SyscallDecoder* decoder) { inference_.ZxPortCreate(decoder); }
// Called when we intercept zx_timer_create.
void ZxTimerCreate(SyscallDecoder* decoder) { inference_.ZxTimerCreate(decoder); }
// Decoding options.
const DecodeOptions& decode_options_;
// The definition of all the syscalls we can decode.
std::vector<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_;
std::map<zx_koid_t, std::unique_ptr<Process>> processes_;
std::map<zx_koid_t, std::unique_ptr<Thread>> threads_;
// All the handles for which we have some information.
Inference inference_;
};
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) {}
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 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;
}
bool dump_messages() const { return dump_messages_; }
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) override;
std::unique_ptr<ExceptionDecoder> CreateDecoder(InterceptionWorkflow* workflow,
zxdb::Thread* thread) override;
void ProcessLaunched(const std::string& command, std::string_view error_message) override;
void ProcessMonitored(std::string_view name, zx_koid_t koid,
std::string_view error_message) override;
void StopMonitoring(zx_koid_t koid) override;
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 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_;
};
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) 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>(-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);
}
// Display a value on a stream.
template <typename ValueType>
void DisplayValue(SyscallDecoder* /*decoder*/, const fidl_codec::Colors& /*colors*/,
SyscallType type, ValueType /*value*/, std::ostream& os) {
os << "unimplemented generic value " << static_cast<uint32_t>(type);
}
template <>
inline void DisplayValue<bool>(SyscallDecoder* /*decoder*/, const fidl_codec::Colors& colors,
SyscallType type, bool value, std::ostream& os) {
switch (type) {
case SyscallType::kBool:
os << colors.blue << (value ? "true" : "false") << colors.reset;
break;
default:
os << "unimplemented bool value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<std::pair<const char*, size_t>>(SyscallDecoder* /*decoder*/,
const fidl_codec::Colors& colors,
SyscallType type,
std::pair<const char*, size_t> value,
std::ostream& os) {
switch (type) {
case SyscallType::kCharArray:
os << colors.red << '"';
for (size_t i = 0; i < value.second; ++i) {
if (value.first[i] == 0) {
break;
}
os << value.first[i];
}
os << '"' << colors.reset;
break;
default:
os << "unimplemented char array value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<int32_t>(SyscallDecoder* /*decoder*/, const fidl_codec::Colors& colors,
SyscallType type, int32_t value, std::ostream& os) {
switch (type) {
case SyscallType::kInt32:
os << colors.blue << value << colors.reset;
break;
case SyscallType::kFutex:
os << colors.red << value << colors.reset;
break;
case SyscallType::kStatus:
StatusName(colors, value, os);
break;
default:
os << "unimplemented int32_t value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<int64_t>(SyscallDecoder* /*decoder*/, const fidl_codec::Colors& colors,
SyscallType type, int64_t value, std::ostream& os) {
switch (type) {
case SyscallType::kInt64:
os << colors.blue << value << colors.reset;
break;
case SyscallType::kDuration:
os << DisplayDuration(colors, value);
break;
case SyscallType::kFutex:
os << colors.red << value << colors.reset;
break;
case SyscallType::kMonotonicTime:
os << DisplayDuration(colors, value);
break;
case SyscallType::kTime:
os << DisplayTime(colors, value);
break;
default:
os << "unimplemented int64_t value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<uint8_t>(SyscallDecoder* /*decoder*/, const fidl_codec::Colors& colors,
SyscallType type, uint8_t value, std::ostream& os) {
switch (type) {
case SyscallType::kUint8:
os << colors.blue << static_cast<uint32_t>(value) << colors.reset;
break;
case SyscallType::kUint8Hexa: {
std::vector<char> buffer(sizeof(uint8_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%02" PRIx8, value);
os << colors.blue << buffer.data() << colors.reset;
break;
}
case SyscallType::kPacketGuestVcpuType:
os << colors.blue;
PacketGuestVcpuTypeName(value, os);
os << colors.reset;
break;
default:
os << "unimplemented uint8_t value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<std::pair<const uint8_t*, int>>(SyscallDecoder* decoder,
const fidl_codec::Colors& colors,
SyscallType type,
std::pair<const uint8_t*, int> value,
std::ostream& os) {
switch (type) {
case SyscallType::kUint8ArrayDecimal:
case SyscallType::kUint8ArrayHexa: {
const char* separator = "";
for (int i = 0; i < value.second; ++i) {
os << separator;
DisplayValue(
decoder, colors,
(type == SyscallType::kUint8ArrayHexa) ? SyscallType::kUint8Hexa : SyscallType::kUint8,
value.first[i], os);
separator = ", ";
}
break;
}
default:
os << "unimplemented uint8_t array value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<uint16_t>(SyscallDecoder* /*decoder*/, const fidl_codec::Colors& colors,
SyscallType type, uint16_t value, std::ostream& os) {
switch (type) {
case SyscallType::kUint16:
os << colors.blue << value << colors.reset;
break;
case SyscallType::kUint16Hexa: {
std::vector<char> buffer(sizeof(uint16_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%04" PRIx16, value);
os << colors.blue << buffer.data() << colors.reset;
break;
}
case SyscallType::kPacketPageRequestCommand:
os << colors.blue;
PacketPageRequestCommandName(value, os);
os << colors.reset;
break;
default:
os << "unimplemented uint16_t value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<std::pair<const uint16_t*, int>>(SyscallDecoder* decoder,
const fidl_codec::Colors& colors,
SyscallType type,
std::pair<const uint16_t*, int> value,
std::ostream& os) {
switch (type) {
case SyscallType::kUint16ArrayDecimal:
case SyscallType::kUint16ArrayHexa: {
const char* separator = "";
for (int i = 0; i < value.second; ++i) {
os << separator;
DisplayValue(decoder, colors,
(type == SyscallType::kUint16ArrayHexa) ? SyscallType::kUint16Hexa
: SyscallType::kUint16,
value.first[i], os);
separator = ", ";
}
break;
}
default:
os << "unimplemented uint16_t array value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<uint32_t>(SyscallDecoder* decoder, const fidl_codec::Colors& colors,
SyscallType type, uint32_t value, std::ostream& os) {
switch (type) {
case SyscallType::kUint32:
os << colors.blue << value << colors.reset;
break;
case SyscallType::kUint32Hexa: {
std::vector<char> buffer(sizeof(uint32_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%08" PRIx32, value);
os << colors.blue << buffer.data() << colors.reset;
break;
}
case SyscallType::kBtiPerm:
os << colors.blue;
BtiPermName(value, os);
os << colors.reset;
break;
case SyscallType::kCachePolicy:
os << colors.red;
CachePolicyName(value, os);
os << colors.reset;
break;
case SyscallType::kClock:
os << colors.red;
ClockName(value, os);
os << colors.reset;
break;
case SyscallType::kExceptionChannelType:
os << colors.blue;
ExceptionChannelTypeName(value, os);
os << colors.reset;
break;
case SyscallType::kExceptionState:
os << colors.blue;
ExceptionStateName(value, os);
os << colors.reset;
break;
case SyscallType::kFeatureKind:
os << colors.red;
FeatureKindName(value, os);
os << colors.reset;
break;
case SyscallType::kGuestTrap:
os << colors.red;
GuestTrapName(value, os);
os << colors.reset;
break;
case SyscallType::kHandle: {
zx_handle_info_t handle_info;
handle_info.handle = value;
handle_info.type = ZX_OBJ_TYPE_NONE;
handle_info.rights = 0;
decoder->DisplayHandle(handle_info, colors, os);
break;
}
case SyscallType::kInfoMapsType:
os << colors.red;
InfoMapsTypeName(value, os);
os << colors.reset;
break;
case SyscallType::kInterruptFlags:
os << colors.red;
InterruptFlagsName(value, os);
os << colors.reset;
break;
case SyscallType::kIommuType:
os << colors.red;
IommuTypeName(value, os);
os << colors.reset;
break;
case SyscallType::kKtraceControlAction:
os << colors.blue;
KtraceControlActionName(value, os);
os << colors.reset;
break;
case SyscallType::kObjectInfoTopic:
os << colors.blue;
TopicName(value, os);
os << colors.reset;
break;
case SyscallType::kObjProps:
os << colors.blue;
ObjPropsName(value, os);
os << colors.reset;
break;
case SyscallType::kObjType:
os << colors.blue;
fidl_codec::ObjTypeName(value, os);
os << colors.reset;
break;
case SyscallType::kPciBarType:
os << colors.blue;
PciBarTypeName(value, os);
os << colors.reset;
break;
case SyscallType::kPolicyAction:
os << colors.blue;
PolicyActionName(value, os);
os << colors.reset;
break;
case SyscallType::kPolicyCondition:
os << colors.blue;
PolicyConditionName(value, os);
os << colors.reset;
break;
case SyscallType::kPolicyTopic:
os << colors.blue;
PolicyTopicName(value, os);
os << colors.reset;
break;
case SyscallType::kPortPacketType:
os << colors.blue;
PortPacketTypeName(value, os);
os << colors.reset;
break;
case SyscallType::kProfileInfoFlags:
os << colors.blue;
ProfileInfoFlagsName(value, os);
os << colors.reset;
break;
case SyscallType::kPropType:
os << colors.blue;
PropTypeName(value, os);
os << colors.reset;
break;
case SyscallType::kRights:
os << colors.blue;
fidl_codec::RightsName(value, os);
os << colors.reset;
break;
case SyscallType::kRsrcKind:
os << colors.blue;
RsrcKindName(value, os);
os << colors.reset;
break;
case SyscallType::kSignals:
os << colors.blue;
SignalName(value, os);
os << colors.reset;
break;
case SyscallType::kSocketCreateOptions:
os << colors.blue;
SocketCreateOptionsName(value, os);
os << colors.reset;
break;
case SyscallType::kSocketReadOptions:
os << colors.blue;
SocketReadOptionsName(value, os);
os << colors.reset;
break;
case SyscallType::kSocketShutdownOptions:
os << colors.blue;
SocketShutdownOptionsName(value, os);
os << colors.reset;
break;
case SyscallType::kSystemEventType:
os << colors.blue;
SystemEventTypeName(value, os);
os << colors.reset;
break;
case SyscallType::kSystemPowerctl:
os << colors.blue;
SystemPowerctlName(value, os);
os << colors.reset;
break;
case SyscallType::kThreadState:
os << colors.blue;
ThreadStateName(value, os);
os << colors.reset;
break;
case SyscallType::kThreadStateTopic:
os << colors.blue;
ThreadStateTopicName(value, os);
os << colors.reset;
break;
case SyscallType::kTimerOption:
os << colors.blue;
TimerOptionName(value, os);
os << colors.reset;
break;
case SyscallType::kVcpu:
os << colors.red;
VcpuName(value, os);
os << colors.reset;
break;
case SyscallType::kVmOption:
os << colors.red;
VmOptionName(value, os);
os << colors.reset;
break;
case SyscallType::kVmoCreationOption:
os << colors.blue;
VmoCreationOptionName(value, os);
os << colors.reset;
break;
case SyscallType::kVmoOp:
os << colors.blue;
VmoOpName(value, os);
os << colors.reset;
break;
case SyscallType::kVmoOption:
os << colors.blue;
VmoOptionName(value, os);
os << colors.reset;
break;
case SyscallType::kVmoType:
os << colors.blue;
VmoTypeName(value, os);
os << colors.reset;
break;
default:
os << "unimplemented uint32_t value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<std::pair<const uint32_t*, int>>(SyscallDecoder* decoder,
const fidl_codec::Colors& colors,
SyscallType type,
std::pair<const uint32_t*, int> value,
std::ostream& os) {
switch (type) {
case SyscallType::kUint32ArrayDecimal:
case SyscallType::kUint32ArrayHexa: {
const char* separator = "";
for (int i = 0; i < value.second; ++i) {
os << separator;
DisplayValue(decoder, colors,
(type == SyscallType::kUint32ArrayHexa) ? SyscallType::kUint32Hexa
: SyscallType::kUint32,
value.first[i], os);
separator = ", ";
}
break;
}
default:
os << "unimplemented uint32_t array value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<uint64_t>(SyscallDecoder* /*decoder*/, const fidl_codec::Colors& colors,
SyscallType type, uint64_t value, std::ostream& os) {
switch (type) {
case SyscallType::kUint64:
os << colors.blue << value << colors.reset;
break;
case SyscallType::kUint64Hexa: {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIx64, value);
os << colors.blue << buffer.data() << colors.reset;
break;
}
#ifndef __MACH__
case SyscallType::kGpAddr: {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIx64, value);
os << colors.blue << buffer.data() << colors.reset;
break;
}
#endif
case SyscallType::kKoid:
os << colors.red << value << colors.reset;
break;
#ifndef __MACH__
case SyscallType::kSize:
os << colors.blue << value << colors.reset;
break;
#endif
case SyscallType::kTime:
os << DisplayTime(colors, value);
break;
case SyscallType::kPaddr: {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIx64, value);
os << colors.blue << buffer.data() << colors.reset;
break;
}
#ifndef __MACH__
case SyscallType::kUintptr: {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIx64, value);
os << colors.blue << buffer.data() << colors.reset;
break;
}
case SyscallType::kVaddr: {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIx64, value);
os << colors.blue << buffer.data() << colors.reset;
break;
}
#endif
default:
os << "unimplemented uint64_t value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<std::pair<const uint64_t*, int>>(SyscallDecoder* decoder,
const fidl_codec::Colors& colors,
SyscallType type,
std::pair<const uint64_t*, int> value,
std::ostream& os) {
switch (type) {
case SyscallType::kUint64ArrayDecimal:
case SyscallType::kUint64ArrayHexa: {
const char* separator = "";
for (int i = 0; i < value.second; ++i) {
os << separator;
DisplayValue(decoder, colors,
(type == SyscallType::kUint64ArrayHexa) ? SyscallType::kUint64Hexa
: SyscallType::kUint64,
value.first[i], os);
separator = ", ";
}
break;
}
default:
os << "unimplemented uint64_t array value " << static_cast<uint32_t>(type);
break;
}
}
#ifdef __MACH__
template <>
inline void DisplayValue<uintptr_t>(SyscallDecoder* /*decoder*/, const fidl_codec::Colors& colors,
SyscallType type, uintptr_t value, std::ostream& os) {
switch (type) {
case SyscallType::kGpAddr: {
std::vector<char> buffer(sizeof(uintptr_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIxPTR, value);
os << colors.blue << buffer.data() << colors.reset;
break;
}
case SyscallType::kSize:
os << colors.blue << value << colors.reset;
break;
case SyscallType::kPaddr: {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIxPTR, value);
os << colors.blue << buffer.data() << colors.reset;
break;
}
case SyscallType::kUintptr: {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIxPTR, value);
os << colors.blue << buffer.data() << colors.reset;
break;
}
case SyscallType::kVaddr: {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIxPTR, value);
os << colors.blue << buffer.data() << colors.reset;
break;
}
default:
os << "unimplemented uintptr_t value " << static_cast<uint32_t>(type);
break;
}
}
#endif
template <>
inline void DisplayValue<zx_uint128_t>(SyscallDecoder* /*decoder*/,
const fidl_codec::Colors& colors, SyscallType type,
zx_uint128_t value, std::ostream& os) {
switch (type) {
case SyscallType::kUint128Hexa: {
std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1);
snprintf(buffer.data(), buffer.size(), "%016" PRIx64, value.low);
os << colors.blue << "{ low = " << buffer.data();
snprintf(buffer.data(), buffer.size(), "%016" PRIx64, value.high);
os << ", high = " << buffer.data() << " }" << colors.reset;
break;
}
default:
os << "unimplemented zx_uint128_t value " << static_cast<uint32_t>(type);
break;
}
}
template <>
inline void DisplayValue<std::pair<const zx_uint128_t*, int>>(
SyscallDecoder* decoder, const fidl_codec::Colors& colors, SyscallType type,
std::pair<const zx_uint128_t*, int> value, std::ostream& os) {
switch (type) {
case SyscallType::kUint128ArrayHexa: {
const char* separator = "";
for (int i = 0; i < value.second; ++i) {
os << separator;
DisplayValue(decoder, colors, SyscallType::kUint128Hexa, value.first[i], os);
separator = ", ";
}
break;
}
default:
os << "unimplemented zx_uint128_t array value " << static_cast<uint32_t>(type);
break;
}
}
template <typename ClassType, typename Type>
bool ClassFieldCondition<ClassType, Type>::True(const ClassType* object, debug_ipc::Arch /*arch*/) {
return field_->get()(object) == value_;
}
template <typename ClassType, typename Type>
bool ClassFieldMaskedCondition<ClassType, Type>::True(const ClassType* object,
debug_ipc::Arch /*arch*/) {
return (field_->get()(object) & mask_) == value_;
}
template <typename ClassType, typename Type>
bool ArchCondition<ClassType, Type>::True(const ClassType* /*object*/, debug_ipc::Arch arch) {
return arch_ == arch;
}
template <typename ClassType, typename Type>
void ClassField<ClassType, Type>::Display(const ClassType* object, debug_ipc::Arch /*arch*/,
SyscallDecoder* decoder, const fidl_codec::Colors& colors,
std::string_view line_header, int tabs,
std::ostream& os) const {
os << line_header << std::string(tabs * fidl_codec::kTabSize, ' ')
<< ClassFieldBase<ClassType>::name();
DisplayType(colors, ClassFieldBase<ClassType>::syscall_type(), os);
DisplayValue<Type>(decoder, colors, ClassFieldBase<ClassType>::syscall_type(), get_(object), os);
os << '\n';
}
template <typename ClassType, typename Type>
void ArrayField<ClassType, Type>::Display(const ClassType* object, debug_ipc::Arch /*arch*/,
SyscallDecoder* decoder, const fidl_codec::Colors& colors,
std::string_view line_header, int tabs,
std::ostream& os) const {
os << line_header << std::string(tabs * fidl_codec::kTabSize, ' ')
<< ClassFieldBase<ClassType>::name();
DisplayType(colors, ClassFieldBase<ClassType>::syscall_type(), os);
os << "[]: {";
const char* separator = " ";
std::pair<const Type*, int> array = get_(object);
for (int i = 0; i < array.second; ++i) {
os << separator;
DisplayValue<Type>(decoder, colors, ClassFieldBase<ClassType>::syscall_type(), array.first[i],
os);
separator = ", ";
}
os << " }\n";
}
template <typename ClassType, typename Type>
void ClassClassField<ClassType, Type>::Display(const ClassType* object, debug_ipc::Arch arch,
SyscallDecoder* decoder,
const fidl_codec::Colors& colors,
std::string_view line_header, int tabs,
std::ostream& os) const {
os << line_header << std::string(tabs * fidl_codec::kTabSize, ' ')
<< ClassFieldBase<ClassType>::name() << ':' << colors.green << field_class_->name()
<< colors.reset << ": ";
const Type* sub_object = get_(object);
field_class_->DisplayObject(sub_object, arch, decoder, colors, line_header, tabs, os);
os << '\n';
}
template <typename ClassType, typename Type>
void ArrayClassField<ClassType, Type>::Display(const ClassType* object, debug_ipc::Arch arch,
SyscallDecoder* decoder,
const fidl_codec::Colors& colors,
std::string_view line_header, int tabs,
std::ostream& os) const {
os << line_header << std::string(tabs * fidl_codec::kTabSize, ' ')
<< ClassFieldBase<ClassType>::name() << ':' << colors.green << sub_class_->name()
<< colors.reset << "[]: {\n";
std::pair<const Type*, int> array = get_(object);
for (int i = 0; i < array.second; ++i) {
os << line_header << std::string((tabs + 1) * fidl_codec::kTabSize, ' ');
sub_class_->DisplayObject(array.first + i, arch, decoder, colors, line_header, tabs + 1, os);
os << '\n';
}
os << line_header << std::string(tabs * fidl_codec::kTabSize, ' ') << "}\n";
}
template <typename ClassType, typename Type>
void DynamicArrayClassField<ClassType, Type>::Display(const ClassType* object, debug_ipc::Arch arch,
SyscallDecoder* decoder,
const fidl_codec::Colors& colors,
std::string_view line_header, int tabs,
std::ostream& os) const {
os << line_header << std::string(tabs * fidl_codec::kTabSize, ' ')
<< ClassFieldBase<ClassType>::name() << ':' << colors.green << sub_class_->name()
<< colors.reset << "[]: {\n";
const Type* array = get_(object);
uint32_t size = get_size_(object);
for (uint32_t i = 0; i < size; ++i) {
os << line_header << std::string((tabs + 1) * fidl_codec::kTabSize, ' ');
sub_class_->DisplayObject(array + i, arch, decoder, colors, line_header, tabs + 1, os);
os << '\n';
}
os << line_header << std::string(tabs * fidl_codec::kTabSize, ' ') << "}\n";
}
template <typename Type>
std::unique_ptr<fidl_codec::Value> Access<Type>::GenerateValue(SyscallDecoder* decoder,
Stage stage) const {
if (ValueValid(decoder, stage)) {
if (GetSyscallType() == SyscallType::kHandle) {
zx_handle_info_t info;
info.handle = Value(decoder, stage);
info.type = ZX_OBJ_TYPE_NONE;
info.rights = 0;
return std::make_unique<fidl_codec::HandleValue>(info);
}
return fidlcat::GenerateValue<Type>(Value(decoder, stage));
}
return std::make_unique<fidl_codec::NullValue>();
}
template <typename Type>
void Access<Type>::Display(SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder,
Stage stage, std::string_view name, std::ostream& os) const {
const fidl_codec::Colors& colors = dispatcher->colors();
os << name;
DisplayType(colors, GetSyscallType(), os);
if (ValueValid(decoder, stage)) {
DisplayValue<Type>(decoder, colors, GetSyscallType(), Value(decoder, stage), os);
} else {
os << colors.red << "(nullptr)" << colors.reset;
}
}
template <typename Type>
const char* SyscallInputOutputActualAndRequested<Type>::DisplayInline(
SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder, Stage stage,
const char* separator, std::ostream& os) const {
os << separator;
actual_->Display(dispatcher, decoder, stage, name(), os);
os << "/";
const fidl_codec::Colors& colors = dispatcher->colors();
if (asked_->ValueValid(decoder, stage)) {
DisplayValue<Type>(decoder, colors, asked_->GetSyscallType(), asked_->Value(decoder, stage),
os);
} else {
os << colors.red << "(nullptr)" << colors.reset;
}
return ", ";
}
template <typename Type, typename FromType>
const char* SyscallInputOutputIndirect<Type, FromType>::DisplayInline(
SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder, Stage stage,
const char* separator, std::ostream& os) const {
os << separator << name();
const fidl_codec::Colors& colors = dispatcher->colors();
DisplayType(colors, syscall_type_, os);
const FromType* buffer = buffer_->Content(decoder, stage);
if (buffer == nullptr) {
os << colors.red << "nullptr" << colors.reset;
} else {
DisplayValue<Type>(decoder, colors, syscall_type_, *reinterpret_cast<const Type*>(buffer), os);
}
return ", ";
}
template <typename Type, typename FromType, typename SizeType>
void SyscallInputOutputBuffer<Type, FromType, SizeType>::DisplayOutline(
SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder, Stage stage,
std::string_view line_header, int tabs, std::ostream& os) const {
os << line_header << std::string((tabs + 1) * fidl_codec::kTabSize, ' ') << name();
const fidl_codec::Colors& colors = dispatcher->colors();
DisplayType(colors, syscall_type_, os);
const FromType* buffer = buffer_->Content(decoder, stage);
if (buffer == nullptr) {
os << colors.red << "nullptr" << colors.reset;
} else {
size_t buffer_size = elem_size_->Value(decoder, stage);
if (elem_count_ != nullptr) {
buffer_size *= elem_count_->Value(decoder, stage);
}
if (buffer_size == 0) {
os << "empty\n";
return;
}
const char* separator = "";
for (size_t i = 0; i < buffer_size; ++i) {
os << separator;
DisplayValue<Type>(decoder, colors, syscall_type_, reinterpret_cast<const Type*>(buffer)[i],
os);
separator = ", ";
}
}
os << '\n';
}
template <>
inline void SyscallInputOutputBuffer<uint8_t, uint8_t, size_t>::DisplayOutline(
SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder, Stage stage,
std::string_view line_header, int tabs, std::ostream& os) const {
os << line_header << std::string((tabs + 1) * fidl_codec::kTabSize, ' ') << name();
const fidl_codec::Colors& colors = dispatcher->colors();
DisplayType(colors, syscall_type_, os);
const uint8_t* buffer = buffer_->Content(decoder, stage);
if (buffer == nullptr) {
os << colors.red << "nullptr" << colors.reset;
} else {
size_t buffer_size = elem_size_->Value(decoder, stage);
if (elem_count_ != nullptr) {
buffer_size *= elem_count_->Value(decoder, stage);
}
if (buffer_size == 0) {
os << "empty\n";
return;
}
for (size_t i = 0;; ++i) {
if (i == buffer_size) {
os << colors.red << '"';
for (size_t i = 0; i < buffer_size; ++i) {
char value = reinterpret_cast<const char*>(buffer)[i];
switch (value) {
case 0:
break;
case '\\':
os << "\\\\";
break;
case '\n':
os << "\\n";
break;
default:
os << value;
break;
}
}
os << '"' << colors.reset << '\n';
return;
}
if ((buffer[i] == 0) && (i != buffer_size - 1)) {
break;
}
if (!std::isprint(buffer[i]) && (buffer[i] != '\n')) {
break;
}
}
const char* separator = "";
for (size_t i = 0; i < buffer_size; ++i) {
os << separator;
DisplayValue<uint8_t>(decoder, colors, buffer_->GetSyscallType(), buffer[i], os);
separator = ", ";
}
}
os << '\n';
}
template <typename FromType>
const char* SyscallInputOutputString<FromType>::DisplayInline(SyscallDisplayDispatcher* dispatcher,
SyscallDecoder* decoder, Stage stage,
const char* separator,
std::ostream& os) const {
const fidl_codec::Colors& colors = dispatcher->colors();
os << separator;
os << name() << ':' << colors.green << "string" << colors.reset << ": ";
const char* string = reinterpret_cast<const char*>(string_->Content(decoder, stage));
size_t string_size = string_size_->Value(decoder, stage);
DisplayString(colors, string, string_size, os);
return ", ";
}
template <typename ClassType, typename SizeType>
void SyscallInputOutputObject<ClassType, SizeType>::DisplayOutline(
SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder, Stage stage,
std::string_view line_header, int tabs, std::ostream& os) const {
const fidl_codec::Colors& colors = dispatcher->colors();
os << line_header << std::string((tabs + 1) * fidl_codec::kTabSize, ' ') << name() << ":"
<< colors.green << class_definition_->name() << colors.reset << ": ";
auto object = reinterpret_cast<const ClassType*>(buffer_->Uint8Content(decoder, stage));
if (object == nullptr) {
os << colors.red << "nullptr" << colors.reset;
} else {
class_definition_->DisplayObject(object, decoder->arch(), decoder, colors, line_header,
tabs + 1, os);
}
os << '\n';
}
template <typename ClassType, typename SizeType>
void SyscallInputOutputObjectArray<ClassType, SizeType>::DisplayOutline(
SyscallDisplayDispatcher* dispatcher, SyscallDecoder* decoder, Stage stage,
std::string_view line_header, int tabs, std::ostream& os) const {
const fidl_codec::Colors& colors = dispatcher->colors();
os << line_header << std::string((tabs + 1) * fidl_codec::kTabSize, ' ') << name() << ":"
<< colors.green << class_definition_->name() << colors.reset << "[]: ";
auto object = reinterpret_cast<const ClassType*>(buffer_->Uint8Content(decoder, stage));
if (object == nullptr) {
os << colors.red << "nullptr" << colors.reset;
} else {
os << " {";
SizeType count = buffer_size_->Value(decoder, stage);
const char* separator = "\n";
for (SizeType i = 0; i < count; ++i) {
os << separator << line_header << std::string((tabs + 2) * fidl_codec::kTabSize, ' ');
class_definition_->DisplayObject(object + i, decoder->arch(), decoder, colors, line_header,
tabs + 2, os);
separator = ",\n";
}
os << '\n' << line_header << std::string((tabs + 1) * fidl_codec::kTabSize, ' ') << '}';
}
os << '\n';
}
} // namespace fidlcat
#endif // TOOLS_FIDLCAT_LIB_SYSCALL_DECODER_DISPATCHER_H_