Viable solution to grab the end of a method

97 views
Skip to first unread message

Indiefreaks.com

unread,
Jul 7, 2011, 5:47:17 AM7/7/11
to mono-cecil
Hi,

In my current Xna game profiler tool (learn more at http://indiefreaks.com),
I'm wrapping each method that should be profiled with my profiling
code.

Right now, in order to do so, I'm getting the first instruction from
the MethodDefinition Body as well as the last one and inject my code
before them.

It works perfectly fine on most cases except when a control flow
operation code is set in the body.

My profiling code that is injected before the actual method is invoked
works fine but because of the previous statement, the code I inject at
what I would consider the end of the method never gets called or gets
called inside an if or switch statement which crashes the resulting
assembly.

From what I understand, if I have such control flow operations, the
last instruction in a method body may not be the end of the current
Method.

I'm now considering parsing the whole method instructions and look for
each OpCodes.Ret instance to add my custom code there but I wondered
if there was a better solution to find the end of a method or hook to
it.

Thanks

Alex

unread,
Jul 7, 2011, 7:44:04 AM7/7/11
to mono-...@googlegroups.com
Hi,

I think what you propose is the best way to achieve this. I'm not sure
if 'ret' is the only opcode that can leave a method normally, though.

Regards,
Alex

> --
> --
> mono-cecil

Indiefreaks.com

unread,
Jul 7, 2011, 8:30:21 AM7/7/11
to mono-cecil
Well, I tried that method but I get way too many return operations in
some situations like when a switch operation occurs in the method.
Moreover, if I fear that simply injecting my code before the
OpCodes.Ret instruction wouldn't work neither in some cases like when
a value is returned by the method.

I may also go down the route of cloning the current method and adding
it (with a different name) to the current class and then clear the
current method instructions and surround a call to the cached method
by my profiling code making sure that I catch the returned value if
different from "System.Void" to return it back once the original
method ends...

On 7 juil, 13:44, Alex <xtzgzo...@gmail.com> wrote:
> Hi,
>
> I think what you propose is the best way to achieve this. I'm not sure
> if 'ret' is the only opcode that can leave a method normally, though.
>
> Regards,
> Alex
>
> On Thu, Jul 7, 2011 at 11:47 AM, Indiefreaks.com
>
>
>
> <webmas...@indiefreaks.com> wrote:
> > Hi,
>
> > In my current Xna game profiler tool (learn more athttp://indiefreaks.com),
> > I'm wrapping each method that should be profiled with my profiling
> > code.
>
> > Right now, in order to do so, I'm getting the first instruction from
> > the MethodDefinition Body as well as the last one and inject my code
> > before them.
>
> > It works perfectly fine on most cases except when a control flow
> > operation code is set in the body.
>
> > My profiling code that is injected before the actual method is invoked
> > works fine but because of the previous statement, the code I inject at
> > what I would consider the end of the method never gets called or gets
> > called inside an if or switch statement which crashes the resulting
> > assembly.
>
> > From what I understand, if I have such control flow operations, the
> > last instruction in a method body may not be the end of the current
> > Method.
>
> > I'm now considering parsing the whole method instructions and look for
> > each OpCodes.Ret instance to add my custom code there but I wondered
> > if there was a better solution to find the end of a method or hook to
> > it.
>
> > Thanks
>
> > --
> > --
> > mono-cecil- Masquer le texte des messages précédents -
>
> - Afficher le texte des messages précédents -

Alex

unread,
Jul 7, 2011, 8:37:09 AM7/7/11
to mono-...@googlegroups.com
Hi,

>Well, I tried that method but I get way too many return operations in
some situations like when a switch operation occurs in the method.
Moreover, if I fear that simply injecting my code before the
OpCodes.Ret instruction wouldn't work neither in some cases like when
a value is returned by the method.

I don't think that's a problem as long as you correctly stabilize the stack.

Regards,
Alex

> --
> --
> mono-cecil

Indiefreaks.com

unread,
Jul 7, 2011, 9:15:56 AM7/7/11
to mono-cecil
What do you mean stabilizing the stack?

Gábor Kozár

unread,
Jul 7, 2011, 4:34:13 PM7/7/11
to mono-...@googlegroups.com
It means that the exact same values will be on the stack after your custom injected code, than before. Essentially means that your injected code does not push any value to the stack it does not pop later, and also it does not pop any values from the stack that it did not push (unless you save the value to, for example, a local variable first and then load it back later).

My project had a similar problem, when I was writing a method using Mono.Cecil to inline (specific) method calls. I solved it by basically injecting my custom code before all 'ret' instructions. (It's a bit more complicated than that, because most of the 'ret' instructions are removed during inlining, but you get the point.)

2011/7/7 Indiefreaks.com <webm...@indiefreaks.com>
--
--
mono-cecil

Indiefreaks.com

unread,
Jul 7, 2011, 5:57:58 PM7/7/11
to mono-cecil
Thanks Gabor.

I actually implemented adding my code injection before each
OpCodes.Return instance in the MethodDefinition.Body.Instructions
list.

I did need to inject the code differently depending if the method was
returning a value or void so that it kept returning it correctly.

However, I'm now facing an issue with if statements that are on the
end of a method. For instance, imagine the following method:

public void Foo()
{
this.DoSomething();

if(myProperty.IsTrue)
{
this.DoSomethingElse();
}
}

If I use the last OpCodes.Ret, it actually injects my code in the if
statement like so:

public void Foo()
{
this.DoSomething();

if(myProperty.IsTrue)
{
this.DoSomethingElse();
this.MyInjectedCodeMethod();
}
}

instead of putting it at the real end of the method. Do I have no
other choice than parsing the method and see if I have a brfalse or
brtrue that points to one of my ret addresses in the IL instructions?

And if that is the case, is there any other similar case that I should
take care of?

Thanks

On 7 juil, 22:34, Gábor Kozár <kozarga...@gmail.com> wrote:
> It means that the exact same values will be on the stack after your custom
> injected code, than before. Essentially means that your injected code does
> not push any value to the stack it does not pop later, and also it does not
> pop any values from the stack that it did not push (unless you save the
> value to, for example, a local variable first and then load it back later).
>
> My project had a similar problem, when I was writing a method using
> Mono.Cecil to inline (specific) method calls. I solved it by basically
> injecting my custom code before all 'ret' instructions. (It's a bit more
> complicated than that, because most of the 'ret' instructions are removed
> during inlining, but you get the point.)
>
> 2011/7/7 Indiefreaks.com <webmas...@indiefreaks.com>

Gábor Kozár

unread,
Jul 8, 2011, 4:30:49 AM7/8/11
to mono-...@googlegroups.com
I see your problem. That never came up for me, as I mentioned, it's a slightly different situation. What I actually did was creating a map of Instruction -> offset (int), and I went through the method body and adjusted all Instruction pointers.

I think this is possible for all instructions whose OpCode.OperandType is InlineBr or InlineBr_S (i.e. all instructions whose operands are of type Instruction).
I'm not sure if the switch instruction can also cause the same problem.

Good luck! :)

2011/7/7 Indiefreaks.com <webm...@indiefreaks.com>
--
--
mono-cecil

Alex

unread,
Jul 8, 2011, 4:48:58 AM7/8/11
to mono-...@googlegroups.com
Hi,

>If I use the last OpCodes.Ret, it actually injects my code in the if statement like so:

Right. You don't have much choice but to do control flow analysis
here; build a control flow graph from the IL and figure out where to
inject code.

>I'm not sure if the switch instruction can also cause the same problem.

Its operand is just an array of instruction pointers, so someone
certainly could write some pretty whacky code with that...

Regards,
Alex

> --
> --
> mono-cecil

Jb Evain

unread,
Jul 8, 2011, 4:51:25 AM7/8/11
to mono-...@googlegroups.com
On Thu, Jul 7, 2011 at 2:30 PM, Indiefreaks.com
<webm...@indiefreaks.com> wrote:
> I may also go down the route of cloning the current method and adding
> it (with a different name) to the current class and then clear the
> current method instructions and surround a call to the cached method
> by my profiling code making sure that I catch the returned value if
> different from "System.Void" to return it back once the original
> method ends...

This is much easier.

Jb

Gábor Kozár

unread,
Jul 8, 2011, 5:08:07 AM7/8/11
to mono-...@googlegroups.com
But wouldn't that hurt performance? I realize that the JIT can inline methods when it feels like it, but I don't know under what circumstances.
Although this potentially could be one, as the cached method would only be called in a single other method (that was generated)...

2011/7/8 Jb Evain <jbe...@gmail.com>
--
--
mono-cecil

Jb Evain

unread,
Jul 8, 2011, 5:11:16 AM7/8/11
to mono-...@googlegroups.com
On Fri, Jul 8, 2011 at 11:08 AM, Gábor Kozár <kozar...@gmail.com> wrote:
> But wouldn't that hurt performance? I realize that the JIT can inline
> methods when it feels like it, but I don't know under what circumstances.

Not more than injecting code at every ret point.

Jb

Gábor Kozár

unread,
Jul 8, 2011, 5:16:22 AM7/8/11
to mono-...@googlegroups.com
I mean that method calls are expensive operations, unless, of course, the JIT decides to inline them.

2011/7/8 Jb Evain <jbe...@gmail.com>
--
--
mono-cecil

Indiefreaks.com

unread,
Jul 8, 2011, 7:45:07 AM7/8/11
to mono-cecil
I believe it would be expensive too as far as I understand how IL is
then transformed to native code: each method found within IL will be
transformed in a native method call and if a program uses say, for
instance, a simple for loop on hundreds of classes' methods, adding a
"nested" method call would increase cpu usage.

On another hand, I managed to inject the code before every ret making
sure that the stack stays correct when a value is returned.

However, I have some PEVerify errors in assemblies that use Generics
stating that fields inside generic classes should be referenced using
MemberRefs even on the same module than the class. How can I solve
that?

Thanks

On 8 juil, 11:16, Gábor Kozár <kozarga...@gmail.com> wrote:
> I mean that method calls are expensive operations, unless, of course, the
> JIT decides to inline them.
>
> 2011/7/8 Jb Evain <jbev...@gmail.com>
>
>
>
> > On Fri, Jul 8, 2011 at 11:08 AM, Gábor Kozár <kozarga...@gmail.com> wrote:
> > > But wouldn't that hurt performance? I realize that the JIT can inline
> > > methods when it feels like it, but I don't know under what circumstances.
>
> > Not more than injecting code at every ret point.
>
> > Jb
>
> > --
> > --

Alex

unread,
Jul 8, 2011, 8:23:03 AM7/8/11
to mono-...@googlegroups.com
Hi,

>However, I have some PEVerify errors in assemblies that use Generics
stating that fields inside generic classes should be referenced using
MemberRefs even on the same module than the class. How can I solve
that?

Make a FieldReference and use that instead of a FieldDefinition in
whatever place you're getting the error.

Regards,
Alex

> --
> --
> mono-cecil

Indiefreaks.com

unread,
Jul 8, 2011, 8:54:46 AM7/8/11
to mono-cecil
> Make a FieldReference and use that instead of a FieldDefinition in
> whatever place you're getting the error.

But then I won't be able to add it to the class Fields collection: how
can I let the class type know of its existence then?

Since FieldDefinition inherits from FieldReference, casting to
FieldReference should work, shouldn't it?

Jb Evain

unread,
Jul 8, 2011, 8:57:11 AM7/8/11
to mono-...@googlegroups.com
On Fri, Jul 8, 2011 at 2:54 PM, Indiefreaks.com
<webm...@indiefreaks.com> wrote:
>> Make a FieldReference and use that instead of a FieldDefinition in
>> whatever place you're getting the error.
>
> But then I won't be able to add it to the class Fields collection: how
> can I let the class type know of its existence then?
>
> Since FieldDefinition inherits from FieldReference, casting to
> FieldReference should work, shouldn't it?

We have nice threads in this group about how to deal with generics.
Look them up.

Jb

Indiefreaks.com

unread,
Jul 8, 2011, 9:56:40 AM7/8/11
to mono-cecil
I will Jb. Thanks for pointing that out.

On 8 juil, 14:57, Jb Evain <jbev...@gmail.com> wrote:
> On Fri, Jul 8, 2011 at 2:54 PM, Indiefreaks.com
>

Indiefreaks.com

unread,
Jul 8, 2011, 10:31:52 AM7/8/11
to mono-cecil
By the way, I searched for such threads but I'm having difficulties
finding one that actually talks about my issue: injecting a field in a
generic class & injecting code to call this field in the generic class
methods.

Thanks

On 8 juil, 15:56, "Indiefreaks.com" <webmas...@indiefreaks.com> wrote:
> I will Jb. Thanks for pointing that out.
>
> On 8 juil, 14:57, Jb Evain <jbev...@gmail.com> wrote:
>
>
>
> > On Fri, Jul 8, 2011 at 2:54 PM, Indiefreaks.com
>
> > <webmas...@indiefreaks.com> wrote:
> > >> Make a FieldReference and use that instead of a FieldDefinition in
> > >> whatever place you're getting the error.
>
> > > But then I won't be able to add it to theclassFields collection: how
> > > can I let theclasstype know of its existence then?
>
> > > Since FieldDefinition inherits from FieldReference, casting to
> > > FieldReference should work, shouldn't it?
>
> > We have nice threads in this group about how to deal with generics.
> > Look them up.
>
> > Jb- Masquer le texte des messages précédents -

Jb Evain

unread,
Jul 8, 2011, 10:37:07 AM7/8/11
to mono-...@googlegroups.com
On Fri, Jul 8, 2011 at 4:31 PM, Indiefreaks.com
<webm...@indiefreaks.com> wrote:
> By the way, I searched for such threads but I'm having difficulties
> finding one that actually talks about my issue: injecting a field in a
> generic class & injecting code to call this field in the generic class
> methods.

You need to create one FieldDefinition for, well, the definition of
the field, and then for each usage, create a FieldReference with the
proper generic signature, based on the definition, search for
MakeGeneric*

Jb

Indiefreaks.com

unread,
Jul 12, 2011, 4:45:25 AM7/12/11
to mono-cecil
Ok, after a while on this topic, I decided to postpone Generics
support to my profiler system until I got it right.

I'll let you know of my progress.

Thanks

On 8 juil, 16:37, Jb Evain <jbev...@gmail.com> wrote:
> On Fri, Jul 8, 2011 at 4:31 PM, Indiefreaks.com
>
Reply all
Reply to author
Forward
0 new messages