Files
UnrealEngineUWP/Engine/Source/Runtime/Analytics/QoSReporter/Private/QoSReporterModule.cpp
Andrew Grant f25badee7f Copying //UE4/Orion-Staging to //UE4/Main (Origin: //Orion/Dev-General @2826496)
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2826201 on 2016/01/13 by Zabir.Hoque

	Add more verbose logging to try to understand #OR-11297

	#lockdown Andrew.Grant
	#CodeReview Marcus.Wassmer
	#RB none
	#TESTS compiled Win64 debug editor, ran agora_p

Change 2826170 on 2016/01/13 by Marcus.Wassmer

	Flush unloaded resource properly in LoadMap
	#codereview Gil.Gribb
	#rb none
	#test cycling game.  memory is freed properly now.
	#lockdown Andrew.Grant

Change 2826135 on 2016/01/12 by Michael.Noland

	Orion: Improve login screen on PC to reduce the potential impact of framerate on data center ping calculation
	- Disabled async streaming for the duration of the QOS ping measurement to avoid hitches
	- Added a circular throbber in the top left corner of the login screen indicating that something is async streaming (as a diagnostic tool for users affected by the datacenter ping, can be removed in the future)
	- Added logging of the current average frame time when the datacenter ping is finalized
	- Added a 'Pick Ideal Settings' button to the login screen (note: on the actual screen, not the login widget, so it will not appear on PS4)
	#jira OR-12453
	#rb paul.moore
	#tests Ran a QOS server and client and verified that the new logging is occurring, tried out the new benchmark button, etc...

	Merging CL# 2826128 using //Orion/Main_to_//Orion/Dev-General

Change 2826131 on 2016/01/12 by Michael.Noland

	#UE4 - added print out of MS/FPS during Qos ping evaluation
	#rb michael.noland
	#tests loaded up through login screen to see output

	Merging CL# 2825678 using //Orion/Main_to_//Orion/Dev-General

Change 2826128 on 2016/01/12 by Michael.Noland

	Orion: Improve login screen on PC to reduce the potential impact of framerate on data center ping calculation
	- Disabled async streaming for the duration of the QOS ping measurement to avoid hitches
	- Added a circular throbber in the top left corner of the login screen indicating that something is async streaming (as a diagnostic tool for users affected by the datacenter ping, can be removed in the future)
	- Added logging of the current average frame time when the datacenter ping is finalized
	- Added a 'Pick Ideal Settings' button to the login screen (note: on the actual screen, not the login widget, so it will not appear on PS4)
	#jira OR-12453
	#rb paul.moore
	#tests Ran a QOS server and client and verified that the new logging is occurring, tried out the new benchmark button, etc...

	Merging CL# 2826116 using //Orion/Release-Next->//Orion/Main

Change 2826116 on 2016/01/12 by Michael.Noland

	Orion: Improve login screen on PC to reduce the potential impact of framerate on data center ping calculation
	- Disabled async streaming for the duration of the QOS ping measurement to avoid hitches
	- Added a circular throbber in the top left corner of the login screen indicating that something is async streaming (as a diagnostic tool for users affected by the datacenter ping, can be removed in the future)
	- Added logging of the current average frame time when the datacenter ping is finalized
	- Added a 'Pick Ideal Settings' button to the login screen (note: on the actual screen, not the login widget, so it will not appear on PS4)
	#jira OR-12453
	#rb paul.moore
	#tests Ran a QOS server and client and verified that the new logging is occurring, tried out the new benchmark button, etc...
	#lockdown andrew.grant
	#codereview josh.markiewicz

Change 2825772 on 2016/01/12 by Dmitry.Rekman

	Linux signal handling improvements.

	- Switch crash handlers to use "crash malloc" (preallocated memory) on crash.
	- Remove unnecessary memory allocations from graceful termination handler.

	#rb none
	#tests Run the Linux server and crashed it a few times.
	#codereview David.Vossel, Michael.Trepka

Change 2825768 on 2016/01/12 by Josh.Markiewicz

	#UE4 - added print out of MS/FPS during Qos ping evaluation
	#rb michael.noland
	#tests loaded up through login screen to see output

Change 2825703 on 2016/01/12 by Brian.Karis

	Switched on new motion blur. Set temporal AA sharpness to 1.

	#rb none
	#TESTS editor

Change 2825689 on 2016/01/12 by Lina.Halper

	Fix for get animation notify crash

	https://jira.ol.epicgames.net/browse/OR-12248
	https://jira.ol.epicgames.net/browse/OR-12348

	- Also fixed the crash in preview of persona due to blend sample cache contains previous animation data
	- Also fixed blend space player to reinitialize cache data
	- The main issue is marker doesn't clamp the length, causing notifies ensure to trigger.

	#rb : Laurent.Delayen
	#tests: 10 Sparrows bot match for long time
	#code review: Martin.Wilson
	#lockdown: Andrew.Grant

Change 2825680 on 2016/01/12 by Martin.Mittring

	fixed all cases with r.Tonemapper.ScreenPercentage, ScreenPercentage, Fringe, Vignette, ViewRect, flickering with transluceny (View members have been modified while other thread was reading)
	#rb:Olaf.Piesche, David.Hill
	#test: PC, many cases

Change 2825579 on 2016/01/12 by Chris.Bunner

	Force shadow shape bone indices on the required update list.
	#rb Lina.Halper, Rolando.Caloca
	#tests Editor
	#codereview Daniel.Wright
	#jira OR-12339

Change 2825443 on 2016/01/12 by Martin.Mittring
2016-01-14 08:11:47 -05:00

307 lines
10 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "QoSReporterPrivatePCH.h"
#include "Core.h"
#include "Json.h"
#include "Analytics.h"
#include "Http.h"
#include "EngineVersion.h"
#include "QoSReporter.h"
DEFINE_LOG_CATEGORY(LogQoSReporter);
IMPLEMENT_MODULE(FQoSReporterModule, QoSReporter);
// helps to version QoS events (date*10 to allow for 10 revisions per day)
#define QOS_EVENTS_REVISION 201512170
FString FQoSReporterModule::Config::GetDefaultAppVersion()
{
return FString::Printf(TEXT("UE4-CL-%d"), FEngineVersion::Current().GetChangelist());
}
FString FQoSReporterModule::Config::GetDefaultAppEnvironment()
{
return FAnalytics::ToString(FAnalytics::Get().GetBuildType());
}
class FAnalyticsProviderQoSReporter : public IAnalyticsProvider
{
public:
FAnalyticsProviderQoSReporter(const FQoSReporterModule::Config& ConfigValues);
/** This provider does not have a concept of sessions */
virtual bool StartSession(const TArray<FAnalyticsEventAttribute>& Attributes) override { return true; };
/** This provider does not have a concept of sessions */
virtual void EndSession() override {};
/** This provider is not supposed to send many events, and due to nature of QoS we don't want to cache them */
virtual void FlushEvents() override {};
/** This provider is not using user IDs */
virtual void SetUserID(const FString& InUserID) override {};
/** This provider is not using user IDs, but we're (ab)using this API to return InstanceId */
virtual FString GetUserID() const override { return InstanceId.ToString(); };
/** This provider does not have a concept of sessions */
virtual FString GetSessionID() const override { checkf(false, TEXT("FAnalyticsProviderQoSReporter is not session based")); return TEXT("UnknownSessionId"); };
/** This provider does not have a concept of sessions */
virtual bool SetSessionID(const FString& InSessionID) override { return false; };
/** We're (ab)using this API to set DeploymentName */
virtual void SetLocation(const FString& InLocation) override { DeploymentName = InLocation; };
virtual void RecordEvent(const FString& EventName, const TArray<FAnalyticsEventAttribute>& Attributes) override;
virtual ~FAnalyticsProviderQoSReporter();
FString GetAPIKey() const { return APIKey; }
private:
/** API key (also known as "upload type" on data router) */
FString APIKey;
/** API Server to use (also known as "endpoint"). */
FString APIServer;
/** The AppVersion to use. */
FString AppVersion;
/** The AppEnvironment to use. */
FString AppEnvironment;
/** The upload type to use. */
FString UploadType;
/** Unique identifier for this QoS reporter instance (only changed on module initialization) */
FGuid InstanceId;
/** Deployment name (if empty, it won't be sent). */
FString DeploymentName;
/**
* Delegate called when an event Http request completes
*/
void EventRequestComplete(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded);
/**
* Returns application role (server, client)
*/
static FString GetApplicationRole();
};
void FQoSReporterModule::StartupModule()
{
// FQoSReporter::Initialize() is expected to be called by game code with proper config
}
void FQoSReporterModule::ShutdownModule()
{
FQoSReporter::Shutdown();
}
TSharedPtr<IAnalyticsProvider> FQoSReporterModule::CreateAnalyticsProvider(const FAnalytics::FProviderConfigurationDelegate& GetConfigValue) const
{
if (GetConfigValue.IsBound())
{
Config ConfigValues;
ConfigValues.APIServer = GetConfigValue.Execute(Config::GetKeyNameForAPIServer(), true);
ConfigValues.APIKey = GetConfigValue.Execute(Config::GetKeyNameForAPIKey(), false);
ConfigValues.AppVersion = GetConfigValue.Execute(Config::GetKeyNameForAppVersion(), false);
ConfigValues.AppEnvironment = GetConfigValue.Execute(Config::GetKeyNameForAppEnvironment(), false);
ConfigValues.UploadType = GetConfigValue.Execute(Config::GetKeyNameForUploadType(), false);
return CreateAnalyticsProvider(ConfigValues);
}
else
{
UE_LOG(LogQoSReporter, Warning, TEXT("CreateAnalyticsProvider called with an unbound delegate"));
}
return nullptr;
}
TSharedPtr<IAnalyticsProvider> FQoSReporterModule::CreateAnalyticsProvider(const Config& ConfigValues) const
{
return TSharedPtr<IAnalyticsProvider>(new FAnalyticsProviderQoSReporter(ConfigValues));
}
/**
* Perform any initialization.
*/
FAnalyticsProviderQoSReporter::FAnalyticsProviderQoSReporter(const FQoSReporterModule::Config& ConfigValues)
{
UE_LOG(LogQoSReporter, Verbose, TEXT("Initializing QoS Reporter"));
APIKey = ConfigValues.APIKey;
if (APIKey.IsEmpty())
{
UE_LOG(LogQoSReporter, Error, TEXT("QoS API key is not configured, no QoS metrics will be reported."));
}
APIServer = ConfigValues.APIServer;
if (APIServer.IsEmpty())
{
UE_LOG(LogQoSReporter, Error, TEXT("QoS API server is not configured, no QoS metrics will be reported."));
}
AppVersion = ConfigValues.AppVersion;
if (AppVersion.IsEmpty())
{
AppVersion = ConfigValues.GetDefaultAppVersion();
}
AppEnvironment = ConfigValues.AppEnvironment;
if (AppEnvironment.IsEmpty())
{
AppEnvironment = ConfigValues.GetDefaultAppEnvironment();
}
UploadType = ConfigValues.UploadType;
if (UploadType.IsEmpty())
{
UploadType = ConfigValues.GetDefaultUploadType();
}
// add a unique id
InstanceId = FGuid::NewGuid();
FPlatformMisc::CreateGuid(InstanceId);
UE_LOG(LogQoSReporter, Log, TEXT("QoSReporter initialized (Guid = '%s')"), *InstanceId.ToString());
UE_LOG(LogQoSReporter, Log, TEXT("APIKey = '%s'. APIServer = '%s'. AppVersion = '%s'. AppEnvironment = '%s'"), *APIKey, *APIServer, *AppVersion, *AppEnvironment);
}
FAnalyticsProviderQoSReporter::~FAnalyticsProviderQoSReporter()
{
UE_LOG(LogQoSReporter, Verbose, TEXT("Destroying QoS Reporter"));
EndSession();
}
void FAnalyticsProviderQoSReporter::RecordEvent(const FString& EventName, const TArray<FAnalyticsEventAttribute>& InAttributes)
{
if (APIKey.IsEmpty() || APIServer.IsEmpty())
{
return;
}
// add attributes common to each QoS event first
TArray<FAnalyticsEventAttribute> Attributes;
Attributes.Add(FAnalyticsEventAttribute(TEXT("QoSRevision"), QOS_EVENTS_REVISION));
Attributes.Add(FAnalyticsEventAttribute(TEXT("Role"), GetApplicationRole()));
Attributes.Add(FAnalyticsEventAttribute(TEXT("SystemId"), FPlatformMisc::GetOperatingSystemId()));
Attributes.Add(FAnalyticsEventAttribute(TEXT("InstanceId"), InstanceId.ToString()));
if (LIKELY(DeploymentName.Len() > 0))
{
Attributes.Add(FAnalyticsEventAttribute(TEXT("Deployment"), DeploymentName));
}
else
{
UE_LOG(LogQoSReporter, Warning, TEXT("QoSReporter was not configured for any deployment; metrics will be likely discarded."));
}
// append the rest
Attributes += InAttributes;
// encode params as Json
if (ensure(FModuleManager::Get().IsModuleLoaded("HTTP")))
{
FString Payload;
FDateTime CurrentTime = FDateTime::UtcNow();
TSharedRef< TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR> > > JsonWriter = TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR> >::Create(&Payload);
JsonWriter->WriteObjectStart();
JsonWriter->WriteArrayStart(TEXT("Events"));
// write just a single event
JsonWriter->WriteObjectStart();
JsonWriter->WriteValue(TEXT("EventName"), EventName);
if (Attributes.Num() > 0)
{
// optional attributes for this event
for (int32 AttrIdx = 0; AttrIdx < Attributes.Num(); AttrIdx++)
{
const FAnalyticsEventAttribute& Attr = Attributes[AttrIdx];
JsonWriter->WriteValue(Attr.AttrName, Attr.AttrValue);
}
}
JsonWriter->WriteObjectEnd();
JsonWriter->WriteArrayEnd();
JsonWriter->WriteObjectEnd();
JsonWriter->Close();
FString URLPath = FString::Printf(TEXT("?AppID=%s&AppVersion=%s&AppEnvironment=%s&UploadType=%s"),
*FGenericPlatformHttp::UrlEncode(APIKey),
*FGenericPlatformHttp::UrlEncode(AppVersion),
*FGenericPlatformHttp::UrlEncode(AppEnvironment),
*FGenericPlatformHttp::UrlEncode(UploadType)
);
// Recreate the URLPath for logging because we do not want to escape the parameters when logging.
// We cannot simply UrlEncode the entire Path after logging it because UrlEncode(Params) != UrlEncode(Param1) & UrlEncode(Param2) ...
UE_LOG(LogQoSReporter, VeryVerbose, TEXT("[%s] QoS URL:%s?AppID=%s&AppVersion=%s&AppEnvironment=%s&UploadType=%s. Payload:%s"),
*APIKey,
*APIServer,
*APIKey,
*AppVersion,
*AppEnvironment,
*UploadType,
*Payload);
// Create/send Http request for an event
TSharedRef<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json; charset=utf-8"));
HttpRequest->SetURL(APIServer + URLPath);
HttpRequest->SetVerb(TEXT("POST"));
HttpRequest->SetContentAsString(Payload);
HttpRequest->OnProcessRequestComplete().BindRaw(this, &FAnalyticsProviderQoSReporter::EventRequestComplete);
HttpRequest->ProcessRequest();
}
}
void FAnalyticsProviderQoSReporter::EventRequestComplete(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded)
{
if (bSucceeded && HttpResponse.IsValid())
{
// normal operation is silent, but any problems are reported as warnings
if (EHttpResponseCodes::IsOk(HttpResponse->GetResponseCode()))
{
UE_LOG(LogQoSReporter, VeryVerbose, TEXT("QoS response for [%s]. Code: %d. Payload: %s"),
*HttpRequest->GetURL(), HttpResponse->GetResponseCode(), *HttpResponse->GetContentAsString());
}
else
{
UE_LOG(LogQoSReporter, Warning, TEXT("Bad QoS response for [%s] - code: %d. Payload: %s"),
*HttpRequest->GetURL(), HttpResponse->GetResponseCode(), *HttpResponse->GetContentAsString());
}
}
else
{
// if we cannot report QoS metrics this is pretty bad; report at least a warning
UE_LOG(LogQoSReporter, Warning, TEXT("QoS response for [%s]. No response"), *HttpRequest->GetURL());
}
}
/**
* Returns application role (server, client)
*/
FString FAnalyticsProviderQoSReporter::GetApplicationRole()
{
if (IsRunningDedicatedServer())
{
static FString DedicatedServer(TEXT("DedicatedServer"));
return DedicatedServer;
}
else if (IsRunningClientOnly())
{
static FString ClientOnly(TEXT("ClientOnly"));
return ClientOnly;
}
else if (IsRunningGame())
{
static FString StandaloneGame(TEXT("StandaloneGame"));
return StandaloneGame;
}
static FString Editor(TEXT("Editor"));
return Editor;
}