// 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 LuaMemInfoMap; LuaMemInfoMap memoryInfo; typedef TQueue MemoryFrameQueue; MemoryFrameQueue memoryQueue; MemoryFramePtr currentMemory = MakeShareable(new MemoryFrame()); ProfileCallInfoArray profilerStack; ProfileNodePtr funcProfilerRoot; TQueue 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("LevelEditor"); { TSharedPtr 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 funcProfilerNode; funcProfilerNodeQueue.Dequeue(funcProfilerNode); sluaProfilerInspector->Refresh(funcProfilerNode, memoryInfo, memoryFrame); } return true; } TSharedRef 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(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) { 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 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)