#include "Core/Analytics.h" #include #include #include #include #include #if defined(_WIN32) #include #elif defined(__APPLE__) #include #endif #include "Common/Analytics.h" #include "Common/CPUDetect.h" #include "Common/CommonTypes.h" #include "Common/Random.h" #include "Common/StringUtil.h" #include "Common/Version.h" #include "Core/ConfigManager.h" #include "Core/HW/GCPad.h" #include "Core/Movie.h" #include "Core/NetPlayProto.h" #include "InputCommon/GCAdapter.h" #include "InputCommon/InputConfig.h" #include "VideoCommon/VideoBackendBase.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::s_instance; DolphinAnalytics::DolphinAnalytics() { ReloadConfig(); MakeBaseBuilder(); } std::shared_ptr DolphinAnalytics::Instance() { std::lock_guard lk(s_instance_mutex); if (!s_instance) { s_instance.reset(new DolphinAnalytics()); } return s_instance; } void DolphinAnalytics::ReloadConfig() { std::lock_guard lk(m_reporter_mutex); // Install the HTTP backend if analytics support is enabled. std::unique_ptr new_backend; if (SConfig::GetInstance().m_analytics_enabled) { new_backend = std::make_unique(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() { u64 id_high, id_low; Common::Random::Generate(&id_high, sizeof(id_high)); Common::Random::Generate(&id_low, sizeof(id_low)); 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(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", Common::scm_desc_str); builder.AddData("version-hash", Common::scm_rev_git_str); builder.AddData("version-branch", Common::scm_branch_str); builder.AddData("version-dist", Common::scm_distributor_str); // Auto-Update information. builder.AddData("update-track", SConfig::GetInstance().m_auto_update_track); // 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(winver.dwMajorVersion)); builder.AddData("win-ver-minor", static_cast(winver.dwMinorVersion)); builder.AddData("win-ver-build", static_cast(winver.dwBuildNumber)); builder.AddData("win-ver-spmajor", static_cast(winver.wServicePackMajor)); builder.AddData("win-ver-spminor", static_cast(winver.wServicePackMinor)); } #elif defined(ANDROID) builder.AddData("os-type", "android"); #elif defined(__APPLE__) builder.AddData("os-type", "osx"); SInt32 osxmajor, osxminor, osxbugfix; // Gestalt is deprecated, but the replacement (NSProcessInfo // operatingSystemVersion) is only available on OS X 10.10, so we need to use // it anyway. Change this someday when Dolphin depends on 10.10+. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" Gestalt(gestaltSystemVersionMajor, &osxmajor); Gestalt(gestaltSystemVersionMinor, &osxminor); Gestalt(gestaltSystemVersionBugFix, &osxbugfix); #pragma GCC diagnostic pop 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; } static const char* GetShaderCompilationMode(const VideoConfig& video_config) { switch (video_config.iShaderCompilationMode) { case ShaderCompilationMode::AsynchronousUberShaders: return "async-ubershaders"; case ShaderCompilationMode::AsynchronousSkipRendering: return "async-skip-rendering"; case ShaderCompilationMode::SynchronousUberShaders: return "sync-ubershaders"; case ShaderCompilationMode::Synchronous: default: return "sync"; } } void DolphinAnalytics::MakePerGameBuilder() { Common::AnalyticsReportBuilder builder(m_base_builder); // Gameid. builder.AddData("gameid", SConfig::GetInstance().GetGameID()); // Unique id bound to the gameid. builder.AddData("id", MakeUniqueId(SConfig::GetInstance().GetGameID())); // 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-fastmem", SConfig::GetInstance().bFastmem); builder.AddData("cfg-syncgpu", SConfig::GetInstance().bSyncGPU); builder.AddData("cfg-audio-backend", SConfig::GetInstance().sBackend); 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); if (g_video_backend) { builder.AddData("cfg-video-backend", g_video_backend->GetName()); } // 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-vsync", g_Config.bVSync); builder.AddData("cfg-gfx-aspect-ratio", static_cast(g_Config.aspect_mode)); builder.AddData("cfg-gfx-efb-access", g_Config.bEFBAccessEnable); 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-xfb-copy-ram", !g_Config.bSkipXFBCopyToRam); builder.AddData("cfg-gfx-immediate-xfb", !g_Config.bImmediateXFB); builder.AddData("cfg-gfx-efb-copy-scaled", g_Config.bCopyEFBScaled); builder.AddData("cfg-gfx-internal-resolution", g_Config.iEFBScale); builder.AddData("cfg-gfx-tc-samples", g_Config.iSafeTextureCache_ColorSamples); builder.AddData("cfg-gfx-stereo-mode", static_cast(g_Config.stereo_mode)); builder.AddData("cfg-gfx-per-pixel-lighting", g_Config.bEnablePixelLighting); builder.AddData("cfg-gfx-shader-compilation-mode", GetShaderCompilationMode(g_Config)); builder.AddData("cfg-gfx-wait-for-shaders", g_Config.bWaitForShadersBeforeStarting); builder.AddData("cfg-gfx-fast-depth", g_Config.bFastDepthCalc); builder.AddData("cfg-gfx-vertex-rounding", g_Config.UseVertexRounding()); // GPU features. if (g_Config.iAdapter < static_cast(g_Config.backend_info.Adapters.size())) { builder.AddData("gpu-adapter", g_Config.backend_info.Adapters[g_Config.iAdapter]); } else if (!g_Config.backend_info.AdapterName.empty()) { builder.AddData("gpu-adapter", g_Config.backend_info.AdapterName); } 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-fragment-stores-and-atomics", g_Config.backend_info.bSupportsFragmentStoresAndAtomics); 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()); // Controller information // We grab enough to tell what percentage of our users are playing with keyboard/mouse, some kind // of gamepad // or the official gamecube adapter. builder.AddData("gcadapter-detected", GCAdapter::IsDetected()); builder.AddData("has-controller", Pad::GetConfig()->IsControllerControlledByGamepadDevice(0) || GCAdapter::IsDetected()); m_per_game_builder = builder; }