/ src / common / telemetry.h
telemetry.h
  1  // Copyright 2017 Citra Emulator Project
  2  // Licensed under GPLv2 or any later version
  3  // Refer to the license.txt file included.
  4  
  5  #pragma once
  6  
  7  #include <chrono>
  8  #include <map>
  9  #include <memory>
 10  #include <string>
 11  #include "common/common_types.h"
 12  
 13  namespace Common::Telemetry {
 14  
 15  /// Field type, used for grouping fields together in the final submitted telemetry log
 16  enum class FieldType : u8 {
 17      None = 0,     ///< No specified field group
 18      App,          ///< Citra application fields (e.g. version, branch, etc.)
 19      Session,      ///< Emulated session fields (e.g. title ID, log, etc.)
 20      Performance,  ///< Emulated performance (e.g. fps, emulated CPU speed, etc.)
 21      UserFeedback, ///< User submitted feedback (e.g. star rating, user notes, etc.)
 22      UserConfig,   ///< User configuration fields (e.g. emulated CPU core, renderer, etc.)
 23      UserSystem,   ///< User system information (e.g. host CPU type, RAM, etc.)
 24  };
 25  
 26  struct VisitorInterface;
 27  
 28  /**
 29   * Interface class for telemetry data fields.
 30   */
 31  class FieldInterface : NonCopyable {
 32  public:
 33      virtual ~FieldInterface() = default;
 34  
 35      /**
 36       * Accept method for the visitor pattern.
 37       * @param visitor Reference to the visitor that will visit this field.
 38       */
 39      virtual void Accept(VisitorInterface& visitor) const = 0;
 40  
 41      /**
 42       * Gets the name of this field.
 43       * @returns Name of this field as a string.
 44       */
 45      virtual const std::string& GetName() const = 0;
 46  };
 47  
 48  /**
 49   * Represents a telemetry data field, i.e. a unit of data that gets logged and submitted to our
 50   * telemetry web service.
 51   */
 52  template <typename T>
 53  class Field : public FieldInterface {
 54  public:
 55      Field(FieldType type, std::string name, T value)
 56          : name(std::move(name)), type(type), value(std::move(value)) {}
 57  
 58      Field(const Field&) = default;
 59      Field& operator=(const Field&) = default;
 60  
 61      Field(Field&&) = default;
 62      Field& operator=(Field&& other) = default;
 63  
 64      void Accept(VisitorInterface& visitor) const override;
 65  
 66      [[nodiscard]] const std::string& GetName() const override {
 67          return name;
 68      }
 69  
 70      /**
 71       * Returns the type of the field.
 72       */
 73      [[nodiscard]] FieldType GetType() const {
 74          return type;
 75      }
 76  
 77      /**
 78       * Returns the value of the field.
 79       */
 80      [[nodiscard]] const T& GetValue() const {
 81          return value;
 82      }
 83  
 84      [[nodiscard]] bool operator==(const Field& other) const {
 85          return (type == other.type) && (name == other.name) && (value == other.value);
 86      }
 87  
 88      [[nodiscard]] bool operator!=(const Field& other) const {
 89          return !operator==(other);
 90      }
 91  
 92  private:
 93      std::string name; ///< Field name, must be unique
 94      FieldType type{}; ///< Field type, used for grouping fields together
 95      T value;          ///< Field value
 96  };
 97  
 98  /**
 99   * Collection of data fields that have been logged.
100   */
101  class FieldCollection final : NonCopyable {
102  public:
103      FieldCollection() = default;
104  
105      /**
106       * Accept method for the visitor pattern, visits each field in the collection.
107       * @param visitor Reference to the visitor that will visit each field.
108       */
109      void Accept(VisitorInterface& visitor) const;
110  
111      /**
112       * Creates a new field and adds it to the field collection.
113       * @param type Type of the field to add.
114       * @param name Name of the field to add.
115       * @param value Value for the field to add.
116       */
117      template <typename T>
118      void AddField(FieldType type, const char* name, T value) {
119          return AddField(std::make_unique<Field<T>>(type, name, std::move(value)));
120      }
121  
122      /**
123       * Adds a new field to the field collection.
124       * @param field Field to add to the field collection.
125       */
126      void AddField(std::unique_ptr<FieldInterface> field);
127  
128  private:
129      std::map<std::string, std::unique_ptr<FieldInterface>> fields;
130  };
131  
132  /**
133   * Telemetry fields visitor interface class. A backend to log to a web service should implement
134   * this interface.
135   */
136  struct VisitorInterface : NonCopyable {
137      virtual ~VisitorInterface() = default;
138  
139      virtual void Visit(const Field<bool>& field) = 0;
140      virtual void Visit(const Field<double>& field) = 0;
141      virtual void Visit(const Field<float>& field) = 0;
142      virtual void Visit(const Field<u8>& field) = 0;
143      virtual void Visit(const Field<u16>& field) = 0;
144      virtual void Visit(const Field<u32>& field) = 0;
145      virtual void Visit(const Field<u64>& field) = 0;
146      virtual void Visit(const Field<s8>& field) = 0;
147      virtual void Visit(const Field<s16>& field) = 0;
148      virtual void Visit(const Field<s32>& field) = 0;
149      virtual void Visit(const Field<s64>& field) = 0;
150      virtual void Visit(const Field<std::string>& field) = 0;
151      virtual void Visit(const Field<const char*>& field) = 0;
152      virtual void Visit(const Field<std::chrono::microseconds>& field) = 0;
153  
154      /// Completion method, called once all fields have been visited
155      virtual void Complete() = 0;
156      virtual bool SubmitTestcase() = 0;
157  };
158  
159  /**
160   * Empty implementation of VisitorInterface that drops all fields. Used when a functional
161   * backend implementation is not available.
162   */
163  struct NullVisitor : public VisitorInterface {
164      ~NullVisitor() = default;
165  
166      void Visit(const Field<bool>& /*field*/) override {}
167      void Visit(const Field<double>& /*field*/) override {}
168      void Visit(const Field<float>& /*field*/) override {}
169      void Visit(const Field<u8>& /*field*/) override {}
170      void Visit(const Field<u16>& /*field*/) override {}
171      void Visit(const Field<u32>& /*field*/) override {}
172      void Visit(const Field<u64>& /*field*/) override {}
173      void Visit(const Field<s8>& /*field*/) override {}
174      void Visit(const Field<s16>& /*field*/) override {}
175      void Visit(const Field<s32>& /*field*/) override {}
176      void Visit(const Field<s64>& /*field*/) override {}
177      void Visit(const Field<std::string>& /*field*/) override {}
178      void Visit(const Field<const char*>& /*field*/) override {}
179      void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
180  
181      void Complete() override {}
182      bool SubmitTestcase() override {
183          return false;
184      }
185  };
186  
187  /// Appends build-specific information to the given FieldCollection,
188  /// such as branch name, revision hash, etc.
189  void AppendBuildInfo(FieldCollection& fc);
190  
191  /// Appends CPU-specific information to the given FieldCollection,
192  /// such as instruction set extensions, etc.
193  void AppendCPUInfo(FieldCollection& fc);
194  
195  /// Appends OS-specific information to the given FieldCollection,
196  /// such as platform name, etc.
197  void AppendOSInfo(FieldCollection& fc);
198  
199  } // namespace Common::Telemetry