diff --git a/.gitignore b/.gitignore index c2d01bd3..7869bf9f 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,5 @@ Assets/Slua/Meta/Resources/lua3rdmeta.asset Assets/Slua/Meta/Resources/lua3rdmeta.asset.meta /standalone/NUnit.Runners.2.6.4 +/Assets/LuaDeepProfilerSettings.asset +/Assets/LuaDeepProfilerSettings.asset.meta diff --git a/Assets/Slua/LuaProfiler.meta b/Assets/Slua/LuaProfiler.meta new file mode 100644 index 00000000..8bfd6dfa --- /dev/null +++ b/Assets/Slua/LuaProfiler.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 029d8948c60ac254d801540d2ba0770f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/Common.meta b/Assets/Slua/LuaProfiler/Common.meta new file mode 100644 index 00000000..407dbe18 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5b42cf4e195733142af7e5ec6ed657f2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/Common/Editor.meta b/Assets/Slua/LuaProfiler/Common/Editor.meta new file mode 100644 index 00000000..a1d2ed0f --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 15c6e2e95af9f664a992b01a678a1fa8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/Common/Editor/TreeView.meta b/Assets/Slua/LuaProfiler/Common/Editor/TreeView.meta new file mode 100644 index 00000000..32af665f --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Editor/TreeView.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0c63c0f81959e204793846088a3b3c18 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/Common/Editor/TreeView/LuaProfilerTreeView.cs b/Assets/Slua/LuaProfiler/Common/Editor/TreeView/LuaProfilerTreeView.cs new file mode 100644 index 00000000..fdbc3b21 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Editor/TreeView/LuaProfilerTreeView.cs @@ -0,0 +1,495 @@ +/* +* ============================================================================== +* Filename: LuaExport +* Created: 2018/7/13 14:29:22 +* Author: エル・プサイ・コングリィ +* Purpose: +* ============================================================================== +*/ +using System; +using System.Collections.Generic; +using UnityEditor.IMGUI.Controls; +using UnityEngine; + +namespace MikuLuaProfiler +{ +#if UNITY_5_6_OR_NEWER + #region item + [Serializable] + //The TreeElement data class is extended to hold extra data, which you can show and edit in the front-end TreeView. + public class LuaProfilerTreeViewItem : TreeViewItem + { + private static ObjectPool objectPool = new ObjectPool(30); + public static LuaProfilerTreeViewItem Create(LuaProfiler.Sample sample, int depth, LuaProfilerTreeViewItem father) + { + var dict = LuaProfilerTreeView.m_nodeDict; + + LuaProfilerTreeViewItem mt; + mt = objectPool.GetObject(); + mt.ResetBySample(sample, depth, father); + dict.Add(mt.fullName, mt); + + return mt; + } + public void Restore() + { + objectPool.Store(this); + } + + public int frameCalls { private set; get; } + private long[] _gc = new long[] { 0, 0, 0, 0 }; + //没什么意义,因为Lua 执行代码的同时 异步GC,所以导致GC的数字一直闪烁,用上这个去闪烁 + private long _showGC = 0; + public long showGC + { + private set + { + _showGC = value; + } + get + { + if (!UnityEditor.EditorApplication.isPlaying) + { + return _showGC; + } + + if (Time.frameCount == _frameCount) + { + return _showGC; + } + else { return 0; } + } + } + public long totalMemory { private set; get; } + public long totalTime { private set; get; } + public long averageTime { private set; get; } + public float currentTime { private set; get; } + public int totalCallTime { private set; get; } + + public string fullName + { + get; + private set; + } + public readonly List childs = new List(); + public LuaProfilerTreeViewItem father { private set; get; } + private int _frameCount; + public LuaProfilerTreeViewItem() + { + } + + public void ResetBySample(LuaProfiler.Sample sample, int depth, LuaProfilerTreeViewItem father) + { + if (sample != null) + { + totalMemory = sample.costGC; + totalTime = (long)(sample.costTime * 1000000); + displayName = sample.name; + fullName = sample.fullName; + } + else + { + totalMemory = 0; + totalTime = 0; + displayName = "root"; + fullName = "root"; + } + totalCallTime = 1; + averageTime = totalTime / totalCallTime; + + this.id = LuaProfilerTreeView.GetUniqueId(); + this.depth = depth; + + + childs.Clear(); + if (sample != null) + { + for (int i = 0, imax = sample.childs.Count; i < imax; i++) + { + var dict = LuaProfilerTreeView.m_nodeDict; + + LuaProfilerTreeViewItem mt; + var childSample = sample.childs[i]; + if (dict.TryGetValue(childSample.fullName, out mt)) + { + mt.AddSample(childSample); + } + else + { + var item = Create(sample.childs[i], depth + 1, this); + childs.Add(item); + } + } + } + this.father = father; + + _frameCount = Time.frameCount; + } + public void AddSample(LuaProfiler.Sample sample) + { + if (_frameCount == Time.frameCount) + { + _gc[3] += sample.costGC; + frameCalls += sample.oneFrameCall; + currentTime += sample.costTime; + } + else + { + _gc[0] = _gc[1]; + _gc[1] = _gc[2]; + _gc[2] = _gc[3]; + _gc[3] = sample.costGC; + frameCalls = sample.oneFrameCall; + currentTime = sample.costTime; + } + totalMemory += sample.costGC; + + totalTime += (long)(sample.costTime * 1000000); + totalCallTime += sample.oneFrameCall; + averageTime = totalTime / totalCallTime; + for (int i = 0, imax = sample.childs.Count; i < imax; i++) + { + LuaProfilerTreeViewItem childItem = null; + var sampleChild = sample.childs[i]; + if (LuaProfilerTreeView.m_nodeDict.TryGetValue(sampleChild.fullName, out childItem)) + { + childItem.AddSample(sampleChild); + } + else + { + var treeItem = Create(sampleChild, depth + 1, this); + childs.Add(treeItem); + } + } + //以下代码只不过为了 gc的显示数值不闪烁 + if (_gc[0] == _gc[1] || _gc[0] == _gc[2] || _gc[0] == _gc[3]) + { + showGC = _gc[0]; + } + else if (_gc[1] == _gc[2] || _gc[1] == _gc[3]) + { + showGC = _gc[1]; + } + else if (_gc[2] == _gc[3]) + { + showGC = _gc[2]; + } + else + { + showGC = _gc[3]; + } + _frameCount = Time.frameCount; + } + } + #endregion + + public class LuaProfilerTreeView : TreeView + { + #region pool + private static int _uniqueId = 0; + public static int GetUniqueId() + { + return _uniqueId++; + } + + private readonly List roots = new List(); + #endregion + + #region field + private readonly LuaProfilerTreeViewItem root; + private readonly List treeViewItems = new List(); + #endregion + public LuaProfilerTreeView(TreeViewState treeViewState, float width) + : base(treeViewState, CreateDefaultMultiColumnHeaderState(width)) + { + LuaProfiler.SetSampleEnd(LoadRootSample); + root = LuaProfilerTreeViewItem.Create(null, -1, null); + Reload(); + } + + private static MultiColumnHeader CreateDefaultMultiColumnHeaderState(float treeViewWidth) + { + var columns = new[] + { + new MultiColumnHeaderState.Column + { + headerContent = new GUIContent("Overview"), + contextMenuText = "Overview", + headerTextAlignment = TextAlignment.Center, + sortedAscending = false, + sortingArrowAlignment = TextAlignment.Right, + width = 500, + minWidth = 500, + maxWidth = 500, + autoResize = true, + canSort = false, + allowToggleVisibility = true + }, + new MultiColumnHeaderState.Column + { + headerContent = new GUIContent("totalMemory"), + contextMenuText = "totalMemory", + headerTextAlignment = TextAlignment.Center, + sortedAscending = false, + sortingArrowAlignment = TextAlignment.Right, + width = 80, + minWidth = 80, + maxWidth = 80, + autoResize = true, + canSort = true, + allowToggleVisibility = false + }, + new MultiColumnHeaderState.Column + { + headerContent = new GUIContent("currentTime"), + contextMenuText = "currentTime", + headerTextAlignment = TextAlignment.Center, + sortedAscending = false, + sortingArrowAlignment = TextAlignment.Right, + width = 120, + minWidth = 120, + maxWidth = 120, + autoResize = true, + canSort = true, + allowToggleVisibility = false + }, + new MultiColumnHeaderState.Column + { + headerContent = new GUIContent("averageTime"), + contextMenuText = "averageTime", + headerTextAlignment = TextAlignment.Center, + sortedAscending = false, + sortingArrowAlignment = TextAlignment.Right, + width = 120, + minWidth = 120, + maxWidth = 120, + autoResize = true, + canSort = true, + allowToggleVisibility = false + }, + new MultiColumnHeaderState.Column + { + headerContent = new GUIContent("totalTime"), + contextMenuText = "totalTime", + headerTextAlignment = TextAlignment.Center, + sortedAscending = false, + sortingArrowAlignment = TextAlignment.Right, + width = 120, + minWidth = 120, + maxWidth = 120, + autoResize = true, + canSort = true, + allowToggleVisibility = false + }, + new MultiColumnHeaderState.Column + { + headerContent = new GUIContent("Now GC"), + contextMenuText = "Now GC", + headerTextAlignment = TextAlignment.Center, + sortedAscending = false, + sortingArrowAlignment = TextAlignment.Right, + width = 60, + minWidth = 60, + maxWidth = 60, + autoResize = true, + canSort = true, + allowToggleVisibility = false + }, + new MultiColumnHeaderState.Column + { + headerContent = new GUIContent("totalCalls"), + contextMenuText = "totalCalls", + headerTextAlignment = TextAlignment.Center, + sortedAscending = false, + sortingArrowAlignment = TextAlignment.Right, + width = 120, + minWidth = 120, + maxWidth = 120, + autoResize = true, + canSort = true, + allowToggleVisibility = false + }, + new MultiColumnHeaderState.Column + { + headerContent = new GUIContent("Now Calls"), + contextMenuText = "Now Calls", + headerTextAlignment = TextAlignment.Center, + sortedAscending = false, + sortingArrowAlignment = TextAlignment.Right, + width = 80, + minWidth = 80, + maxWidth = 80, + autoResize = true, + canSort = true, + allowToggleVisibility = false + }, + }; + + var state = new MultiColumnHeaderState(columns); + return new MultiColumnHeader(state); + } + + public void Clear() + { + roots.Clear(); + m_nodeDict.Clear(); + treeViewItems.Clear(); + } + + protected override void DoubleClickedItem(int id) + { + /* + base.DoubleClickedItem(id); + var selectItem = FindItem(id, BuildRoot()); + string fileName = "/" + selectItem.displayName.Split(new char[] { ',' }, 2)[0].Replace(".", "/").Replace("/lua", ".lua").Trim(); + try + { + int line = 0; + int.TryParse(Regex.Match(selectItem.displayName, @"(?<=(line:))\d*(?=( ))").Value, out line); + //LocalToLuaIDE.OnOpenAsset(fileName, line); + } + catch + { + }*/ + } + + public static Dictionary m_nodeDict = new Dictionary(); + + private void LoadRootSample(LuaProfiler.Sample sample) + { + LuaProfilerTreeViewItem item; + string f = sample.fullName; + string f1 = sample.fullName; + if (m_nodeDict.TryGetValue(sample.fullName, out item)) + { + item.AddSample(sample); + } + else + { + item = LuaProfilerTreeViewItem.Create(sample, 0, null); + roots.Add(item); + } + } + + private void ReLoadTreeItems() + { + treeViewItems.Clear(); + List rootList = new List(roots); + int sortIndex = multiColumnHeader.sortedColumnIndex; + int sign = 0; + if (sortIndex > 0) + { + sign = multiColumnHeader.IsSortedAscending(sortIndex) ? -1 : 1; + } + switch (sortIndex) + { + case 1: rootList.Sort((a, b) => { return sign * Math.Sign(a.totalMemory - b.totalMemory); }); break; + case 2: rootList.Sort((a, b) => { return sign * Math.Sign(a.currentTime - b.currentTime); }); break; + case 3: rootList.Sort((a, b) => { return sign * Math.Sign(a.averageTime - b.averageTime); }); break; + case 4: rootList.Sort((a, b) => { return sign * Math.Sign(a.totalTime - b.totalTime); }); break; + case 5: rootList.Sort((a, b) => { return sign * Math.Sign(a.showGC - b.showGC); }); break; + case 6: rootList.Sort((a, b) => { return sign * Math.Sign(a.totalCallTime - b.totalCallTime); }); break; + case 7: rootList.Sort((a, b) => { return sign * Math.Sign(a.frameCalls - b.frameCalls); }); break; + } + foreach (var item in rootList) + { + AddOneNode(item); + SortChildren(sortIndex, sign, item); + } + } + + private void SortChildren(int sortIndex, int sign, LuaProfilerTreeViewItem item) + { + if (item.childs != null && item.childs.Count > 0) + { + List rootList = item.childs; + switch (sortIndex) + { + case 1: rootList.Sort((a, b) => { return sign * Math.Sign(a.totalMemory - b.totalMemory); }); break; + case 2: rootList.Sort((a, b) => { return sign * Math.Sign(a.currentTime - b.currentTime); }); break; + case 3: rootList.Sort((a, b) => { return sign * Math.Sign(a.averageTime - b.averageTime); }); break; + case 4: rootList.Sort((a, b) => { return sign * Math.Sign(a.totalTime - b.totalTime); }); break; + case 5: rootList.Sort((a, b) => { return sign * Math.Sign(a.showGC - b.showGC); }); break; + case 6: rootList.Sort((a, b) => { return sign * Math.Sign(a.totalCallTime - b.totalCallTime); }); break; + case 7: rootList.Sort((a, b) => { return sign * Math.Sign(a.frameCalls - b.frameCalls); }); break; + } + foreach (var t in rootList) + { + SortChildren(sortIndex, sign, t); + } + } + } + + private void AddOneNode(LuaProfilerTreeViewItem root) + { + treeViewItems.Add(root); + + m_nodeDict[root.fullName] = root; + if (root.children != null) + { + root.children.Clear(); + } + foreach (var item in root.childs) + { + AddOneNode(item); + } + } + + protected override TreeViewItem BuildRoot() + { + ReLoadTreeItems(); + + // Utility method that initializes the TreeViewItem.children and -parent for all items. + SetupParentsAndChildrenFromDepths(root, treeViewItems); + + // Return root of the tree + return root; + } + + protected override void RowGUI(RowGUIArgs args) + { + GUIStyle gs = new GUIStyle(); + gs.normal.textColor = Color.green; //new Color32(0, 200, 0, 255); + gs.alignment = TextAnchor.MiddleCenter; + + var item = (LuaProfilerTreeViewItem)args.item; + Rect r = args.rowRect; + + base.RowGUI(args); + + //r.x = r.x + 30; + //GUI.Label(r, item.displayName); + + r.x = r.x + 500; + r.width = 80; + GUI.Label(r, LuaProfiler.GetMemoryString(item.totalMemory), gs); + + r.x = r.x + 80; + r.width = 120; + GUI.Label(r, item.currentTime.ToString("f6") + "s", gs); + + r.x = r.x + 120; + r.width = 120; + GUI.Label(r, ((float)item.averageTime / 1000000).ToString("f6") + "s", gs); + + r.x = r.x + 120; + r.width = 120; + GUI.Label(r, ((float)item.totalTime / 1000000).ToString("f6") + "s", gs); + + r.x = r.x + 120; + r.width = 60; + GUI.Label(r, LuaProfiler.GetMemoryString(item.showGC), gs); + + r.x = r.x + 60; + r.width = 120; + GUI.Label(r, LuaProfiler.GetMemoryString(item.totalCallTime, ""), gs); + + r.x = r.x + 120; + r.width = 80; + GUI.Label(r, item.frameCalls.ToString(), gs); + + } + + } +#endif +} diff --git a/Assets/Slua/LuaProfiler/Common/Editor/TreeView/LuaProfilerTreeView.cs.meta b/Assets/Slua/LuaProfiler/Common/Editor/TreeView/LuaProfilerTreeView.cs.meta new file mode 100644 index 00000000..44d4382a --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Editor/TreeView/LuaProfilerTreeView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2bf7eb6c860d30044aab7f400fcd5b71 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/Common/Editor/TreeView/LuaProfilerWindow.cs b/Assets/Slua/LuaProfiler/Common/Editor/TreeView/LuaProfilerWindow.cs new file mode 100644 index 00000000..cf9c3db5 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Editor/TreeView/LuaProfilerWindow.cs @@ -0,0 +1,122 @@ +/* +* ============================================================================== +* Filename: LuaExport +* Created: 2018/7/13 14:29:22 +* Author: エル・プサイ・コングリィ +* Purpose: +* ============================================================================== +*/ +using UnityEngine; +using UnityEditor.IMGUI.Controls; +using UnityEditor; +using System; + +namespace MikuLuaProfiler +{ +#if UNITY_5_6_OR_NEWER + public class LuaProfilerWindow : EditorWindow + { + [SerializeField] TreeViewState m_TreeViewState; + + LuaProfilerTreeView m_TreeView; + SearchField m_SearchField; + + void OnEnable() + { + if (m_TreeViewState == null) + m_TreeViewState = new TreeViewState(); + + LuaProfilerTreeView.m_nodeDict.Clear(); + m_TreeView = new LuaProfilerTreeView(m_TreeViewState, position.width - 40); + m_SearchField = new SearchField(); + m_SearchField.downOrUpArrowKeyPressed += m_TreeView.SetFocusAndEnsureSelectedItem; + } + void OnGUI() + { + DoToolbar(); + DoTreeView(); + } + + private bool m_isStop = false; + void DoToolbar() + { + GUILayout.BeginHorizontal(EditorStyles.toolbar); + + #region clear + bool isClear = GUILayout.Button("Clear", EditorStyles.toolbarButton, GUILayout.Height(30)); + if (isClear) + { + m_TreeView.Clear(); + } + GUILayout.Space(5); + #endregion + + #region deep + bool flag = GUILayout.Toggle(LuaDeepProfilerSetting.Instance.isDeepProfiler, + "Deep Profiler", EditorStyles.toolbarButton, GUILayout.Height(30)); + if (flag != LuaDeepProfilerSetting.Instance.isDeepProfiler) + { + LuaDeepProfilerSetting.Instance.isDeepProfiler = flag; + EditorApplication.isPlaying = false; + } + GUILayout.Space(5); + #endregion + + #region stop + bool isStop = GUILayout.Toggle(m_isStop, "Stop GC", EditorStyles.toolbarButton, GUILayout.Height(30)); + + if (m_isStop != isStop) + { + if (isStop) + { + LuaLib.StopGC(); + m_isStop = true; + } + else + { + LuaLib.ResumeGC(); + m_isStop = false; + } + } + GUILayout.Space(5); + #endregion + + #region run gc + bool isRunGC = GUILayout.Button("Run GC", EditorStyles.toolbarButton, GUILayout.Height(30)); + if (isRunGC) + { + LuaLib.RunGC(); + } + GUILayout.Space(20); + GUILayout.FlexibleSpace(); + #endregion + + #region gc value + GUILayout.Label(string.Format("Lua Total:{0}", LuaProfiler.GetLuaMemory()), EditorStyles.toolbarButton, GUILayout.Height(30)); + #endregion + + GUILayout.Space(100); + GUILayout.FlexibleSpace(); + m_TreeView.searchString = m_SearchField.OnToolbarGUI(m_TreeView.searchString); + GUILayout.EndHorizontal(); + } + + void DoTreeView() + { + Rect rect = GUILayoutUtility.GetRect(0, 100000, 0, 100000); + m_TreeView.Reload(); + m_TreeView.OnGUI(rect); + } + + // Add menu named "My Window" to the Window menu + [MenuItem("Window/Lua Profiler Window")] + static public void ShowWindow() + { + // Get existing open window or if none, make a new one: + var window = GetWindow(); + window.titleContent = new GUIContent("Lua Profiler"); + window.Show(); + } + } +#endif +} diff --git a/Assets/Slua/LuaProfiler/Common/Editor/TreeView/LuaProfilerWindow.cs.meta b/Assets/Slua/LuaProfiler/Common/Editor/TreeView/LuaProfilerWindow.cs.meta new file mode 100644 index 00000000..801ba080 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Editor/TreeView/LuaProfilerWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d00b76b720f401640829dbe4539d5ff1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/Common/LuaDeepProfilerSetting.cs b/Assets/Slua/LuaProfiler/Common/LuaDeepProfilerSetting.cs new file mode 100644 index 00000000..cebe4d5f --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/LuaDeepProfilerSetting.cs @@ -0,0 +1,90 @@ +/* +* ============================================================================== +* Filename: LuaDeepProfilerSetting +* Created: 2018/7/13 14:29:22 +* Author: エル・プサイ・コングリィ +* Purpose: +* ============================================================================== +*/ +#if UNITY_EDITOR +namespace MikuLuaProfiler +{ + using System.Collections.Generic; + using System.IO; + using UnityEditor; + using UnityEngine; + + public class LuaDeepProfilerSetting : ScriptableObject + { + public const string SettingsAssetName = "LuaDeepProfilerSettings"; + private static LuaDeepProfilerSetting instance; + public static LuaDeepProfilerSetting Instance + { + get + { + if (instance == null) + { + instance = AssetDatabase.LoadAssetAtPath("Assets/" + SettingsAssetName + ".asset"); + if (instance == null) + { + Debug.Log("Lua Profiler: cannot find integration settings, creating default settings"); + instance = CreateInstance(); + instance.name = "Lua Profiler Integration Settings"; +#if UNITY_EDITOR + AssetDatabase.CreateAsset(instance, "Assets/" + SettingsAssetName + ".asset"); +#endif + } + } + return instance; + } + } + [SerializeField] + private bool m_isDeepProfiler = false; + public bool isDeepProfiler + { + get + { + return m_isDeepProfiler; + } + set + { + m_isDeepProfiler = value; + } + } + + [MenuItem("LuaProfiler/ExportFiles", priority = 10)] + public static void EditSettings() + { + string path = EditorUtility.OpenFolderPanel("请选择Lua脚本存放文件夹", "", "*"); + path = path.Replace("/", "\\"); + string rootProfilerDirPath = path + "Profiler"; + DirectoryInfo dir = new DirectoryInfo(path); + FileInfo[] files = dir.GetFiles("*.lua"); + int count = files.Length; + int process = 0; + foreach (FileInfo item in files) + { + process++; + + EditorUtility.DisplayProgressBar("profiler lua", item.FullName, (float)process / count); + string allCode = File.ReadAllText(item.FullName); + allCode = MikuLuaProfiler.Parse.InsertSample(allCode, "Template"); + string profilerPath = item.FullName.Replace(path, rootProfilerDirPath); + string profilerDirPath = profilerPath.Replace(item.Name, ""); + if (!Directory.Exists(profilerDirPath)) + { + Directory.CreateDirectory(profilerDirPath); + } + File.WriteAllText(profilerPath, allCode); + } + EditorUtility.ClearProgressBar(); + Selection.activeObject = Instance; +#if UNITY_2018_1_OR_NEWER + EditorApplication.ExecuteMenuItem("Window/General/Inspector"); +#else + EditorApplication.ExecuteMenuItem("Window/Inspector"); +#endif + } + } +} +#endif \ No newline at end of file diff --git a/Assets/Slua/LuaProfiler/Common/LuaDeepProfilerSetting.cs.meta b/Assets/Slua/LuaProfiler/Common/LuaDeepProfilerSetting.cs.meta new file mode 100644 index 00000000..6cdba291 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/LuaDeepProfilerSetting.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 144cbe4d057341d479a8e09b5bf689dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/Common/LuaProfiler.cs b/Assets/Slua/LuaProfiler/Common/LuaProfiler.cs new file mode 100644 index 00000000..25d2ddd9 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/LuaProfiler.cs @@ -0,0 +1,333 @@ +#pragma warning disable CS0219//故意在c#这里产生于lua那边的等量GC +#if UNITY_EDITOR +using System; +using System.Collections.Generic; +using UnityEngine; +#if UNITY_5_5_OR_NEWER +using UnityEngine.Profiling; +#endif +using UnityEditorInternal; + +namespace MikuLuaProfiler +{ + public class LuaProfiler + { + private static IntPtr _mainL = IntPtr.Zero; + public static IntPtr mainL + { + get + { + return _mainL; + } + set + { + _mainL = value; + } + } + + public static string GetLuaMemory() + { + long result = 0; + if (mainL != IntPtr.Zero) + { + try + { + result = LuaLib.GetLuaMemory(mainL); + } + catch { } + } + + return GetMemoryString(result); + } + + public class Sample + { + private static ObjectPool samplePool = new ObjectPool(250); + public static Sample Create(float time, long memory, string name) + { + Sample s = samplePool.GetObject(); + s.currentTime = time; + s.currentLuaMemory = memory; + s.realCurrentLuaMemory = memory; + s.costGC = 0; + s.name = name; + s.costTime = 0; + s.childs.Clear(); + s._father = null; + s._fullName = null; + + return s; + } + + public void Restore() + { + for (int i = 0, imax = childs.Count; i < imax; i++) + { + childs[i].Restore(); + } + samplePool.Store(this); + } + + public int oneFrameCall + { + get + { + return 1; + } + } + public float currentTime { private set; get; } + public long realCurrentLuaMemory { private set; get; } + private string _name; + public string name + { + private set + { + _name = value; + } + get + { + return _name; + } + } + + private static Dictionary> m_fullNamePool = new Dictionary>(); + private string _fullName = null; + public string fullName + { + get + { + if (_father == null) return _name; + + if (_fullName == null) + { + Dictionary childDict; + if (!m_fullNamePool.TryGetValue(_father.fullName, out childDict)) + { + childDict = new Dictionary(); + m_fullNamePool.Add(_father.fullName, childDict); + } + + if (!childDict.TryGetValue(_name, out _fullName)) + { + string value = _name; + var f = _father; + while (f != null) + { + value = f.name + value; + f = f.fahter; + } + _fullName = value; + childDict[_name] = _fullName; + } + + return _fullName; + } + else + { + return _fullName; + } + } + } + //这玩意在统计的window里面没啥卵用 + public long currentLuaMemory { set; get; } + + private float _costTime; + public float costTime + { + set + { + _costTime = value; + } + get + { + float result = _costTime; + return result; + } + } + + private long _costGC; + public long costGC + { + set + { + _costGC = value; + } + get + { + return _costGC; + } + } + private Sample _father; + public Sample fahter + { + set + { + _father = value; + if (_father != null) + { + _father.childs.Add(this); + } + } + get + { + return _father; + } + } + + public readonly List childs = new List(256); + } + //开始采样时候的lua内存情况,因为中间有可能会有二次采样,所以要丢到一个盏中 + public static readonly List beginSampleMemoryStack = new List(); + + private static Action m_SampleEndAction; + + private static bool isDeep + { + get + { +#if UNITY_EDITOR + return ProfilerDriver.deepProfiling; +#else + return false; +#endif + } + } + public static void SetSampleEnd(Action action) + { + m_SampleEndAction = action; + } + public static void BeginSample(string name) + { +#if DEBUG + if (mainL != IntPtr.Zero) + { + BeginSample(mainL, name); + } +#endif + } + public static void BeginSample(IntPtr luaState) + { +#if DEBUG + BeginSample(luaState, "lua gc"); +#endif + } + private static int m_currentFrame = 0; + public static void BeginSample(IntPtr luaState, string name) + { + if (m_currentFrame != Time.frameCount) + { + PopAllSampleWhenLateUpdate(); + m_currentFrame = Time.frameCount; + } + +#if DEBUG + long memoryCount = LuaLib.GetLuaMemory(luaState); + Sample sample = Sample.Create(Time.realtimeSinceStartup, memoryCount, name); + + beginSampleMemoryStack.Add(sample); + if (!isDeep) + { + Profiler.BeginSample(name); + } +#endif + } + public static void PopAllSampleWhenLateUpdate() + { + for (int i = 0, imax = beginSampleMemoryStack.Count; i < imax; i++) + { + var item = beginSampleMemoryStack[i]; + if (item.fahter == null) + { + item.Restore(); + } + } + beginSampleMemoryStack.Clear(); + } + public static void EndSample() + { +#if DEBUG + if (_mainL != IntPtr.Zero) + { + EndSample(_mainL); + } +#endif + } + public static void EndSample(IntPtr luaState) + { +#if DEBUG + if (beginSampleMemoryStack.Count <= 0) + { + return; + } + int count = beginSampleMemoryStack.Count; + Sample sample = beginSampleMemoryStack[beginSampleMemoryStack.Count - 1]; + long oldMemoryCount = sample.currentLuaMemory; + beginSampleMemoryStack.RemoveAt(count - 1); + long nowMemoryCount = LuaLib.GetLuaMemory(luaState); + sample.fahter = count > 1 ? beginSampleMemoryStack[count - 2] : null; + + if (!isDeep) + { + long delta = nowMemoryCount - oldMemoryCount; + + long tmpDelta = delta; + if (delta > 0) + { + delta = Math.Max(delta - 40, 0);//byte[0] 的字节占用是40 + byte[] luagc = new byte[delta]; + } + for (int i = 0, imax = beginSampleMemoryStack.Count; i < imax; i++) + { + Sample s = beginSampleMemoryStack[i]; + s.currentLuaMemory += tmpDelta; + beginSampleMemoryStack[i] = s; + } + Profiler.EndSample(); + } + + sample.costTime = Time.realtimeSinceStartup - sample.currentTime; + var gc = nowMemoryCount - sample.realCurrentLuaMemory; + sample.costGC = gc > 0 ? gc : 0; + + if (m_SampleEndAction != null && beginSampleMemoryStack.Count == 0) + { + m_SampleEndAction(sample); + } + + if (sample.fahter == null) + { + sample.Restore(); + } +#endif + } + + const long MaxB = 1024; + const long MaxK = MaxB * 1024; + const long MaxM = MaxK * 1024; + const long MaxG = MaxM * 1024; + + public static string GetMemoryString(long value, string unit = "B") + { + string result = null; + if (value < MaxB) + { + result = string.Format("{0}{1}", value, unit); + } + else if (value < MaxK) + { + result = string.Format("{0:N2}K{1}", (float)value / MaxB, unit); + } + else if (value < MaxM) + { + result = string.Format("{0:N2}M{1}", (float)value / MaxK, unit); + } + else if (value < MaxG) + { + result = string.Format("{0:N2}G{1}", (float)value / MaxM, unit); + } + return result; + } + } +} +#endif + diff --git a/Assets/Slua/LuaProfiler/Common/LuaProfiler.cs.meta b/Assets/Slua/LuaProfiler/Common/LuaProfiler.cs.meta new file mode 100644 index 00000000..5959b682 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/LuaProfiler.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 352e62d8c1f35874f83c198dcbe84d8f +timeCreated: 1527823831 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/Common/ObjectPool.cs b/Assets/Slua/LuaProfiler/Common/ObjectPool.cs new file mode 100644 index 00000000..10109148 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/ObjectPool.cs @@ -0,0 +1,170 @@ +/* + * 对象池 + */ +#if UNITY_EDITOR +using System; +using System.Collections; +using System.Collections.Generic; + +namespace MikuLuaProfiler +{ + public class ObjectPool where T : class, new() + { + public delegate T CreateFunc(); + + public ObjectPool() + { + + } + public ObjectPool(int poolSize, CreateFunc createFunc = null, Action resetAction = null) + { + Init(poolSize, createFunc, resetAction); + } + public T GetObject() + { + if (m_objStack.Count > 0) + { + T t = m_objStack.Pop(); + if (m_resetAction != null) + { + m_resetAction(t); + } + return t; + } + return (m_createFunc != null) ? m_createFunc() : new T(); + } + + public void Init(int poolSize, CreateFunc createFunc = null, Action resetAction = null) + { + m_objStack = new Stack(); + m_resetAction = resetAction; + m_createFunc = createFunc; + for (int i = 0; i < poolSize; i++) + { + T item = (m_createFunc != null) ? m_createFunc() : new T(); + m_objStack.Push(item); + } + } + + public void Store(T obj) + { + if (obj == null) + return; + if (m_resetAction != null) + m_resetAction(obj); + m_objStack.Push(obj); + } + + // 少用,调用这个池的作用就没有了 + public void Clear() + { + if (m_objStack != null) + m_objStack.Clear(); + } + + public int Count + { + get + { + if (m_objStack == null) + return 0; + return m_objStack.Count; + } + } + + public Stack.Enumerator GetIter() + { + if (m_objStack == null) + return new Stack.Enumerator(); + return m_objStack.GetEnumerator(); + } + + private Stack m_objStack = null; + private Action m_resetAction = null; + private CreateFunc m_createFunc = null; + } + + public class ListObjectPool where T : class + { + public delegate T CreateFunc(); + + public ListObjectPool(int poolSize, CreateFunc createFunc, Action resetAction = null) + { + Init(poolSize, createFunc, resetAction); + } + public T GetObject() + { + T t; + if (m_topIndex >= 0) + { + t = m_objStack[m_topIndex]; + m_topIndex--; + if (m_resetAction != null) + { + m_resetAction(t); + } + + } + else + { + t = m_createFunc(); + m_objStack.Add(t); + } + return t; + } + + public void Init(int poolSize, CreateFunc createFunc = null, Action resetAction = null) + { + m_objStack = new List(); + m_resetAction = resetAction; + m_createFunc = createFunc; + for (int i = 0; i < poolSize; i++) + { + T item = m_createFunc(); + m_objStack.Add(item); + } + m_topIndex = poolSize - 1; + } + + public void Store() + { + for (int i = Math.Max(0, m_topIndex); i < m_objStack.Count; i++) + { + if (m_resetAction != null) + { + m_resetAction(m_objStack[i]); + } + } + m_topIndex = m_objStack.Count - 1; + } + + // 少用,调用这个池的作用就没有了 + public void Clear() + { + if (m_objStack != null) + m_objStack.Clear(); + } + + public int Count + { + get + { + if (m_objStack == null) + return 0; + return m_objStack.Count; + } + } + + public List.Enumerator GetIter() + { + if (m_objStack == null) + return new List.Enumerator(); + return m_objStack.GetEnumerator(); + } + private int m_topIndex = 0; + private List m_objStack = null; + private Action m_resetAction = null; + private CreateFunc m_createFunc = null; + } +} +#endif \ No newline at end of file diff --git a/Assets/Slua/LuaProfiler/Common/ObjectPool.cs.meta b/Assets/Slua/LuaProfiler/Common/ObjectPool.cs.meta new file mode 100644 index 00000000..f9df1335 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/ObjectPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 047a18bfa9169b94da0db4aa973b9980 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/Common/Parse.meta b/Assets/Slua/LuaProfiler/Common/Parse.meta new file mode 100644 index 00000000..a7d6a01f --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Parse.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c728c77c691363e45a9b18e716754e99 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/Common/Parse/Common.cs b/Assets/Slua/LuaProfiler/Common/Parse/Common.cs new file mode 100644 index 00000000..d7f73684 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Parse/Common.cs @@ -0,0 +1,128 @@ +#if UNITY_EDITOR +namespace UniLua +{ + public static class LuaConf + { + public const int LUAI_BITSINT = 32; + +#pragma warning disable 0429 + public const int LUAI_MAXSTACK = (LUAI_BITSINT >= 32) + ? 1000000 + : 15000 + ; +#pragma warning restore 0429 + + // reserve some space for error handling + public const int LUAI_FIRSTPSEUDOIDX = (-LUAI_MAXSTACK-1000); + + public const string LUA_SIGNATURE = "\u001bLua"; + public static string LUA_DIRSEP { + get { return System.IO.Path.DirectorySeparatorChar.ToString(); } + } + } + + public static class LuaLimits + { + public const int MAX_INT = System.Int32.MaxValue - 2; + public const int MAXUPVAL = System.Byte.MaxValue; + public const int LUAI_MAXCCALLS = 200; + public const int MAXSTACK = 250; + } + + public static class LuaDef + { + public const int LUA_MINSTACK = 20; + public const int BASIC_STACK_SIZE = LUA_MINSTACK * 2; + public const int EXTRA_STACK = 5; + + public const int LUA_RIDX_MAINTHREAD = 1; + public const int LUA_RIDX_GLOBALS = 2; + public const int LUA_RIDX_LAST = LUA_RIDX_GLOBALS; + + public const int LUA_MULTRET = -1; + + public const int LUA_REGISTRYINDEX = LuaConf.LUAI_FIRSTPSEUDOIDX; + + // number of list items accumulate before a SETLIST instruction + public const int LFIELDS_PER_FLUSH = 50; + + public const int LUA_IDSIZE = 60; + + public const string LUA_VERSION_MAJOR = "5"; + public const string LUA_VERSION_MINOR = "2"; + public const string LUA_VERSION = "Lua " + LUA_VERSION_MAJOR + "." + LUA_VERSION_MINOR; + + public const string LUA_ENV = "_ENV"; + + public const int BASE_CI_SIZE = 8; + } + + public static class LuaConstants + { + public const int LUA_NOREF = -2; + public const int LUA_REFNIL = -1; + } + + public enum LuaType + { + LUA_TNONE = -1, + LUA_TNIL = 0, + LUA_TBOOLEAN = 1, + LUA_TLIGHTUSERDATA = 2, + LUA_TNUMBER = 3, + LUA_TSTRING = 4, + LUA_TTABLE = 5, + LUA_TFUNCTION = 6, + LUA_TUSERDATA = 7, + LUA_TTHREAD = 8, + + LUA_TUINT64 = 9, + + LUA_NUMTAGS = 10, + + LUA_TPROTO, + LUA_TUPVAL, + LUA_TDEADKEY, + } + + public enum ClosureType + { + LUA, + CSHARP, + } + + public enum ThreadStatus + { + LUA_RESUME_ERROR = -1, + LUA_OK = 0, + LUA_YIELD = 1, + LUA_ERRRUN = 2, + LUA_ERRSYNTAX = 3, + LUA_ERRMEM = 4, + LUA_ERRGCMM = 5, + LUA_ERRERR = 6, + + LUA_ERRFILE = 7, + } + + /* ORDER TM */ + internal enum LuaOp + { + LUA_OPADD = 0, + LUA_OPSUB = 1, + LUA_OPMUL = 2, + LUA_OPDIV = 3, + LUA_OPMOD = 4, + LUA_OPPOW = 5, + LUA_OPUNM = 6, + } + + public enum LuaEq + { + LUA_OPEQ = 0, + LUA_OPLT = 1, + LUA_OPLE = 2, + } + +} +#endif diff --git a/Assets/Slua/LuaProfiler/Common/Parse/Common.cs.meta b/Assets/Slua/LuaProfiler/Common/Parse/Common.cs.meta new file mode 100644 index 00000000..666ce914 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Parse/Common.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: db0ce5a0e23232d4eacc46d0cf7f2390 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Slua/LuaProfiler/Common/Parse/LLex.cs b/Assets/Slua/LuaProfiler/Common/Parse/LLex.cs new file mode 100644 index 00000000..a425ab56 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Parse/LLex.cs @@ -0,0 +1,943 @@ + +using System; +using System.Collections.Generic; +using System.Text; +using NumberStyles = System.Globalization.NumberStyles; + +namespace UniLua +{ + public class LLexException : Exception + { + public LLexException(string info) : base(info) { } + } + + public enum TK + { + // reserved words + AND = 257, + BREAK, + CONTINUE, + DO, + ELSE, + ELSEIF, + END, + FALSE, + FOR, + FUNCTION, + GOTO, + IF, + IN, + LOCAL, + NIL, + NOT, + OR, + REPEAT, + RETURN, + THEN, + TRUE, + UNTIL, + WHILE, + // other terminal symbols + CONCAT, + DOTS, + EQ, + GE, + LE, + NE, + DBCOLON, + NUMBER, + STRING, + NAME, + EOS, + } + + public class StringLoadInfo + { + public StringLoadInfo(string s) + { + Str = new StringBuilder(s); + Pos = 0; + } + + public int ReadByte() + { + if (Pos >= Str.Length) + return -1; + else + return Str[Pos++]; + } + + public int PosChar + { + get + { + if (Pos >= Str.Length) + return (int)TK.EOS; + else + return Str[Pos]; + } + } + + public int PeekByte() + { + if (Pos >= Str.Length) + return -1; + else + return Str[Pos]; + } + + public string Replace(int start, int len, string value) + { + string result = Str.ToString().Substring(start, len); + Str = Str.Remove(start, len); + Str = Str.Insert(start, value); + if ((start + len) <= Pos) + { + Pos = Pos - (len - value.Length); + } + return result; + } + public string ReadString(int startPos, int len) + { + char[] chars = new char[len]; + for (int i = 0, imax = len; i < imax; i++) + { + chars[i] = Str[i + startPos]; + } + + return new string(chars); + } + + public void InsertString(int startPos, string value) + { + Str = Str.Insert(startPos, value); + Pos += value.Length; + } + public string code + { + get + { + return Str.ToString(); + } + } + private StringBuilder Str; + public int Pos; + } + + public class BytesLoadInfo + { + public BytesLoadInfo(byte[] bytes) + { + Bytes = bytes; + Pos = 0; + } + + public int ReadByte() + { + if (Pos >= Bytes.Length) + return -1; + else + return Bytes[Pos++]; + } + + public int PeekByte() + { + if (Pos >= Bytes.Length) + return -1; + else + return Bytes[Pos]; + } + + private byte[] Bytes; + private int Pos; + } + + public abstract class Token + { + public abstract int TokenType { get; } + + public bool EqualsToToken(Token other) + { + return TokenType == other.TokenType; + } + + public bool EqualsToToken(int other) + { + return TokenType == other; + } + + public bool EqualsToToken(TK other) + { + return TokenType == (int)other; + } + } + + public class CommentToken : Token + { + public CommentToken() + { + } + + public override int TokenType + { + get { return -1; } + } + + public override string ToString() + { + return "CommentToken"; + } + } + + public class LiteralToken : Token + { + private int _Literal; + + public LiteralToken(int literal) + { + _Literal = literal; + } + + public override int TokenType + { + get { return _Literal; } + } + + public override string ToString() + { + return string.Format("LiteralToken: {0}", (char)_Literal); + } + } + + public class TypedToken : Token + { + private TK _Type; + + public TypedToken(TK type) + { + _Type = type; + } + + public override int TokenType + { + get { return (int)_Type; } + } + + public override string ToString() + { + return string.Format("TypedToken: {0}", _Type); + } + } + + public class StringToken : TypedToken + { + public string SemInfo; + + public StringToken(string seminfo) : base(TK.STRING) + { + SemInfo = seminfo; + } + + public override string ToString() + { + return string.Format("StringToken: {0}", SemInfo); + } + } + + public class NameToken : TypedToken + { + public string SemInfo; + + public NameToken(string seminfo) : base(TK.NAME) + { + SemInfo = seminfo; + } + + public override string ToString() + { + return string.Format("NameToken: {0}", SemInfo); + } + } + + public class NumberToken : TypedToken + { + public double SemInfo; + + public NumberToken(double seminfo) : base(TK.NUMBER) + { + SemInfo = seminfo; + } + + public override string ToString() + { + return string.Format("NumberToken: {0}", SemInfo); + } + } + + public class LLex + { + public const char EOZ = Char.MaxValue; + + public int pos + { + get + { + return LoadInfo.Pos; + } + } + private int Current; + public int LineNumber; + public int LastLine; + private StringLoadInfo LoadInfo; + public string Source; + + public Token Token; + private Token LookAhead; + + private StringBuilder _Saved; + private StringBuilder Saved + { + get + { + if (_Saved == null) { _Saved = new StringBuilder(); } + return _Saved; + } + } + + private static Dictionary ReservedWordDict; + static LLex() + { + ReservedWordDict = new Dictionary(); + ReservedWordDict.Add("and", TK.AND); + ReservedWordDict.Add("break", TK.BREAK); + ReservedWordDict.Add("continue", TK.CONTINUE); + ReservedWordDict.Add("do", TK.DO); + ReservedWordDict.Add("else", TK.ELSE); + ReservedWordDict.Add("elseif", TK.ELSEIF); + ReservedWordDict.Add("end", TK.END); + ReservedWordDict.Add("false", TK.FALSE); + ReservedWordDict.Add("for", TK.FOR); + ReservedWordDict.Add("function", TK.FUNCTION); + ReservedWordDict.Add("goto", TK.GOTO); + ReservedWordDict.Add("if", TK.IF); + ReservedWordDict.Add("in", TK.IN); + ReservedWordDict.Add("local", TK.LOCAL); + ReservedWordDict.Add("nil", TK.NIL); + ReservedWordDict.Add("not", TK.NOT); + ReservedWordDict.Add("or", TK.OR); + ReservedWordDict.Add("repeat", TK.REPEAT); + ReservedWordDict.Add("return", TK.RETURN); + ReservedWordDict.Add("then", TK.THEN); + ReservedWordDict.Add("true", TK.TRUE); + ReservedWordDict.Add("until", TK.UNTIL); + ReservedWordDict.Add("while", TK.WHILE); + } + + public LLex(StringLoadInfo loadinfo, string name) + { + LoadInfo = loadinfo; + LineNumber = 1; + LastLine = 1; + Token = null; + LookAhead = null; + _Saved = null; + Source = name; + + _Next(); + } + + public string code + { + get + { + return LoadInfo.code; + } + } + + public void InsertString(int startPos, string value) + { + LoadInfo.InsertString(startPos, value); + } + + public void Next() + { + LastLine = LineNumber; + if (LookAhead != null) + { + Token = LookAhead; + LookAhead = null; + } + else + { + Token = _Lex(); + } + } + + public Token GetLookAhead() + { + Utl.Assert(LookAhead == null); + LookAhead = _Lex(); + return LookAhead; + } + + public string Replace(int start, int end, string value) + { + return LoadInfo.Replace(start, end + 1 - start, value); + } + + public string ReadString(int start, int end) + { + int len = end - start + 1; + return LoadInfo.ReadString(start, len); + } + + private void _Next() + { + var c = LoadInfo.ReadByte(); + Current = (c == -1) ? EOZ : c; + } + + private void _SaveAndNext() + { + Saved.Append((char)Current); + _Next(); + } + + private void _Save(char c) + { + Saved.Append(c); + } + + private string _GetSavedString() + { + return Saved.ToString(); + } + + private void _ClearSaved() + { + _Saved = null; + } + + private bool _CurrentIsNewLine() + { + return Current == '\n' || Current == '\r'; + } + + private bool _CurrentIsDigit() + { + return Char.IsDigit((char)Current); + } + + private bool _CurrentIsXDigit() + { + return _CurrentIsDigit() || + ('A' <= Current && Current <= 'F') || + ('a' <= Current && Current <= 'f'); + } + + private bool _CurrentIsSpace() + { + return Char.IsWhiteSpace((char)Current); + } + + private bool _CurrentIsAlpha() + { + return Char.IsLetter((char)Current); + } + + private bool _IsReserved(string identifier, out TK type) + { + return ReservedWordDict.TryGetValue(identifier, out type); + } + + public bool IsReservedWord(string name) + { + return ReservedWordDict.ContainsKey(name); + } + + private void _IncLineNumber() + { + var old = Current; + _Next(); + if (_CurrentIsNewLine() && Current != old) + _Next(); + if (++LineNumber >= Int32.MaxValue) + _Error("chunk has too many lines"); + } + + private string _ReadLongString(int sep) + { + _SaveAndNext(); + + if (_CurrentIsNewLine()) + _IncLineNumber(); + + while (true) + { + switch (Current) + { + case EOZ: + _LexError(_GetSavedString(), + "unfinished long string/comment", + (int)TK.EOS); + break; + + case '[': + { + if (_SkipSep() == sep) + { + _SaveAndNext(); + if (sep == 0) + { + _LexError(_GetSavedString(), + "nesting of [[...]] is deprecated", + (int)TK.EOS); + } + } + break; + } + + case ']': + { + if (_SkipSep() == sep) + { + _SaveAndNext(); + goto endloop; + } + break; + } + + case '\n': + case '\r': + { + _Save('\n'); + _IncLineNumber(); + break; + } + + default: + { + _SaveAndNext(); + break; + } + } + } + endloop: + var r = _GetSavedString(); + return r.Substring(2 + sep, r.Length - 2 * (2 + sep)); + } + + private void _EscapeError(string info, string msg) + { + _LexError("\\" + info, msg, (int)TK.STRING); + } + + private byte _ReadHexEscape() + { + int r = 0; + var c = new char[3] { 'x', (char)0, (char)0 }; + // read two hex digits + for (int i = 1; i < 3; ++i) + { + _Next(); + c[i] = (char)Current; + if (!_CurrentIsXDigit()) + { + _EscapeError(new String(c, 0, i + 1), + "hexadecimal digit expected"); + // error + } + r = (r << 4) + Int32.Parse(Current.ToString(), + NumberStyles.HexNumber); + } + return (byte)r; + } + + private byte _ReadDecEscape() + { + int r = 0; + var c = new char[3]; + // read up to 3 digits + int i = 0; + for (i = 0; i < 3 && _CurrentIsDigit(); ++i) + { + c[i] = (char)Current; + r = r * 10 + Current - '0'; + _Next(); + } + if (r > Byte.MaxValue) + _EscapeError(new String(c, 0, i), + "decimal escape too large"); + return (byte)r; + } + + private string _ReadString() + { + var del = Current; + _Next(); + while (Current != del) + { + switch (Current) + { + case EOZ: + _Error("unfinished string"); + continue; + + case '\n': + case '\r': + _Error("unfinished string"); + continue; + + case '\\': + { + byte c; + _Next(); + switch (Current) + { + case 'a': c = (byte)'\a'; break; + case 'b': c = (byte)'\b'; break; + case 'f': c = (byte)'\f'; break; + case 'n': c = (byte)'\n'; break; + case 'r': c = (byte)'\r'; break; + case 't': c = (byte)'\t'; break; + case 'v': c = (byte)'\v'; break; + case 'x': c = _ReadHexEscape(); break; + + case '\n': + case '\r': _Save('\n'); _IncLineNumber(); continue; + + case '\\': + case '\"': + case '\'': c = (byte)Current; break; + + case EOZ: continue; + + // zap following span of spaces + case 'z': + { + _Next(); // skip `z' + while (_CurrentIsSpace()) + { + if (_CurrentIsNewLine()) + _IncLineNumber(); + else + _Next(); + } + continue; + } + + default: + { + if (!_CurrentIsDigit()) + _EscapeError(Current.ToString(), + "invalid escape sequence"); + + // digital escape \ddd + c = _ReadDecEscape(); + _Save((char)c); + continue; + // { + // c = (char)0; + // for(int i=0; i<3 && _CurrentIsDigit(); ++i) + // { + // c = (char)(c*10 + Current - '0'); + // _Next(); + // } + // _Save( c ); + // } + // continue; + } + } + _Save((char)c); + _Next(); + continue; + } + + default: + _SaveAndNext(); + continue; + } + } + _Next(); + return _GetSavedString(); + } + + private double _ReadNumber() + { + var expo = new char[] { 'E', 'e' }; + Utl.Assert(_CurrentIsDigit()); + var first = Current; + _SaveAndNext(); + if (first == '0' && (Current == 'X' || Current == 'x')) + { + expo = new char[] { 'P', 'p' }; + _SaveAndNext(); + } + for (; ; ) + { + if (Current == expo[0] || Current == expo[1]) + { + _SaveAndNext(); + if (Current == '+' || Current == '-') + _SaveAndNext(); + } + if (_CurrentIsXDigit() || Current == '.') + _SaveAndNext(); + else + break; + } + + double ret; + var str = _GetSavedString(); + if (O_Str2Decimal(str, out ret)) + { + return ret; + } + else + { + _Error("malformed number: " + str); + return 0.0; + } + } + + public static bool O_Str2Decimal(string s, out double result) + { + result = 0.0; + + if (s.Contains("n") || s.Contains("N")) // reject `inf' and `nan' + return false; + + int pos = 0; + if (s.Contains("x") || s.Contains("X")) + result = Utl.StrX2Number(s, ref pos); + else + result = Utl.Str2Number(s, ref pos); + + if (pos == 0) + return false; // nothing recognized + + while (pos < s.Length && Char.IsWhiteSpace(s[pos])) ++pos; + return pos == s.Length; // OK if no trailing characters + } + + // private float _ReadNumber() + // { + // do + // { + // _SaveAndNext(); + // } while( _CurrentIsDigit() || Current == '.' ); + // if( Current == 'E' || Current == 'e' ) + // { + // _SaveAndNext(); + // if( Current == '+' || Current == '-' ) + // _SaveAndNext(); + // } + // while( _CurrentIsAlpha() || _CurrentIsDigit() || Current == '_' ) + // _SaveAndNext(); + // float ret; + // if( !Single.TryParse( _GetSavedString(), out ret ) ) + // _Error( "malformed number" ); + // return ret; + // } + + private void _Error(string error) + { + throw new Exception(string.Format("{0}:{1}: {2}", Source, LineNumber, error)); + } + + private void _LexError(string info, string msg, int tokenType) + { + // TODO + _Error(msg + ":" + info); + } + + public void SyntaxError(string msg) + { + // TODO + _Error(msg); + } + + private int _SkipSep() + { + int count = 0; + var boundary = Current; + _SaveAndNext(); + while (Current == '=') + { + _SaveAndNext(); + count++; + } + return (Current == boundary ? count : (-count) - 1); + } + + private Token _Lex() + { + _ClearSaved(); + while (true) + { + switch (Current) + { + case '\n': + case '\r': + { + _IncLineNumber(); + continue; + } + + case '-': + { + _Next(); + if (Current != '-') return new LiteralToken('-'); + + // else is a long comment + _Next(); + if (Current == '[') + { + int sep = _SkipSep(); + _ClearSaved(); + if (sep >= 0) + { + _ReadLongString(sep); + _ClearSaved(); + return new CommentToken(); + } + } + + // else is a short comment + while (!_CurrentIsNewLine() && Current != EOZ) + _Next(); + return new CommentToken(); + } + + case '[': + { + int sep = _SkipSep(); + if (sep >= 0) + { + string seminfo = _ReadLongString(sep); + return new StringToken(seminfo); + } + else if (sep == -1) return new LiteralToken('['); + else _Error("invalid long string delimiter"); + continue; + } + + case '=': + { + _Next(); + if (Current != '=') return new LiteralToken('='); + _Next(); + return new TypedToken(TK.EQ); + } + + case '<': + { + _Next(); + if (Current != '=') return new LiteralToken('<'); + _Next(); + return new TypedToken(TK.LE); + } + + case '>': + { + _Next(); + if (Current != '=') return new LiteralToken('>'); + _Next(); + return new TypedToken(TK.GE); + } + + case '~': + { + _Next(); + if (Current != '=') return new LiteralToken('~'); + _Next(); + return new TypedToken(TK.NE); + } + + case ':': + { + _Next(); + if (Current != ':') return new LiteralToken(':'); + _Next(); + return new TypedToken(TK.DBCOLON); // new in 5.2 ? + } + + case '"': + case '\'': + { + return new StringToken(_ReadString()); + } + + case '.': + { + _SaveAndNext(); + if (Current == '.') + { + _SaveAndNext(); + if (Current == '.') + { + _SaveAndNext(); + return new TypedToken(TK.DOTS); + } + else + { + return new TypedToken(TK.CONCAT); + } + } + else if (!_CurrentIsDigit()) + return new LiteralToken('.'); + else + return new NumberToken(_ReadNumber()); + } + + case EOZ: + { + return new TypedToken(TK.EOS); + } + + default: + { + if (_CurrentIsSpace()) + { + _Next(); + continue; + } + else if (_CurrentIsDigit()) + { + return new NumberToken(_ReadNumber()); + } + else if (_CurrentIsAlpha() || Current == '_') + { + do + { + _SaveAndNext(); + } while (_CurrentIsAlpha() || + _CurrentIsDigit() || + Current == '_'); + + string identifier = _GetSavedString(); + TK type; + if (_IsReserved(identifier, out type)) + { + return new TypedToken(type); + } + else + { + return new NameToken(identifier); + } + } + else + { + var c = Current; + _Next(); + return new LiteralToken(c); + } + } + } + } + } + + } + +} + diff --git a/Assets/Slua/LuaProfiler/Common/Parse/LLex.cs.meta b/Assets/Slua/LuaProfiler/Common/Parse/LLex.cs.meta new file mode 100644 index 00000000..e12b4185 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Parse/LLex.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 45c6de50fce82bc4fb1287ae03999b43 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Slua/LuaProfiler/Common/Parse/Parse.cs b/Assets/Slua/LuaProfiler/Common/Parse/Parse.cs new file mode 100644 index 00000000..d75eea00 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Parse/Parse.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using UniLua; + +namespace MikuLuaProfiler +{ + public static class Parse + { + #region parse + public static string InsertSample(string value, string name) + { + LLex l = new LLex(new StringLoadInfo(value), name); + l.InsertString(0, "BeginMikuSample(\"" + name + ", line:1 require file\")\r\n"); + int lastPos = 0; + int nextPos = l.pos; + l.Next(); + int tokenType = l.Token.TokenType; + + lastPos = nextPos; + nextPos = l.pos; + + InsertSample(l, ref lastPos, ref nextPos, tokenType, false); + + return l.code; + } + + static void InsertSample(LLex l, ref int lastPos, ref int nextPos, int tokenType, bool onlyFun) + { + Stack tokens = new Stack(); + + bool needLastSample = true; + bool hasReturn = false; + int lastStackToken = -1; + while (tokenType != (int)TK.EOS) + { + switch (tokenType) + { + case (int)TK.FUNCTION: + hasReturn = false; + tokens.Push(tokenType); + lastStackToken = tokenType; + string funName = ""; + bool isLeft = false; + + while (tokenType != (int)TK.EOS) + { + l.Next(); + tokenType = l.Token.TokenType; + + lastPos = nextPos; + nextPos = l.pos; + if (!isLeft) + { + if (l.Token is NameToken) + { + funName += ((NameToken)l.Token).SemInfo; + } + else if ((l.Token.TokenType == (int)':')) + { + funName += ':'; + } + else if ((l.Token.TokenType == (int)'.')) + { + funName += '.'; + } + } + + + if (tokenType == (int)'(') + { + isLeft = true; + } + + if (tokenType == (int)')') + { + l.InsertString(nextPos - 1, "\r\nBeginMikuSample(\"" + l.Source + + ",line:" + l.LineNumber + " funName:" + funName + "\")\r\n"); + nextPos = l.pos; + break; + } + } + break; + case (int)TK.IF: + case (int)TK.FOR: + case (int)TK.WHILE: + if (tokens.Count > 0) + { + tokens.Push(tokenType); + lastStackToken = tokenType; + } + break; + case (int)TK.RETURN: + int insertPos = lastPos - 1; + + if (tokens.Count == 0) + { + needLastSample = false; + } + Token lastTokenType = null; + int commentPos = insertPos; + while (tokenType != (int)TK.EOS) + { + lastTokenType = l.Token; + l.Next(); + + tokenType = l.Token.TokenType; + if (l.Token is CommentToken && !(lastTokenType is CommentToken) ) + { + commentPos = nextPos - 1; + } + lastPos = nextPos; + nextPos = l.pos; + + if (tokenType == (int)TK.FUNCTION) + { + InsertSample(l, ref lastPos, ref nextPos, tokenType, true); + tokenType = l.Token.TokenType; + if (l.Token is CommentToken && !(lastTokenType is CommentToken)) + { + commentPos = lastPos - 1; + } + lastTokenType = l.Token; + } + + if (tokenType == (int)TK.END + || tokenType == (int)TK.ELSEIF + || tokenType == (int)TK.ELSE + || tokenType == (int)TK.EOS) + { + if (lastTokenType is CommentToken) + { + lastPos = commentPos; + } + + string returnStr = l.ReadString(insertPos, lastPos - 1); ; + + returnStr = returnStr.Trim(); + + if (returnStr.Length > 0 && returnStr[returnStr.Length - 1] == ';') + { + returnStr = returnStr.Substring(0, returnStr.Length - 1); + } + returnStr = "\r\nreturn miku_unpack_return_value(" + returnStr.Substring(6, returnStr.Length - 6).Trim() + ")\r\n"; + + l.Replace(insertPos, lastPos - 1, returnStr); + nextPos = l.pos; + if (tokenType == (int)TK.END) + { + if (tokens.Count > 0) + tokens.Pop(); + if (onlyFun && tokens.Count <= 0) + { + l.Next(); + lastPos = nextPos; + nextPos = l.pos; + return; + } + } + break; + } + } + + if (lastStackToken != (int)TK.IF) + { + hasReturn = true; + } + break; + case (int)TK.END: + if (tokens.Count > 0) + { + int token = tokens.Pop(); + if (token == (int)TK.FUNCTION) + { + if (!hasReturn) + { + l.InsertString(lastPos, "\r\nEndMikuSample()\r\n"); + lastPos = nextPos; + nextPos = l.pos; + } + if (onlyFun && tokens.Count <= 0) + { + l.Next(); + lastPos = nextPos; + nextPos = l.pos; + return; + } + } + if (tokens.Count > 0) + { + var tA = tokens.ToArray(); + lastStackToken = tA[tA.Length - 1]; + } + hasReturn = false; + } + break; + } + l.Next(); + tokenType = l.Token.TokenType; + lastPos = nextPos; + nextPos = l.pos; + } + + if (needLastSample) + { + l.InsertString(nextPos, "\r\nEndMikuSample()"); + } + } + #endregion + } +} diff --git a/Assets/Slua/LuaProfiler/Common/Parse/Parse.cs.meta b/Assets/Slua/LuaProfiler/Common/Parse/Parse.cs.meta new file mode 100644 index 00000000..dec4f822 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Parse/Parse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a52e2cc637b97674e95c3febdf48dc15 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/Common/Parse/Util.cs b/Assets/Slua/LuaProfiler/Common/Parse/Util.cs new file mode 100644 index 00000000..f8b15381 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Parse/Util.cs @@ -0,0 +1,225 @@ + +#define API_CHECK +#define UNILUA_ASSERT + +using System; + +namespace UniLua +{ + using DebugS = System.Diagnostics.Debug; + using NumberStyles = System.Globalization.NumberStyles; + + internal static class Utl + { + private static void Throw( params string[] msgs ) + { + throw new Exception(String.Join("", msgs)); + } + + public static void Assert( bool condition ) + { +#if UNILUA_ASSERT + if( !condition ) + Throw("assert failed!" ); + DebugS.Assert( condition ); +#endif + } + + public static void Assert( bool condition, string message ) + { +#if UNILUA_ASSERT + if( !condition ) + Throw( "assert failed! ", message ); + DebugS.Assert( condition, message ); +#endif + } + + public static void Assert( bool condition, string message, string detailMessage ) + { +#if UNILUA_ASSERT + if( !condition ) + Throw( "assert failed! ", message, "\n", detailMessage ); + DebugS.Assert( condition, message, detailMessage ); +#endif + } + + public static void ApiCheck( bool condition, string message ) + { +#if UNILUA_ASSERT +#if API_CHECK + Assert( condition, message ); +#endif +#endif + } + + private static bool IsNegative( string s, ref int pos ) + { + if( pos >= s.Length ) + return false; + + char c = s[pos]; + if( c == '-' ) + { + ++pos; + return true; + } + else if( c == '+' ) + { + ++pos; + } + return false; + } + + private static bool IsXDigit( char c ) + { + if( Char.IsDigit( c ) ) + return true; + + if( 'a' <= c && c <= 'f' ) + return true; + + if( 'A' <= c && c <= 'F' ) + return true; + + return false; + } + + private static double ReadHexa( string s, ref int pos, double r, out int count ) + { + count = 0; + while( pos < s.Length && IsXDigit( s[pos] ) ) + { + r = (r * 16.0) + Int32.Parse( s[pos].ToString(), NumberStyles.HexNumber ); + ++pos; + ++count; + } + return r; + } + + private static double ReadDecimal( string s, ref int pos, double r, out int count ) + { + count = 0; + while( pos < s.Length && Char.IsDigit( s[pos] ) ) + { + r = (r * 10.0) + Int32.Parse( s[pos].ToString() ); + ++pos; + ++count; + } + return r; + } + + // following C99 specification for 'strtod' + public static double StrX2Number( string s, ref int curpos ) + { + int pos = curpos; + while( pos < s.Length && Char.IsWhiteSpace( s[pos] )) ++pos; + bool negative = IsNegative( s, ref pos ); + + // check `0x' + if( pos >= s.Length || !(s[pos] == '0' && (s[pos+1] == 'x' || s[pos+1] == 'X')) ) + return 0.0; + + pos += 2; // skip `0x' + + double r = 0.0; + int i = 0; + int e = 0; + r = ReadHexa( s, ref pos, r, out i ); + if( pos < s.Length && s[pos] == '.' ) + { + ++pos; // skip `.' + r = ReadHexa( s, ref pos, r, out e ); + } + if( i == 0 && e == 0 ) + return 0.0; // invalid format (no digit) + + // each fractional digit divides value by 2^-4 + e *= -4; + curpos = pos; + + // exponent part + if( pos < s.Length && (s[pos] == 'p' || s[pos] == 'P') ) + { + ++pos; // skip `p' + bool expNegative = IsNegative( s, ref pos ); + if( pos >= s.Length || !Char.IsDigit( s[pos] ) ) + goto ret; + + int exp1 = 0; + while( pos < s.Length && Char.IsDigit( s[pos] ) ) + { + exp1 = exp1 * 10 + Int32.Parse( s[pos].ToString() ); + ++pos; + } + if( expNegative ) + exp1 = -exp1; + e += exp1; + } + curpos = pos; + +ret: + if( negative ) r = -r; + + return r * Math.Pow(2.0, e); + } + + public static double Str2Number( string s, ref int curpos ) + { + int pos = curpos; + while( pos < s.Length && Char.IsWhiteSpace( s[pos] )) ++pos; + bool negative = IsNegative( s, ref pos ); + + double r = 0.0; + int i = 0; + int f = 0; + r = ReadDecimal( s, ref pos, r, out i ); + if( pos < s.Length && s[pos] == '.' ) + { + ++pos; + r = ReadDecimal( s, ref pos, r, out f ); + } + if( i == 0 && f == 0 ) + return 0.0; + + f = -f; + curpos = pos; + + // exponent part + double e = 0.0; + if( pos < s.Length && (s[pos] == 'e' || s[pos] == 'E') ) + { + ++pos; + bool expNegative = IsNegative( s, ref pos ); + if( pos >= s.Length || !Char.IsDigit( s[pos] ) ) + goto ret; + + int n; + e = ReadDecimal( s, ref pos, e, out n ); + if( expNegative ) + e = -e; + f += (int)e; + } + curpos = pos; + +ret: + if( negative ) r = -r; + + return r * Math.Pow(10, f); + } + + public static string TrimWhiteSpace( string str ) + { + int s = 0; + int e = str.Length; + + while( s < str.Length && Char.IsWhiteSpace( str[s] ) ) ++s; + if( s >= e ) + return ""; + + while( e >= 0 && Char.IsWhiteSpace( str[e-1] ) ) --e; + return str.Substring( s, e-s ); + } + } + +} + diff --git a/Assets/Slua/LuaProfiler/Common/Parse/Util.cs.meta b/Assets/Slua/LuaProfiler/Common/Parse/Util.cs.meta new file mode 100644 index 00000000..77be9fe8 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Parse/Util.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e8e03e8ba75219a4b9ed5f669f6bc86b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Slua/LuaProfiler/Common/Plugins.meta b/Assets/Slua/LuaProfiler/Common/Plugins.meta new file mode 100644 index 00000000..ef91f0bb --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ce88095a24ce5a04797f2335bf7c7fb7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/Common/Plugins/MonoHook.dll b/Assets/Slua/LuaProfiler/Common/Plugins/MonoHook.dll new file mode 100644 index 00000000..8b10642e Binary files /dev/null and b/Assets/Slua/LuaProfiler/Common/Plugins/MonoHook.dll differ diff --git a/Assets/Slua/LuaProfiler/Common/Plugins/MonoHook.dll.meta b/Assets/Slua/LuaProfiler/Common/Plugins/MonoHook.dll.meta new file mode 100644 index 00000000..5287a4b4 --- /dev/null +++ b/Assets/Slua/LuaProfiler/Common/Plugins/MonoHook.dll.meta @@ -0,0 +1,107 @@ +fileFormatVersion: 2 +guid: c6d09b9a6b740e34e8f15528fdc572ee +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + isPreloaded: 0 + isOverridable: 0 + platformData: + - first: + '': Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux: 1 + Exclude Linux64: 1 + Exclude LinuxUniversal: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Facebook: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Facebook: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Linux + second: + enabled: 0 + settings: + CPU: x86 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: x86_64 + - first: + Standalone: LinuxUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 0 + settings: + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/HookSetup.meta b/Assets/Slua/LuaProfiler/HookSetup.meta new file mode 100644 index 00000000..fdee4569 --- /dev/null +++ b/Assets/Slua/LuaProfiler/HookSetup.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3026c398091420c44925795549cfcd6e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/HookSetup/SLuaHookSetup.cs b/Assets/Slua/LuaProfiler/HookSetup/SLuaHookSetup.cs new file mode 100644 index 00000000..878e91f5 --- /dev/null +++ b/Assets/Slua/LuaProfiler/HookSetup/SLuaHookSetup.cs @@ -0,0 +1,478 @@ +/* +* ============================================================================== +* Filename: SLuaHookSetup +* Created: 2018/7/2 11:36:16 +* Author: エル・プサイ・コングリィ +* Purpose: +* ============================================================================== +*/ + +//#define SLUA + +#if SLUA +#if UNITY_EDITOR + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; +using SLua; +using LuaDLL = SLua.LuaDLL; + +namespace MikuLuaProfiler { + + public class LuaLib + { + public static void RunGC() + { + var env = LuaProfiler.mainL; + if (env != IntPtr.Zero) + { + LuaDLL.lua_gc(env, LuaGCOptions.LUA_GCCOLLECT, 0); + } + } + public static void StopGC() + { + var env = LuaProfiler.mainL; + if (env != IntPtr.Zero) + { + LuaDLL.lua_gc(env, LuaGCOptions.LUA_GCSTOP, 0); + } + } + public static void ResumeGC() + { + var env = LuaProfiler.mainL; + if (env != IntPtr.Zero) + { + LuaDLL.lua_gc(env, LuaGCOptions.LUA_GCRESTART, 0); + } + } + + public static long GetLuaMemory(IntPtr luaState) + { + long result = 0; + + result = LuaDLL.lua_gc(luaState, LuaGCOptions.LUA_GCCOUNT, 0); + result = result * 1024 + LuaDLL.lua_gc(luaState, LuaGCOptions.LUA_GCCOUNTB, 0); + + return result; + } + } + + [InitializeOnLoad] + public static class Startup + { + private static MethodHooker hookNewLuaEnv; + + public static readonly string luaPath; + static Startup() + { + HookNewLuaEnv(); + } + + public static void HookNewLuaEnv() + { + if (hookNewLuaEnv == null) + { + Type envReplace = typeof(LuaEnvReplace); + Type typeEnv = typeof(LuaState); + var clickFun = typeEnv.GetConstructors()[0]; + MethodInfo clickReplace = envReplace.GetMethod("Ctor"); + MethodInfo clickProxy = envReplace.GetMethod("Proxy", BindingFlags.Public | BindingFlags.Static); + hookNewLuaEnv = new MethodHooker(clickFun, clickReplace, clickProxy); + hookNewLuaEnv.Install(); + + Type typeDll = typeof(LuaDLL); + var newstateFun = typeDll.GetMethod("luaL_newstate"); + clickReplace = envReplace.GetMethod("luaL_newstate"); + clickProxy = envReplace.GetMethod("ProxyNewstate", BindingFlags.Public | BindingFlags.Static); + hookNewLuaEnv = new MethodHooker(newstateFun, clickReplace, clickProxy); + hookNewLuaEnv.Install(); + + newstateFun = typeDll.GetMethod("lua_close"); + clickReplace = envReplace.GetMethod("lua_close"); + clickProxy = envReplace.GetMethod("ProxyClose", BindingFlags.Public | BindingFlags.Static); + hookNewLuaEnv = new MethodHooker(newstateFun, clickReplace, clickProxy); + hookNewLuaEnv.Install(); + } + } + + public static class LuaEnvReplace + { + public static void Ctor(LuaState env) + { + Proxy(env); + MikuLuaProfiler.HookSetup.SetMainLuaEnv(env); + } + public static void Proxy(LuaState env) + { + } + + public static void lua_close(IntPtr luaState) + { + if (LuaProfiler.mainL == luaState) + { + LuaProfiler.mainL = IntPtr.Zero; + HookSetup.Uninstall(); + } + } + + public static void ProxyClose(IntPtr luaState) + { + } + + public static IntPtr luaL_newstate() + { + IntPtr l = ProxyNewstate(); + MikuLuaProfiler.LuaProfiler.mainL = l; + return l; + } + public static IntPtr ProxyNewstate() + { + return IntPtr.Zero; + } + } + + } + + [InitializeOnLoad] + static class HookSetup + { +#if !UNITY_2017_1_OR_NEWER + static bool isPlaying = false; +#endif + static HookSetup() + { +#if UNITY_2017_1_OR_NEWER + EditorApplication.playModeStateChanged += OnEditorPlaying; +#else + EditorApplication.playmodeStateChanged += () => + { + + if (isPlaying == true && EditorApplication.isPlaying == false) + { + SetMainLuaEnv(null); + } + + isPlaying = EditorApplication.isPlaying; + }; +#endif + } + + + public static void SetMainLuaEnv(LuaState env) + { + if (LuaDeepProfilerSetting.Instance.isDeepProfiler) + { + if (env != null) + { + Lua_MikuLuaProfiler_LuaProfiler.reg(LuaProfiler.mainL); + env.doString(@" +BeginMikuSample = MikuLuaProfiler.LuaProfiler.BeginSample +EndMikuSample = MikuLuaProfiler.LuaProfiler.EndSample + +function miku_unpack_return_value(...) + EndMikuSample() + return ... +end +"); + HookSetup.HookLuaFuns(); + } + } + + if (env == null) + { + HookSetup.Uninstall(); + LuaProfiler.mainL = IntPtr.Zero; + } + } + +#if UNITY_2017_1_OR_NEWER + public static void OnEditorPlaying(PlayModeStateChange playModeStateChange) + { + if (playModeStateChange == PlayModeStateChange.ExitingPlayMode) + { + SetMainLuaEnv(null); + } + } +#endif + +#region hook + +#region hook tostring + + public class LuaDll + { +#region luastring + public static readonly Dictionary stringDict = new Dictionary(); + public static bool TryGetLuaString(IntPtr p, out string result) + { + + return stringDict.TryGetValue((long)p, out result); + } + public static void RefString(IntPtr strPoint, int index, string s, IntPtr L) + { + int oldTop = LuaDLL.lua_gettop(L); + LuaDLL.lua_pushvalue(L, index); + //把字符串ref了之后就不GC了 + LuaDLL.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX); + LuaDLL.lua_settop(L, oldTop); + stringDict[(long)strPoint] = s; + } +#endregion + + public static int luaL_loadbuffer(IntPtr L, byte[] buff, int size, string name) + { + if (LuaDeepProfilerSetting.Instance.isDeepProfiler)//&& name != "chunk" + { + var utf8WithoutBom = new System.Text.UTF8Encoding(true); + string fileName = name.Replace("@", "").Replace("/", ".") + ".lua"; + string value = utf8WithoutBom.GetString(buff); + value = Parse.InsertSample(value, fileName); + + //System.IO.File.WriteAllText(fileName, value); + + buff = utf8WithoutBom.GetBytes(value); + size = buff.Length; + } + + return ProxyLoadbuffer(L, buff, size, name); + } + + public static int ProxyLoadbuffer(IntPtr L, byte[] buff, int size, string name) + { + return 0; + } + + public static string lua_tostring(IntPtr L, int index) + { + int strlen; + + IntPtr str = LuaDLL.luaS_tolstring32(L, index, out strlen); // fix il2cpp 64 bit + string s = null; + if (TryGetLuaString(str, out s)) + { + return s; + } + + if (strlen > 0 && str != IntPtr.Zero) + { + s = Marshal.PtrToStringAnsi(str); + // fallback method + if (s == null) + { + byte[] b = new byte[strlen]; + Marshal.Copy(str, b, 0, strlen); + s = System.Text.Encoding.Default.GetString(b); + } + } + if (s != null) + { + RefString(str, index, s, L); + } + + return (s == null) ? string.Empty : s; + } + + public static string PoxyToString(IntPtr L, int index) + { + return null; + } + } +#endregion + + +#region hook profiler + public class Profiler + { + private static Stack m_Stack = new Stack(); + private static int m_currentFrame = 0; + public static void BeginSampleOnly(string name) + { + if (ProfilerDriver.deepProfiling) return; + if (Time.frameCount != m_currentFrame) + { + m_Stack.Clear(); + m_currentFrame = Time.frameCount; + } + m_Stack.Push(name); + ProxyBeginSample(name); + } + public static void BeginSample(string name, UnityEngine.Object targetObject) + { + if (ProfilerDriver.deepProfiling) return; + m_Stack.Push(name); + ProxyBeginSample(name, targetObject); + } + + public static void EndSample() + { + if (ProfilerDriver.deepProfiling) return; + if (m_Stack.Count <= 0) + { + return; + } + m_Stack.Pop(); + ProxyEndSample(); + } + + public static void ProxyBeginSample(string name) + { + } + public static void ProxyBeginSample(string name, UnityEngine.Object targetObject) + { + } + + public static void ProxyEndSample() + { + } + } +#endregion + +#region do hook + private static MethodHooker beginSampeOnly; + private static MethodHooker beginObjetSample; + private static MethodHooker endSample; + private static MethodHooker tostringHook; + private static MethodHooker loaderHook; + + private static bool m_hooked = false; + public static void HookLuaFuns() + { + if (m_hooked) return; + if (tostringHook == null) + { + Type typeLogReplace = typeof(LuaDll); + Type typeLog = typeof(LuaDLL); + MethodInfo tostringFun = typeLog.GetMethod("lua_tostring"); + MethodInfo tostringReplace = typeLogReplace.GetMethod("lua_tostring"); + MethodInfo tostringProxy = typeLogReplace.GetMethod("ProxyToString"); + + tostringHook = new MethodHooker(tostringFun, tostringReplace, tostringProxy); + tostringHook.Install(); + + tostringFun = typeLog.GetMethod("luaL_loadbuffer"); + tostringReplace = typeLogReplace.GetMethod("luaL_loadbuffer"); + tostringProxy = typeLogReplace.GetMethod("ProxyLoadbuffer"); + + tostringHook = new MethodHooker(tostringFun, tostringReplace, tostringProxy); + tostringHook.Install(); + } + + if (beginSampeOnly == null) + { + Type typeTarget = typeof(UnityEngine.Profiling.Profiler); + Type typeReplace = typeof(Profiler); + + MethodInfo hookTarget = typeTarget.GetMethod("BeginSampleOnly", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(string) }, null); + MethodInfo hookReplace = typeReplace.GetMethod("BeginSampleOnly"); + MethodInfo hookProxy = typeReplace.GetMethod("ProxyBeginSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, null); + beginSampeOnly = new MethodHooker(hookTarget, hookReplace, hookProxy); + beginSampeOnly.Install(); + + hookTarget = typeTarget.GetMethod("BeginSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), typeof(UnityEngine.Object) }, null); + hookReplace = typeReplace.GetMethod("BeginSample"); + hookProxy = typeReplace.GetMethod("ProxyBeginSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), typeof(UnityEngine.Object) }, null); + beginObjetSample = new MethodHooker(hookTarget, hookReplace, hookProxy); + beginObjetSample.Install(); + + hookTarget = typeTarget.GetMethod("EndSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { }, null); + hookReplace = typeReplace.GetMethod("EndSample"); + hookProxy = typeReplace.GetMethod("ProxyEndSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { }, null); + endSample = new MethodHooker(hookTarget, hookReplace, hookProxy); + endSample.Install(); + } + + m_hooked = true; + } + + public static void Uninstall() + { + if (beginSampeOnly != null) + { + beginSampeOnly.Uninstall(); + beginSampeOnly = null; + } + if (beginObjetSample != null) + { + beginObjetSample.Uninstall(); + beginObjetSample = null; + } + if (endSample != null) + { + endSample.Uninstall(); + endSample = null; + } + if (tostringHook != null) + { + tostringHook.Uninstall(); + tostringHook = null; + } + if (loaderHook != null) + { + loaderHook.Uninstall(); + loaderHook = null; + } + + m_hooked = false; + } +#endregion + +#endregion + } + + #region bind + [UnityEngine.Scripting.Preserve] + public class Lua_MikuLuaProfiler_LuaProfiler : LuaObject + { + [SLua.MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] + [UnityEngine.Scripting.Preserve] + static public int BeginSample_s(IntPtr l) + { + try + { + System.String a1; + checkType(l, 1, out a1); + MikuLuaProfiler.LuaProfiler.BeginSample(a1); + pushValue(l, true); + return 1; + } + catch (Exception e) + { + return error(l, e); + } + } + [SLua.MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] + [UnityEngine.Scripting.Preserve] + static public int EndSample_s(IntPtr l) + { + try + { + MikuLuaProfiler.LuaProfiler.EndSample(); + pushValue(l, true); + return 1; + } + catch (Exception e) + { + return error(l, e); + } + } + [UnityEngine.Scripting.Preserve] + static public void reg(IntPtr l) + { + getTypeTable(l, "MikuLuaProfiler.LuaProfiler"); + addMember(l, BeginSample_s); + addMember(l, EndSample_s); + createTypeMetatable(l, typeof(MikuLuaProfiler.LuaProfiler)); + } + } + #endregion +} + +#endif + +#endif \ No newline at end of file diff --git a/Assets/Slua/LuaProfiler/HookSetup/SLuaHookSetup.cs.meta b/Assets/Slua/LuaProfiler/HookSetup/SLuaHookSetup.cs.meta new file mode 100644 index 00000000..45135d2f --- /dev/null +++ b/Assets/Slua/LuaProfiler/HookSetup/SLuaHookSetup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f34f72e56a74d4f49bf98472ecaa4bb0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/HookSetup/ToLuaHookSetup.cs b/Assets/Slua/LuaProfiler/HookSetup/ToLuaHookSetup.cs new file mode 100644 index 00000000..5d58d06c --- /dev/null +++ b/Assets/Slua/LuaProfiler/HookSetup/ToLuaHookSetup.cs @@ -0,0 +1,502 @@ +/* +* ============================================================================== +* Filename: ToLuaHookSetup +* Created: 2018/7/2 11:36:16 +* Author: エル・プサイ・コングリィ +* Purpose: +* ============================================================================== +*/ + +//#define ToLua + +#if ToLua +#if UNITY_EDITOR + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; +using LuaInterface; +using LuaDLL = LuaInterface.LuaDLL; + +namespace MikuLuaProfiler +{ + + public class LuaLib + { + public static void RunGC() + { + var env = LuaProfiler.mainL; + if (env != IntPtr.Zero) + { + LuaDLL.lua_gc(env, LuaGCOptions.LUA_GCCOLLECT, 0); + } + } + public static void StopGC() + { + var env = LuaProfiler.mainL; + if (env != IntPtr.Zero) + { + LuaDLL.lua_gc(env, LuaGCOptions.LUA_GCSTOP, 0); + } + } + public static void ResumeGC() + { + var env = LuaProfiler.mainL; + if (env != IntPtr.Zero) + { + LuaDLL.lua_gc(env, LuaGCOptions.LUA_GCRESTART, 0); + } + } + + public static long GetLuaMemory(IntPtr luaState) + { + long result = 0; + + result = LuaDLL.lua_gc(luaState, LuaGCOptions.LUA_GCCOUNT, 0); + result = result * 1024 + LuaDLL.lua_gc(luaState, LuaGCOptions.LUA_GCCOUNTB, 0); + + return result; + } + } + + [InitializeOnLoad] + public static class Startup + { + private static MethodHooker hookNewLuaEnv; + + public static readonly string luaPath; + static Startup() + { + HookNewLuaEnv(); + } + + public static void HookNewLuaEnv() + { + if (hookNewLuaEnv == null) + { + Type envReplace = typeof(LuaEnvReplace); + Type typeEnv = typeof(LuaState); + var clickFun = typeEnv.GetMethod("Start"); + MethodInfo clickReplace = envReplace.GetMethod("Start"); + MethodInfo clickProxy = envReplace.GetMethod("Proxy", BindingFlags.Public | BindingFlags.Static); + hookNewLuaEnv = new MethodHooker(clickFun, clickReplace, clickProxy); + hookNewLuaEnv.Install(); + + Type typeDll = typeof(LuaDLL); + var newstateFun = typeDll.GetMethod("luaL_newstate"); + clickReplace = envReplace.GetMethod("luaL_newstate"); + clickProxy = envReplace.GetMethod("ProxyNewstate", BindingFlags.Public | BindingFlags.Static); + hookNewLuaEnv = new MethodHooker(newstateFun, clickReplace, clickProxy); + hookNewLuaEnv.Install(); + + newstateFun = typeDll.GetMethod("lua_close"); + clickReplace = envReplace.GetMethod("lua_close"); + clickProxy = envReplace.GetMethod("ProxyClose", BindingFlags.Public | BindingFlags.Static); + hookNewLuaEnv = new MethodHooker(newstateFun, clickReplace, clickProxy); + hookNewLuaEnv.Install(); + } + } + + public static class LuaEnvReplace + { + public static void Start(LuaState env) + { + Proxy(env); + HookSetup.SetMainLuaEnv(env); + } + public static void Proxy(LuaState env) + { + } + + public static void lua_close(IntPtr luaState) + { + if (LuaProfiler.mainL == luaState) + { + LuaProfiler.mainL = IntPtr.Zero; + HookSetup.Uninstall(); + } + } + public static void ProxyClose(IntPtr luaState) + { + } + + public static IntPtr luaL_newstate() + { + IntPtr l = ProxyNewstate(); + MikuLuaProfiler.LuaProfiler.mainL = l; + return l; + } + public static IntPtr ProxyNewstate() + { + return IntPtr.Zero; + } + } + + } + + [InitializeOnLoad] + static class HookSetup + { +#if !UNITY_2017_1_OR_NEWER + static bool isPlaying = false; +#endif + static HookSetup() + { +#if UNITY_2017_1_OR_NEWER + EditorApplication.playModeStateChanged += OnEditorPlaying; +#else + EditorApplication.playmodeStateChanged += () => + { + + if (isPlaying == true && EditorApplication.isPlaying == false) + { + SetMainLuaEnv(null); + } + + isPlaying = EditorApplication.isPlaying; + }; +#endif + } + + + public static void SetMainLuaEnv(LuaState env) + { + if (LuaDeepProfilerSetting.Instance.isDeepProfiler) + { + if (env != null) + { + env.BeginModule(null); + env.BeginModule("MikuLuaProfiler"); + MikuLuaProfiler_LuaProfilerWrap.Register(env); + env.EndModule(); + env.EndModule(); + env.DoString(@" +BeginMikuSample = MikuLuaProfiler.LuaProfiler.BeginSample +EndMikuSample = MikuLuaProfiler.LuaProfiler.EndSample + +function miku_unpack_return_value(...) + EndMikuSample() + return ... +end +"); + HookSetup.HookLuaFuns(); + } + } + + if (env == null) + { + HookSetup.Uninstall(); + LuaProfiler.mainL = IntPtr.Zero; + } + } + +#if UNITY_2017_1_OR_NEWER + public static void OnEditorPlaying(PlayModeStateChange playModeStateChange) + { + if (playModeStateChange == PlayModeStateChange.ExitingPlayMode) + { + SetMainLuaEnv(null); + } + } +#endif + +#region hook + +#region hook tostring + + public class LuaDll + { +#region luastring + public static readonly Dictionary stringDict = new Dictionary(); + public static bool TryGetLuaString(IntPtr p, out string result) + { + + return stringDict.TryGetValue((long)p, out result); + } + public static void RefString(IntPtr strPoint, int index, string s, IntPtr L) + { + int oldTop = LuaDLL.lua_gettop(L); + LuaDLL.lua_pushvalue(L, index); + //把字符串ref了之后就不GC了 + LuaDLL.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX); + LuaDLL.lua_settop(L, oldTop); + stringDict[(long)strPoint] = s; + } +#endregion + + public static int luaL_loadbuffer(IntPtr L, byte[] buff, int size, string name) + { + if (LuaDeepProfilerSetting.Instance.isDeepProfiler)//&& name != "chunk" + { + var utf8WithoutBom = new System.Text.UTF8Encoding(true); + string fileName = name.Replace("@", "").Replace("/", ".") + ".lua"; + string value = utf8WithoutBom.GetString(buff); + value = Parse.InsertSample(value, fileName); + + //System.IO.File.WriteAllText(fileName, value); + + buff = utf8WithoutBom.GetBytes(value); + size = buff.Length; + } + + return ProxyLoadbuffer(L, buff, size, name); + } + + public static int ProxyLoadbuffer(IntPtr L, byte[] buff, int size, string name) + { + return 0; + } + + public static string lua_tostring(IntPtr L, int index) + { + int len = 0; + IntPtr str = LuaDLL.tolua_tolstring(L, index, out len); + + if (str != IntPtr.Zero) + { + string s; + if (!TryGetLuaString(str, out s)) + { + s = LuaDLL.lua_ptrtostring(str, len); + } + return s; + } + + return null; + } + + public static string PoxyToString(IntPtr L, int index) + { + return null; + } + } +#endregion + + +#region hook profiler + public class Profiler + { + private static Stack m_Stack = new Stack(); + private static int m_currentFrame = 0; + public static void BeginSampleOnly(string name) + { + if (ProfilerDriver.deepProfiling) return; + if (Time.frameCount != m_currentFrame) + { + m_Stack.Clear(); + m_currentFrame = Time.frameCount; + } + m_Stack.Push(name); + ProxyBeginSample(name); + } + public static void BeginSample(string name, UnityEngine.Object targetObject) + { + if (ProfilerDriver.deepProfiling) return; + m_Stack.Push(name); + ProxyBeginSample(name, targetObject); + } + + public static void EndSample() + { + if (ProfilerDriver.deepProfiling) return; + if (m_Stack.Count <= 0) + { + return; + } + m_Stack.Pop(); + ProxyEndSample(); + } + + public static void ProxyBeginSample(string name) + { + } + public static void ProxyBeginSample(string name, UnityEngine.Object targetObject) + { + } + + public static void ProxyEndSample() + { + } + } +#endregion + +#region do hook + private static MethodHooker beginSampeOnly; + private static MethodHooker beginObjetSample; + private static MethodHooker endSample; + private static MethodHooker tostringHook; + private static MethodHooker loaderHook; + + private static bool m_hooked = false; + public static void HookLuaFuns() + { + if (m_hooked) return; + if (tostringHook == null) + { + Type typeLogReplace = typeof(LuaDll); + Type typeLog = typeof(LuaDLL); + MethodInfo tostringFun = typeLog.GetMethod("lua_tostring"); + MethodInfo tostringReplace = typeLogReplace.GetMethod("lua_tostring"); + MethodInfo tostringProxy = typeLogReplace.GetMethod("ProxyToString"); + + tostringHook = new MethodHooker(tostringFun, tostringReplace, tostringProxy); + tostringHook.Install(); + + tostringFun = typeLog.GetMethod("luaL_loadbuffer"); + tostringReplace = typeLogReplace.GetMethod("luaL_loadbuffer"); + tostringProxy = typeLogReplace.GetMethod("ProxyLoadbuffer"); + + tostringHook = new MethodHooker(tostringFun, tostringReplace, tostringProxy); + tostringHook.Install(); + } + + if (beginSampeOnly == null) + { + Type typeTarget = typeof(UnityEngine.Profiling.Profiler); + Type typeReplace = typeof(Profiler); + + MethodInfo hookTarget = typeTarget.GetMethod("BeginSampleOnly", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(string) }, null); + MethodInfo hookReplace = typeReplace.GetMethod("BeginSampleOnly"); + MethodInfo hookProxy = typeReplace.GetMethod("ProxyBeginSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, null); + beginSampeOnly = new MethodHooker(hookTarget, hookReplace, hookProxy); + beginSampeOnly.Install(); + + hookTarget = typeTarget.GetMethod("BeginSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), typeof(UnityEngine.Object) }, null); + hookReplace = typeReplace.GetMethod("BeginSample"); + hookProxy = typeReplace.GetMethod("ProxyBeginSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), typeof(UnityEngine.Object) }, null); + beginObjetSample = new MethodHooker(hookTarget, hookReplace, hookProxy); + beginObjetSample.Install(); + + hookTarget = typeTarget.GetMethod("EndSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { }, null); + hookReplace = typeReplace.GetMethod("EndSample"); + hookProxy = typeReplace.GetMethod("ProxyEndSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { }, null); + endSample = new MethodHooker(hookTarget, hookReplace, hookProxy); + endSample.Install(); + } + + m_hooked = true; + } + + public static void Uninstall() + { + if (beginSampeOnly != null) + { + beginSampeOnly.Uninstall(); + beginSampeOnly = null; + } + if (beginObjetSample != null) + { + beginObjetSample.Uninstall(); + beginObjetSample = null; + } + if (endSample != null) + { + endSample.Uninstall(); + endSample = null; + } + if (tostringHook != null) + { + tostringHook.Uninstall(); + tostringHook = null; + } + if (loaderHook != null) + { + loaderHook.Uninstall(); + loaderHook = null; + } + + m_hooked = false; + } +#endregion + +#endregion + } + + #region bind + public class MikuLuaProfiler_LuaProfilerWrap + { + public static void Register(LuaState L) + { + L.BeginClass(typeof(MikuLuaProfiler.LuaProfiler), typeof(System.Object)); + L.RegFunction("BeginSample", BeginSample); + L.RegFunction("EndSample", EndSample); + L.EndClass(); + } + + [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] + static int BeginSample(IntPtr L) + { + try + { + int count = LuaDLL.lua_gettop(L); + + if (count == 1 && TypeChecker.CheckTypes(L, 1)) + { + System.IntPtr arg0 = ToLua.CheckIntPtr(L, 1); + MikuLuaProfiler.LuaProfiler.BeginSample(arg0); + return 0; + } + else if (count == 1 && TypeChecker.CheckTypes(L, 1)) + { + string arg0 = ToLua.ToString(L, 1); + MikuLuaProfiler.LuaProfiler.BeginSample(arg0); + return 0; + } + else if (count == 2) + { + System.IntPtr arg0 = ToLua.CheckIntPtr(L, 1); + string arg1 = ToLua.CheckString(L, 2); + MikuLuaProfiler.LuaProfiler.BeginSample(arg0, arg1); + return 0; + } + else + { + return LuaDLL.luaL_throw(L, "invalid arguments to method: MikuLuaProfiler.LuaProfiler.BeginSample"); + } + } + catch (Exception e) + { + return LuaDLL.toluaL_exception(L, e); + } + } + + [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] + static int EndSample(IntPtr L) + { + try + { + int count = LuaDLL.lua_gettop(L); + + if (count == 0) + { + MikuLuaProfiler.LuaProfiler.EndSample(); + return 0; + } + else if (count == 1) + { + System.IntPtr arg0 = ToLua.CheckIntPtr(L, 1); + MikuLuaProfiler.LuaProfiler.EndSample(arg0); + return 0; + } + else + { + return LuaDLL.luaL_throw(L, "invalid arguments to method: MikuLuaProfiler.LuaProfiler.EndSample"); + } + } + catch (Exception e) + { + return LuaDLL.toluaL_exception(L, e); + } + } + } + #endregion +} + +#endif + +#endif \ No newline at end of file diff --git a/Assets/Slua/LuaProfiler/HookSetup/ToLuaHookSetup.cs.meta b/Assets/Slua/LuaProfiler/HookSetup/ToLuaHookSetup.cs.meta new file mode 100644 index 00000000..af11a100 --- /dev/null +++ b/Assets/Slua/LuaProfiler/HookSetup/ToLuaHookSetup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f92f64e7d491c7c44827f9f4c992f655 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Slua/LuaProfiler/HookSetup/XLuaHookSetup.cs b/Assets/Slua/LuaProfiler/HookSetup/XLuaHookSetup.cs new file mode 100644 index 00000000..35ee28fa --- /dev/null +++ b/Assets/Slua/LuaProfiler/HookSetup/XLuaHookSetup.cs @@ -0,0 +1,439 @@ +/* +* ============================================================================== +* Filename: XLuaHookSetup +* Created: 2018/7/2 11:36:16 +* Author: エル・プサイ・コングリィ +* Purpose: +* ============================================================================== +*/ + +//#define XLUA +#if XLUA + +#if UNITY_EDITOR +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; +using XLua; +using LuaState = XLua.LuaEnv; +using LuaDLL = XLua.LuaDLL.Lua; + +namespace MikuLuaProfiler { + + public class LuaLib + { + public static void RunGC() + { + var env = LuaProfiler.mainL; + if (env != IntPtr.Zero) + { + LuaDLL.lua_gc(env, LuaGCOptions.LUA_GCCOLLECT, 0); + } + } + public static void StopGC() + { + var env = LuaProfiler.mainL; + if (env != IntPtr.Zero) + { + LuaDLL.lua_gc(env, LuaGCOptions.LUA_GCSTOP, 0); + } + } + public static void ResumeGC() + { + var env = LuaProfiler.mainL; + if (env != IntPtr.Zero) + { + LuaDLL.lua_gc(env, LuaGCOptions.LUA_GCRESTART, 0); + } + } + + public static long GetLuaMemory(IntPtr luaState) + { + long result = 0; + + result = LuaDLL.lua_gc(luaState, LuaGCOptions.LUA_GCCOUNT, 0); + result = result * 1024 + LuaDLL.lua_gc(luaState, LuaGCOptions.LUA_GCCOUNTB, 0); + + return result; + } + } + + [InitializeOnLoad] + public static class Startup + { + private static MethodHooker hookNewLuaEnv; + + public static readonly string luaPath; + static Startup() + { + HookNewLuaEnv(); + } + + public static void HookNewLuaEnv() + { + if (hookNewLuaEnv == null) + { + Type envReplace = typeof(LuaEnvReplace); + Type typeEnv = typeof(XLua.LuaEnv); + var clickFun = typeEnv.GetConstructors()[0]; + MethodInfo clickReplace = envReplace.GetMethod("Ctor"); + MethodInfo clickProxy = envReplace.GetMethod("Proxy", BindingFlags.Public | BindingFlags.Static); + hookNewLuaEnv = new MethodHooker(clickFun, clickReplace, clickProxy); + hookNewLuaEnv.Install(); + + Type typeDll = typeof(XLua.LuaDLL.Lua); + var newstateFun = typeDll.GetMethod("luaL_newstate"); + clickReplace = envReplace.GetMethod("luaL_newstate"); + clickProxy = envReplace.GetMethod("ProxyNewstate", BindingFlags.Public | BindingFlags.Static); + hookNewLuaEnv = new MethodHooker(newstateFun, clickReplace, clickProxy); + hookNewLuaEnv.Install(); + + newstateFun = typeDll.GetMethod("lua_close"); + clickReplace = envReplace.GetMethod("lua_close"); + clickProxy = envReplace.GetMethod("ProxyClose", BindingFlags.Public | BindingFlags.Static); + hookNewLuaEnv = new MethodHooker(newstateFun, clickReplace, clickProxy); + hookNewLuaEnv.Install(); + } + } + + public static class LuaEnvReplace + { + public static void Ctor(LuaState env) + { + Proxy(env); + MikuLuaProfiler.HookSetup.SetMainLuaEnv(env); + } + public static void Proxy(LuaState env) + { + } + + public static void lua_close(IntPtr luaState) + { + if (LuaProfiler.mainL == luaState) + { + LuaProfiler.mainL = IntPtr.Zero; + HookSetup.Uninstall(); + } + } + + public static void ProxyClose(IntPtr luaState) + { + } + + public static IntPtr luaL_newstate() + { + IntPtr l = ProxyNewstate(); + MikuLuaProfiler.LuaProfiler.mainL = l; + return l; + } + public static IntPtr ProxyNewstate() + { + return IntPtr.Zero; + } + } + + } + + [InitializeOnLoad] + static class HookSetup + { +#if !UNITY_2017_1_OR_NEWER + static bool isPlaying = false; +#endif + static HookSetup() + { +#if UNITY_2017_1_OR_NEWER + EditorApplication.playModeStateChanged += OnEditorPlaying; +#else + EditorApplication.playmodeStateChanged += () => + { + + if (isPlaying == true && EditorApplication.isPlaying == false) + { + SetMainLuaEnv(null); + } + + isPlaying = EditorApplication.isPlaying; + }; +#endif + } + + + public static void SetMainLuaEnv(LuaState env) + { + if (LuaDeepProfilerSetting.Instance.isDeepProfiler) + { + if (env != null) + { + env.DoString(@" +BeginMikuSample = CS.MikuLuaProfiler.LuaProfiler.BeginSample +EndMikuSample = CS.MikuLuaProfiler.LuaProfiler.EndSample + +function miku_unpack_return_value(...) + EndMikuSample() + return ... +end +"); + HookSetup.HookLuaFuns(); + } + } + + if (env == null) + { + HookSetup.Uninstall(); + LuaProfiler.mainL = IntPtr.Zero; + } + } + +#if UNITY_2017_1_OR_NEWER + public static void OnEditorPlaying(PlayModeStateChange playModeStateChange) + { + if (playModeStateChange == PlayModeStateChange.ExitingPlayMode) + { + SetMainLuaEnv(null); + } + } +#endif + +#region hook + +#region hook tostring + + public class LuaDll + { +#region luastring + public static readonly Dictionary stringDict = new Dictionary(); + public static bool TryGetLuaString(IntPtr p, out string result) + { + + return stringDict.TryGetValue((long)p, out result); + } + public static void RefString(IntPtr strPoint, int index, string s, IntPtr L) + { + int oldTop = LuaDLL.lua_gettop(L); + LuaDLL.lua_pushvalue(L, index); + //把字符串ref了之后就不GC了 + LuaDLL.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX); + LuaDLL.lua_settop(L, oldTop); + stringDict[(long)strPoint] = s; + } +#endregion + + public static int xluaL_loadbuffer(IntPtr L, byte[] buff, int size, string name) + { + if (LuaDeepProfilerSetting.Instance.isDeepProfiler)//&& name != "chunk" + { + var utf8WithoutBom = new System.Text.UTF8Encoding(true); + string fileName = name.Replace("@", "").Replace("/", ".") + ".lua"; + string value = utf8WithoutBom.GetString(buff); + value = Parse.InsertSample(value, fileName); + + //System.IO.File.WriteAllText(fileName, value); + + buff = utf8WithoutBom.GetBytes(value); + size = buff.Length; + } + + return ProxyLoadbuffer(L, buff, size, name); + } + + public static int ProxyLoadbuffer(IntPtr L, byte[] buff, int size, string name) + { + return 0; + } + + public static string lua_tostring(IntPtr L, int index) + { + IntPtr strlen; + + IntPtr str = LuaDLL.lua_tolstring(L, index, out strlen); + if (str != IntPtr.Zero) + { +#if XLUA_GENERAL || (UNITY_WSA && !UNITY_EDITOR) + int len = strlen.ToInt32(); + byte[] buffer = new byte[len]; + Marshal.Copy(str, buffer, 0, len); + return Encoding.UTF8.GetString(buffer); +#else + string ret; + if (TryGetLuaString(str, out ret)) + { + return ret; + } + + ret = Marshal.PtrToStringAnsi(str, strlen.ToInt32()); + if (ret == null) + { + int len = strlen.ToInt32(); + byte[] buffer = new byte[len]; + Marshal.Copy(str, buffer, 0, len); + ret = Encoding.UTF8.GetString(buffer); + } + if (ret != null) + { + RefString(str, index, ret, L); + } + return ret; +#endif + } + else + { + return null; + } + } + + public static string PoxyToString(IntPtr L, int index) + { + return null; + } + } +#endregion + + +#region hook profiler + public class Profiler + { + private static Stack m_Stack = new Stack(); + private static int m_currentFrame = 0; + public static void BeginSampleOnly(string name) + { + if (ProfilerDriver.deepProfiling) return; + if (Time.frameCount != m_currentFrame) + { + m_Stack.Clear(); + m_currentFrame = Time.frameCount; + } + m_Stack.Push(name); + ProxyBeginSample(name); + } + public static void BeginSample(string name, UnityEngine.Object targetObject) + { + if (ProfilerDriver.deepProfiling) return; + m_Stack.Push(name); + ProxyBeginSample(name, targetObject); + } + + public static void EndSample() + { + if (ProfilerDriver.deepProfiling) return; + if (m_Stack.Count <= 0) + { + return; + } + m_Stack.Pop(); + ProxyEndSample(); + } + + public static void ProxyBeginSample(string name) + { + } + public static void ProxyBeginSample(string name, UnityEngine.Object targetObject) + { + } + + public static void ProxyEndSample() + { + } + } +#endregion + +#region do hook + private static MethodHooker beginSampeOnly; + private static MethodHooker beginObjetSample; + private static MethodHooker endSample; + private static MethodHooker tostringHook; + private static MethodHooker loaderHook; + + private static bool m_hooked = false; + public static void HookLuaFuns() + { + if (m_hooked) return; + if (tostringHook == null) + { + Type typeLogReplace = typeof(LuaDll); + Type typeLog = typeof(LuaDLL); + MethodInfo tostringFun = typeLog.GetMethod("lua_tostring"); + MethodInfo tostringReplace = typeLogReplace.GetMethod("lua_tostring"); + MethodInfo tostringProxy = typeLogReplace.GetMethod("ProxyToString"); + + tostringHook = new MethodHooker(tostringFun, tostringReplace, tostringProxy); + tostringHook.Install(); + + tostringFun = typeLog.GetMethod("xluaL_loadbuffer"); + tostringReplace = typeLogReplace.GetMethod("xluaL_loadbuffer"); + tostringProxy = typeLogReplace.GetMethod("ProxyLoadbuffer"); + + tostringHook = new MethodHooker(tostringFun, tostringReplace, tostringProxy); + tostringHook.Install(); + } + + if (beginSampeOnly == null) + { + Type typeTarget = typeof(UnityEngine.Profiling.Profiler); + Type typeReplace = typeof(Profiler); + + MethodInfo hookTarget = typeTarget.GetMethod("BeginSampleOnly", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(string) }, null); + MethodInfo hookReplace = typeReplace.GetMethod("BeginSampleOnly"); + MethodInfo hookProxy = typeReplace.GetMethod("ProxyBeginSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, null); + beginSampeOnly = new MethodHooker(hookTarget, hookReplace, hookProxy); + beginSampeOnly.Install(); + + hookTarget = typeTarget.GetMethod("BeginSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), typeof(UnityEngine.Object) }, null); + hookReplace = typeReplace.GetMethod("BeginSample"); + hookProxy = typeReplace.GetMethod("ProxyBeginSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), typeof(UnityEngine.Object) }, null); + beginObjetSample = new MethodHooker(hookTarget, hookReplace, hookProxy); + beginObjetSample.Install(); + + hookTarget = typeTarget.GetMethod("EndSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { }, null); + hookReplace = typeReplace.GetMethod("EndSample"); + hookProxy = typeReplace.GetMethod("ProxyEndSample", BindingFlags.Public | BindingFlags.Static, null, new Type[] { }, null); + endSample = new MethodHooker(hookTarget, hookReplace, hookProxy); + endSample.Install(); + } + + m_hooked = true; + } + + public static void Uninstall() + { + if (beginSampeOnly != null) + { + beginSampeOnly.Uninstall(); + beginSampeOnly = null; + } + if (beginObjetSample != null) + { + beginObjetSample.Uninstall(); + beginObjetSample = null; + } + if (endSample != null) + { + endSample.Uninstall(); + endSample = null; + } + if (tostringHook != null) + { + tostringHook.Uninstall(); + tostringHook = null; + } + if (loaderHook != null) + { + loaderHook.Uninstall(); + loaderHook = null; + } + + m_hooked = false; + } +#endregion + +#endregion + } +} +#endif + +#endif \ No newline at end of file diff --git a/Assets/Slua/LuaProfiler/HookSetup/XLuaHookSetup.cs.meta b/Assets/Slua/LuaProfiler/HookSetup/XLuaHookSetup.cs.meta new file mode 100644 index 00000000..8553be7c --- /dev/null +++ b/Assets/Slua/LuaProfiler/HookSetup/XLuaHookSetup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d33d63a9cf6baa741908f7be36106221 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md b/README.md index 6197f0fd..29f8a0b5 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,10 @@ public class HelloWorld { ~~~~~~~~~~ +### LuaProfiler +if you want to profiler lua like C#,Tap "Window->Lua Profiler Window" to Open Profiler Window and click "Deep Profiler" Toggle +
+![](profiler.png) ### Benchmark diff --git a/profiler.png b/profiler.png new file mode 100644 index 00000000..5b68bd49 Binary files /dev/null and b/profiler.png differ