Repository: unity3d-jp/ProfilerReader Branch: main Commit: 5d073dd7ebe1 Files: 71 Total size: 145.1 KB Directory structure: gitextract_s0y3s9kw/ ├── Editor/ │ ├── Analyzer/ │ │ ├── AnalyzeToTextbaseFileBase.cs │ │ ├── AnalyzeToTextbaseFileBase.cs.meta │ │ ├── AnalyzerUtil.cs │ │ ├── AnalyzerUtil.cs.meta │ │ ├── CsvStringGenerator.cs │ │ ├── CsvStringGenerator.cs.meta │ │ ├── IAnalyzeFileWriter.cs │ │ ├── IAnalyzeFileWriter.cs.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 │ │ ├── Impl.meta │ │ ├── ProfilingScope.cs │ │ └── ProfilingScope.cs.meta │ ├── Analyzer.meta │ ├── CUI/ │ │ ├── CUIInterface.cs │ │ └── CUIInterface.cs.meta │ ├── CUI.meta │ ├── GUI/ │ │ ├── AnalyzeToCsvWindow.cs │ │ ├── AnalyzeToCsvWindow.cs.meta │ │ ├── LogAnalyzeWindow.cs │ │ ├── LogAnalyzeWindow.cs.meta │ │ ├── language/ │ │ │ ├── LanguageEn.cs │ │ │ ├── LanguageEn.cs.meta │ │ │ ├── LanguageInterface.cs │ │ │ ├── LanguageInterface.cs.meta │ │ │ ├── LanguageJa.cs │ │ │ └── LanguageJa.cs.meta │ │ └── language.meta │ ├── GUI.meta │ ├── UTJProfileReader.dll.meta │ ├── Utj.ProfilerReader.Editor.asmdef │ ├── Utj.ProfilerReader.Editor.asmdef.meta │ ├── UtjProfilerInitializer.cs │ └── UtjProfilerInitializer.cs.meta ├── Editor.meta ├── LICENSE.md ├── LICENSE.md.meta ├── README.ja.md ├── README.ja.md.meta ├── README.md ├── README.md.meta ├── package.json └── package.json.meta ================================================ FILE CONTENTS ================================================ ================================================ FILE: Editor/Analyzer/AnalyzeToTextbaseFileBase.cs ================================================ using System.Collections; using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; namespace UTJ.ProfilerReader.Analyzer { public abstract class AnalyzeToTextbaseFileBase : IAnalyzeFileWriter { protected ProfilerLogFormat logFormat { get; private set; } protected uint logVersion { get; private set; } protected ushort logPlatform { get; private set; } protected abstract string FooterName { get; } protected string unityVersion { get; private set; } public void SetInfo(ProfilerLogFormat format, string unityVer, uint dataversion, ushort platform) { this.logFormat = format; this.logVersion = dataversion; this.logPlatform = platform; this.unityVersion = unityVer; } public abstract void CollectData(ProfilerFrameData frameData); protected abstract string GetResultText(); public void WriteResultFile(string logfile, string outputpath) { try { var path = System.IO.Path.Combine(outputpath, logfile.Replace(".", "_") + this.FooterName); string result = GetResultText(); System.IO.File.WriteAllText(path, result); } catch (System.Exception e) { ProfilerLogUtil.logErrorException(e); } } public void SetFileInfo(string logfile, string outputpath) { } } } ================================================ FILE: Editor/Analyzer/AnalyzeToTextbaseFileBase.cs.meta ================================================ fileFormatVersion: 2 guid: 55c111e5c73e93e45a21a05c4e5109db MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/AnalyzerUtil.cs ================================================ using System; using System.Collections.Generic; namespace UTJ.ProfilerReader.Analyzer { public class AnalyzerUtil { public static List CreateAnalyzerInterfaceObjects() { var types = GetInterfaceType(); return CreateInstanciateObjects(types); } private static List CreateInstanciateObjects(List types) where T : class { List ret = new List(); foreach (var t in types) { if (t.IsAbstract) { continue; } var inst = Activator.CreateInstance(t) as T; ret.Add(inst); } return ret; } public static List GetInterfaceType() { List ret = new List(); var domain = System.AppDomain.CurrentDomain; var assemblies = domain.GetAssemblies(); foreach (var assembly in assemblies) { var types = assembly.GetTypes(); foreach (var type in types) { var interfaces = type.GetInterfaces(); if (interfaces == null) { continue; } foreach (var interfacetype in interfaces) { if (interfacetype == typeof(T) && !type.IsAbstract) { ret.Add(type); } } } } return ret; } } } ================================================ FILE: Editor/Analyzer/AnalyzerUtil.cs.meta ================================================ fileFormatVersion: 2 guid: c4b017d20dfef334383db409d73c50ab MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/CsvStringGenerator.cs ================================================ using System.Text; namespace UTJ.ProfilerReader.Analyzer { public class CsvStringGenerator { private StringBuilder stringBuilder; public CsvStringGenerator() { stringBuilder = new StringBuilder(1024 * 1024); } public CsvStringGenerator(StringBuilder sb) { stringBuilder = sb; } public CsvStringGenerator AppendColumn(string val) { if( val == null) { val = ""; } if (val.Contains(",")) { val = val.Replace(',', '.'); } if (val.Contains("\n")) { val = val.Replace('\n', ' '); } stringBuilder.Append(val).Append(','); return this; } public CsvStringGenerator AppendColumn(string val,int idx,int length) { if (val == null) { stringBuilder.Append(","); return this; } if (val.Contains(",") ) { val = val.Replace(',', '.'); } if (val.Contains("\n")) { val = val.Replace('\n', ' '); } else { stringBuilder.Append(val, idx, length).Append(','); } return this; } public CsvStringGenerator AppendColumn(int val) { stringBuilder.Append(val).Append(','); return this; } public CsvStringGenerator AppendColumn(bool val) { stringBuilder.Append(val).Append(','); return this; } public CsvStringGenerator AppendColumn(float val) { stringBuilder.Append(val).Append(','); return this; } public CsvStringGenerator AppendColumn(ulong val) { stringBuilder.Append(val).Append(','); return this; } public CsvStringGenerator AppendColumnAsAddr(ulong val) { stringBuilder.Append("0x"); AppendAddrStr(stringBuilder, val,16).Append(','); return this; } public CsvStringGenerator NextRow() { stringBuilder.Append("\n"); return this; } public override string ToString() { return stringBuilder.ToString(); } private static char[] addrChars = new char[] { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; // AppendFormat("0x{0,0:X16}", val) <- allocate a lot of managed memory... public static StringBuilder AppendAddrStr(StringBuilder sb,ulong val,int num) { for (int i = num - 1; i >= 0; --i ) { ulong masked = (val >> (i*4) )& 0xf; sb.Append(addrChars[masked]); } return sb; } } } ================================================ FILE: Editor/Analyzer/CsvStringGenerator.cs.meta ================================================ fileFormatVersion: 2 guid: dc945cd84f5e43148bc02deadfd310b2 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/IAnalyzeFileWriter.cs ================================================ using System.Collections; using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; namespace UTJ.ProfilerReader.Analyzer { public enum ProfilerLogFormat { TypeData, TypeRaw, } public interface IAnalyzeFileWriter { void SetFileInfo(string logfilename, string outputpath); void SetInfo(ProfilerLogFormat logformat,string unityVersion, uint dataversion, ushort platform); void CollectData(ProfilerFrameData frameData); void WriteResultFile(string logfilaneme,string outputpath); } } ================================================ FILE: Editor/Analyzer/IAnalyzeFileWriter.cs.meta ================================================ fileFormatVersion: 2 guid: d90901940130ecf48a5862025c483e10 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/EngineFileOperateAnalyzeToFile.cs ================================================ using System.Collections.Generic; using System.Diagnostics; using System.Text; using UnityEngine.Profiling; using UTJ.ProfilerReader.BinaryData; using UTJ.ProfilerReader.BinaryData.Stats; namespace UTJ.ProfilerReader.Analyzer { public class EngineFileOperateAnalyzeToFile : AnalyzeToTextbaseFileBase { struct FileEvent { public string thread; public string eventStr; public int frameIdx; public string file; public ulong size; public long seekOffset; public int param; public ulong startTime; public float tm; } private List fileEvents = new List(2048); public override void CollectData(ProfilerFrameData frameData) { if( frameData == null ) { return; } HashSet doneList = new HashSet(); foreach( var thread in frameData.m_ThreadData) { if(thread.m_AllSamples == null) { continue; } foreach( var sample in thread.m_AllSamples) { if (!sample.sampleName.StartsWith("File.")) { continue; } if (sample.sampleName == "File.Open") { AddFileOpenCloseData(frameData.frameIndex, thread, sample); } else if (sample.sampleName == "File.Read") { AddFileReadData(frameData.frameIndex, thread, sample); } else if (sample.sampleName == "File.Seek") { AddFileSeekData(frameData.frameIndex, thread, sample); } else if (sample.sampleName == "File.Close") { AddFileOpenCloseData(frameData.frameIndex, thread, sample); } } } } private void SetupInfo(ref FileEvent evt,int idx, ThreadData thread, ProfilerSample sample) { evt.frameIdx = idx; evt.thread = thread.FullName; evt.startTime = sample.startTimeUS; evt.tm = sample.selfTimeUs / 1000.0f; evt.eventStr = sample.sampleName; } private void AddFileOpenCloseData(int idx, ThreadData thread, ProfilerSample sample) { FileEvent evt = new FileEvent(); SetupInfo(ref evt, idx, thread, sample); var metaData = sample.metadataValues; if (metaData != null) { try { if (metaData.Count > 0) { evt.file = metaData[0].convertedObject.ToString(); } } catch (System.Exception e) { ProfilerLogUtil.logErrorException(e); } } fileEvents.Add(evt); } private void AddFileSeekData(int idx, ThreadData thread, ProfilerSample sample) { FileEvent evt = new FileEvent(); SetupInfo(ref evt, idx, thread, sample); var metaData = sample.metadataValues; if (metaData != null) { try { if (metaData.Count > 0) { evt.file = metaData[0].convertedObject.ToString(); } if (metaData.Count > 1) { evt.seekOffset = (long)metaData[1].convertedObject; } if (metaData.Count > 2) { evt.param = (int)metaData[2].convertedObject; } } catch (System.Exception e) { ProfilerLogUtil.logErrorException(e); } } fileEvents.Add(evt); } private void AddFileReadData(int idx ,ThreadData thread, ProfilerSample sample) { FileEvent evt = new FileEvent(); SetupInfo(ref evt, idx, thread, sample); var metaData = sample.metadataValues; if (metaData != null ) { try { if (metaData.Count > 0) { evt.file = metaData[0].convertedObject.ToString(); } if (metaData.Count > 1) { evt.param = (int)metaData[1].convertedObject; } if (metaData.Count > 2) { evt.size = (ulong)metaData[2].convertedObject; } }catch(System.Exception e) { ProfilerLogUtil.logErrorException(e); } } fileEvents.Add(evt); } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); AppendHeaderToStringBuilder(csvStringGenerator); fileEvents.Sort((a, b) => { if( a.startTime > b.startTime) { return 1; } else if (a.startTime < b.startTime) { return -1; } return 0; }); foreach (var evt in fileEvents) { int filePathIdx = 0; int length = 0; if (evt.file != null) { filePathIdx = evt.file.LastIndexOf('/') +1; length = evt.file.Length; } csvStringGenerator.AppendColumn(evt.frameIdx); csvStringGenerator.AppendColumn(evt.file, filePathIdx, length - filePathIdx); csvStringGenerator.AppendColumn(evt.eventStr); csvStringGenerator.AppendColumn(evt.thread); csvStringGenerator.AppendColumn(evt.param); csvStringGenerator.AppendColumn(evt.size); csvStringGenerator.AppendColumn(evt.seekOffset); csvStringGenerator.AppendColumn(evt.tm); csvStringGenerator.AppendColumn(evt.file); csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } private void AppendHeaderToStringBuilder(CsvStringGenerator csvStringGenerator) { csvStringGenerator.AppendColumn("frameIdx"); csvStringGenerator.AppendColumn("file"); csvStringGenerator.AppendColumn("event"); csvStringGenerator.AppendColumn("thread"); csvStringGenerator.AppendColumn("param"); csvStringGenerator.AppendColumn("size"); csvStringGenerator.AppendColumn("seekOffset"); csvStringGenerator.AppendColumn("execTime"); csvStringGenerator.AppendColumn("fullPath"); csvStringGenerator.NextRow(); } protected override string FooterName { get { return "_engine_file_operate.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/EngineFileOperateAnalyzeToFile.cs.meta ================================================ fileFormatVersion: 2 guid: e15215876c0578b4aa9d055d04534599 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/GCAnalyzeToFile.cs ================================================ using System.Collections.Generic; using System.Diagnostics; using System.Text; using UnityEngine.Profiling; using UTJ.ProfilerReader.BinaryData; using UTJ.ProfilerReader.BinaryData.Stats; namespace UTJ.ProfilerReader.Analyzer { public class GCAnalyzeToFile : AnalyzeToTextbaseFileBase { class SampleKey : System.IEquatable { public string threadName; public string methodName; public string fullMethodName; public override int GetHashCode() { return threadName.GetHashCode()+ methodName.GetHashCode() ; } public bool Equals(SampleKey other) { return ((other.threadName == this.threadName) && (other.fullMethodName == this.fullMethodName)); } public SampleKey(string th,string method,string fullMethod) { threadName = th; methodName = method; fullMethodName = fullMethod; } } class GcInfo { public uint allocNum; public ulong allocAll; public uint allocMax = uint.MinValue; public uint allocMin = uint.MaxValue; } private Dictionary gcDitionary = new Dictionary(); private void AddData(string threadName,ProfilerSample sample,uint gcAlloc) { var key = new SampleKey(threadName,sample.sampleName, sample.fullSampleName); GcInfo data; if (!gcDitionary.TryGetValue(key,out data)) { data = new GcInfo(); gcDitionary.Add(key, data); } data.allocAll += gcAlloc; data.allocNum ++; data.allocMin = ProfilerLogUtil.Min(data.allocMin, gcAlloc); data.allocMax = ProfilerLogUtil.Max(data.allocMax, gcAlloc); } public override void CollectData(ProfilerFrameData frameData) { if( frameData == null ) { return; } HashSet doneList = new HashSet(); foreach( var thread in frameData.m_ThreadData) { if(thread.m_AllSamples == null) { continue; } foreach( var sample in thread.m_AllSamples) { if(sample != null && sample.parent != null && sample.sampleName == "GC.Alloc") { var parent = sample.parent; if (!doneList.Contains(parent)) { AddData(thread.FullName, parent, parent.GetSelfChildGcAlloc()); doneList.Add(parent); } } } } } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); AppendHeaderToStringBuilder(csvStringGenerator); foreach( var kvs in gcDitionary) { csvStringGenerator.AppendColumn(kvs.Key.threadName); csvStringGenerator.AppendColumn(kvs.Key.methodName); csvStringGenerator.AppendColumn(kvs.Key.fullMethodName); csvStringGenerator.AppendColumn(kvs.Value.allocNum); csvStringGenerator.AppendColumn(kvs.Value.allocAll); csvStringGenerator.AppendColumn(kvs.Value.allocAll / kvs.Value.allocNum); csvStringGenerator.AppendColumn(kvs.Value.allocMin); csvStringGenerator.AppendColumn(kvs.Value.allocMax); csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } private void AppendHeaderToStringBuilder(CsvStringGenerator csvStringGenerator) { csvStringGenerator.AppendColumn("thread"); // Total csvStringGenerator.AppendColumn("SampleName"); csvStringGenerator.AppendColumn("FullName"); csvStringGenerator.AppendColumn("calls"); csvStringGenerator.AppendColumn("all(byte)"); csvStringGenerator.AppendColumn("average(byte)"); csvStringGenerator.AppendColumn("min(byte)"); csvStringGenerator.AppendColumn("max(byte)"); csvStringGenerator.NextRow(); } protected override string FooterName { get { return "_gc_result.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/GCAnalyzeToFile.cs.meta ================================================ fileFormatVersion: 2 guid: 95d7f661b5a908c4ea5067c04e49ef60 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/GcCallStackInfoAnalyzeToFile.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Text; using UTJ.ProfilerReader.BinaryData; using UnityEngine.Profiling; using UTJ.ProfilerReader.BinaryData.Stats; using UTJ.ProfilerReader.RawData.Protocol; using UnityEngine.Playables; namespace UTJ.ProfilerReader.Analyzer { public class GcCallStackInfoAnalyzeToFile : AnalyzeToTextbaseFileBase { struct SampleKey : System.IEquatable { public string threadName; public string methodName; public string fullMethodName; public string callStackName; private int hashCodeCache; public override int GetHashCode() { return this.hashCodeCache; } public bool Equals(SampleKey other) { return ( (other.callStackName == this.callStackName) && (other.fullMethodName == this.fullMethodName) && (other.threadName == this.threadName) ); } public SampleKey(string th,string method,string fullMethod,string callStack) { threadName = th; methodName = method; fullMethodName = fullMethod; callStackName = callStack; this.hashCodeCache = threadName.GetHashCode() + fullMethodName.GetHashCode(); } } class GcInfo { public uint allocNum; public ulong allocAll; public uint allocMax = uint.MinValue; public uint allocMin = uint.MaxValue; } #if UTJ_CHECK private static readonly CustomSampler _collectDataSampler = CustomSampler.Create("GcCallStackInfoAnalyzeToFile.CollectData"); private static readonly CustomSampler _addDataSampler = CustomSampler.Create("GcCallStackInfoAnalyzeToFile.AddData"); private static readonly CustomSampler _callStackSampler = CustomSampler.Create("GcCallStackInfoAnalyzeToFile.GetCallStack"); private static readonly CustomSampler _gcAllocGetSampler = CustomSampler.Create("GcCallStackInfoAnalyzeToFile.GetSelfChildGcAlloc"); #endif private Dictionary gcDitionary = new Dictionary(); private StringBuilder stringBuilder = new StringBuilder(1024); private void AddData(string threadName,ProfilerSample sample,string callstack,uint gcAlloc) { #if UTJ_CHECK using (new ProfilingScope(_addDataSampler)) #endif { var key = new SampleKey(threadName, sample.sampleName, sample.fullSampleName, callstack); GcInfo data; if (!gcDitionary.TryGetValue(key, out data)) { data = new GcInfo(); gcDitionary.Add(key, data); } data.allocAll += gcAlloc; data.allocNum++; data.allocMin = ProfilerLogUtil.Min(data.allocMin, gcAlloc); data.allocMax = ProfilerLogUtil.Max(data.allocMax, gcAlloc); } } public override void CollectData(ProfilerFrameData frameData) { #if UTJ_CHECK using (new ProfilingScope(_collectDataSampler)) #endif { if (frameData == null) { return; } FindGCAllocMarkerId(frameData); if (useMakerId) { foreach (var thread in frameData.m_ThreadData) { if (thread.m_AllSamples == null) { continue; } foreach (var sample in thread.m_AllSamples) { if (sample != null && sample.parent != null && sample.makerId == this.gcAllocMakerId) { uint selfGcAlloc = 0; var parent = sample.parent; #if UTJ_CHECK using (new ProfilingScope(_gcAllocGetSampler)) #endif { selfGcAlloc = sample.currenGcAlloc; } AddData(thread.FullName, parent, GetCallStackInfo(frameData, sample), selfGcAlloc); } } } } else { foreach (var thread in frameData.m_ThreadData) { if (thread.m_AllSamples == null) { continue; } foreach (var sample in thread.m_AllSamples) { if (sample != null && sample.parent != null && sample.sampleName == "GC.Alloc") { var parent = sample.parent; AddData(thread.FullName, parent, GetCallStackInfo(frameData, sample), sample.currenGcAlloc); } } } } } } private bool useMakerId = false; private bool foundMakerId = false; private uint gcAllocMakerId = 0xFFFFFFFF; private void FindGCAllocMarkerId(ProfilerFrameData frameData) { if (foundMakerId) { return; } foreach (var thread in frameData.m_ThreadData) { if (thread.m_AllSamples == null) { continue; } foreach (var sample in thread.m_AllSamples) { if (sample != null && sample.parent != null && sample.sampleName == "GC.Alloc") { gcAllocMakerId = sample.makerId; foundMakerId = true; if(gcAllocMakerId != 0 && gcAllocMakerId != 0xFFFFFFFF) { useMakerId = true; } break; } } } } private string GetCallStackInfo( ProfilerFrameData frameData,ProfilerSample profilerSample) { #if UTJ_CHECK using (new ProfilingScope(_callStackSampler)) #endif { if (profilerSample == null) { return ""; } var callStackInfo = profilerSample.callStackInfo; if (callStackInfo == null) { return ""; } stringBuilder.Length = 0; int length = callStackInfo.stack.Length; bool isAlreadyAdd = false; for (int i = length - 1; i >= 0; --i) { var info = frameData.FindJitInfoFromAddr(callStackInfo.stack[i]); if (info == null) { continue; } if (isAlreadyAdd) { stringBuilder.Append("->"); } stringBuilder.Append("["); CsvStringGenerator.AppendAddrStr(stringBuilder, info.codeAddr, 16).Append("]"); stringBuilder.Append(info.name); isAlreadyAdd = true; } return stringBuilder.ToString(); } } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); AppendHeaderToStringBuilder(csvStringGenerator); foreach( var kvs in gcDitionary) { csvStringGenerator.AppendColumn(kvs.Key.threadName); csvStringGenerator.AppendColumn(kvs.Key.methodName); csvStringGenerator.AppendColumn(kvs.Key.fullMethodName); csvStringGenerator.AppendColumn(kvs.Key.callStackName); csvStringGenerator.AppendColumn(kvs.Value.allocNum); csvStringGenerator.AppendColumn(kvs.Value.allocAll); csvStringGenerator.AppendColumn(kvs.Value.allocAll / kvs.Value.allocNum); csvStringGenerator.AppendColumn(kvs.Value.allocMin); csvStringGenerator.AppendColumn(kvs.Value.allocMax); csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } private void AppendHeaderToStringBuilder(CsvStringGenerator csvStringGenerator) { csvStringGenerator.AppendColumn("thread"); // Total csvStringGenerator.AppendColumn("SampleName"); csvStringGenerator.AppendColumn("FullName"); csvStringGenerator.AppendColumn("CallStack"); csvStringGenerator.AppendColumn("calls"); csvStringGenerator.AppendColumn("all(byte)"); csvStringGenerator.AppendColumn("average(byte)"); csvStringGenerator.AppendColumn("min(byte)"); csvStringGenerator.AppendColumn("max(byte)"); csvStringGenerator.NextRow(); } protected override string FooterName { get { return "_gc_detail.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/GcCallStackInfoAnalyzeToFile.cs.meta ================================================ fileFormatVersion: 2 guid: 926a0e3638071e54e981f2dd25e21d82 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/GpuSampleToFile.cs ================================================ using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; using System.Text; using UTJ.ProfilerReader.BinaryData.Thread; using UTJ.ProfilerReader.RawData.Protocol; namespace UTJ.ProfilerReader.Analyzer { public class GPUSampleToFile : AnalyzeToTextbaseFileBase { private struct GpuTimeInfo { public int time; public int count; } private class FrameGpuTime { public int frameIdx; public Dictionary gpuTimeByCategory; public void AddGpuSample(GPUTime gpuTime) { if(gpuTimeByCategory == null) { gpuTimeByCategory = new Dictionary(); } GpuTimeInfo time; if( gpuTimeByCategory.TryGetValue( gpuTime.gpuSection,out time)) { time.time += gpuTime.gpuTimeInMicroSec; time.count += 1; gpuTimeByCategory[gpuTime.gpuSection] = time; } else { time = new GpuTimeInfo { time = gpuTime.gpuTimeInMicroSec, count = 1 }; gpuTimeByCategory.Add(gpuTime.gpuSection, time); } } } private List frameGpuTimes = new List(); public override void CollectData(ProfilerFrameData frameData) { FrameGpuTime frameGpuTime = new FrameGpuTime(); frameGpuTime.frameIdx = frameData.frameIndex; foreach ( var threadData in frameData.m_ThreadData){ AddGpuSampleByThread(threadData, frameGpuTime); } this.frameGpuTimes.Add(frameGpuTime); } private void AddGpuSampleByThread(ThreadData thread, FrameGpuTime frameGpuTime) { if( thread == null) { return; } if( thread.m_GPUTimeSamples == null) { return; } foreach( var gpuSample in thread.m_GPUTimeSamples) { frameGpuTime.AddGpuSample(gpuSample); } } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); csvStringGenerator.AppendColumn("frameIdx"); for (int i = 0; i < GPUTime.SECTION_NUM; ++i) { csvStringGenerator.AppendColumn(((GPUTime.GpuSection)i).ToString() + "(ms)"); } csvStringGenerator.AppendColumn("callNum"); for (int i = 0; i < GPUTime.SECTION_NUM; ++i) { csvStringGenerator.AppendColumn(((GPUTime.GpuSection)i).ToString() + "(calls)"); } csvStringGenerator.NextRow(); foreach( var gpuFrame in frameGpuTimes) { if (gpuFrame.gpuTimeByCategory == null) { continue; } csvStringGenerator.AppendColumn(gpuFrame.frameIdx); for( int i = 0; i < GPUTime.SECTION_NUM; ++i) { GpuTimeInfo val ; if(gpuFrame.gpuTimeByCategory.TryGetValue(i,out val)) { csvStringGenerator.AppendColumn( (float)val.time / 1000.0f); } else { csvStringGenerator.AppendColumn(0); } } csvStringGenerator.AppendColumn(""); for (int i = 0; i < GPUTime.SECTION_NUM; ++i) { GpuTimeInfo val; if (gpuFrame.gpuTimeByCategory.TryGetValue(i, out val)) { csvStringGenerator.AppendColumn(val.count); } else { csvStringGenerator.AppendColumn(0); } } csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } protected override string FooterName { get { return "_gpu_sample.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/GpuSampleToFile.cs.meta ================================================ fileFormatVersion: 2 guid: 13b9f169806de6541af34024f978dd04 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/JitInfoAnalyzeToFile.cs ================================================ using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; using UTJ.ProfilerReader.BinaryData.Stats; namespace UTJ.ProfilerReader.Analyzer { public class JitInfoAnalyzeToFile : AnalyzeToTextbaseFileBase { private Dictionary jitInfoDict = new Dictionary(); public override void CollectData(ProfilerFrameData frameData) { if( frameData == null ) { return; } var jitInfos = frameData.m_jitInfos; if( jitInfos == null) { return; } foreach( var jitInfo in jitInfos) { if(jitInfo == null) { continue; } if(!jitInfoDict.ContainsKey(jitInfo.codeAddr)) { jitInfoDict.Add(jitInfo.codeAddr, jitInfo); } } } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); AppendHeaderToStringBuilder(csvStringGenerator); List sortedJitInfo = new List(this.jitInfoDict.Values); sortedJitInfo.Sort(new JitInfo.CompareByAddr() ); foreach( var jitInfo in sortedJitInfo) { string name = jitInfo.name; string sourceFileName = jitInfo.sourceFileName; csvStringGenerator.AppendColumnAsAddr(jitInfo.codeAddr); csvStringGenerator.AppendColumn(jitInfo.size); csvStringGenerator.AppendColumn(name); csvStringGenerator.AppendColumn(sourceFileName); csvStringGenerator.AppendColumn(jitInfo.sourceFileLine); csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } private void AppendHeaderToStringBuilder(CsvStringGenerator csvStringGenerator) { csvStringGenerator.AppendColumn("Address"); // Total csvStringGenerator.AppendColumn("Code Size"); csvStringGenerator.AppendColumn("FunctionName"); csvStringGenerator.AppendColumn("SourceFile"); csvStringGenerator.AppendColumn("SourceLine"); csvStringGenerator.NextRow(); } protected override string FooterName { get { return "_jitInfos.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/JitInfoAnalyzeToFile.cs.meta ================================================ fileFormatVersion: 2 guid: e62bb9e4f2af06a4cb9bea7b4bbb7bd2 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/MainThreadAnalyzeToFile.cs ================================================ using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; namespace UTJ.ProfilerReader.Analyzer { public class MainThreadAnalyzeToFile : AnalyzeToTextbaseFileBase { private class SampleData { public string fullName; public string sampleName; public float totalSelfMsec = 0.0f; public float selfMinMSec = float.MaxValue; public float selfMaxMsec = 0.0f; public float totalExecMsec = 0.0f; public float execMinMSec = float.MaxValue; public float execMaxMsec = 0.0f; public int callNum = 0; public string categoryName; public SampleData(string fname, string name,string category) { this.sampleName = name; this.fullName = fname; this.categoryName = category; } public void Called(float selfMsec, float execMsec) { selfMinMSec = ProfilerLogUtil.Min(selfMinMSec, selfMsec); selfMaxMsec = ProfilerLogUtil.Max(selfMaxMsec, selfMsec); totalSelfMsec += selfMsec; execMinMSec = ProfilerLogUtil.Min(execMinMSec, execMsec); execMaxMsec = ProfilerLogUtil.Max(execMaxMsec, execMsec); totalExecMsec += execMsec; ++callNum; } } private Dictionary samples = new Dictionary(); private int frameNum = 0; public override void CollectData(ProfilerFrameData frameData) { // 特別枠で frameDataのCPU時間を追加 // 同一フレーム内に同じスレッド名が複数できるので… Dictionary threadNameCounter = new Dictionary(8); foreach (var thread in frameData.m_ThreadData) { if (thread.IsMainThread) { CollectThread(frameData,thread); } } ++frameNum; } private void CollectThread(ProfilerFrameData frameData,ThreadData thread) { if (thread == null || thread.m_AllSamples == null) { return; } foreach (var sample in thread.m_AllSamples) { if (sample.parent == null) { CollectFromNamedChildren(frameData,sample); } } } private void CollectFromNamedChildren(ProfilerFrameData frameData,ProfilerSample sample) { if (!string.IsNullOrEmpty(sample.sampleName)) { string category = ProtocolData.GetCategory(frameData,unityVersion, sample.group); AddSampleData(sample.fullSampleName, sample.sampleName, category, sample.selfTimeUs / 1000.0f, sample.timeUS / 1000.0f); } if (sample.children != null) { foreach (var child in sample.children) { CollectFromNamedChildren(frameData,child); } } return; } private void AddSampleData(string fullName, string sampleName,string categoryName, float selfMsec, float execMsec) { SampleData sampleData = null; if (selfMsec < 0.0f) { ProfilerLogUtil.logErrorString("minus Param " + sampleName + ":" + selfMsec + ":" + execMsec); return; } if (selfMsec > 1000.0f * 50.0f) { ProfilerLogUtil.logErrorString("minus Param " + sampleName + ":" + selfMsec + ":" + execMsec); return; } if (!this.samples.TryGetValue(fullName, out sampleData)) { sampleData = new SampleData(fullName, sampleName,categoryName); this.samples.Add(fullName, sampleData); } sampleData.Called(selfMsec, execMsec); } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); csvStringGenerator.AppendColumn("name").AppendColumn("fullname").AppendColumn("category").AppendColumn("callNum"). AppendColumn("self").AppendColumn("sum(msec)").AppendColumn("perFrame(msec)").AppendColumn("min(msec)").AppendColumn("max(msec)"). AppendColumn("total").AppendColumn("sum(msec)").AppendColumn("perFrame(msec)").AppendColumn("min(msec)").AppendColumn("max(msec)"). NextRow(); var sampleDataList = new List(samples.Values); sampleDataList.Sort((a, b) => { if (a.totalSelfMsec > b.totalSelfMsec) { return -1; }else if (a.totalSelfMsec < b.totalSelfMsec) { return 1; } return 0; }); foreach (var sampleData in sampleDataList) { csvStringGenerator.AppendColumn(sampleData.sampleName). AppendColumn(sampleData.fullName). AppendColumn(sampleData.categoryName). AppendColumn(sampleData.callNum).AppendColumn(""); csvStringGenerator.AppendColumn(sampleData.totalSelfMsec). AppendColumn(sampleData.totalSelfMsec / frameNum). AppendColumn(sampleData.selfMinMSec). AppendColumn(sampleData.selfMaxMsec).AppendColumn(""); csvStringGenerator.AppendColumn(sampleData.totalExecMsec). AppendColumn(sampleData.totalExecMsec / frameNum). AppendColumn(sampleData.execMinMSec). AppendColumn(sampleData.execMaxMsec); csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } protected override string FooterName { get { return "_main_self.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/MainThreadAnalyzeToFile.cs.meta ================================================ fileFormatVersion: 2 guid: 180ee3acbe5f93944998f208ebc9db00 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/MainThreadCategoryAnalyzeToFile.cs ================================================ using System.Collections; using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; namespace UTJ.ProfilerReader.Analyzer { public class MainThreadCategoryAnalyzeToFile : AnalyzeToTextbaseFileBase { private class FrameByCategory { public Dictionary frameData; public int frameIdx; public void AddData(string category,float msec) { if(frameData == null) { frameData = new Dictionary(); } float val; if( frameData.TryGetValue(category,out val) ){ frameData[category] = val + msec; } else { frameData.Add( category , msec ); } } public void AppendCsv(CsvStringGenerator csvStringGenerator, List categoriesStr) { foreach (var category in categoriesStr) { float val; if (!frameData.TryGetValue(category, out val)) { val = 0.0f; } csvStringGenerator.AppendColumn(val); } } } private Dictionary categoryDictionary; private List categoriesStr; private List frames = new List(); private void CollectThread(ThreadData thread,FrameByCategory frameByCategory) { if (thread.m_AllSamples == null) { return; } foreach (var sample in thread.m_AllSamples) { string category = null; if(categoryDictionary.TryGetValue(sample.group,out category) ){ frameByCategory.AddData(categoriesStr[sample.group], sample.selfTimeUs * 0.001f); } } } public override void CollectData(ProfilerFrameData frameData) { // Categoryのセットアップ SetupCategories(frameData); FrameByCategory frameByCategory = new FrameByCategory(); frameByCategory.frameIdx = frameData.frameIndex; // 特別枠で frameDataのCPU時間を追加 // 同一フレーム内に同じスレッド名が複数できるので… Dictionary threadNameCounter = new Dictionary(8); foreach (var thread in frameData.m_ThreadData) { if (thread.IsMainThread) { CollectThread(thread, frameByCategory); } } this.frames.Add(frameByCategory); } private void SetupCategories(ProfilerFrameData frameData) { if(this.categoryDictionary != null) { return; } this.categoriesStr = new List(); this.categoryDictionary = new Dictionary(); var categories = ProtocolData.GetCategories(frameData, this.unityVersion); foreach( var item in categories) { string name = item.Value.name; int idx = (int)item.Value.categoryId; if (!categoryDictionary.ContainsKey(idx)) { categoriesStr.Add(name); categoryDictionary.Add(idx,name); } } } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); csvStringGenerator.AppendColumn("frameIdx"); foreach( var str in categoriesStr) { csvStringGenerator.AppendColumn(str+"(msec)"); } csvStringGenerator.NextRow(); foreach( var frame in frames) { csvStringGenerator.AppendColumn(frame.frameIdx); frame.AppendCsv(csvStringGenerator,this.categoriesStr); csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } protected override string FooterName { get { return "_category_mainThread_frame.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/MainThreadCategoryAnalyzeToFile.cs.meta ================================================ fileFormatVersion: 2 guid: b29edfc38841eb247a4efcfbb71c9123 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/MemoryAnalyzeToFile.cs ================================================ using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; using UTJ.ProfilerReader.BinaryData.Stats; namespace UTJ.ProfilerReader.Analyzer { public class MemoryAnalyzeToFile : AnalyzeToTextbaseFileBase { private List frameIdxList = new List(); private List memoryStatsList = new List(); public override void CollectData(ProfilerFrameData frameData) { if( frameData == null || frameData.allStats == null || frameData.allStats.memoryStats == null) { return; } frameIdxList.Add(frameData.frameIndex); memoryStatsList.Add( frameData.allStats.memoryStats ); } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); AppendHeaderToStringBuilder(csvStringGenerator); for ( int i = 0; i < memoryStatsList.Count; ++i) { MemoryStats memoryStats = memoryStatsList[i]; csvStringGenerator.AppendColumn(frameIdxList[i]); csvStringGenerator.AppendColumn(""); // Used Memory csvStringGenerator.AppendColumn(memoryStats.bytesUsedTotal); csvStringGenerator.AppendColumn(memoryStats.bytesUsedUnity); csvStringGenerator.AppendColumn(memoryStats.bytesUsedMono); csvStringGenerator.AppendColumn(memoryStats.bytesUsedGFX); csvStringGenerator.AppendColumn(memoryStats.bytesUsedFMOD); csvStringGenerator.AppendColumn(memoryStats.bytesUsedVideo); csvStringGenerator.AppendColumn(memoryStats.bytesUsedProfiler); csvStringGenerator.AppendColumn(""); // Reserved Memory csvStringGenerator.AppendColumn(memoryStats.bytesReservedTotal); csvStringGenerator.AppendColumn(memoryStats.bytesReservedUnity); csvStringGenerator.AppendColumn(memoryStats.bytesReservedMono); csvStringGenerator.AppendColumn(memoryStats.bytesReservedFMOD); csvStringGenerator.AppendColumn(memoryStats.bytesReservedVideo); csvStringGenerator.AppendColumn(memoryStats.bytesReservedProfiler); csvStringGenerator.AppendColumn(""); // by Assets csvStringGenerator.AppendColumn(memoryStats.textureCount); csvStringGenerator.AppendColumn(memoryStats.textureBytes); csvStringGenerator.AppendColumn(memoryStats.meshCount); csvStringGenerator.AppendColumn(memoryStats.meshBytes); csvStringGenerator.AppendColumn(memoryStats.materialCount); csvStringGenerator.AppendColumn(memoryStats.materialBytes); csvStringGenerator.AppendColumn(memoryStats.audioCount); csvStringGenerator.AppendColumn(memoryStats.audioBytes); csvStringGenerator.AppendColumn(memoryStats.assetCount); csvStringGenerator.AppendColumn(memoryStats.gameObjectCount); csvStringGenerator.AppendColumn(memoryStats.sceneObjectCount); csvStringGenerator.AppendColumn(memoryStats.totalObjectsCount); csvStringGenerator.AppendColumn(""); // GC csvStringGenerator.AppendColumn(memoryStats.frameGCAllocCount); csvStringGenerator.AppendColumn(memoryStats.frameGCAllocBytes); csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } private void AppendHeaderToStringBuilder(CsvStringGenerator csvStringGenerator) { csvStringGenerator.AppendColumn("frameIdx"); csvStringGenerator.AppendColumn("UsedMemory"); // Used Memory csvStringGenerator.AppendColumn("bytesUsedTotal"); csvStringGenerator.AppendColumn("bytesUsedUnity"); csvStringGenerator.AppendColumn("bytesUsedMono"); csvStringGenerator.AppendColumn("bytesUsedGFX"); csvStringGenerator.AppendColumn("bytesUsedFMOD"); csvStringGenerator.AppendColumn("bytesUsedVideo"); csvStringGenerator.AppendColumn("bytesUsedProfiler"); csvStringGenerator.AppendColumn("ReservedMemory"); // Reserved Memory csvStringGenerator.AppendColumn("bytesReservedTotal"); csvStringGenerator.AppendColumn("bytesReservedUnity"); csvStringGenerator.AppendColumn("bytesReservedMono"); csvStringGenerator.AppendColumn("bytesReservedFMOD"); csvStringGenerator.AppendColumn("bytesReservedVideo"); csvStringGenerator.AppendColumn("bytesReservedProfiler"); csvStringGenerator.AppendColumn("AssetUsage"); // by Assets csvStringGenerator.AppendColumn("textureCount"); csvStringGenerator.AppendColumn("textureBytes"); csvStringGenerator.AppendColumn("meshCount"); csvStringGenerator.AppendColumn("meshBytes"); csvStringGenerator.AppendColumn("meshCount"); csvStringGenerator.AppendColumn("meshBytes"); csvStringGenerator.AppendColumn("materialCount"); csvStringGenerator.AppendColumn("materialBytes"); csvStringGenerator.AppendColumn("audioCount"); csvStringGenerator.AppendColumn("audioBytes"); csvStringGenerator.AppendColumn("assetCount"); csvStringGenerator.AppendColumn("gameObjectCount"); csvStringGenerator.AppendColumn("sceneObjectCount"); csvStringGenerator.AppendColumn("totalObjectsCount"); csvStringGenerator.AppendColumn("GC"); // GC csvStringGenerator.AppendColumn("frameGCAllocCount"); csvStringGenerator.AppendColumn("frameGCAllocBytes"); csvStringGenerator.NextRow(); } protected override string FooterName { get { return "_memory.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/MemoryAnalyzeToFile.cs.meta ================================================ fileFormatVersion: 2 guid: 615d3f2b68411c54d9571d4b44071800 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/RenderThreadToFile.cs ================================================ using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; using System.Text; namespace UTJ.ProfilerReader.Analyzer { public class RenderThreadToFile : AnalyzeToTextbaseFileBase { private struct CameraRenderData { public float renderTime; public float updateDepth; public float opaque; public float transparent; public float imageEffect; public void AppendToStringBuilder(CsvStringGenerator csvStringGenerator) { csvStringGenerator.AppendColumn(renderTime); csvStringGenerator.AppendColumn(updateDepth); csvStringGenerator.AppendColumn(opaque); csvStringGenerator.AppendColumn(transparent); csvStringGenerator.AppendColumn(imageEffect); } } private class FrameRenderingData { public int frameIdx; public float processCommandsTime; public float waitForCommandsTime; public float scheduleGeometryJobTime; public int scheduleGeometryJobNum; public float presentFrameTime; public float guiRepaint; public List cameraRenders = new List(8); public void AppendToCsvGenerator(CsvStringGenerator csvStringGenerator) { csvStringGenerator.AppendColumn(frameIdx); csvStringGenerator.AppendColumn(processCommandsTime); csvStringGenerator.AppendColumn(waitForCommandsTime); csvStringGenerator.AppendColumn(scheduleGeometryJobTime); csvStringGenerator.AppendColumn(scheduleGeometryJobNum); csvStringGenerator.AppendColumn(presentFrameTime); csvStringGenerator.AppendColumn(guiRepaint); csvStringGenerator.AppendColumn( cameraRenders.Count); foreach (var cameraRender in cameraRenders) { csvStringGenerator.AppendColumn(""); cameraRender.AppendToStringBuilder(csvStringGenerator); } } } private List frameRenderingDatas = new List(512); private int maxCameraNum = 0; public override void CollectData(ProfilerFrameData frameData) { foreach( var threadData in frameData.m_ThreadData){ if(threadData.m_ThreadName == "Render Thread") { CollectRenderThreadData(frameData.frameIndex, threadData); } } } private void CollectRenderThreadData(int frameIdx,ThreadData threadData) { if(threadData.m_AllSamples == null) { return; } FrameRenderingData frameRenderingData = new FrameRenderingData(); frameRenderingData.frameIdx = frameIdx; frameRenderingData.processCommandsTime = threadData.m_AllSamples[0].timeUS / 1000.0f; int cameraNum = 0; foreach ( var sample in threadData.m_AllSamples) { switch(sample.sampleName ){ case "Camera.Render": { var cameraRender = CollectCameraRenderData(sample); frameRenderingData.cameraRenders.Add(cameraRender); ++cameraNum; } break; case "Gfx.WaitForCommands": frameRenderingData.waitForCommandsTime += sample.timeUS / 1000.0f; break; case "Gfx.PresentFrame": frameRenderingData.presentFrameTime += sample.timeUS / 1000.0f; break; case "ScheduleGeometryJobs": frameRenderingData.scheduleGeometryJobTime += sample.timeUS / 1000.0f; frameRenderingData.scheduleGeometryJobNum += 1; break; case "GUIRepaint": frameRenderingData.guiRepaint += sample.timeUS / 1000.0f; break; } } if(maxCameraNum < cameraNum) { maxCameraNum = cameraNum; } frameRenderingDatas.Add(frameRenderingData); } private CameraRenderData CollectCameraRenderData( ProfilerSample profilerSample) { CameraRenderData renderData = new CameraRenderData(); renderData.renderTime = profilerSample.timeUS / 1000.0f; VisitChildren(profilerSample, (sample) => { switch(sample.sampleName){ case "UpdateDepthTexture": renderData.updateDepth = sample.timeUS / 1000.0f; return true; case "Render.OpaqueGeometry": renderData.opaque = sample.timeUS / 1000.0f; return true; case "Render.TransparentGeometry": renderData.transparent = sample.timeUS / 1000.0f; return true; case "Camera.ImageEffects": renderData.imageEffect = sample.timeUS / 1000.0f; return true; } return false; }); return renderData; } private List FindChildren(ProfilerSample profilerSample,string name) { List results = new List(); return results; } private void VisitChildren(ProfilerSample sample, System.Func filter) { if( sample == null) { return; } if( filter(sample)) { return; } if( sample.children == null) { return; } foreach( var child in sample.children) { VisitChildren(child, filter); } } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); csvStringGenerator.AppendColumn("frameIdx").AppendColumn("processCommandsTime"). AppendColumn("waitForCommandsTime").AppendColumn("scheduleGeometryJobTime").AppendColumn("scheduleGeometryJobNum").AppendColumn("presentFrameTime").AppendColumn("guiRepaint").AppendColumn("cameraRenders"); for (int i = 0; i < maxCameraNum; ++i) { csvStringGenerator.AppendColumn("Camera"+i); csvStringGenerator.AppendColumn("renderTime").AppendColumn("updateDepth").AppendColumn("opaque").AppendColumn("transparent").AppendColumn("imageEffect"); } csvStringGenerator.NextRow(); foreach (var frameRenderingData in this.frameRenderingDatas) { frameRenderingData.AppendToCsvGenerator(csvStringGenerator); csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } protected override string FooterName { get { return "_renderthread.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/RenderThreadToFile.cs.meta ================================================ fileFormatVersion: 2 guid: ea57c279a8d206a4484872add86970c8 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/RenderingAnalyzeToFile.cs ================================================ using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; using UTJ.ProfilerReader.BinaryData.Stats; namespace UTJ.ProfilerReader.Analyzer { public class RenderingAnalyzeToFile : AnalyzeToTextbaseFileBase { const string FrameWholeDataSpecialKey = "CPUTotal"; private List frameIdxList = new List(); private List drawStatsList = new List(); public override void CollectData(ProfilerFrameData frameData) { if( frameData == null || frameData.allStats == null || frameData.allStats.drawStats == null) { return; } frameIdxList.Add(frameData.frameIndex); drawStatsList.Add(frameData.allStats.drawStats); } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); AppendHeaderToStringBuilder(csvStringGenerator); for (int i = 0; i < drawStatsList.Count; ++i) { DrawStats drawStats = drawStatsList[i]; csvStringGenerator.AppendColumn(frameIdxList[i]); csvStringGenerator.AppendColumn(""); // Total csvStringGenerator.AppendColumn(drawStats.setPassCalls); csvStringGenerator.AppendColumn(drawStats.drawCalls); csvStringGenerator.AppendColumn(drawStats.batches); csvStringGenerator.AppendColumn(drawStats.triangles); csvStringGenerator.AppendColumn(drawStats.vertices); csvStringGenerator.AppendColumn(""); // DynamicBatching csvStringGenerator.AppendColumn(drawStats.dynamicBatchedDrawCalls); csvStringGenerator.AppendColumn(drawStats.dynamicBatchedTriangles); csvStringGenerator.AppendColumn(drawStats.dynamicBatchedTriangles); csvStringGenerator.AppendColumn(drawStats.dynamicBatchedVertices); csvStringGenerator.AppendColumn(""); // static batching csvStringGenerator.AppendColumn(drawStats.staticBatchedDrawCalls); csvStringGenerator.AppendColumn(drawStats.staticBatchedTriangles); csvStringGenerator.AppendColumn(drawStats.staticBatchedTriangles); csvStringGenerator.AppendColumn(drawStats.staticBatchedVertices); // instancing csvStringGenerator.AppendColumn(""); csvStringGenerator.AppendColumn(drawStats.hasInstancing); csvStringGenerator.AppendColumn(drawStats.instancedBatchedDrawCalls); csvStringGenerator.AppendColumn(drawStats.instancedBatches); csvStringGenerator.AppendColumn(drawStats.instancedTriangles); csvStringGenerator.AppendColumn(drawStats.instancedVertices); //screen Info csvStringGenerator.AppendColumn(""); csvStringGenerator.AppendColumn(drawStats.screenWidth); csvStringGenerator.AppendColumn(drawStats.screenHeight); csvStringGenerator.AppendColumn(drawStats.screenBytes); // RenderTexture Info csvStringGenerator.AppendColumn(""); csvStringGenerator.AppendColumn(drawStats.renderTextureCount); csvStringGenerator.AppendColumn(drawStats.renderTextureBytes); csvStringGenerator.AppendColumn(drawStats.renderTextureStateChanges); // SkinnedMesh csvStringGenerator.AppendColumn(""); csvStringGenerator.AppendColumn(drawStats.visibleSkinnedMeshes); // etc... csvStringGenerator.AppendColumn(""); csvStringGenerator.AppendColumn(drawStats.totalAvailableVRamMBytes); csvStringGenerator.AppendColumn(drawStats.vboTotal); csvStringGenerator.AppendColumn(drawStats.vboUploads); csvStringGenerator.AppendColumn(drawStats.ibUploads); csvStringGenerator.AppendColumn(drawStats.shadowCasters); csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } private void AppendHeaderToStringBuilder(CsvStringGenerator csvStringGenerator) { csvStringGenerator.AppendColumn("frameIdx"); // Total csvStringGenerator.AppendColumn("Total"); csvStringGenerator.AppendColumn("setPassCalls"); csvStringGenerator.AppendColumn("drawCalls"); csvStringGenerator.AppendColumn("batches"); csvStringGenerator.AppendColumn("triangles"); csvStringGenerator.AppendColumn("vertices"); // DynamicBatching csvStringGenerator.AppendColumn("DynamicBatching"); csvStringGenerator.AppendColumn("dynamicBatchedDrawCalls"); csvStringGenerator.AppendColumn("dynamicBatchedTriangles"); csvStringGenerator.AppendColumn("dynamicBatchedTriangles"); csvStringGenerator.AppendColumn("dynamicBatchedVertices"); // static batching csvStringGenerator.AppendColumn("StaticBatching"); csvStringGenerator.AppendColumn("staticBatchedDrawCalls"); csvStringGenerator.AppendColumn("staticBatchedTriangles"); csvStringGenerator.AppendColumn("staticBatchedTriangles"); csvStringGenerator.AppendColumn("staticBatchedVertices"); // instancing csvStringGenerator.AppendColumn("Instancing"); csvStringGenerator.AppendColumn("hasInstancing"); csvStringGenerator.AppendColumn("instancedBatchedDrawCalls"); csvStringGenerator.AppendColumn("instancedBatches"); csvStringGenerator.AppendColumn("instancedTriangles"); csvStringGenerator.AppendColumn("instancedVertices"); //screen Info csvStringGenerator.AppendColumn("ScreenInfo"); csvStringGenerator.AppendColumn("screenWidth"); csvStringGenerator.AppendColumn("screenHeight"); csvStringGenerator.AppendColumn("screenBytes"); // RenderTexture Info csvStringGenerator.AppendColumn("RenderTextureInfo"); csvStringGenerator.AppendColumn("renderTextureCount"); csvStringGenerator.AppendColumn("renderTextureBytes"); csvStringGenerator.AppendColumn("renderTextureStateChanges"); // SkinnedMesh csvStringGenerator.AppendColumn("SkinnedMesh"); csvStringGenerator.AppendColumn("visibleSkinnedMeshes"); // etc... csvStringGenerator.AppendColumn("etc"); csvStringGenerator.AppendColumn("totalAvailableVRamMBytes"); csvStringGenerator.AppendColumn("vboTotal"); csvStringGenerator.AppendColumn("vboUploads"); csvStringGenerator.AppendColumn("ibUploads"); csvStringGenerator.AppendColumn("shadowCasters"); csvStringGenerator.NextRow(); } protected override string FooterName { get { return "_rendering.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/RenderingAnalyzeToFile.cs.meta ================================================ fileFormatVersion: 2 guid: 659f15cfe80d1a9479e0a497890dc806 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/ScreenshotToPng.cs ================================================ using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; using UTJ.ProfilerReader.RawData.Protocol; using System.Text; using System.IO; #if UNITY_EDITOR using UnityEngine; #endif namespace UTJ.ProfilerReader.Analyzer { public class ScreenShotToProfiler : IAnalyzeFileWriter { public static readonly System.Guid MetadataGuid = new System.Guid("4389DCEB-F9B3-4D49-940B-E98482F3A3F8"); public static readonly int InfoTag = -1; private string outputPath; private string logFile; private bool createDir = false; private StringBuilder stringBuilder = new StringBuilder(); public enum TextureCompress : byte { None = 0, RGB_565 = 1, PNG = 2, JPG_BufferRGB565 = 3, JPG_BufferRGBA = 4, } private class CaptureData { public int profilerFrameIndex; public int idx; public int width; public int height; public int originWidth; public int originHeight; public TextureCompress compress; public CaptureData(int frameIdx ,byte[] data) { this.profilerFrameIndex = frameIdx; this.idx = GetIntValue(data, 0); this.width = GetShortValue(data, 4); this.height = GetShortValue(data, 6); this.originWidth = GetShortValue(data, 8); this.originHeight = GetShortValue(data, 10); if (data.Length > 12) { this.compress = (ScreenShotToProfiler.TextureCompress)data[12]; } else { this.compress = ScreenShotToProfiler.TextureCompress.None; } } public static int GetIntValue(byte[] bin, int offset) { return (bin[offset + 0] << 0) + (bin[offset + 1] << 8) + (bin[offset + 2] << 16) + (bin[offset + 3] << 24); } public static int GetShortValue(byte[] bin, int offset) { return (bin[offset + 0] << 0) + (bin[offset + 1] << 8); } } private Dictionary captureFrameData = new Dictionary(); public void CollectData(ProfilerFrameData frameData) { foreach( var thread in frameData.m_ThreadData) { if (thread.IsMainThread) { ExecuteThreadData(frameData.frameIndex,thread); } } } private void ExecuteThreadData(int frameIdx,ThreadData thread) { if(thread == null || thread.m_AllSamples == null) { return; } foreach( var sample in thread.m_AllSamples) { if( sample == null || sample.sampleName != RawDataDefines.EmitFramemetataSample) { continue; } if( sample.metaDatas == null || sample.metaDatas.metadatas == null ) { continue; } ExecuteFrameMetadata(frameIdx,sample); } } private void ExecuteFrameMetadata(int frameIdx,ProfilerSample sample) { var metadatas = sample.metaDatas.metadatas; if (metadatas.Count < 2) { return; } var guidBin = metadatas[0].convertedObject as byte[]; var tagId = (int)metadatas[1].convertedObject; var valueBin = metadatas[2].convertedObject as byte[]; if (guidBin == null || valueBin == null) { return; } System.Guid guid = new System.Guid(guidBin); CaptureData captureData = null; if (guid != MetadataGuid) { return; } if (tagId == InfoTag) { captureData = new CaptureData(frameIdx,valueBin); this.captureFrameData.Add(captureData.idx, captureData); return; } if( this.captureFrameData.TryGetValue(tagId,out captureData)){ ExecuteBinData(captureData, valueBin); } } private void InitDirectory() { if (!createDir) { if (!Directory.Exists(this.outputPath)) { Directory.CreateDirectory(this.outputPath); } createDir = true; } } // execute data private void ExecuteBinData(CaptureData captureData,byte[] binData) { this.InitDirectory(); string file = GetFilePath(captureData); byte[] pngBin = GetImageBin(captureData,binData); if ( pngBin != null) { File.WriteAllBytes(file, pngBin); } } private byte[] GetImageBin(CaptureData captureData,byte[] binData) { if(binData == null) { return null; } switch (captureData.compress) { #if UNITY_EDITOR case TextureCompress.None: return ImageConversion.EncodeArrayToPNG(binData, UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8A8_SRGB, (uint)captureData.width, (uint)captureData.height); case TextureCompress.RGB_565: return ImageConversion.EncodeArrayToPNG(binData, UnityEngine.Experimental.Rendering.GraphicsFormat.R5G6B5_UNormPack16, (uint)captureData.width, (uint)captureData.height); #endif case TextureCompress.PNG: case TextureCompress.JPG_BufferRGB565: case TextureCompress.JPG_BufferRGBA: return binData; } return null; } private string GetFilePath(CaptureData captureData) { stringBuilder.Length = 0; stringBuilder.Append(this.outputPath).Append("/ss-"); stringBuilder.Append(string.Format("{0:D5}", captureData.idx)); switch (captureData.compress) { case TextureCompress.None: case TextureCompress.RGB_565: case TextureCompress.PNG: stringBuilder.Append(".png"); break; case TextureCompress.JPG_BufferRGB565: case TextureCompress.JPG_BufferRGBA: stringBuilder.Append(".jpg"); break; } return stringBuilder.ToString(); } public void SetFileInfo(string logfilename, string outputpath) { this.outputPath = Path.Combine(outputpath, "screenshots"); this.logFile = logfilename; } public void WriteResultFile(string logfilaneme, string outputpath) { } // nothing todo... public void SetInfo(ProfilerLogFormat logformat, string unityVersion, uint dataversion, ushort platform) { } } } ================================================ FILE: Editor/Analyzer/Impl/ScreenshotToPng.cs.meta ================================================ fileFormatVersion: 2 guid: 79348514f73120440b42f98f02a29bbf MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/ShaderCompileToFile.cs ================================================ using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; using System.Text; using UTJ.ProfilerReader.BinaryData.Thread; using UTJ.ProfilerReader.RawData.Protocol; namespace UTJ.ProfilerReader.Analyzer { public class ShaderCompileToFile : AnalyzeToTextbaseFileBase { private class ShaderCompileInfo { public int frameIdx = 0; public string shader; public float msec = 0.0f; public string pass; public string stage; public string keywords; public bool callFromWarmup = false; public ShaderCompileInfo() { this.shader = ""; this.pass = ""; this.stage = ""; this.keywords = ""; } } private List compileInfos = new List(); private bool hasPassStageKeywordsInfo = false; public override void CollectData(ProfilerFrameData frameData) { foreach ( var threadData in frameData.m_ThreadData){ CollectThreadData(frameData.frameIndex,threadData); } } private void CollectThreadData(int frameIdx,ThreadData thread) { if( thread == null) { return; } if( thread.m_AllSamples == null) { return; } foreach( var sample in thread.m_AllSamples) { if (sample.sampleName == "Shader.CreateGPUProgram") { AddShaderCompileSample(frameIdx, sample); } } } private void AddShaderCompileSample(int frameIdx,ProfilerSample sampleData) { var compileInfo = new ShaderCompileInfo(); compileInfo.frameIdx = frameIdx; compileInfo.msec = sampleData.timeUS / 1000.0f; compileInfo.callFromWarmup = IsCalledWarmup(sampleData.parent); if( sampleData.metaDatas != null ) { var metadatas = sampleData.metaDatas.metadatas; if( metadatas != null) { if (metadatas.Count > 0) { compileInfo.shader = metadatas[0].convertedObject as string; } if (metadatas.Count > 1) { compileInfo.pass = metadatas[1].convertedObject as string; this.hasPassStageKeywordsInfo = true; } if (metadatas.Count > 2) { compileInfo.stage = metadatas[2].convertedObject as string; } if (metadatas.Count > 3) { compileInfo.keywords = metadatas[3].convertedObject as string; } } } this.compileInfos.Add(compileInfo); } // private bool IsCalledWarmup(ProfilerSample sampleData) { for (var current = sampleData; current != null; current = current.parent) { if( current.sampleName == "ShaderVariantCollection.WarmupShaders" || current.sampleName == "Shader.WarmupAllShaders") { return true; } } // "ShaderVariantCollection.WarmupShaders" return false; } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); csvStringGenerator.AppendColumn("frameIdx"); csvStringGenerator.AppendColumn("Shader") .AppendColumn("exec(ms)").AppendColumn("isWarmupCall"); if (this.hasPassStageKeywordsInfo) { csvStringGenerator.AppendColumn("pass") .AppendColumn("stage") .AppendColumn("keyword"); } csvStringGenerator.NextRow(); foreach (var compileInfo in this.compileInfos) { if( compileInfo == null) { continue; } csvStringGenerator.AppendColumn(compileInfo.frameIdx) .AppendColumn(compileInfo.shader) .AppendColumn(compileInfo.msec) .AppendColumn(compileInfo.callFromWarmup); if (this.hasPassStageKeywordsInfo) { csvStringGenerator.AppendColumn(compileInfo.pass). AppendColumn(compileInfo.stage). AppendColumn(compileInfo.keywords); } csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } protected override string FooterName { get { return "_shader_compile.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/ShaderCompileToFile.cs.meta ================================================ fileFormatVersion: 2 guid: 35071278dc1dc3c4eb59250e45d74b73 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/ThreadAnalyzeToFile.cs ================================================ using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; namespace UTJ.ProfilerReader.Analyzer { public class ThreadAnalyzeToFile : AnalyzeToTextbaseFileBase { private class ThreadViewData { public int maxFrame = 0; public string threadName; public Dictionary totalMsecList; public Dictionary idleMsecList; public Dictionary totalCount; public Dictionary idleCount; public ThreadViewData(string name) { this.threadName = name; this.totalMsecList = new Dictionary(512); this.idleMsecList = new Dictionary(512); idleCount = new Dictionary(512); totalCount = new Dictionary(512); } public void AddMsec(int frame, float total, float idle, int totalCnt, int idleCnt) { maxFrame = ProfilerLogUtil.Max(frame, maxFrame); if (!this.totalMsecList.ContainsKey(frame)) { this.totalMsecList.Add(frame, total); this.idleMsecList.Add(frame, idle); this.totalCount.Add(frame, totalCnt); this.idleCount.Add(frame, idleCnt); } else { ProfilerLogUtil.logErrorString("same frame " + threadName +"::" + frame); } } public void GetMSecData(int frame, out float total, out float idle, out int totalCnt, out int idleCnt) { total = 0.0f; idle = 0.0f; totalMsecList.TryGetValue(frame, out total); idleMsecList.TryGetValue(frame, out idle); totalCount.TryGetValue(frame, out totalCnt); idleCount.TryGetValue(frame, out idleCnt); } } private Dictionary viewData = new Dictionary(); const string FrameWholeDataSpecialKey = "CPUTotal"; public override void CollectData(ProfilerFrameData frameData) { // 特別枠で frameDataのCPU時間を追加 ThreadViewData frameViewData = null; if (!this.viewData.TryGetValue(FrameWholeDataSpecialKey, out frameViewData)) { frameViewData = new ThreadViewData(FrameWholeDataSpecialKey); viewData.Add(FrameWholeDataSpecialKey, frameViewData); } frameViewData.AddMsec(frameData.frameIndex, frameData.m_TotalCPUTimeInMicroSec / 1000.0f, 0.0f, 0, 0); // 同一フレーム内に同じスレッド名が複数できるので… Dictionary threadNameCounter = new Dictionary(8); foreach (var thread in frameData.m_ThreadData) { string threadName = thread.FullName; if (threadName == null) { continue; } int cnt = 0; if (threadNameCounter.TryGetValue(threadName, out cnt)) { ++cnt; threadName = threadName + cnt; } threadNameCounter[threadName] = cnt; this.AddDataTo(frameData.frameIndex, threadName, thread); } } private void AddDataTo(int frameIdx, string threadName, ThreadData data) { ThreadViewData threadViewData = null; if (!this.viewData.TryGetValue(threadName, out threadViewData)) { threadViewData = new ThreadViewData(threadName); viewData.Add(threadName, threadViewData); } float totalMsec = 0.0f; float idleMsec = 0.0f; int totalCount = 0; int idleCount = 0; if (data.m_AllSamples != null) { foreach (var sample in data.m_AllSamples) { if (sample.parent == null) { idleMsec += GetSumOfTimeInSampleChildren(sample, "Idle", ref idleCount); totalMsec += GetSumeOfTimeWithNamedSampleInChildren(sample, ref totalCount);// sample.timeUS / 1000.0f; } } } threadViewData.AddMsec(frameIdx, totalMsec, idleMsec, totalCount, idleCount); } private float GetSumeOfTimeWithNamedSampleInChildren(ProfilerSample sample, ref int count) { float sum = 0.0f; if (!string.IsNullOrEmpty(sample.sampleName)) { count += 1; return sample.timeUS / 1000.0f; } if (sample.children == null) { return sum; } foreach (var child in sample.children) { sum += GetSumeOfTimeWithNamedSampleInChildren(child, ref count); } return sum; } private float GetSumOfTimeInSampleChildren(ProfilerSample sample, string matchStr, ref int count) { float sum = 0.0f; if (sample == null) { return sum; } if (sample.sampleName == matchStr) { count += 1; return sample.timeUS / 1000.0f; } if (sample.children == null) { return sum; } foreach (var child in sample.children) { sum += GetSumOfTimeInSampleChildren(child, matchStr, ref count); } return sum; } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); int frameNum = 0; var threadViewDataList = new List(viewData.Values); foreach (var data in threadViewDataList) { if (data.threadName == FrameWholeDataSpecialKey) { csvStringGenerator.AppendColumn(data.threadName).AppendColumn(""); } else { csvStringGenerator.AppendColumn(data.threadName + "(msec)"); csvStringGenerator.AppendColumn("idle(msec)"); csvStringGenerator.AppendColumn("working(msec)"); csvStringGenerator.AppendColumn("rootBlock"); csvStringGenerator.AppendColumn("idleBlock").AppendColumn(""); } frameNum = ProfilerLogUtil.Max(data.maxFrame, frameNum); } csvStringGenerator.NextRow(); for (int i = 0; i < frameNum; ++i) { foreach (var data in threadViewDataList) { int totalCnt, idleCnt; float total, idle; data.GetMSecData(i, out total, out idle, out totalCnt, out idleCnt); if (data.threadName == FrameWholeDataSpecialKey) { csvStringGenerator.AppendColumn(total).AppendColumn(""); } else { csvStringGenerator.AppendColumn(total).AppendColumn(idle).AppendColumn(total - idle); csvStringGenerator.AppendColumn(totalCnt).AppendColumn(idleCnt).AppendColumn(""); } } csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } protected override string FooterName { get { return "_result.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/ThreadAnalyzeToFile.cs.meta ================================================ fileFormatVersion: 2 guid: 2a8ca0b714cbc7044987ba7b2a4c42ef MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/UrpGpuSampleToFile .cs ================================================ using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; using System.Text; using UTJ.ProfilerReader.BinaryData.Thread; using UTJ.ProfilerReader.RawData.Protocol; using System.Runtime.Remoting.Channels; using System; namespace UTJ.ProfilerReader.Analyzer { public class UrpGPUSampleToFile : AnalyzeToTextbaseFileBase { public enum Category : int { Opaque = 0, Transparent = 1, Shadowmap = 2, PostProcess = 3, Other = 4 }; private struct GpuTimeInfo { public int time; public int count; } private class FrameGpuTime { public int frameIdx; public Dictionary gpuTimeByCategory; public void AddGpuSample(GPUTime gpuTime, int category) { if (gpuTimeByCategory == null) { gpuTimeByCategory = new Dictionary(); } GpuTimeInfo time; if (gpuTimeByCategory.TryGetValue(category, out time)) { time.time += gpuTime.gpuTimeInMicroSec; time.count += 1; gpuTimeByCategory[category] = time; } else { time = new GpuTimeInfo { time = gpuTime.gpuTimeInMicroSec, count = 1 }; gpuTimeByCategory.Add(category, time); } } } private List frameGpuTimes = new List(); public override void CollectData(ProfilerFrameData frameData) { FrameGpuTime frameGpuTime = new FrameGpuTime(); frameGpuTime.frameIdx = frameData.frameIndex; foreach (var threadData in frameData.m_ThreadData) { AddGpuSampleByThread(threadData, frameGpuTime); } this.frameGpuTimes.Add(frameGpuTime); } private void AddGpuSampleByThread(ThreadData thread, FrameGpuTime frameGpuTime) { if (thread == null) { return; } if (thread.m_GPUTimeSamples == null) { return; } foreach (var gpuSample in thread.m_GPUTimeSamples) { frameGpuTime.AddGpuSample(gpuSample, GetGpuCategoryByCpuSample(thread, gpuSample)); } } private int GetGpuCategoryByCpuSample(ThreadData thread, GPUTime gpuSample) { int category = (int)Category.Other; var cpuSample = GetCpuSample(thread, gpuSample); for (var current = cpuSample; current != null;current = current.parent) { if(current.sampleName == null) { continue; } if (current.sampleName.EndsWith("Opaques")) { return (int)Category.Opaque; } else if (current.sampleName.EndsWith("Transparents")) { return (int)Category.Transparent; } else if (current.sampleName.EndsWith("ShadowMap")) { return (int)Category.Shadowmap; } else if (current.sampleName.Contains("PostProcessing")) { return (int)Category.PostProcess; } } return category; } private ProfilerSample GetCpuSample(ThreadData thread, GPUTime gpuSample) { int idx = (int)gpuSample.relatedSampleIndex; if(thread.m_AllSamples == null) { return null; } if( idx < 0 || idx >= thread.m_AllSamples.Count) { return null; } ProfilerSample cpuSample = thread.m_AllSamples[idx]; return cpuSample; } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); csvStringGenerator.AppendColumn("frameIdx"); foreach (var category in System.Enum.GetValues(typeof(Category))) { csvStringGenerator.AppendColumn(category.ToString() + "(ms)"); } csvStringGenerator.AppendColumn("callNum"); foreach (var category in System.Enum.GetValues(typeof(Category))) { csvStringGenerator.AppendColumn(category.ToString() + "(calls)"); } csvStringGenerator.NextRow(); foreach( var gpuFrame in frameGpuTimes) { if (gpuFrame.gpuTimeByCategory == null) { continue; } csvStringGenerator.AppendColumn(gpuFrame.frameIdx); foreach (var category in System.Enum.GetValues(typeof(Category))) { GpuTimeInfo val ; if(gpuFrame.gpuTimeByCategory.TryGetValue((int)category,out val)) { csvStringGenerator.AppendColumn( (float)val.time / 1000.0f); } else { csvStringGenerator.AppendColumn(0); } } csvStringGenerator.AppendColumn(""); foreach (var category in System.Enum.GetValues(typeof(Category))) { GpuTimeInfo val; if (gpuFrame.gpuTimeByCategory.TryGetValue((int)category, out val)) { csvStringGenerator.AppendColumn(val.count); } else { csvStringGenerator.AppendColumn(0); } } csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } protected override string FooterName { get { return "_urp_gpu_sample.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/UrpGpuSampleToFile .cs.meta ================================================ fileFormatVersion: 2 guid: 49f5a491901f9f646afcadc3c2c9dda7 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl/WorkerJobAnalyzeToFile.cs ================================================ using System.Collections; using System.Collections.Generic; using UTJ.ProfilerReader.BinaryData; namespace UTJ.ProfilerReader.Analyzer { public class WorkerJobAnalyzeToFile : AnalyzeToTextbaseFileBase { private class WorkerThreadSample { public string sampleName; public float minMSec = float.MaxValue; public float maxMsec = 0.0f; public float sumMsec = 0.0f; public int callNum = 0; public WorkerThreadSample(string name) { this.sampleName = name; } public void Called(float msec) { minMSec = ProfilerLogUtil.Min(minMSec, msec); maxMsec = ProfilerLogUtil.Max(maxMsec, msec); sumMsec += msec; ++callNum; } } private void AddSampleData(string sampleName, float msec) { WorkerThreadSample sampleData = null; if (!this.samples.TryGetValue(sampleName, out sampleData)) { sampleData = new WorkerThreadSample(sampleName); this.samples.Add(sampleName, sampleData); } sampleData.Called(msec); } private Dictionary samples = new Dictionary(); private void CollectThread(ThreadData thread) { if (thread.m_AllSamples == null) { return; } foreach (var sample in thread.m_AllSamples) { if (sample.parent == null) { CollectFromNamedChildren(sample); } } } private void CollectFromNamedChildren(ProfilerSample sample) { if (!string.IsNullOrEmpty(sample.sampleName)) { AddSampleData(sample.sampleName, sample.timeUS / 1000.0f); return; } if (sample.children == null) { return; } foreach (var child in sample.children) { CollectFromNamedChildren(child); } return; } public override void CollectData(ProfilerFrameData frameData) { // 特別枠で frameDataのCPU時間を追加 // 同一フレーム内に同じスレッド名が複数できるので… Dictionary threadNameCounter = new Dictionary(8); foreach (var thread in frameData.m_ThreadData) { if (thread.m_ThreadName == "Worker Thread" || thread.m_GroupName == "Job" ) { CollectThread(thread); } } } /// /// 結果書き出し /// protected override string GetResultText() { CsvStringGenerator csvStringGenerator = new CsvStringGenerator(); csvStringGenerator.AppendColumn("name").AppendColumn("sum(msec)").AppendColumn("call").AppendColumn("min(msec)").AppendColumn("max(msec)").NextRow(); var sampleDataList = new List(samples.Values); sampleDataList.Sort((a, b) => { if (a.sumMsec > b.sumMsec) { return -1; } else if (a.sumMsec < b.sumMsec) { return 1; } return 0; }); foreach (var sampleData in sampleDataList) { csvStringGenerator.AppendColumn(sampleData.sampleName). AppendColumn(sampleData.sumMsec). AppendColumn(sampleData.callNum). AppendColumn(sampleData.minMSec). AppendColumn(sampleData.maxMsec); csvStringGenerator.NextRow(); } return csvStringGenerator.ToString(); } protected override string FooterName { get { return "_worker.csv"; } } } } ================================================ FILE: Editor/Analyzer/Impl/WorkerJobAnalyzeToFile.cs.meta ================================================ fileFormatVersion: 2 guid: 1a89b4a11c81a764fbb5546d719a95cd MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/Impl.meta ================================================ fileFormatVersion: 2 guid: 96fae94d084a0e44383e880362d47836 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer/ProfilingScope.cs ================================================ using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Profiling; namespace UTJ.ProfilerReader.Analyzer { struct ProfilingScope : System.IDisposable { CustomSampler _sampler; public ProfilingScope(CustomSampler sampler) { _sampler = sampler; _sampler.Begin(); } public void Dispose() { _sampler.End(); } } } ================================================ FILE: Editor/Analyzer/ProfilingScope.cs.meta ================================================ fileFormatVersion: 2 guid: 364fed2e9c0775e448a30297b36b1b11 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Analyzer.meta ================================================ fileFormatVersion: 2 guid: 0f01dcbdc9f147a4fbdcfe25cfc31014 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/CUI/CUIInterface.cs ================================================ using System.Collections; using System.Collections.Generic; using UTJ.ProfilerReader.Analyzer; using UnityEngine; using System.IO; using System.Threading.Tasks; namespace UTJ.ProfilerReader { public class CUIInterface { const int NormalCode = 0; const int TimeoutCode = 10; const int ReadErrorCode = 11; public enum CsvFileType { }; private static int timeoutSec = 0; private static ILogReaderPerFrameData currentReader = null; private static bool timeouted = false; private static string overrideUnityVersion = null; public static void SetTimeout(int sec) { Debug.Log("SetTimeout " + sec); timeoutSec = sec; System.Threading.Thread th = new System.Threading.Thread(TimeOutExecute); th.Start(); } public static void TimeOutExecute() { System.Threading.Thread.Sleep(timeoutSec * 1000); Debug.Log("Timeout!!!"); currentReader.ForceExit(); timeouted = true; } public static void ProfilerToCsv() { var args = System.Environment.GetCommandLineArgs(); string inputFile = null; string outputDir = null; bool exitFlag = true; bool logFlag = false; bool isLegacyOutputDirPath = false; for (int i = 0; i < args.Length; ++i) { if (args[i] == "-PH.inputFile") { inputFile = args[i + 1]; i += 1; } if (args[i] == "-PH.outputDir") { outputDir = args[i + 1]; i += 1; } if (args[i] == "-PH.timeout") { SetTimeout(int.Parse(args[i + 1])); i += 1; } if( args[i] == "-PH.overrideUnityVersion") { overrideUnityVersion = args[i + 1]; i += 1; } if (args[i] == "-PH.exitcode") { exitFlag = true; } if (args[i] == "-PH.log") { logFlag = true; } if (args[i] == "-PH.dirLegacy") ; { isLegacyOutputDirPath = true; } } int code = ProfilerToCsv(inputFile, outputDir, logFlag, isLegacyOutputDirPath); if(timeouted) { code = TimeoutCode; } if (exitFlag) { UnityEditor.EditorApplication.Exit(code); } } public static int ProfilerToCsv(string inputFile,string outputDir,bool logFlag,bool isLegacyOutputDirPath) { int retCode = NormalCode; if ( string.IsNullOrEmpty(outputDir)) { if (isLegacyOutputDirPath) { outputDir = Path.GetDirectoryName(inputFile); } else { string file = Path.GetFileName(inputFile); outputDir = Path.Combine(Path.GetDirectoryName(inputFile), file.Replace('.', '_')); } } var logReader = ProfilerLogUtil.CreateLogReader(inputFile); currentReader = logReader; List analyzeExecutes = AnalyzerUtil.CreateAnalyzerInterfaceObjects(); var frameData = logReader.ReadFrameData(); SetAnalyzerInfo(analyzeExecutes, logReader,outputDir,inputFile); if ( frameData == null) { Debug.LogError("No FrameDataFile " + inputFile); } // Loop and execute each frame while (frameData != null) { try { frameData = logReader.ReadFrameData(); if (logFlag && frameData != null) { System.Console.WriteLine("ReadFrame:" + frameData.frameIndex); } } catch (System.Exception e) { retCode = ReadErrorCode; Debug.LogError(e); } if (frameData != null) { List tasks = new List(analyzeExecutes.Count); foreach (var analyzer in analyzeExecutes) { var task = Task.Run(() => { try { analyzer.CollectData(frameData); } catch (System.Exception e) { Debug.LogError(e); } }); tasks.Add(task); } while (true) { bool isComplete = true; foreach (var task in tasks) { if (!task.IsCompleted) { isComplete = false; break; } } if (isComplete) { break; } } } System.GC.Collect(); } foreach (var analyzer in analyzeExecutes) { analyzer.WriteResultFile(System.IO.Path.GetFileName(inputFile), outputDir); } return retCode; } private static void SetAnalyzerInfo(List analyzeExecutes, ILogReaderPerFrameData logReader, string outDir,string inFile) { ProfilerLogFormat format = ProfilerLogFormat.TypeData; if (logReader.GetType() == typeof(UTJ.ProfilerReader.RawData.ProfilerRawLogReader)) { format = ProfilerLogFormat.TypeRaw; } string unityVersion = Application.unityVersion; if ( !string.IsNullOrEmpty(overrideUnityVersion)) { unityVersion = overrideUnityVersion; } foreach (var analyzer in analyzeExecutes) { analyzer.SetInfo(format, unityVersion, logReader.GetLogFileVersion(), logReader.GetLogFilePlatform()); analyzer.SetFileInfo(inFile,outDir); } } } } ================================================ FILE: Editor/CUI/CUIInterface.cs.meta ================================================ fileFormatVersion: 2 guid: 3f87dac947add4540b9b1f12ecfd9035 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/CUI.meta ================================================ fileFormatVersion: 2 guid: 486ca37e5d486f34096200caa31394df folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/GUI/AnalyzeToCsvWindow.cs ================================================ using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; using UTJ.ProfilerReader.Analyzer; using System.Reflection; using System.IO; using System.Threading.Tasks; using System.Text; using UnityEditor.Overlays; namespace UTJ.ProfilerReader.UI { public class AnalyzeToCsvWindow : EditorWindow { private class FileWriterFlag { public string name; public System.Type type; public bool flag; } private List fileWriterFlags = new List(); private List analyzeExecutes = new List(); private ILogReaderPerFrameData logReader = null; private bool isFirstFrame = true; private Vector2 scrollPos; private string filePath; private string outputDir; private string logfilename; private bool isWindowExists; private bool forceStopRequest = false; private bool requestDialogFlag = false; private bool isMultiThreadExecute = true; private List analyzeTasks = new List(16); private List analyzeTaskNames = new List(16); private string taskStatus = ""; private GUIStyle uiStyle; [MenuItem("Tools/UTJ/ProfilerReader/AnalyzeToCsv")] public static void CreateWindow() { EditorWindow.GetWindow(); } private void OnEnable() { this.uiStyle = new GUIStyle( ); this.uiStyle.normal.textColor = Color.red; this.isWindowExists = true; this.fileWriterFlags.Clear(); var types = AnalyzerUtil.GetInterfaceType(); foreach( var t in types) { this.fileWriterFlags.Add(new FileWriterFlag() { name = t.Name, type = t, flag = true }); } } private void OnDisable() { this.isWindowExists = false; } private void UpdateThread() { while (this.isWindowExists) { if(!ExecuteFrame()){ return; } } } bool ExecuteFrame() { if (logReader == null) { return false; } try { var frameData = logReader.ReadFrameData(); if (isFirstFrame) { InitOutputPathInfo(); SetAnalyzerInfo(analyzeExecutes, logReader); isFirstFrame = false; } if (frameData == null || forceStopRequest) { // write all result foreach (var analyzer in this.analyzeExecutes) { analyzer.WriteResultFile(logfilename, outputDir); } requestDialogFlag = true; logReader = null; return false; } foreach (var analyzer in this.analyzeExecutes) { var task = Task.Run(() => { try { analyzer.CollectData(frameData); } catch (System.Exception e) { Debug.LogError(e); } }); analyzeTaskNames.Add(analyzer.GetType().Name); analyzeTasks.Add(task); } // waiting all tasks var sb = new StringBuilder(); while (true) { bool isDone = true; sb.Length = 0; for(int i = 0;i < analyzeTasks.Count;++i) { var task = analyzeTasks[i]; if (!task.IsCompleted) { isDone = false; if(sb.Length > 0) { sb.Append("\n"); } sb.Append(analyzeTaskNames[i]).Append("::").Append(task.Status); } } taskStatus = sb.ToString(); if (isDone) { break; } } taskStatus = ""; analyzeTasks.Clear(); analyzeTaskNames.Clear(); System.GC.Collect(); } catch (System.Exception e) { logReader = null; Debug.LogError(e); } return true; } private void DisplayDialog() { requestDialogFlag = false; string dialogStr = "Write to csv files\n"; foreach (var analyzer in this.analyzeExecutes) { dialogStr += analyzer.GetType() + "\n"; } EditorUtility.DisplayDialog("Result", dialogStr, "ok"); analyzeExecutes.Clear(); } void OnGUI() { EditorGUILayout.LabelField("Convert profiler log to csv"); EditorGUILayout.BeginHorizontal(); if (string.IsNullOrEmpty(filePath)) { EditorGUILayout.LabelField("Select File"); } else { EditorGUILayout.LabelField(this.filePath); } if (GUILayout.Button("File", GUILayout.Width(40.0f))) { this.filePath = EditorUtility.OpenFilePanelWithFilters("", "Select BinaryLogFile", new string[] { "profiler log", "data,raw" }); } if (!IsExecute()) { if (GUILayout.Button("Analyze", GUILayout.Width(100))) { if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath)) { Debug.LogError("No such File "); } else { StartAnalyze(); } } } else { if (GUILayout.Button("ForceExit", GUILayout.Width(100))) { forceStopRequest = true; } } EditorGUILayout.EndHorizontal(); if (IsExecute() ) { uiStyle.alignment = TextAnchor.UpperLeft; EditorGUILayout.LabelField("Progress " + logReader.Progress * 100.0f + "%"); EditorGUILayout.LabelField(this.taskStatus, uiStyle, GUILayout.Height(200)); } else { for (int i = 0; i < fileWriterFlags.Count; ++i) { EditorGUILayout.BeginHorizontal(); this.fileWriterFlags[i].flag = EditorGUILayout.Toggle(this.fileWriterFlags[i].flag ,GUILayout.Width(20) ); EditorGUILayout.LabelField(this.fileWriterFlags[i].name); EditorGUILayout.EndHorizontal(); } } EditorGUILayout.LabelField("The results are in csv file."); } private void StartAnalyze() { logReader = ProfilerLogUtil.CreateLogReader(filePath); isFirstFrame = true; forceStopRequest = false; analyzeExecutes.Clear(); for (int i = 0; i < fileWriterFlags.Count; ++i) { if (this.fileWriterFlags[i].flag) { var analyzer = System.Activator.CreateInstance(fileWriterFlags[i].type) as IAnalyzeFileWriter; analyzeExecutes.Add(analyzer); } } // start analyze if (isMultiThreadExecute) { var thread = new System.Threading.Thread(this.UpdateThread); thread.Start(); } } private void Update() { if (!isMultiThreadExecute) { this.ExecuteFrame(); } if (IsExecute()) { this.Repaint(); } if (requestDialogFlag) { this.DisplayDialog(); } } private bool IsExecute() { return (logReader != null && 0.0f < logReader.Progress && logReader.Progress < 1.0f); } private void InitOutputPathInfo() { this.logfilename = Path.GetFileName(this.filePath); this.outputDir = Path.Combine(Path.GetDirectoryName(this.filePath), logfilename.Replace('.', '_')); if (!Directory.Exists(this.outputDir)) { Directory.CreateDirectory(this.outputDir); } } private void SetAnalyzerInfo(List analyzeExecutes, ILogReaderPerFrameData logReader) { ProfilerLogFormat format = ProfilerLogFormat.TypeData; if (logReader.GetType() == typeof(UTJ.ProfilerReader.RawData.ProfilerRawLogReader)) { format = ProfilerLogFormat.TypeRaw; } foreach (var analyzer in analyzeExecutes) { analyzer.SetInfo(format, Application.unityVersion, logReader.GetLogFileVersion(), logReader.GetLogFilePlatform()); analyzer.SetFileInfo(logfilename, this.outputDir); } } } } ================================================ FILE: Editor/GUI/AnalyzeToCsvWindow.cs.meta ================================================ fileFormatVersion: 2 guid: abd0154d324b92e4584511ce7086dea7 timeCreated: 1479298377 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/GUI/LogAnalyzeWindow.cs ================================================ using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; using UTJ.ProfilerReader; using UTJ.ProfilerReader.BinaryData; using UTJ.ProfilerReader.RawData; namespace UTJ.ProfilerReader.UI{ public class LogAnalyzeWindow : EditorWindow { private struct ColumnData { public ProfilerSample sample; public int frameIndex; } public enum EConditionType { StartsWith, Contains, EndsWith, } // 表示条件 public enum ESampleCondition { Always, ParentOnly, ChildOnly, } private const int PagingNumber = 100; private ILogReaderPerFrameData logReader = null; private SampleDetailView treeView = new SampleDetailView(); private List columnList = new List(); private Vector2 scrollPos; private string filePath; private EConditionType stringCheckCondition; private ESampleCondition sampleCondition; private string sampleNameCondition; private float conditionExecuteTime; private int conditionAlloc; private int pageIndex; [MenuItem("Tools/UTJ/ProfilerReader/LogAnalyzer")] public static void CreateWindow() { EditorWindow.GetWindow(); } void OnEnable() { } void Update() { try { if (logReader != null) { var data = logReader.ReadFrameData(); if (data == null) { return; } this.CollectData(data); System.GC.Collect(); if (data == null) { logReader = null; } this.Repaint(); } } catch (System.Exception e) { logReader = null; Debug.LogError(e); } } void OnGUI() { EditorGUILayout.LabelField("Profiler BinLog Hack"); EditorGUILayout.BeginHorizontal(); this.filePath = EditorGUILayout.TextField(this.filePath); if (GUILayout.Button("File", GUILayout.Width(40.0f))) { this.filePath = EditorUtility.OpenFilePanelWithFilters("", "Select BinaryLogFile", new string[]{ "profiler log", "data,raw" }); } EditorGUILayout.EndHorizontal(); { EditorGUILayout.LabelField("Concition"); EditorGUILayout.BeginHorizontal(); sampleNameCondition = EditorGUILayout.TextField("sample name ", sampleNameCondition); this.stringCheckCondition = (EConditionType)EditorGUILayout.EnumPopup(this.stringCheckCondition); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("HierarchyMode"); this.sampleCondition = (ESampleCondition)EditorGUILayout.EnumPopup(this.sampleCondition); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); this.conditionExecuteTime = EditorGUILayout.FloatField("Execute time(ms)", this.conditionExecuteTime); this.conditionAlloc = EditorGUILayout.IntField("Alloc(byte)", conditionAlloc); EditorGUILayout.EndHorizontal(); } EditorGUILayout.BeginHorizontal(); if (!IsExecuting()) { if (GUILayout.Button("Analyze", GUILayout.Width(100))) { this.pageIndex = 1; if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath)) { Debug.LogError("No such File "); } else { logReader = ProfilerLogUtil.CreateLogReader(filePath); logReader.SetUnityVersion(Application.unityVersion); columnList.Clear(); } } } else { if (GUILayout.Button("Cancel", GUILayout.Width(100))) { logReader.ForceExit(); } } GUILayout.Label(""); if (logReader != null && logReader.IsComplete) { if (GUILayout.Button("Write To CSV", GUILayout.Width(100))) { SaveToCsv(); } } EditorGUILayout.EndHorizontal(); // execute now if (IsExecuting()) { EditorGUILayout.LabelField("Progress " + logReader.Progress * 100.0f + "%"); } else { this.ONGUIResultList(); treeView.OnGUI(); } } private bool IsExecuting() { return (logReader != null && 0.0f < logReader.Progress && logReader.Progress < 1.0f); } private void ONGUIResultList() { EditorGUILayout.BeginHorizontal(); if( GUILayout.Button("<-",GUILayout.Width(40))) { if (pageIndex > 1) { --pageIndex; } } var pageStr = EditorGUILayout.TextField(this.pageIndex.ToString(), GUILayout.Width(40)); int.TryParse(pageStr, out this.pageIndex); EditorGUILayout.LabelField("/" + (columnList.Count + PagingNumber - 1) / PagingNumber, GUILayout.Width(40)); if( GUILayout.Button("->", GUILayout.Width(40))) { if (pageIndex + 1 <= (columnList.Count + PagingNumber-1) / PagingNumber) { ++pageIndex; } } EditorGUILayout.EndHorizontal(); this.scrollPos = EditorGUILayout.BeginScrollView(scrollPos, true, true); EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true)); for(int i = (this.pageIndex -1) * PagingNumber; i< columnList.Count && i < (this.pageIndex) * PagingNumber; ++ i) { if( i < 0 || i > columnList.Count) { continue; } ColumnData column = columnList[i]; var sample = column.sample; EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("More", GUILayout.Width(40))) { this.treeView.SetCurrentSample(sample); } string str = column.frameIndex + "::" + sample.sampleName + "::" + (sample.timeUS / 1000.0f) + "ms Alloc:"; if (sample.totalGcAlloc < 1024 * 10) { str += sample.totalGcAlloc + " Byte"; } else if (sample.totalGcAlloc < 1024 * 1024 * 10) { str += (sample.totalGcAlloc / 1024) + " KByte"; } else { str += (sample.totalGcAlloc / 1024 / 1024) + " MByte"; } EditorGUILayout.LabelField(str); EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); EditorGUILayout.EndScrollView(); } private void CollectData(ProfilerFrameData frameData) { var mainThread = frameData.MainThread; if( mainThread == null || mainThread.m_AllSamples == null ) { return; } List hitInThisFrame = new List(); foreach (var sample in mainThread.m_AllSamples) { if (ChcekSearchCondition(sample)) { ColumnData data; data.sample = sample; data.frameIndex = frameData.frameIndex; hitInThisFrame.Add(data); } } foreach (var hit in hitInThisFrame) { switch (this.sampleCondition) { case ESampleCondition.Always: this.columnList.Add(hit); break; case ESampleCondition.ChildOnly: if (!IsChildExists(hit, hitInThisFrame)) { this.columnList.Add(hit); } break; case ESampleCondition.ParentOnly: if (!IsParentExists(hit, hitInThisFrame)) { this.columnList.Add(hit); } break; } } } private bool IsChildExists(ColumnData column, List list) { foreach (var target in list) { if (target.sample != column.sample && IsParent(column.sample, target.sample)) { return true; } } return false; } private bool IsParentExists(ColumnData column, List list) { foreach (var target in list) { if (target.sample != column.sample && IsParent(target.sample, column.sample)) { return true; } } return false; } private bool IsParent(ProfilerSample parent, ProfilerSample child) { for (ProfilerSample current = child.parent; current != null; current = current.parent) { if (current == parent) { return true; } } return false; } private bool ChcekSearchCondition(ProfilerSample sample) { if (sample.totalGcAlloc < this.conditionAlloc) { return false; } if (sample.timeUS / 1000.0f < this.conditionExecuteTime) { return false; } if (string.IsNullOrEmpty(this.sampleNameCondition)) { return true; } switch (this.stringCheckCondition) { case EConditionType.Contains: return sample.sampleName.Contains(this.sampleNameCondition); case EConditionType.StartsWith: return sample.sampleName.StartsWith(this.sampleNameCondition); case EConditionType.EndsWith: return sample.sampleName.EndsWith(this.sampleNameCondition); } return false; } void OnDisable() { } // CSV保存を押したときの処理 private void SaveToCsv() { if (this.columnList == null || this.columnList.Count == 0) { EditorUtility.DisplayDialog("No Result", "There are no results.Please analyze before.", "OK"); return; } if (0.0f < logReader.Progress && logReader.Progress < 1.0f) { EditorUtility.DisplayDialog("Progress", "Now executing...Wait a moment...", "OK"); return; } string savePath = EditorUtility.SaveFilePanel("CSV file", "", "result", "csv"); if (string.IsNullOrEmpty(savePath)) { return; } WriteCsv(savePath); } // 実際にファイルを書き込むところ private void WriteCsv(string path) { var sb = new System.Text.StringBuilder(); sb.Append("frame,SampleName,executeTime(ms),memoryAlloc(byte)\n"); foreach (var column in columnList) { if (column.sample == null) { continue; } sb.Append(column.frameIndex).Append(","); // コンマあると sb.Append(column.sample.sampleName.Replace(',', '_')).Append(","); sb.Append(column.sample.timeUS / 1000.0f).Append(","); sb.Append(column.sample.totalGcAlloc).Append(","); sb.Append("\n"); } System.IO.File.WriteAllText(path, sb.ToString()); EditorUtility.DisplayDialog("Complete to write result","Save result to"+ path , "OK"); } } public class SampleDetailView { private ProfilerSample currentSample; private Vector2 scrollPos; private Dictionary openList; private System.Text.StringBuilder stringBuilder; private Dictionary allocateBlock; private string[] guiTabStr = new string[] { "Children", "Memory" }; private int guiTabSelect; public SampleDetailView() { openList = new Dictionary(); stringBuilder = new System.Text.StringBuilder(); } public void SetCurrentSample(ProfilerSample sample) { openList.Clear(); this.currentSample = sample; scrollPos = Vector2.zero; if (sample != null) { allocateBlock = sample.GetBlockAllocCount(); } } public void OnGUI() { if (this.currentSample == null) { EditorGUILayout.BeginVertical(); EditorGUILayout.LabelField("Not Select"); EditorGUILayout.EndVertical(); return; } EditorGUILayout.LabelField(currentSample.fullSampleName); if (GUILayout.Button("Close", GUILayout.Width(60))) { this.SetCurrentSample(null); return; } this.guiTabSelect = GUILayout.Toolbar(this.guiTabSelect, this.guiTabStr); scrollPos = EditorGUILayout.BeginScrollView(scrollPos, true, true, GUILayout.MinHeight(300)); switch (this.guiTabSelect) { case 0: ChildrenDraw(this.currentSample); break; case 1: AllocateBlockListDraw(); // test break; } EditorGUILayout.EndScrollView(); } private void ChildrenDraw(ProfilerSample sample) { if (sample == null) { return; } this.stringBuilder.Length = 0; this.stringBuilder.Append(sample.sampleName).Append(" ").Append((sample.timeUS / 1000.0f)).Append("ms Alloc:").Append(sample.totalGcAlloc).Append(" Byte"); string outputStr = this.stringBuilder.ToString(); // style.contentOffset = new Vector2(sample.hierarchyLevel * 10.0f, 0.0f); bool isOpen = false; if (sample.children != null) { GUIStyle style = new GUIStyle(EditorStyles.foldout); style.margin.left = (sample.hierarchyLevel - currentSample.hierarchyLevel) * 20; if (openList.TryGetValue(sample, out isOpen)) { bool openVal = EditorGUILayout.Foldout(isOpen, outputStr, style); if (openVal != isOpen) { openList[sample] = openVal; } if (isOpen) { foreach (var child in sample.children) { ChildrenDraw(child); } } } else { openList.Add(sample, false); EditorGUILayout.Foldout(isOpen, outputStr, style); } } else { GUIStyle style = new GUIStyle(EditorStyles.label); style.padding.left = (sample.hierarchyLevel - currentSample.hierarchyLevel) * 20; EditorGUILayout.LabelField(outputStr, style); } } private void AllocateBlockListDraw() { if (allocateBlock == null) { return; } EditorGUILayout.LabelField("total Memory " + this.currentSample.totalGcAlloc + " Byte"); EditorGUILayout.Space(); List keys = new List(allocateBlock.Keys); keys.Sort(); keys.Reverse(); foreach (var key in keys) { EditorGUILayout.LabelField(key + " Byte X " + allocateBlock[key]); } } private void ParentGUIDraw(ProfilerSample sample) { if (sample == null) { return; } ParentGUIDraw(sample.parent); EditorGUILayout.LabelField(sample.sampleName); } } } ================================================ FILE: Editor/GUI/LogAnalyzeWindow.cs.meta ================================================ fileFormatVersion: 2 guid: 0b7140a5b12e4764fa5245c6e12d476f timeCreated: 1479298377 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/GUI/language/LanguageEn.cs ================================================ using System.Collections; using System.Collections.Generic; namespace UTJ.ProfilerReader.UI { public class LanguageEn : LanguageInterface { protected override void CreateDictionary() { } } } ================================================ FILE: Editor/GUI/language/LanguageEn.cs.meta ================================================ fileFormatVersion: 2 guid: 8abc955aa69cb5a45a48d1c754c65a77 timeCreated: 1516345952 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/GUI/language/LanguageInterface.cs ================================================ using System.Collections; using System.Collections.Generic; using UnityEngine; namespace UTJ.ProfilerReader.UI { public abstract class LanguageInterface { private static LanguageInterface instance; protected abstract void CreateDictionary(); } } ================================================ FILE: Editor/GUI/language/LanguageInterface.cs.meta ================================================ fileFormatVersion: 2 guid: 68a8ada8059764945b73502b7a916297 timeCreated: 1516345952 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/GUI/language/LanguageJa.cs ================================================ using System.Collections; using System.Collections.Generic; using UnityEngine; namespace UTJ.ProfilerReader.UI { public class LanguageJa : LanguageInterface { protected override void CreateDictionary() { } } } ================================================ FILE: Editor/GUI/language/LanguageJa.cs.meta ================================================ fileFormatVersion: 2 guid: 285da0058b5be6944b84589f7071b118 timeCreated: 1516345952 licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/GUI/language.meta ================================================ fileFormatVersion: 2 guid: 8e7a70037606f1944a544856de86e8c8 folderAsset: yes timeCreated: 1516345932 licenseType: Pro DefaultImporter: userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/GUI.meta ================================================ fileFormatVersion: 2 guid: 105db1c87ad9d724dbcb5c713dc1338e folderAsset: yes timeCreated: 1478603637 licenseType: Pro DefaultImporter: userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/UTJProfileReader.dll.meta ================================================ fileFormatVersion: 2 guid: 7b17b1edfad53ec42b71e81a27087216 PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 1 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 0 settings: {} - first: Editor: Editor second: enabled: 1 settings: DefaultValueInitialized: true - first: Windows Store Apps: WindowsStoreApps second: enabled: 0 settings: CPU: AnyCPU userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/Utj.ProfilerReader.Editor.asmdef ================================================ { "name": "UTJ.ProfilerReader.Editor", "references": [ "UTJ.ProfilerReader.Core" ], "optionalUnityReferences": [], "includePlatforms": [ "Editor" ], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [] } ================================================ FILE: Editor/Utj.ProfilerReader.Editor.asmdef.meta ================================================ fileFormatVersion: 2 guid: 5298c21b9b9dc8043a4297316264695f AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor/UtjProfilerInitializer.cs ================================================ using System.Collections; using System.Collections.Generic; using UnityEngine; namespace UTJ.ProfilerReader { public class UtjProfilerInitializer { [UnityEditor.InitializeOnLoadMethod] public static void Init() { ProfilerLogUtil.logErrorException = (e) => { // Debug.LogError(e); }; ProfilerLogUtil.logErrorString = (str) => { // Debug.LogError(str); }; } } } ================================================ FILE: Editor/UtjProfilerInitializer.cs.meta ================================================ fileFormatVersion: 2 guid: ad49b5af4070f2a4dbdea0ed31314305 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Editor.meta ================================================ fileFormatVersion: 2 guid: 17970b62d246c2a4bb30ed8b06ac53cf folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: LICENSE.md ================================================ Profiler Reader Copyright (c) 2017-2018 Unity Technologies Japan Licensed under the Unity Companion License for Unity-dependent projects--see Unity Companion License. Unless expressly provided otherwise, the Software under this license is made available strictly on an gAS ISh BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions. ================================================ FILE: LICENSE.md.meta ================================================ fileFormatVersion: 2 guid: 3fc50eb3859c8a54cac1595ff61a265f TextScriptImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: README.ja.md ================================================ # ProfilerReader vt@C[̃Ot@C͂c[ł
Read this in other languages: [English](README.md), {
## Tv ̃c[ "Unity Profiler". For example, generate csv files that shows Samples allocating many Managed Heap from "Unity Profiler" binary log. ## Ήo[W 2019.4 / 2020.3/2021.1/2021.2/2021.3/2022.2/2022.3
2022.1 skip ## Filter@\ uTools->UTJ->ProfilerReader->AnalyzeToCsvvʼnLEBhEo܂B
![alt text](Documentation~/img/ProfilerReaderFilter.png)
1.vt@C[̃Ow肵܂
2.‚Tv̏w肵܂
3.ɍTv‚܂
4.ɍTv̌ʂ\܂
5.ʂCSVɏo܂B

## CSV@\ ### GUIɂ‚ uTools->UTJ->ProfilerReader->AnalyzeToCsvvʼnLEBhEo܂B
![alt text](Documentation~/img/ProfilerLogToCsv.png)
WindowłCSVꂽT}[𐶐܂B
1.osʂ̎ws܂B
2.Profiler̃Ot@Cw肵܂B
3.͂s܂
### CUIł̃Tv Unity.exe -batchMode -projectPath "ProjectPath" -logFile .\Editor.log -executeMethod UTJ.ProfilerReader.CUIInterface.ProfilerToCsv -PH.inputFile "Binary logFile(.data/.raw)" -PH.timeout 2400 -PH.log oCiOt@CƓꏊɃTutH_쐬ACSVt@C܂B ## CSV Files: CSt@ĆÃt@CɃtb^[tt@Cŏo͂܂B
L̂悤Ȍ`CSV͂‚o܂
uxxx_mainThread_frame.csvv
t[̃CXbhł̃JeSʂCPŨׂXg
uxxx_gc_result.csvv
ŜʂGCmی̃Xg
uxxx_gpu_sample.csvv
ProfilerGPUڂt[ɃJeSʂɏo郊Xg
uxxx_main_self.csvv
ŜʂăTvʂCPU׃Xg
uxxx_memory.csvv
ProfilerMemoryڂt[ɏoXg
uxxx_rendering.csvv
ProfilerRenderingڂt[ɏoXg
uxxx_renderthread.csvv
RenderThread̏󋵂t[ɏoXg
uxxx_result.csvv
t[Thread̏󋵃Xg
uxxx_shader_compile.csvv
ShaderRpC󋵂oXg
uxxx_urp_gpu_sample.csvv
GPUڂUniversal RPɏoXg
uxxx_worker.csvv
WorkerThread̏󋵂oXg
================================================ FILE: README.ja.md.meta ================================================ fileFormatVersion: 2 guid: c0d2319c301d3374bafab003c7b9ff5a TextScriptImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: README.md ================================================ # ProfilerReader Tools for analyze profiler log data.
Read this in other languages: English, [日本語](README.ja.md)
## Summary With this tool, you can analyze the binary log of "Unity Profiler". For example, generate csv files that shows Samples allocating many Managed Heap from "Unity Profiler" binary log. ## Avalable versions 2019.4 / 2020.3/2021.1/2021.2/2021.3/2022.2/2022.3
2022.1 skip ## Filter Search Call "Tools->UTJ->ProfilerReader->AnalyzeToCsv" and then this window will be displayed.
![alt text](Documentation~/img/ProfilerReaderFilter.png)
1.Set Profiler log file.
2.Set Conditions to search samples.
3.Execute Analyze
4.The results are here
5.Write the results to csv file.
## CSV Feature ### GUI Call "Tools->UTJ->ProfilerReader->AnalyzeToCsv" and then this window will be displayed.
![alt text](Documentation~/img/ProfilerLogToCsv.png)
This tool generate summarized csv files.
1.Select the categories to generate csv files.
2.Set Profler log File.
3.Execute Analyze
### CUI Sample Unity.exe -batchMode -projectPath "ProjectPath" -logFile .\Editor.log -executeMethod UTJ.ProfilerReader.CUIInterface.ProfilerToCsv -PH.inputFile "Binary logFile(.data/.raw)" -PH.timeout 2400 -PH.log And some csv file will be generated at subfolder for binary data. ## CSV Files: This tool generate csv files with footer name.
These are samples.
-"xxx_mainThread_frame.csv"
The CPU stats by category in each fraemes.
-"xxx_gc_result.csv"
The list that shows "GC.Alloc".
-"xxx_gc_detail.csv"
The list of "GC.Alloc" and with Callstackinfo.
-"xxx_gpu_sample.csv"
The list about GPU status in each frames .
-"xxx_main_self.csv"
The list about CPU Samples.
-"xxx_memory.csv"
The list about Memory status in each frames .
-"xxx_rendering.csv"
The list about Rendering status in each frames .
-"xxx_renderthread.csv"
The list about RenderThread status in each frames .
-"xxx_result.csv"
The list about threads status in each frames .
-"xxx_shader_compile.csv"
The list that shows Shader Compiling.
-"xxx_urp_gpu_sample.csv"
The list about GPU for Universal RP.
-"xxx_worker.csv"
The list about workerThread status.
- "xxx_jitInfos.csv"
The list of callstack symbol infos.( should enable "Profiler.enableAllocationCallstacks").
================================================ FILE: README.md.meta ================================================ fileFormatVersion: 2 guid: 2c6b5239c79879f4cb86ecc915d6d29f TextScriptImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: package.json ================================================ { "name": "com.utj.profilerreader", "displayName": "Profiler Reader", "version": "0.8.2-preview", "unity": "2018.1", "description": "read profiler log data package", "keywords": [ "profiler" ], "category": "profiler" } ================================================ FILE: package.json.meta ================================================ fileFormatVersion: 2 guid: fbdf912d26e1f004587772ecc03857cb TextScriptImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: