/** * 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; }
/** * 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; }