Analytics.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Copyright 2016 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "Common/Analytics.h"
  4. #include <cmath>
  5. #include <cstdio>
  6. #include <string>
  7. #include <type_traits>
  8. #include "Common/CommonTypes.h"
  9. #include "Common/StringUtil.h"
  10. #include "Common/Thread.h"
  11. namespace Common
  12. {
  13. namespace
  14. {
  15. // Format version number, used as the first byte of every report sent.
  16. // Increment for any change to the wire format.
  17. constexpr u8 WIRE_FORMAT_VERSION = 1;
  18. // Identifiers for the value types supported by the analytics reporting wire
  19. // format.
  20. enum class TypeId : u8
  21. {
  22. STRING = 0,
  23. BOOL = 1,
  24. UINT = 2,
  25. SINT = 3,
  26. FLOAT = 4,
  27. // Flags which can be combined with other types.
  28. ARRAY = 0x80,
  29. };
  30. TypeId operator|(TypeId l, TypeId r)
  31. {
  32. using ut = std::underlying_type_t<TypeId>;
  33. return static_cast<TypeId>(static_cast<ut>(l) | static_cast<ut>(r));
  34. }
  35. void AppendBool(std::string* out, bool v)
  36. {
  37. out->push_back(v ? '\xFF' : '\x00');
  38. }
  39. void AppendVarInt(std::string* out, u64 v)
  40. {
  41. do
  42. {
  43. u8 current_byte = v & 0x7F;
  44. v >>= 7;
  45. current_byte |= (!!v) << 7;
  46. out->push_back(current_byte);
  47. } while (v);
  48. }
  49. void AppendBytes(std::string* out, const u8* bytes, u32 length, bool encode_length = true)
  50. {
  51. if (encode_length)
  52. {
  53. AppendVarInt(out, length);
  54. }
  55. out->append(reinterpret_cast<const char*>(bytes), length);
  56. }
  57. void AppendType(std::string* out, TypeId type)
  58. {
  59. out->push_back(static_cast<u8>(type));
  60. }
  61. } // namespace
  62. AnalyticsReportBuilder::AnalyticsReportBuilder()
  63. {
  64. m_report.push_back(WIRE_FORMAT_VERSION);
  65. }
  66. void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, std::string_view v)
  67. {
  68. AppendType(report, TypeId::STRING);
  69. AppendBytes(report, reinterpret_cast<const u8*>(v.data()), static_cast<u32>(v.size()));
  70. }
  71. // We can't remove this overload despite the string_view overload due to the fact that
  72. // pointers can implicitly convert to bool, so if we removed the overload, then all
  73. // const char strings passed in would begin forwarding to the bool overload,
  74. // which is definitely not what we want to occur.
  75. void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, const char* v)
  76. {
  77. AppendSerializedValue(report, std::string_view(v));
  78. }
  79. void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, bool v)
  80. {
  81. AppendType(report, TypeId::BOOL);
  82. AppendBool(report, v);
  83. }
  84. void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, u64 v)
  85. {
  86. AppendType(report, TypeId::UINT);
  87. AppendVarInt(report, v);
  88. }
  89. void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, s64 v)
  90. {
  91. AppendType(report, TypeId::SINT);
  92. AppendBool(report, v >= 0);
  93. AppendVarInt(report, static_cast<u64>(std::abs(v)));
  94. }
  95. void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, u32 v)
  96. {
  97. AppendSerializedValue(report, static_cast<u64>(v));
  98. }
  99. void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, s32 v)
  100. {
  101. AppendSerializedValue(report, static_cast<s64>(v));
  102. }
  103. void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, float v)
  104. {
  105. AppendType(report, TypeId::FLOAT);
  106. AppendBytes(report, reinterpret_cast<u8*>(&v), sizeof(v), false);
  107. }
  108. void AnalyticsReportBuilder::AppendSerializedValueVector(std::string* report,
  109. const std::vector<u32>& v)
  110. {
  111. AppendType(report, TypeId::UINT | TypeId::ARRAY);
  112. AppendVarInt(report, v.size());
  113. for (u32 x : v)
  114. AppendVarInt(report, x);
  115. }
  116. AnalyticsReporter::AnalyticsReporter()
  117. {
  118. m_reporter_thread = std::thread(&AnalyticsReporter::ThreadProc, this);
  119. }
  120. AnalyticsReporter::~AnalyticsReporter()
  121. {
  122. // Set the exit request flag and wait for the thread to honor it.
  123. m_reporter_stop_request.Set();
  124. m_reporter_event.Set();
  125. m_reporter_thread.join();
  126. }
  127. void AnalyticsReporter::Send(AnalyticsReportBuilder&& report)
  128. {
  129. #if defined(USE_ANALYTICS) && USE_ANALYTICS
  130. // Put a bound on the size of the queue to avoid uncontrolled memory growth.
  131. constexpr u32 QUEUE_SIZE_LIMIT = 25;
  132. if (m_reports_queue.Size() < QUEUE_SIZE_LIMIT)
  133. {
  134. m_reports_queue.Push(report.Consume());
  135. m_reporter_event.Set();
  136. }
  137. #endif
  138. }
  139. void AnalyticsReporter::ThreadProc()
  140. {
  141. Common::SetCurrentThreadName("Analytics");
  142. while (true)
  143. {
  144. m_reporter_event.Wait();
  145. if (m_reporter_stop_request.IsSet())
  146. {
  147. return;
  148. }
  149. while (!m_reports_queue.Empty())
  150. {
  151. std::shared_ptr<AnalyticsReportingBackend> backend(m_backend);
  152. if (backend)
  153. {
  154. std::string report;
  155. m_reports_queue.Pop(report);
  156. backend->Send(std::move(report));
  157. }
  158. else
  159. {
  160. break;
  161. }
  162. // Recheck after each report sent.
  163. if (m_reporter_stop_request.IsSet())
  164. {
  165. return;
  166. }
  167. }
  168. }
  169. }
  170. void StdoutAnalyticsBackend::Send(std::string report)
  171. {
  172. printf("Analytics report sent:\n%s",
  173. HexDump(reinterpret_cast<const u8*>(report.data()), report.size()).c_str());
  174. }
  175. HttpAnalyticsBackend::HttpAnalyticsBackend(std::string endpoint) : m_endpoint(std::move(endpoint))
  176. {
  177. }
  178. HttpAnalyticsBackend::~HttpAnalyticsBackend() = default;
  179. void HttpAnalyticsBackend::Send(std::string report)
  180. {
  181. if (m_http.IsValid())
  182. m_http.Post(m_endpoint, report);
  183. }
  184. } // namespace Common