StartUp.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. #define TOLUA
  2. /*
  3. #########
  4. ############
  5. #############
  6. ## ###########
  7. ### ###### #####
  8. ### ####### ####
  9. ### ########## ####
  10. #### ########### ####
  11. #### ########### #####
  12. ##### ### ######## #####
  13. ##### ### ######## ######
  14. ###### ### ########### ######
  15. ###### #### ############## ######
  16. ####### ##################### ######
  17. ####### ###################### ######
  18. ####### ###### ################# ######
  19. ####### ###### ###### ######### ######
  20. ####### ## ###### ###### ######
  21. ####### ###### ##### #####
  22. ###### ##### ##### ####
  23. ##### #### ##### ###
  24. ##### ### ### #
  25. ### ### ###
  26. ## ### ###
  27. __________#_______####_______####______________
  28. 我们的未来没有BUG
  29. * ==============================================================================
  30. * Filename: StartUp
  31. * Created: 2018/7/2 11:36:16
  32. * Author: エル・プサイ・コングリィ
  33. * Purpose:
  34. * ==============================================================================
  35. */
  36. using Miku.Cecil;
  37. using Miku.Cecil.Cil;
  38. using System;
  39. using System.Collections.Generic;
  40. using System.IO;
  41. using System.Linq;
  42. using System.Security.Cryptography;
  43. using UnityEditor;
  44. using UnityEngine;
  45. namespace MikuLuaProfiler
  46. {
  47. [InitializeOnLoad]
  48. public static class StartUp
  49. {
  50. //private static int tickNum = 0;
  51. static StartUp()
  52. {
  53. if (LuaDeepProfilerSetting.Instance.isDeepMonoProfiler)
  54. {
  55. InjectMethods.InjectAllMethods();
  56. }
  57. #if XLUA || TOLUA || SLUA
  58. if (LuaDeepProfilerSetting.Instance.isInited) return;
  59. #endif
  60. string[] paths = Directory.GetFiles(Application.dataPath, "*.dll", SearchOption.AllDirectories);
  61. foreach (var item in paths)
  62. {
  63. string fileName = Path.GetFileName(item);
  64. if (fileName == "slua.dll")
  65. {
  66. AppendMacro("#define SLUA");
  67. }
  68. if (fileName == "xlua.dll")
  69. {
  70. AppendMacro("#define XLUA");
  71. break;
  72. }
  73. if (fileName == "tolua.dll")
  74. {
  75. AppendMacro("#define TOLUA");
  76. break;
  77. }
  78. }
  79. LuaDeepProfilerSetting.Instance.isInited = true;
  80. }
  81. private static void AppendMacro(string macro)
  82. {
  83. System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace(true);
  84. System.Diagnostics.StackFrame sf = st.GetFrame(0);
  85. string path = sf.GetFileName();
  86. string selfPath = path;
  87. #if UNITY_EDITOR_WIN
  88. path = path.Replace("Editor\\StartUp.cs", "Core\\Driver\\LuaDLL.cs");
  89. #else
  90. path = path.Replace("Editor/StartUp.cs", "Core/Driver/LuaDLL.cs");
  91. #endif
  92. AppendText(macro, selfPath);
  93. AppendText(macro, path);
  94. }
  95. private static void AppendText(string macro, string path)
  96. {
  97. string text = File.ReadAllText(path);
  98. string text2 = new StringReader(text).ReadLine();
  99. if (text2.Contains("#define"))
  100. {
  101. text = text.Substring(text2.Length, text.Length - text2.Length);
  102. }
  103. else
  104. {
  105. macro += "\r\n";
  106. }
  107. text = text.Insert(0, macro);
  108. File.WriteAllText(path, text);
  109. }
  110. }
  111. public static class InjectMethods
  112. {
  113. private static MethodDefinition m_beginSampleMethod;
  114. private static MethodDefinition m_endSampleMethod;
  115. #region try finally
  116. public static void InjectAllMethods()
  117. {
  118. if (EditorApplication.isCompiling)
  119. {
  120. Debug.LogError("is compiling");
  121. return;
  122. }
  123. var projectPath = System.Reflection.Assembly.Load("Assembly-CSharp").ManifestModule.FullyQualifiedName;
  124. var profilerPath = (typeof(LuaProfiler).Assembly).ManifestModule.FullyQualifiedName;
  125. InjectAllMethods(projectPath, profilerPath);
  126. }
  127. private static bool IsMonoBehavior(TypeDefinition td)
  128. {
  129. if (td == null) return false;
  130. if (td.FullName == "UnityEngine.MonoBehaviour")
  131. {
  132. return true;
  133. }
  134. else
  135. {
  136. if (td.BaseType == null)
  137. {
  138. return false;
  139. }
  140. else
  141. {
  142. return IsMonoBehavior(td.BaseType.Resolve());
  143. }
  144. }
  145. }
  146. private static void InjectAllMethods(string injectPath, string profilerPath)
  147. {
  148. string md5 = null;
  149. md5 = new FileInfo(injectPath).LastWriteTimeUtc.Ticks.ToString();
  150. if (md5 == LuaDeepProfilerSetting.Instance.assMd5) return;
  151. AssemblyDefinition injectAss = LoadAssembly(injectPath);
  152. AssemblyDefinition profilerAss = null;
  153. if (injectPath == profilerPath)
  154. {
  155. profilerAss = injectAss;
  156. }
  157. else
  158. {
  159. profilerAss = LoadAssembly(profilerPath);
  160. }
  161. var profilerType = profilerAss.MainModule.GetType("MikuLuaProfiler.LuaProfiler");
  162. foreach (var m in profilerType.Methods)
  163. {
  164. if (m.Name == "BeginSampleCSharp")
  165. {
  166. m_beginSampleMethod = m;
  167. }
  168. if (m.Name == "EndSampleCSharp")
  169. {
  170. m_endSampleMethod = m;
  171. }
  172. }
  173. var module = injectAss.MainModule;
  174. foreach (var type in injectAss.MainModule.Types)
  175. {
  176. if (type.FullName.Contains("MikuLuaProfiler"))
  177. {
  178. continue;
  179. }
  180. foreach (var item in type.Methods)
  181. {
  182. //丢弃协同
  183. if (item.ReturnType.Name.Contains("IEnumerator"))
  184. {
  185. continue;
  186. }
  187. if (item.Name == ".cctor")
  188. {
  189. continue;
  190. }
  191. if (item.Name == ".ctor")
  192. {
  193. if (item.DeclaringType.IsSerializable)
  194. {
  195. continue;
  196. }
  197. bool isMonoBehaviour = IsMonoBehavior(item.DeclaringType.BaseType.Resolve());
  198. if (isMonoBehaviour)
  199. {
  200. continue;
  201. }
  202. }
  203. if (item.IsAbstract)
  204. {
  205. continue;
  206. }
  207. if (item.IsPInvokeImpl)
  208. {
  209. continue;
  210. }
  211. if (item.Body == null)
  212. {
  213. continue;
  214. }
  215. InjectTryFinally(item, module);
  216. }
  217. }
  218. WriteAssembly(injectPath, injectAss);
  219. LuaDeepProfilerSetting.Instance.assMd5 = new FileInfo(injectPath).LastWriteTimeUtc.Ticks.ToString();
  220. }
  221. private static string GetReflectionName(this TypeReference type)
  222. {
  223. if (type.IsGenericInstance)
  224. {
  225. var genericInstance = (GenericInstanceType)type;
  226. return string.Format("{0}.{1}[{2}]", genericInstance.Namespace, type.Name, String.Join(",", genericInstance.GenericArguments.Select(p => p.GetReflectionName()).ToArray()));
  227. }
  228. return type.FullName;
  229. }
  230. internal static Instruction FixReturns(this ILProcessor ilProcessor)
  231. {
  232. var methodDefinition = ilProcessor.Body.Method;
  233. if (methodDefinition.ReturnType == methodDefinition.Module.TypeSystem.Void)
  234. {
  235. var instructions = ilProcessor.Body.Instructions.ToArray();
  236. var newReturnInstruction = ilProcessor.Create(OpCodes.Ret);
  237. ilProcessor.Append(newReturnInstruction);
  238. foreach (var instruction in instructions)
  239. {
  240. if (instruction.OpCode == OpCodes.Ret)
  241. {
  242. var leaveInstruction = ilProcessor.Create(OpCodes.Leave, newReturnInstruction);
  243. ilProcessor.Replace(instruction, leaveInstruction);
  244. ilProcessor.ReplaceInstructionReferences(instruction, leaveInstruction);
  245. }
  246. }
  247. return newReturnInstruction;
  248. }
  249. else
  250. {
  251. var instructions = ilProcessor.Body.Instructions.ToArray();
  252. var returnVariable = new VariableDefinition(methodDefinition.ReturnType);
  253. ilProcessor.Body.Variables.Add(returnVariable);
  254. var loadResultInstruction = ilProcessor.Create(OpCodes.Ldloc, returnVariable);
  255. ilProcessor.Append(loadResultInstruction);
  256. var newReturnInstruction = ilProcessor.Create(OpCodes.Ret);
  257. ilProcessor.Append(newReturnInstruction);
  258. foreach (var instruction in instructions)
  259. {
  260. if (instruction.OpCode == OpCodes.Ret)
  261. {
  262. var leaveInstruction = ilProcessor.Create(OpCodes.Leave, loadResultInstruction);
  263. ilProcessor.Replace(instruction, leaveInstruction);
  264. ilProcessor.ReplaceInstructionReferences(instruction, leaveInstruction);
  265. var saveResultInstruction = ilProcessor.Create(OpCodes.Stloc, returnVariable);
  266. ilProcessor.InsertBefore(leaveInstruction, saveResultInstruction);
  267. ilProcessor.ReplaceInstructionReferences(leaveInstruction, saveResultInstruction);
  268. }
  269. }
  270. return loadResultInstruction;
  271. }
  272. }
  273. internal static void ReplaceInstructionReferences(
  274. this ILProcessor ilProcessor,
  275. Instruction oldInstruction,
  276. Instruction newInstruction)
  277. {
  278. foreach (var handler in ilProcessor.Body.ExceptionHandlers)
  279. {
  280. if (handler.FilterStart == oldInstruction)
  281. handler.FilterStart = newInstruction;
  282. if (handler.TryStart == oldInstruction)
  283. handler.TryStart = newInstruction;
  284. if (handler.TryEnd == oldInstruction)
  285. handler.TryEnd = newInstruction;
  286. if (handler.HandlerStart == oldInstruction)
  287. handler.HandlerStart = newInstruction;
  288. if (handler.HandlerEnd == oldInstruction)
  289. handler.HandlerEnd = newInstruction;
  290. }
  291. // Update instructions with a target instruction
  292. foreach (var iteratedInstruction in ilProcessor.Body.Instructions)
  293. {
  294. var operand = iteratedInstruction.Operand;
  295. if (operand == oldInstruction)
  296. {
  297. iteratedInstruction.Operand = newInstruction;
  298. continue;
  299. }
  300. else if (operand is Instruction[])
  301. {
  302. Instruction[] operands = (Instruction[])operand;
  303. for (var i = 0; i < operands.Length; ++i)
  304. {
  305. if (operands[i] == oldInstruction)
  306. operands[i] = newInstruction;
  307. }
  308. }
  309. }
  310. }
  311. private static Instruction FirstInstructionSkipCtor(MethodDefinition Method)
  312. {
  313. var body = Method.Body;
  314. if (Method.IsConstructor && !Method.IsStatic)
  315. {
  316. return body.Instructions[1];
  317. }
  318. return body.Instructions[0];
  319. }
  320. private static void InjectTryFinally(MethodDefinition method, ModuleDefinition module)
  321. {
  322. if (method.Body == null) return;
  323. if (method.Body.Instructions.Count == 0) return;
  324. if (method.IsConstructor && !method.IsStatic && method.Body.Instructions.Count == 1) return;
  325. var il = method.Body.GetILProcessor();
  326. var firstInstruction = FirstInstructionSkipCtor(method);
  327. var beginSample = il.Create(
  328. OpCodes.Call,
  329. module.ImportReference(m_beginSampleMethod));
  330. il.InsertBefore(il.Body.Instructions[0], beginSample);
  331. il.InsertBefore(il.Body.Instructions[0], il.Create(OpCodes.Ldstr, "[C#]:" + method.DeclaringType.Name + "::" + method.Name));
  332. il.InsertBefore(il.Body.Instructions[0], il.Create(OpCodes.Nop));
  333. var returnInstruction = il.FixReturns();
  334. var beforeReturn = Instruction.Create(OpCodes.Nop);
  335. il.InsertBefore(returnInstruction, beforeReturn);
  336. var endSample = il.Create(
  337. OpCodes.Call,
  338. module.ImportReference(m_endSampleMethod));
  339. il.InsertBefore(returnInstruction, endSample);
  340. il.InsertBefore(returnInstruction, Instruction.Create(OpCodes.Endfinally));
  341. var handler = new ExceptionHandler(ExceptionHandlerType.Finally)
  342. {
  343. TryStart = firstInstruction,
  344. TryEnd = beforeReturn,
  345. HandlerStart = beforeReturn,
  346. HandlerEnd = returnInstruction,
  347. };
  348. method.Body.ExceptionHandlers.Add(handler);
  349. method.Body.InitLocals = true;
  350. method = method.Resolve();
  351. }
  352. #endregion
  353. #region tool
  354. private static SequencePoint GetSequencePoint(MethodBody body)
  355. {
  356. if (body == null)
  357. {
  358. return null;
  359. }
  360. Instruction instruction = body.Instructions.FirstOrDefault(x => x.SequencePoint != null);
  361. return instruction == null ? null : instruction.SequencePoint;
  362. }
  363. public static AssemblyDefinition LoadAssembly(string path)
  364. {
  365. AssemblyDefinition result = null;
  366. result = AssemblyDefinition.ReadAssembly(path);
  367. AddResolver(result);
  368. return result;
  369. }
  370. public static void WriteAssembly(string path, AssemblyDefinition ass)
  371. {
  372. ass.Write(path);
  373. }
  374. public static void Recompile()
  375. {
  376. BuildTargetGroup bg = BuildTargetGroup.Standalone;
  377. switch (EditorUserBuildSettings.activeBuildTarget)
  378. {
  379. case BuildTarget.Android:
  380. bg = BuildTargetGroup.Android;
  381. break;
  382. case BuildTarget.iOS:
  383. bg = BuildTargetGroup.iOS;
  384. break;
  385. }
  386. string path = PlayerSettings.GetScriptingDefineSymbolsForGroup(bg);
  387. bool hasRecompile = false;
  388. string[] heads = path.Split(';');
  389. path = "";
  390. foreach (var item in heads)
  391. {
  392. if (item == "MIKU_RECOMPILE")
  393. {
  394. hasRecompile = true;
  395. continue;
  396. }
  397. path += item + ";";
  398. }
  399. if (!hasRecompile)
  400. {
  401. path += "MIKU_RECOMPILE;";
  402. }
  403. PlayerSettings.SetScriptingDefineSymbolsForGroup(bg, path);
  404. }
  405. private static string GetMD5HashFromFile(string path)
  406. {
  407. if (!File.Exists(path))
  408. throw new ArgumentException(string.Format("<{0}>, 不存在", path));
  409. int bufferSize = 1024 * 1024;//自定义缓冲区大小16K
  410. byte[] buffer = new byte[bufferSize];
  411. Stream inputStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
  412. HashAlgorithm hashAlgorithm = new MD5CryptoServiceProvider();
  413. int readLength = 0;//每次读取长度
  414. var output = new byte[bufferSize];
  415. while ((readLength = inputStream.Read(buffer, 0, buffer.Length)) > 0)
  416. {
  417. //计算MD5
  418. hashAlgorithm.TransformBlock(buffer, 0, readLength, output, 0);
  419. }
  420. //完成最后计算,必须调用(由于上一部循环已经完成所有运算,所以调用此方法时后面的两个参数都为0)
  421. hashAlgorithm.TransformFinalBlock(buffer, 0, 0);
  422. string md5 = BitConverter.ToString(hashAlgorithm.Hash);
  423. hashAlgorithm.Clear();
  424. inputStream.Close();
  425. inputStream.Dispose();
  426. md5 = md5.Replace("-", "");
  427. return md5;
  428. }
  429. private static void AddResolver(AssemblyDefinition assembly)
  430. {
  431. var assemblyResolver = assembly.MainModule.AssemblyResolver as DefaultAssemblyResolver;
  432. HashSet<string> paths = new HashSet<string>();
  433. paths.Add("./Library/ScriptAssemblies/");
  434. foreach (string path in (from asm in System.AppDomain.CurrentDomain.GetAssemblies()
  435. select asm.ManifestModule.FullyQualifiedName).Distinct<string>())
  436. {
  437. try
  438. {
  439. string dir = Path.GetDirectoryName(path);
  440. if (!paths.Contains(dir))
  441. {
  442. paths.Add(dir);
  443. }
  444. }
  445. catch
  446. {
  447. }
  448. }
  449. foreach (var item in paths)
  450. {
  451. assemblyResolver.AddSearchDirectory(item);
  452. }
  453. }
  454. #endregion
  455. #if USE_LUA_PROFILER
  456. [UnityEditor.Callbacks.PostProcessScene]
  457. private static void OnPostprocessScene()
  458. {
  459. #if XLUA || TOLUA || SLUA
  460. var luaPath = (typeof(LuaDLL).Assembly).ManifestModule.FullyQualifiedName;
  461. var projectPath = System.Reflection.Assembly.Load("Assembly-CSharp").ManifestModule.FullyQualifiedName;
  462. var profilerPath = (typeof(MikuLuaProfiler.LuaProfiler).Assembly).ManifestModule.FullyQualifiedName;
  463. if (LuaDeepProfilerSetting.Instance.isDeepMonoProfiler)
  464. {
  465. InjectAllMethods(projectPath, profilerPath);
  466. }
  467. #endif
  468. }
  469. #endif
  470. }
  471. }