Welcome to CodeBetter.Com Sign
in | Join
| Help

in Greg Young [MVP] (Entire
Site) Search

Stuff you need to code better!

Home

Blogs

Forums

Photos

Files

This Blog

Home

About

Links

Post Calendar

<

July 2006

>

Su

Mo

Tu

We

Th

Fr

Sa

25

26

27

28

29

30

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

1

2

3

4

5

Post Categories

Agile
(3)

Announcements
(2)

Aspect
Oriented Programming (1)

Contests
(1)

Domain
Driven Design (0)

Opinions
(2)

Reviews
(1)

Under
The Covers (9)

News

20060807 - Speaking at Atlanta "Cutting Edge" .NET group 20060717 -
Speaking at New Orleans .NET group 20060615 - Speaking at Huntsville AL
.NET group 20060609 - Blog moved to CodeBetter.com

Syndication

RSS 2.0

Atom
1.0

Ads

Blogad

Explore CodeBetter

firefox datagrid dualmonitor crystal dataset nullreferenceexception
sharepoint atlas services xp objectdatasource biztalk gridview ajax vista nhibernate nunit iis ado dropdownlist events testing aspx msgbox tablet getpostbackeventreference
viewstate resharper reflector vs2005 usercontrol vb6 nant agile subversion adsdsoobject spiral method tostring rhapsody httphandler tableadapter tdd webservice arraylist datareader svn configurationmanager
addhandler infopath rails adsi fitnesse scrum codesmith loopback sqlserver nullreferenceexception
winforms ruby wpf regex smartphone sourcesafe

Greg Young
[MVP]

Method Calls: Part 1 (Normal Call)

In the
next few “under the covers” posts I am going to put up a bit about
something that most people take for granted, how does that method call
actually work (note in this article we are looking at a very basic IL
"call"? While it seems mundane, this is actually very important
information as it can greatly affect code performance and offer you a
greater understanding of how various items work in the CLR. Let’s start by
putting up a small bit of test code.

class Foo {

public void Test() {

for (int i = 0; i < 10; i++)
{

Console.WriteLine("Test");

}

}

}

class Program {

static void Main(string[] args) {

Foo f = new Foo();

f.Test();

f.Test();

f.Test();

}

}

Listing 1: A simple example for
discussion

The JIT
must deal with some huge problems just to run a simple bit of code like
this. Let’s step into the process to get a better idea of what happens.
Just before control is given to our code the Main method is JIT compiled,
control is then passed to the main method to begin execution. We can see
the disassembly for this code in Listing 2.

static void Main(string[] args) {

Foo f = new Foo();

00000000  push
esi

00000001  mov
ecx,913080h

00000006  call
FFB21FAC

0000000b  mov
esi,eax

f.Test();

0000000d  mov
ecx,esi

0000000f  cmp
dword ptr [ecx],ecx

00000011  call
dword ptr ds:[009130B8h]

f.Test();

00000017  mov
ecx,esi

00000019  cmp
dword ptr [ecx],ecx

0000001b  call
dword ptr ds:[009130B8h]

f.Test();

00000021  mov
ecx,esi

00000023  cmp
dword ptr [ecx],ecx

00000025  call
dword ptr ds:[009130B8h]

0000002b  pop
esi

}

0000002c  ret

Listing 2: Disassembly of the main
method

From
looking at the disassembled code we can see that the generated code is
dereferencing a pointer in order to make the call. There are a few reasons
for this but before we get into them let’s look a bit at how this works.
It’s time to break out our trusty debugger to see what’s going on. If you
have not yet read it, now would be a good time to go back and read my
first post here at CodeBetter Viewing Unmanaged Code in VS.NET as you need this and SOS (Son Of Strike) setup in order to
follow along.

I will put all debugger commands in
bold fixed width and the
output in
normal fixed width.

Put a break point on
the first line of code and start debugging. All commands for SOS should be
typed in your immediate window.

.load
SOS

extension
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\SOS.dll loaded

This
first command loads the SOS debugging tool making it available for our
use.

!Name2EE
ConsoleApplication29.exe
ConsoleApplication29.Foo.Test

PDB symbol for
mscorwks.dll not loaded

Module: 00912c14
(ConsoleApplication29.exe)

Token:
0x06000001

MethodDesc: 00913070

Name:
ConsoleApplication29.Foo.Test()

Not JITTED yet. Use
!bpmd -md 00913070 to break on run.

This command gives us more information on our method. There
is a lot of useful information here but the most important bit of
informatio is the MethodDesc address. We can use this to find out more
information about our method.

!DumpMD 00913070

Method Name:
ConsoleApplication29.Foo.Test()

Class:
009113b8

MethodTable: 00913080

mdToken:
06000001

Module:
00912c14

IsJitted:
no

m_CodeOrIL:
ffffffffffffffff

Here we can get our method table, we can examine our full
method table using this address.

!DumpMT -md 00913080

EEClass:
009113b8

Module:
00912c14

Name:
ConsoleApplication29.Foo

mdToken:
02000002  (C:\Documents and
Settings\Greg\My Documents\Visual Studio
2005\Projects\ConsoleApplication29\ConsoleApplication29\bin\Release\ConsoleApplication29.exe)

BaseSize:
0xc

ComponentSize:
0x0

Number of IFaces in
IFaceMap: 0

Slots in VTable:
6

--------------------------------------

MethodDesc
Table

Entry MethodDesc      JIT
Name

79354bec   7913bd48   PreJIT
System.Object.ToString()

793539c0   7913bd50   PreJIT
System.Object.Equals(System.Object)

793539b0   7913bd68   PreJIT
System.Object.GetHashCode()

7934a4c0   7913bd70   PreJIT
System.Object.Finalize()

009130c8   00913070     NONE
ConsoleApplication29.Foo.Test()

009130d4   00913078     NONE
ConsoleApplication29.Foo..ctor()

As I am sure many people have noticed by now (it has been in
every one of these listings J), our method has not yet been JIT’ed. This is one of the
reasons why we dereference in order to make the call above, at the time
that the main method was compiled it had no idea where it should
call.  This leads us to an
interesting question.

How does the JIT know when
to compile a method?

Essentially the JIT is lazy loading our compilation for us…
It uses a concept known as a “thunk” in order to catch the first time we
try to call into our method. The thunk is a little piece of unmanaged code
that is emitted by the CLR when it first loads the type. The thunk as we
will see shortly contains code to call the JIT or code to call the
method.

Figure
1 : Process of JIT’ing a method

The
process as shown in Figure 1 is almost deceptively simple; in practice
this would be far too slow to create functional systems with. The
difference between this drawing and how it actually works has to deal with
the decision. The picture would lead you to believe that the thunk has
branching code; it does not. Instead the JIT uses a concept known as back
patching.

The
term back patching may be familiar to you as it is also used in garbage
collection, it means to go back and update a pointer to reflect new
information. When the method is called for the first time it reads from
the method table an address that points to our thunk and calls it. Our
thunk forwards us off to the JIT. The key is that when the JIT has
completed we go back and change our method table to now directly point to
the JIT’ed code (we update the table). Figure
2 & Figure
3 illustrate a before and after view of this process. Note that the call moves directly in
the pictures while in reality they are actually reading a memory address
that changes.

Now that we have a high
level view of what happens, let’s verify it by watching it in the
debugger. You can use either your memory window
(debug->windows->memory and enter the memory address of your call
(i.e. 009130B8h from the listing)) or you can use the registers window
(debug->windows->registers (make sure effective address is enabled))
to view the data we need.

Showing stuff like this to your
friends in the debugger is also good for asserting your role as alpha nerd in the office.

Using the debugger step to
line 0011 (the first call). We can see in either location that the memory
address 009130B8 (the address we are dereferencing for our call) contains
009130C8 just before we make our call. This memory address should look
familiar as it was in our method table listing earlier as the entry for
our Test method. This is pointing to our thunk, we can even look at our
thunk as unmanaged code by disassembling that address with the !u
command.

!u
009130C8

Unmanaged
code

009130C8
B870309100
mov
eax,913070h

009130CD
89ED
mov
ebp,ebp

009130CF
E938EEA2FF
jmp
00341F0C

009130D4
B878309100
mov
eax,913078h

009130D9 89ED
mov
ebp,ebp

009130DB
E92CEEA2FF
jmp
00341F0C

009130E0 0000
add
byte ptr [eax],al

009130E2 0000
add
byte ptr [eax],al

009130E4 0000
add
byte ptr [eax],al

009130E6 0000
add
byte ptr [eax],al

This listing is a bit
confusing because it is actually two thunks followed by nothing. So our
call that we were looking at is set to jump to the first instruction here.
That 913070 being loaded should also look familiar it is the address of
our method desc, it is being pushed onto EAX to be passed as a parameter
to the JIT compiler (so the JIT compiler knows what to compile!). Let’s
set a break point in our other method hit f5 and take a look at our
environment after to see what has changed.

!DumpMT -md
00913080

EEClass:
009113b8

Module:
00912c14

Name:
ConsoleApplication29.Foo

mdToken:
02000002  (C:\Documents and
Settings\Greg\My Documents\Visual Studio
2005\Projects\ConsoleApplication29\ConsoleApplication29\bin\Release\ConsoleApplication29.exe)

BaseSize:
0xc

ComponentSize:
0x0

Number of IFaces in
IFaceMap: 0

Slots in VTable:
6

--------------------------------------

MethodDesc
Table

Entry MethodDesc      JIT
Name

79354bec   7913bd48   PreJIT
System.Object.ToString()

793539c0   7913bd50   PreJIT
System.Object.Equals(System.Object)

793539b0   7913bd68   PreJIT
System.Object.GetHashCode()

7934a4c0   7913bd70   PreJIT
System.Object.Finalize()

00de00b0   00913070      JIT
ConsoleApplication29.Foo.Test()

009130d4   00913078     NONE
ConsoleApplication29.Foo..ctor()

We can see here that our
method has now been JIT’ed (as a normal JIT) and that our  method table entry has been updated
to reflect a new location (00de00b0). This is the location that our
unmanaged code has been created by the JIT. We can verify this by looking
at our current stack as we are currently in the method.

!CLRStack

OS Thread Id: 0x8e8
(2280)

ESP
EIP

0012f47c 00de00b0
ConsoleApplication29.Foo.Test()

0012f480 00de0087
ConsoleApplication29.Program.Main(System.String[])

0012f69c 79e88f63
[GCFrame: 0012f69c]

Let’s step out of this
method and back out to our main method.  Our code is again setting up the
same call, by stepping to line 1b and re-checking our pointer we can see
that our code will now call the newly generated unmanaged code directly as
opposed to passing through the thunk.

This will all stay just
like this, unless our code is pitched which will be the next
discussion!

Share this post: Email
it! | bookmark
it! | digg
it! | reddit!|
kick
it!

Published Thursday, July 20, 2006 1:14 AM by Greg

Filed Under: Under The Covers

Comments

johnwood
said:

Another interesting topic well explored.

Any thought as to what happens in a multithreaded application
when it comes to JIT a method. What if two threads hit that
method... obviously one gets there first, but what does it do with
the other call while it JITs? Does it somehow gain a lock on the
method before it calls it, blocking the other call? How else would
it stop the instructions changing from under its feet, and other
race condition issues?

July 20, 2006 2:29 AM

Greg said:

Ah good question, I will have to address that
... Of course I can only look at disassembled code in the production
JIT (and even doing that to make a comment is well umm against the
EULA:))

I can however point you to exactly how this works in
the SSCLI the method being called is in prestub.cpp -http://dotnet.di.unipi.it/Content/sscli/docs/doxygen/clr/clr/prestub_8cpp-source.html,
it is MethodDesc::DoPreStub .. it is quite a long and involved
method but there are some comments there discussing how thread
safety is handled (in fact in the SSCLI this is the method that the
thunk calls). There is also a bit of discussion on this in SSCLI
Essentials from O'Reilly

July 20, 2006 2:46 AM

Sam Gentile said:

Welcome new readers! There are a number of
great posts that caught my attention today. In addition, I...

July 20, 2006 2:23 PM

Alois
said:

Hi Greg,

you did very nicely explain
how the JITer works. I was wondering about this table in your
article
00de00b0   00913070      JIT
ConsoleApplication29.Foo.Test()
009130d4   00913078
NONE ConsoleApplication29.Foo..ctor()

How can it be
that the test method has been jited before the ctor has been run? To
get your program compiled you must instantiate the class before you
can call a function of it. I assume that the ctor must have been
JITed before the Test function or does the JITer know that the
default ctor does nothing? Did I miss here something or did you
"patch" the method table with notepad? ;-).

Yours,

Alois Kraus

Yours,
Alois Kraus

July 20, 2006 6:23 PM

Greg said:

No edit .. try it for yourself

notice
the construction code

00000000  push
esi
00000001  mov
ecx,913080h
00000006  call
FFB21FAC
0000000b  mov
esi,eax

The address in ECX look familiar? :)

I
believe what is happenning here is that the consructor is being
inlined (and as such it is not being JIT'ed). Good catch, I hadn't
noticed that, that is worth a post on its own!

July 20, 2006 10:22 PM

Jason Haley said:

July 26, 2006 10:40 PM

Jason Haley said:

July 26, 2006 11:21 PM

Greg Young [MVP] said:

What is &quot;Code Pitching&quot;,
how does it work and why do we want it?

August 2, 2006 1:30 AM

Anonymous comments are
disabled

Copyright © 2003 - 2006 CodeBetter.Com
Bloggers

CodeBetter.Com
FAQ | Our
Manifesto