cil - Use Cecil to insert begin/end block around functions -
this simple code works fine , allows add beginsample/endsample
call around each update/lateupdate/fixedupdate
function. doesn't take in consideration return instructions, example result of condition. know how write similar function take in considerations returns endsample call executed under every circumstance?
note not cecil expert, learning now. appears me cecil automatically updates operations returns after calling insertbefore
, similar functions. if br
opcode jumping specific instruction address, address updated after insertions in order jump original instruction. ok in of cases, in case means if
statement skip last inserted operation br
operation still point directly final ret
instruction. note update
, lateupdate
, fixedupdate
void functions.
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; } }
as bonus, if can explain me difference between emit
, append
newly created instruction same operand. append execute emit
under hood or more?
i may have found solution, @ least apparently works. followed code used solve similar problem here:
https://groups.google.com/forum/#!msg/mono-cecil/ne6jbjvefcq/mqv6tgdcb4aj
i adapted purposes , seemed work, although may find out other issues. complete code:
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 classes related 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); (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) { (var index = 0; index < body.instructions.count - 2; index++) { var instruction = body.instructions[index]; if (instruction.operand != null && instruction.operand == formallylastretinstruction) { instruction.operand = lastleaveinstruction; } } }
basically add ret
instuction, replace previous ret
(usually one, why should more one?) leave
function (don't know means :) ), previous jumps remain valid. differently original code, make leave
instruction point endsample
call before last ret
Comments
Post a Comment