Вы были почти правы с вашим кодом. Было несколько изменений, которые нужно было сделать.
Не уверен, зачем вам нужен код операции ldstr
, так как он нигде не нужен. Для вызова, в который вы хотите вставить до , первый код операции не после. Что касается последней инструкции, вы можете go с InsertBefore
. Таким образом, окончательный код может быть таким:
foreach (var module_ in target_module)
{
foreach (var method_ in module_.Methods)
{
var processor2 = method_.Body.GetILProcessor();
var first_instruction = method_.Body.Instructions.First();
var last_instruction = method_.Body.Instructions.Last();
var call = processor2.Create(OpCodes.Call, method_.Module.Import(start_method));
var call2 = processor2.Create(OpCodes.Call, method_.Module.Import(stop_method));
processor2.InsertBefore(first_instruction, call);
processor2.InsertBefore(last_instruction, call2);
}
}
, но это не сработает с некоторыми ранними возвратами. Почему? Ранние возвраты кодируются в виде кода операции br
или br_s
для ret
в конце процедуры, и если мы введем наш call
до ретрита, эти ранние возвраты пропустят это. В вашем примере это не нужно, так как этот код преобразуется в if-else
, и у нас есть ветвь в обоих случаях, правильно подобранная. Но у нас есть код, подобный следующему:
int a = 3;
if (a == 3)
{
return; // very early return here
}
// the rest as in original one
мы не увидим истекшее время, напечатанное для этого метода, так как return будет направлять выполнение после нашего введенного вызова. Здесь нам нужно обновить все инструкции ветвления, которые отвечают за ранний возврат (поэтому они переходят на ret
код операции) и направить их на наш вызов. Мы можем сделать это следующим образом:
foreach (var bodyInstruction in method_.Body.Instructions)
{
if (bodyInstruction.OpCode != OpCodes.Br && bodyInstruction.OpCode != OpCodes.Br_S) continue;
if (((Instruction)bodyInstruction.Operand).OpCode != OpCodes.Ret) continue;
bodyInstruction.Operand = call2;
}
Итак, что мы делаем здесь, так это то, что мы сканируем все коды операций и видим, есть ли у нас br
или br_s
, который переходит на возврат, мы обновляем его вместо этого перейти к нашему звонку. Violà.
Примечание: использовалось Elapsed
вместо ElapsedMilliseconds
, так как первое предоставляло все нули.
Полный код :
var start_method = (dynamic) null;
var stop_method = (dynamic) null;
AssemblyDefinition target_assembly = AssemblyDefinition.ReadAssembly("target.exe", new ReaderParameters {ReadWrite = true});
var target_modules = target_assembly.MainModule;
TypeDefinition[] target_module = target_modules.Types.ToArray();
AssemblyDefinition source_assembly = AssemblyDefinition.ReadAssembly("stopwatch.dll", new ReaderParameters {ReadWrite = true});
var source_modules = source_assembly.MainModule;
TypeDefinition[] source_module = source_modules.Types.ToArray();
foreach (var type in source_module)
{
foreach (var method in type.Methods)
{
if (method.Name == "stopwatch_start")
{
start_method = method;
}
if (method.Name == "stopwatch_stop")
{
stop_method = method;
}
}
}
foreach (var module_ in target_module)
{
foreach (var method_ in module_.Methods)
{
var processor2 = method_.Body.GetILProcessor();
var first_instruction = method_.Body.Instructions.First();
var last_instruction = method_.Body.Instructions.Last();
var call = processor2.Create(OpCodes.Call, method_.Module.Import(start_method));
var call2 = processor2.Create(OpCodes.Call, method_.Module.Import(stop_method));
processor2.InsertBefore(first_instruction, call);
processor2.InsertBefore(last_instruction, call2);
foreach (var bodyInstruction in method_.Body.Instructions)
{
if (bodyInstruction.OpCode != OpCodes.Br && bodyInstruction.OpCode != OpCodes.Br_S) continue;
if (((Instruction)bodyInstruction.Operand).OpCode != OpCodes.Ret) continue;
bodyInstruction.Operand = call2;
}
}
}
target_assembly.Write();
самореклама на
Я случайно записал два видео о том, как сделать это (немного по-другому) с Mono.Cecil. Вы можете найти его Запись простых. NET трассировщик выполнения с Mono.Cecil и Instrumenting. NET сборки для измерения времени выполнения метода с помощью Mono.Cecil .
самореклама отключена