123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- // Copyright 2016 Dolphin Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #pragma once
- #include <chrono>
- #include <memory>
- #include <shared_mutex>
- #include <string>
- #include <string_view>
- #include <thread>
- #include <utility>
- #include <vector>
- #include "Common/CommonTypes.h"
- #include "Common/Event.h"
- #include "Common/Flag.h"
- #include "Common/HttpRequest.h"
- #include "Common/SPSCQueue.h"
- // Utilities for analytics reporting in Dolphin. This reporting is designed to
- // provide anonymous data about how well Dolphin performs in the wild. It also
- // allows developers to declare trace points in Dolphin's source code and get
- // information about what games trigger these conditions.
- //
- // This unfortunately implements Yet Another Serialization Framework within
- // Dolphin. We cannot really use ChunkFile because there is precedents for
- // backwards incompatible changes in the ChunkFile format. We could use
- // something like protobuf but setting up external dependencies is Hard™.
- //
- // Example usage:
- //
- // static auto s_reporter = std::make_unique<AnalyticsReporter>();
- // if (user_gave_consent)
- // {
- // s_reporter->SetBackend(std::make_unique<MyReportingBackend>());
- // }
- // s_reporter->Send(s_reporter->Builder()
- // .AddData("my_key", 42)
- // .AddData("other_key", false));
- namespace Common
- {
- // Generic interface for an analytics reporting backends. The main
- // implementation used in Dolphin can be found in Core/DolphinAnalytics.h.
- class AnalyticsReportingBackend
- {
- public:
- virtual ~AnalyticsReportingBackend() = default;
- // Called from the AnalyticsReporter backend thread.
- virtual void Send(std::string report) = 0;
- };
- // Builder object for an analytics report.
- class AnalyticsReportBuilder
- {
- public:
- AnalyticsReportBuilder();
- ~AnalyticsReportBuilder() = default;
- AnalyticsReportBuilder(const AnalyticsReportBuilder& other) : m_report{other.Get()} {}
- AnalyticsReportBuilder(AnalyticsReportBuilder&& other) : m_report{other.Consume()} {}
- const AnalyticsReportBuilder& operator=(const AnalyticsReportBuilder& other)
- {
- if (this != &other)
- {
- std::string other_report = other.Get();
- std::lock_guard lk{m_lock};
- m_report = std::move(other_report);
- }
- return *this;
- }
- // Append another builder to this one.
- AnalyticsReportBuilder& AddBuilder(const AnalyticsReportBuilder& other)
- {
- // Get before locking the object to avoid deadlocks with this += this.
- std::string other_report = other.Get();
- std::lock_guard lk{m_lock};
- m_report += other_report;
- return *this;
- }
- template <typename T>
- AnalyticsReportBuilder& AddData(std::string_view key, const T& value)
- {
- std::lock_guard lk{m_lock};
- AppendSerializedValue(&m_report, key);
- AppendSerializedValue(&m_report, value);
- return *this;
- }
- template <typename T>
- AnalyticsReportBuilder& AddData(std::string_view key, const std::vector<T>& value)
- {
- std::lock_guard lk{m_lock};
- AppendSerializedValue(&m_report, key);
- AppendSerializedValueVector(&m_report, value);
- return *this;
- }
- std::string Get() const
- {
- std::shared_lock lk{m_lock};
- return m_report;
- }
- // More efficient version of Get().
- std::string Consume()
- {
- std::lock_guard lk{m_lock};
- return std::move(m_report);
- }
- protected:
- static void AppendSerializedValue(std::string* report, std::string_view v);
- static void AppendSerializedValue(std::string* report, const char* v);
- static void AppendSerializedValue(std::string* report, bool v);
- static void AppendSerializedValue(std::string* report, u64 v);
- static void AppendSerializedValue(std::string* report, s64 v);
- static void AppendSerializedValue(std::string* report, u32 v);
- static void AppendSerializedValue(std::string* report, s32 v);
- static void AppendSerializedValue(std::string* report, float v);
- static void AppendSerializedValueVector(std::string* report, const std::vector<u32>& v);
- mutable std::shared_mutex m_lock;
- std::string m_report;
- };
- class AnalyticsReporter
- {
- public:
- AnalyticsReporter();
- ~AnalyticsReporter();
- // Sets a reporting backend and enables sending reports. Do not set a remote
- // backend without user consent.
- void SetBackend(std::unique_ptr<AnalyticsReportingBackend> backend)
- {
- m_backend = std::move(backend);
- m_reporter_event.Set(); // In case reports are waiting queued.
- }
- // Gets the base report builder which is closed for each subsequent report
- // being sent. DO NOT use this builder to send a report. Only use it to add
- // new fields that should be globally available.
- AnalyticsReportBuilder& BaseBuilder() { return m_base_builder; }
- // Gets a cloned builder that can be used to send a report.
- AnalyticsReportBuilder Builder() const { return m_base_builder; }
- // Enqueues a report for sending. Consumes the report builder.
- void Send(AnalyticsReportBuilder&& report);
- // For convenience.
- void Send(AnalyticsReportBuilder& report) { Send(std::move(report)); }
- protected:
- void ThreadProc();
- std::shared_ptr<AnalyticsReportingBackend> m_backend;
- AnalyticsReportBuilder m_base_builder;
- std::thread m_reporter_thread;
- Common::Event m_reporter_event;
- Common::Flag m_reporter_stop_request;
- SPSCQueue<std::string> m_reports_queue;
- };
- // Analytics backend to be used for debugging purpose, which dumps reports to
- // stdout.
- class StdoutAnalyticsBackend : public AnalyticsReportingBackend
- {
- public:
- void Send(std::string report) override;
- };
- // Analytics backend that POSTs data to a remote HTTP(s) endpoint. WARNING:
- // remember to get explicit user consent before using.
- class HttpAnalyticsBackend : public AnalyticsReportingBackend
- {
- public:
- explicit HttpAnalyticsBackend(std::string endpoint);
- ~HttpAnalyticsBackend() override;
- void Send(std::string report) override;
- protected:
- std::string m_endpoint;
- HttpRequest m_http{std::chrono::seconds{5}};
- };
- } // namespace Common
|