식 나무의 동적 생성 결과를 디버깅하기 위해 우리 취재반은 남미 아마존으로 날아갔다.

IL Spy도 DASM도 할 수 없었던 것



ExpresionTree를 이용한 동적인 메소드 구축의 결과를 디컴파일하려고 했을 경우, IL Spy를 사용해도 DASM을 사용해도 일근줄로는 가지 않는다.
왜냐하면, 프로그램을 컴파일해 어셈블리를 생성한 시점이라면, 그 어셈블리 중에는 ExpressioTree 그 자체인가, ExpressionTree를 구축하기 위한 코드가 컴파일 되고 있는 것만으로 생성 결과는 당연히 런타임까지 존재하지 않을 수 없다 .
그래서, 그 소스 코드나 ExoressionTree 등을 표시하는 것은 가능하더라도 그 컴파일 결과를 디컴파일하는 것은 꽤 귀찮은 일이었다.
그런 가운데, 우리 취재반은 dnSpy를 사용하는 것으로 이 ExpressionTree의 동적 생성 결과를 디컴파일은 커녕, 디버그조차 가능하다는 정보를 잡아 일로 남미 아마존으로 날아갔다.

dnSpy를 사용해 보았습니다.



dnSpy 단독으로 어제 쓴 대로 디버그가 가능해지고 있다. 디버깅할 수 있다고 하는 것은 대상 어셈블리를 dnSpy 안에서 실행 가능하다고 하는 것이기 때문에, 아무것도 생각하지 않고 Compile 메소드를 통해 실행하면 스텝 인 할 수 있지? 그리고 달콤한 생각을 가지고 시도했습니다.

using System;
using System.Linq.Expressions;

namespace GoAmazon
{
    class Program
    {
        static void Main(string[] args)
        {
            Expression<Action> expr = () => Console.WriteLine("我々取材班は南米アマゾンへと飛んだ!");
            Action act = expr.Compile();
            act();
        }
    }
}

이것을 dnSpy에 먹여 디버그 해 보니 동적으로 컴파일되었다 act 오치였다.

테나 코토에서 결국 스텝인 할 수 없다는 것은 내용을 긁어 열 수가 없을까 예측을 세웠다.

어떻게 든



이러한 결과에서 런타임에 DynamicAssembly로 올려 버리면 어떻게 될까 생각했을 때 dnSpy의 readme

Set breakpoints in any assembly, including framework assemblies, assemblies in the GAC and assemblies existing only in memory

그리고 있었기 때문에 Assembly는 메모리에 존재하는 것만으로 갈 수 있지 않습니까? 라고 하는 것으로, 헬퍼 만들어 시험해 보았다.

using System;
using System.Linq.Expressions;

namespace GoAmazon
{
    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<int, int, int>> addExpr = (x, y) => x + y;
            ExpressionHelper.CreateDebugHelper(addExpr).Run(10, 20);

            Expression<Action> goAmazon = () => Console.WriteLine("我々取材班は南米アマゾンへと飛んだ!");
            ExpressionHelper.CreateDebugHelper(goAmazon).Run();
        }
    }
}

이런 느낌으로 쓰고 컴파일한 물건을 dnSpy에 먹이게 한다. 먹인 직후가 이하


Expression이 전개되고 있지만, 그 근처는 무시하고, 우선 디버그 해 본다
기본값은 엔트리 포인트에서 중단되므로 Continue를 우선 Click!

그렇다면 방금 전까지 할 수 없었던 ExpressionTree의 결과를 디 컴파일하고 디버깅 할 수 있습니다.





씨앗과 장치



씨앗과 장치는 다음과 같습니다.
using System;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;

namespace GoAmazon
{
    public abstract class ExpressionHelper
    {
        protected static readonly MethodInfo BreakInfo = typeof(Debugger).GetMethod("Break");
        public static ExpressionHelper<TDelegate> CreateDebugHelper<TDelegate>(Expression<TDelegate> expression) where TDelegate:class=>
            new ExpressionHelper<TDelegate>(expression);

    }

    public class ExpressionHelper<TDelegate> : ExpressionHelper where TDelegate : class
    {
        internal ExpressionHelper(Expression<TDelegate> expression)
        {
            (Type[] parameterTypes, Type returnType) getInfo()
            {
                var tmp = typeof(TDelegate).GetMethod("Invoke");
                return (tmp.GetParameters().Select(p => p.ParameterType).ToArray(), tmp.ReturnType);
            }

            Expression<TDelegate> addBreakPoint()
            {
                var breakExpr = Expression.Call(BreakInfo);
                var blockExpr = Expression.Block(breakExpr, Expression.Invoke(expression, expression.Parameters));
                return Expression.Lambda<TDelegate>(blockExpr, expression.Parameters);
            }


            var asmBld =
                AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()),
                    AssemblyBuilderAccess.RunAndCollect);

            var modBld = asmBld.DefineDynamicModule("DynamicallyModule");

            var typeBld = modBld.DefineType("Dependence");

            var delegateInfo = getInfo();

            var methodBld = typeBld.DefineMethod("Target", MethodAttributes.Static | MethodAttributes.Public,
                delegateInfo.returnType, delegateInfo.parameterTypes);

            addBreakPoint().CompileToMethod(methodBld);

            var type = typeBld.CreateType();
            Run = type.GetMethod("Target").CreateDelegate(typeof(TDelegate)) as TDelegate;
        }

        public TDelegate Run { get; }

    }
}


자꾸 말하면, 동적으로 어셈블리 모듈 타입 메소드를 쭉 올리고 있다.
그때 받은 Expression을 바탕으로 Debugger.Break()

요약



한 번에 남미 아마존까지 날아가면 우선 디버깅할 수 있었다(`・ω・´)
도우미의 내용은 그 중 마음이 가면 다시 씁니다.

좋은 웹페이지 즐겨찾기