private static Instruction GetFirstPreviousLdarg_0(this Instruction instruction) { var previous = instruction.Previous; if (previous == null) { Console.WriteLine(instruction.ToString()); return(instruction); } if (previous.OpCode == OpCodes.Ldarg_0 || previous.OpCode == OpCodes.Ldarg && previous.Operand is ParameterDefinition && ((ParameterDefinition)previous.Operand).Name == string.Empty) { return(previous); } return(previous.GetFirstPreviousLdarg_0()); }
private static void Instrument ( AssemblyDefinition assembly, TypeDefinition type, Instruction instruction, MethodReference countReference, MethodDefinition method, ILProcessor worker, string lastLine, InstrumentConfig config, TextWriter writer, ref int instrumentIndex) { //if the previous instruction is a Prefix instruction then this instruction MUST go with it. //we cannot put an instruction between the two. if (instruction.Previous != null && instruction.Previous.OpCode.OpCodeType == OpCodeType.Prefix) return; if (config.HasOffset (method.FullName, instruction.Offset)) return; if (lastLine != null && config.HasLine (method.FullName, lastLine)) { return; } var lineNumStart = -1; var lineNumEnd = -1; if (instruction.SequencePoint != null) { lineNumStart = instruction.SequencePoint.StartLine; lineNumEnd = instruction.SequencePoint.EndLine; } var parentTypeRef = type; while (parentTypeRef.DeclaringType != null) parentTypeRef = parentTypeRef.DeclaringType; var line = string.Join ("\t", assembly.Name, //0 parentTypeRef.FullName,//1 method.FullName, //2 lineNumStart, //3 lineNumEnd, //4 instruction.Offset, //5 instruction.ToString ().Replace ("\n", " "), //6 instruction.SequencePoint?.Document.Url); //7 writer.WriteLine (line); var pathParamLoadInstruction = worker.Create (OpCodes.Ldstr, config.HitsPathPrefix); var lineParamLoadInstruction = worker.Create (OpCodes.Ldc_I4, instrumentIndex); var registerInstruction = worker.Create (OpCodes.Call, countReference); //inserting method before instruction because after will not happen after a method Ret instruction worker.InsertBefore (instruction, pathParamLoadInstruction); worker.InsertAfter (pathParamLoadInstruction, lineParamLoadInstruction); worker.InsertAfter (lineParamLoadInstruction, registerInstruction); ++instrumentIndex; //change try/finally etc to point to our first instruction if they referenced the one we inserted before foreach (var handler in method.Body.ExceptionHandlers) { if (handler.FilterStart == instruction) handler.FilterStart = pathParamLoadInstruction; if (handler.TryStart == instruction) handler.TryStart = pathParamLoadInstruction; if (handler.TryEnd == instruction) handler.TryEnd = pathParamLoadInstruction; if (handler.HandlerStart == instruction) handler.HandlerStart = pathParamLoadInstruction; if (handler.HandlerEnd == instruction) handler.HandlerEnd = pathParamLoadInstruction; } //change instructions with a target instruction if they referenced the one we inserted before to be our first instruction foreach (var iteratedInstruction in method.Body.Instructions) { var operand = iteratedInstruction.Operand; if (operand == instruction) { iteratedInstruction.Operand = pathParamLoadInstruction; continue; } if (!(operand is Instruction [])) continue; var operands = (Instruction [])operand; for (var i = 0; i < operands.Length; ++i) { if (operands [i] == instruction) operands [i] = pathParamLoadInstruction; } } }
/// <summary> /// Create an Instruction record in the database /// </summary> /// <param name="command">Command factory object</param> /// <param name="instruction">Instruction</param> private void CreateInstructionRecord(CommandFactory command, Instruction instruction) { command.Insert<InstructionField>( command.GetLastId<AssemblyField>(), command.GetLastId<ModuleField>(), command.GetLastId<TypeField>(), command.GetLastId<MethodField>(), instruction.ToString() ); }
private string InstructionToString(Instruction i) { return i.ToString().Split(new[] {": "}, 2, StringSplitOptions.None)[1]; }
public static void MethodInstrument(string targetAssembly, string src, string dst) { var inputAssembly = targetAssembly; var path = Path.GetDirectoryName(inputAssembly); var assemblyResolver = new DefaultAssemblyResolver(); var assemblyLocation = Path.GetDirectoryName(inputAssembly); assemblyResolver.AddSearchDirectory(assemblyLocation); var readerParameters = new ReaderParameters { AssemblyResolver = assemblyResolver }; var writerParameters = new WriterParameters(); var origPdb = Path.ChangeExtension(inputAssembly, "pdb"); bool existpdb = false; if (File.Exists(origPdb)) { existpdb = true; var symbolReaderProvider = new PdbReaderProvider(); readerParameters.SymbolReaderProvider = symbolReaderProvider; readerParameters.ReadSymbols = true; //var symbolWriterProvider = new PdbWriterProvider(); //writerParameters.SymbolWriterProvider = symbolWriterProvider; writerParameters.WriteSymbols = true; } else { Console.WriteLine(".pdb file is unavailable."); } var assemblyDefinition = AssemblyDefinition.ReadAssembly(inputAssembly, readerParameters); var module = assemblyDefinition.MainModule; //Mono.Collections.Generic.Collection<ModuleDefinition> modules = assemblyDefinition.Modules; //var module2 = ModuleDefinition.ReadModule(inputAssembly, readerParameters); //---------------------- bool isChanged = false; //---------------------------- foreach (var typeDefinition in module.Types) { if (typeDefinition.IsInterface) { continue; } foreach (var methodDefinition in typeDefinition.Methods) { ILProcessor ilprocessor = methodDefinition.Body.GetILProcessor(); Mono.Collections.Generic.Collection <Mono.Cecil.Cil.Instruction> allinstructions = methodDefinition.Body.Instructions; int instructioncnt = allinstructions.Count; for (int i = 0; i < instructioncnt; i++) { Mono.Cecil.Cil.Instruction instruction = allinstructions[i]; string instructpresent = instruction.ToString(); OpCode code = instruction.OpCode; string codepresent = code.Name; var operand = instruction.Operand; string methodname = ""; if (operand != null) { methodname = operand.ToString(); //with parameter } if ((code == OpCodes.Callvirt || code == OpCodes.Call) && //codepresent.Contains("call") (isSameMethod(methodname, src))) //or (operand as MethodReference == module.Import(src as MethodInfo)) { Console.WriteLine(codepresent + " " + methodname); //debug //---------------------- remove --------------------------// /**** remove instruction in case of no replacing method is provided ****/ //TODO: 1. I note the stack depth after the call and start removing instructions, beginning with the Code.Call // and moving backward until I get to that stack depth again. // 2. return value handling ilprocessor.Remove(instruction); instructioncnt--; if (allinstructions[i].OpCode == OpCodes.Pop) //TODO: just for test { ilprocessor.Remove(allinstructions[i]); instructioncnt--; } i--; if (dst == "") { continue; } //---------------------- replace (remove + insert) ------------------------// /**** create new MethodInfo ****/ string m_type = getNamespace(dst); string m_name = getMethodName(dst); string [] m_para = getParameters(dst); MethodInfo writeLineMethod; //e.g. MethodInfo writeLineMethod = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }); if (m_para != null) { int paraCnt = m_para.Length; Type[] m_para_type = new Type[paraCnt]; for (int k = 0; k < paraCnt; k++) { m_para_type[k] = Type.GetType(m_para[k]); } writeLineMethod = Type.GetType(m_type).GetMethod(m_name, m_para_type); } else //the method does not have input parameters { writeLineMethod = Type.GetType(m_type).GetMethod(m_name, new Type[] { }); } if (writeLineMethod == null) { throw new InvalidFileFormatException("no MethodInfo is created -- possibly a wrong method delaration."); } /*** Import the new (e.g. Console.WriteLine() method) ***/ MethodReference writeLine; writeLine = module.Import(writeLineMethod); //convert "MethodInfo/MethodDefinition" to "MethodReference" /*** Creates the CIL instruction for calling the new method (e.g. Console.WriteLine(string value) method) ***/ Mono.Cecil.Cil.Instruction callWriteLine; callWriteLine = ilprocessor.Create(OpCodes.Call, writeLine); /*** replace old instruction ***/ //TODO: 1. for simple-type parameters, create one instance and push to stack; // otherwise just a null object?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // 2. return value handling ilprocessor.InsertAfter(allinstructions[i], callWriteLine); //----------------------------------------------// isChanged = true; } } //foreach instruction of a method } //foreach method of a type/class } //foreach type/class of loaded assembly if (isChanged) { var temporaryFileName = Path.Combine(path, string.Format("{0}_temp{1}", Path.GetFileNameWithoutExtension(inputAssembly), Path.GetExtension(inputAssembly))); var backupFileName = Path.Combine(path, string.Format("{0}_orig{1}", Path.GetFileNameWithoutExtension(inputAssembly), Path.GetExtension(inputAssembly))); var tmpPdb = Path.Combine(path, string.Format("{0}_temp{1}", Path.GetFileNameWithoutExtension(inputAssembly), ".pdb")); var backupPdb = Path.Combine(path, string.Format("{0}_orig{1}", Path.GetFileNameWithoutExtension(inputAssembly), ".pdb")); //write an assembly with symbol file being rewritten //module.Write(temporaryFileName, writerParameters); assemblyDefinition.Write(temporaryFileName, writerParameters); File.Replace(temporaryFileName, inputAssembly, backupFileName); if (existpdb) { File.Replace(tmpPdb, origPdb, backupPdb); } //TODO: add snkfile in configuration file and parse ([email protected]) // If you have access to the key pair, you can ask Cecil to sign it directly. //if (snkFile != "") //{ //sn -R inputAssembly snkFile //} } }