// SPDX-FileCopyrightText: 2017 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include #include "common/common_funcs.h" #include "common/common_types.h" namespace Common::Telemetry { /// Field type, used for grouping fields together in the final submitted telemetry log enum class FieldType : u8 { None = 0, ///< No specified field group App, ///< yuzu application fields (e.g. version, branch, etc.) Session, ///< Emulated session fields (e.g. title ID, log, etc.) Performance, ///< Emulated performance (e.g. fps, emulated CPU speed, etc.) UserFeedback, ///< User submitted feedback (e.g. star rating, user notes, etc.) UserConfig, ///< User configuration fields (e.g. emulated CPU core, renderer, etc.) UserSystem, ///< User system information (e.g. host CPU type, RAM, etc.) }; struct VisitorInterface; /** * Interface class for telemetry data fields. */ class FieldInterface { public: virtual ~FieldInterface() = default; /** * Accept method for the visitor pattern. * @param visitor Reference to the visitor that will visit this field. */ virtual void Accept(VisitorInterface& visitor) const = 0; /** * Gets the name of this field. * @returns Name of this field as a string. */ virtual const std::string& GetName() const = 0; }; /** * Represents a telemetry data field, i.e. a unit of data that gets logged and submitted to our * telemetry web service. */ template class Field : public FieldInterface { public: YUZU_NON_COPYABLE(Field); Field(FieldType type_, std::string_view name_, T value_) : name(name_), type(type_), value(std::move(value_)) {} ~Field() override = default; Field(Field&&) noexcept = default; Field& operator=(Field&& other) noexcept = default; void Accept(VisitorInterface& visitor) const override; [[nodiscard]] const std::string& GetName() const override { return name; } /** * Returns the type of the field. */ [[nodiscard]] FieldType GetType() const { return type; } /** * Returns the value of the field. */ [[nodiscard]] const T& GetValue() const { return value; } [[nodiscard]] bool operator==(const Field& other) const { return (type == other.type) && (name == other.name) && (value == other.value); } [[nodiscard]] bool operator!=(const Field& other) const { return !operator==(other); } private: std::string name; ///< Field name, must be unique FieldType type{}; ///< Field type, used for grouping fields together T value; ///< Field value }; /** * Collection of data fields that have been logged. */ class FieldCollection final { public: YUZU_NON_COPYABLE(FieldCollection); FieldCollection() = default; ~FieldCollection() = default; FieldCollection(FieldCollection&&) noexcept = default; FieldCollection& operator=(FieldCollection&&) noexcept = default; /** * Accept method for the visitor pattern, visits each field in the collection. * @param visitor Reference to the visitor that will visit each field. */ void Accept(VisitorInterface& visitor) const; /** * Creates a new field and adds it to the field collection. * @param type Type of the field to add. * @param name Name of the field to add. * @param value Value for the field to add. */ template void AddField(FieldType type, std::string_view name, T value) { return AddField(std::make_unique>(type, name, std::move(value))); } /** * Adds a new field to the field collection. * @param field Field to add to the field collection. */ void AddField(std::unique_ptr field); private: std::map> fields; }; /** * Telemetry fields visitor interface class. A backend to log to a web service should implement * this interface. */ struct VisitorInterface { virtual ~VisitorInterface() = default; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; virtual void Visit(const Field& field) = 0; /// Completion method, called once all fields have been visited virtual void Complete() = 0; virtual bool SubmitTestcase() = 0; }; /** * Empty implementation of VisitorInterface that drops all fields. Used when a functional * backend implementation is not available. */ struct NullVisitor final : public VisitorInterface { YUZU_NON_COPYABLE(NullVisitor); NullVisitor() = default; ~NullVisitor() override = default; void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Visit(const Field& /*field*/) override {} void Complete() override {} bool SubmitTestcase() override { return false; } }; /// Appends build-specific information to the given FieldCollection, /// such as branch name, revision hash, etc. void AppendBuildInfo(FieldCollection& fc); /// Appends CPU-specific information to the given FieldCollection, /// such as instruction set extensions, etc. void AppendCPUInfo(FieldCollection& fc); /// Appends OS-specific information to the given FieldCollection, /// such as platform name, etc. void AppendOSInfo(FieldCollection& fc); } // namespace Common::Telemetry