使用 Mono.Cecil 輔助 Unity3D 手遊進行效能測試(續)
之前的方法及其侷限
問題背景和最初的嘗試見這裡。最開始的想法比較簡單,只想著利用 PostprocessBuild
這個事件,來對已經準備好的本地工程檔案(iOS 或 Android)中的 .NET 程式集進行注入。但是,這樣做限制很多。
首先,無法對 IL2CPP 作為 Scripting Backend 的情況進行注入。因為觸發這個事件時,本地工程檔案中沒有 .NET 程式集,只有 C++ 程式碼,無法用 Cecil 進行注入。
第二,Android 平臺,用 Mono2x 作為 Scripting Backend 的情況下,也需要打包為 Android Studio Project 才能使用。對於直接打包成 apk 的情況,無法簡單的進行注入(除非使用解包、注入、重新簽名打包的方法,比較麻煩)。
第三,iOS 平臺,即使用 Mono2x 作為 Scripting Backend,也無法成功。這是因為在 iOS 平臺打包需要先進行一個叫 AOT Cross Compiling 的步驟,對所有的程式集生成對應的 .dll.s 檔案。這些檔案包含的資訊會在執行時被校驗,如果我篡改了程式集,而沒有理會 .dll.s 檔案,在執行時會報錯。錯誤資訊類似
A script behaviour (probably XXX?) has a different seralization layout when loading. (Read ** bytes but expected ** bytes)
Did you #ifdef UNITY_EDITOR a section of your serialized properties in any of your scripts?
其中 XXX
是 .NET 指令碼名稱,兩組星號表示兩個不同值。這錯誤最終導致指令碼載入失敗,無法執行遊戲。與錯誤資訊描述不同,我並沒有在出問題的指令碼上寫任何條件編譯的程式碼。要想解決這個問題,估計需要篡改 .dll.s 檔案才可以,仍然是很不經濟的。
篡改編譯器的方法
接下來一個辦法,就是對 Unity 的 C# 編譯器 mcs.exe 進行篡改。我沒有深入實驗,因為幾個簡單的實驗就耗費了一天多的時間。我主要嘗試了兩種方法,當然,都沒成功。
方法一,將原 mcs.exe 重新命名(如 mcs1.exe),而後自己寫一個 .NET 控制檯應用程式,佔據原來 mcs.exe 的位置,在其中用 System.Diagnostic.Process 類來啟動 mcs1.exe。這個過程中,我對 Process 物件的一些配置,如環境變數(EnvironmentVariables 屬性)、輸入輸出重定向(RedirectStandardXXX 屬性)進行了多種排列組合,仍無法正確呼叫 mcs1.exe,就更不要說呼叫之後的事情了。
方法二,直接在 mcs.exe 中注入程式碼。因為 mcs.exe 也是一個 .NET 應用程式,並且看上去未經混淆,所以直接注入是可行的。即,「把向遊戲程式集中注入程式碼的程式碼,注入到編譯器中。」這樣做主要的問題,是 mcs.exe 的輸出目錄是臨時資料夾,無法保證其中有我們依賴的(如注入後寫入程式集時,需要用 Mono.Cecil 的 DefaultAssemblyResolver 進行解析的)程式集。
通過 OnPostprocessScene 回撥事件來進行注入
Unity 雖然沒有在執行 mcs.exe 和後續步驟(IL2CPP、Android 打包 apk、iOS 上的 AOT 交叉編譯等)之間提供回撥,但是回撥事件 OnPostprocessScene
目前是確保在它們之間至少觸發一次的。多虧 https://github.com/rayosu/UnityDllInjector 提醒了我。在這個事件回撥中處理 DLL,理論上在任何平臺、任何 Scripting Backend 上都可以有效注入。實現過程中有幾個要點需要注意:
事件
OnPostprocessScene
對應 Build Settings 中指定打包的場景個數,所以它可能執行多次,故而需要防止重複。除了上述 UnityDllInjector 中提供的方法,還可以直接把注入標記寫入你的目標程式集。但值得注意的是,新增一個用於標記的空類在 iOS + Mono2x 下又是不好用的,猜測還和 AOT 交叉編譯有關。保險的做法之一,是在遊戲程式碼中保留幾個bool
常量,值為false
,注入前檢查相應的值,如果為true
則跳過,否則注入。注入完成後,將相應的bool
常量篡改為true
即可。遊戲指令碼對應的程式集,在注入時一定處於和 Assets 同級的 Library 下的 ScriptAssemblies 資料夾下,但要注意你依賴的 Unity 程式集。我使用 UnityDllInjector 提供的方法,依然不能保證獲取到需要的程式集。最終我採用的方法是,使用
EditorApplication.applicationContentsPath
獲取 Unity 安裝目錄,在其中 Data/Managed 目錄裡尋找必要的程式集。
目前我測試了 Android + Mono/IL2CPP 和 iOS + IL2CPP,都沒有問題。iOS + Mono2x 可能由於我們專案本身的一些問題,在 Xcode 連結階段有一些問題。
舊文搬運,2017-06-15 首發於部落格園。
相關文章
- 使用Loadrunner進行效能測試
- 使用 Sysbench 進行 Linux 效能測試Linux
- 手遊前端效能測試前端
- 使用 functrace 輔助進行 Go 專案原始碼分析Go原始碼
- 使用python對oracle進行簡單效能測試PythonOracle
- PoCBox – 漏洞測試驗證輔助平臺
- 【TEST】Oracle19c使用benchmarksql進行效能測試OracleSQL
- 使用 jMeter 對 SAP Spartacus 進行併發效能測試JMeter
- 效能測試進階實踐篇:10分鐘教你使用JMeter進行websocket測試!JMeterWeb
- 哪些功能需要進行效能測試?
- AI 大模型輔助測試提效的思考AI大模型
- AI 大模型輔助測試全流程提效AI大模型
- 對XAML進行編輯的輔助類(XamlHelper)
- AI輔助Kano模型進行產品開發AI模型
- 小米手環3輔助睡眠檢測怎麼開啟?小米手環3開啟輔助睡眠檢測的方法步驟
- 通用輔助生成: 使用任意輔助模型加速解碼模型
- 使用PostMan進行API測試PostmanAPI
- 使用 HTTPie 進行 API 測試HTTPAPI
- 持續測試效能的方法
- 什麼情況下進行效能測試
- 使用 OSProfiler 對 OpenStack 進行效能測量
- 自己動手2小時學會配置遊戲輔助遊戲
- 基於目標TPS的效能測試,如何通過手動設定場景進行測試?
- 在遊戲陪玩系統開發中,如何進行效能測試?遊戲
- 移動app效能測試有哪些需要進行?效能測試報告如何收費?APP測試報告
- 使用JUnit進行單元測試
- 使用jest進行單元測試
- 使用 MeterSphere 進行 Dubbo 介面測試
- 使用JMeter進行壓力測試JMeter
- 使用Wiremock進行整合測試 - kubilayREMMock
- 效能測試乾貨分享:JMeter如何使用Bean Shell進行引數化?JMeterBean
- 軟體效能測試計劃如何進行?權威效能測試報告需要多少錢?測試報告
- 使用Jmeter進行效能測試實戰:詳解HTTP請求和JDBC request進階篇JMeterHTTPJDBC
- 《2020年上半年手遊測試白皮書》輕度手遊熱度不減,Unity3D佔主流Unity3D
- 對node工程進行壓力測試與效能分析
- 如何進行裝置的非對稱效能測試
- 【PG效能測試】pgbench效能測試工具簡單使用
- 使用Jest進行React單元測試React
- 使用 PostMan 進行自動化測試Postman