358 lines
10 KiB
C++
358 lines
10 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "InsightsFrontend/StoreService/TraceServerControl.h"
|
|
|
|
#include "CoreGlobals.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "ProfilingDebugging/TraceAuxiliary.h"
|
|
#include "SlateOptMacros.h"
|
|
|
|
// TraceAnalysis
|
|
#include "Trace/StoreClient.h"
|
|
|
|
// TraceInsightsCore
|
|
#include "InsightsCore/Common/Log.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define LOCTEXT_NAMESPACE "UE::Insights::FTraceServerControl"
|
|
|
|
namespace UE::Insights
|
|
{
|
|
|
|
// Number of times to attempt state change before failing
|
|
constexpr uint32 GStateChangeRetries = 6;
|
|
// Number of times to attempt reconnection while starting server
|
|
constexpr uint32 GStartConnectAttempts = 5;
|
|
// Number of seconds between each reconnection attempt
|
|
constexpr float GStartConnectFrequencySeconds = 0.5;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
FTraceServerControl::FTraceServerControl(const TCHAR* InHost, uint32 InPort, FName InStyleSet)
|
|
: Host(InHost)
|
|
, Port(InPort)
|
|
, StyleSet(InStyleSet)
|
|
{
|
|
bIsLocalHost = Host.Equals(TEXT("127.0.0.1")) || Host.Equals(TEXT("localhost"));
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
FTraceServerControl::~FTraceServerControl()
|
|
{
|
|
bIsCancelRequested = true;
|
|
FScopeLock _(&AsyncTaskLock); // wait for async tasks to complete
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
void FTraceServerControl::MakeMenu(FMenuBuilder& Builder)
|
|
{
|
|
// Create client
|
|
if (!Client)
|
|
{
|
|
Client.Reset(UE::Trace::FStoreClient::Connect(*Host, Port));
|
|
if (Client)
|
|
{
|
|
ChangeState(EState::NotConnected, EState::Connected);
|
|
}
|
|
}
|
|
|
|
// If connected kick off status and version check
|
|
if (State.load(std::memory_order_relaxed) == EState::Connected)
|
|
{
|
|
TriggerStatusUpdate();
|
|
}
|
|
|
|
if (bIsLocalHost)
|
|
{
|
|
Builder.BeginSection("LocalTraceServer", LOCTEXT("Section_LocalServer", "Local Trace Server"));
|
|
}
|
|
else
|
|
{
|
|
Builder.BeginSection("TraceServer", FText::Format(LOCTEXT("Section_RemoteServer", "Remote Trace Server '{0}'"), FText::FromString(Host)));
|
|
}
|
|
Builder.AddMenuEntry(
|
|
TAttribute<FText>::CreateLambda([this] { FScopeLock _(&StringsLock); return FText::FromString(StatusString.IsEmpty() ? TEXT("Not running") : StatusString); }),
|
|
FText::GetEmpty(),
|
|
FSlateIcon(),
|
|
FUIAction(FExecuteAction(), FCanExecuteAction::CreateLambda([]() { return false; })),
|
|
NAME_None,
|
|
EUserInterfaceActionType::Button
|
|
);
|
|
if (bIsLocalHost)
|
|
{
|
|
Builder.AddMenuEntry(
|
|
LOCTEXT("ServerControlSponsoredLabel", "Sponsored Mode"),
|
|
LOCTEXT("ServerControlSponsoredTooltip", "In sponsored mode the server only runs as long as local processes that uses it are alive."),
|
|
FSlateIcon(), //?
|
|
FUIAction(
|
|
FExecuteAction::CreateRaw(this, &FTraceServerControl::OnSponsored_Changed),
|
|
FCanExecuteAction::CreateRaw(this, &FTraceServerControl::AreControlsEnabled),
|
|
FGetActionCheckState::CreateLambda([this](){ return IsSponsored() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; })
|
|
),
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton
|
|
);
|
|
|
|
Builder.AddMenuEntry(
|
|
LOCTEXT("ServerControlStartLabel", "Start"),
|
|
LOCTEXT("ServerControlStartTooltip", "Starts the Trace Server"),
|
|
FSlateIcon(StyleSet, "Icons.TraceServerStart"),
|
|
FUIAction(
|
|
FExecuteAction::CreateRaw(this, &FTraceServerControl::OnStart_Clicked),
|
|
FCanExecuteAction::CreateRaw(this, &FTraceServerControl::CanServerBeStarted)
|
|
),
|
|
NAME_None,
|
|
EUserInterfaceActionType::Button
|
|
);
|
|
|
|
Builder.AddMenuEntry(
|
|
LOCTEXT("ServerControlStopLabel", "Stop"),
|
|
LOCTEXT("ServerControlStopTooltip", "Stops the Trace Server. Any running traces will be canceled."),
|
|
FSlateIcon(StyleSet, "Icons.TraceServerStop"),
|
|
FUIAction(
|
|
FExecuteAction::CreateRaw(this, &FTraceServerControl::OnStop_Clicked),
|
|
FCanExecuteAction::CreateRaw(this, &FTraceServerControl::CanServerBeStopped)
|
|
),
|
|
NAME_None,
|
|
EUserInterfaceActionType::Button
|
|
);
|
|
}
|
|
Builder.EndSection();
|
|
}
|
|
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const TCHAR* LexState(FTraceServerControl::EState state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case FTraceServerControl::EState::NotConnected: return TEXT("NotConnected");
|
|
case FTraceServerControl::EState::Connecting: return TEXT("Connecting");
|
|
case FTraceServerControl::EState::Connected: return TEXT("Connected");
|
|
case FTraceServerControl::EState::CheckStatus: return TEXT("CheckStatus");
|
|
case FTraceServerControl::EState::Command: return TEXT("Command");
|
|
default: return TEXT("Unknown");
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool FTraceServerControl::ChangeState(EState Expected, EState ChangeTo, uint32 Attempts)
|
|
{
|
|
while (Attempts-- > 0)
|
|
{
|
|
const bool bChanged = State.compare_exchange_strong(Expected, ChangeTo);
|
|
if (bChanged)
|
|
{
|
|
UE_LOG(LogInsights, VeryVerbose, TEXT("Changing state from '%s' -> '%s'"), LexState(Expected), LexState(ChangeTo))
|
|
return true;
|
|
}
|
|
UE_LOG(LogInsights, VeryVerbose, TEXT("Busy wait for '%s'..."), LexState(Expected));
|
|
FPlatformProcess::Sleep(0.25);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FTraceServerControl::TriggerStatusUpdate()
|
|
{
|
|
FGraphEventRef CheckStatusTask = FFunctionGraphTask::CreateAndDispatchWhenReady([this]
|
|
{
|
|
if (bIsCancelRequested)
|
|
{
|
|
return;
|
|
}
|
|
FScopeLock _(&AsyncTaskLock);
|
|
|
|
if (ChangeState(EState::Connected, EState::CheckStatus, GStateChangeRetries))
|
|
{
|
|
UpdateStatus();
|
|
ChangeState(EState::CheckStatus, EState::Connected);
|
|
}
|
|
});
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FTraceServerControl::UpdateStatus()
|
|
{
|
|
const UE::Trace::FStoreClient::FStatus* Status = Client->GetStatus();
|
|
|
|
// Check current status
|
|
const bool bServerIsRunning = Status != nullptr;
|
|
bCanServerBeStarted.store(!bServerIsRunning, std::memory_order_relaxed);
|
|
bCanServerBeStopped.store(bServerIsRunning, std::memory_order_relaxed);
|
|
|
|
if (!bServerIsRunning)
|
|
{
|
|
ResetStatus();
|
|
Client.Reset();
|
|
ChangeState(EState::CheckStatus, EState::NotConnected);
|
|
return;
|
|
}
|
|
|
|
TStringBuilder<64> PortsStringBuilder;
|
|
if (Status)
|
|
{
|
|
bSponsored.store(Status->GetSponsored());
|
|
PortsStringBuilder << TEXT("Recorder Port: ") << Status->GetRecorderPort()
|
|
<< TEXT(", Store Port: ") << Status->GetStorePort();
|
|
}
|
|
|
|
// If not previously checked, also query version information
|
|
if (StatusString.IsEmpty())
|
|
{
|
|
const UE::Trace::FStoreClient::FVersion* Version = Client->GetVersion();
|
|
if (Version)
|
|
{
|
|
TStringBuilder<64> StatusStringBuilder;
|
|
StatusStringBuilder << TEXT("Version: ") << Version->GetMajorVersion() << TEXT(".") << Version->GetMinorVersion();
|
|
// Only print configuration if it's not release
|
|
if (!Version->GetConfiguration().Equals(UTF8TEXT("Release")))
|
|
{
|
|
StatusStringBuilder << TEXT(" (") << Version->GetConfiguration() << TEXT(")");
|
|
}
|
|
StatusStringBuilder << TEXT(", ") << PortsStringBuilder;
|
|
{
|
|
FScopeLock Lock(&StringsLock);
|
|
StatusString = StatusStringBuilder.ToString();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FTraceServerControl::ResetStatus()
|
|
{
|
|
FScopeLock Lock(&StringsLock);
|
|
StatusString.Empty();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FTraceServerControl::OnStart_Clicked()
|
|
{
|
|
FGraphEventRef CommandTask = FFunctionGraphTask::CreateAndDispatchWhenReady([this]
|
|
{
|
|
if (bIsCancelRequested)
|
|
{
|
|
return;
|
|
}
|
|
FScopeLock _(&AsyncTaskLock);
|
|
|
|
if (ChangeState(EState::NotConnected, EState::Command, GStateChangeRetries))
|
|
{
|
|
#if UE_TRACE_SERVER_CONTROLS_ENABLED
|
|
FTraceServerControls::Start();
|
|
#endif
|
|
ChangeState(EState::Command, EState::Connecting);
|
|
|
|
if (!Client)
|
|
{
|
|
for (uint32 Attempts = 0; Attempts < GStartConnectAttempts; ++Attempts)
|
|
{
|
|
UE::Trace::FStoreClient* NewClient = UE::Trace::FStoreClient::Connect(TEXT("127.0.0.1"));
|
|
if (bIsCancelRequested)
|
|
{
|
|
if (NewClient)
|
|
{
|
|
delete NewClient;
|
|
}
|
|
break;
|
|
}
|
|
if (NewClient)
|
|
{
|
|
Client.Reset(NewClient);
|
|
break;
|
|
}
|
|
FPlatformProcess::Sleep(GStartConnectFrequencySeconds);
|
|
}
|
|
}
|
|
|
|
if (Client)
|
|
{
|
|
ChangeState(EState::Connecting, EState::Connected);
|
|
}
|
|
else
|
|
{
|
|
ChangeState(EState::Connecting, EState::NotConnected);
|
|
UE_LOG(LogInsights, Warning, TEXT("Failed to connect to store."));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogInsights, Warning, TEXT("Failed to start server."));
|
|
}
|
|
});
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FTraceServerControl::OnStop_Clicked()
|
|
{
|
|
FGraphEventRef CommandTask = FFunctionGraphTask::CreateAndDispatchWhenReady([this]
|
|
{
|
|
if (bIsCancelRequested)
|
|
{
|
|
return;
|
|
}
|
|
FScopeLock _(&AsyncTaskLock);
|
|
|
|
if (ChangeState(EState::Connected, EState::Command, GStateChangeRetries))
|
|
{
|
|
#if UE_TRACE_SERVER_CONTROLS_ENABLED
|
|
FTraceServerControls::Stop();
|
|
#endif
|
|
Client.Reset();
|
|
ResetStatus();
|
|
ChangeState(EState::Command, EState::NotConnected);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogInsights, Warning, TEXT("Failed to stop server."));
|
|
}
|
|
});
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FTraceServerControl::OnSponsored_Changed()
|
|
{
|
|
FGraphEventRef CommandTask = FFunctionGraphTask::CreateAndDispatchWhenReady([this]
|
|
{
|
|
if (bIsCancelRequested)
|
|
{
|
|
return;
|
|
}
|
|
FScopeLock _(&AsyncTaskLock);
|
|
|
|
if (ChangeState(EState::Connected, EState::Command, GStateChangeRetries))
|
|
{
|
|
const bool Success = Client->SetSponsored(!IsSponsored());
|
|
ChangeState(EState::Command, EState::Connected);
|
|
if (Success)
|
|
{
|
|
TriggerStatusUpdate();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogInsights, Warning, TEXT("Failed to set sponsored mode."));
|
|
}
|
|
});
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
} // namespace UE::Insights
|
|
|
|
#undef LOCTEXT_NAMESPACE |