├── Documentation~ └── img │ ├── ProfilerLogToCsv.png │ └── ProfilerReaderFilter.png ├── Editor.meta ├── Editor ├── Analyzer.meta ├── Analyzer │ ├── AnalyzeToTextbaseFileBase.cs │ ├── AnalyzeToTextbaseFileBase.cs.meta │ ├── AnalyzerUtil.cs │ ├── AnalyzerUtil.cs.meta │ ├── CsvStringGenerator.cs │ ├── CsvStringGenerator.cs.meta │ ├── IAnalyzeFileWriter.cs │ ├── IAnalyzeFileWriter.cs.meta │ ├── Impl.meta │ ├── Impl │ │ ├── EngineFileOperateAnalyzeToFile.cs │ │ ├── EngineFileOperateAnalyzeToFile.cs.meta │ │ ├── GCAnalyzeToFile.cs │ │ ├── GCAnalyzeToFile.cs.meta │ │ ├── GcCallStackInfoAnalyzeToFile.cs │ │ ├── GcCallStackInfoAnalyzeToFile.cs.meta │ │ ├── GpuSampleToFile.cs │ │ ├── GpuSampleToFile.cs.meta │ │ ├── JitInfoAnalyzeToFile.cs │ │ ├── JitInfoAnalyzeToFile.cs.meta │ │ ├── MainThreadAnalyzeToFile.cs │ │ ├── MainThreadAnalyzeToFile.cs.meta │ │ ├── MainThreadCategoryAnalyzeToFile.cs │ │ ├── MainThreadCategoryAnalyzeToFile.cs.meta │ │ ├── MemoryAnalyzeToFile.cs │ │ ├── MemoryAnalyzeToFile.cs.meta │ │ ├── RenderThreadToFile.cs │ │ ├── RenderThreadToFile.cs.meta │ │ ├── RenderingAnalyzeToFile.cs │ │ ├── RenderingAnalyzeToFile.cs.meta │ │ ├── ScreenshotToPng.cs │ │ ├── ScreenshotToPng.cs.meta │ │ ├── ShaderCompileToFile.cs │ │ ├── ShaderCompileToFile.cs.meta │ │ ├── ThreadAnalyzeToFile.cs │ │ ├── ThreadAnalyzeToFile.cs.meta │ │ ├── UrpGpuSampleToFile .cs │ │ ├── UrpGpuSampleToFile .cs.meta │ │ ├── WorkerJobAnalyzeToFile.cs │ │ └── WorkerJobAnalyzeToFile.cs.meta │ ├── ProfilingScope.cs │ └── ProfilingScope.cs.meta ├── CUI.meta ├── CUI │ ├── CUIInterface.cs │ └── CUIInterface.cs.meta ├── GUI.meta ├── GUI │ ├── AnalyzeToCsvWindow.cs │ ├── AnalyzeToCsvWindow.cs.meta │ ├── LogAnalyzeWindow.cs │ ├── LogAnalyzeWindow.cs.meta │ ├── language.meta │ └── language │ │ ├── LanguageEn.cs │ │ ├── LanguageEn.cs.meta │ │ ├── LanguageInterface.cs │ │ ├── LanguageInterface.cs.meta │ │ ├── LanguageJa.cs │ │ └── LanguageJa.cs.meta ├── UTJProfileReader.dll ├── UTJProfileReader.dll.meta ├── Utj.ProfilerReader.Editor.asmdef ├── Utj.ProfilerReader.Editor.asmdef.meta ├── UtjProfilerInitializer.cs └── UtjProfilerInitializer.cs.meta ├── LICENSE.md ├── LICENSE.md.meta ├── README.ja.md ├── README.ja.md.meta ├── README.md ├── README.md.meta ├── package.json └── package.json.meta /Documentation~/img/ProfilerLogToCsv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unity3d-jp/ProfilerReader/5843fd01fd68d2cf0ac606c28c31c4d4b36dac58/Documentation~/img/ProfilerLogToCsv.png -------------------------------------------------------------------------------- /Documentation~/img/ProfilerReaderFilter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unity3d-jp/ProfilerReader/5843fd01fd68d2cf0ac606c28c31c4d4b36dac58/Documentation~/img/ProfilerReaderFilter.png -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 17970b62d246c2a4bb30ed8b06ac53cf 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Analyzer.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0f01dcbdc9f147a4fbdcfe25cfc31014 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Analyzer/AnalyzeToTextbaseFileBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UTJ.ProfilerReader.BinaryData; 4 | 5 | namespace UTJ.ProfilerReader.Analyzer 6 | { 7 | 8 | 9 | 10 | public abstract class AnalyzeToTextbaseFileBase : IAnalyzeFileWriter 11 | { 12 | protected ProfilerLogFormat logFormat 13 | { 14 | get; private set; 15 | } 16 | protected uint logVersion 17 | { 18 | get; private set; 19 | } 20 | protected ushort logPlatform 21 | { 22 | get; private set; 23 | } 24 | 25 | protected abstract string FooterName 26 | { 27 | get; 28 | } 29 | protected string unityVersion 30 | { 31 | get; private set; 32 | } 33 | 34 | public void SetInfo(ProfilerLogFormat format, string unityVer, uint dataversion, ushort platform) 35 | { 36 | this.logFormat = format; 37 | this.logVersion = dataversion; 38 | this.logPlatform = platform; 39 | this.unityVersion = unityVer; 40 | } 41 | 42 | public abstract void CollectData(ProfilerFrameData frameData); 43 | 44 | protected abstract string GetResultText(); 45 | 46 | public void WriteResultFile(string logfile, string outputpath) 47 | { 48 | try 49 | { 50 | var path = System.IO.Path.Combine(outputpath, logfile.Replace(".", "_") + this.FooterName); 51 | string result = GetResultText(); 52 | System.IO.File.WriteAllText(path, result); 53 | } 54 | catch (System.Exception e) 55 | { 56 | ProfilerLogUtil.logErrorException(e); 57 | } 58 | } 59 | 60 | public void SetFileInfo(string logfile, string outputpath) 61 | { 62 | 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Editor/Analyzer/AnalyzeToTextbaseFileBase.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 55c111e5c73e93e45a21a05c4e5109db 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/AnalyzerUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | 5 | namespace UTJ.ProfilerReader.Analyzer 6 | { 7 | public class AnalyzerUtil 8 | { 9 | public static List CreateAnalyzerInterfaceObjects() 10 | { 11 | var types = GetInterfaceType(); 12 | return CreateInstanciateObjects(types); 13 | } 14 | 15 | private static List CreateInstanciateObjects(List types) where T : class 16 | { 17 | List ret = new List(); 18 | foreach (var t in types) 19 | { 20 | if (t.IsAbstract) { continue; } 21 | var inst = Activator.CreateInstance(t) as T; 22 | ret.Add(inst); 23 | } 24 | return ret; 25 | } 26 | 27 | public static List GetInterfaceType() 28 | { 29 | List ret = new List(); 30 | var domain = System.AppDomain.CurrentDomain; 31 | var assemblies = domain.GetAssemblies(); 32 | foreach (var assembly in assemblies) 33 | { 34 | var types = assembly.GetTypes(); 35 | foreach (var type in types) 36 | { 37 | var interfaces = type.GetInterfaces(); 38 | if (interfaces == null) 39 | { 40 | continue; 41 | } 42 | foreach (var interfacetype in interfaces) 43 | { 44 | if (interfacetype == typeof(T) && !type.IsAbstract) 45 | { 46 | ret.Add(type); 47 | } 48 | } 49 | } 50 | } 51 | return ret; 52 | } 53 | 54 | } 55 | } -------------------------------------------------------------------------------- /Editor/Analyzer/AnalyzerUtil.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c4b017d20dfef334383db409d73c50ab 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/CsvStringGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | 4 | namespace UTJ.ProfilerReader.Analyzer 5 | { 6 | public class CsvStringGenerator 7 | { 8 | private StringBuilder stringBuilder; 9 | public CsvStringGenerator() 10 | { 11 | stringBuilder = new StringBuilder(1024 * 1024); 12 | } 13 | public CsvStringGenerator(StringBuilder sb) 14 | { 15 | stringBuilder = sb; 16 | } 17 | public CsvStringGenerator AppendColumn(string val) 18 | { 19 | if( val == null) { val = ""; } 20 | if (val.Contains(",")) { 21 | val = val.Replace(',', '.'); 22 | } 23 | if (val.Contains("\n")) 24 | { 25 | val = val.Replace('\n', ' '); 26 | } 27 | stringBuilder.Append(val).Append(','); 28 | return this; 29 | } 30 | public CsvStringGenerator AppendColumn(string val,int idx,int length) 31 | { 32 | if (val == null) 33 | { 34 | stringBuilder.Append(","); 35 | return this; 36 | } 37 | if (val.Contains(",") ) 38 | { 39 | val = val.Replace(',', '.'); 40 | } 41 | if (val.Contains("\n")) 42 | { 43 | val = val.Replace('\n', ' '); 44 | } 45 | else 46 | { 47 | stringBuilder.Append(val, idx, length).Append(','); 48 | } 49 | return this; 50 | } 51 | public CsvStringGenerator AppendColumn(int val) 52 | { 53 | stringBuilder.Append(val).Append(','); 54 | return this; 55 | } 56 | public CsvStringGenerator AppendColumn(bool val) 57 | { 58 | stringBuilder.Append(val).Append(','); 59 | return this; 60 | } 61 | public CsvStringGenerator AppendColumn(float val) 62 | { 63 | stringBuilder.Append(val).Append(','); 64 | return this; 65 | } 66 | public CsvStringGenerator AppendColumn(ulong val) 67 | { 68 | stringBuilder.Append(val).Append(','); 69 | return this; 70 | } 71 | public CsvStringGenerator AppendColumnAsAddr(ulong val) 72 | { 73 | stringBuilder.Append("0x"); 74 | AppendAddrStr(stringBuilder, val,16).Append(','); 75 | return this; 76 | } 77 | public CsvStringGenerator NextRow() 78 | { 79 | stringBuilder.Append("\n"); 80 | return this; 81 | } 82 | 83 | public override string ToString() 84 | { 85 | return stringBuilder.ToString(); 86 | } 87 | 88 | private static char[] addrChars = new char[] { 89 | '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' 90 | }; 91 | 92 | // AppendFormat("0x{0,0:X16}", val) <- allocate a lot of managed memory... 93 | public static StringBuilder AppendAddrStr(StringBuilder sb,ulong val,int num) { 94 | for (int i = num - 1; i >= 0; --i ) 95 | { 96 | ulong masked = (val >> (i*4) )& 0xf; 97 | sb.Append(addrChars[masked]); 98 | } 99 | return sb; 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /Editor/Analyzer/CsvStringGenerator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dc945cd84f5e43148bc02deadfd310b2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/IAnalyzeFileWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UTJ.ProfilerReader.BinaryData; 4 | 5 | namespace UTJ.ProfilerReader.Analyzer 6 | { 7 | 8 | public enum ProfilerLogFormat 9 | { 10 | TypeData, 11 | TypeRaw, 12 | } 13 | 14 | public interface IAnalyzeFileWriter 15 | { 16 | 17 | void SetFileInfo(string logfilename, string outputpath); 18 | void SetInfo(ProfilerLogFormat logformat,string unityVersion, uint dataversion, ushort platform); 19 | 20 | void CollectData(ProfilerFrameData frameData); 21 | 22 | void WriteResultFile(string logfilaneme,string outputpath); 23 | 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /Editor/Analyzer/IAnalyzeFileWriter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d90901940130ecf48a5862025c483e10 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 96fae94d084a0e44383e880362d47836 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/EngineFileOperateAnalyzeToFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Text; 4 | using UnityEngine.Profiling; 5 | using UTJ.ProfilerReader.BinaryData; 6 | 7 | using UTJ.ProfilerReader.BinaryData.Stats; 8 | 9 | namespace UTJ.ProfilerReader.Analyzer 10 | { 11 | 12 | public class EngineFileOperateAnalyzeToFile : AnalyzeToTextbaseFileBase 13 | { 14 | struct FileEvent 15 | { 16 | public string thread; 17 | 18 | public string eventStr; 19 | 20 | public int frameIdx; 21 | public string file; 22 | public ulong size; 23 | public long seekOffset; 24 | public int param; 25 | 26 | public ulong startTime; 27 | public float tm; 28 | } 29 | 30 | private List fileEvents = new List(2048); 31 | 32 | 33 | 34 | public override void CollectData(ProfilerFrameData frameData) 35 | { 36 | if( frameData == null ) 37 | { 38 | return; 39 | } 40 | 41 | 42 | HashSet doneList = new HashSet(); 43 | foreach( var thread in frameData.m_ThreadData) 44 | { 45 | if(thread.m_AllSamples == null) { continue; } 46 | foreach( var sample in thread.m_AllSamples) 47 | { 48 | if (!sample.sampleName.StartsWith("File.")) { continue; } 49 | 50 | if (sample.sampleName == "File.Open") 51 | { 52 | AddFileOpenCloseData(frameData.frameIndex, thread, sample); 53 | } 54 | else if (sample.sampleName == "File.Read") 55 | { 56 | AddFileReadData(frameData.frameIndex, thread, sample); 57 | } 58 | else if (sample.sampleName == "File.Seek") 59 | { 60 | AddFileSeekData(frameData.frameIndex, thread, sample); 61 | } 62 | else if (sample.sampleName == "File.Close") 63 | { 64 | AddFileOpenCloseData(frameData.frameIndex, thread, sample); 65 | } 66 | } 67 | } 68 | } 69 | 70 | private void SetupInfo(ref FileEvent evt,int idx, ThreadData thread, ProfilerSample sample) 71 | { 72 | evt.frameIdx = idx; 73 | evt.thread = thread.FullName; 74 | evt.startTime = sample.startTimeUS; 75 | evt.tm = sample.selfTimeUs / 1000.0f; 76 | evt.eventStr = sample.sampleName; 77 | } 78 | 79 | private void AddFileOpenCloseData(int idx, ThreadData thread, ProfilerSample sample) 80 | { 81 | FileEvent evt = new FileEvent(); 82 | SetupInfo(ref evt, idx, thread, sample); 83 | 84 | var metaData = sample.metadataValues; 85 | if (metaData != null) 86 | { 87 | try 88 | { 89 | if (metaData.Count > 0) 90 | { 91 | evt.file = metaData[0].convertedObject.ToString(); 92 | } 93 | } 94 | catch (System.Exception e) 95 | { 96 | ProfilerLogUtil.logErrorException(e); 97 | } 98 | } 99 | 100 | fileEvents.Add(evt); 101 | } 102 | 103 | 104 | private void AddFileSeekData(int idx, ThreadData thread, ProfilerSample sample) 105 | { 106 | FileEvent evt = new FileEvent(); 107 | SetupInfo(ref evt, idx, thread, sample); 108 | 109 | var metaData = sample.metadataValues; 110 | if (metaData != null) 111 | { 112 | try 113 | { 114 | if (metaData.Count > 0) 115 | { 116 | evt.file = metaData[0].convertedObject.ToString(); 117 | } 118 | if (metaData.Count > 1) 119 | { 120 | evt.seekOffset = (long)metaData[1].convertedObject; 121 | } 122 | if (metaData.Count > 2) 123 | { 124 | evt.param = (int)metaData[2].convertedObject; 125 | } 126 | } 127 | catch (System.Exception e) 128 | { 129 | ProfilerLogUtil.logErrorException(e); 130 | } 131 | } 132 | 133 | fileEvents.Add(evt); 134 | } 135 | 136 | private void AddFileReadData(int idx ,ThreadData thread, ProfilerSample sample) 137 | { 138 | FileEvent evt = new FileEvent(); 139 | SetupInfo(ref evt, idx, thread, sample); 140 | 141 | var metaData = sample.metadataValues; 142 | if (metaData != null ) 143 | { 144 | try 145 | { 146 | if (metaData.Count > 0) 147 | { 148 | evt.file = metaData[0].convertedObject.ToString(); 149 | } 150 | if (metaData.Count > 1) 151 | { 152 | evt.param = (int)metaData[1].convertedObject; 153 | } 154 | if (metaData.Count > 2) 155 | { 156 | evt.size = (ulong)metaData[2].convertedObject; 157 | } 158 | }catch(System.Exception e) 159 | { 160 | ProfilerLogUtil.logErrorException(e); 161 | } 162 | } 163 | 164 | fileEvents.Add(evt); 165 | } 166 | 167 | 168 | 169 | 170 | /// 171 | /// 結果書き出し 172 | /// 173 | protected override string GetResultText() 174 | { 175 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 176 | AppendHeaderToStringBuilder(csvStringGenerator); 177 | 178 | fileEvents.Sort((a, b) => 179 | { 180 | if( a.startTime > b.startTime) 181 | { 182 | return 1; 183 | } 184 | else if (a.startTime < b.startTime) 185 | { 186 | return -1; 187 | } 188 | return 0; 189 | 190 | }); 191 | 192 | foreach (var evt in fileEvents) 193 | { 194 | int filePathIdx = 0; 195 | int length = 0; 196 | if (evt.file != null) 197 | { 198 | filePathIdx = evt.file.LastIndexOf('/') +1; 199 | length = evt.file.Length; 200 | } 201 | 202 | csvStringGenerator.AppendColumn(evt.frameIdx); 203 | csvStringGenerator.AppendColumn(evt.file, filePathIdx, length - filePathIdx); 204 | csvStringGenerator.AppendColumn(evt.eventStr); 205 | csvStringGenerator.AppendColumn(evt.thread); 206 | csvStringGenerator.AppendColumn(evt.param); 207 | csvStringGenerator.AppendColumn(evt.size); 208 | csvStringGenerator.AppendColumn(evt.seekOffset); 209 | csvStringGenerator.AppendColumn(evt.tm); 210 | csvStringGenerator.AppendColumn(evt.file); 211 | csvStringGenerator.NextRow(); 212 | } 213 | 214 | return csvStringGenerator.ToString(); 215 | } 216 | private void AppendHeaderToStringBuilder(CsvStringGenerator csvStringGenerator) 217 | { 218 | 219 | csvStringGenerator.AppendColumn("frameIdx"); 220 | csvStringGenerator.AppendColumn("file"); 221 | csvStringGenerator.AppendColumn("event"); 222 | csvStringGenerator.AppendColumn("thread"); 223 | csvStringGenerator.AppendColumn("param"); 224 | csvStringGenerator.AppendColumn("size"); 225 | csvStringGenerator.AppendColumn("seekOffset"); 226 | csvStringGenerator.AppendColumn("execTime"); 227 | csvStringGenerator.AppendColumn("fullPath"); 228 | csvStringGenerator.NextRow(); 229 | 230 | } 231 | 232 | 233 | protected override string FooterName 234 | { 235 | get 236 | { 237 | return "_engine_file_operate.csv"; 238 | } 239 | } 240 | 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/EngineFileOperateAnalyzeToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e15215876c0578b4aa9d055d04534599 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/GCAnalyzeToFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Text; 4 | using UnityEngine.Profiling; 5 | using UTJ.ProfilerReader.BinaryData; 6 | 7 | using UTJ.ProfilerReader.BinaryData.Stats; 8 | 9 | namespace UTJ.ProfilerReader.Analyzer 10 | { 11 | 12 | public class GCAnalyzeToFile : AnalyzeToTextbaseFileBase 13 | { 14 | 15 | class SampleKey : System.IEquatable 16 | { 17 | public string threadName; 18 | public string methodName; 19 | public string fullMethodName; 20 | 21 | public override int GetHashCode() 22 | { 23 | return threadName.GetHashCode()+ methodName.GetHashCode() ; 24 | } 25 | public bool Equals(SampleKey other) 26 | { 27 | return ((other.threadName == this.threadName) && (other.fullMethodName == this.fullMethodName)); 28 | } 29 | 30 | public SampleKey(string th,string method,string fullMethod) 31 | { 32 | threadName = th; 33 | methodName = method; 34 | fullMethodName = fullMethod; 35 | } 36 | } 37 | 38 | class GcInfo 39 | { 40 | public uint allocNum; 41 | public ulong allocAll; 42 | public uint allocMax = uint.MinValue; 43 | public uint allocMin = uint.MaxValue; 44 | } 45 | 46 | private Dictionary gcDitionary = new Dictionary(); 47 | 48 | private void AddData(string threadName,ProfilerSample sample,uint gcAlloc) 49 | { 50 | var key = new SampleKey(threadName,sample.sampleName, sample.fullSampleName); 51 | GcInfo data; 52 | if (!gcDitionary.TryGetValue(key,out data)) 53 | { 54 | data = new GcInfo(); 55 | gcDitionary.Add(key, data); 56 | } 57 | data.allocAll += gcAlloc; 58 | data.allocNum ++; 59 | data.allocMin = ProfilerLogUtil.Min(data.allocMin, gcAlloc); 60 | data.allocMax = ProfilerLogUtil.Max(data.allocMax, gcAlloc); 61 | } 62 | 63 | 64 | public override void CollectData(ProfilerFrameData frameData) 65 | { 66 | if( frameData == null ) 67 | { 68 | return; 69 | } 70 | 71 | 72 | HashSet doneList = new HashSet(); 73 | foreach( var thread in frameData.m_ThreadData) 74 | { 75 | if(thread.m_AllSamples == null) { continue; } 76 | foreach( var sample in thread.m_AllSamples) 77 | { 78 | if(sample != null && sample.parent != null && sample.sampleName == "GC.Alloc") 79 | { 80 | var parent = sample.parent; 81 | if (!doneList.Contains(parent)) 82 | { 83 | AddData(thread.FullName, parent, parent.GetSelfChildGcAlloc()); 84 | doneList.Add(parent); 85 | } 86 | } 87 | } 88 | } 89 | } 90 | 91 | 92 | 93 | 94 | /// 95 | /// 結果書き出し 96 | /// 97 | protected override string GetResultText() 98 | { 99 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 100 | AppendHeaderToStringBuilder(csvStringGenerator); 101 | foreach( var kvs in gcDitionary) 102 | { 103 | csvStringGenerator.AppendColumn(kvs.Key.threadName); 104 | csvStringGenerator.AppendColumn(kvs.Key.methodName); 105 | csvStringGenerator.AppendColumn(kvs.Key.fullMethodName); 106 | csvStringGenerator.AppendColumn(kvs.Value.allocNum); 107 | csvStringGenerator.AppendColumn(kvs.Value.allocAll); 108 | csvStringGenerator.AppendColumn(kvs.Value.allocAll / kvs.Value.allocNum); 109 | csvStringGenerator.AppendColumn(kvs.Value.allocMin); 110 | csvStringGenerator.AppendColumn(kvs.Value.allocMax); 111 | csvStringGenerator.NextRow(); 112 | } 113 | return csvStringGenerator.ToString(); 114 | } 115 | private void AppendHeaderToStringBuilder(CsvStringGenerator csvStringGenerator) 116 | { 117 | csvStringGenerator.AppendColumn("thread"); 118 | // Total 119 | csvStringGenerator.AppendColumn("SampleName"); 120 | csvStringGenerator.AppendColumn("FullName"); 121 | csvStringGenerator.AppendColumn("calls"); 122 | csvStringGenerator.AppendColumn("all(byte)"); 123 | csvStringGenerator.AppendColumn("average(byte)"); 124 | csvStringGenerator.AppendColumn("min(byte)"); 125 | csvStringGenerator.AppendColumn("max(byte)"); 126 | csvStringGenerator.NextRow(); 127 | 128 | } 129 | 130 | 131 | protected override string FooterName 132 | { 133 | get 134 | { 135 | return "_gc_result.csv"; 136 | } 137 | } 138 | 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/GCAnalyzeToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 95d7f661b5a908c4ea5067c04e49ef60 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/GcCallStackInfoAnalyzeToFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.Diagnostics; 5 | using System.Text; 6 | using UTJ.ProfilerReader.BinaryData; 7 | using UnityEngine.Profiling; 8 | 9 | using UTJ.ProfilerReader.BinaryData.Stats; 10 | using UTJ.ProfilerReader.RawData.Protocol; 11 | using UnityEngine.Playables; 12 | 13 | namespace UTJ.ProfilerReader.Analyzer 14 | { 15 | 16 | public class GcCallStackInfoAnalyzeToFile : AnalyzeToTextbaseFileBase 17 | { 18 | struct SampleKey : System.IEquatable 19 | { 20 | public string threadName; 21 | public string methodName; 22 | public string fullMethodName; 23 | public string callStackName; 24 | private int hashCodeCache; 25 | 26 | public override int GetHashCode() 27 | { 28 | return this.hashCodeCache; 29 | } 30 | public bool Equals(SampleKey other) 31 | { 32 | return ( 33 | (other.callStackName == this.callStackName) && 34 | (other.fullMethodName == this.fullMethodName) && 35 | (other.threadName == this.threadName) ); 36 | } 37 | 38 | public SampleKey(string th,string method,string fullMethod,string callStack) 39 | { 40 | threadName = th; 41 | methodName = method; 42 | fullMethodName = fullMethod; 43 | callStackName = callStack; 44 | this.hashCodeCache = threadName.GetHashCode() + fullMethodName.GetHashCode(); 45 | } 46 | } 47 | 48 | class GcInfo 49 | { 50 | public uint allocNum; 51 | public ulong allocAll; 52 | public uint allocMax = uint.MinValue; 53 | public uint allocMin = uint.MaxValue; 54 | } 55 | 56 | 57 | #if UTJ_CHECK 58 | private static readonly CustomSampler _collectDataSampler = CustomSampler.Create("GcCallStackInfoAnalyzeToFile.CollectData"); 59 | private static readonly CustomSampler _addDataSampler = CustomSampler.Create("GcCallStackInfoAnalyzeToFile.AddData"); 60 | private static readonly CustomSampler _callStackSampler = CustomSampler.Create("GcCallStackInfoAnalyzeToFile.GetCallStack"); 61 | private static readonly CustomSampler _gcAllocGetSampler = CustomSampler.Create("GcCallStackInfoAnalyzeToFile.GetSelfChildGcAlloc"); 62 | #endif 63 | 64 | 65 | private Dictionary gcDitionary = new Dictionary(); 66 | private StringBuilder stringBuilder = new StringBuilder(1024); 67 | 68 | private void AddData(string threadName,ProfilerSample sample,string callstack,uint gcAlloc) 69 | { 70 | #if UTJ_CHECK 71 | using (new ProfilingScope(_addDataSampler)) 72 | #endif 73 | { 74 | var key = new SampleKey(threadName, sample.sampleName, sample.fullSampleName, callstack); 75 | GcInfo data; 76 | if (!gcDitionary.TryGetValue(key, out data)) 77 | { 78 | data = new GcInfo(); 79 | gcDitionary.Add(key, data); 80 | } 81 | data.allocAll += gcAlloc; 82 | data.allocNum++; 83 | data.allocMin = ProfilerLogUtil.Min(data.allocMin, gcAlloc); 84 | data.allocMax = ProfilerLogUtil.Max(data.allocMax, gcAlloc); 85 | } 86 | } 87 | 88 | 89 | public override void CollectData(ProfilerFrameData frameData) 90 | { 91 | #if UTJ_CHECK 92 | using (new ProfilingScope(_collectDataSampler)) 93 | #endif 94 | { 95 | if (frameData == null) 96 | { 97 | return; 98 | } 99 | 100 | FindGCAllocMarkerId(frameData); 101 | 102 | if (useMakerId) 103 | { 104 | foreach (var thread in frameData.m_ThreadData) 105 | { 106 | if (thread.m_AllSamples == null) { continue; } 107 | foreach (var sample in thread.m_AllSamples) 108 | { 109 | if (sample != null && sample.parent != null && 110 | sample.makerId == this.gcAllocMakerId) 111 | { 112 | uint selfGcAlloc = 0; 113 | var parent = sample.parent; 114 | #if UTJ_CHECK 115 | using (new ProfilingScope(_gcAllocGetSampler)) 116 | #endif 117 | { 118 | selfGcAlloc = sample.currenGcAlloc; 119 | } 120 | AddData(thread.FullName, parent, GetCallStackInfo(frameData, sample), 121 | selfGcAlloc); 122 | } 123 | } 124 | 125 | } 126 | } 127 | else 128 | { 129 | foreach (var thread in frameData.m_ThreadData) 130 | { 131 | if (thread.m_AllSamples == null) { continue; } 132 | foreach (var sample in thread.m_AllSamples) 133 | { 134 | if (sample != null && sample.parent != null && 135 | sample.sampleName == "GC.Alloc") 136 | { 137 | var parent = sample.parent; 138 | AddData(thread.FullName, parent, GetCallStackInfo(frameData, sample), 139 | sample.currenGcAlloc); 140 | } 141 | } 142 | } 143 | } 144 | } 145 | } 146 | 147 | private bool useMakerId = false; 148 | private bool foundMakerId = false; 149 | private uint gcAllocMakerId = 0xFFFFFFFF; 150 | 151 | private void FindGCAllocMarkerId(ProfilerFrameData frameData) 152 | { 153 | if (foundMakerId) { return; } 154 | foreach (var thread in frameData.m_ThreadData) 155 | { 156 | if (thread.m_AllSamples == null) { continue; } 157 | foreach (var sample in thread.m_AllSamples) 158 | { 159 | if (sample != null && sample.parent != null && 160 | sample.sampleName == "GC.Alloc") 161 | { 162 | gcAllocMakerId = sample.makerId; 163 | foundMakerId = true; 164 | if(gcAllocMakerId != 0 && gcAllocMakerId != 0xFFFFFFFF) 165 | { 166 | useMakerId = true; 167 | } 168 | break; 169 | } 170 | } 171 | } 172 | } 173 | 174 | 175 | private string GetCallStackInfo( ProfilerFrameData frameData,ProfilerSample profilerSample) 176 | { 177 | #if UTJ_CHECK 178 | using (new ProfilingScope(_callStackSampler)) 179 | #endif 180 | { 181 | 182 | if (profilerSample == null) { return ""; } 183 | var callStackInfo = profilerSample.callStackInfo; 184 | if (callStackInfo == null) 185 | { 186 | return ""; 187 | } 188 | stringBuilder.Length = 0; 189 | 190 | int length = callStackInfo.stack.Length; 191 | bool isAlreadyAdd = false; 192 | for (int i = length - 1; i >= 0; --i) 193 | { 194 | var info = frameData.FindJitInfoFromAddr(callStackInfo.stack[i]); 195 | if (info == null) { continue; } 196 | if (isAlreadyAdd) 197 | { 198 | stringBuilder.Append("->"); 199 | } 200 | stringBuilder.Append("["); 201 | CsvStringGenerator.AppendAddrStr(stringBuilder, info.codeAddr, 16).Append("]"); 202 | stringBuilder.Append(info.name); 203 | isAlreadyAdd = true; 204 | } 205 | return stringBuilder.ToString(); 206 | } 207 | } 208 | 209 | 210 | 211 | 212 | /// 213 | /// 結果書き出し 214 | /// 215 | protected override string GetResultText() 216 | { 217 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 218 | AppendHeaderToStringBuilder(csvStringGenerator); 219 | foreach( var kvs in gcDitionary) 220 | { 221 | csvStringGenerator.AppendColumn(kvs.Key.threadName); 222 | csvStringGenerator.AppendColumn(kvs.Key.methodName); 223 | csvStringGenerator.AppendColumn(kvs.Key.fullMethodName); 224 | csvStringGenerator.AppendColumn(kvs.Key.callStackName); 225 | csvStringGenerator.AppendColumn(kvs.Value.allocNum); 226 | csvStringGenerator.AppendColumn(kvs.Value.allocAll); 227 | csvStringGenerator.AppendColumn(kvs.Value.allocAll / kvs.Value.allocNum); 228 | csvStringGenerator.AppendColumn(kvs.Value.allocMin); 229 | csvStringGenerator.AppendColumn(kvs.Value.allocMax); 230 | csvStringGenerator.NextRow(); 231 | } 232 | return csvStringGenerator.ToString(); 233 | } 234 | private void AppendHeaderToStringBuilder(CsvStringGenerator csvStringGenerator) 235 | { 236 | csvStringGenerator.AppendColumn("thread"); 237 | // Total 238 | csvStringGenerator.AppendColumn("SampleName"); 239 | csvStringGenerator.AppendColumn("FullName"); 240 | csvStringGenerator.AppendColumn("CallStack"); 241 | csvStringGenerator.AppendColumn("calls"); 242 | csvStringGenerator.AppendColumn("all(byte)"); 243 | csvStringGenerator.AppendColumn("average(byte)"); 244 | csvStringGenerator.AppendColumn("min(byte)"); 245 | csvStringGenerator.AppendColumn("max(byte)"); 246 | csvStringGenerator.NextRow(); 247 | 248 | } 249 | 250 | 251 | protected override string FooterName 252 | { 253 | get 254 | { 255 | return "_gc_detail.csv"; 256 | } 257 | } 258 | 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/GcCallStackInfoAnalyzeToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 926a0e3638071e54e981f2dd25e21d82 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/GpuSampleToFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UTJ.ProfilerReader.BinaryData; 3 | using System.Text; 4 | using UTJ.ProfilerReader.BinaryData.Thread; 5 | using UTJ.ProfilerReader.RawData.Protocol; 6 | 7 | namespace UTJ.ProfilerReader.Analyzer 8 | { 9 | public class GPUSampleToFile : AnalyzeToTextbaseFileBase 10 | { 11 | private struct GpuTimeInfo 12 | { 13 | public int time; 14 | public int count; 15 | } 16 | private class FrameGpuTime 17 | { 18 | public int frameIdx; 19 | public Dictionary gpuTimeByCategory; 20 | 21 | public void AddGpuSample(GPUTime gpuTime) 22 | { 23 | if(gpuTimeByCategory == null) 24 | { 25 | gpuTimeByCategory = new Dictionary(); 26 | } 27 | GpuTimeInfo time; 28 | if( gpuTimeByCategory.TryGetValue( gpuTime.gpuSection,out time)) 29 | { 30 | time.time += gpuTime.gpuTimeInMicroSec; 31 | time.count += 1; 32 | gpuTimeByCategory[gpuTime.gpuSection] = time; 33 | } 34 | else 35 | { 36 | time = new GpuTimeInfo { time = gpuTime.gpuTimeInMicroSec, count = 1 }; 37 | gpuTimeByCategory.Add(gpuTime.gpuSection, time); 38 | } 39 | } 40 | } 41 | 42 | private List frameGpuTimes = new List(); 43 | 44 | public override void CollectData(ProfilerFrameData frameData) 45 | { 46 | FrameGpuTime frameGpuTime = new FrameGpuTime(); 47 | frameGpuTime.frameIdx = frameData.frameIndex; 48 | 49 | foreach ( var threadData in frameData.m_ThreadData){ 50 | AddGpuSampleByThread(threadData, frameGpuTime); 51 | } 52 | this.frameGpuTimes.Add(frameGpuTime); 53 | } 54 | 55 | private void AddGpuSampleByThread(ThreadData thread, FrameGpuTime frameGpuTime) 56 | { 57 | if( thread == null) { return; } 58 | if( thread.m_GPUTimeSamples == null) { return; } 59 | foreach( var gpuSample in thread.m_GPUTimeSamples) 60 | { 61 | frameGpuTime.AddGpuSample(gpuSample); 62 | } 63 | } 64 | 65 | 66 | /// 67 | /// 結果書き出し 68 | /// 69 | protected override string GetResultText() 70 | { 71 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 72 | csvStringGenerator.AppendColumn("frameIdx"); 73 | 74 | for (int i = 0; i < GPUTime.SECTION_NUM; ++i) 75 | { 76 | csvStringGenerator.AppendColumn(((GPUTime.GpuSection)i).ToString() + "(ms)"); 77 | } 78 | csvStringGenerator.AppendColumn("callNum"); 79 | for (int i = 0; i < GPUTime.SECTION_NUM; ++i) 80 | { 81 | csvStringGenerator.AppendColumn(((GPUTime.GpuSection)i).ToString() + "(calls)"); 82 | } 83 | 84 | csvStringGenerator.NextRow(); 85 | foreach( var gpuFrame in frameGpuTimes) 86 | { 87 | if (gpuFrame.gpuTimeByCategory == null) { continue; } 88 | csvStringGenerator.AppendColumn(gpuFrame.frameIdx); 89 | 90 | for( int i = 0; i < GPUTime.SECTION_NUM; ++i) 91 | { 92 | GpuTimeInfo val ; 93 | if(gpuFrame.gpuTimeByCategory.TryGetValue(i,out val)) 94 | { 95 | csvStringGenerator.AppendColumn( (float)val.time / 1000.0f); 96 | } 97 | else 98 | { 99 | csvStringGenerator.AppendColumn(0); 100 | } 101 | } 102 | csvStringGenerator.AppendColumn(""); 103 | 104 | for (int i = 0; i < GPUTime.SECTION_NUM; ++i) 105 | { 106 | GpuTimeInfo val; 107 | if (gpuFrame.gpuTimeByCategory.TryGetValue(i, out val)) 108 | { 109 | csvStringGenerator.AppendColumn(val.count); 110 | } 111 | else 112 | { 113 | csvStringGenerator.AppendColumn(0); 114 | } 115 | } 116 | 117 | csvStringGenerator.NextRow(); 118 | } 119 | return csvStringGenerator.ToString(); 120 | } 121 | 122 | 123 | protected override string FooterName 124 | { 125 | get 126 | { 127 | return "_gpu_sample.csv"; 128 | } 129 | } 130 | 131 | 132 | } 133 | 134 | } -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/GpuSampleToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 13b9f169806de6541af34024f978dd04 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/JitInfoAnalyzeToFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UTJ.ProfilerReader.BinaryData; 3 | 4 | using UTJ.ProfilerReader.BinaryData.Stats; 5 | 6 | namespace UTJ.ProfilerReader.Analyzer 7 | { 8 | 9 | public class JitInfoAnalyzeToFile : AnalyzeToTextbaseFileBase 10 | { 11 | private Dictionary jitInfoDict = new Dictionary(); 12 | 13 | 14 | public override void CollectData(ProfilerFrameData frameData) 15 | { 16 | if( frameData == null ) 17 | { 18 | return; 19 | } 20 | var jitInfos = frameData.m_jitInfos; 21 | if( jitInfos == null) 22 | { 23 | return; 24 | } 25 | foreach( var jitInfo in jitInfos) 26 | { 27 | if(jitInfo == null) { continue; } 28 | if(!jitInfoDict.ContainsKey(jitInfo.codeAddr)) 29 | { 30 | jitInfoDict.Add(jitInfo.codeAddr, jitInfo); 31 | } 32 | } 33 | 34 | } 35 | 36 | 37 | 38 | /// 39 | /// 結果書き出し 40 | /// 41 | protected override string GetResultText() 42 | { 43 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 44 | AppendHeaderToStringBuilder(csvStringGenerator); 45 | List sortedJitInfo = new List(this.jitInfoDict.Values); 46 | sortedJitInfo.Sort(new JitInfo.CompareByAddr() ); 47 | 48 | foreach( var jitInfo in sortedJitInfo) 49 | { 50 | string name = jitInfo.name; 51 | string sourceFileName = jitInfo.sourceFileName; 52 | csvStringGenerator.AppendColumnAsAddr(jitInfo.codeAddr); 53 | csvStringGenerator.AppendColumn(jitInfo.size); 54 | csvStringGenerator.AppendColumn(name); 55 | csvStringGenerator.AppendColumn(sourceFileName); 56 | csvStringGenerator.AppendColumn(jitInfo.sourceFileLine); 57 | csvStringGenerator.NextRow(); 58 | } 59 | 60 | return csvStringGenerator.ToString(); 61 | } 62 | 63 | 64 | private void AppendHeaderToStringBuilder(CsvStringGenerator csvStringGenerator) 65 | { 66 | csvStringGenerator.AppendColumn("Address"); 67 | // Total 68 | csvStringGenerator.AppendColumn("Code Size"); 69 | csvStringGenerator.AppendColumn("FunctionName"); 70 | csvStringGenerator.AppendColumn("SourceFile"); 71 | csvStringGenerator.AppendColumn("SourceLine"); 72 | csvStringGenerator.NextRow(); 73 | } 74 | 75 | 76 | protected override string FooterName 77 | { 78 | get 79 | { 80 | return "_jitInfos.csv"; 81 | } 82 | } 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/JitInfoAnalyzeToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e62bb9e4f2af06a4cb9bea7b4bbb7bd2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/MainThreadAnalyzeToFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using UTJ.ProfilerReader.BinaryData; 4 | 5 | 6 | namespace UTJ.ProfilerReader.Analyzer 7 | { 8 | public class MainThreadAnalyzeToFile : AnalyzeToTextbaseFileBase 9 | { 10 | private class SampleData 11 | { 12 | public string fullName; 13 | public string sampleName; 14 | 15 | public float totalSelfMsec = 0.0f; 16 | public float selfMinMSec = float.MaxValue; 17 | public float selfMaxMsec = 0.0f; 18 | 19 | public float totalExecMsec = 0.0f; 20 | public float execMinMSec = float.MaxValue; 21 | public float execMaxMsec = 0.0f; 22 | 23 | public int callNum = 0; 24 | 25 | public string categoryName; 26 | 27 | public SampleData(string fname, string name,string category) 28 | { 29 | this.sampleName = name; 30 | this.fullName = fname; 31 | this.categoryName = category; 32 | } 33 | 34 | public void Called(float selfMsec, float execMsec) 35 | { 36 | 37 | selfMinMSec = ProfilerLogUtil.Min(selfMinMSec, selfMsec); 38 | selfMaxMsec = ProfilerLogUtil.Max(selfMaxMsec, selfMsec); 39 | totalSelfMsec += selfMsec; 40 | 41 | execMinMSec = ProfilerLogUtil.Min(execMinMSec, execMsec); 42 | execMaxMsec = ProfilerLogUtil.Max(execMaxMsec, execMsec); 43 | totalExecMsec += execMsec; 44 | ++callNum; 45 | } 46 | } 47 | private Dictionary samples = new Dictionary(); 48 | private int frameNum = 0; 49 | 50 | 51 | public override void CollectData(ProfilerFrameData frameData) 52 | { 53 | // 特別枠で frameDataのCPU時間を追加 54 | // 同一フレーム内に同じスレッド名が複数できるので… 55 | Dictionary threadNameCounter = new Dictionary(8); 56 | foreach (var thread in frameData.m_ThreadData) 57 | { 58 | if (thread.IsMainThread) 59 | { 60 | CollectThread(frameData,thread); 61 | } 62 | } 63 | ++frameNum; 64 | } 65 | 66 | private void CollectThread(ProfilerFrameData frameData,ThreadData thread) 67 | { 68 | if (thread == null || thread.m_AllSamples == null) { return; } 69 | foreach (var sample in thread.m_AllSamples) 70 | { 71 | if (sample.parent == null) 72 | { 73 | CollectFromNamedChildren(frameData,sample); 74 | } 75 | } 76 | } 77 | 78 | private void CollectFromNamedChildren(ProfilerFrameData frameData,ProfilerSample sample) 79 | { 80 | if (!string.IsNullOrEmpty(sample.sampleName)) 81 | { 82 | string category = ProtocolData.GetCategory(frameData,unityVersion, sample.group); 83 | AddSampleData(sample.fullSampleName, sample.sampleName, category, sample.selfTimeUs / 1000.0f, sample.timeUS / 1000.0f); 84 | } 85 | if (sample.children != null) 86 | { 87 | foreach (var child in sample.children) 88 | { 89 | CollectFromNamedChildren(frameData,child); 90 | } 91 | } 92 | return; 93 | } 94 | 95 | private void AddSampleData(string fullName, string sampleName,string categoryName, float selfMsec, float execMsec) 96 | { 97 | 98 | SampleData sampleData = null; 99 | if (selfMsec < 0.0f) 100 | { 101 | ProfilerLogUtil.logErrorString("minus Param " + sampleName + ":" + selfMsec + ":" + execMsec); 102 | return; 103 | } 104 | if (selfMsec > 1000.0f * 50.0f) 105 | { 106 | ProfilerLogUtil.logErrorString("minus Param " + sampleName + ":" + selfMsec + ":" + execMsec); 107 | return; 108 | } 109 | 110 | if (!this.samples.TryGetValue(fullName, out sampleData)) 111 | { 112 | sampleData = new SampleData(fullName, sampleName,categoryName); 113 | this.samples.Add(fullName, sampleData); 114 | } 115 | sampleData.Called(selfMsec, execMsec); 116 | } 117 | 118 | /// 119 | /// 結果書き出し 120 | /// 121 | protected override string GetResultText() 122 | { 123 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 124 | csvStringGenerator.AppendColumn("name").AppendColumn("fullname").AppendColumn("category").AppendColumn("callNum"). 125 | AppendColumn("self").AppendColumn("sum(msec)").AppendColumn("perFrame(msec)").AppendColumn("min(msec)").AppendColumn("max(msec)"). 126 | AppendColumn("total").AppendColumn("sum(msec)").AppendColumn("perFrame(msec)").AppendColumn("min(msec)").AppendColumn("max(msec)"). 127 | NextRow(); 128 | var sampleDataList = new List(samples.Values); 129 | sampleDataList.Sort((a, b) => 130 | { 131 | if (a.totalSelfMsec > b.totalSelfMsec) 132 | { 133 | return -1; 134 | }else if (a.totalSelfMsec < b.totalSelfMsec) 135 | { 136 | return 1; 137 | } 138 | return 0; 139 | }); 140 | foreach (var sampleData in sampleDataList) 141 | { 142 | csvStringGenerator.AppendColumn(sampleData.sampleName). 143 | AppendColumn(sampleData.fullName). 144 | AppendColumn(sampleData.categoryName). 145 | AppendColumn(sampleData.callNum).AppendColumn(""); 146 | 147 | csvStringGenerator.AppendColumn(sampleData.totalSelfMsec). 148 | AppendColumn(sampleData.totalSelfMsec / frameNum). 149 | AppendColumn(sampleData.selfMinMSec). 150 | AppendColumn(sampleData.selfMaxMsec).AppendColumn(""); 151 | 152 | 153 | csvStringGenerator.AppendColumn(sampleData.totalExecMsec). 154 | AppendColumn(sampleData.totalExecMsec / frameNum). 155 | AppendColumn(sampleData.execMinMSec). 156 | AppendColumn(sampleData.execMaxMsec); 157 | csvStringGenerator.NextRow(); 158 | } 159 | 160 | return csvStringGenerator.ToString(); 161 | } 162 | protected override string FooterName 163 | { 164 | get 165 | { 166 | return "_main_self.csv"; 167 | } 168 | } 169 | 170 | } 171 | } -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/MainThreadAnalyzeToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 180ee3acbe5f93944998f208ebc9db00 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/MainThreadCategoryAnalyzeToFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UTJ.ProfilerReader.BinaryData; 4 | 5 | 6 | namespace UTJ.ProfilerReader.Analyzer 7 | { 8 | public class MainThreadCategoryAnalyzeToFile : AnalyzeToTextbaseFileBase 9 | { 10 | private class FrameByCategory 11 | { 12 | public Dictionary frameData; 13 | public int frameIdx; 14 | 15 | public void AddData(string category,float msec) 16 | { 17 | if(frameData == null) 18 | { 19 | frameData = new Dictionary(); 20 | } 21 | float val; 22 | if( frameData.TryGetValue(category,out val) ){ 23 | frameData[category] = val + msec; 24 | } 25 | else 26 | { 27 | frameData.Add( category , msec ); 28 | } 29 | } 30 | 31 | public void AppendCsv(CsvStringGenerator csvStringGenerator, List categoriesStr) 32 | { 33 | foreach (var category in categoriesStr) 34 | { 35 | float val; 36 | 37 | if (!frameData.TryGetValue(category, out val)) 38 | { 39 | val = 0.0f; 40 | } 41 | csvStringGenerator.AppendColumn(val); 42 | } 43 | } 44 | } 45 | 46 | private Dictionary categoryDictionary; 47 | private List categoriesStr; 48 | private List frames = new List(); 49 | 50 | private void CollectThread(ThreadData thread,FrameByCategory frameByCategory) 51 | { 52 | if (thread.m_AllSamples == null) { return; } 53 | foreach (var sample in thread.m_AllSamples) 54 | { 55 | string category = null; 56 | if(categoryDictionary.TryGetValue(sample.group,out category) ){ 57 | frameByCategory.AddData(categoriesStr[sample.group], sample.selfTimeUs * 0.001f); 58 | } 59 | } 60 | } 61 | public override void CollectData(ProfilerFrameData frameData) 62 | { 63 | // Categoryのセットアップ 64 | SetupCategories(frameData); 65 | FrameByCategory frameByCategory = new FrameByCategory(); 66 | frameByCategory.frameIdx = frameData.frameIndex; 67 | // 特別枠で frameDataのCPU時間を追加 68 | // 同一フレーム内に同じスレッド名が複数できるので… 69 | Dictionary threadNameCounter = new Dictionary(8); 70 | foreach (var thread in frameData.m_ThreadData) 71 | { 72 | if (thread.IsMainThread) 73 | { 74 | CollectThread(thread, frameByCategory); 75 | } 76 | } 77 | this.frames.Add(frameByCategory); 78 | } 79 | private void SetupCategories(ProfilerFrameData frameData) 80 | { 81 | if(this.categoryDictionary != null) 82 | { 83 | return; 84 | } 85 | this.categoriesStr = new List(); 86 | this.categoryDictionary = new Dictionary(); 87 | var categories = ProtocolData.GetCategories(frameData, this.unityVersion); 88 | foreach( var item in categories) 89 | { 90 | string name = item.Value.name; 91 | int idx = (int)item.Value.categoryId; 92 | if (!categoryDictionary.ContainsKey(idx)) 93 | { 94 | categoriesStr.Add(name); 95 | categoryDictionary.Add(idx,name); 96 | } 97 | } 98 | } 99 | /// 100 | /// 結果書き出し 101 | /// 102 | protected override string GetResultText() 103 | { 104 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 105 | csvStringGenerator.AppendColumn("frameIdx"); 106 | foreach( var str in categoriesStr) 107 | { 108 | csvStringGenerator.AppendColumn(str+"(msec)"); 109 | } 110 | csvStringGenerator.NextRow(); 111 | 112 | foreach( var frame in frames) 113 | { 114 | csvStringGenerator.AppendColumn(frame.frameIdx); 115 | frame.AppendCsv(csvStringGenerator,this.categoriesStr); 116 | csvStringGenerator.NextRow(); 117 | } 118 | 119 | return csvStringGenerator.ToString(); 120 | } 121 | 122 | protected override string FooterName 123 | { 124 | get 125 | { 126 | return "_category_mainThread_frame.csv"; 127 | } 128 | } 129 | 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/MainThreadCategoryAnalyzeToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b29edfc38841eb247a4efcfbb71c9123 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/MemoryAnalyzeToFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UTJ.ProfilerReader.BinaryData; 3 | 4 | using UTJ.ProfilerReader.BinaryData.Stats; 5 | 6 | namespace UTJ.ProfilerReader.Analyzer 7 | { 8 | 9 | public class MemoryAnalyzeToFile : AnalyzeToTextbaseFileBase 10 | { 11 | private List frameIdxList = new List(); 12 | private List memoryStatsList = new List(); 13 | 14 | public override void CollectData(ProfilerFrameData frameData) 15 | { 16 | if( frameData == null || frameData.allStats == null || frameData.allStats.memoryStats == null) 17 | { 18 | return; 19 | } 20 | frameIdxList.Add(frameData.frameIndex); 21 | memoryStatsList.Add( frameData.allStats.memoryStats ); 22 | } 23 | 24 | /// 25 | /// 結果書き出し 26 | /// 27 | protected override string GetResultText() 28 | { 29 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 30 | 31 | AppendHeaderToStringBuilder(csvStringGenerator); 32 | 33 | for ( int i = 0; i < memoryStatsList.Count; ++i) 34 | { 35 | MemoryStats memoryStats = memoryStatsList[i]; 36 | csvStringGenerator.AppendColumn(frameIdxList[i]); 37 | csvStringGenerator.AppendColumn(""); 38 | // Used Memory 39 | csvStringGenerator.AppendColumn(memoryStats.bytesUsedTotal); 40 | csvStringGenerator.AppendColumn(memoryStats.bytesUsedUnity); 41 | csvStringGenerator.AppendColumn(memoryStats.bytesUsedMono); 42 | csvStringGenerator.AppendColumn(memoryStats.bytesUsedGFX); 43 | csvStringGenerator.AppendColumn(memoryStats.bytesUsedFMOD); 44 | csvStringGenerator.AppendColumn(memoryStats.bytesUsedVideo); 45 | csvStringGenerator.AppendColumn(memoryStats.bytesUsedProfiler); 46 | csvStringGenerator.AppendColumn(""); 47 | 48 | // Reserved Memory 49 | csvStringGenerator.AppendColumn(memoryStats.bytesReservedTotal); 50 | csvStringGenerator.AppendColumn(memoryStats.bytesReservedUnity); 51 | csvStringGenerator.AppendColumn(memoryStats.bytesReservedMono); 52 | csvStringGenerator.AppendColumn(memoryStats.bytesReservedFMOD); 53 | csvStringGenerator.AppendColumn(memoryStats.bytesReservedVideo); 54 | csvStringGenerator.AppendColumn(memoryStats.bytesReservedProfiler); 55 | csvStringGenerator.AppendColumn(""); 56 | 57 | // by Assets 58 | csvStringGenerator.AppendColumn(memoryStats.textureCount); 59 | csvStringGenerator.AppendColumn(memoryStats.textureBytes); 60 | csvStringGenerator.AppendColumn(memoryStats.meshCount); 61 | csvStringGenerator.AppendColumn(memoryStats.meshBytes); 62 | csvStringGenerator.AppendColumn(memoryStats.materialCount); 63 | csvStringGenerator.AppendColumn(memoryStats.materialBytes); 64 | csvStringGenerator.AppendColumn(memoryStats.audioCount); 65 | csvStringGenerator.AppendColumn(memoryStats.audioBytes); 66 | csvStringGenerator.AppendColumn(memoryStats.assetCount); 67 | csvStringGenerator.AppendColumn(memoryStats.gameObjectCount); 68 | csvStringGenerator.AppendColumn(memoryStats.sceneObjectCount); 69 | csvStringGenerator.AppendColumn(memoryStats.totalObjectsCount); 70 | csvStringGenerator.AppendColumn(""); 71 | 72 | // GC 73 | csvStringGenerator.AppendColumn(memoryStats.frameGCAllocCount); 74 | csvStringGenerator.AppendColumn(memoryStats.frameGCAllocBytes); 75 | csvStringGenerator.NextRow(); 76 | } 77 | 78 | return csvStringGenerator.ToString(); 79 | } 80 | private void AppendHeaderToStringBuilder(CsvStringGenerator csvStringGenerator) 81 | { 82 | csvStringGenerator.AppendColumn("frameIdx"); 83 | csvStringGenerator.AppendColumn("UsedMemory"); 84 | // Used Memory 85 | csvStringGenerator.AppendColumn("bytesUsedTotal"); 86 | csvStringGenerator.AppendColumn("bytesUsedUnity"); 87 | csvStringGenerator.AppendColumn("bytesUsedMono"); 88 | csvStringGenerator.AppendColumn("bytesUsedGFX"); 89 | csvStringGenerator.AppendColumn("bytesUsedFMOD"); 90 | csvStringGenerator.AppendColumn("bytesUsedVideo"); 91 | csvStringGenerator.AppendColumn("bytesUsedProfiler"); 92 | 93 | csvStringGenerator.AppendColumn("ReservedMemory"); 94 | // Reserved Memory 95 | csvStringGenerator.AppendColumn("bytesReservedTotal"); 96 | csvStringGenerator.AppendColumn("bytesReservedUnity"); 97 | csvStringGenerator.AppendColumn("bytesReservedMono"); 98 | csvStringGenerator.AppendColumn("bytesReservedFMOD"); 99 | csvStringGenerator.AppendColumn("bytesReservedVideo"); 100 | csvStringGenerator.AppendColumn("bytesReservedProfiler"); 101 | 102 | csvStringGenerator.AppendColumn("AssetUsage"); 103 | // by Assets 104 | csvStringGenerator.AppendColumn("textureCount"); 105 | csvStringGenerator.AppendColumn("textureBytes"); 106 | csvStringGenerator.AppendColumn("meshCount"); 107 | csvStringGenerator.AppendColumn("meshBytes"); 108 | csvStringGenerator.AppendColumn("meshCount"); 109 | csvStringGenerator.AppendColumn("meshBytes"); 110 | csvStringGenerator.AppendColumn("materialCount"); 111 | csvStringGenerator.AppendColumn("materialBytes"); 112 | csvStringGenerator.AppendColumn("audioCount"); 113 | csvStringGenerator.AppendColumn("audioBytes"); 114 | csvStringGenerator.AppendColumn("assetCount"); 115 | csvStringGenerator.AppendColumn("gameObjectCount"); 116 | csvStringGenerator.AppendColumn("sceneObjectCount"); 117 | csvStringGenerator.AppendColumn("totalObjectsCount"); 118 | csvStringGenerator.AppendColumn("GC"); 119 | 120 | // GC 121 | csvStringGenerator.AppendColumn("frameGCAllocCount"); 122 | csvStringGenerator.AppendColumn("frameGCAllocBytes"); 123 | csvStringGenerator.NextRow(); 124 | 125 | } 126 | 127 | protected override string FooterName 128 | { 129 | get 130 | { 131 | return "_memory.csv"; 132 | } 133 | } 134 | 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/MemoryAnalyzeToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 615d3f2b68411c54d9571d4b44071800 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/RenderThreadToFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UTJ.ProfilerReader.BinaryData; 3 | using System.Text; 4 | 5 | 6 | namespace UTJ.ProfilerReader.Analyzer 7 | { 8 | public class RenderThreadToFile : AnalyzeToTextbaseFileBase 9 | { 10 | 11 | private struct CameraRenderData 12 | { 13 | public float renderTime; 14 | public float updateDepth; 15 | public float opaque; 16 | public float transparent; 17 | public float imageEffect; 18 | 19 | 20 | public void AppendToStringBuilder(CsvStringGenerator csvStringGenerator) 21 | { 22 | csvStringGenerator.AppendColumn(renderTime); 23 | csvStringGenerator.AppendColumn(updateDepth); 24 | csvStringGenerator.AppendColumn(opaque); 25 | csvStringGenerator.AppendColumn(transparent); 26 | csvStringGenerator.AppendColumn(imageEffect); 27 | } 28 | } 29 | 30 | 31 | private class FrameRenderingData 32 | { 33 | public int frameIdx; 34 | public float processCommandsTime; 35 | public float waitForCommandsTime; 36 | 37 | public float scheduleGeometryJobTime; 38 | public int scheduleGeometryJobNum; 39 | public float presentFrameTime; 40 | public float guiRepaint; 41 | 42 | public List cameraRenders = new List(8); 43 | 44 | public void AppendToCsvGenerator(CsvStringGenerator csvStringGenerator) 45 | { 46 | csvStringGenerator.AppendColumn(frameIdx); 47 | csvStringGenerator.AppendColumn(processCommandsTime); 48 | csvStringGenerator.AppendColumn(waitForCommandsTime); 49 | csvStringGenerator.AppendColumn(scheduleGeometryJobTime); 50 | csvStringGenerator.AppendColumn(scheduleGeometryJobNum); 51 | csvStringGenerator.AppendColumn(presentFrameTime); 52 | csvStringGenerator.AppendColumn(guiRepaint); 53 | csvStringGenerator.AppendColumn( cameraRenders.Count); 54 | 55 | foreach (var cameraRender in cameraRenders) 56 | { 57 | csvStringGenerator.AppendColumn(""); 58 | cameraRender.AppendToStringBuilder(csvStringGenerator); 59 | } 60 | } 61 | } 62 | 63 | private List frameRenderingDatas = new List(512); 64 | private int maxCameraNum = 0; 65 | 66 | 67 | public override void CollectData(ProfilerFrameData frameData) 68 | { 69 | foreach( var threadData in frameData.m_ThreadData){ 70 | if(threadData.m_ThreadName == "Render Thread") 71 | { 72 | CollectRenderThreadData(frameData.frameIndex, threadData); 73 | } 74 | } 75 | } 76 | 77 | private void CollectRenderThreadData(int frameIdx,ThreadData threadData) 78 | { 79 | if(threadData.m_AllSamples == null) { return; } 80 | FrameRenderingData frameRenderingData = new FrameRenderingData(); 81 | frameRenderingData.frameIdx = frameIdx; 82 | frameRenderingData.processCommandsTime = threadData.m_AllSamples[0].timeUS / 1000.0f; 83 | 84 | int cameraNum = 0; 85 | foreach ( var sample in threadData.m_AllSamples) 86 | { 87 | switch(sample.sampleName ){ 88 | case "Camera.Render": 89 | { 90 | var cameraRender = CollectCameraRenderData(sample); 91 | frameRenderingData.cameraRenders.Add(cameraRender); 92 | ++cameraNum; 93 | } 94 | break; 95 | case "Gfx.WaitForCommands": 96 | frameRenderingData.waitForCommandsTime += sample.timeUS / 1000.0f; 97 | break; 98 | case "Gfx.PresentFrame": 99 | frameRenderingData.presentFrameTime += sample.timeUS / 1000.0f; 100 | break; 101 | case "ScheduleGeometryJobs": 102 | frameRenderingData.scheduleGeometryJobTime += sample.timeUS / 1000.0f; 103 | frameRenderingData.scheduleGeometryJobNum += 1; 104 | break; 105 | case "GUIRepaint": 106 | frameRenderingData.guiRepaint += sample.timeUS / 1000.0f; 107 | break; 108 | } 109 | } 110 | if(maxCameraNum < cameraNum) 111 | { 112 | maxCameraNum = cameraNum; 113 | } 114 | frameRenderingDatas.Add(frameRenderingData); 115 | } 116 | 117 | private CameraRenderData CollectCameraRenderData( ProfilerSample profilerSample) 118 | { 119 | CameraRenderData renderData = new CameraRenderData(); 120 | renderData.renderTime = profilerSample.timeUS / 1000.0f; 121 | VisitChildren(profilerSample, (sample) => { 122 | switch(sample.sampleName){ 123 | case "UpdateDepthTexture": 124 | renderData.updateDepth = sample.timeUS / 1000.0f; 125 | return true; 126 | case "Render.OpaqueGeometry": 127 | renderData.opaque = sample.timeUS / 1000.0f; 128 | return true; 129 | case "Render.TransparentGeometry": 130 | renderData.transparent = sample.timeUS / 1000.0f; 131 | return true; 132 | case "Camera.ImageEffects": 133 | renderData.imageEffect = sample.timeUS / 1000.0f; 134 | return true; 135 | } 136 | return false; 137 | }); 138 | return renderData; 139 | } 140 | 141 | private List FindChildren(ProfilerSample profilerSample,string name) 142 | { 143 | List results = new List(); 144 | 145 | return results; 146 | } 147 | private void VisitChildren(ProfilerSample sample, 148 | System.Func filter) 149 | { 150 | if( sample == null) { return; } 151 | if( filter(sample)) { 152 | return; 153 | } 154 | if( sample.children == null) 155 | { 156 | return; 157 | } 158 | foreach( var child in sample.children) 159 | { 160 | VisitChildren(child, filter); 161 | } 162 | } 163 | 164 | /// 165 | /// 結果書き出し 166 | /// 167 | protected override string GetResultText() 168 | { 169 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 170 | csvStringGenerator.AppendColumn("frameIdx").AppendColumn("processCommandsTime"). 171 | AppendColumn("waitForCommandsTime").AppendColumn("scheduleGeometryJobTime").AppendColumn("scheduleGeometryJobNum").AppendColumn("presentFrameTime").AppendColumn("guiRepaint").AppendColumn("cameraRenders"); 172 | 173 | for (int i = 0; i < maxCameraNum; ++i) 174 | { 175 | csvStringGenerator.AppendColumn("Camera"+i); 176 | csvStringGenerator.AppendColumn("renderTime").AppendColumn("updateDepth").AppendColumn("opaque").AppendColumn("transparent").AppendColumn("imageEffect"); 177 | } 178 | csvStringGenerator.NextRow(); 179 | 180 | foreach (var frameRenderingData in this.frameRenderingDatas) 181 | { 182 | frameRenderingData.AppendToCsvGenerator(csvStringGenerator); 183 | csvStringGenerator.NextRow(); 184 | } 185 | 186 | return csvStringGenerator.ToString(); 187 | } 188 | 189 | 190 | protected override string FooterName 191 | { 192 | get 193 | { 194 | return "_renderthread.csv"; 195 | } 196 | } 197 | 198 | 199 | } 200 | 201 | } -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/RenderThreadToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ea57c279a8d206a4484872add86970c8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/RenderingAnalyzeToFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UTJ.ProfilerReader.BinaryData; 3 | 4 | using UTJ.ProfilerReader.BinaryData.Stats; 5 | 6 | namespace UTJ.ProfilerReader.Analyzer 7 | { 8 | 9 | public class RenderingAnalyzeToFile : AnalyzeToTextbaseFileBase 10 | { 11 | 12 | const string FrameWholeDataSpecialKey = "CPUTotal"; 13 | 14 | private List frameIdxList = new List(); 15 | private List drawStatsList = new List(); 16 | 17 | public override void CollectData(ProfilerFrameData frameData) 18 | { 19 | if( frameData == null || frameData.allStats == null || frameData.allStats.drawStats == null) 20 | { 21 | return; 22 | } 23 | frameIdxList.Add(frameData.frameIndex); 24 | drawStatsList.Add(frameData.allStats.drawStats); 25 | } 26 | 27 | /// 28 | /// 結果書き出し 29 | /// 30 | protected override string GetResultText() 31 | { 32 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 33 | AppendHeaderToStringBuilder(csvStringGenerator); 34 | 35 | for (int i = 0; i < drawStatsList.Count; ++i) 36 | { 37 | DrawStats drawStats = drawStatsList[i]; 38 | csvStringGenerator.AppendColumn(frameIdxList[i]); 39 | 40 | csvStringGenerator.AppendColumn(""); 41 | // Total 42 | csvStringGenerator.AppendColumn(drawStats.setPassCalls); 43 | csvStringGenerator.AppendColumn(drawStats.drawCalls); 44 | csvStringGenerator.AppendColumn(drawStats.batches); 45 | csvStringGenerator.AppendColumn(drawStats.triangles); 46 | csvStringGenerator.AppendColumn(drawStats.vertices); 47 | csvStringGenerator.AppendColumn(""); 48 | // DynamicBatching 49 | csvStringGenerator.AppendColumn(drawStats.dynamicBatchedDrawCalls); 50 | csvStringGenerator.AppendColumn(drawStats.dynamicBatchedTriangles); 51 | csvStringGenerator.AppendColumn(drawStats.dynamicBatchedTriangles); 52 | csvStringGenerator.AppendColumn(drawStats.dynamicBatchedVertices); 53 | csvStringGenerator.AppendColumn(""); 54 | // static batching 55 | csvStringGenerator.AppendColumn(drawStats.staticBatchedDrawCalls); 56 | csvStringGenerator.AppendColumn(drawStats.staticBatchedTriangles); 57 | csvStringGenerator.AppendColumn(drawStats.staticBatchedTriangles); 58 | csvStringGenerator.AppendColumn(drawStats.staticBatchedVertices); 59 | // instancing 60 | csvStringGenerator.AppendColumn(""); 61 | csvStringGenerator.AppendColumn(drawStats.hasInstancing); 62 | csvStringGenerator.AppendColumn(drawStats.instancedBatchedDrawCalls); 63 | csvStringGenerator.AppendColumn(drawStats.instancedBatches); 64 | csvStringGenerator.AppendColumn(drawStats.instancedTriangles); 65 | csvStringGenerator.AppendColumn(drawStats.instancedVertices); 66 | //screen Info 67 | csvStringGenerator.AppendColumn(""); 68 | csvStringGenerator.AppendColumn(drawStats.screenWidth); 69 | csvStringGenerator.AppendColumn(drawStats.screenHeight); 70 | csvStringGenerator.AppendColumn(drawStats.screenBytes); 71 | // RenderTexture Info 72 | csvStringGenerator.AppendColumn(""); 73 | csvStringGenerator.AppendColumn(drawStats.renderTextureCount); 74 | csvStringGenerator.AppendColumn(drawStats.renderTextureBytes); 75 | csvStringGenerator.AppendColumn(drawStats.renderTextureStateChanges); 76 | // SkinnedMesh 77 | csvStringGenerator.AppendColumn(""); 78 | csvStringGenerator.AppendColumn(drawStats.visibleSkinnedMeshes); 79 | // etc... 80 | csvStringGenerator.AppendColumn(""); 81 | csvStringGenerator.AppendColumn(drawStats.totalAvailableVRamMBytes); 82 | csvStringGenerator.AppendColumn(drawStats.vboTotal); 83 | csvStringGenerator.AppendColumn(drawStats.vboUploads); 84 | csvStringGenerator.AppendColumn(drawStats.ibUploads); 85 | csvStringGenerator.AppendColumn(drawStats.shadowCasters); 86 | csvStringGenerator.NextRow(); 87 | } 88 | 89 | return csvStringGenerator.ToString(); 90 | } 91 | private void AppendHeaderToStringBuilder(CsvStringGenerator csvStringGenerator) 92 | { 93 | csvStringGenerator.AppendColumn("frameIdx"); 94 | // Total 95 | csvStringGenerator.AppendColumn("Total"); 96 | csvStringGenerator.AppendColumn("setPassCalls"); 97 | csvStringGenerator.AppendColumn("drawCalls"); 98 | csvStringGenerator.AppendColumn("batches"); 99 | csvStringGenerator.AppendColumn("triangles"); 100 | csvStringGenerator.AppendColumn("vertices"); 101 | // DynamicBatching 102 | csvStringGenerator.AppendColumn("DynamicBatching"); 103 | csvStringGenerator.AppendColumn("dynamicBatchedDrawCalls"); 104 | csvStringGenerator.AppendColumn("dynamicBatchedTriangles"); 105 | csvStringGenerator.AppendColumn("dynamicBatchedTriangles"); 106 | csvStringGenerator.AppendColumn("dynamicBatchedVertices"); 107 | // static batching 108 | csvStringGenerator.AppendColumn("StaticBatching"); 109 | csvStringGenerator.AppendColumn("staticBatchedDrawCalls"); 110 | csvStringGenerator.AppendColumn("staticBatchedTriangles"); 111 | csvStringGenerator.AppendColumn("staticBatchedTriangles"); 112 | csvStringGenerator.AppendColumn("staticBatchedVertices"); 113 | // instancing 114 | csvStringGenerator.AppendColumn("Instancing"); 115 | csvStringGenerator.AppendColumn("hasInstancing"); 116 | csvStringGenerator.AppendColumn("instancedBatchedDrawCalls"); 117 | csvStringGenerator.AppendColumn("instancedBatches"); 118 | csvStringGenerator.AppendColumn("instancedTriangles"); 119 | csvStringGenerator.AppendColumn("instancedVertices"); 120 | //screen Info 121 | csvStringGenerator.AppendColumn("ScreenInfo"); 122 | csvStringGenerator.AppendColumn("screenWidth"); 123 | csvStringGenerator.AppendColumn("screenHeight"); 124 | csvStringGenerator.AppendColumn("screenBytes"); 125 | // RenderTexture Info 126 | csvStringGenerator.AppendColumn("RenderTextureInfo"); 127 | csvStringGenerator.AppendColumn("renderTextureCount"); 128 | csvStringGenerator.AppendColumn("renderTextureBytes"); 129 | csvStringGenerator.AppendColumn("renderTextureStateChanges"); 130 | // SkinnedMesh 131 | csvStringGenerator.AppendColumn("SkinnedMesh"); 132 | csvStringGenerator.AppendColumn("visibleSkinnedMeshes"); 133 | // etc... 134 | csvStringGenerator.AppendColumn("etc"); 135 | csvStringGenerator.AppendColumn("totalAvailableVRamMBytes"); 136 | csvStringGenerator.AppendColumn("vboTotal"); 137 | csvStringGenerator.AppendColumn("vboUploads"); 138 | csvStringGenerator.AppendColumn("ibUploads"); 139 | csvStringGenerator.AppendColumn("shadowCasters"); 140 | csvStringGenerator.NextRow(); 141 | 142 | } 143 | 144 | protected override string FooterName 145 | { 146 | get 147 | { 148 | return "_rendering.csv"; 149 | } 150 | } 151 | 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/RenderingAnalyzeToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 659f15cfe80d1a9479e0a497890dc806 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/ScreenshotToPng.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UTJ.ProfilerReader.BinaryData; 3 | using UTJ.ProfilerReader.RawData.Protocol; 4 | using System.Text; 5 | using System.IO; 6 | 7 | #if UNITY_EDITOR 8 | using UnityEngine; 9 | #endif 10 | 11 | namespace UTJ.ProfilerReader.Analyzer 12 | { 13 | public class ScreenShotToProfiler : IAnalyzeFileWriter 14 | { 15 | 16 | public static readonly System.Guid MetadataGuid = new System.Guid("4389DCEB-F9B3-4D49-940B-E98482F3A3F8"); 17 | public static readonly int InfoTag = -1; 18 | 19 | private string outputPath; 20 | private string logFile; 21 | private bool createDir = false; 22 | private StringBuilder stringBuilder = new StringBuilder(); 23 | 24 | public enum TextureCompress : byte 25 | { 26 | None = 0, 27 | RGB_565 = 1, 28 | PNG = 2, 29 | JPG_BufferRGB565 = 3, 30 | JPG_BufferRGBA = 4, 31 | } 32 | private class CaptureData 33 | { 34 | public int profilerFrameIndex; 35 | public int idx; 36 | public int width; 37 | public int height; 38 | public int originWidth; 39 | public int originHeight; 40 | public TextureCompress compress; 41 | 42 | public CaptureData(int frameIdx ,byte[] data) 43 | { 44 | this.profilerFrameIndex = frameIdx; 45 | this.idx = GetIntValue(data, 0); 46 | this.width = GetShortValue(data, 4); 47 | this.height = GetShortValue(data, 6); 48 | this.originWidth = GetShortValue(data, 8); 49 | this.originHeight = GetShortValue(data, 10); 50 | 51 | if (data.Length > 12) 52 | { 53 | this.compress = (ScreenShotToProfiler.TextureCompress)data[12]; 54 | } 55 | else 56 | { 57 | this.compress = ScreenShotToProfiler.TextureCompress.None; 58 | } 59 | 60 | } 61 | 62 | public static int GetIntValue(byte[] bin, int offset) 63 | { 64 | return (bin[offset + 0] << 0) + 65 | (bin[offset + 1] << 8) + 66 | (bin[offset + 2] << 16) + 67 | (bin[offset + 3] << 24); 68 | } 69 | public static int GetShortValue(byte[] bin, int offset) 70 | { 71 | return (bin[offset + 0] << 0) + 72 | (bin[offset + 1] << 8); 73 | } 74 | } 75 | private Dictionary captureFrameData = new Dictionary(); 76 | 77 | 78 | public void CollectData(ProfilerFrameData frameData) 79 | { 80 | foreach( var thread in frameData.m_ThreadData) 81 | { 82 | if (thread.IsMainThread) 83 | { 84 | ExecuteThreadData(frameData.frameIndex,thread); 85 | } 86 | } 87 | } 88 | private void ExecuteThreadData(int frameIdx,ThreadData thread) 89 | { 90 | if(thread == null || thread.m_AllSamples == null) { return; } 91 | foreach( var sample in thread.m_AllSamples) 92 | { 93 | if( sample == null || sample.sampleName != RawDataDefines.EmitFramemetataSample) 94 | { 95 | continue; 96 | } 97 | if( sample.metaDatas == null || 98 | sample.metaDatas.metadatas == null ) { 99 | continue; 100 | } 101 | ExecuteFrameMetadata(frameIdx,sample); 102 | } 103 | } 104 | 105 | private void ExecuteFrameMetadata(int frameIdx,ProfilerSample sample) 106 | { 107 | var metadatas = sample.metaDatas.metadatas; 108 | if (metadatas.Count < 2) 109 | { 110 | return; 111 | } 112 | var guidBin = metadatas[0].convertedObject as byte[]; 113 | var tagId = (int)metadatas[1].convertedObject; 114 | var valueBin = metadatas[2].convertedObject as byte[]; 115 | if (guidBin == null || valueBin == null) 116 | { 117 | return; 118 | } 119 | System.Guid guid = new System.Guid(guidBin); 120 | 121 | CaptureData captureData = null; 122 | if (guid != MetadataGuid) 123 | { 124 | return; 125 | } 126 | if (tagId == InfoTag) 127 | { 128 | captureData = new CaptureData(frameIdx,valueBin); 129 | this.captureFrameData.Add(captureData.idx, captureData); 130 | return; 131 | } 132 | if( this.captureFrameData.TryGetValue(tagId,out captureData)){ 133 | ExecuteBinData(captureData, valueBin); 134 | } 135 | } 136 | 137 | private void InitDirectory() 138 | { 139 | if (!createDir) 140 | { 141 | if (!Directory.Exists(this.outputPath)) 142 | { 143 | Directory.CreateDirectory(this.outputPath); 144 | } 145 | createDir = true; 146 | } 147 | } 148 | 149 | // execute data 150 | private void ExecuteBinData(CaptureData captureData,byte[] binData) 151 | { 152 | this.InitDirectory(); 153 | 154 | string file = GetFilePath(captureData); 155 | byte[] pngBin = GetImageBin(captureData,binData); 156 | if ( pngBin != null) 157 | { 158 | File.WriteAllBytes(file, pngBin); 159 | } 160 | } 161 | private byte[] GetImageBin(CaptureData captureData,byte[] binData) 162 | { 163 | if(binData == null) { return null; } 164 | switch (captureData.compress) 165 | { 166 | #if UNITY_EDITOR 167 | case TextureCompress.None: 168 | return ImageConversion.EncodeArrayToPNG(binData, 169 | UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8A8_SRGB, 170 | (uint)captureData.width, (uint)captureData.height); 171 | case TextureCompress.RGB_565: 172 | return ImageConversion.EncodeArrayToPNG(binData, 173 | UnityEngine.Experimental.Rendering.GraphicsFormat.R5G6B5_UNormPack16, 174 | (uint)captureData.width, (uint)captureData.height); 175 | #endif 176 | case TextureCompress.PNG: 177 | case TextureCompress.JPG_BufferRGB565: 178 | case TextureCompress.JPG_BufferRGBA: 179 | return binData; 180 | } 181 | return null; 182 | } 183 | private string GetFilePath(CaptureData captureData) 184 | { 185 | stringBuilder.Length = 0; 186 | stringBuilder.Append(this.outputPath).Append("/ss-"); 187 | stringBuilder.Append(string.Format("{0:D5}", captureData.idx)); 188 | switch (captureData.compress) 189 | { 190 | case TextureCompress.None: 191 | case TextureCompress.RGB_565: 192 | case TextureCompress.PNG: 193 | stringBuilder.Append(".png"); 194 | break; 195 | case TextureCompress.JPG_BufferRGB565: 196 | case TextureCompress.JPG_BufferRGBA: 197 | stringBuilder.Append(".jpg"); 198 | break; 199 | } 200 | return stringBuilder.ToString(); 201 | } 202 | 203 | 204 | public void SetFileInfo(string logfilename, string outputpath) 205 | { 206 | this.outputPath = Path.Combine(outputpath, "screenshots"); 207 | this.logFile = logfilename; 208 | } 209 | 210 | public void WriteResultFile(string logfilaneme, string outputpath) 211 | { 212 | } 213 | 214 | // nothing todo... 215 | public void SetInfo(ProfilerLogFormat logformat, string unityVersion, uint dataversion, ushort platform) 216 | { 217 | } 218 | 219 | } 220 | 221 | } -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/ScreenshotToPng.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 79348514f73120440b42f98f02a29bbf 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/ShaderCompileToFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UTJ.ProfilerReader.BinaryData; 3 | using System.Text; 4 | using UTJ.ProfilerReader.BinaryData.Thread; 5 | using UTJ.ProfilerReader.RawData.Protocol; 6 | 7 | namespace UTJ.ProfilerReader.Analyzer 8 | { 9 | public class ShaderCompileToFile : AnalyzeToTextbaseFileBase 10 | { 11 | private class ShaderCompileInfo 12 | { 13 | public int frameIdx = 0; 14 | public string shader; 15 | public float msec = 0.0f; 16 | public string pass; 17 | public string stage; 18 | public string keywords; 19 | public bool callFromWarmup = false; 20 | 21 | public ShaderCompileInfo() 22 | { 23 | this.shader = ""; 24 | this.pass = ""; 25 | this.stage = ""; 26 | this.keywords = ""; 27 | } 28 | } 29 | 30 | private List compileInfos = new List(); 31 | private bool hasPassStageKeywordsInfo = false; 32 | 33 | public override void CollectData(ProfilerFrameData frameData) 34 | { 35 | 36 | foreach ( var threadData in frameData.m_ThreadData){ 37 | CollectThreadData(frameData.frameIndex,threadData); 38 | } 39 | } 40 | 41 | private void CollectThreadData(int frameIdx,ThreadData thread) 42 | { 43 | if( thread == null) { return; } 44 | if( thread.m_AllSamples == null) { return; } 45 | 46 | foreach( var sample in thread.m_AllSamples) 47 | { 48 | if (sample.sampleName == "Shader.CreateGPUProgram") 49 | { 50 | AddShaderCompileSample(frameIdx, sample); 51 | } 52 | } 53 | } 54 | 55 | 56 | private void AddShaderCompileSample(int frameIdx,ProfilerSample sampleData) 57 | { 58 | var compileInfo = new ShaderCompileInfo(); 59 | compileInfo.frameIdx = frameIdx; 60 | compileInfo.msec = sampleData.timeUS / 1000.0f; 61 | compileInfo.callFromWarmup = IsCalledWarmup(sampleData.parent); 62 | if( sampleData.metaDatas != null ) 63 | { 64 | var metadatas = sampleData.metaDatas.metadatas; 65 | if( metadatas != null) 66 | { 67 | if (metadatas.Count > 0) 68 | { 69 | compileInfo.shader = metadatas[0].convertedObject as string; 70 | } 71 | if (metadatas.Count > 1) 72 | { 73 | compileInfo.pass = metadatas[1].convertedObject as string; 74 | this.hasPassStageKeywordsInfo = true; 75 | } 76 | 77 | if (metadatas.Count > 2) 78 | { 79 | compileInfo.stage = metadatas[2].convertedObject as string; 80 | } 81 | 82 | if (metadatas.Count > 3) 83 | { 84 | compileInfo.keywords = metadatas[3].convertedObject as string; 85 | } 86 | } 87 | } 88 | this.compileInfos.Add(compileInfo); 89 | } 90 | // 91 | private bool IsCalledWarmup(ProfilerSample sampleData) 92 | { 93 | for (var current = sampleData; current != null; current = current.parent) 94 | { 95 | if( current.sampleName == "ShaderVariantCollection.WarmupShaders" || 96 | current.sampleName == "Shader.WarmupAllShaders") 97 | { 98 | return true; 99 | } 100 | } 101 | // "ShaderVariantCollection.WarmupShaders" 102 | return false; 103 | } 104 | 105 | 106 | /// 107 | /// 結果書き出し 108 | /// 109 | protected override string GetResultText() 110 | { 111 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 112 | csvStringGenerator.AppendColumn("frameIdx"); 113 | csvStringGenerator.AppendColumn("Shader") 114 | .AppendColumn("exec(ms)").AppendColumn("isWarmupCall"); 115 | if (this.hasPassStageKeywordsInfo) 116 | { 117 | csvStringGenerator.AppendColumn("pass") 118 | .AppendColumn("stage") 119 | .AppendColumn("keyword"); 120 | } 121 | csvStringGenerator.NextRow(); 122 | foreach (var compileInfo in this.compileInfos) 123 | { 124 | if( compileInfo == null) { continue; } 125 | 126 | csvStringGenerator.AppendColumn(compileInfo.frameIdx) 127 | .AppendColumn(compileInfo.shader) 128 | .AppendColumn(compileInfo.msec) 129 | .AppendColumn(compileInfo.callFromWarmup); 130 | if (this.hasPassStageKeywordsInfo) { 131 | csvStringGenerator.AppendColumn(compileInfo.pass). 132 | AppendColumn(compileInfo.stage). 133 | AppendColumn(compileInfo.keywords); 134 | } 135 | csvStringGenerator.NextRow(); 136 | } 137 | 138 | return csvStringGenerator.ToString(); 139 | } 140 | 141 | 142 | protected override string FooterName 143 | { 144 | get 145 | { 146 | return "_shader_compile.csv"; 147 | } 148 | } 149 | 150 | 151 | } 152 | 153 | } -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/ShaderCompileToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 35071278dc1dc3c4eb59250e45d74b73 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/ThreadAnalyzeToFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UTJ.ProfilerReader.BinaryData; 3 | 4 | 5 | namespace UTJ.ProfilerReader.Analyzer 6 | { 7 | 8 | public class ThreadAnalyzeToFile : AnalyzeToTextbaseFileBase 9 | { 10 | private class ThreadViewData 11 | { 12 | public int maxFrame = 0; 13 | public string threadName; 14 | public Dictionary totalMsecList; 15 | public Dictionary idleMsecList; 16 | 17 | public Dictionary totalCount; 18 | public Dictionary idleCount; 19 | 20 | public ThreadViewData(string name) 21 | { 22 | this.threadName = name; 23 | this.totalMsecList = new Dictionary(512); 24 | this.idleMsecList = new Dictionary(512); 25 | idleCount = new Dictionary(512); 26 | totalCount = new Dictionary(512); 27 | } 28 | 29 | public void AddMsec(int frame, float total, float idle, int totalCnt, int idleCnt) 30 | { 31 | maxFrame = ProfilerLogUtil.Max(frame, maxFrame); 32 | if (!this.totalMsecList.ContainsKey(frame)) 33 | { 34 | 35 | this.totalMsecList.Add(frame, total); 36 | this.idleMsecList.Add(frame, idle); 37 | this.totalCount.Add(frame, totalCnt); 38 | this.idleCount.Add(frame, idleCnt); 39 | } 40 | else 41 | { 42 | ProfilerLogUtil.logErrorString("same frame " + threadName +"::" + frame); 43 | } 44 | } 45 | 46 | public void GetMSecData(int frame, out float total, out float idle, out int totalCnt, out int idleCnt) 47 | { 48 | total = 0.0f; 49 | idle = 0.0f; 50 | totalMsecList.TryGetValue(frame, out total); 51 | idleMsecList.TryGetValue(frame, out idle); 52 | totalCount.TryGetValue(frame, out totalCnt); 53 | idleCount.TryGetValue(frame, out idleCnt); 54 | } 55 | } 56 | private Dictionary viewData = new Dictionary(); 57 | 58 | const string FrameWholeDataSpecialKey = "CPUTotal"; 59 | 60 | public override void CollectData(ProfilerFrameData frameData) 61 | { 62 | // 特別枠で frameDataのCPU時間を追加 63 | ThreadViewData frameViewData = null; 64 | if (!this.viewData.TryGetValue(FrameWholeDataSpecialKey, out frameViewData)) 65 | { 66 | frameViewData = new ThreadViewData(FrameWholeDataSpecialKey); 67 | viewData.Add(FrameWholeDataSpecialKey, frameViewData); 68 | } 69 | frameViewData.AddMsec(frameData.frameIndex, frameData.m_TotalCPUTimeInMicroSec / 1000.0f, 0.0f, 0, 0); 70 | 71 | // 同一フレーム内に同じスレッド名が複数できるので… 72 | Dictionary threadNameCounter = new Dictionary(8); 73 | foreach (var thread in frameData.m_ThreadData) 74 | { 75 | string threadName = thread.FullName; 76 | if (threadName == null) { continue; } 77 | int cnt = 0; 78 | if (threadNameCounter.TryGetValue(threadName, out cnt)) 79 | { 80 | ++cnt; 81 | threadName = threadName + cnt; 82 | } 83 | threadNameCounter[threadName] = cnt; 84 | this.AddDataTo(frameData.frameIndex, threadName, thread); 85 | } 86 | } 87 | 88 | private void AddDataTo(int frameIdx, string threadName, ThreadData data) 89 | { 90 | ThreadViewData threadViewData = null; 91 | if (!this.viewData.TryGetValue(threadName, out threadViewData)) 92 | { 93 | threadViewData = new ThreadViewData(threadName); 94 | viewData.Add(threadName, threadViewData); 95 | } 96 | float totalMsec = 0.0f; 97 | float idleMsec = 0.0f; 98 | int totalCount = 0; 99 | int idleCount = 0; 100 | if (data.m_AllSamples != null) 101 | { 102 | foreach (var sample in data.m_AllSamples) 103 | { 104 | if (sample.parent == null) 105 | { 106 | idleMsec += GetSumOfTimeInSampleChildren(sample, "Idle", ref idleCount); 107 | totalMsec += GetSumeOfTimeWithNamedSampleInChildren(sample, ref totalCount);// sample.timeUS / 1000.0f; 108 | } 109 | } 110 | } 111 | threadViewData.AddMsec(frameIdx, totalMsec, idleMsec, totalCount, idleCount); 112 | } 113 | 114 | private float GetSumeOfTimeWithNamedSampleInChildren(ProfilerSample sample, ref int count) 115 | { 116 | float sum = 0.0f; 117 | if (!string.IsNullOrEmpty(sample.sampleName)) 118 | { 119 | count += 1; 120 | return sample.timeUS / 1000.0f; 121 | } 122 | if (sample.children == null) 123 | { 124 | return sum; 125 | } 126 | foreach (var child in sample.children) 127 | { 128 | sum += GetSumeOfTimeWithNamedSampleInChildren(child, ref count); 129 | } 130 | return sum; 131 | } 132 | 133 | private float GetSumOfTimeInSampleChildren(ProfilerSample sample, string matchStr, ref int count) 134 | { 135 | float sum = 0.0f; 136 | if (sample == null) { return sum; } 137 | if (sample.sampleName == matchStr) 138 | { 139 | count += 1; 140 | return sample.timeUS / 1000.0f; 141 | } 142 | if (sample.children == null) 143 | { 144 | return sum; 145 | } 146 | 147 | foreach (var child in sample.children) 148 | { 149 | sum += GetSumOfTimeInSampleChildren(child, matchStr, ref count); 150 | } 151 | return sum; 152 | } 153 | 154 | /// 155 | /// 結果書き出し 156 | /// 157 | protected override string GetResultText() 158 | { 159 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 160 | int frameNum = 0; 161 | var threadViewDataList = new List(viewData.Values); 162 | 163 | foreach (var data in threadViewDataList) 164 | { 165 | if (data.threadName == FrameWholeDataSpecialKey) 166 | { 167 | csvStringGenerator.AppendColumn(data.threadName).AppendColumn(""); 168 | } 169 | else 170 | { 171 | csvStringGenerator.AppendColumn(data.threadName + "(msec)"); 172 | csvStringGenerator.AppendColumn("idle(msec)"); 173 | csvStringGenerator.AppendColumn("working(msec)"); 174 | csvStringGenerator.AppendColumn("rootBlock"); 175 | csvStringGenerator.AppendColumn("idleBlock").AppendColumn(""); 176 | } 177 | frameNum = ProfilerLogUtil.Max(data.maxFrame, frameNum); 178 | } 179 | csvStringGenerator.NextRow(); 180 | 181 | for (int i = 0; i < frameNum; ++i) 182 | { 183 | foreach (var data in threadViewDataList) 184 | { 185 | int totalCnt, idleCnt; 186 | float total, idle; 187 | data.GetMSecData(i, out total, out idle, out totalCnt, out idleCnt); 188 | if (data.threadName == FrameWholeDataSpecialKey) 189 | { 190 | csvStringGenerator.AppendColumn(total).AppendColumn(""); 191 | } 192 | else 193 | { 194 | csvStringGenerator.AppendColumn(total).AppendColumn(idle).AppendColumn(total - idle); 195 | csvStringGenerator.AppendColumn(totalCnt).AppendColumn(idleCnt).AppendColumn(""); 196 | } 197 | } 198 | csvStringGenerator.NextRow(); 199 | } 200 | return csvStringGenerator.ToString(); 201 | } 202 | 203 | protected override string FooterName 204 | { 205 | get 206 | { 207 | return "_result.csv"; 208 | } 209 | } 210 | 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/ThreadAnalyzeToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2a8ca0b714cbc7044987ba7b2a4c42ef 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/UrpGpuSampleToFile .cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UTJ.ProfilerReader.BinaryData; 3 | using System.Text; 4 | using UTJ.ProfilerReader.BinaryData.Thread; 5 | using UTJ.ProfilerReader.RawData.Protocol; 6 | using System.Runtime.Remoting.Channels; 7 | using System; 8 | 9 | namespace UTJ.ProfilerReader.Analyzer 10 | { 11 | public class UrpGPUSampleToFile : AnalyzeToTextbaseFileBase 12 | { 13 | public enum Category : int 14 | { 15 | Opaque = 0, 16 | Transparent = 1, 17 | Shadowmap = 2, 18 | PostProcess = 3, 19 | Other = 4 20 | }; 21 | 22 | private struct GpuTimeInfo 23 | { 24 | public int time; 25 | public int count; 26 | } 27 | private class FrameGpuTime 28 | { 29 | public int frameIdx; 30 | public Dictionary gpuTimeByCategory; 31 | 32 | public void AddGpuSample(GPUTime gpuTime, int category) 33 | { 34 | if (gpuTimeByCategory == null) 35 | { 36 | gpuTimeByCategory = new Dictionary(); 37 | } 38 | GpuTimeInfo time; 39 | if (gpuTimeByCategory.TryGetValue(category, out time)) 40 | { 41 | time.time += gpuTime.gpuTimeInMicroSec; 42 | time.count += 1; 43 | gpuTimeByCategory[category] = time; 44 | } 45 | else 46 | { 47 | time = new GpuTimeInfo { time = gpuTime.gpuTimeInMicroSec, count = 1 }; 48 | gpuTimeByCategory.Add(category, time); 49 | } 50 | } 51 | } 52 | 53 | private List frameGpuTimes = new List(); 54 | 55 | public override void CollectData(ProfilerFrameData frameData) 56 | { 57 | FrameGpuTime frameGpuTime = new FrameGpuTime(); 58 | frameGpuTime.frameIdx = frameData.frameIndex; 59 | 60 | foreach (var threadData in frameData.m_ThreadData) { 61 | AddGpuSampleByThread(threadData, frameGpuTime); 62 | } 63 | this.frameGpuTimes.Add(frameGpuTime); 64 | } 65 | 66 | private void AddGpuSampleByThread(ThreadData thread, FrameGpuTime frameGpuTime) 67 | { 68 | if (thread == null) { return; } 69 | if (thread.m_GPUTimeSamples == null) { return; } 70 | foreach (var gpuSample in thread.m_GPUTimeSamples) 71 | { 72 | frameGpuTime.AddGpuSample(gpuSample, GetGpuCategoryByCpuSample(thread, gpuSample)); 73 | } 74 | } 75 | private int GetGpuCategoryByCpuSample(ThreadData thread, GPUTime gpuSample) 76 | { 77 | int category = (int)Category.Other; 78 | var cpuSample = GetCpuSample(thread, gpuSample); 79 | for (var current = cpuSample; current != null;current = current.parent) 80 | { 81 | if(current.sampleName == null) { continue; } 82 | 83 | if (current.sampleName.EndsWith("Opaques")) 84 | { 85 | return (int)Category.Opaque; 86 | } 87 | else if (current.sampleName.EndsWith("Transparents")) 88 | { 89 | return (int)Category.Transparent; 90 | } 91 | else if (current.sampleName.EndsWith("ShadowMap")) 92 | { 93 | return (int)Category.Shadowmap; 94 | } 95 | else if (current.sampleName.Contains("PostProcessing")) 96 | { 97 | return (int)Category.PostProcess; 98 | } 99 | } 100 | return category; 101 | } 102 | 103 | private ProfilerSample GetCpuSample(ThreadData thread, GPUTime gpuSample) 104 | { 105 | int idx = (int)gpuSample.relatedSampleIndex; 106 | if(thread.m_AllSamples == null) { return null; } 107 | if( idx < 0 || idx >= thread.m_AllSamples.Count) 108 | { 109 | return null; 110 | } 111 | ProfilerSample cpuSample = thread.m_AllSamples[idx]; 112 | return cpuSample; 113 | 114 | } 115 | 116 | 117 | /// 118 | /// 結果書き出し 119 | /// 120 | protected override string GetResultText() 121 | { 122 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 123 | csvStringGenerator.AppendColumn("frameIdx"); 124 | 125 | 126 | foreach (var category in System.Enum.GetValues(typeof(Category))) 127 | { 128 | csvStringGenerator.AppendColumn(category.ToString() + "(ms)"); 129 | } 130 | csvStringGenerator.AppendColumn("callNum"); 131 | foreach (var category in System.Enum.GetValues(typeof(Category))) 132 | { 133 | csvStringGenerator.AppendColumn(category.ToString() + "(calls)"); 134 | } 135 | 136 | csvStringGenerator.NextRow(); 137 | foreach( var gpuFrame in frameGpuTimes) 138 | { 139 | if (gpuFrame.gpuTimeByCategory == null) { continue; } 140 | csvStringGenerator.AppendColumn(gpuFrame.frameIdx); 141 | 142 | foreach (var category in System.Enum.GetValues(typeof(Category))) 143 | { 144 | GpuTimeInfo val ; 145 | if(gpuFrame.gpuTimeByCategory.TryGetValue((int)category,out val)) 146 | { 147 | csvStringGenerator.AppendColumn( (float)val.time / 1000.0f); 148 | } 149 | else 150 | { 151 | csvStringGenerator.AppendColumn(0); 152 | } 153 | } 154 | csvStringGenerator.AppendColumn(""); 155 | 156 | foreach (var category in System.Enum.GetValues(typeof(Category))) 157 | { 158 | GpuTimeInfo val; 159 | if (gpuFrame.gpuTimeByCategory.TryGetValue((int)category, out val)) 160 | { 161 | csvStringGenerator.AppendColumn(val.count); 162 | } 163 | else 164 | { 165 | csvStringGenerator.AppendColumn(0); 166 | } 167 | } 168 | 169 | csvStringGenerator.NextRow(); 170 | } 171 | return csvStringGenerator.ToString(); 172 | } 173 | 174 | 175 | protected override string FooterName 176 | { 177 | get 178 | { 179 | return "_urp_gpu_sample.csv"; 180 | } 181 | } 182 | 183 | 184 | } 185 | 186 | } -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/UrpGpuSampleToFile .cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 49f5a491901f9f646afcadc3c2c9dda7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/WorkerJobAnalyzeToFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UTJ.ProfilerReader.BinaryData; 4 | 5 | 6 | namespace UTJ.ProfilerReader.Analyzer 7 | { 8 | public class WorkerJobAnalyzeToFile : AnalyzeToTextbaseFileBase 9 | { 10 | private class WorkerThreadSample 11 | { 12 | public string sampleName; 13 | public float minMSec = float.MaxValue; 14 | public float maxMsec = 0.0f; 15 | public float sumMsec = 0.0f; 16 | public int callNum = 0; 17 | 18 | public WorkerThreadSample(string name) 19 | { 20 | this.sampleName = name; 21 | } 22 | 23 | public void Called(float msec) 24 | { 25 | minMSec = ProfilerLogUtil.Min(minMSec, msec); 26 | maxMsec = ProfilerLogUtil.Max(maxMsec, msec); 27 | sumMsec += msec; 28 | ++callNum; 29 | } 30 | } 31 | private void AddSampleData(string sampleName, float msec) 32 | { 33 | WorkerThreadSample sampleData = null; 34 | if (!this.samples.TryGetValue(sampleName, out sampleData)) 35 | { 36 | sampleData = new WorkerThreadSample(sampleName); 37 | this.samples.Add(sampleName, sampleData); 38 | } 39 | sampleData.Called(msec); 40 | } 41 | 42 | private Dictionary samples = new Dictionary(); 43 | 44 | 45 | private void CollectThread(ThreadData thread) 46 | { 47 | if (thread.m_AllSamples == null) { return; } 48 | foreach (var sample in thread.m_AllSamples) 49 | { 50 | if (sample.parent == null) 51 | { 52 | CollectFromNamedChildren(sample); 53 | } 54 | } 55 | } 56 | 57 | private void CollectFromNamedChildren(ProfilerSample sample) 58 | { 59 | if (!string.IsNullOrEmpty(sample.sampleName)) 60 | { 61 | AddSampleData(sample.sampleName, sample.timeUS / 1000.0f); 62 | return; 63 | } 64 | if (sample.children == null) 65 | { 66 | return; 67 | } 68 | foreach (var child in sample.children) 69 | { 70 | CollectFromNamedChildren(child); 71 | } 72 | return; 73 | } 74 | 75 | public override void CollectData(ProfilerFrameData frameData) 76 | { 77 | // 特別枠で frameDataのCPU時間を追加 78 | // 同一フレーム内に同じスレッド名が複数できるので… 79 | Dictionary threadNameCounter = new Dictionary(8); 80 | foreach (var thread in frameData.m_ThreadData) 81 | { 82 | if (thread.m_ThreadName == "Worker Thread" || thread.m_GroupName == "Job" ) 83 | { 84 | CollectThread(thread); 85 | } 86 | } 87 | } 88 | /// 89 | /// 結果書き出し 90 | /// 91 | protected override string GetResultText() 92 | { 93 | CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); 94 | csvStringGenerator.AppendColumn("name").AppendColumn("sum(msec)").AppendColumn("call").AppendColumn("min(msec)").AppendColumn("max(msec)").NextRow(); 95 | 96 | var sampleDataList = new List(samples.Values); 97 | sampleDataList.Sort((a, b) => 98 | { 99 | if (a.sumMsec > b.sumMsec) 100 | { 101 | return -1; 102 | } 103 | else if (a.sumMsec < b.sumMsec) 104 | { 105 | return 1; 106 | } 107 | return 0; 108 | }); 109 | foreach (var sampleData in sampleDataList) 110 | { 111 | csvStringGenerator.AppendColumn(sampleData.sampleName). 112 | AppendColumn(sampleData.sumMsec). 113 | AppendColumn(sampleData.callNum). 114 | AppendColumn(sampleData.minMSec). 115 | AppendColumn(sampleData.maxMsec); 116 | csvStringGenerator.NextRow(); 117 | } 118 | 119 | return csvStringGenerator.ToString(); 120 | } 121 | 122 | protected override string FooterName 123 | { 124 | get 125 | { 126 | return "_worker.csv"; 127 | } 128 | } 129 | 130 | } 131 | 132 | } -------------------------------------------------------------------------------- /Editor/Analyzer/Impl/WorkerJobAnalyzeToFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1a89b4a11c81a764fbb5546d719a95cd 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Analyzer/ProfilingScope.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | using UnityEngine.Profiling; 6 | 7 | namespace UTJ.ProfilerReader.Analyzer 8 | { 9 | struct ProfilingScope : System.IDisposable 10 | { 11 | CustomSampler _sampler; 12 | public ProfilingScope(CustomSampler sampler) 13 | { 14 | _sampler = sampler; 15 | _sampler.Begin(); 16 | } 17 | public void Dispose() 18 | { 19 | _sampler.End(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Editor/Analyzer/ProfilingScope.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 364fed2e9c0775e448a30297b36b1b11 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/CUI.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 486ca37e5d486f34096200caa31394df 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/CUI/CUIInterface.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UTJ.ProfilerReader.Analyzer; 4 | using UnityEngine; 5 | using System.IO; 6 | using System.Threading.Tasks; 7 | 8 | namespace UTJ.ProfilerReader 9 | { 10 | public class CUIInterface 11 | { 12 | const int NormalCode = 0; 13 | const int TimeoutCode = 10; 14 | const int ReadErrorCode = 11; 15 | 16 | public enum CsvFileType 17 | { 18 | 19 | }; 20 | 21 | private static int timeoutSec = 0; 22 | private static ILogReaderPerFrameData currentReader = null; 23 | private static bool timeouted = false; 24 | 25 | private static string overrideUnityVersion = null; 26 | 27 | 28 | public static void SetTimeout(int sec) 29 | { 30 | Debug.Log("SetTimeout " + sec); 31 | timeoutSec = sec; 32 | System.Threading.Thread th = new System.Threading.Thread(TimeOutExecute); 33 | th.Start(); 34 | } 35 | public static void TimeOutExecute() 36 | { 37 | System.Threading.Thread.Sleep(timeoutSec * 1000); 38 | Debug.Log("Timeout!!!"); 39 | currentReader.ForceExit(); 40 | timeouted = true; 41 | } 42 | 43 | public static void ProfilerToCsv() 44 | { 45 | var args = System.Environment.GetCommandLineArgs(); 46 | string inputFile = null; 47 | string outputDir = null; 48 | bool exitFlag = true; 49 | bool logFlag = false; 50 | bool isLegacyOutputDirPath = false; 51 | 52 | for (int i = 0; i < args.Length; ++i) 53 | { 54 | if (args[i] == "-PH.inputFile") 55 | { 56 | inputFile = args[i + 1]; 57 | i += 1; 58 | } 59 | if (args[i] == "-PH.outputDir") 60 | { 61 | outputDir = args[i + 1]; 62 | i += 1; 63 | } 64 | if (args[i] == "-PH.timeout") 65 | { 66 | SetTimeout(int.Parse(args[i + 1])); 67 | i += 1; 68 | } 69 | if( args[i] == "-PH.overrideUnityVersion") 70 | { 71 | overrideUnityVersion = args[i + 1]; 72 | i += 1; 73 | } 74 | if (args[i] == "-PH.exitcode") 75 | { 76 | exitFlag = true; 77 | } 78 | if (args[i] == "-PH.log") 79 | { 80 | logFlag = true; 81 | } 82 | if (args[i] == "-PH.dirLegacy") ; 83 | { 84 | isLegacyOutputDirPath = true; 85 | } 86 | } 87 | int code = ProfilerToCsv(inputFile, outputDir, logFlag, isLegacyOutputDirPath); 88 | if(timeouted) 89 | { 90 | code = TimeoutCode; 91 | } 92 | if (exitFlag) 93 | { 94 | UnityEditor.EditorApplication.Exit(code); 95 | } 96 | } 97 | 98 | public static int ProfilerToCsv(string inputFile,string outputDir,bool logFlag,bool isLegacyOutputDirPath) 99 | { 100 | int retCode = NormalCode; 101 | if ( string.IsNullOrEmpty(outputDir)) 102 | { 103 | if (isLegacyOutputDirPath) 104 | { 105 | outputDir = Path.GetDirectoryName(inputFile); 106 | } 107 | else 108 | { 109 | string file = Path.GetFileName(inputFile); 110 | outputDir = Path.Combine(Path.GetDirectoryName(inputFile), file.Replace('.', '_')); 111 | } 112 | } 113 | 114 | var logReader = ProfilerLogUtil.CreateLogReader(inputFile); 115 | currentReader = logReader; 116 | 117 | List analyzeExecutes = AnalyzerUtil.CreateAnalyzerInterfaceObjects(); 118 | 119 | var frameData = logReader.ReadFrameData(); 120 | SetAnalyzerInfo(analyzeExecutes, logReader,outputDir,inputFile); 121 | 122 | if ( frameData == null) 123 | { 124 | Debug.LogError("No FrameDataFile " + inputFile); 125 | } 126 | // Loop and execute each frame 127 | while (frameData != null) 128 | { 129 | try 130 | { 131 | frameData = logReader.ReadFrameData(); 132 | if (logFlag && frameData != null) 133 | { 134 | System.Console.WriteLine("ReadFrame:" + frameData.frameIndex); 135 | } 136 | } 137 | catch (System.Exception e) 138 | { 139 | retCode = ReadErrorCode; 140 | Debug.LogError(e); 141 | } 142 | 143 | if (frameData != null) 144 | { 145 | List tasks = new List(analyzeExecutes.Count); 146 | foreach (var analyzer in analyzeExecutes) 147 | { 148 | var task = Task.Run(() => 149 | { 150 | try 151 | { 152 | analyzer.CollectData(frameData); 153 | 154 | } 155 | catch (System.Exception e) 156 | { 157 | Debug.LogError(e); 158 | } 159 | }); 160 | tasks.Add(task); 161 | } 162 | while (true) 163 | { 164 | bool isComplete = true; 165 | foreach (var task in tasks) 166 | { 167 | if (!task.IsCompleted) 168 | { 169 | isComplete = false; 170 | break; 171 | } 172 | } 173 | if (isComplete) 174 | { 175 | break; 176 | } 177 | } 178 | } 179 | System.GC.Collect(); 180 | } 181 | foreach (var analyzer in analyzeExecutes) 182 | { 183 | analyzer.WriteResultFile(System.IO.Path.GetFileName(inputFile), outputDir); 184 | } 185 | 186 | return retCode; 187 | } 188 | private static void SetAnalyzerInfo(List analyzeExecutes, 189 | ILogReaderPerFrameData logReader, 190 | string outDir,string inFile) 191 | { 192 | ProfilerLogFormat format = ProfilerLogFormat.TypeData; 193 | if (logReader.GetType() == typeof(UTJ.ProfilerReader.RawData.ProfilerRawLogReader)) 194 | { 195 | format = ProfilerLogFormat.TypeRaw; 196 | } 197 | string unityVersion = Application.unityVersion; 198 | if ( !string.IsNullOrEmpty(overrideUnityVersion)) 199 | { 200 | unityVersion = overrideUnityVersion; 201 | } 202 | foreach (var analyzer in analyzeExecutes) 203 | { 204 | analyzer.SetInfo(format, unityVersion, logReader.GetLogFileVersion(), logReader.GetLogFilePlatform()); 205 | analyzer.SetFileInfo(inFile,outDir); 206 | } 207 | } 208 | } 209 | } -------------------------------------------------------------------------------- /Editor/CUI/CUIInterface.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3f87dac947add4540b9b1f12ecfd9035 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/GUI.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 105db1c87ad9d724dbcb5c713dc1338e 3 | folderAsset: yes 4 | timeCreated: 1478603637 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Editor/GUI/AnalyzeToCsvWindow.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using UTJ.ProfilerReader.Analyzer; 6 | using System.Reflection; 7 | using System.IO; 8 | using System.Threading.Tasks; 9 | using System.Text; 10 | using UnityEditor.Overlays; 11 | 12 | namespace UTJ.ProfilerReader.UI { 13 | 14 | 15 | 16 | public class AnalyzeToCsvWindow : EditorWindow 17 | { 18 | 19 | 20 | private class FileWriterFlag 21 | { 22 | public string name; 23 | public System.Type type; 24 | public bool flag; 25 | 26 | } 27 | 28 | private List fileWriterFlags = new List(); 29 | private List analyzeExecutes = new List(); 30 | private ILogReaderPerFrameData logReader = null; 31 | private bool isFirstFrame = true; 32 | private Vector2 scrollPos; 33 | private string filePath; 34 | 35 | private string outputDir; 36 | private string logfilename; 37 | private bool isWindowExists; 38 | 39 | private bool forceStopRequest = false; 40 | private bool requestDialogFlag = false; 41 | 42 | private bool isMultiThreadExecute = true; 43 | private List analyzeTasks = new List(16); 44 | private List analyzeTaskNames = new List(16); 45 | private string taskStatus = ""; 46 | private GUIStyle uiStyle; 47 | 48 | 49 | 50 | [MenuItem("Tools/UTJ/ProfilerReader/AnalyzeToCsv")] 51 | public static void CreateWindow() 52 | { 53 | EditorWindow.GetWindow(); 54 | } 55 | 56 | private void OnEnable() 57 | { 58 | this.uiStyle = new GUIStyle( ); 59 | this.uiStyle.normal.textColor = Color.red; 60 | this.isWindowExists = true; 61 | this.fileWriterFlags.Clear(); 62 | var types = AnalyzerUtil.GetInterfaceType(); 63 | foreach( var t in types) 64 | { 65 | this.fileWriterFlags.Add(new FileWriterFlag() { name = t.Name, type = t, flag = true }); 66 | } 67 | } 68 | private void OnDisable() 69 | { 70 | this.isWindowExists = false; 71 | } 72 | 73 | private void UpdateThread() 74 | { 75 | while (this.isWindowExists) 76 | { 77 | if(!ExecuteFrame()){ 78 | return; 79 | } 80 | } 81 | } 82 | 83 | bool ExecuteFrame() 84 | { 85 | if (logReader == null) 86 | { 87 | return false; 88 | } 89 | try 90 | { 91 | var frameData = logReader.ReadFrameData(); 92 | if (isFirstFrame) 93 | { 94 | InitOutputPathInfo(); 95 | SetAnalyzerInfo(analyzeExecutes, logReader); 96 | isFirstFrame = false; 97 | } 98 | if (frameData == null || forceStopRequest) 99 | { 100 | // write all result 101 | foreach (var analyzer in this.analyzeExecutes) 102 | { 103 | analyzer.WriteResultFile(logfilename, outputDir); 104 | } 105 | requestDialogFlag = true; 106 | logReader = null; 107 | return false; 108 | } 109 | foreach (var analyzer in this.analyzeExecutes) 110 | { 111 | var task = Task.Run(() => 112 | { 113 | try 114 | { 115 | analyzer.CollectData(frameData); 116 | } 117 | catch (System.Exception e) 118 | { 119 | Debug.LogError(e); 120 | } 121 | }); 122 | analyzeTaskNames.Add(analyzer.GetType().Name); 123 | analyzeTasks.Add(task); 124 | } 125 | // waiting all tasks 126 | var sb = new StringBuilder(); 127 | while (true) 128 | { 129 | bool isDone = true; 130 | sb.Length = 0; 131 | for(int i = 0;i < analyzeTasks.Count;++i) 132 | { 133 | var task = analyzeTasks[i]; 134 | if (!task.IsCompleted) 135 | { 136 | isDone = false; 137 | if(sb.Length > 0) 138 | { 139 | sb.Append("\n"); 140 | } 141 | sb.Append(analyzeTaskNames[i]).Append("::").Append(task.Status); 142 | } 143 | } 144 | taskStatus = sb.ToString(); 145 | if (isDone) 146 | { 147 | break; 148 | } 149 | } 150 | taskStatus = ""; 151 | analyzeTasks.Clear(); 152 | analyzeTaskNames.Clear(); 153 | 154 | System.GC.Collect(); 155 | 156 | } 157 | catch (System.Exception e) 158 | { 159 | logReader = null; 160 | Debug.LogError(e); 161 | } 162 | return true; 163 | } 164 | 165 | private void DisplayDialog() 166 | { 167 | requestDialogFlag = false; 168 | string dialogStr = "Write to csv files\n"; 169 | foreach (var analyzer in this.analyzeExecutes) 170 | { 171 | dialogStr += analyzer.GetType() + "\n"; 172 | } 173 | EditorUtility.DisplayDialog("Result", dialogStr, "ok"); 174 | analyzeExecutes.Clear(); 175 | } 176 | 177 | 178 | void OnGUI() 179 | { 180 | EditorGUILayout.LabelField("Convert profiler log to csv"); 181 | EditorGUILayout.BeginHorizontal(); 182 | if (string.IsNullOrEmpty(filePath)) 183 | { 184 | EditorGUILayout.LabelField("Select File"); 185 | } 186 | else 187 | { 188 | EditorGUILayout.LabelField(this.filePath); 189 | } 190 | if (GUILayout.Button("File", GUILayout.Width(40.0f))) 191 | { 192 | this.filePath = EditorUtility.OpenFilePanelWithFilters("", "Select BinaryLogFile", new string[] { "profiler log", "data,raw" }); 193 | } 194 | 195 | if (!IsExecute()) 196 | { 197 | if (GUILayout.Button("Analyze", GUILayout.Width(100))) 198 | { 199 | if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath)) 200 | { 201 | Debug.LogError("No such File "); 202 | } 203 | else 204 | { 205 | StartAnalyze(); 206 | } 207 | } 208 | } 209 | else 210 | { 211 | if (GUILayout.Button("ForceExit", GUILayout.Width(100))) 212 | { 213 | forceStopRequest = true; 214 | } 215 | } 216 | EditorGUILayout.EndHorizontal(); 217 | if (IsExecute() ) 218 | { 219 | uiStyle.alignment = TextAnchor.UpperLeft; 220 | 221 | EditorGUILayout.LabelField("Progress " + logReader.Progress * 100.0f + "%"); 222 | EditorGUILayout.LabelField(this.taskStatus, uiStyle, GUILayout.Height(200)); 223 | } 224 | else 225 | { 226 | for (int i = 0; i < fileWriterFlags.Count; ++i) 227 | { 228 | EditorGUILayout.BeginHorizontal(); 229 | this.fileWriterFlags[i].flag = EditorGUILayout.Toggle(this.fileWriterFlags[i].flag ,GUILayout.Width(20) ); 230 | EditorGUILayout.LabelField(this.fileWriterFlags[i].name); 231 | EditorGUILayout.EndHorizontal(); 232 | } 233 | } 234 | 235 | EditorGUILayout.LabelField("The results are in csv file."); 236 | } 237 | 238 | private void StartAnalyze() 239 | { 240 | logReader = ProfilerLogUtil.CreateLogReader(filePath); 241 | isFirstFrame = true; 242 | forceStopRequest = false; 243 | analyzeExecutes.Clear(); 244 | for (int i = 0; i < fileWriterFlags.Count; ++i) 245 | { 246 | if (this.fileWriterFlags[i].flag) 247 | { 248 | var analyzer = System.Activator.CreateInstance(fileWriterFlags[i].type) as IAnalyzeFileWriter; 249 | analyzeExecutes.Add(analyzer); 250 | } 251 | } 252 | // start analyze 253 | if (isMultiThreadExecute) 254 | { 255 | var thread = new System.Threading.Thread(this.UpdateThread); 256 | thread.Start(); 257 | } 258 | } 259 | 260 | 261 | private void Update() 262 | { 263 | if (!isMultiThreadExecute) 264 | { 265 | this.ExecuteFrame(); 266 | } 267 | if (IsExecute()) 268 | { 269 | this.Repaint(); 270 | } 271 | if (requestDialogFlag) 272 | { 273 | this.DisplayDialog(); 274 | } 275 | } 276 | 277 | private bool IsExecute() 278 | { 279 | return (logReader != null && 0.0f < logReader.Progress && logReader.Progress < 1.0f); 280 | } 281 | 282 | private void InitOutputPathInfo() 283 | { 284 | this.logfilename = Path.GetFileName(this.filePath); 285 | this.outputDir = Path.Combine(Path.GetDirectoryName(this.filePath), logfilename.Replace('.', '_')); 286 | if (!Directory.Exists(this.outputDir)) 287 | { 288 | Directory.CreateDirectory(this.outputDir); 289 | } 290 | } 291 | 292 | private void SetAnalyzerInfo(List analyzeExecutes, ILogReaderPerFrameData logReader) 293 | { 294 | 295 | ProfilerLogFormat format = ProfilerLogFormat.TypeData; 296 | if (logReader.GetType() == typeof(UTJ.ProfilerReader.RawData.ProfilerRawLogReader)) 297 | { 298 | format = ProfilerLogFormat.TypeRaw; 299 | } 300 | foreach (var analyzer in analyzeExecutes) 301 | { 302 | analyzer.SetInfo(format, Application.unityVersion, logReader.GetLogFileVersion(), logReader.GetLogFilePlatform()); 303 | analyzer.SetFileInfo(logfilename, this.outputDir); 304 | } 305 | } 306 | 307 | 308 | } 309 | 310 | 311 | } -------------------------------------------------------------------------------- /Editor/GUI/AnalyzeToCsvWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: abd0154d324b92e4584511ce7086dea7 3 | timeCreated: 1479298377 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/GUI/LogAnalyzeWindow.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using UTJ.ProfilerReader; 6 | using UTJ.ProfilerReader.BinaryData; 7 | using UTJ.ProfilerReader.RawData; 8 | 9 | namespace UTJ.ProfilerReader.UI{ 10 | public class LogAnalyzeWindow : EditorWindow 11 | { 12 | private struct ColumnData 13 | { 14 | public ProfilerSample sample; 15 | public int frameIndex; 16 | } 17 | public enum EConditionType 18 | { 19 | StartsWith, 20 | Contains, 21 | EndsWith, 22 | } 23 | 24 | // 表示条件 25 | public enum ESampleCondition 26 | { 27 | Always, 28 | ParentOnly, 29 | ChildOnly, 30 | } 31 | private const int PagingNumber = 100; 32 | 33 | private ILogReaderPerFrameData logReader = null; 34 | private SampleDetailView treeView = new SampleDetailView(); 35 | private List columnList = new List(); 36 | private Vector2 scrollPos; 37 | private string filePath; 38 | 39 | private EConditionType stringCheckCondition; 40 | private ESampleCondition sampleCondition; 41 | 42 | private string sampleNameCondition; 43 | private float conditionExecuteTime; 44 | private int conditionAlloc; 45 | private int pageIndex; 46 | 47 | 48 | [MenuItem("Tools/UTJ/ProfilerReader/LogAnalyzer")] 49 | public static void CreateWindow() 50 | { 51 | EditorWindow.GetWindow(); 52 | } 53 | 54 | void OnEnable() 55 | { 56 | } 57 | 58 | void Update() 59 | { 60 | try 61 | { 62 | if (logReader != null) 63 | { 64 | var data = logReader.ReadFrameData(); 65 | if (data == null) { return; } 66 | this.CollectData(data); 67 | System.GC.Collect(); 68 | if (data == null) 69 | { 70 | logReader = null; 71 | } 72 | this.Repaint(); 73 | } 74 | } 75 | catch (System.Exception e) 76 | { 77 | logReader = null; 78 | Debug.LogError(e); 79 | } 80 | } 81 | void OnGUI() 82 | { 83 | EditorGUILayout.LabelField("Profiler BinLog Hack"); 84 | EditorGUILayout.BeginHorizontal(); 85 | this.filePath = EditorGUILayout.TextField(this.filePath); 86 | if (GUILayout.Button("File", GUILayout.Width(40.0f))) 87 | { 88 | this.filePath = EditorUtility.OpenFilePanelWithFilters("", "Select BinaryLogFile", new string[]{ "profiler log", "data,raw" }); 89 | } 90 | EditorGUILayout.EndHorizontal(); 91 | { 92 | EditorGUILayout.LabelField("Concition"); 93 | EditorGUILayout.BeginHorizontal(); 94 | sampleNameCondition = EditorGUILayout.TextField("sample name ", sampleNameCondition); 95 | this.stringCheckCondition = (EConditionType)EditorGUILayout.EnumPopup(this.stringCheckCondition); 96 | EditorGUILayout.EndHorizontal(); 97 | 98 | EditorGUILayout.BeginHorizontal(); 99 | EditorGUILayout.LabelField("HierarchyMode"); 100 | this.sampleCondition = (ESampleCondition)EditorGUILayout.EnumPopup(this.sampleCondition); 101 | EditorGUILayout.EndHorizontal(); 102 | 103 | EditorGUILayout.BeginHorizontal(); 104 | this.conditionExecuteTime = EditorGUILayout.FloatField("Execute time(ms)", this.conditionExecuteTime); 105 | this.conditionAlloc = EditorGUILayout.IntField("Alloc(byte)", conditionAlloc); 106 | EditorGUILayout.EndHorizontal(); 107 | } 108 | 109 | EditorGUILayout.BeginHorizontal(); 110 | if (!IsExecuting()) 111 | { 112 | 113 | if (GUILayout.Button("Analyze", GUILayout.Width(100))) 114 | { 115 | this.pageIndex = 1; 116 | if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath)) 117 | { 118 | Debug.LogError("No such File "); 119 | } 120 | else 121 | { 122 | logReader = ProfilerLogUtil.CreateLogReader(filePath); 123 | logReader.SetUnityVersion(Application.unityVersion); 124 | columnList.Clear(); 125 | } 126 | } 127 | } 128 | else 129 | { 130 | if (GUILayout.Button("Cancel", GUILayout.Width(100))) 131 | { 132 | logReader.ForceExit(); 133 | } 134 | } 135 | GUILayout.Label(""); 136 | if (logReader != null && logReader.IsComplete) 137 | { 138 | if (GUILayout.Button("Write To CSV", GUILayout.Width(100))) 139 | { 140 | SaveToCsv(); 141 | } 142 | } 143 | EditorGUILayout.EndHorizontal(); 144 | // execute now 145 | if (IsExecuting()) 146 | { 147 | EditorGUILayout.LabelField("Progress " + logReader.Progress * 100.0f + "%"); 148 | } 149 | else 150 | { 151 | this.ONGUIResultList(); 152 | treeView.OnGUI(); 153 | } 154 | } 155 | 156 | private bool IsExecuting() 157 | { 158 | return (logReader != null && 0.0f < logReader.Progress && logReader.Progress < 1.0f); 159 | } 160 | 161 | 162 | private void ONGUIResultList() 163 | { 164 | EditorGUILayout.BeginHorizontal(); 165 | if( GUILayout.Button("<-",GUILayout.Width(40))) 166 | { 167 | if (pageIndex > 1) 168 | { 169 | --pageIndex; 170 | } 171 | } 172 | var pageStr = EditorGUILayout.TextField(this.pageIndex.ToString(), GUILayout.Width(40)); 173 | int.TryParse(pageStr, out this.pageIndex); 174 | EditorGUILayout.LabelField("/" + (columnList.Count + PagingNumber - 1) / PagingNumber, GUILayout.Width(40)); 175 | 176 | if( GUILayout.Button("->", GUILayout.Width(40))) 177 | { 178 | if (pageIndex + 1 <= (columnList.Count + PagingNumber-1) / PagingNumber) 179 | { 180 | ++pageIndex; 181 | } 182 | } 183 | EditorGUILayout.EndHorizontal(); 184 | this.scrollPos = EditorGUILayout.BeginScrollView(scrollPos, true, true); 185 | EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true)); 186 | for(int i = (this.pageIndex -1) * PagingNumber; i< columnList.Count && i < (this.pageIndex) * PagingNumber; ++ i) 187 | { 188 | if( i < 0 || i > columnList.Count) { continue; } 189 | ColumnData column = columnList[i]; 190 | var sample = column.sample; 191 | EditorGUILayout.BeginHorizontal(); 192 | if (GUILayout.Button("More", GUILayout.Width(40))) 193 | { 194 | this.treeView.SetCurrentSample(sample); 195 | } 196 | string str = column.frameIndex + "::" + sample.sampleName + "::" + (sample.timeUS / 1000.0f) + 197 | "ms Alloc:"; 198 | if (sample.totalGcAlloc < 1024 * 10) 199 | { 200 | str += sample.totalGcAlloc + " Byte"; 201 | } 202 | else if (sample.totalGcAlloc < 1024 * 1024 * 10) 203 | { 204 | str += (sample.totalGcAlloc / 1024) + " KByte"; 205 | } 206 | else 207 | { 208 | str += (sample.totalGcAlloc / 1024 / 1024) + " MByte"; 209 | } 210 | 211 | EditorGUILayout.LabelField(str); 212 | EditorGUILayout.EndHorizontal(); 213 | } 214 | EditorGUILayout.EndVertical(); 215 | EditorGUILayout.EndScrollView(); 216 | } 217 | 218 | private void CollectData(ProfilerFrameData frameData) 219 | { 220 | var mainThread = frameData.MainThread; 221 | if( mainThread == null || mainThread.m_AllSamples == null ) { return; } 222 | List hitInThisFrame = new List(); 223 | foreach (var sample in mainThread.m_AllSamples) 224 | { 225 | if (ChcekSearchCondition(sample)) 226 | { 227 | ColumnData data; 228 | data.sample = sample; 229 | data.frameIndex = frameData.frameIndex; 230 | hitInThisFrame.Add(data); 231 | } 232 | } 233 | 234 | foreach (var hit in hitInThisFrame) 235 | { 236 | switch (this.sampleCondition) 237 | { 238 | case ESampleCondition.Always: 239 | this.columnList.Add(hit); 240 | break; 241 | case ESampleCondition.ChildOnly: 242 | if (!IsChildExists(hit, hitInThisFrame)) 243 | { 244 | this.columnList.Add(hit); 245 | } 246 | break; 247 | case ESampleCondition.ParentOnly: 248 | if (!IsParentExists(hit, hitInThisFrame)) 249 | { 250 | this.columnList.Add(hit); 251 | } 252 | break; 253 | } 254 | } 255 | } 256 | private bool IsChildExists(ColumnData column, List list) 257 | { 258 | foreach (var target in list) 259 | { 260 | if (target.sample != column.sample && IsParent(column.sample, target.sample)) 261 | { 262 | return true; 263 | } 264 | } 265 | return false; 266 | } 267 | 268 | private bool IsParentExists(ColumnData column, List list) 269 | { 270 | foreach (var target in list) 271 | { 272 | if (target.sample != column.sample && IsParent(target.sample, column.sample)) 273 | { 274 | return true; 275 | } 276 | } 277 | return false; 278 | } 279 | 280 | private bool IsParent(ProfilerSample parent, ProfilerSample child) 281 | { 282 | for (ProfilerSample current = child.parent; current != null; current = current.parent) 283 | { 284 | if (current == parent) 285 | { 286 | return true; 287 | } 288 | } 289 | return false; 290 | } 291 | 292 | 293 | private bool ChcekSearchCondition(ProfilerSample sample) 294 | { 295 | if (sample.totalGcAlloc < this.conditionAlloc) 296 | { 297 | return false; 298 | } 299 | if (sample.timeUS / 1000.0f < this.conditionExecuteTime) 300 | { 301 | return false; 302 | } 303 | if (string.IsNullOrEmpty(this.sampleNameCondition)) 304 | { 305 | return true; 306 | } 307 | switch (this.stringCheckCondition) 308 | { 309 | case EConditionType.Contains: 310 | return sample.sampleName.Contains(this.sampleNameCondition); 311 | case EConditionType.StartsWith: 312 | return sample.sampleName.StartsWith(this.sampleNameCondition); 313 | case EConditionType.EndsWith: 314 | return sample.sampleName.EndsWith(this.sampleNameCondition); 315 | } 316 | return false; 317 | } 318 | 319 | void OnDisable() 320 | { 321 | } 322 | 323 | // CSV保存を押したときの処理 324 | private void SaveToCsv() 325 | { 326 | if (this.columnList == null || this.columnList.Count == 0) 327 | { 328 | EditorUtility.DisplayDialog("No Result", "There are no results.Please analyze before.", "OK"); 329 | return; 330 | } 331 | if (0.0f < logReader.Progress && logReader.Progress < 1.0f) 332 | { 333 | EditorUtility.DisplayDialog("Progress", "Now executing...Wait a moment...", "OK"); 334 | return; 335 | } 336 | string savePath = EditorUtility.SaveFilePanel("CSV file", "", "result", "csv"); 337 | if (string.IsNullOrEmpty(savePath)) 338 | { 339 | return; 340 | } 341 | WriteCsv(savePath); 342 | } 343 | 344 | // 実際にファイルを書き込むところ 345 | private void WriteCsv(string path) 346 | { 347 | var sb = new System.Text.StringBuilder(); 348 | sb.Append("frame,SampleName,executeTime(ms),memoryAlloc(byte)\n"); 349 | foreach (var column in columnList) 350 | { 351 | if (column.sample == null) { continue; } 352 | sb.Append(column.frameIndex).Append(","); 353 | // コンマあると 354 | sb.Append(column.sample.sampleName.Replace(',', '_')).Append(","); 355 | sb.Append(column.sample.timeUS / 1000.0f).Append(","); 356 | sb.Append(column.sample.totalGcAlloc).Append(","); 357 | sb.Append("\n"); 358 | } 359 | 360 | System.IO.File.WriteAllText(path, sb.ToString()); 361 | EditorUtility.DisplayDialog("Complete to write result","Save result to"+ path , "OK"); 362 | 363 | } 364 | } 365 | 366 | 367 | 368 | public class SampleDetailView 369 | { 370 | 371 | private ProfilerSample currentSample; 372 | private Vector2 scrollPos; 373 | private Dictionary openList; 374 | private System.Text.StringBuilder stringBuilder; 375 | private Dictionary allocateBlock; 376 | private string[] guiTabStr = new string[] { "Children", "Memory" }; 377 | 378 | private int guiTabSelect; 379 | 380 | public SampleDetailView() 381 | { 382 | openList = new Dictionary(); 383 | stringBuilder = new System.Text.StringBuilder(); 384 | } 385 | 386 | public void SetCurrentSample(ProfilerSample sample) 387 | { 388 | openList.Clear(); 389 | this.currentSample = sample; 390 | scrollPos = Vector2.zero; 391 | if (sample != null) 392 | { 393 | allocateBlock = sample.GetBlockAllocCount(); 394 | } 395 | } 396 | 397 | public void OnGUI() 398 | { 399 | if (this.currentSample == null) 400 | { 401 | EditorGUILayout.BeginVertical(); 402 | EditorGUILayout.LabelField("Not Select"); 403 | EditorGUILayout.EndVertical(); 404 | return; 405 | } 406 | EditorGUILayout.LabelField(currentSample.fullSampleName); 407 | if (GUILayout.Button("Close", GUILayout.Width(60))) 408 | { 409 | this.SetCurrentSample(null); 410 | return; 411 | } 412 | this.guiTabSelect = GUILayout.Toolbar(this.guiTabSelect, this.guiTabStr); 413 | 414 | scrollPos = EditorGUILayout.BeginScrollView(scrollPos, true, true, GUILayout.MinHeight(300)); 415 | 416 | switch (this.guiTabSelect) 417 | { 418 | case 0: 419 | ChildrenDraw(this.currentSample); 420 | break; 421 | case 1: 422 | AllocateBlockListDraw(); // test 423 | break; 424 | } 425 | 426 | EditorGUILayout.EndScrollView(); 427 | } 428 | 429 | private void ChildrenDraw(ProfilerSample sample) 430 | { 431 | if (sample == null) { return; } 432 | this.stringBuilder.Length = 0; 433 | this.stringBuilder.Append(sample.sampleName).Append(" ").Append((sample.timeUS / 1000.0f)).Append("ms Alloc:").Append(sample.totalGcAlloc).Append(" Byte"); 434 | string outputStr = this.stringBuilder.ToString(); 435 | 436 | // style.contentOffset = new Vector2(sample.hierarchyLevel * 10.0f, 0.0f); 437 | 438 | bool isOpen = false; 439 | if (sample.children != null) 440 | { 441 | GUIStyle style = new GUIStyle(EditorStyles.foldout); 442 | style.margin.left = (sample.hierarchyLevel - currentSample.hierarchyLevel) * 20; 443 | if (openList.TryGetValue(sample, out isOpen)) 444 | { 445 | bool openVal = EditorGUILayout.Foldout(isOpen, outputStr, style); 446 | if (openVal != isOpen) { openList[sample] = openVal; } 447 | if (isOpen) 448 | { 449 | foreach (var child in sample.children) 450 | { 451 | ChildrenDraw(child); 452 | } 453 | } 454 | } 455 | else 456 | { 457 | openList.Add(sample, false); 458 | EditorGUILayout.Foldout(isOpen, outputStr, style); 459 | } 460 | } 461 | else 462 | { 463 | GUIStyle style = new GUIStyle(EditorStyles.label); 464 | style.padding.left = (sample.hierarchyLevel - currentSample.hierarchyLevel) * 20; 465 | EditorGUILayout.LabelField(outputStr, style); 466 | } 467 | } 468 | 469 | private void AllocateBlockListDraw() 470 | { 471 | if (allocateBlock == null) { return; } 472 | EditorGUILayout.LabelField("total Memory " + this.currentSample.totalGcAlloc + " Byte"); 473 | EditorGUILayout.Space(); 474 | List keys = new List(allocateBlock.Keys); 475 | keys.Sort(); 476 | keys.Reverse(); 477 | foreach (var key in keys) 478 | { 479 | EditorGUILayout.LabelField(key + " Byte X " + allocateBlock[key]); 480 | } 481 | } 482 | 483 | 484 | private void ParentGUIDraw(ProfilerSample sample) 485 | { 486 | if (sample == null) { return; } 487 | ParentGUIDraw(sample.parent); 488 | EditorGUILayout.LabelField(sample.sampleName); 489 | } 490 | } 491 | } -------------------------------------------------------------------------------- /Editor/GUI/LogAnalyzeWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0b7140a5b12e4764fa5245c6e12d476f 3 | timeCreated: 1479298377 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/GUI/language.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8e7a70037606f1944a544856de86e8c8 3 | folderAsset: yes 4 | timeCreated: 1516345932 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Editor/GUI/language/LanguageEn.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | 4 | namespace UTJ.ProfilerReader.UI 5 | { 6 | public class LanguageEn : LanguageInterface 7 | { 8 | 9 | protected override void CreateDictionary() { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Editor/GUI/language/LanguageEn.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8abc955aa69cb5a45a48d1c754c65a77 3 | timeCreated: 1516345952 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/GUI/language/LanguageInterface.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace UTJ.ProfilerReader.UI 6 | { 7 | public abstract class LanguageInterface 8 | { 9 | private static LanguageInterface instance; 10 | 11 | protected abstract void CreateDictionary(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Editor/GUI/language/LanguageInterface.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 68a8ada8059764945b73502b7a916297 3 | timeCreated: 1516345952 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/GUI/language/LanguageJa.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace UTJ.ProfilerReader.UI 6 | { 7 | public class LanguageJa : LanguageInterface 8 | { 9 | protected override void CreateDictionary() 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Editor/GUI/language/LanguageJa.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 285da0058b5be6944b84589f7071b118 3 | timeCreated: 1516345952 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/UTJProfileReader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unity3d-jp/ProfilerReader/5843fd01fd68d2cf0ac606c28c31c4d4b36dac58/Editor/UTJProfileReader.dll -------------------------------------------------------------------------------- /Editor/UTJProfileReader.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7b17b1edfad53ec42b71e81a27087216 3 | PluginImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | iconMap: {} 7 | executionOrder: {} 8 | defineConstraints: [] 9 | isPreloaded: 0 10 | isOverridable: 1 11 | isExplicitlyReferenced: 0 12 | validateReferences: 1 13 | platformData: 14 | - first: 15 | Any: 16 | second: 17 | enabled: 0 18 | settings: {} 19 | - first: 20 | Editor: Editor 21 | second: 22 | enabled: 1 23 | settings: 24 | DefaultValueInitialized: true 25 | - first: 26 | Windows Store Apps: WindowsStoreApps 27 | second: 28 | enabled: 0 29 | settings: 30 | CPU: AnyCPU 31 | userData: 32 | assetBundleName: 33 | assetBundleVariant: 34 | -------------------------------------------------------------------------------- /Editor/Utj.ProfilerReader.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UTJ.ProfilerReader.Editor", 3 | "references": [ 4 | "UTJ.ProfilerReader.Core" 5 | ], 6 | "optionalUnityReferences": [], 7 | "includePlatforms": [ 8 | "Editor" 9 | ], 10 | "excludePlatforms": [], 11 | "allowUnsafeCode": false, 12 | "overrideReferences": false, 13 | "precompiledReferences": [], 14 | "autoReferenced": true, 15 | "defineConstraints": [] 16 | } -------------------------------------------------------------------------------- /Editor/Utj.ProfilerReader.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5298c21b9b9dc8043a4297316264695f 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/UtjProfilerInitializer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace UTJ.ProfilerReader 6 | { 7 | public class UtjProfilerInitializer 8 | { 9 | [UnityEditor.InitializeOnLoadMethod] 10 | public static void Init() 11 | { 12 | ProfilerLogUtil.logErrorException = (e) => { 13 | // Debug.LogError(e); 14 | }; 15 | ProfilerLogUtil.logErrorString = (str) => { 16 | // Debug.LogError(str); 17 | }; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Editor/UtjProfilerInitializer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ad49b5af4070f2a4dbdea0ed31314305 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unity3d-jp/ProfilerReader/5843fd01fd68d2cf0ac606c28c31c4d4b36dac58/LICENSE.md -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3fc50eb3859c8a54cac1595ff61a265f 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.ja.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unity3d-jp/ProfilerReader/5843fd01fd68d2cf0ac606c28c31c4d4b36dac58/README.ja.md -------------------------------------------------------------------------------- /README.ja.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c0d2319c301d3374bafab003c7b9ff5a 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProfilerReader 2 | Tools for analyze profiler log data.
3 | Read this in other languages: English, [日本語](README.ja.md)
4 | 5 | ## Summary 6 | With this tool, you can analyze the binary log of "Unity Profiler". 7 | For example, generate csv files that shows Samples allocating many Managed Heap from "Unity Profiler" binary log. 8 | 9 | ## Avalable versions 10 | 2019.4 / 2020.3/2021.1/2021.2/2021.3/2022.2/2022.3 11 |
2022.1 skip 12 | 13 | ## Filter Search 14 | Call "Tools->UTJ->ProfilerReader->AnalyzeToCsv" and then this window will be displayed.
15 | ![alt text](Documentation~/img/ProfilerReaderFilter.png) 16 |
17 | 1.Set Profiler log file.
18 | 2.Set Conditions to search samples.
19 | 3.Execute Analyze
20 | 4.The results are here
21 | 5.Write the results to csv file.
22 | 23 | ## CSV Feature 24 | ### GUI 25 | Call "Tools->UTJ->ProfilerReader->AnalyzeToCsv" and then this window will be displayed.
26 | ![alt text](Documentation~/img/ProfilerLogToCsv.png) 27 | 28 |
29 | This tool generate summarized csv files.
30 | 1.Select the categories to generate csv files.
31 | 2.Set Profler log File.
32 | 3.Execute Analyze
33 | 34 | ### CUI Sample 35 | Unity.exe -batchMode -projectPath "ProjectPath" -logFile .\Editor.log -executeMethod UTJ.ProfilerReader.CUIInterface.ProfilerToCsv -PH.inputFile "Binary logFile(.data/.raw)" -PH.timeout 2400 -PH.log 36 | 37 | And some csv file will be generated at subfolder for binary data. 38 | 39 | 40 | ## CSV Files: 41 | This tool generate csv files with footer name.
42 | These are samples.
43 | 44 | -"xxx_mainThread_frame.csv"
45 | The CPU stats by category in each fraemes. 46 |
47 | -"xxx_gc_result.csv"
48 | The list that shows "GC.Alloc". 49 |
50 | -"xxx_gc_detail.csv"
51 | The list of "GC.Alloc" and with Callstackinfo. 52 |
53 | -"xxx_gpu_sample.csv"
54 | The list about GPU status in each frames . 55 |
56 | -"xxx_main_self.csv"
57 | The list about CPU Samples. 58 |
59 | -"xxx_memory.csv"
60 | The list about Memory status in each frames . 61 |
62 | -"xxx_rendering.csv"
63 | The list about Rendering status in each frames . 64 |
65 | -"xxx_renderthread.csv"
66 | The list about RenderThread status in each frames . 67 |
68 | -"xxx_result.csv"
69 | The list about threads status in each frames . 70 |
71 | -"xxx_shader_compile.csv"
72 | The list that shows Shader Compiling. 73 |
74 | -"xxx_urp_gpu_sample.csv"
75 | The list about GPU for Universal RP. 76 |
77 | -"xxx_worker.csv"
78 | The list about workerThread status. 79 |
80 | - "xxx_jitInfos.csv"
81 | The list of callstack symbol infos.( should enable "Profiler.enableAllocationCallstacks"). 82 |
83 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2c6b5239c79879f4cb86ecc915d6d29f 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.utj.profilerreader", 3 | "displayName": "Profiler Reader", 4 | "version": "0.8.1-preview", 5 | "unity": "2018.1", 6 | "description": "read profiler log data package", 7 | "keywords": [ 8 | "profiler" 9 | ], 10 | "category": "profiler" 11 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fbdf912d26e1f004587772ecc03857cb 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------