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:
Field(FieldType type,std::string name,T value)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 
GetName()66     [[nodiscard]] const std::string& GetName() const override {
67         return name;
68     }
69 
70     /**
71      * Returns the type of the field.
72      */
GetType()73     [[nodiscard]] FieldType GetType() const {
74         return type;
75     }
76 
77     /**
78      * Returns the value of the field.
79      */
GetValue()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>
AddField(FieldType type,const char * name,T value)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 
VisitNullVisitor166     void Visit(const Field<bool>& /*field*/) override {}
VisitNullVisitor167     void Visit(const Field<double>& /*field*/) override {}
VisitNullVisitor168     void Visit(const Field<float>& /*field*/) override {}
VisitNullVisitor169     void Visit(const Field<u8>& /*field*/) override {}
VisitNullVisitor170     void Visit(const Field<u16>& /*field*/) override {}
VisitNullVisitor171     void Visit(const Field<u32>& /*field*/) override {}
VisitNullVisitor172     void Visit(const Field<u64>& /*field*/) override {}
VisitNullVisitor173     void Visit(const Field<s8>& /*field*/) override {}
VisitNullVisitor174     void Visit(const Field<s16>& /*field*/) override {}
VisitNullVisitor175     void Visit(const Field<s32>& /*field*/) override {}
VisitNullVisitor176     void Visit(const Field<s64>& /*field*/) override {}
VisitNullVisitor177     void Visit(const Field<std::string>& /*field*/) override {}
VisitNullVisitor178     void Visit(const Field<const char*>& /*field*/) override {}
VisitNullVisitor179     void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
180 
CompleteNullVisitor181     void Complete() override {}
SubmitTestcaseNullVisitor182     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
200