void FObjectMemoryAnalyzer::PrintResults(FOutputDevice& Ar, uint32 PrintFlags) { TArray<FObjectMemoryUsage> Results; GetResults(Results); Results.Sort(FCompareFSortBySize(ESortKey::InclusiveTotal)); Ar.Logf( TEXT("%-100s %-10s %-10s %-10s %-10s"), TEXT("Object"), TEXT("InclBytes"), TEXT("ExclBytes"), TEXT("InclResKBytes"), TEXT("ExclResKBytes") ); for( int32 i=0; i < Results.Num(); ++i ) { const FObjectMemoryUsage& Annotation = Results[i]; if (Annotation.IsRoot() || (Annotation.RootReferencer.Num() == 0 && Annotation.NonRootReferencer.Num() == 0) ) { Ar.Logf( TEXT("%-100s %-10d %-10d %-10d %-10d"), *FString::Printf(TEXT("%s %s"), *Annotation.Object->GetClass()->GetName(), *Annotation.Object->GetName()), (int32)Annotation.InclusiveMemoryUsage, (int32)Annotation.ExclusiveMemoryUsage, (int32)(Annotation.InclusiveResourceSize/1024), (int32)(Annotation.ExclusiveResourceSize/1024) ); if (!!(PrintFlags&EPrintFlags::PrintReferences)) { PrintSubObjects(Ar, TEXT(" -> "), Annotation.Object, PrintFlags); } } } }
void ConsoleCommandLibrary_DumpLibrary(UWorld* InWorld, FExec& SubSystem, const FString& Pattern, FOutputDevice& Ar) { ConsoleCommandLibrary LocalConsoleCommandLibrary(Pattern); FOutputDeviceNull Null; bool bExecuted = SubSystem.Exec( InWorld, *Pattern, Null); { IConsoleManager::Get().ForEachConsoleObjectThatStartsWith( FConsoleObjectVisitor::CreateStatic< TSet<FString>& >( &FConsoleVariableDumpVisitor::OnConsoleVariable, LocalConsoleCommandLibrary.KnownNames ) ); } LocalConsoleCommandLibrary.KnownNames.Sort( TLess<FString>() ); for(TSet<FString>::TConstIterator It(LocalConsoleCommandLibrary.KnownNames); It; ++It) { const FString Name = *It; Ar.Logf(TEXT("%s"), *Name); } Ar.Logf(TEXT("")); // the pattern (e.g. Motion*) should not really trigger the execution if(bExecuted) { Ar.Logf(TEXT("ERROR: The function was supposed to only find matching commands but not have any side effect.")); Ar.Logf(TEXT("However Exec() returned true which means we either executed a command or the command parsing returned true where it shouldn't.")); } }
void UExporter::ExportComponentDefinitions(const FExportObjectInnerContext* Context, const TArray<UObject*>& Components, FOutputDevice& Ar, uint32 PortFlags) { PortFlags |= PPF_ExportsNotFullyQualified; if (!(PortFlags & PPF_SeparateDefine)) { // export forward declarations // technically we only need to do this if there are circular references but it doesn't seem worth it // to complicate this code for a minor speed improvement in the text import path for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++) { UObject* Component = Components[ComponentIndex]; FName ComponentName = Component->GetFName(); if (!Component->HasAnyMarks(OBJECTMARK_TagImp) && !Component->HasAnyFlags(RF_TextExportTransient)) { if (Component->HasAnyFlags(RF_ClassDefaultObject) || Component->GetArchetype()->HasAllFlags(RF_ClassDefaultObject)) { Ar.Logf(TEXT("%sBegin Object Class=%s Name=%s ObjName=%s%s"), FCString::Spc(TextIndent), *Component->GetClass()->GetName(), *ComponentName.ToString(), *Component->GetName(), LINE_TERMINATOR); } else { Ar.Logf(TEXT("%sBegin Object Class=%s Name=%s ObjName=%s Archetype=%s'%s'%s"),FCString::Spc(TextIndent),*Component->GetClass()->GetName(), *ComponentName.ToString(), *Component->GetName(), *Component->GetArchetype()->GetClass()->GetName(), *Component->GetArchetype()->GetPathName(), LINE_TERMINATOR); } if (PortFlags & PPF_SeparateDeclare) { ExportObjectInner(Context, Component, Ar, PortFlags, false); } Ar.Logf(TEXT("%sEnd Object%s"), FCString::Spc(TextIndent), LINE_TERMINATOR); } } } if (!(PortFlags & PPF_SeparateDeclare)) { // export property definitions for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++) { UObject* Component = Components[ComponentIndex]; FName ComponentName = Component->GetFName(); if (!Component->HasAnyMarks(OBJECTMARK_TagImp) && !Component->HasAnyFlags(RF_TextExportTransient)) { Ar.Logf(TEXT("%sBegin Object Name=%s%s"), FCString::Spc(TextIndent), *ComponentName.ToString(), LINE_TERMINATOR); uint32 OldPortFlags = PortFlags; if (!(Component->HasAnyFlags(RF_ClassDefaultObject) || Component->GetArchetype()->HasAllFlags(RF_ClassDefaultObject))) { // we created this thing with an archetype (see archetype=, above), so we don't want to list the archetype because it is unqualified and will clash, resetting the archetype pointer to something silly PortFlags |= PPF_NoInternalArcheType; } ExportObjectInner(Context, Component, Ar, PortFlags, false); PortFlags = OldPortFlags; Ar.Logf(TEXT("%sEnd Object%s"), FCString::Spc(TextIndent), LINE_TERMINATOR); Component->Mark(OBJECTMARK_TagImp); } } } }
/** * Dump allocation information. */ void FBestFitAllocator::DumpAllocs( FOutputDevice& Ar/*=*GLog*/ ) { // Memory usage stats. INT UsedSize = 0; INT FreeSize = 0; INT NumUsedChunks = 0; INT NumFreeChunks = 0; // Fragmentation and allocation size visualization. INT NumBlocks = MemorySize / AllocationAlignment; INT Dimension = 1 + NumBlocks / appTrunc(appSqrt(NumBlocks)); TArray<FColor> AllocationVisualization; AllocationVisualization.AddZeroed( Dimension * Dimension ); INT VisIndex = 0; // Traverse linked list and gather allocation information. FMemoryChunk* CurrentChunk = FirstChunk; while( CurrentChunk ) { FColor VisColor; // Free chunk. if( CurrentChunk->bIsAvailable ) { NumFreeChunks++; FreeSize += CurrentChunk->Size; VisColor = FColor(0,255,0); } // Allocated chunk. else { NumUsedChunks++; UsedSize += CurrentChunk->Size; // Slightly alternate coloration to also visualize allocation sizes. if( NumUsedChunks % 2 == 0 ) { VisColor = FColor(255,0,0); } else { VisColor = FColor(192,0,0); } } for( INT i=0; i<(CurrentChunk->Size/AllocationAlignment); i++ ) { AllocationVisualization(VisIndex++) = VisColor; } CurrentChunk = CurrentChunk->NextChunk; } check(UsedSize == AllocatedMemorySize); check(FreeSize == AvailableMemorySize); // Write out bitmap for visualization of fragmentation and allocation patterns. appCreateBitmap( TEXT("..\\..\\Binaries\\TextureMemory"), Dimension, Dimension, AllocationVisualization.GetTypedData() ); Ar.Logf( TEXT("BestFitAllocator: Allocated %i KByte in %i chunks, leaving %i KByte in %i chunks."), UsedSize / 1024, NumUsedChunks, FreeSize / 1024, NumFreeChunks ); Ar.Logf( TEXT("BestFitAllocator: %5.2f ms in allocator"), TimeSpentInAllocator * 1000 ); }
/** * Dumps capture stack trace summary to the passed in log. */ void FScriptStackTracker::DumpStackTraces( int32 StackThreshold, FOutputDevice& Ar ) { // Avoid distorting results while we log them. check( !bAvoidCapturing ); bAvoidCapturing = true; // Make a copy as sorting causes index mismatch with TMap otherwise. TArray<FCallStack> SortedCallStacks = CallStacks; // Compare function, sorting callstack by stack count in descending order. struct FCompareStackCount { FORCEINLINE bool operator()( const FCallStack& A, const FCallStack& B ) const { return B.StackCount < A.StackCount; } }; // Sort callstacks in descending order by stack count. SortedCallStacks.Sort( FCompareStackCount() ); // Iterate over each callstack to get total stack count. uint64 TotalStackCount = 0; for( int32 CallStackIndex=0; CallStackIndex<SortedCallStacks.Num(); CallStackIndex++ ) { const FCallStack& CallStack = SortedCallStacks[CallStackIndex]; TotalStackCount += CallStack.StackCount; } // Calculate the number of frames we captured. int32 FramesCaptured = 0; if( bIsEnabled ) { FramesCaptured = GFrameCounter - StartFrameCounter; } else { FramesCaptured = StopFrameCounter - StartFrameCounter; } // Log quick summary as we don't log each individual so totals in CSV won't represent real totals. Ar.Logf(TEXT("Captured %i unique callstacks totalling %i function calls over %i frames, averaging %5.2f calls/frame"), SortedCallStacks.Num(), (int32)TotalStackCount, FramesCaptured, (float) TotalStackCount / FramesCaptured); // Iterate over each callstack and write out info in human readable form in CSV format for( int32 CallStackIndex=0; CallStackIndex<SortedCallStacks.Num(); CallStackIndex++ ) { const FCallStack& CallStack = SortedCallStacks[CallStackIndex]; // Avoid log spam by only logging above threshold. if( CallStack.StackCount > StackThreshold ) { // First row is stack count. FString CallStackString = FString::FromInt((int32)CallStack.StackCount); CallStackString += LINE_TERMINATOR; CallStackString += CallStack.StackTrace; // Finally log with ',' prefix so "Log:" can easily be discarded as row in Excel. Ar.Logf(TEXT(",%s"),*CallStackString); } } // Done logging. bAvoidCapturing = false; }
void UExporter::EmitEndObject( FOutputDevice& Ar ) { if ( bEnableDebugBrackets ) { Ar.Logf(TEXT("%s}%s"), FCString::Spc(TextIndent), LINE_TERMINATOR); } Ar.Logf( TEXT("%sEnd Object\r\n"), FCString::Spc(TextIndent) ); }
void UK2Node_EditablePinBase::ExportCustomProperties(FOutputDevice& Out, uint32 Indent) { for (int32 PinIndex = 0; PinIndex < UserDefinedPins.Num(); ++PinIndex) { const FUserPinInfo& PinInfo = *UserDefinedPins[PinIndex].Get(); Out.Logf( TEXT("%sCustomProperties UserDefinedPin "), FCString::Spc(Indent)); Out.Logf( TEXT("Name=%s "), *PinInfo.PinName); Out.Logf( TEXT("IsArray=%s "), (PinInfo.PinType.bIsArray ? TEXT("1") : TEXT("0"))); Out.Logf( TEXT("IsReference=%s "), (PinInfo.PinType.bIsReference ? TEXT("1") : TEXT("0"))); if (PinInfo.PinType.PinCategory.Len() > 0) { Out.Logf( TEXT("Category=%s "), *PinInfo.PinType.PinCategory); } if (PinInfo.PinType.PinSubCategory.Len() > 0) { Out.Logf( TEXT("SubCategory=%s "), *PinInfo.PinType.PinSubCategory); } if (PinInfo.PinType.PinSubCategoryObject.IsValid()) { Out.Logf( TEXT("SubCategoryObject=%s "), *PinInfo.PinType.PinSubCategoryObject.Get()->GetPathName()); } if (PinInfo.PinDefaultValue.Len() > 0) { Out.Logf( TEXT("DefaultValue=%s "), *PinInfo.PinDefaultValue); } Out.Logf( TEXT("\r\n")); } }
void FHttpManager::DumpRequests(FOutputDevice& Ar) const { FScopeLock ScopeLock(&RequestLock); Ar.Logf(TEXT("------- (%d) Http Requests"), Requests.Num()); for (const TSharedRef<IHttpRequest>& Request : Requests) { Ar.Logf(TEXT(" verb=[%s] url=[%s] status=%s"), *Request->GetVerb(), *Request->GetURL(), EHttpRequestStatus::ToString(Request->GetStatus())); } }
/** * Outputs the contents of the ObjectMap to the specified output device. */ void FTransaction::DumpObjectMap(FOutputDevice& Ar) const { Ar.Logf( TEXT("===== DumpObjectMap %s ==== "), *Title.ToString() ); for ( ObjectMapType::TConstIterator It(ObjectMap) ; It ; ++It ) { const UObject* CurrentObject = It.Key(); const int32 SaveCount = It.Value(); Ar.Logf( TEXT("%i\t: %s"), SaveCount, *CurrentObject->GetPathName() ); } Ar.Logf( TEXT("=== EndDumpObjectMap %s === "), *Title.ToString() ); }
/** * Dumps capture stack trace summary to the passed in log. */ void FScriptStackTracker::DumpStackTraces( INT StackThreshold, FOutputDevice& Ar ) { // Avoid distorting results while we log them. check( !bAvoidCapturing ); bAvoidCapturing = TRUE; // Make a copy as sorting causes index mismatch with TMap otherwise. TArray<FCallStack> SortedCallStacks = CallStacks; // Sort callstacks in descending order by stack count. Sort<USE_COMPARE_CONSTREF(FCallStack,StackTracker)>( SortedCallStacks.GetTypedData(), SortedCallStacks.Num() ); // Iterate over each callstack to get total stack count. QWORD TotalStackCount = 0; for( INT CallStackIndex=0; CallStackIndex<SortedCallStacks.Num(); CallStackIndex++ ) { const FCallStack& CallStack = SortedCallStacks(CallStackIndex); TotalStackCount += CallStack.StackCount; } // Calculate the number of frames we captured. INT FramesCaptured = 0; if( bIsEnabled ) { FramesCaptured = GFrameCounter - StartFrameCounter; } else { FramesCaptured = StopFrameCounter - StartFrameCounter; } // Log quick summary as we don't log each individual so totals in CSV won't represent real totals. Ar.Logf(TEXT("Captured %i unique callstacks totalling %i function calls over %i frames, averaging %5.2f calls/frame"), SortedCallStacks.Num(), (int)TotalStackCount, FramesCaptured, (FLOAT) TotalStackCount / FramesCaptured); // Iterate over each callstack and write out info in human readable form in CSV format for( INT CallStackIndex=0; CallStackIndex<SortedCallStacks.Num(); CallStackIndex++ ) { const FCallStack& CallStack = SortedCallStacks(CallStackIndex); // Avoid log spam by only logging above threshold. if( CallStack.StackCount > StackThreshold ) { // First row is stack count. FString CallStackString = appItoa(CallStack.StackCount); CallStackString += LINE_TERMINATOR; CallStackString += CallStack.StackTrace; // Finally log with ',' prefix so "Log:" can easily be discarded as row in Excel. Ar.Logf(TEXT(",%s"),*CallStackString); } } // Done logging. bAvoidCapturing = FALSE; }
bool UIpNetDriver::HandleSocketsCommand( const TCHAR* Cmd, FOutputDevice& Ar, UWorld* InWorld ) { Ar.Logf(TEXT("")); if (Socket != NULL) { TSharedRef<FInternetAddr> LocalAddr = GetSocketSubsystem()->CreateInternetAddr(); Socket->GetAddress(*LocalAddr); Ar.Logf(TEXT("%s Socket: %s"), *GetDescription(), *LocalAddr->ToString(true)); } else { Ar.Logf(TEXT("%s Socket: null"), *GetDescription()); } return UNetDriver::Exec( InWorld, TEXT("SOCKETS"),Ar); }
bool FOSVRHMD::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) { if (FParse::Command(&Cmd, TEXT("STEREO"))) { if (FParse::Command(&Cmd, TEXT("ON"))) { if (!IsHMDEnabled()) { Ar.Logf(TEXT("HMD is disabled. Use 'hmd enable' to re-enable it.")); } EnableStereo(true); return true; } else if (FParse::Command(&Cmd, TEXT("OFF"))) { EnableStereo(false); return true; } } else if (FParse::Command(&Cmd, TEXT("HMD"))) { if (FParse::Command(&Cmd, TEXT("ENABLE"))) { EnableHMD(true); return true; } else if (FParse::Command(&Cmd, TEXT("DISABLE"))) { EnableHMD(false); return true; } } else if (FParse::Command(&Cmd, TEXT("UNCAPFPS"))) { GEngine->bSmoothFrameRate = false; return true; } else if (FParse::Command(&Cmd, TEXT("HEADTRACKING"))) { FString val; if (FParse::Value(Cmd, TEXT("SOURCE="), val)) { EnablePositionalTracking(false); //OSVRInterfaceName = val; EnablePositionalTracking(true); } if (FParse::Command(&Cmd, TEXT("ENABLE"))) { EnablePositionalTracking(true); return true; } else if (FParse::Command(&Cmd, TEXT("DISABLE"))) { EnablePositionalTracking(false); return true; } } return false; }
/** * Exec handler implementation. * * @param InWorld World context * @param Cmd Command to parse * @param Ar Output device to log to * * @return true if command was handled, false otherwise */ bool FSystemSettings::Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar ) { FSystemSettingsData OldSystemSettings = *this; // Keep track whether the command was handled or not. bool bHandledCommand = false; if( FParse::Command(&Cmd,TEXT("SCALE")) ) { // Some of these settings are used in both threads so we need to stop the rendering thread before changing them. FlushRenderingCommands(); if( FParse::Command(&Cmd,TEXT("LOWEND")) ) { bHandledCommand = HandleLowendCommand( Cmd, Ar ); } else if( FParse::Command(&Cmd,TEXT("HIGHEND")) ) { bHandledCommand = HandleHighendCommand( Cmd, Ar ); } if (!bHandledCommand) { Ar.Logf(TEXT("Unrecognized system setting (note that console variables can be set much easier)")); } else { // Write the new settings to the INI. SaveToIni(); } } return bHandledCommand; }
void FPakPlatformFile::HandlePakListCommand(const TCHAR* Cmd, FOutputDevice& Ar) { TArray<FPakListEntry> Paks; GetMountedPaks(Paks); for (auto Pak : Paks) { Ar.Logf(TEXT("%s"), *Pak.PakFile->GetFilename()); } }
static void DumpRHIMemory(FOutputDevice& OutputDevice) { TArray<FStatMessage> Stats; GetPermanentStats(Stats); FName NAME_STATGROUP_RHI(FStatGroup_STATGROUP_RHI::GetGroupName()); OutputDevice.Logf(TEXT("RHI resource memory (not tracked by our allocator)")); int64 TotalMemory = 0; for (int32 Index = 0; Index < Stats.Num(); Index++) { FStatMessage const& Meta = Stats[Index]; FName LastGroup = Meta.NameAndInfo.GetGroupName(); if (LastGroup == NAME_STATGROUP_RHI && Meta.NameAndInfo.GetFlag(EStatMetaFlags::IsMemory)) { OutputDevice.Logf(TEXT("%s"), *FStatsUtils::DebugPrint(Meta)); TotalMemory += Meta.GetValue_int64(); } } OutputDevice.Logf(TEXT("%.3fMB total"), TotalMemory / 1024.f / 1024.f); }
// Disassemble all functions in any classes that have matching names. void FKismetBytecodeDisassembler::DisassembleAllFunctionsInClasses(FOutputDevice& Ar, const FString& ClassnameSubstring) { FKismetBytecodeDisassembler Disasm(Ar); for (TObjectIterator<UClass> ClassIter; ClassIter; ++ClassIter) { UClass* Class = *ClassIter; FString ClassName = Class->GetName(); if (FCString::Strfind(*ClassName, *ClassnameSubstring)) { Ar.Logf(TEXT("Processing class %s"), *ClassName); for (TFieldIterator<UFunction> FunctionIter(Class, EFieldIteratorFlags::ExcludeSuper); FunctionIter; ++FunctionIter) { UFunction* Function = *FunctionIter; FString FunctionName = Function->GetName(); Ar.Logf(TEXT(" Processing function %s (%d bytes)"), *FunctionName, Function->Script.Num()); Disasm.DisassembleStructure(Function); Ar.Logf(TEXT("")); } Ar.Logf(TEXT("")); Ar.Logf(TEXT("-----------")); Ar.Logf(TEXT("")); } } }
void UExporter::EmitBeginObject( FOutputDevice& Ar, UObject* Obj, uint32 PortFlags ) { check(Obj); // figure out how to export bool bIsExportingDefaultObject = Obj->HasAnyFlags(RF_ClassDefaultObject) || Obj->GetArchetype()->HasAnyFlags(RF_ClassDefaultObject); // start outputting the string for the Begin Object line Ar.Logf(TEXT("%sBegin Object"), FCString::Spc(TextIndent)); if (!(PortFlags & PPF_SeparateDefine)) { Ar.Logf(TEXT(" Class=%s"), *Obj->GetClass()->GetName()); } // always need a name, adding "" for space handling Ar.Logf(TEXT(" Name=\"%s\""), *Obj->GetName()); if (!(PortFlags & PPF_SeparateDefine)) { // do we want the archetype string? if (!bIsExportingDefaultObject) { Ar.Logf(TEXT(" Archetype=%s'%s'"), *Obj->GetArchetype()->GetClass()->GetName(), *Obj->GetArchetype()->GetPathName()); } } // end in a return Ar.Logf(TEXT("\r\n")); if ( bEnableDebugBrackets ) { Ar.Logf(TEXT("%s{%s"), FCString::Spc(TextIndent), LINE_TERMINATOR); } }
void FObjectMemoryAnalyzer::PrintSubObjects(FOutputDevice& Ar, const FString& Indent, UObject* Parent, uint32 PrintFlags) { TArray<UObject*> ReferencedObjects; GetReferencedObjects(Parent, ReferencedObjects); for( int32 ObjIndex = 0; ObjIndex < ReferencedObjects.Num(); ObjIndex++ ) { UObject* SubObj = ReferencedObjects[ObjIndex]; const FObjectMemoryUsage& Annotation = GetObjectMemoryUsage(SubObj); if (!Annotation.IsRoot()) { Ar.Logf( TEXT("%-100s %-10d %-10d %-10d %-10d"), *FString::Printf(TEXT("%s%s %s"), *Indent, *SubObj->GetClass()->GetName(), *SubObj->GetName()), (int32)Annotation.InclusiveMemoryUsage, (int32)Annotation.ExclusiveMemoryUsage, (int32)(Annotation.InclusiveResourceSize/1024), (int32)(Annotation.ExclusiveResourceSize/1024) ); if (!!(PrintFlags&EPrintFlags::PrintReferencer)) { for (int32 i=0; i < Annotation.NonRootReferencer.Num(); ++i) { Ar.Logf(TEXT("%s >> NonRootRef: %s"), *Indent, *Annotation.NonRootReferencer[i]->GetName()); } for (int32 i=0; i < Annotation.RootReferencer.Num(); ++i) { Ar.Logf(TEXT("%s >> RootRef: %s"), *Indent, *Annotation.RootReferencer[i]->GetName()); } } if (!!(PrintFlags&EPrintFlags::PrintReferences)) { PrintSubObjects(Ar, Indent + TEXT(" -> "), SubObj, PrintFlags); } } } }
void FName::DisplayHash( FOutputDevice& Ar ) { int32 UsedBins=0, NameCount=0, MemUsed = 0; for( int32 i=0; i<ARRAY_COUNT(NameHash); i++ ) { if( NameHash[i] != NULL ) UsedBins++; for( FNameEntry *Hash = NameHash[i]; Hash; Hash=Hash->HashNext ) { NameCount++; // Count how much memory this entry is using MemUsed += FNameEntry::GetSize( Hash->GetNameLength(), Hash->IsWide() ); } } Ar.Logf( TEXT("Hash: %i names, %i/%i hash bins, Mem in bytes %i"), NameCount, UsedBins, ARRAY_COUNT(NameHash), MemUsed); }
/** * Prints the subtitle associated with the SoundWave to the console */ void USoundWave::LogSubtitle( FOutputDevice& Ar ) { FString Subtitle = ""; for( int32 i = 0; i < Subtitles.Num(); i++ ) { Subtitle += Subtitles[ i ].Text.ToString(); } if( Subtitle.Len() == 0 ) { Subtitle = SpokenText; } if( Subtitle.Len() == 0 ) { Subtitle = "<NO SUBTITLE>"; } Ar.Logf( TEXT( "Subtitle: %s" ), *Subtitle ); #if WITH_EDITORONLY_DATA Ar.Logf( TEXT( "Comment: %s" ), *Comment ); #endif // WITH_EDITORONLY_DATA Ar.Logf( bMature ? TEXT( "Mature: Yes" ) : TEXT( "Mature: No" ) ); }
/** * Process a "[cat] only" string command to the logging suppression system * @param CmdString, string to process * @return true if CmdString was a "[cat] only" string command, false otherwise */ bool ProcessLogOnly(const FString& CmdString, FOutputDevice& Ar) { TArray<FString> CommandParts; CmdString.ParseIntoArrayWS(CommandParts); if (CommandParts.Num() <= 1) { return false; } static FName NAME_Only(TEXT("only")); if (NAME_Only != FName(*CommandParts[1])) { return false; } FName LogCategory = FName(*CommandParts[0]); static const FString OffString = FString(" off"); static const FString OnString = FString(" Verbose"); for (auto It : Associations) { FName Name = It.Value; if (Name == LogCategory) { ProcessCmdString(Name.ToString() + OnString); FLogCategoryBase* Verb = It.Key; Ar.Logf(TEXT("%s is now %s"), *CommandParts[0], FOutputDevice::VerbosityToString(Verb ? ELogVerbosity::Type(Verb->Verbosity) : ELogVerbosity::Verbose)); } else { ProcessCmdString(Name.ToString() + OffString); } } Ar.Logf(TEXT("Disabling other logs finished")); return true; }
bool UGameInstance::HandleOpenCommand(const TCHAR* Cmd, FOutputDevice& Ar, UWorld* InWorld) { check(WorldContext && WorldContext->World() == InWorld); UEngine* const Engine = GetEngine(); FURL TestURL(&WorldContext->LastURL, Cmd, TRAVEL_Absolute); if (TestURL.IsLocalInternal()) { // make sure the file exists if we are opening a local file if (!Engine->MakeSureMapNameIsValid(TestURL.Map)) { Ar.Logf(TEXT("ERROR: The map '%s' does not exist."), *TestURL.Map); return true; } } Engine->SetClientTravel(InWorld, Cmd, TRAVEL_Absolute); return true; }
/** * Dumps capture stack trace summary to the passed in log. */ void FStackTracker::DumpStackTraces( INT StackThreshold, FOutputDevice& Ar ) { // Avoid distorting results while we log them. check( !bAvoidCapturing ); bAvoidCapturing = TRUE; // Make a copy as sorting causes index mismatch with TMap otherwise. TArray<FCallStack> SortedCallStacks = CallStacks; // Sort callstacks in descending order by stack count. Sort<USE_COMPARE_CONSTREF(FCallStack,StackTracker)>( SortedCallStacks.GetTypedData(), SortedCallStacks.Num() ); // Iterate over each callstack to get total stack count. QWORD TotalStackCount = 0; for( INT CallStackIndex=0; CallStackIndex<SortedCallStacks.Num(); CallStackIndex++ ) { const FCallStack& CallStack = SortedCallStacks(CallStackIndex); TotalStackCount += CallStack.StackCount; } // Calculate the number of frames we captured. INT FramesCaptured = 0; if( bIsEnabled ) { FramesCaptured = GFrameCounter - StartFrameCounter; } else { FramesCaptured = StopFrameCounter - StartFrameCounter; } // Log quick summary as we don't log each individual so totals in CSV won't represent real totals. Ar.Logf(TEXT("Captured %i unique callstacks totalling %i function calls over %i frames, averaging %5.2f calls/frame, Avg Per Frame"), SortedCallStacks.Num(), (int)TotalStackCount, FramesCaptured, (FLOAT) TotalStackCount / FramesCaptured); // Iterate over each callstack and write out info in human readable form in CSV format for( INT CallStackIndex=0; CallStackIndex<SortedCallStacks.Num(); CallStackIndex++ ) { const FCallStack& CallStack = SortedCallStacks(CallStackIndex); // Avoid log spam by only logging above threshold. if( CallStack.StackCount > StackThreshold ) { // First row is stack count. FString CallStackString = appItoa(CallStack.StackCount); CallStackString += FString::Printf( TEXT(",%5.2f"), static_cast<FLOAT>(CallStack.StackCount)/static_cast<FLOAT>(FramesCaptured) ); // Iterate over all addresses in the callstack to look up symbol name. for( INT AddressIndex=0; AddressIndex<ARRAY_COUNT(CallStack.Addresses) && CallStack.Addresses[AddressIndex]; AddressIndex++ ) { ANSICHAR AddressInformation[512]; AddressInformation[0] = 0; appProgramCounterToHumanReadableString( CallStack.Addresses[AddressIndex], AddressInformation, ARRAY_COUNT(AddressInformation)-1, VF_DISPLAY_FILENAME ); CallStackString = CallStackString + LINE_TERMINATOR TEXT(",,,") + FString(AddressInformation); } // Finally log with ',' prefix so "Log:" can easily be discarded as row in Excel. Ar.Logf(TEXT(",%s"),*CallStackString); //Append any user information before moving on to the next callstack if (ReportFn) { ReportFn(CallStack, Ar); } } } // Done logging. bAvoidCapturing = FALSE; }
/** * Exports the property values for the specified object as text to the output device. * * @param Context Context from which the set of 'inner' objects is extracted. If NULL, an object iterator will be used. * @param Out the output device to send the exported text to * @param ObjectClass the class of the object to dump properties for * @param Object the address of the object to dump properties for * @param Indent number of spaces to prepend to each line of output * @param DiffClass the class to use for comparing property values when delta export is desired. * @param Diff the address of the object to use for determining whether a property value should be exported. If the value in Object matches the corresponding * value in Diff, it is not exported. Specify NULL to export all properties. * @param Parent the UObject corresponding to Object * @param PortFlags flags used for modifying the output and/or behavior of the export */ void ExportProperties ( const FExportObjectInnerContext* Context, FOutputDevice& Out, UClass* ObjectClass, uint8* Object, int32 Indent, UClass* DiffClass, uint8* Diff, UObject* Parent, uint32 PortFlags, UObject* ExportRootScope ) { FString ThisName = TEXT("(none)"); check(ObjectClass != NULL); for( UProperty* Property = ObjectClass->PropertyLink; Property; Property = Property->PropertyLinkNext ) { if (!Property->ShouldPort(PortFlags)) continue; ThisName = Property->GetName(); UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Property); UObjectPropertyBase* ExportObjectProp = (Property->PropertyFlags & CPF_ExportObject) != 0 ? Cast<UObjectPropertyBase>(Property) : NULL; const uint32 ExportFlags = PortFlags | PPF_Delimited; if ( ArrayProperty != NULL ) { // Export dynamic array. UProperty* InnerProp = ArrayProperty->Inner; ExportObjectProp = (Property->PropertyFlags & CPF_ExportObject) != 0 ? Cast<UObjectPropertyBase>(InnerProp) : NULL; // This is used as the default value in the case of an array property that has // fewer elements than the exported object. uint8* StructDefaults = NULL; UStructProperty* StructProperty = Cast<UStructProperty>(InnerProp); if ( StructProperty != NULL ) { checkSlow(StructProperty->Struct); StructDefaults = (uint8*)FMemory::Malloc(StructProperty->Struct->GetStructureSize()); StructProperty->InitializeValue(StructDefaults); } for( int32 PropertyArrayIndex=0; PropertyArrayIndex<Property->ArrayDim; PropertyArrayIndex++ ) { void* Arr = Property->ContainerPtrToValuePtr<void>(Object, PropertyArrayIndex); FScriptArrayHelper ArrayHelper(ArrayProperty, Arr); void* DiffArr = NULL; if( DiffClass ) { DiffArr = Property->ContainerPtrToValuePtrForDefaults<void>(DiffClass, Diff, PropertyArrayIndex); } // we won't use this if DiffArr is NULL, but we have to set it up to something FScriptArrayHelper DiffArrayHelper(ArrayProperty, DiffArr); bool bAnyElementDiffered = false; for( int32 DynamicArrayIndex=0; DynamicArrayIndex<ArrayHelper.Num(); DynamicArrayIndex++ ) { FString Value; // compare each element's value manually so that elements which match the NULL value for the array's inner property type // but aren't in the diff array are still exported uint8* SourceData = ArrayHelper.GetRawPtr(DynamicArrayIndex); uint8* DiffData = DiffArr && DynamicArrayIndex < DiffArrayHelper.Num() ? DiffArrayHelper.GetRawPtr(DynamicArrayIndex) : StructDefaults; bool bExportItem = DiffData == NULL || (DiffData != SourceData && !InnerProp->Identical(SourceData, DiffData, ExportFlags)); if ( bExportItem ) { bAnyElementDiffered = true; InnerProp->ExportTextItem(Value, SourceData, DiffData, Parent, ExportFlags, ExportRootScope); if(ExportObjectProp) { UObject* Obj = ExportObjectProp->GetObjectPropertyValue(ArrayHelper.GetRawPtr(DynamicArrayIndex)); check(!Obj || Obj->IsValidLowLevel()); if( Obj && !Obj->HasAnyMarks(OBJECTMARK_TagImp) ) { // only export the BEGIN OBJECT block for a component if Parent is the component's Outer....when importing subobject definitions, // (i.e. BEGIN OBJECT), whichever BEGIN OBJECT block a component's BEGIN OBJECT block is located within is the object that will be // used as the Outer to create the component // Is this an array of components? if ( InnerProp->HasAnyPropertyFlags(CPF_InstancedReference) ) { if ( Obj->GetOuter() == Parent ) { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } else { // set the OBJECTMARK_TagExp flag so that the calling code knows we wanted to export this object Obj->Mark(OBJECTMARK_TagExp); } } else { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } } } Out.Logf( TEXT("%s%s(%i)=%s\r\n"), FCString::Spc(Indent), *Property->GetName(), DynamicArrayIndex, *Value ); } // if some other element has already been determined to differ from the defaults, then export this item with no data so that // the different array's size is maintained on import (this item will get the default values for that index, if any) // however, if no elements of the array have changed, we still don't want to export anything // so that the array size will also be taken from the defaults, which won't be the case if any element is exported else if (bAnyElementDiffered) { Out.Logf( TEXT("%s%s(%i)=()\r\n"), FCString::Spc(Indent), *Property->GetName(), DynamicArrayIndex ); } } } if (StructDefaults) { StructProperty->DestroyValue(StructDefaults); FMemory::Free(StructDefaults); } } else { for( int32 PropertyArrayIndex=0; PropertyArrayIndex<Property->ArrayDim; PropertyArrayIndex++ ) { FString Value; // Export single element. uint8* DiffData = (DiffClass && Property->IsInContainer(DiffClass->GetPropertiesSize())) ? Diff : NULL; if( Property->ExportText_InContainer( PropertyArrayIndex, Value, Object, DiffData, Parent, ExportFlags, ExportRootScope ) ) { if ( ExportObjectProp ) { UObject* Obj = ExportObjectProp->GetObjectPropertyValue(Property->ContainerPtrToValuePtr<void>(Object, PropertyArrayIndex)); if( Obj && !Obj->HasAnyMarks(OBJECTMARK_TagImp) ) { // only export the BEGIN OBJECT block for a component if Parent is the component's Outer....when importing subobject definitions, // (i.e. BEGIN OBJECT), whichever BEGIN OBJECT block a component's BEGIN OBJECT block is located within is the object that will be // used as the Outer to create the component if ( Property->HasAnyPropertyFlags(CPF_InstancedReference) ) { if ( Obj->GetOuter() == Parent ) { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } else { // set the OBJECTMARK_TagExp flag so that the calling code knows we wanted to export this object Obj->Mark(OBJECTMARK_TagExp); } } else { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } } } if( Property->ArrayDim == 1 ) { Out.Logf( TEXT("%s%s=%s\r\n"), FCString::Spc(Indent), *Property->GetName(), *Value ); } else { Out.Logf( TEXT("%s%s(%i)=%s\r\n"), FCString::Spc(Indent), *Property->GetName(), PropertyArrayIndex, *Value ); } } } } } // Allows to import/export C++ properties in case the automatic unreal script mesh wouldn't work. Parent->ExportCustomProperties(Out, Indent); }
FProcHandle FDesktopPlatformBase::InvokeUnrealBuildToolAsync(const FString& InCmdLineParams, FOutputDevice &Ar, void*& OutReadPipe, void*& OutWritePipe, bool bSkipBuildUBT) { FString CmdLineParams = InCmdLineParams; #if PLATFORM_WINDOWS #if _MSC_VER >= 1900 CmdLineParams += TEXT(" -2015"); #elif _MSC_VER >= 1800 CmdLineParams += TEXT(" -2013"); #else CmdLineParams += TEXT(" -2012"); #endif #endif // PLATFORM_WINDOWS // UnrealBuildTool is currently always located in the Binaries/DotNET folder FString ExecutableFileName = GetUnrealBuildToolExecutableFilename(FPaths::RootDir()); // Rocket never builds UBT, UnrealBuildTool should already exist bool bSkipBuild = FApp::IsEngineInstalled() || bSkipBuildUBT; if (!bSkipBuild) { // When not using rocket, we should attempt to build UBT to make sure it is up to date // Only do this if we have not already successfully done it once during this session. static bool bSuccessfullyBuiltUBTOnce = false; if (!bSuccessfullyBuiltUBTOnce) { Ar.Log(TEXT("Building UnrealBuildTool...")); if (BuildUnrealBuildTool(FPaths::RootDir(), Ar)) { bSuccessfullyBuiltUBTOnce = true; } else { // Failed to build UBT Ar.Log(TEXT("Failed to build UnrealBuildTool.")); return FProcHandle(); } } } #if PLATFORM_LINUX CmdLineParams += (" -progress"); #endif // PLATFORM_LINUX Ar.Logf(TEXT("Launching UnrealBuildTool... [%s %s]"), *ExecutableFileName, *CmdLineParams); #if PLATFORM_MAC // On Mac we launch UBT with Mono FString ScriptPath = FPaths::ConvertRelativePathToFull(FPaths::EngineDir() / TEXT("Build/BatchFiles/Mac/RunMono.sh")); CmdLineParams = FString::Printf(TEXT("\"%s\" \"%s\" %s"), *ScriptPath, *ExecutableFileName, *CmdLineParams); ExecutableFileName = TEXT("/bin/sh"); #elif PLATFORM_LINUX // Real men run Linux (with Mono??) FString ScriptPath = FPaths::ConvertRelativePathToFull(FPaths::EngineDir() / TEXT("Build/BatchFiles/Linux/RunMono.sh")); CmdLineParams = FString::Printf(TEXT("\"%s\" \"%s\" %s"), *ScriptPath, *ExecutableFileName, *CmdLineParams); ExecutableFileName = TEXT("/bin/bash"); #endif // Run UnrealBuildTool const bool bLaunchDetached = false; const bool bLaunchHidden = true; const bool bLaunchReallyHidden = bLaunchHidden; FProcHandle ProcHandle = FPlatformProcess::CreateProc(*ExecutableFileName, *CmdLineParams, bLaunchDetached, bLaunchHidden, bLaunchReallyHidden, NULL, 0, NULL, OutWritePipe); if (!ProcHandle.IsValid()) { Ar.Logf(TEXT("Failed to launch Unreal Build Tool. (%s)"), *ExecutableFileName); } return ProcHandle; }
// // Command line. // UBOOL UXViewport::Exec( const TCHAR* Cmd, FOutputDevice& Ar ) { guard(UXViewport::Exec); if( UViewport::Exec( Cmd, Ar ) ) { return 1; } else if( ParseCommand(&Cmd,TEXT("EndFullscreen")) ) { EndFullscreen(); return 1; } else if( ParseCommand(&Cmd,TEXT("ToggleFullscreen")) ) { ToggleFullscreen(); return 1; } else if( ParseCommand(&Cmd,TEXT("Iconify")) ) { Iconify(); return 1; } else if( ParseCommand(&Cmd,TEXT("GetCurrentRes")) ) { Ar.Logf( TEXT("%ix%i"), SizeX, SizeY, (ColorBytes?ColorBytes:2)*8 ); return 1; } else if( ParseCommand(&Cmd,TEXT("GetCurrentColorDepth")) ) { Ar.Logf( TEXT("%i"), (ColorBytes?ColorBytes:2)*8 ); return 1; } else if( ParseCommand(&Cmd,TEXT("GetColorDepths")) ) { Ar.Log( TEXT("16 32") ); return 1; } else if( ParseCommand(&Cmd,TEXT("GetCurrentRenderDevice")) ) { Ar.Log( RenDev->GetClass()->GetPathName() ); return 1; } else if( ParseCommand(&Cmd,TEXT("SetRenderDevice")) ) { FString Saved = RenDev->GetClass()->GetPathName(); INT SavedSizeX=SizeX, SavedSizeY=SizeY, SavedColorBytes=ColorBytes, SavedFullscreen=((BlitFlags & BLIT_Fullscreen)!=0); TryRenderDevice( Cmd, SizeX, SizeY, ColorBytes, SavedFullscreen ); if( !RenDev ) { TryRenderDevice( *Saved, SavedSizeX, SavedSizeY, SavedColorBytes, SavedFullscreen ); check(RenDev); Ar.Log(TEXT("0")); } else Ar.Log(TEXT("1")); return 1; } else if( ParseCommand(&Cmd,TEXT("GetRes")) ) { return 1; } else if( ParseCommand(&Cmd,TEXT("SetRes")) ) { INT X=appAtoi(Cmd); TCHAR* CmdTemp = (TCHAR*) (appStrchr(Cmd,'x') ? appStrchr(Cmd,'x')+1 : appStrchr(Cmd,'X') ? appStrchr(Cmd,'X')+1 : TEXT("")); INT Y=appAtoi(CmdTemp); Cmd = CmdTemp; CmdTemp = (TCHAR*) (appStrchr(Cmd,'x') ? appStrchr(Cmd,'x')+1 : appStrchr(Cmd,'X') ? appStrchr(Cmd,'X')+1 : TEXT("")); INT C=appAtoi(CmdTemp); INT NewColorBytes = C ? C/8 : ColorBytes; if( X && Y ) { HoldCount++; UBOOL Result = RenDev->SetRes( X, Y, NewColorBytes, IsFullscreen() ); HoldCount--; if( !Result ) EndFullscreen(); } return 1; } else if( ParseCommand(&Cmd,TEXT("Preferences")) ) { // No preferences window. return 1; } else return 0; unguard; }
/** * Dumps capture stack trace summary to the passed in log. */ void FStackTracker::DumpStackTraces( int32 StackThreshold, FOutputDevice& Ar ) { // Avoid distorting results while we log them. check( !bAvoidCapturing ); bAvoidCapturing = true; // Make a copy as sorting causes index mismatch with TMap otherwise. TArray<FCallStack> SortedCallStacks = CallStacks; // Compare function, sorting callstack by stack count in descending order. struct FCompareStackCount { FORCEINLINE bool operator()( const FCallStack& A, const FCallStack& B ) const { return B.StackCount < A.StackCount; } }; // Sort callstacks in descending order by stack count. SortedCallStacks.Sort( FCompareStackCount() ); // Iterate over each callstack to get total stack count. uint64 TotalStackCount = 0; for( int32 CallStackIndex=0; CallStackIndex<SortedCallStacks.Num(); CallStackIndex++ ) { const FCallStack& CallStack = SortedCallStacks[CallStackIndex]; TotalStackCount += CallStack.StackCount; } // Calculate the number of frames we captured. int32 FramesCaptured = 0; if( bIsEnabled ) { FramesCaptured = GFrameCounter - StartFrameCounter; } else { FramesCaptured = StopFrameCounter - StartFrameCounter; } // Log quick summary as we don't log each individual so totals in CSV won't represent real totals. Ar.Logf(TEXT("Captured %i unique callstacks totalling %i function calls over %i frames, averaging %5.2f calls/frame, Avg Per Frame"), SortedCallStacks.Num(), (int32)TotalStackCount, FramesCaptured, (float) TotalStackCount / FramesCaptured); // Iterate over each callstack and write out info in human readable form in CSV format for( int32 CallStackIndex=0; CallStackIndex<SortedCallStacks.Num(); CallStackIndex++ ) { const FCallStack& CallStack = SortedCallStacks[CallStackIndex]; // Avoid log spam by only logging above threshold. if( CallStack.StackCount > StackThreshold ) { // First row is stack count. FString CallStackString = FString::FromInt((int32)CallStack.StackCount); CallStackString += FString::Printf( TEXT(",%5.2f"), static_cast<float>(CallStack.StackCount)/static_cast<float>(FramesCaptured) ); // Iterate over all addresses in the callstack to look up symbol name. for( int32 AddressIndex=0; AddressIndex<ARRAY_COUNT(CallStack.Addresses) && CallStack.Addresses[AddressIndex]; AddressIndex++ ) { ANSICHAR AddressInformation[512]; AddressInformation[0] = 0; FPlatformStackWalk::ProgramCounterToHumanReadableString( AddressIndex, CallStack.Addresses[AddressIndex], AddressInformation, ARRAY_COUNT(AddressInformation)-1 ); CallStackString = CallStackString + LINE_TERMINATOR TEXT(",,,") + FString(AddressInformation); } // Finally log with ',' prefix so "Log:" can easily be discarded as row in Excel. Ar.Logf(TEXT(",%s"),*CallStackString); //Append any user information before moving on to the next callstack if (ReportFn) { ReportFn(CallStack, CallStack.StackCount, Ar); } } } // Done logging. bAvoidCapturing = false; }
bool FSteamVRHMD::Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar ) { if (FParse::Command( &Cmd, TEXT("STEREO") )) { if (FParse::Command(&Cmd, TEXT("ON"))) { if (!IsHMDEnabled()) { Ar.Logf(TEXT("HMD is disabled. Use 'hmd enable' to re-enable it.")); } EnableStereo(true); return true; } else if (FParse::Command(&Cmd, TEXT("OFF"))) { EnableStereo(false); return true; } float val; if (FParse::Value(Cmd, TEXT("E="), val)) { IPD = val; } } else if (FParse::Command(&Cmd, TEXT("HMD"))) { if (FParse::Command(&Cmd, TEXT("ENABLE"))) { EnableHMD(true); return true; } else if (FParse::Command(&Cmd, TEXT("DISABLE"))) { EnableHMD(false); return true; } int32 val; if (FParse::Value(Cmd, TEXT("MIRROR"), val)) { if ((val >= 0) && (val <= 2)) { WindowMirrorMode = val; } else { Ar.Logf(TEXT("HMD MIRROR accepts values from 0 though 2")); } return true; } } else if (FParse::Command(&Cmd, TEXT("UNCAPFPS"))) { GEngine->bForceDisableFrameRateSmoothing = true; return true; } return false; }
/** * Dumps detailed information of bulk data usage. * * @param Log FOutputDevice to use for logging */ void FUntypedBulkData::DumpBulkDataUsage( FOutputDevice& Log ) { #if TRACK_BULKDATA_USE // Arrays about to hold per object and per class size information. TArray<FObjectAndSize> PerObjectSizeArray; TArray<FObjectAndSize> PerClassSizeArray; { FScopeLock Lock(&FThreadSafeBulkDataToObjectMap::Get().GetLock()); // Iterate over all "live" bulk data and add size to arrays if it is loaded. for( auto It(FThreadSafeBulkDataToObjectMap::Get().GetIterator()); It; ++It ) { const FUntypedBulkData* BulkData = It.Key(); const UObject* Owner = It.Value(); // Only add bulk data that is consuming memory to array. if( Owner && BulkData->IsBulkDataLoaded() && BulkData->GetBulkDataSize() > 0 ) { // Per object stats. PerObjectSizeArray.Add( FObjectAndSize( Owner, BulkData->GetBulkDataSize() ) ); // Per class stats. bool bFoundExistingPerClassSize = false; // Iterate over array, trying to find existing entry. for( int32 PerClassIndex=0; PerClassIndex<PerClassSizeArray.Num(); PerClassIndex++ ) { FObjectAndSize& PerClassSize = PerClassSizeArray[ PerClassIndex ]; // Add to existing entry if found. if( PerClassSize.Object == Owner->GetClass() ) { PerClassSize.Size += BulkData->GetBulkDataSize(); bFoundExistingPerClassSize = true; break; } } // Add new entry if we didn't find an existing one. if( !bFoundExistingPerClassSize ) { PerClassSizeArray.Add( FObjectAndSize( Owner->GetClass(), BulkData->GetBulkDataSize() ) ); } } } } /** Compare operator, sorting by size in descending order */ struct FCompareFObjectAndSize { FORCEINLINE bool operator()( const FObjectAndSize& A, const FObjectAndSize& B ) const { return B.Size < A.Size; } }; // Sort by size. PerObjectSizeArray.Sort( FCompareFObjectAndSize() ); PerClassSizeArray.Sort( FCompareFObjectAndSize() ); // Log information. UE_LOG(LogSerialization, Log, TEXT("")); UE_LOG(LogSerialization, Log, TEXT("Per class summary of bulk data use:")); for( int32 PerClassIndex=0; PerClassIndex<PerClassSizeArray.Num(); PerClassIndex++ ) { const FObjectAndSize& PerClassSize = PerClassSizeArray[ PerClassIndex ]; Log.Logf( TEXT(" %5d KByte of bulk data for Class %s"), PerClassSize.Size / 1024, *PerClassSize.Object->GetPathName() ); } UE_LOG(LogSerialization, Log, TEXT("")); UE_LOG(LogSerialization, Log, TEXT("Detailed per object stats of bulk data use:")); for( int32 PerObjectIndex=0; PerObjectIndex<PerObjectSizeArray.Num(); PerObjectIndex++ ) { const FObjectAndSize& PerObjectSize = PerObjectSizeArray[ PerObjectIndex ]; Log.Logf( TEXT(" %5d KByte of bulk data for %s"), PerObjectSize.Size / 1024, *PerObjectSize.Object->GetFullName() ); } UE_LOG(LogSerialization, Log, TEXT("")); #else UE_LOG(LogSerialization, Log, TEXT("Please recompiled with TRACK_BULKDATA_USE set to 1 in UnBulkData.cpp.")); #endif }
/** Console commands, see embeded usage statement **/ virtual bool Exec( UWorld* Inworld, const TCHAR* Cmd, FOutputDevice& Ar ) { if(FParse::Command(&Cmd,TEXT("LOG"))) { if(FParse::Command(&Cmd,TEXT("LIST"))) // if they didn't use the list command, we will show usage { TArray<FLogCategoryPtrs> Found; FString Cat(FParse::Token(Cmd, 0)); for (TMap<FLogCategoryBase*, FName>::TIterator It(Associations); It; ++It) { FLogCategoryBase* Verb = It.Key(); FString Name = It.Value().ToString(); if (!Cat.Len() || Name.Contains(Cat) ) { Found.Add(FLogCategoryPtrs(Name, ELogVerbosity::Type(Verb->Verbosity), Verb->DebugBreakOnLog)); } } Found.Sort(); for (TArray<FLogCategoryPtrs>::TConstIterator It = Found.CreateConstIterator(); It; ++It) { Ar.Logf(TEXT("%-40s %-12s %s"), *It->Name, FOutputDevice::VerbosityToString(It->Verbosity), It->Postfix ? TEXT(" - DebugBreak") : TEXT("")); } } else { FString Rest(Cmd); Rest = Rest.Trim(); if (Rest.Len()) { if (ProcessLogOnly(Rest, Ar)) { return true; } TMap<FName, uint8> OldValues; for (TMap<FLogCategoryBase*, FName>::TIterator It(Associations); It; ++It) { FLogCategoryBase* Verb = It.Key(); FName Name = It.Value(); OldValues.Add(Name, Verb->Verbosity); } ProcessCmdString(Rest); for (TMap<FLogCategoryBase*, FName>::TIterator It(Associations); It; ++It) { FLogCategoryBase* Verb = It.Key(); FName Name = It.Value(); uint8 OldValue = OldValues.FindRef(Name); if (Verb->Verbosity != OldValue) { Ar.Logf(TEXT("%-40s %-12s %s"), *Name.ToString(), FOutputDevice::VerbosityToString(ELogVerbosity::Type(Verb->Verbosity)), Verb->DebugBreakOnLog ? TEXT(" - DebugBreak") : TEXT("")); } } } else { Ar.Logf( TEXT("------- Log conventions") ); Ar.Logf( TEXT("[cat] = a category for the command to operate on, or 'global' for all categories.") ); Ar.Logf( TEXT("[level] = verbosity level, one of: none, error, warning, display, log, verbose, all, default") ); Ar.Logf( TEXT("At boot time, compiled in default is overridden by ini files setting, which is overridden by command line") ); Ar.Logf( TEXT("------- Log console command usage") ); Ar.Logf( TEXT("Log list - list all log categories") ); Ar.Logf( TEXT("Log list [string] - list all log categories containing a substring") ); Ar.Logf( TEXT("Log reset - reset all log categories to their boot-time default") ); Ar.Logf( TEXT("Log [cat] - toggle the display of the category [cat]") ); Ar.Logf( TEXT("Log [cat] off - disable display of the category [cat]") ); Ar.Logf( TEXT("Log [cat] on - resume display of the category [cat]") ); Ar.Logf( TEXT("Log [cat] only - enables [cat] and disables all other categories")); Ar.Logf( TEXT("Log [cat] [level] - set the verbosity level of the category [cat]") ); Ar.Logf( TEXT("Log [cat] break - toggle the debug break on display of the category [cat]") ); Ar.Logf( TEXT("------- Log command line") ); Ar.Logf( TEXT("-LogCmds=\"[arguments],[arguments]...\" - applies a list of console commands at boot time") ); Ar.Logf( TEXT("-LogCmds=\"foo verbose, bar off\" - turns on the foo category and turns off the bar category") ); Ar.Logf( TEXT("------- Environment variables") ); Ar.Logf( TEXT("Any command line option can be set via the environment variable UE-CmdLineArgs") ); Ar.Logf( TEXT("set UE-CmdLineArgs=\"-LogCmds=foo verbose breakon, bar off\"") ); Ar.Logf( TEXT("------- Config file") ); Ar.Logf( TEXT("[Core.Log]") ); Ar.Logf( TEXT("global=[default verbosity for things not listed later]") ); Ar.Logf( TEXT("[cat]=[level]") ); Ar.Logf( TEXT("foo=verbose break") ); } } return true; } return false; }