2019-12-26 23:01:54 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-02-05 09:50:56 -05:00
2019-02-05 00:35:21 -05:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Text ;
using System.Text.RegularExpressions ;
using System.Threading.Tasks ;
2020-12-21 23:07:37 -04:00
using EpicGames.Core ;
2019-02-05 00:35:21 -05:00
namespace AutomationTool
{
[Help("Parses Visual C++ timing information (as generated by UBT with the -Timing flag), and converts it into JSON format which can be visualized using the chrome://tracing tab")]
[Help("-File=<Path>", "Path to the input file")]
class ParseMsvcTimingInfo : BuildCommand
{
public override void ExecuteBuild ( )
{
FileReference InputFile = ParseRequiredFileReferenceParam ( "File" ) ;
string [ ] Lines = FileReference . ReadAllLines ( InputFile ) ;
2019-02-05 01:03:02 -05:00
for ( int LineIdx = 0 ; LineIdx < Lines . Length ; )
2019-02-05 00:35:21 -05:00
{
2019-02-05 01:03:02 -05:00
string Line = Lines [ LineIdx ] ;
if ( Line . StartsWith ( "Include Headers:" , StringComparison . Ordinal ) )
{
LineIdx = ParseIncludeHeaders ( Lines , LineIdx + 1 , InputFile . ChangeExtension ( ".json" ) ) ;
}
else if ( Line . StartsWith ( "Class Definitions:" , StringComparison . Ordinal ) )
{
LineIdx = ParseDefinitions ( Lines , LineIdx + 1 , InputFile . ChangeExtension ( ".classes.txt" ) ) ;
}
else if ( Line . StartsWith ( "Function Definitions:" , StringComparison . Ordinal ) )
{
LineIdx = ParseDefinitions ( Lines , LineIdx + 1 , InputFile . ChangeExtension ( ".functions.txt" ) ) ;
}
else
{
LineIdx + + ;
}
2019-02-05 00:35:21 -05:00
}
2019-02-05 01:03:02 -05:00
}
2019-02-05 00:35:21 -05:00
2019-02-05 01:03:02 -05:00
int ParseIncludeHeaders ( string [ ] Lines , int LineIdx , FileReference OutputFile )
{
2019-02-05 00:35:21 -05:00
if ( LineIdx < Lines . Length & & Lines [ LineIdx ] . StartsWith ( "\tCount:" , StringComparison . Ordinal ) )
{
LineIdx + + ;
}
using ( JsonWriter Writer = new JsonWriter ( OutputFile ) )
{
Writer . WriteObjectStart ( ) ;
Writer . WriteArrayStart ( "traceEvents" ) ;
Stack < float > FinishTimesForIndent = new Stack < float > ( ) ;
FinishTimesForIndent . Push ( 0.0f ) ;
float StartTime = 0.0f ;
for ( ; LineIdx < Lines . Length ; LineIdx + + )
{
Match Match = Regex . Match ( Lines [ LineIdx ] , "^\t\t(\t*)([^\t]+):\\s*([0-9\\.]+)s$" ) ;
if ( ! Match . Success )
{
break ;
}
int Indent = Match . Groups [ 1 ] . Length ;
string FileName = Match . Groups [ 2 ] . Value ;
float Duration = float . Parse ( Match . Groups [ 3 ] . Value ) ;
while ( Indent < = FinishTimesForIndent . Count - 1 )
{
StartTime = FinishTimesForIndent . Pop ( ) ;
}
Writer . WriteObjectStart ( ) ;
Writer . WriteValue ( "pid" , 1 ) ;
Writer . WriteValue ( "tid" , 1 ) ;
2019-02-06 13:47:04 -05:00
Writer . WriteValue ( "ts" , ( long ) ( StartTime * 1000.0f * 1000.0f ) ) ;
Writer . WriteValue ( "dur" , ( long ) ( Duration * 1000.0f * 1000.0f ) ) ;
2019-02-05 00:35:21 -05:00
Writer . WriteValue ( "ph" , "X" ) ;
Writer . WriteValue ( "name" , Path . GetFileName ( FileName ) ) ;
Writer . WriteObjectStart ( "args" ) ;
Writer . WriteValue ( "path" , FileName ) ;
Writer . WriteObjectEnd ( ) ;
Writer . WriteObjectEnd ( ) ;
while ( Indent > = FinishTimesForIndent . Count )
{
FinishTimesForIndent . Push ( StartTime + Duration ) ;
}
}
Writer . WriteArrayEnd ( ) ;
Writer . WriteObjectEnd ( ) ;
}
2019-02-05 01:03:02 -05:00
return LineIdx ;
}
int ParseDefinitions ( string [ ] Lines , int LineIdx , FileReference OutputFile )
{
if ( LineIdx < Lines . Length & & Lines [ LineIdx ] . StartsWith ( "\tCount:" , StringComparison . Ordinal ) )
{
LineIdx + + ;
}
Dictionary < string , float > ClassNameToTime = new Dictionary < string , float > ( ) ;
for ( ; LineIdx < Lines . Length ; LineIdx + + )
{
Match Match = Regex . Match ( Lines [ LineIdx ] , "^\t\t\t*([^\t]+):\\s*([0-9\\.]+)s$" ) ;
if ( ! Match . Success )
{
break ;
}
string ClassName = Match . Groups [ 1 ] . Value ;
int TemplateIdx = ClassName . IndexOf ( '<' ) ;
if ( TemplateIdx ! = - 1 )
{
ClassName = ClassName . Substring ( 0 , TemplateIdx ) + "<>" ;
}
float Time ;
ClassNameToTime . TryGetValue ( ClassName , out Time ) ;
Time + = float . Parse ( Match . Groups [ 2 ] . Value ) ;
ClassNameToTime [ ClassName ] = Time ;
}
using ( StreamWriter Writer = new StreamWriter ( OutputFile . FullName ) )
{
foreach ( KeyValuePair < string , float > Pair in ClassNameToTime . OrderByDescending ( x = > x . Value ) )
{
Writer . WriteLine ( "{0,7:0.000}: {1}" , Pair . Value , Pair . Key ) ;
}
}
return LineIdx ;
2019-02-05 00:35:21 -05:00
}
}
}