初始化提交

This commit is contained in:
2025-07-09 01:08:35 +08:00
parent d3296791cf
commit 62e0f56c60
618 changed files with 173543 additions and 0 deletions

View File

@ -0,0 +1,248 @@
// Tencent is pleased to support the open source community by making sluaunreal available.
// Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
// Licensed under the BSD 3-Clause License (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the License at
// https://opensource.org/licenses/BSD-3-Clause
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
#include "slua_profile.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "HAL/FileManager.h"
#include "Containers/Ticker.h"
#include "LuaState.h"
#if WITH_EDITOR
#include "LevelEditor.h"
#include "EditorStyle.h"
#include "EditorStyleSet.h"
#endif
#include "LuaProfiler.h"
#include "slua_remote_profile.h"
#include "ProfileDataDefine.h"
#include "SluaProfilerDataManager.h"
DEFINE_LOG_CATEGORY(LogSluaProfile)
#define LOCTEXT_NAMESPACE "Fslua_profileModule"
#define ROOT_NAME "ROOT"
namespace {
static const FName slua_profileTabName(TEXT("slua_profile"));
typedef TMap<int64, NS_SLUA::LuaMemInfo> LuaMemInfoMap;
LuaMemInfoMap memoryInfo;
typedef TQueue<MemoryFramePtr, EQueueMode::Mpsc> MemoryFrameQueue;
MemoryFrameQueue memoryQueue;
MemoryFramePtr currentMemory = MakeShareable(new MemoryFrame());
ProfileCallInfoArray profilerStack;
ProfileNodePtr funcProfilerRoot;
TQueue<ProfileNodePtr, EQueueMode::Mpsc> funcProfilerNodeQueue;
}
void Fslua_profileModule::StartupModule()
{
#if WITH_EDITOR
Flua_profileCommands::Register();
PluginCommands = MakeShareable(new FUICommandList);
PluginCommands->MapAction(
Flua_profileCommands::Get().OpenPluginWindow,
FExecuteAction::CreateRaw(this, &Fslua_profileModule::PluginButtonClicked),
FCanExecuteAction());
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
{
TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
MenuExtender->AddMenuExtension("WindowLayout", EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateRaw(this, &Fslua_profileModule::AddMenuExtension));
LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
}
if (GIsEditor && !IsRunningCommandlet())
{
sluaProfilerInspector = MakeShareable(new SProfilerInspector);
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(slua_profileTabName,
FOnSpawnTab::CreateRaw(this, &Fslua_profileModule::OnSpawnPluginTab))
.SetDisplayName(LOCTEXT("Flua_wrapperTabTitle", "slua Profiler"))
.SetMenuType(ETabSpawnerMenuType::Hidden);
TickDelegate = FTickerDelegate::CreateRaw(this, &Fslua_profileModule::Tick);
#if ENGINE_MAJOR_VERSION==5
TickDelegateHandle = FTSTicker::GetCoreTicker().AddTicker(TickDelegate);
#else
TickDelegateHandle = FTicker::GetCoreTicker().AddTicker(TickDelegate);
#endif
}
#endif
}
void Fslua_profileModule::ShutdownModule()
{
#if WITH_EDITOR
sluaProfilerInspector = nullptr;
ClearCurProfiler();
Flua_profileCommands::Unregister();
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(slua_profileTabName);
#endif
}
void Fslua_profileModule::PluginButtonClicked()
{
#if (ENGINE_MINOR_VERSION<25) && (ENGINE_MAJOR_VERSION==4)
FGlobalTabmanager::Get()->InvokeTab(slua_profileTabName);
#else
FGlobalTabmanager::Get()->TryInvokeTab(slua_profileTabName);
#endif
}
bool Fslua_profileModule::Tick(float DeltaTime)
{
if (!tabOpened)
{
return true;
}
//UE_LOG(LogTemp, Warning, TEXT("Fslua_profileModule::Tick"));
while (!funcProfilerNodeQueue.IsEmpty())
{
MemoryFramePtr memoryFrame;
memoryQueue.Dequeue(memoryFrame);
TSharedPtr<FunctionProfileNode> funcProfilerNode;
funcProfilerNodeQueue.Dequeue(funcProfilerNode);
sluaProfilerInspector->Refresh(funcProfilerNode, memoryInfo, memoryFrame);
}
return true;
}
TSharedRef<class SDockTab> Fslua_profileModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
{
if (sluaProfilerInspector.IsValid())
{
sluaProfilerInspector->StartChartRolling();
auto tab = sluaProfilerInspector->GetSDockTab();
tab->SetOnTabClosed(SDockTab::FOnTabClosedCallback::CreateRaw(this, &Fslua_profileModule::OnTabClosed));
SluaProfilerDataManager::InitProfileNode(funcProfilerRoot, *FLuaFunctionDefine::Root, 0);
sluaProfilerInspector->ProfileServer = MakeShareable(new NS_SLUA::FProfileServer());
sluaProfilerInspector->ProfileServer->OnProfileMessageRecv().BindLambda([this](NS_SLUA::FProfileMessagePtr Message) {
this->debug_hook_c(Message);
});
tabOpened = true;
return tab;
}
else
{
return SNew(SDockTab).TabRole(ETabRole::NomadTab);
}
}
//////////////////////////////////////////////////////////////////////////
#if WITH_EDITOR
Flua_profileCommands::Flua_profileCommands()
: TCommands<Flua_profileCommands>(slua_profileTabName,
#if ENGINE_MAJOR_VERSION==5 && ENGINE_MINOR_VERSION>0
NSLOCTEXT("Contexts", "slua_profile", "slua_profile Plugin"), NAME_None, FAppStyle::GetAppStyleSetName())
#else
NSLOCTEXT("Contexts", "slua_profile", "slua_profile Plugin"), NAME_None, FEditorStyle::GetStyleSetName())
#endif
{
}
void Flua_profileCommands::RegisterCommands()
{
UI_COMMAND(OpenPluginWindow, "slua Profile", "Open slua Profile tool", EUserInterfaceActionType::Button, FInputGesture());
}
#endif
void Fslua_profileModule::ClearCurProfiler()
{
SluaProfilerDataManager::InitProfileNode(funcProfilerRoot, *FLuaFunctionDefine::Root, 0);
currentMemory = MakeShareable(new MemoryFrame());
currentMemory->bMemoryTick = false;
profilerStack.Empty();
}
void Fslua_profileModule::AddMenuExtension(FMenuBuilder& Builder)
{
#if WITH_EDITOR
Builder.AddMenuEntry(Flua_profileCommands::Get().OpenPluginWindow);
#endif
}
void Fslua_profileModule::OnTabClosed(TSharedRef<SDockTab>)
{
tabOpened = false;
}
void Fslua_profileModule::debug_hook_c(NS_SLUA::FProfileMessagePtr Message)
{
//int event, double nanoseconds, int linedefined, const FString& name, const FString& short_src, TArray<NS_SLUA::LuaMemInfo> memoryInfoList
int event = Message->Event;
double nanoseconds = Message->Time;
int linedefined = Message->Linedefined;
const FString& name = Message->Name;
const FString& short_src = Message->ShortSrc;
if (event == NS_SLUA::ProfilerHookEvent::PHE_CALL)
{
if (linedefined == -1 && name.IsEmpty())
{
return;
}
//UE_LOG(LogTemp, Log, TEXT("Profile Call %s"), *functionName);
SluaProfilerDataManager::WatchBegin(short_src, linedefined, name, nanoseconds, funcProfilerRoot, profilerStack);
}
else if (event == NS_SLUA::ProfilerHookEvent::PHE_RETURN)
{
if (linedefined == -1 && name.IsEmpty())
{
return;
}
//UE_LOG(LogTemp, Log, TEXT("Profile Return %s"), *functionName);
SluaProfilerDataManager::WatchEnd(short_src, linedefined, name, nanoseconds, profilerStack);
}
else if (event == NS_SLUA::ProfilerHookEvent::PHE_TICK)
{
funcProfilerNodeQueue.Enqueue(funcProfilerRoot);
memoryQueue.Enqueue(currentMemory);
ClearCurProfiler();
}
else if (event == NS_SLUA::ProfilerHookEvent::PHE_MEMORY_TICK)
{
currentMemory->memoryInfoList = Message->memoryInfoList;
currentMemory->bMemoryTick = true;
}
else if (event == NS_SLUA::ProfilerHookEvent::PHE_MEMORY_INCREACE)
{
currentMemory->memoryIncrease = Message->memoryIncrease;
}
else if (event == NS_SLUA::ProfilerHookEvent::PHE_ENTER_COROUTINE)
{
//UE_LOG(LogTemp, Log, TEXT("Profile CoBegin %s"), *functionName);
SluaProfilerDataManager::CoroutineBegin(linedefined, name, nanoseconds, funcProfilerRoot, profilerStack);
}
else if (event == NS_SLUA::ProfilerHookEvent::PHE_EXIT_COROUTINE)
{
//UE_LOG(LogTemp, Log, TEXT("Profile CoEnd"));
SluaProfilerDataManager::CoroutineEnd(nanoseconds, profilerStack);
}
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(Fslua_profileModule, slua_profile)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,389 @@
// Tencent is pleased to support the open source community by making sluaunreal available.
// Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
// Licensed under the BSD 3-Clause License (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the License at
// https://opensource.org/licenses/BSD-3-Clause
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
#include "slua_remote_profile.h"
#include "slua_profile.h"
#include "Common/TcpListener.h"
#include "Templates/SharedPointer.h"
#include "SocketSubsystem.h"
#include "SluaUtil.h"
#include "LuaProfiler.h"
FAutoConsoleVariableRef CVarSluaProfilerPort(
TEXT("slua.ProfilerPort"),
NS_SLUA::FProfileServer::Port,
TEXT("Slua profiler server port.\n"),
ECVF_Default);
namespace NS_SLUA
{
int32 FProfileServer::Port = 8081;
FProfileServer::FProfileServer()
: Thread(nullptr)
, bStop(true)
{
Thread = FRunnableThread::Create(this, TEXT("FProfileServer"), 0, TPri_Normal);
}
FProfileServer::~FProfileServer()
{
StopTransport();
Thread->WaitForCompletion();
delete Thread;
}
FOnProfileMessageDelegate& FProfileServer::OnProfileMessageRecv()
{
return OnProfileMessageDelegate;
}
bool FProfileServer::Init()
{
bStop = false;
ListenEndpoint.Address = FIPv4Address(0, 0, 0, 0);
ListenEndpoint.Port = (std::numeric_limits<uint16>::min() < Port) && (Port < std::numeric_limits<uint16>::max()) ? Port : 8081;
Listener = new FTcpListener(ListenEndpoint);
Listener->OnConnectionAccepted().BindRaw(this, &FProfileServer::HandleConnectionAccepted);
return true;
}
TArray<TSharedPtr<FProfileConnection>> FProfileServer::GetConnections() {
return Connections;
}
uint32 FProfileServer::Run()
{
while (!bStop)
{
TSharedPtr<FProfileConnection> Connection;
while (PendingConnections.Dequeue(Connection))
{
Connection->Start();
Connections.Add(Connection);
}
int32 ActiveConnections = 0;
for (int32 Index = 0; Index < Connections.Num(); Index++)
{
auto& conn = Connections[Index];
// handle disconnected by remote
switch (conn->GetConnectionState())
{
case FProfileConnection::STATE_Connected:
ActiveConnections++;
break;
case FProfileConnection::STATE_Disconnected:
Connections.RemoveAtSwap(Index);
Index--;
break;
default:
break;
}
}
for (auto& conn : Connections)
{
TSharedPtr<FProfileMessage, ESPMode::ThreadSafe> Message;
while (conn->ReceiveData(Message))
{
(void)OnProfileMessageDelegate.ExecuteIfBound(Message);
}
}
FPlatformProcess::Sleep(ActiveConnections > 0 ? 0.01f : 1.f);
}
return 0;
}
void FProfileServer::Stop()
{
bStop = true;
}
void FProfileServer::StopTransport()
{
bStop = true;
if (Listener)
{
delete Listener;
Listener = nullptr;
}
for (auto& Connection : Connections)
{
Connection->Close();
}
Connections.Empty();
PendingConnections.Empty();
}
bool FProfileServer::HandleConnectionAccepted(FSocket* ClientSocket, const FIPv4Endpoint& ClientEndpoint)
{
PendingConnections.Enqueue(MakeShareable(new FProfileConnection(ClientSocket, ClientEndpoint)));
return true;
}
FProfileConnection::FProfileConnection(FSocket* InSocket, const FIPv4Endpoint& InRemoteEndpoint)
: RemoteEndpoint(InRemoteEndpoint)
, Socket(InSocket)
, Thread(nullptr)
, TotalBytesReceived(0)
, RecvMessageDataRemaining(0)
, hookEvent(0)
, ConnectionState(EConnectionState::STATE_Connecting)
, bRun(false)
{
int32 NewSize = 0;
// Socket->IsNotEncrypt = true; // For PUBG Mobile
Socket->SetReceiveBufferSize(2 * 1024 * 1024, NewSize);
Socket->SetSendBufferSize(2 * 1024 * 1024, NewSize);
}
FProfileConnection::~FProfileConnection()
{
if (Thread != nullptr)
{
if (bRun)
{
bRun = false;
Thread->WaitForCompletion();
}
delete Thread;
}
if (Socket)
{
Socket->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(Socket);
Socket = nullptr;
}
}
void FProfileConnection::Start()
{
check(Thread == nullptr);
bRun = true;
Thread = FRunnableThread::Create(this, *FString::Printf(TEXT("FProfileConnection %s"), *RemoteEndpoint.ToString()), 128 * 1024, TPri_Normal);
}
FProfileConnection::EConnectionState FProfileConnection::GetConnectionState() const
{
return ConnectionState;
}
bool FProfileConnection::ReceiveData(TSharedPtr<FProfileMessage, ESPMode::ThreadSafe>& OutMessage)
{
if (Inbox.Dequeue(OutMessage))
{
return true;
}
return false;
}
void FProfileConnection::Close()
{
// let the thread shutdown on its own
if (Thread != nullptr)
{
bRun = false;
Thread->WaitForCompletion();
delete Thread;
Thread = nullptr;
}
// if there a socket, close it so our peer will get a quick disconnect notification
if (Socket)
{
Socket->Close();
}
}
bool FProfileConnection::Init()
{
ConnectionState = EConnectionState::STATE_Connected;
return true;
}
FSocket* FProfileConnection::GetSocket()
{
if(Socket) return Socket;
return nullptr;
}
uint32 FProfileConnection::Run()
{
while (bRun)
{
if ((!ReceiveMessages() || Socket->GetConnectionState() == SCS_ConnectionError) && bRun)
{
bRun = false;
}
FPlatformProcess::SleepNoStats(0.0001f);
}
ConnectionState = EConnectionState::STATE_Disconnected;
return 0;
}
void FProfileConnection::Stop()
{
if (Socket)
{
Socket->Close();
}
}
void FProfileConnection::Exit()
{
}
bool FProfileConnection::ReceiveMessages()
{
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
uint32 PendingDataSize = 0;
// check if the socket has closed
{
int32 BytesRead;
uint8 Dummy;
if (!Socket->Recv(&Dummy, 1, BytesRead, ESocketReceiveFlags::Peek))
{
UE_LOG(LogSluaProfile, Verbose, TEXT("Dummy read failed with code %d"), (int32)SocketSubsystem->GetLastErrorCode());
return false;
}
}
// Block waiting for some data
if (!Socket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::FromSeconds(1.0)))
{
return (Socket->GetConnectionState() != SCS_ConnectionError);
}
// keep going until we have no data.
for (;;)
{
int32 BytesRead = 0;
// See if we're in the process of receiving a (large) message
if (RecvMessageDataRemaining == 0)
{
// no partial message. Try to receive the size of a message
if (!Socket->HasPendingData(PendingDataSize) || (PendingDataSize < sizeof(uint32)))
{
// no messages
return true;
}
FArrayReader MessagesizeData = FArrayReader(true);
MessagesizeData.SetNumUninitialized(sizeof(uint32));
// read message size from the stream
BytesRead = 0;
if (!Socket->Recv(MessagesizeData.GetData(), sizeof(uint32), BytesRead))
{
UE_LOG(LogSluaProfile, Verbose, TEXT("In progress read failed with code %d"), (int32)SocketSubsystem->GetLastErrorCode());
return false;
}
check(BytesRead == sizeof(uint32));
TotalBytesReceived += BytesRead;
// Setup variables to receive the message
MessagesizeData << RecvMessageDataRemaining;
RecvMessageData = MakeShareable(new FArrayReader(true));
RecvMessageData->SetNumUninitialized(RecvMessageDataRemaining);
check(RecvMessageDataRemaining > 0);
}
BytesRead = 0;
if (!Socket->Recv(RecvMessageData->GetData() + RecvMessageData->Num() - RecvMessageDataRemaining, RecvMessageDataRemaining, BytesRead))
{
UE_LOG(LogSluaProfile, Verbose, TEXT("Read failed with code %d"), (int32)SocketSubsystem->GetLastErrorCode());
return false;
}
if (BytesRead > 0)
{
check(BytesRead <= RecvMessageDataRemaining);
TotalBytesReceived += BytesRead;
RecvMessageDataRemaining -= BytesRead;
if (RecvMessageDataRemaining == 0)
{
FProfileMessage* DeserializedMessage = new FProfileMessage();
if (DeserializedMessage->Deserialize(RecvMessageData))
{
Inbox.Enqueue(MakeShareable(DeserializedMessage));
}
RecvMessageData.Reset();
}
}
else
{
// no data
return true;
}
}
}
FProfileMessage::FProfileMessage()
: Event(0)
, Time(0)
, Linedefined(-1)
, Name()
, ShortSrc()
{
}
FProfileMessage::~FProfileMessage()
{
}
bool FProfileMessage::Deserialize(const TSharedPtr<FArrayReader, ESPMode::ThreadSafe>& Message)
{
FArrayReader& MessageReader = Message.ToSharedRef().Get();
MessageReader << Event;
switch (Event)
{
case NS_SLUA::ProfilerHookEvent::PHE_MEMORY_TICK:
MessageReader << memoryInfoList;
return true;
case NS_SLUA::ProfilerHookEvent::PHE_MEMORY_INCREACE:
MessageReader << memoryIncrease;
return true;
default:
break;
}
MessageReader << Time;
MessageReader << Linedefined;
MessageReader << Name;
MessageReader << ShortSrc;
return true;
}
}

View File

@ -0,0 +1,79 @@
// Tencent is pleased to support the open source community by making sluaunreal available.
// Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
// Licensed under the BSD 3-Clause License (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the License at
// https://opensource.org/licenses/BSD-3-Clause
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
#pragma once
#include "CoreMinimal.h"
#include "Containers/Ticker.h"
#include "Modules/ModuleManager.h"
#include "Framework/Commands/Commands.h"
#include "slua_profile_inspector.h"
#include "LuaMemoryProfile.h"
#include "slua_profile.h"
/** Declares a log category for this module. */
DECLARE_LOG_CATEGORY_EXTERN(LogSluaProfile, Log, All);
namespace NS_SLUA
{
class FProfileServer;
}
class SLUA_PROFILE_API Fslua_profileModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
void PluginButtonClicked();
private:
// fields
FTickerDelegate TickDelegate;
#if ENGINE_MAJOR_VERSION==5
FTSTicker::FDelegateHandle TickDelegateHandle;
#else
FDelegateHandle TickDelegateHandle;
#endif
TSharedPtr<SProfilerInspector> sluaProfilerInspector;
bool tabOpened = false;
TSharedPtr<class FUICommandList> PluginCommands;
// functions
void OnTabClosed(TSharedRef<SDockTab> tab);
TSharedRef<class SDockTab> OnSpawnPluginTab(const class FSpawnTabArgs& SpawnTabArgs);
bool Tick(float DeltaTime);
void ClearCurProfiler();
void AddMenuExtension(FMenuBuilder& Builder);
void debug_hook_c(NS_SLUA::FProfileMessagePtr Message);
};
#if WITH_EDITOR
class Flua_profileCommands : public TCommands<Flua_profileCommands>
{
public:
Flua_profileCommands();
// TCommands<> interface
virtual void RegisterCommands() override;
public:
TSharedPtr< FUICommandInfo > OpenPluginWindow;
};
#endif

View File

@ -0,0 +1,216 @@
// Tencent is pleased to support the open source community by making sluaunreal available.
// Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
// Licensed under the BSD 3-Clause License (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the License at
// https://opensource.org/licenses/BSD-3-Clause
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
#pragma once
#include "Widgets/Docking/SDockTab.h"
#include "Widgets/Layout/SScrollBox.h"
#include "Widgets/Layout/SWidgetSwitcher.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Views/STreeView.h"
#include "Widgets/Views/SListView.h"
#include "Widgets/Notifications/SProgressBar.h"
#include "slua_remote_profile.h"
#include "LuaMemoryProfile.h"
#include "ProfileDataDefine.h"
#include "slua_profile_inspector.h"
#define IsMemoryProfiler (m_stdLineVisibility.Get() != EVisibility::Visible)
class SProfilerWidget;
class SProfilerTabWidget;
class Profiler;
class SSlider;
class SLUA_PROFILE_API SProfilerInspector
{
public:
SProfilerInspector();
~SProfilerInspector();
void Refresh(TSharedPtr<FunctionProfileNode> funcInfoRoot, TMap<int64, NS_SLUA::LuaMemInfo>& memoryInfoList, MemoryFramePtr memoryFrame);
TSharedRef<class SDockTab> GetSDockTab();
TSharedRef<ITableRow> OnGenerateMemRowForList(TSharedPtr<FileMemInfo> Item, const TSharedRef<STableViewBase>& OwnerTable);
void OnGetMemChildrenForTree(TSharedPtr<FileMemInfo> Parent, TArray<TSharedPtr<FileMemInfo>>& OutChildren);
void OnGetChildrenForTree(TSharedPtr<FunctionProfileNode> Parent, TArray<TSharedPtr<FunctionProfileNode>>& OutChildren);
TSharedRef<ITableRow> OnGenerateRowForList(TSharedPtr<FunctionProfileNode> Item, const TSharedRef<STableViewBase>& OwnerTable);
void StartChartRolling();
bool GetNeedProfilerCleared() const
{
return needProfilerCleared;
}
void SetNeedProfilerCleared(const bool needClear)
{
needProfilerCleared = needClear;
}
MemFileInfoMap GetMemoryFrameInfo(int32 memoryFrameIndex) const;
TSharedPtr<NS_SLUA::FProfileServer> ProfileServer;
private:
const static int sampleNum = cMaxSampleNum;
const static int fixRowWidth = 600;
const static int refreshInterval = 1;
const float perMilliSec = 1000.0f;
const static int maxMemoryFile = 30;
bool bIsTouching = false;
typedef TMap<FString, int> MemInfoIndexMap;
TSharedPtr<STreeView<TSharedPtr<FunctionProfileNode>>> treeview;
TSharedPtr<SListView<TSharedPtr<FileMemInfo>>> listview;
TSharedPtr<STreeView<TSharedPtr<FileMemInfo>>> memTreeView;
TSharedPtr<SSlider> cpuSlider,memSlider;
TSharedPtr<SProgressBar> profilerBarArray[sampleNum];
TSharedPtr<SProfilerWidget> cpuProfilerWidget;
TSharedPtr<SProfilerWidget> memProfilerWidget;
TSharedPtr<SProfilerTabWidget> cpuTabWidget;
TSharedPtr<SProfilerTabWidget> memTabWidget;
TSharedPtr<SProfilerTabWidget> fpsTabWidget;
TSharedPtr<SWidgetSwitcher> tabSwitcher;
TArray<float> chartValArray;
TArray<float> memChartValArray;
bool stopChartRolling;
int cpuViewBeginIndex;
int memViewBeginIndex;
float maxLuaMemory;
float avgLuaMemory;
double luaTotalMemSize;
double lastLuaTotalMemSize;
float maxProfileSamplesCostTime;
float avgProfileSamplesCostTime;
bool hasCleared;
bool needProfilerCleared;
FVector2D mouseUpPoint;
FVector2D mouseDownPoint;
TArray<TArray<TSharedPtr<FunctionProfileNode>>> allProfileData;
TArray<TSharedPtr<FunctionProfileNode>> profileRootArr;
TSharedPtr<FProflierMemNode> lastLuaMemNode;
/* holding all of the memory node which are showed on Profiler chart */
MemNodeInfoList allLuaMemNodeList;
/* refresh with the chart line, when mouse clicks down, it'll get point from this array */
MemFileInfoMap shownFileInfo;
/* store the file name as the parent item in memory treeview */
ShownMemInfoList shownParentFileName;
void RefreshBarValue();
void ShowProfilerTree(TArray<TSharedPtr<FunctionProfileNode>>&selectedProfiler);
void CheckBoxChanged(ECheckBoxState newState);
FString GenBrevFuncName(const FLuaFunctionDefine &functionDefine);
void RestartMemoryStatistis();
void OnClearBtnClicked();
void CalcPointMemdiff(int beginIndex, int endIndex);
void CollectMemoryNode(TMap<int64, NS_SLUA::LuaMemInfo>& memoryInfoMap, MemoryFramePtr memoryFrame);
void CombineSameFileInfo(FProflierMemNode& proflierMemNode, int32 memoryFrameIndex);
int ContainsFile(FString& fileName, MemInfoIndexMap &list);
FString ChooseMemoryUnit(float memorySize);
// save/load disk profile file
void OnLoadFileBtnClicked();
void OnSaveFileBtnClicked();
void OnCpuSliderValueChanged(float Value);
void OnMemSliderValueChanged(float Value);
void ShowNotificationDialog(const FString& message);
TWeakPtr<class SNotificationItem> notification;
};
class SLUA_PROFILE_API SProfilerWidget : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SProfilerWidget)
:_StdLineVisibility(EVisibility::Visible)
{}
SLATE_ATTRIBUTE(EVisibility, StdLineVisibility)
SLATE_END_ARGS()
/** Constructs this widget with InArgs */
void Construct(const FArguments& InArgs);
void SetMouseMovePoint(FVector2D mouseDownPoint);
void SetMouseClickPoint(FVector2D mouseClickPoint);
void SetArrayValue(TArray<float> &chartValArray, float maxCostTime, float maxMemSize);
int CalcClickSampleIdx(FVector2D &cursorPos);
int CalcHoverSampleIdx(const FVector2D cursorPos);
void SetToolTipVal(float val);
void ClearClickedPoint();
void SetStdLineVisibility(TAttribute<EVisibility> InVisibility);
// SWidget interface
virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
protected:
virtual FVector2D ComputeDesiredSize(float size) const override;
private:
void DrawStdLine(const FGeometry& AllottedGeometry, FSlateWindowElementList& OutDrawElements, int32 LayerId, float positionY, FString stdStr) const;
void AddStdLine(float &maxPointValue, float &stdLineValue, FString &stdLineName);
void CalcStdLine(float &maxCostTime);
void CalcMemStdText(float &maxMemorySize);
TArray<FVector2D> m_arraylinePath;
TArray<float> m_arrayVal;
TArray<float> m_stdPositionY;
TArray<FString> m_stdStr;
TArray<FString> m_stdMemStr;
TAttribute<EVisibility> m_stdLineVisibility;
const int32 m_cSliceCount = cMaxSampleNum;
const float m_cStdWidth = 1300;
float m_widgetWidth = 0;
const int32 m_cStdLeftPosition = 30;
float m_maxCostTime = 0;
float m_maxMemSize = 0;
float m_maxPointHeight = 0;
float m_pointInterval = 0;
float m_toolTipVal = 0;
int32 m_memStdScale = 1;
int32 m_pathArrayNum = 0;
FVector2D m_clickedPoint;
FVector2D m_mouseDownPoint;
const float m_cStdHighVal = cMaxViewHeight;
};
class SLUA_PROFILE_API SProfilerTabWidget : public SCompoundWidget
{
SLATE_BEGIN_ARGS(SProfilerTabWidget)
: _TabName(FText::FromString(""))
, _TabIcon(FCoreStyle::Get().GetDefaultBrush())
{}
SLATE_ATTRIBUTE(FText, TabName)
SLATE_ATTRIBUTE(const FSlateBrush*, TabIcon)
SLATE_EVENT(FOnClicked, OnClicked)
/** The visual style of the button */
SLATE_END_ARGS()
public:
int32 activeTabIndex = 0;
void Construct(const FArguments& InArgs);
};

View File

@ -0,0 +1,162 @@
// Tencent is pleased to support the open source community by making sluaunreal available.
// Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
// Licensed under the BSD 3-Clause License (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the License at
// https://opensource.org/licenses/BSD-3-Clause
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
#pragma once
#include "CoreMinimal.h"
#include "HAL/Runnable.h"
#include "Containers/Queue.h"
#include "Interfaces/IPv4/IPv4Endpoint.h"
#include "LuaMemoryProfile.h"
#include "Templates/SharedPointer.h"
#include "Serialization/ArrayReader.h"
#include "Delegates/DelegateCombinations.h"
class FSocket;
class FTcpListener;
namespace NS_SLUA {
class FProfileConnection;
class FProfileMessage;
typedef TSharedPtr<FProfileMessage, ESPMode::ThreadSafe> FProfileMessagePtr;
DECLARE_DELEGATE_OneParam(FOnProfileMessageDelegate, FProfileMessagePtr);
class FProfileServer : public FRunnable
{
public:
FProfileServer();
~FProfileServer();
FOnProfileMessageDelegate& OnProfileMessageRecv();
TArray<TSharedPtr<FProfileConnection>> GetConnections();
static int32 Port;
protected:
bool Init() override;
uint32 Run() override;
void Stop() override;
void StopTransport();
private:
/** Callback for accepted connections to the local server. */
bool HandleConnectionAccepted(FSocket* ClientSocket, const FIPv4Endpoint& ClientEndpoint);
FRunnableThread* Thread;
FIPv4Endpoint ListenEndpoint;
FTcpListener* Listener;
/** Current connections */
TArray<TSharedPtr<FProfileConnection>> Connections;
/** Holds a queue of pending connections. */
TQueue<TSharedPtr<FProfileConnection>, EQueueMode::Mpsc> PendingConnections;
FOnProfileMessageDelegate OnProfileMessageDelegate;
bool bStop;
};
/**
* Implements a TCP message tunnel connection.
*/
class FProfileConnection
: public FRunnable
, public TSharedFromThis<FProfileConnection>
{
public:
FProfileConnection(FSocket* InSocket, const FIPv4Endpoint& InRemoteEndpoint);
/** Virtual destructor. */
virtual ~FProfileConnection();
void Start();
public:
enum EConnectionState
{
STATE_Connecting, // connecting but don't yet have RemoteNodeId
STATE_Connected, // connected and RemoteNodeId is valid
STATE_Disconnected // disconnected. Previous RemoteNodeId is retained
};
EConnectionState GetConnectionState() const;
FSocket* GetSocket();
bool ReceiveData(TSharedPtr<FProfileMessage, ESPMode::ThreadSafe>& OutMessage);
void Close();
private:
//~ FRunnable interface
virtual bool Init() override;
virtual uint32 Run() override;
virtual void Stop() override;
virtual void Exit() override;
protected:
bool ReceiveMessages();
/** Holds the IP endpoint of the remote client. */
FIPv4Endpoint RemoteEndpoint;
/** Holds the connection socket. */
FSocket* Socket;
FRunnableThread* Thread;
/** Holds the total number of bytes received from the connection. */
uint64 TotalBytesReceived;
/** Holds the collection of received Messages. */
TQueue<TSharedPtr<FProfileMessage, ESPMode::ThreadSafe>, EQueueMode::Mpsc> Inbox;
/** Message data we're currently in the process of receiving, if any */
TSharedPtr<FArrayReader, ESPMode::ThreadSafe> RecvMessageData;
int32 RecvMessageDataRemaining;
int hookEvent;
EConnectionState ConnectionState;
bool bRun;
};
class FProfileMessage
{
public:
FProfileMessage();
~FProfileMessage();
bool Deserialize(const TSharedPtr<FArrayReader, ESPMode::ThreadSafe>& Message);
public:
int Event;
int64 Time;
int Linedefined;
FString Name;
FString ShortSrc;
//Memory infomation
TArray<NS_SLUA::LuaMemInfo> memoryInfoList;
TArray<NS_SLUA::LuaMemInfo> memoryIncrease;
};
}

View File

@ -0,0 +1,72 @@
// Tencent is pleased to support the open source community by making sluaunreal available.
// Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
// Licensed under the BSD 3-Clause License (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the License at
// https://opensource.org/licenses/BSD-3-Clause
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
using UnrealBuildTool;
using System.IO;
public class slua_profile : ModuleRules
{
public slua_profile(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
// enable exception
bEnableExceptions = true;
#if UE_5_2_OR_LATER
IWYUSupport = IWYUSupport.None;
#else
bEnforceIWYU = false;
#endif
bEnableUndefinedIdentifierWarnings = false;
PrivateDependencyModuleNames.AddRange(new string[] { "slua_unreal" });
PublicIncludePathModuleNames.AddRange(new string[] { "slua_unreal" });
#if UE_4_21_OR_LATER
PublicDefinitions.Add("ENABLE_PROFILER");
#else
Definitions.Add("ENABLE_PROFILER");
#endif
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"UMG",
// ... add other public dependencies that you statically link with here ...
}
);
if (Target.Type == TargetType.Editor)
{
PrivateDependencyModuleNames.Add("UnrealEd");
PrivateDependencyModuleNames.Add("EditorStyle");
PrivateDependencyModuleNames.Add("LevelEditor");
}
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"UMG",
"InputCore",
"Networking",
"Sockets",
// ... add private dependencies that you statically link with here ...
}
);
}
}