mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Add an Analytics reporting system.
Fully opt-in, reports to analytics.dolphin-emu.org over SSL. Collects system information and settings at Dolphin start time and game start time. UI not implemented yet, so users are required to opt in through config editing.
This commit is contained in:
238
Source/Core/Core/Analytics.cpp
Normal file
238
Source/Core/Core/Analytics.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <mbedtls/sha1.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
#include "Common/Analytics.h"
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/Analytics.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Movie.h"
|
||||
#include "Core/NetPlayProto.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const char* ANALYTICS_ENDPOINT = "https://analytics.dolphin-emu.org/report";
|
||||
} // namespace
|
||||
|
||||
std::mutex DolphinAnalytics::s_instance_mutex;
|
||||
std::shared_ptr<DolphinAnalytics> DolphinAnalytics::s_instance;
|
||||
|
||||
DolphinAnalytics::DolphinAnalytics()
|
||||
{
|
||||
ReloadConfig();
|
||||
MakeBaseBuilder();
|
||||
}
|
||||
|
||||
std::shared_ptr<DolphinAnalytics> DolphinAnalytics::Instance()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(s_instance_mutex);
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance.reset(new DolphinAnalytics());
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void DolphinAnalytics::ReloadConfig()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_reporter_mutex);
|
||||
|
||||
// Install the HTTP backend if analytics support is enabled.
|
||||
std::unique_ptr<Common::AnalyticsReportingBackend> new_backend;
|
||||
if (SConfig::GetInstance().m_analytics_enabled)
|
||||
{
|
||||
new_backend = std::make_unique<Common::HttpAnalyticsBackend>(ANALYTICS_ENDPOINT);
|
||||
}
|
||||
m_reporter.SetBackend(std::move(new_backend));
|
||||
|
||||
// Load the unique ID or generate it if needed.
|
||||
m_unique_id = SConfig::GetInstance().m_analytics_id;
|
||||
if (m_unique_id.empty())
|
||||
{
|
||||
GenerateNewIdentity();
|
||||
}
|
||||
}
|
||||
|
||||
void DolphinAnalytics::GenerateNewIdentity()
|
||||
{
|
||||
std::random_device rd;
|
||||
u64 id_high = (static_cast<u64>(rd()) << 32) | rd();
|
||||
u64 id_low = (static_cast<u64>(rd()) << 32) | rd();
|
||||
m_unique_id = StringFromFormat("%016" PRIx64 "%016" PRIx64, id_high, id_low);
|
||||
|
||||
// Save the new id in the configuration.
|
||||
SConfig::GetInstance().m_analytics_id = m_unique_id;
|
||||
SConfig::GetInstance().SaveSettings();
|
||||
}
|
||||
|
||||
std::string DolphinAnalytics::MakeUniqueId(const std::string& data)
|
||||
{
|
||||
u8 digest[20];
|
||||
std::string input = m_unique_id + data;
|
||||
mbedtls_sha1(reinterpret_cast<const u8*>(input.c_str()), input.size(),
|
||||
digest);
|
||||
|
||||
// Convert to hex string and truncate to 64 bits.
|
||||
std::string out;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
out += StringFromFormat("%02hhx", digest[i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void DolphinAnalytics::ReportDolphinStart(const std::string& ui_type)
|
||||
{
|
||||
Common::AnalyticsReportBuilder builder(m_base_builder);
|
||||
builder.AddData("type", "dolphin-start");
|
||||
builder.AddData("ui-type", ui_type);
|
||||
builder.AddData("id", MakeUniqueId("dolphin-start"));
|
||||
Send(builder);
|
||||
}
|
||||
|
||||
void DolphinAnalytics::ReportGameStart()
|
||||
{
|
||||
MakePerGameBuilder();
|
||||
|
||||
Common::AnalyticsReportBuilder builder(m_per_game_builder);
|
||||
builder.AddData("type", "game-start");
|
||||
Send(builder);
|
||||
}
|
||||
|
||||
void DolphinAnalytics::MakeBaseBuilder()
|
||||
{
|
||||
Common::AnalyticsReportBuilder builder;
|
||||
|
||||
// Version information.
|
||||
builder.AddData("version-desc", scm_desc_str);
|
||||
builder.AddData("version-hash", scm_rev_git_str);
|
||||
builder.AddData("version-branch", scm_branch_str);
|
||||
builder.AddData("version-dist", scm_distributor_str);
|
||||
|
||||
// CPU information.
|
||||
builder.AddData("cpu-summary", cpu_info.Summarize());
|
||||
|
||||
// OS information.
|
||||
#if defined(_WIN32)
|
||||
builder.AddData("os-type", "windows");
|
||||
|
||||
// Windows 8 removes support for GetVersionEx and such. Stupid.
|
||||
DWORD (WINAPI *RtlGetVersion)(LPOSVERSIONINFOEXW);
|
||||
*(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandle(TEXT("ntdll")), "RtlGetVersion");
|
||||
|
||||
OSVERSIONINFOEXW winver;
|
||||
winver.dwOSVersionInfoSize = sizeof(winver);
|
||||
if (RtlGetVersion != nullptr)
|
||||
{
|
||||
RtlGetVersion(&winver);
|
||||
builder.AddData("win-ver-major", static_cast<u32>(winver.dwMajorVersion));
|
||||
builder.AddData("win-ver-minor", static_cast<u32>(winver.dwMinorVersion));
|
||||
builder.AddData("win-ver-build", static_cast<u32>(winver.dwBuildNumber));
|
||||
builder.AddData("win-ver-spmajor", static_cast<u32>(winver.wServicePackMajor));
|
||||
builder.AddData("win-ver-spminor", static_cast<u32>(winver.wServicePackMinor));
|
||||
}
|
||||
#elif defined(ANDROID)
|
||||
builder.AddData("os-type", "android");
|
||||
#elif defined(__APPLE__)
|
||||
builder.AddData("os-type", "osx");
|
||||
|
||||
SInt32 osxmajor, osxminor, osxbugfix;
|
||||
Gestalt(gestaltSystemVersionMajor, &osxmajor);
|
||||
Gestalt(gestaltSystemVersionMinor, &osxminor);
|
||||
Gestalt(gestaltSystemVersionBugFix, &osxbugfix);
|
||||
|
||||
builder.AddData("osx-ver-major", osxmajor);
|
||||
builder.AddData("osx-ver-minor", osxminor);
|
||||
builder.AddData("osx-ver-bugfix", osxbugfix);
|
||||
#elif defined(__linux__)
|
||||
builder.AddData("os-type", "linux");
|
||||
#elif defined(__FreeBSD__)
|
||||
builder.AddData("os-type", "freebsd");
|
||||
#else
|
||||
builder.AddData("os-type", "unknown");
|
||||
#endif
|
||||
|
||||
m_base_builder = builder;
|
||||
}
|
||||
|
||||
void DolphinAnalytics::MakePerGameBuilder()
|
||||
{
|
||||
Common::AnalyticsReportBuilder builder(m_base_builder);
|
||||
|
||||
// Gameid.
|
||||
builder.AddData("gameid", SConfig::GetInstance().GetUniqueID());
|
||||
|
||||
// Unique id bound to the gameid.
|
||||
builder.AddData("id", MakeUniqueId(SConfig::GetInstance().GetUniqueID()));
|
||||
|
||||
// Configuration.
|
||||
builder.AddData("cfg-dsp-hle", SConfig::GetInstance().bDSPHLE);
|
||||
builder.AddData("cfg-dsp-jit", SConfig::GetInstance().m_DSPEnableJIT);
|
||||
builder.AddData("cfg-dsp-thread", SConfig::GetInstance().bDSPThread);
|
||||
builder.AddData("cfg-cpu-thread", SConfig::GetInstance().bCPUThread);
|
||||
builder.AddData("cfg-idle-skip", SConfig::GetInstance().bSkipIdle);
|
||||
builder.AddData("cfg-fastmem", SConfig::GetInstance().bFastmem);
|
||||
builder.AddData("cfg-syncgpu", SConfig::GetInstance().bSyncGPU);
|
||||
builder.AddData("cfg-audio-backend", SConfig::GetInstance().sBackend);
|
||||
builder.AddData("cfg-video-backend", SConfig::GetInstance().m_strVideoBackend);
|
||||
builder.AddData("cfg-oc-enable", SConfig::GetInstance().m_OCEnable);
|
||||
builder.AddData("cfg-oc-factor", SConfig::GetInstance().m_OCFactor);
|
||||
builder.AddData("cfg-render-to-main", SConfig::GetInstance().bRenderToMain);
|
||||
|
||||
// Video configuration.
|
||||
builder.AddData("cfg-gfx-multisamples", g_Config.iMultisamples);
|
||||
builder.AddData("cfg-gfx-ssaa", g_Config.bSSAA);
|
||||
builder.AddData("cfg-gfx-anisotropy", g_Config.iMaxAnisotropy);
|
||||
builder.AddData("cfg-gfx-realxfb", g_Config.RealXFBEnabled());
|
||||
builder.AddData("cfg-gfx-virtualxfb", g_Config.VirtualXFBEnabled());
|
||||
builder.AddData("cfg-gfx-vsync", g_Config.bVSync);
|
||||
builder.AddData("cfg-gfx-fullscreen", g_Config.bFullscreen);
|
||||
builder.AddData("cfg-gfx-exclusive-mode", g_Config.bExclusiveMode);
|
||||
builder.AddData("cfg-gfx-aspect-ratio", g_Config.iAspectRatio);
|
||||
builder.AddData("cfg-gfx-efb-access", g_Config.bEFBAccessEnable);
|
||||
builder.AddData("cfg-gfx-efb-scale", g_Config.iEFBScale);
|
||||
builder.AddData("cfg-gfx-efb-copy-format-changes", g_Config.bEFBEmulateFormatChanges);
|
||||
builder.AddData("cfg-gfx-efb-copy-ram", !g_Config.bSkipEFBCopyToRam);
|
||||
builder.AddData("cfg-gfx-efb-copy-scaled", g_Config.bCopyEFBScaled);
|
||||
builder.AddData("cfg-gfx-tc-samples", g_Config.iSafeTextureCache_ColorSamples);
|
||||
builder.AddData("cfg-gfx-stereo-mode", g_Config.iStereoMode);
|
||||
|
||||
// GPU features.
|
||||
if (g_Config.iAdapter < static_cast<int>(g_Config.backend_info.Adapters.size()))
|
||||
{
|
||||
builder.AddData("gpu-adapter", g_Config.backend_info.Adapters[g_Config.iAdapter]);
|
||||
}
|
||||
builder.AddData("gpu-has-exclusive-fullscreen", g_Config.backend_info.bSupportsExclusiveFullscreen);
|
||||
builder.AddData("gpu-has-dual-source-blend", g_Config.backend_info.bSupportsDualSourceBlend);
|
||||
builder.AddData("gpu-has-primitive-restart", g_Config.backend_info.bSupportsPrimitiveRestart);
|
||||
builder.AddData("gpu-has-oversized-viewports", g_Config.backend_info.bSupportsOversizedViewports);
|
||||
builder.AddData("gpu-has-geometry-shaders", g_Config.backend_info.bSupportsGeometryShaders);
|
||||
builder.AddData("gpu-has-3d-vision", g_Config.backend_info.bSupports3DVision);
|
||||
builder.AddData("gpu-has-early-z", g_Config.backend_info.bSupportsEarlyZ);
|
||||
builder.AddData("gpu-has-binding-layout", g_Config.backend_info.bSupportsBindingLayout);
|
||||
builder.AddData("gpu-has-bbox", g_Config.backend_info.bSupportsBBox);
|
||||
builder.AddData("gpu-has-gs-instancing", g_Config.backend_info.bSupportsGSInstancing);
|
||||
builder.AddData("gpu-has-post-processing", g_Config.backend_info.bSupportsPostProcessing);
|
||||
builder.AddData("gpu-has-palette-conversion", g_Config.backend_info.bSupportsPaletteConversion);
|
||||
builder.AddData("gpu-has-clip-control", g_Config.backend_info.bSupportsClipControl);
|
||||
builder.AddData("gpu-has-ssaa", g_Config.backend_info.bSupportsSSAA);
|
||||
|
||||
// NetPlay / recording.
|
||||
builder.AddData("netplay", NetPlay::IsNetPlayRunning());
|
||||
builder.AddData("movie", Movie::IsMovieActive());
|
||||
|
||||
m_per_game_builder = builder;
|
||||
}
|
Reference in New Issue
Block a user