123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- // Copyright 2016 Dolphin Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include "Common/Analytics.h"
- #include <cmath>
- #include <cstdio>
- #include <string>
- #include <type_traits>
- #include "Common/CommonTypes.h"
- #include "Common/StringUtil.h"
- #include "Common/Thread.h"
- namespace Common
- {
- namespace
- {
- // Format version number, used as the first byte of every report sent.
- // Increment for any change to the wire format.
- constexpr u8 WIRE_FORMAT_VERSION = 1;
- // Identifiers for the value types supported by the analytics reporting wire
- // format.
- enum class TypeId : u8
- {
- STRING = 0,
- BOOL = 1,
- UINT = 2,
- SINT = 3,
- FLOAT = 4,
- // Flags which can be combined with other types.
- ARRAY = 0x80,
- };
- TypeId operator|(TypeId l, TypeId r)
- {
- using ut = std::underlying_type_t<TypeId>;
- return static_cast<TypeId>(static_cast<ut>(l) | static_cast<ut>(r));
- }
- void AppendBool(std::string* out, bool v)
- {
- out->push_back(v ? '\xFF' : '\x00');
- }
- void AppendVarInt(std::string* out, u64 v)
- {
- do
- {
- u8 current_byte = v & 0x7F;
- v >>= 7;
- current_byte |= (!!v) << 7;
- out->push_back(current_byte);
- } while (v);
- }
- void AppendBytes(std::string* out, const u8* bytes, u32 length, bool encode_length = true)
- {
- if (encode_length)
- {
- AppendVarInt(out, length);
- }
- out->append(reinterpret_cast<const char*>(bytes), length);
- }
- void AppendType(std::string* out, TypeId type)
- {
- out->push_back(static_cast<u8>(type));
- }
- } // namespace
- AnalyticsReportBuilder::AnalyticsReportBuilder()
- {
- m_report.push_back(WIRE_FORMAT_VERSION);
- }
- void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, std::string_view v)
- {
- AppendType(report, TypeId::STRING);
- AppendBytes(report, reinterpret_cast<const u8*>(v.data()), static_cast<u32>(v.size()));
- }
- // We can't remove this overload despite the string_view overload due to the fact that
- // pointers can implicitly convert to bool, so if we removed the overload, then all
- // const char strings passed in would begin forwarding to the bool overload,
- // which is definitely not what we want to occur.
- void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, const char* v)
- {
- AppendSerializedValue(report, std::string_view(v));
- }
- void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, bool v)
- {
- AppendType(report, TypeId::BOOL);
- AppendBool(report, v);
- }
- void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, u64 v)
- {
- AppendType(report, TypeId::UINT);
- AppendVarInt(report, v);
- }
- void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, s64 v)
- {
- AppendType(report, TypeId::SINT);
- AppendBool(report, v >= 0);
- AppendVarInt(report, static_cast<u64>(std::abs(v)));
- }
- void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, u32 v)
- {
- AppendSerializedValue(report, static_cast<u64>(v));
- }
- void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, s32 v)
- {
- AppendSerializedValue(report, static_cast<s64>(v));
- }
- void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, float v)
- {
- AppendType(report, TypeId::FLOAT);
- AppendBytes(report, reinterpret_cast<u8*>(&v), sizeof(v), false);
- }
- void AnalyticsReportBuilder::AppendSerializedValueVector(std::string* report,
- const std::vector<u32>& v)
- {
- AppendType(report, TypeId::UINT | TypeId::ARRAY);
- AppendVarInt(report, v.size());
- for (u32 x : v)
- AppendVarInt(report, x);
- }
- AnalyticsReporter::AnalyticsReporter()
- {
- m_reporter_thread = std::thread(&AnalyticsReporter::ThreadProc, this);
- }
- AnalyticsReporter::~AnalyticsReporter()
- {
- // Set the exit request flag and wait for the thread to honor it.
- m_reporter_stop_request.Set();
- m_reporter_event.Set();
- m_reporter_thread.join();
- }
- void AnalyticsReporter::Send(AnalyticsReportBuilder&& report)
- {
- #if defined(USE_ANALYTICS) && USE_ANALYTICS
- // Put a bound on the size of the queue to avoid uncontrolled memory growth.
- constexpr u32 QUEUE_SIZE_LIMIT = 25;
- if (m_reports_queue.Size() < QUEUE_SIZE_LIMIT)
- {
- m_reports_queue.Push(report.Consume());
- m_reporter_event.Set();
- }
- #endif
- }
- void AnalyticsReporter::ThreadProc()
- {
- Common::SetCurrentThreadName("Analytics");
- while (true)
- {
- m_reporter_event.Wait();
- if (m_reporter_stop_request.IsSet())
- {
- return;
- }
- while (!m_reports_queue.Empty())
- {
- std::shared_ptr<AnalyticsReportingBackend> backend(m_backend);
- if (backend)
- {
- std::string report;
- m_reports_queue.Pop(report);
- backend->Send(std::move(report));
- }
- else
- {
- break;
- }
- // Recheck after each report sent.
- if (m_reporter_stop_request.IsSet())
- {
- return;
- }
- }
- }
- }
- void StdoutAnalyticsBackend::Send(std::string report)
- {
- printf("Analytics report sent:\n%s",
- HexDump(reinterpret_cast<const u8*>(report.data()), report.size()).c_str());
- }
- HttpAnalyticsBackend::HttpAnalyticsBackend(std::string endpoint) : m_endpoint(std::move(endpoint))
- {
- }
- HttpAnalyticsBackend::~HttpAnalyticsBackend() = default;
- void HttpAnalyticsBackend::Send(std::string report)
- {
- if (m_http.IsValid())
- m_http.Post(m_endpoint, report);
- }
- } // namespace Common
|