2017-07-25 6 views
0

この単純なコードは正常に動作し、それぞれUpdate/LateUpdate/FixedUpdate関数の周りにBeginSample/EndSampleコールを追加できます。しかし、例えば条件の結果として、早期返却指示を考慮していない。 EndSampleコールがあらゆる状況下で実行されるように、早期復帰を考慮する同様の関数を書く方法を知っていますか?Cecilを使用して、関数の前後にbegin/endブロックを挿入します。

私はセシルの専門家ではないことに注意してください。私は今学んでいます。 InsertBeforeなどの関数を呼び出すと、早く返される操作がCecilによって自動的に更新されるようです。したがって、オペコードがBRのオペコードが以前に特定の命令アドレスにジャンプしていた場合、そのアドレスは挿入後に更新され、元の命令にジャンプします。これは大抵の場合は問題ありませんが、私の場合は、BR操作が最後のRet命令を直接指し示すので、ifステートメントが最後に挿入された操作をスキップすることを意味します。 Update,LateUpdateおよびFixedUpdateはすべて無効関数であることに注意してください。ボーナスとして

foreach (var method in type.Methods) 
{ 
    if ((method.Name == "Update" || method.Name == "LateUpdate" || method.Name == "FixedUpdate") && 
     method.HasParameters == false) 
    { 
     var beginMethod = 
      module.ImportReference(typeof (Profiler).GetMethod("BeginSample", 
                   new[] {typeof (string)})); 
     var endMethod = 
      module.ImportReference(typeof (Profiler).GetMethod("EndSample", 
                   BindingFlags.Static | 
                   BindingFlags.Public)); 

     Debug.Log(method.Name + " method found in class: " + type.Name); 

     var ilProcessor = method.Body.GetILProcessor(); 

     var first = method.Body.Instructions[0]; 
     ilProcessor.InsertBefore(first, 
           Instruction.Create(OpCodes.Ldstr, 
                type.FullName + "." + method.Name)); 
     ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Call, beginMethod)); 

     var lastRet = method.Body.Instructions[method.Body.Instructions.Count - 1]; 
     ilProcessor.InsertBefore(lastRet, Instruction.Create(OpCodes.Call, endMethod)); 

     changed = true; 
    } 
} 

、あなたは私に同じオペランドと新たに作成された命令EmitAppendの違いを説明できるかどうか。 AppendはEmitをフードの下で実行するのでしょうか?

+0

Cecilについて何も知らないうちにクイックコメント:内部コントロールフローに関係なく常に何かが起こることを保証するには、すべての出口ポイントを狩るのではなく、「try .. finallyブロック」を実行するのは明らかです。別の明白なアプローチ(これと組み合わせる)は元の関数の名前を変更し、 'begin()/ function()/ end()'を実行するラッパーで完全に置き換えることです。最後に、ホイールを再開発するのではなく、プロファイラの使用を検討することができます。もちろん、自分の車輪が本当に必要なわけではありません。 –

答えて

0

私は解決策を見つけたかもしれませんが、少なくとも明らかにそれは機能します。私は私の目的のためにそれを適応し、私は他の問題を見つけるかもしれないが、動作するように見え

https://groups.google.com/forum/#!msg/mono-cecil/nE6JBjvEFCQ/MqV6tgDCB4AJ

:私はここから同様の問題を解決するために使用されるコードを追いました。これは完全なコードです:

static bool ProcessAssembly(AssemblyDefinition assembly) 
{ 
    var changed = false; 

    var moduleG = assembly.MainModule; 

    var attributeConstructor = 
      moduleG.ImportReference(
       typeof(RamjetProfilerPostProcessedAssemblyAttribute).GetConstructor(Type.EmptyTypes)); 
    var attribute = new CustomAttribute(attributeConstructor); 
    var ramjet = moduleG.ImportReference(typeof(RamjetProfilerPostProcessedAssemblyAttribute)); 
    if (assembly.HasCustomAttributes) 
    { 
     var attributes = assembly.CustomAttributes; 
     foreach (var attr in attributes) 
     { 
      if (attr.AttributeType.FullName == ramjet.FullName) 
      { 
       Debug.LogWarning("<color=yellow>Skipping already-patched assembly:</color> " + assembly.Name); 
       return false; 
      } 
     } 
    } 
    assembly.CustomAttributes.Add(attribute); 

    foreach (var module in assembly.Modules) 
    { 
     foreach (var type in module.Types) 
     { 
      // Skip any classes related to the RamjetProfiler 
      if (type.Name.Contains("AssemblyPostProcessor") || type.Name.Contains("RamjetProfiler")) 
      { 
       // Todo: use actual type equals, not string matching 
       Debug.Log("Skipping self class : " + type.Name); 
       continue; 
      } 

      if (type.BaseType != null && type.BaseType.FullName.Contains("UnityEngine.MonoBehaviour")) 
      { 
       foreach (var method in type.Methods) 
       { 
        if ((method.Name == "Update" || method.Name == "LateUpdate" || method.Name == "FixedUpdate") && 
         method.HasParameters == false) 
        { 
         var beginMethod = 
          module.ImportReference(typeof(Profiler).GetMethod("BeginSample", 
                       new[] { typeof(string) })); 
         var endMethod = 
          module.ImportReference(typeof(Profiler).GetMethod("EndSample", 
                       BindingFlags.Static | 
                       BindingFlags.Public)); 

         Debug.Log(method.Name + " method found in class: " + type.Name); 

         var ilProcessor = method.Body.GetILProcessor(); 

         var first = method.Body.Instructions[0]; 
         ilProcessor.InsertBefore(first, 
                Instruction.Create(OpCodes.Ldstr, 
                    type.FullName + "." + method.Name)); 
         ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Call, beginMethod)); 

         var lastcall = Instruction.Create(OpCodes.Call, endMethod); 

         FixReturns(method, lastcall); 

         changed = true; 
        } 
       } 
      } 
     } 
    } 

    return changed; 
} 

static void FixReturns(MethodDefinition med, Instruction lastcall) 
{ 
    MethodBody body = med.Body; 

    var instructions = body.Instructions; 
    Instruction formallyLastInstruction = instructions[instructions.Count - 1]; 
    Instruction lastLeaveInstruction = null; 

    var lastRet = Instruction.Create(OpCodes.Ret); 
    instructions.Add(lastcall); 
    instructions.Add(lastRet); 

    for (var index = 0; index < instructions.Count - 1; index++) 
    { 
     var instruction = instructions[index]; 
     if (instruction.OpCode == OpCodes.Ret) 
     { 
      Instruction leaveInstruction = Instruction.Create(OpCodes.Leave, lastcall); 
      if (instruction == formallyLastInstruction) 
      { 
       lastLeaveInstruction = leaveInstruction; 
      } 

      instructions[index] = leaveInstruction; 
     } 
    } 

    FixBranchTargets(lastLeaveInstruction, formallyLastInstruction, body); 
} 

private static void FixBranchTargets(
    Instruction lastLeaveInstruction, 
    Instruction formallyLastRetInstruction, 
    MethodBody body) 
{ 
    for (var index = 0; index < body.Instructions.Count - 2; index++) 
    { 
     var instruction = body.Instructions[index]; 
     if (instruction.Operand != null && instruction.Operand == formallyLastRetInstruction) 
     { 
      instruction.Operand = lastLeaveInstruction; 
     } 
    } 
} 

は基本的にはそれが何をするかRet instuctionを追加することですが、その後Leave機能(ドンと以前のすべてのRetを(通常は1、なぜそれが複数でなければなりません?)を置き換えますそれが何を意味するかを知ることさえありません)、前のジャンプはすべて有効です。元のコードとは違ってLeave命令がEndSampleコールの前に最後にRet

+1

オペコードを残すことは、保護された領域(try、catch、... - blocks)から飛び降りるためにさらに使用できるブランチ(br *)だけです。 – thehennyy

関連する問題