🐼

VRChatのエラーログからUdonSharpソースコードの位置を調べる

2023/02/03に公開

背景

UdonSharpで例外が発生したとき、ClientSimで動かしている場合やUnityとVRCクライアントが繋がっている場合であれば、UnityのコンソールからUdonSharpソースコードのエラー箇所を知ることができます。

そうでない場合はVRChatエラーログからUdonSharpソースコード位置を算出することになります。
ソースコードが短い場合はこちらの記事を参考にUdonAssemblyとソースコードを照らし合わせて算出できますが、ソースコードが長くなるとだんだんと面倒になります。
本記事はこれについての対応の備忘録。

結論

対象のUdonSharpProgramAssetとプログラムカウンタから、ソースコード位置を算出するエディタ拡張作ったのでコード公開します。
UdonSharpEditorの内部処理呼び出しているので、バージョンアップで動かなくなると思います。
少なくともUdon2.0では完全に。

出来る限り本記事のメンテします。

確認バージョン

バージョン
Unity 2019.4.31f1
VRChat SDK - Base 3.1.10
VRChat SDK - Worlds 3.1.10
UdonSharp 1.1.5

エディタ拡張ソースコード

適当なEditorフォルダに格納してください。

UdonErrorAnalyzer.cs
using System;
using UnityEditor;
using UnityEngine;
using UdonSharp;
using UdonSharp.Compiler;

public class UdonErrorAnalyzer : EditorWindow
{
    private UdonSharpProgramAsset _asset;
    private int _programCounter;
    private string _info = "";

    [MenuItem("Test/UdonErrorAnalyzer")]
    static void ShowWindow()
    {
        GetWindow<UdonErrorAnalyzer>("UdonErrorAnalyzer");
    }

    void OnGUI()
    {
        //UdonSharpProgramAsset指定
        _asset = (UdonSharpProgramAsset)EditorGUILayout
		    .ObjectField($"Udon Asset", _asset, typeof(UdonSharpProgramAsset), true);
        
        //プログラムカウンタ指定
        _programCounter = EditorGUILayout.IntField("ProgramCounter", _programCounter);

        if(_asset != null) {
            //実行
            if (GUILayout.Button("Analyze")) {
                GUIUtility.keyboardControl = 0;

                //UdonSharpEditor内部のデバッグ情報取得処理をReflectionで呼び出し
                var type = Type.GetType("UdonSharp.UdonSharpEditorCache, UdonSharp.Editor");
                var obj = type.GetProperty("Instance").GetValue(null);
                var debugInfo = type.GetMethod("GetDebugInfo")
                    .Invoke(obj, new object[] { _asset, 0 }) as AssemblyDebugInfo;
                debugInfo.GetPositionFromProgramCounter(
                    _programCounter, 
                    out string filePath, 
                    out string methodName, 
                    out int line, 
                    out int lineChar);
                
                //表示するTextField用の文字列構築
                _info = $"{filePath}, line:{line+1}, char:{lineChar}, func:{methodName} (pc:0x{_programCounter.ToString("X8")})";

                //ついでにコンソールにも表示
                type = Type.GetType("UdonSharp.UdonSharpUtils, UdonSharp.Editor");
                type.GetMethod("LogRuntimeError").Invoke(null, new object[] {
                    $"(pc:0x{_programCounter.ToString("X8")})",
                    "[<color=#00FFFF>UdonErrorAnalyzer</color>]",
                    filePath,
                    line,
                    lineChar
                });
            }

            EditorGUILayout.TextField("C# code info", _info);
        }
    }
}

使い方

  1. エラーログから対象のUdonSharpクラス名とプログラムカウンタを取得

  1. エディタ拡張にクラス名に対応するUdonSharpProgramAssetとプログラムカウンタを指定して実行

  2. ソースコード位置を取得

ついでにコンソールにも表示されます。ダブルクリックでソースコードの該当行を開いてくれます。

備考

Heap Dumpの情報とUdonAssemblyのdataセクション突き合わせればエラー発生時の各変数の情報がわかる。
この辺もツールで可視化できたら便利そう。

Discussion