| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | // Copyright 2016 Dolphin Emulator Project
 | 
					
						
							|  |  |  | // Licensed under GPLv2+
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <cmath>
 | 
					
						
							|  |  |  | #include <cstdio>
 | 
					
						
							|  |  |  | #include <curl/curl.h>
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include <string>
 | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "Common/Analytics.h"
 | 
					
						
							|  |  |  | #include "Common/CommonTypes.h"
 | 
					
						
							|  |  |  | #include "Common/StringUtil.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 = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Identifiers for the value types supported by the analytics reporting wire
 | 
					
						
							|  |  |  | // format.
 | 
					
						
							|  |  |  | enum class TypeId : u8 | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   STRING = 0, | 
					
						
							|  |  |  |   BOOL = 1, | 
					
						
							|  |  |  |   UINT = 2, | 
					
						
							|  |  |  |   SINT = 3, | 
					
						
							|  |  |  |   FLOAT = 4, | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AppendBool(std::string* out, bool v) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   out->push_back(v ? '\xFF' : '\x00'); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AppendVarInt(std::string* out, u64 v) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   do | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     u8 current_byte = v & 0x7F; | 
					
						
							|  |  |  |     v >>= 7; | 
					
						
							|  |  |  |     current_byte |= (!!v) << 7; | 
					
						
							|  |  |  |     out->push_back(current_byte); | 
					
						
							|  |  |  |   } while (v); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void AppendBytes(std::string* out, const u8* bytes, u32 length, bool encode_length = true) | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (encode_length) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     AppendVarInt(out, length); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   out->append(reinterpret_cast<const char*>(bytes), length); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AppendType(std::string* out, TypeId type) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   out->push_back(static_cast<u8>(type)); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Dummy write function for curl.
 | 
					
						
							|  |  |  | size_t DummyCurlWriteFunction(char* ptr, size_t size, size_t nmemb, void* userdata) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   return size * nmemb; | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AnalyticsReportBuilder::AnalyticsReportBuilder() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_report.push_back(WIRE_FORMAT_VERSION); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, const std::string& v) | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   AppendType(report, TypeId::STRING); | 
					
						
							|  |  |  |   AppendBytes(report, reinterpret_cast<const u8*>(v.data()), static_cast<u32>(v.size())); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, const char* v) | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   AppendSerializedValue(report, std::string(v)); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, bool v) | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   AppendType(report, TypeId::BOOL); | 
					
						
							|  |  |  |   AppendBool(report, v); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, u64 v) | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   AppendType(report, TypeId::UINT); | 
					
						
							|  |  |  |   AppendVarInt(report, v); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, s64 v) | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   AppendType(report, TypeId::SINT); | 
					
						
							|  |  |  |   AppendBool(report, v >= 0); | 
					
						
							|  |  |  |   AppendVarInt(report, static_cast<u64>(std::abs(v))); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, u32 v) | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   AppendSerializedValue(report, static_cast<u64>(v)); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, s32 v) | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   AppendSerializedValue(report, static_cast<s64>(v)); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, float v) | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   AppendType(report, TypeId::FLOAT); | 
					
						
							|  |  |  |   AppendBytes(report, reinterpret_cast<u8*>(&v), sizeof(v), false); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AnalyticsReporter::AnalyticsReporter() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_reporter_thread = std::thread(&AnalyticsReporter::ThreadProc, this); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AnalyticsReporter::~AnalyticsReporter() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   // 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(); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AnalyticsReporter::Send(AnalyticsReportBuilder&& report) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   // 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(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AnalyticsReporter::ThreadProc() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   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; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void StdoutAnalyticsBackend::Send(std::string report) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   printf("Analytics report sent:\n%s", | 
					
						
							|  |  |  |          HexDump(reinterpret_cast<const u8*>(report.data()), report.size()).c_str()); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HttpAnalyticsBackend::HttpAnalyticsBackend(const std::string& endpoint) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   CURL* curl = curl_easy_init(); | 
					
						
							|  |  |  |   if (curl) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str()); | 
					
						
							|  |  |  |     curl_easy_setopt(curl, CURLOPT_POST, true); | 
					
						
							|  |  |  |     curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &DummyCurlWriteFunction); | 
					
						
							|  |  |  |     curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 3000); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-19 12:54:40 +02:00
										 |  |  | #ifdef _WIN32
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     // ALPN support is enabled by default but requires Windows >= 8.1.
 | 
					
						
							|  |  |  |     curl_easy_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, false); | 
					
						
							| 
									
										
										
										
											2016-06-19 12:54:40 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     m_curl = curl; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HttpAnalyticsBackend::~HttpAnalyticsBackend() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (m_curl) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     curl_easy_cleanup(m_curl); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HttpAnalyticsBackend::Send(std::string report) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (!m_curl) | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, report.c_str()); | 
					
						
							|  |  |  |   curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, report.size()); | 
					
						
							|  |  |  |   curl_easy_perform(m_curl); | 
					
						
							| 
									
										
										
										
											2016-06-18 02:43:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace Common
 |