committed by
Briar
29 changed files with 84 additions and 1123 deletions
-
2src/common/CMakeLists.txt
-
72src/common/scm_rev.cpp.in
-
6src/common/scm_rev.h
-
3src/common/settings.h
-
119src/common/telemetry.cpp
-
209src/common/telemetry.h
-
2src/core/CMakeLists.txt
-
32src/core/core.cpp
-
7src/core/core.h
-
294src/core/telemetry_session.cpp
-
101src/core/telemetry_session.h
-
16src/video_core/renderer_opengl/renderer_opengl.cpp
-
5src/video_core/renderer_opengl/renderer_opengl.h
-
16src/video_core/renderer_vulkan/renderer_vulkan.cpp
-
8src/video_core/renderer_vulkan/renderer_vulkan.h
-
9src/video_core/video_core.cpp
-
2src/web_service/CMakeLists.txt
-
130src/web_service/telemetry_json.cpp
-
44src/web_service/telemetry_json.h
-
11src/yuzu/compatdb.cpp
-
5src/yuzu/compatdb.h
-
24src/yuzu/configuration/configure_web.cpp
-
1src/yuzu/configuration/configure_web.h
-
50src/yuzu/configuration/configure_web.ui
-
29src/yuzu/main.cpp
-
1src/yuzu/main.h
-
2src/yuzu/uisettings.h
-
4src/yuzu_cmd/yuzu.cpp
-
3tools/clang-format.sh
@ -1,119 +0,0 @@ |
|||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#include <algorithm>
|
|||
#include <cstring>
|
|||
#include "common/scm_rev.h"
|
|||
#include "common/telemetry.h"
|
|||
|
|||
#ifdef ARCHITECTURE_x86_64
|
|||
#include "common/x64/cpu_detect.h"
|
|||
#endif
|
|||
|
|||
namespace Common::Telemetry { |
|||
|
|||
void FieldCollection::Accept(VisitorInterface& visitor) const { |
|||
for (const auto& field : fields) { |
|||
field.second->Accept(visitor); |
|||
} |
|||
} |
|||
|
|||
void FieldCollection::AddField(std::unique_ptr<FieldInterface> field) { |
|||
fields[field->GetName()] = std::move(field); |
|||
} |
|||
|
|||
template <class T> |
|||
void Field<T>::Accept(VisitorInterface& visitor) const { |
|||
visitor.Visit(*this); |
|||
} |
|||
|
|||
template class Field<bool>; |
|||
template class Field<double>; |
|||
template class Field<float>; |
|||
template class Field<u8>; |
|||
template class Field<u16>; |
|||
template class Field<u32>; |
|||
template class Field<u64>; |
|||
template class Field<s8>; |
|||
template class Field<s16>; |
|||
template class Field<s32>; |
|||
template class Field<s64>; |
|||
template class Field<std::string>; |
|||
template class Field<const char*>; |
|||
template class Field<std::chrono::microseconds>; |
|||
|
|||
void AppendBuildInfo(FieldCollection& fc) { |
|||
const bool is_git_dirty{std::strstr(Common::g_scm_desc, "dirty") != nullptr}; |
|||
fc.AddField(FieldType::App, "Git_IsDirty", is_git_dirty); |
|||
fc.AddField(FieldType::App, "Git_Branch", Common::g_scm_branch); |
|||
fc.AddField(FieldType::App, "Git_Revision", Common::g_scm_rev); |
|||
fc.AddField(FieldType::App, "BuildDate", Common::g_build_date); |
|||
fc.AddField(FieldType::App, "BuildName", Common::g_build_name); |
|||
} |
|||
|
|||
void AppendCPUInfo(FieldCollection& fc) { |
|||
#ifdef ARCHITECTURE_x86_64
|
|||
|
|||
const auto& caps = Common::GetCPUCaps(); |
|||
const auto add_field = [&fc](std::string_view field_name, const auto& field_value) { |
|||
fc.AddField(FieldType::UserSystem, field_name, field_value); |
|||
}; |
|||
add_field("CPU_Model", caps.cpu_string); |
|||
add_field("CPU_BrandString", caps.brand_string); |
|||
|
|||
add_field("CPU_Extension_x64_SSE", caps.sse); |
|||
add_field("CPU_Extension_x64_SSE2", caps.sse2); |
|||
add_field("CPU_Extension_x64_SSE3", caps.sse3); |
|||
add_field("CPU_Extension_x64_SSSE3", caps.ssse3); |
|||
add_field("CPU_Extension_x64_SSE41", caps.sse4_1); |
|||
add_field("CPU_Extension_x64_SSE42", caps.sse4_2); |
|||
|
|||
add_field("CPU_Extension_x64_AVX", caps.avx); |
|||
add_field("CPU_Extension_x64_AVX_VNNI", caps.avx_vnni); |
|||
add_field("CPU_Extension_x64_AVX2", caps.avx2); |
|||
|
|||
// Skylake-X/SP level AVX512, for compatibility with the previous telemetry field
|
|||
add_field("CPU_Extension_x64_AVX512", |
|||
caps.avx512f && caps.avx512cd && caps.avx512vl && caps.avx512dq && caps.avx512bw); |
|||
|
|||
add_field("CPU_Extension_x64_AVX512F", caps.avx512f); |
|||
add_field("CPU_Extension_x64_AVX512CD", caps.avx512cd); |
|||
add_field("CPU_Extension_x64_AVX512VL", caps.avx512vl); |
|||
add_field("CPU_Extension_x64_AVX512DQ", caps.avx512dq); |
|||
add_field("CPU_Extension_x64_AVX512BW", caps.avx512bw); |
|||
add_field("CPU_Extension_x64_AVX512BITALG", caps.avx512bitalg); |
|||
add_field("CPU_Extension_x64_AVX512VBMI", caps.avx512vbmi); |
|||
|
|||
add_field("CPU_Extension_x64_AES", caps.aes); |
|||
add_field("CPU_Extension_x64_BMI1", caps.bmi1); |
|||
add_field("CPU_Extension_x64_BMI2", caps.bmi2); |
|||
add_field("CPU_Extension_x64_F16C", caps.f16c); |
|||
add_field("CPU_Extension_x64_FMA", caps.fma); |
|||
add_field("CPU_Extension_x64_FMA4", caps.fma4); |
|||
add_field("CPU_Extension_x64_GFNI", caps.gfni); |
|||
add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc); |
|||
add_field("CPU_Extension_x64_LZCNT", caps.lzcnt); |
|||
add_field("CPU_Extension_x64_MONITORX", caps.monitorx); |
|||
add_field("CPU_Extension_x64_MOVBE", caps.movbe); |
|||
add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq); |
|||
add_field("CPU_Extension_x64_POPCNT", caps.popcnt); |
|||
add_field("CPU_Extension_x64_SHA", caps.sha); |
|||
add_field("CPU_Extension_x64_WAITPKG", caps.waitpkg); |
|||
#else
|
|||
fc.AddField(FieldType::UserSystem, "CPU_Model", "Other"); |
|||
#endif
|
|||
} |
|||
|
|||
void AppendOSInfo(FieldCollection& fc) { |
|||
#ifdef __APPLE__
|
|||
fc.AddField(FieldType::UserSystem, "OsPlatform", "Apple"); |
|||
#elif defined(_WIN32)
|
|||
fc.AddField(FieldType::UserSystem, "OsPlatform", "Windows"); |
|||
#elif defined(__linux__) || defined(linux) || defined(__linux)
|
|||
fc.AddField(FieldType::UserSystem, "OsPlatform", "Linux"); |
|||
#else
|
|||
fc.AddField(FieldType::UserSystem, "OsPlatform", "Unknown"); |
|||
#endif
|
|||
} |
|||
|
|||
} // namespace Common::Telemetry
|
|||
@ -1,209 +0,0 @@ |
|||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <chrono> |
|||
#include <map> |
|||
#include <memory> |
|||
#include <string> |
|||
#include "common/common_funcs.h" |
|||
#include "common/common_types.h" |
|||
|
|||
namespace Common::Telemetry { |
|||
|
|||
/// Field type, used for grouping fields together in the final submitted telemetry log |
|||
enum class FieldType : u8 { |
|||
None = 0, ///< No specified field group |
|||
App, ///< yuzu application fields (e.g. version, branch, etc.) |
|||
Session, ///< Emulated session fields (e.g. title ID, log, etc.) |
|||
Performance, ///< Emulated performance (e.g. fps, emulated CPU speed, etc.) |
|||
UserFeedback, ///< User submitted feedback (e.g. star rating, user notes, etc.) |
|||
UserConfig, ///< User configuration fields (e.g. emulated CPU core, renderer, etc.) |
|||
UserSystem, ///< User system information (e.g. host CPU type, RAM, etc.) |
|||
}; |
|||
|
|||
struct VisitorInterface; |
|||
|
|||
/** |
|||
* Interface class for telemetry data fields. |
|||
*/ |
|||
class FieldInterface { |
|||
public: |
|||
virtual ~FieldInterface() = default; |
|||
|
|||
/** |
|||
* Accept method for the visitor pattern. |
|||
* @param visitor Reference to the visitor that will visit this field. |
|||
*/ |
|||
virtual void Accept(VisitorInterface& visitor) const = 0; |
|||
|
|||
/** |
|||
* Gets the name of this field. |
|||
* @returns Name of this field as a string. |
|||
*/ |
|||
virtual const std::string& GetName() const = 0; |
|||
}; |
|||
|
|||
/** |
|||
* Represents a telemetry data field, i.e. a unit of data that gets logged and submitted to our |
|||
* telemetry web service. |
|||
*/ |
|||
template <typename T> |
|||
class Field : public FieldInterface { |
|||
public: |
|||
YUZU_NON_COPYABLE(Field); |
|||
|
|||
Field(FieldType type_, std::string_view name_, T value_) |
|||
: name(name_), type(type_), value(std::move(value_)) {} |
|||
|
|||
~Field() override = default; |
|||
|
|||
Field(Field&&) noexcept = default; |
|||
Field& operator=(Field&& other) noexcept = default; |
|||
|
|||
void Accept(VisitorInterface& visitor) const override; |
|||
|
|||
[[nodiscard]] const std::string& GetName() const override { |
|||
return name; |
|||
} |
|||
|
|||
/** |
|||
* Returns the type of the field. |
|||
*/ |
|||
[[nodiscard]] FieldType GetType() const { |
|||
return type; |
|||
} |
|||
|
|||
/** |
|||
* Returns the value of the field. |
|||
*/ |
|||
[[nodiscard]] const T& GetValue() const { |
|||
return value; |
|||
} |
|||
|
|||
[[nodiscard]] bool operator==(const Field& other) const { |
|||
return (type == other.type) && (name == other.name) && (value == other.value); |
|||
} |
|||
|
|||
[[nodiscard]] bool operator!=(const Field& other) const { |
|||
return !operator==(other); |
|||
} |
|||
|
|||
private: |
|||
std::string name; ///< Field name, must be unique |
|||
FieldType type{}; ///< Field type, used for grouping fields together |
|||
T value; ///< Field value |
|||
}; |
|||
|
|||
/** |
|||
* Collection of data fields that have been logged. |
|||
*/ |
|||
class FieldCollection final { |
|||
public: |
|||
YUZU_NON_COPYABLE(FieldCollection); |
|||
|
|||
FieldCollection() = default; |
|||
~FieldCollection() = default; |
|||
|
|||
FieldCollection(FieldCollection&&) noexcept = default; |
|||
FieldCollection& operator=(FieldCollection&&) noexcept = default; |
|||
|
|||
/** |
|||
* Accept method for the visitor pattern, visits each field in the collection. |
|||
* @param visitor Reference to the visitor that will visit each field. |
|||
*/ |
|||
void Accept(VisitorInterface& visitor) const; |
|||
|
|||
/** |
|||
* Creates a new field and adds it to the field collection. |
|||
* @param type Type of the field to add. |
|||
* @param name Name of the field to add. |
|||
* @param value Value for the field to add. |
|||
*/ |
|||
template <typename T> |
|||
void AddField(FieldType type, std::string_view name, T value) { |
|||
return AddField(std::make_unique<Field<T>>(type, name, std::move(value))); |
|||
} |
|||
|
|||
/** |
|||
* Adds a new field to the field collection. |
|||
* @param field Field to add to the field collection. |
|||
*/ |
|||
void AddField(std::unique_ptr<FieldInterface> field); |
|||
|
|||
private: |
|||
std::map<std::string, std::unique_ptr<FieldInterface>> fields; |
|||
}; |
|||
|
|||
/** |
|||
* Telemetry fields visitor interface class. A backend to log to a web service should implement |
|||
* this interface. |
|||
*/ |
|||
struct VisitorInterface { |
|||
virtual ~VisitorInterface() = default; |
|||
|
|||
virtual void Visit(const Field<bool>& field) = 0; |
|||
virtual void Visit(const Field<double>& field) = 0; |
|||
virtual void Visit(const Field<float>& field) = 0; |
|||
virtual void Visit(const Field<u8>& field) = 0; |
|||
virtual void Visit(const Field<u16>& field) = 0; |
|||
virtual void Visit(const Field<u32>& field) = 0; |
|||
virtual void Visit(const Field<u64>& field) = 0; |
|||
virtual void Visit(const Field<s8>& field) = 0; |
|||
virtual void Visit(const Field<s16>& field) = 0; |
|||
virtual void Visit(const Field<s32>& field) = 0; |
|||
virtual void Visit(const Field<s64>& field) = 0; |
|||
virtual void Visit(const Field<std::string>& field) = 0; |
|||
virtual void Visit(const Field<const char*>& field) = 0; |
|||
virtual void Visit(const Field<std::chrono::microseconds>& field) = 0; |
|||
|
|||
/// Completion method, called once all fields have been visited |
|||
virtual void Complete() = 0; |
|||
virtual bool SubmitTestcase() = 0; |
|||
}; |
|||
|
|||
/** |
|||
* Empty implementation of VisitorInterface that drops all fields. Used when a functional |
|||
* backend implementation is not available. |
|||
*/ |
|||
struct NullVisitor final : public VisitorInterface { |
|||
YUZU_NON_COPYABLE(NullVisitor); |
|||
|
|||
NullVisitor() = default; |
|||
~NullVisitor() override = default; |
|||
|
|||
void Visit(const Field<bool>& /*field*/) override {} |
|||
void Visit(const Field<double>& /*field*/) override {} |
|||
void Visit(const Field<float>& /*field*/) override {} |
|||
void Visit(const Field<u8>& /*field*/) override {} |
|||
void Visit(const Field<u16>& /*field*/) override {} |
|||
void Visit(const Field<u32>& /*field*/) override {} |
|||
void Visit(const Field<u64>& /*field*/) override {} |
|||
void Visit(const Field<s8>& /*field*/) override {} |
|||
void Visit(const Field<s16>& /*field*/) override {} |
|||
void Visit(const Field<s32>& /*field*/) override {} |
|||
void Visit(const Field<s64>& /*field*/) override {} |
|||
void Visit(const Field<std::string>& /*field*/) override {} |
|||
void Visit(const Field<const char*>& /*field*/) override {} |
|||
void Visit(const Field<std::chrono::microseconds>& /*field*/) override {} |
|||
|
|||
void Complete() override {} |
|||
bool SubmitTestcase() override { |
|||
return false; |
|||
} |
|||
}; |
|||
|
|||
/// Appends build-specific information to the given FieldCollection, |
|||
/// such as branch name, revision hash, etc. |
|||
void AppendBuildInfo(FieldCollection& fc); |
|||
|
|||
/// Appends CPU-specific information to the given FieldCollection, |
|||
/// such as instruction set extensions, etc. |
|||
void AppendCPUInfo(FieldCollection& fc); |
|||
|
|||
/// Appends OS-specific information to the given FieldCollection, |
|||
/// such as platform name, etc. |
|||
void AppendOSInfo(FieldCollection& fc); |
|||
|
|||
} // namespace Common::Telemetry |
|||
@ -1,294 +0,0 @@ |
|||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#include <array>
|
|||
|
|||
#include <mbedtls/ctr_drbg.h>
|
|||
#include <mbedtls/entropy.h>
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "common/common_types.h"
|
|||
#include "common/fs/file.h"
|
|||
#include "common/fs/fs.h"
|
|||
#include "common/fs/path_util.h"
|
|||
#include "common/logging/log.h"
|
|||
|
|||
#include "common/settings.h"
|
|||
#include "common/settings_enums.h"
|
|||
#include "core/file_sys/control_metadata.h"
|
|||
#include "core/file_sys/patch_manager.h"
|
|||
#include "core/loader/loader.h"
|
|||
#include "core/telemetry_session.h"
|
|||
|
|||
#ifdef ENABLE_WEB_SERVICE
|
|||
#include "web_service/telemetry_json.h"
|
|||
#include "web_service/verify_login.h"
|
|||
#endif
|
|||
|
|||
namespace Core { |
|||
|
|||
namespace Telemetry = Common::Telemetry; |
|||
|
|||
static u64 GenerateTelemetryId() { |
|||
u64 telemetry_id{}; |
|||
|
|||
mbedtls_entropy_context entropy; |
|||
mbedtls_entropy_init(&entropy); |
|||
mbedtls_ctr_drbg_context ctr_drbg; |
|||
static constexpr std::array<char, 18> personalization{{"yuzu Telemetry ID"}}; |
|||
|
|||
mbedtls_ctr_drbg_init(&ctr_drbg); |
|||
ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, |
|||
reinterpret_cast<const unsigned char*>(personalization.data()), |
|||
personalization.size()) == 0); |
|||
ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast<unsigned char*>(&telemetry_id), |
|||
sizeof(u64)) == 0); |
|||
|
|||
mbedtls_ctr_drbg_free(&ctr_drbg); |
|||
mbedtls_entropy_free(&entropy); |
|||
|
|||
return telemetry_id; |
|||
} |
|||
|
|||
static const char* TranslateRenderer(Settings::RendererBackend backend) { |
|||
switch (backend) { |
|||
case Settings::RendererBackend::OpenGL: |
|||
return "OpenGL"; |
|||
case Settings::RendererBackend::Vulkan: |
|||
return "Vulkan"; |
|||
case Settings::RendererBackend::Null: |
|||
return "Null"; |
|||
} |
|||
return "Unknown"; |
|||
} |
|||
|
|||
static const char* TranslateGPUAccuracyLevel(Settings::GpuAccuracy backend) { |
|||
switch (backend) { |
|||
case Settings::GpuAccuracy::Normal: |
|||
return "Normal"; |
|||
case Settings::GpuAccuracy::High: |
|||
return "High"; |
|||
case Settings::GpuAccuracy::Extreme: |
|||
return "Extreme"; |
|||
} |
|||
return "Unknown"; |
|||
} |
|||
|
|||
static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) { |
|||
switch (backend) { |
|||
case Settings::NvdecEmulation::Off: |
|||
return "Off"; |
|||
case Settings::NvdecEmulation::Cpu: |
|||
return "CPU"; |
|||
case Settings::NvdecEmulation::Gpu: |
|||
return "GPU"; |
|||
} |
|||
return "Unknown"; |
|||
} |
|||
|
|||
static constexpr const char* TranslateVSyncMode(Settings::VSyncMode mode) { |
|||
switch (mode) { |
|||
case Settings::VSyncMode::Immediate: |
|||
return "Immediate"; |
|||
case Settings::VSyncMode::Mailbox: |
|||
return "Mailbox"; |
|||
case Settings::VSyncMode::Fifo: |
|||
return "FIFO"; |
|||
case Settings::VSyncMode::FifoRelaxed: |
|||
return "FIFO Relaxed"; |
|||
} |
|||
return "Unknown"; |
|||
} |
|||
|
|||
static constexpr const char* TranslateASTCDecodeMode(Settings::AstcDecodeMode mode) { |
|||
switch (mode) { |
|||
case Settings::AstcDecodeMode::Cpu: |
|||
return "CPU"; |
|||
case Settings::AstcDecodeMode::Gpu: |
|||
return "GPU"; |
|||
case Settings::AstcDecodeMode::CpuAsynchronous: |
|||
return "CPU Asynchronous"; |
|||
} |
|||
return "Unknown"; |
|||
} |
|||
|
|||
u64 GetTelemetryId() { |
|||
u64 telemetry_id{}; |
|||
const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id"; |
|||
|
|||
bool generate_new_id = !Common::FS::Exists(filename); |
|||
|
|||
if (!generate_new_id) { |
|||
Common::FS::IOFile file{filename, Common::FS::FileAccessMode::Read, |
|||
Common::FS::FileType::BinaryFile}; |
|||
|
|||
if (!file.IsOpen()) { |
|||
LOG_ERROR(Core, "failed to open telemetry_id: {}", |
|||
Common::FS::PathToUTF8String(filename)); |
|||
return {}; |
|||
} |
|||
|
|||
if (!file.ReadObject(telemetry_id) || telemetry_id == 0) { |
|||
LOG_ERROR(Frontend, "telemetry_id is 0. Generating a new one.", telemetry_id); |
|||
generate_new_id = true; |
|||
} |
|||
} |
|||
|
|||
if (generate_new_id) { |
|||
Common::FS::IOFile file{filename, Common::FS::FileAccessMode::Write, |
|||
Common::FS::FileType::BinaryFile}; |
|||
|
|||
if (!file.IsOpen()) { |
|||
LOG_ERROR(Core, "failed to open telemetry_id: {}", |
|||
Common::FS::PathToUTF8String(filename)); |
|||
return {}; |
|||
} |
|||
|
|||
telemetry_id = GenerateTelemetryId(); |
|||
|
|||
if (!file.WriteObject(telemetry_id)) { |
|||
LOG_ERROR(Core, "Failed to write telemetry_id to file."); |
|||
} |
|||
} |
|||
|
|||
return telemetry_id; |
|||
} |
|||
|
|||
u64 RegenerateTelemetryId() { |
|||
const u64 new_telemetry_id{GenerateTelemetryId()}; |
|||
const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id"; |
|||
|
|||
Common::FS::IOFile file{filename, Common::FS::FileAccessMode::Write, |
|||
Common::FS::FileType::BinaryFile}; |
|||
|
|||
if (!file.IsOpen()) { |
|||
LOG_ERROR(Core, "failed to open telemetry_id: {}", Common::FS::PathToUTF8String(filename)); |
|||
return {}; |
|||
} |
|||
|
|||
if (!file.WriteObject(new_telemetry_id)) { |
|||
LOG_ERROR(Core, "Failed to write telemetry_id to file."); |
|||
} |
|||
|
|||
return new_telemetry_id; |
|||
} |
|||
|
|||
bool VerifyLogin(const std::string& username, const std::string& token) { |
|||
#ifdef ENABLE_WEB_SERVICE
|
|||
return WebService::VerifyLogin(Settings::values.web_api_url.GetValue(), username, token); |
|||
#else
|
|||
return false; |
|||
#endif
|
|||
} |
|||
|
|||
TelemetrySession::TelemetrySession() = default; |
|||
|
|||
TelemetrySession::~TelemetrySession() { |
|||
// Log one-time session end information
|
|||
const s64 shutdown_time{std::chrono::duration_cast<std::chrono::milliseconds>( |
|||
std::chrono::system_clock::now().time_since_epoch()) |
|||
.count()}; |
|||
AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time); |
|||
|
|||
#ifdef ENABLE_WEB_SERVICE
|
|||
auto backend = std::make_unique<WebService::TelemetryJson>( |
|||
Settings::values.web_api_url.GetValue(), Settings::values.yuzu_username.GetValue(), |
|||
Settings::values.yuzu_token.GetValue()); |
|||
#else
|
|||
auto backend = std::make_unique<Telemetry::NullVisitor>(); |
|||
#endif
|
|||
|
|||
// Complete the session, submitting to the web service backend if necessary
|
|||
field_collection.Accept(*backend); |
|||
if (Settings::values.enable_telemetry) { |
|||
backend->Complete(); |
|||
} |
|||
} |
|||
|
|||
void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader, |
|||
const Service::FileSystem::FileSystemController& fsc, |
|||
const FileSys::ContentProvider& content_provider) { |
|||
// Log one-time top-level information
|
|||
AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId()); |
|||
|
|||
// Log one-time session start information
|
|||
const s64 init_time{std::chrono::duration_cast<std::chrono::milliseconds>( |
|||
std::chrono::system_clock::now().time_since_epoch()) |
|||
.count()}; |
|||
AddField(Telemetry::FieldType::Session, "Init_Time", init_time); |
|||
|
|||
u64 program_id{}; |
|||
const Loader::ResultStatus res{app_loader.ReadProgramId(program_id)}; |
|||
if (res == Loader::ResultStatus::Success) { |
|||
const std::string formatted_program_id{fmt::format("{:016X}", program_id)}; |
|||
AddField(Telemetry::FieldType::Session, "ProgramId", formatted_program_id); |
|||
|
|||
std::string name; |
|||
app_loader.ReadTitle(name); |
|||
|
|||
if (name.empty()) { |
|||
const auto metadata = [&content_provider, &fsc, program_id] { |
|||
const FileSys::PatchManager pm{program_id, fsc, content_provider}; |
|||
return pm.GetControlMetadata(); |
|||
}(); |
|||
if (metadata.first != nullptr) { |
|||
name = metadata.first->GetApplicationName(); |
|||
} |
|||
} |
|||
|
|||
if (!name.empty()) { |
|||
AddField(Telemetry::FieldType::Session, "ProgramName", name); |
|||
} |
|||
} |
|||
|
|||
AddField(Telemetry::FieldType::Session, "ProgramFormat", |
|||
static_cast<u8>(app_loader.GetFileType())); |
|||
|
|||
// Log application information
|
|||
Telemetry::AppendBuildInfo(field_collection); |
|||
|
|||
// Log user system information
|
|||
Telemetry::AppendCPUInfo(field_collection); |
|||
Telemetry::AppendOSInfo(field_collection); |
|||
|
|||
// Log user configuration information
|
|||
constexpr auto field_type = Telemetry::FieldType::UserConfig; |
|||
AddField(field_type, "Audio_SinkId", |
|||
Settings::CanonicalizeEnum(Settings::values.sink_id.GetValue())); |
|||
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue()); |
|||
AddField(field_type, "Renderer_Backend", |
|||
TranslateRenderer(Settings::values.renderer_backend.GetValue())); |
|||
AddField(field_type, "Renderer_UseSpeedLimit", Settings::values.use_speed_limit.GetValue()); |
|||
AddField(field_type, "Renderer_SpeedLimit", Settings::values.speed_limit.GetValue()); |
|||
AddField(field_type, "Renderer_UseDiskShaderCache", |
|||
Settings::values.use_disk_shader_cache.GetValue()); |
|||
AddField(field_type, "Renderer_GPUAccuracyLevel", |
|||
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue())); |
|||
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", |
|||
Settings::values.use_asynchronous_gpu_emulation.GetValue()); |
|||
AddField(field_type, "Renderer_NvdecEmulation", |
|||
TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue())); |
|||
AddField(field_type, "Renderer_AccelerateASTC", |
|||
TranslateASTCDecodeMode(Settings::values.accelerate_astc.GetValue())); |
|||
AddField(field_type, "Renderer_UseVsync", |
|||
TranslateVSyncMode(Settings::values.vsync_mode.GetValue())); |
|||
AddField(field_type, "Renderer_ShaderBackend", |
|||
static_cast<u32>(Settings::values.shader_backend.GetValue())); |
|||
AddField(field_type, "Renderer_UseAsynchronousShaders", |
|||
Settings::values.use_asynchronous_shaders.GetValue()); |
|||
AddField(field_type, "System_UseDockedMode", Settings::IsDockedMode()); |
|||
} |
|||
|
|||
bool TelemetrySession::SubmitTestcase() { |
|||
#ifdef ENABLE_WEB_SERVICE
|
|||
auto backend = std::make_unique<WebService::TelemetryJson>( |
|||
Settings::values.web_api_url.GetValue(), Settings::values.yuzu_username.GetValue(), |
|||
Settings::values.yuzu_token.GetValue()); |
|||
field_collection.Accept(*backend); |
|||
return backend->SubmitTestcase(); |
|||
#else
|
|||
return false; |
|||
#endif
|
|||
} |
|||
|
|||
} // namespace Core
|
|||
@ -1,101 +0,0 @@ |
|||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <string> |
|||
#include "common/telemetry.h" |
|||
|
|||
namespace FileSys { |
|||
class ContentProvider; |
|||
} |
|||
|
|||
namespace Loader { |
|||
class AppLoader; |
|||
} |
|||
|
|||
namespace Service::FileSystem { |
|||
class FileSystemController; |
|||
} |
|||
|
|||
namespace Core { |
|||
|
|||
/** |
|||
* Instruments telemetry for this emulation session. Creates a new set of telemetry fields on each |
|||
* session, logging any one-time fields. Interfaces with the telemetry backend used for submitting |
|||
* data to the web service. Submits session data on close. |
|||
*/ |
|||
class TelemetrySession { |
|||
public: |
|||
explicit TelemetrySession(); |
|||
~TelemetrySession(); |
|||
|
|||
TelemetrySession(const TelemetrySession&) = delete; |
|||
TelemetrySession& operator=(const TelemetrySession&) = delete; |
|||
|
|||
TelemetrySession(TelemetrySession&&) = delete; |
|||
TelemetrySession& operator=(TelemetrySession&&) = delete; |
|||
|
|||
/** |
|||
* Adds the initial telemetry info necessary when starting up a title. |
|||
* |
|||
* This includes information such as: |
|||
* - Telemetry ID |
|||
* - Initialization time |
|||
* - Title ID |
|||
* - Title name |
|||
* - Title file format |
|||
* - Miscellaneous settings values. |
|||
* |
|||
* @param app_loader The application loader to use to retrieve |
|||
* title-specific information. |
|||
* @param fsc Filesystem controller to use to retrieve info. |
|||
* @param content_provider Content provider to use to retrieve info. |
|||
*/ |
|||
void AddInitialInfo(Loader::AppLoader& app_loader, |
|||
const Service::FileSystem::FileSystemController& fsc, |
|||
const FileSys::ContentProvider& content_provider); |
|||
|
|||
/** |
|||
* Wrapper around the Telemetry::FieldCollection::AddField method. |
|||
* @param type Type of the field to add. |
|||
* @param name Name of the field to add. |
|||
* @param value Value for the field to add. |
|||
*/ |
|||
template <typename T> |
|||
void AddField(Common::Telemetry::FieldType type, const char* name, T value) { |
|||
field_collection.AddField(type, name, std::move(value)); |
|||
} |
|||
|
|||
/** |
|||
* Submits a Testcase. |
|||
* @returns A bool indicating whether the submission succeeded |
|||
*/ |
|||
bool SubmitTestcase(); |
|||
|
|||
private: |
|||
/// Tracks all added fields for the session |
|||
Common::Telemetry::FieldCollection field_collection; |
|||
}; |
|||
|
|||
/** |
|||
* Gets TelemetryId, a unique identifier used for the user's telemetry sessions. |
|||
* @returns The current TelemetryId for the session. |
|||
*/ |
|||
u64 GetTelemetryId(); |
|||
|
|||
/** |
|||
* Regenerates TelemetryId, a unique identifier used for the user's telemetry sessions. |
|||
* @returns The new TelemetryId that was generated. |
|||
*/ |
|||
u64 RegenerateTelemetryId(); |
|||
|
|||
/** |
|||
* Verifies the username and token. |
|||
* @param username yuzu username to use for authentication. |
|||
* @param token yuzu token to use for authentication. |
|||
* @returns Future with bool indicating whether the verification succeeded |
|||
*/ |
|||
bool VerifyLogin(const std::string& username, const std::string& token); |
|||
|
|||
} // namespace Core |
|||
@ -1,130 +0,0 @@ |
|||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#include <nlohmann/json.hpp>
|
|||
#include "common/detached_tasks.h"
|
|||
#include "web_service/telemetry_json.h"
|
|||
#include "web_service/web_backend.h"
|
|||
#include "web_service/web_result.h"
|
|||
|
|||
namespace WebService { |
|||
|
|||
namespace Telemetry = Common::Telemetry; |
|||
|
|||
struct TelemetryJson::Impl { |
|||
Impl(std::string host_, std::string username_, std::string token_) |
|||
: host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} {} |
|||
|
|||
nlohmann::json& TopSection() { |
|||
return sections[static_cast<u8>(Telemetry::FieldType::None)]; |
|||
} |
|||
|
|||
const nlohmann::json& TopSection() const { |
|||
return sections[static_cast<u8>(Telemetry::FieldType::None)]; |
|||
} |
|||
|
|||
template <class T> |
|||
void Serialize(Telemetry::FieldType type, const std::string& name, T value) { |
|||
sections[static_cast<u8>(type)][name] = value; |
|||
} |
|||
|
|||
void SerializeSection(Telemetry::FieldType type, const std::string& name) { |
|||
TopSection()[name] = sections[static_cast<unsigned>(type)]; |
|||
} |
|||
|
|||
nlohmann::json output; |
|||
std::array<nlohmann::json, 7> sections; |
|||
std::string host; |
|||
std::string username; |
|||
std::string token; |
|||
}; |
|||
|
|||
TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token) |
|||
: impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {} |
|||
TelemetryJson::~TelemetryJson() = default; |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<bool>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<double>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<float>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<u8>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<u16>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<u32>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<u64>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<s8>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<s16>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<s32>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<s64>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), std::string(field.GetValue())); |
|||
} |
|||
|
|||
void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) { |
|||
impl->Serialize(field.GetType(), field.GetName(), field.GetValue().count()); |
|||
} |
|||
|
|||
void TelemetryJson::Complete() { |
|||
impl->SerializeSection(Telemetry::FieldType::App, "App"); |
|||
impl->SerializeSection(Telemetry::FieldType::Session, "Session"); |
|||
impl->SerializeSection(Telemetry::FieldType::Performance, "Performance"); |
|||
impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); |
|||
impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); |
|||
|
|||
auto content = impl->TopSection().dump(); |
|||
// Send the telemetry async but don't handle the errors since they were written to the log
|
|||
Common::DetachedTasks::AddTask([host{impl->host}, content]() { |
|||
Client{host, "", ""}.PostJson("/telemetry", content, true); |
|||
}); |
|||
} |
|||
|
|||
bool TelemetryJson::SubmitTestcase() { |
|||
impl->SerializeSection(Telemetry::FieldType::App, "App"); |
|||
impl->SerializeSection(Telemetry::FieldType::Session, "Session"); |
|||
impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); |
|||
impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); |
|||
impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); |
|||
|
|||
auto content = impl->TopSection().dump(); |
|||
Client client(impl->host, impl->username, impl->token); |
|||
auto value = client.PostJson("/gamedb/testcase", content, false); |
|||
|
|||
return value.result_code == WebResult::Code::Success; |
|||
} |
|||
|
|||
} // namespace WebService
|
|||
@ -1,44 +0,0 @@ |
|||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <chrono> |
|||
#include <string> |
|||
#include "common/telemetry.h" |
|||
|
|||
namespace WebService { |
|||
|
|||
/** |
|||
* Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the |
|||
* yuzu web service |
|||
*/ |
|||
class TelemetryJson : public Common::Telemetry::VisitorInterface { |
|||
public: |
|||
TelemetryJson(std::string host, std::string username, std::string token); |
|||
~TelemetryJson() override; |
|||
|
|||
void Visit(const Common::Telemetry::Field<bool>& field) override; |
|||
void Visit(const Common::Telemetry::Field<double>& field) override; |
|||
void Visit(const Common::Telemetry::Field<float>& field) override; |
|||
void Visit(const Common::Telemetry::Field<u8>& field) override; |
|||
void Visit(const Common::Telemetry::Field<u16>& field) override; |
|||
void Visit(const Common::Telemetry::Field<u32>& field) override; |
|||
void Visit(const Common::Telemetry::Field<u64>& field) override; |
|||
void Visit(const Common::Telemetry::Field<s8>& field) override; |
|||
void Visit(const Common::Telemetry::Field<s16>& field) override; |
|||
void Visit(const Common::Telemetry::Field<s32>& field) override; |
|||
void Visit(const Common::Telemetry::Field<s64>& field) override; |
|||
void Visit(const Common::Telemetry::Field<std::string>& field) override; |
|||
void Visit(const Common::Telemetry::Field<const char*>& field) override; |
|||
void Visit(const Common::Telemetry::Field<std::chrono::microseconds>& field) override; |
|||
|
|||
void Complete() override; |
|||
bool SubmitTestcase() override; |
|||
|
|||
private: |
|||
struct Impl; |
|||
std::unique_ptr<Impl> impl; |
|||
}; |
|||
|
|||
} // namespace WebService |
|||
@ -0,0 +1,3 @@ |
|||
#! /bin/sh |
|||
|
|||
exec find src -iname *.h -o -iname *.cpp | xargs clang-format-15 -i -style=file:src/.clang-format |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue