Now I'm sure: There's a serious problem inside the CLR when calling
SetFunctionReJIT() on anything but the current method. Add (something like)
the following code as JITCompilationStarted callback in the profiler:
UINT g_testfunction=0;
STDMETHODIMP MyProfilerCallback::JITCompilationStarted(UINT functionId, BOOL
fIsSafeToBlock)
{
wchar_t wszClass[512];
wchar_t wszMethod[512];
wchar_t wszModule[512];
//Gona change this: The match will be on the metadata token,
//not on the name. It's easier and safer and faster.
//This is test code
if (g_testfunction!=0)
{
m_pProfilerInfo->SetFunctionReJIT(g_testfunction);
wprintf(L"Requesting rejit for %d.\n",g_testfunction);
g_testfunction=0;
}
wprintf(L" JIT: ");
if (GetMethodNameFromFunctionId(functionId, wszModule, wszClass, wszMethod))
{
wprintf(L"%ls!%ls::%ls as %d\n",wszModule,wszClass,wszMethod,functionId);
if (wcscmp(wszMethod,L"Test2")==0) //or some other method that is called at
least
//twice in the
test app.
g_testfunction=functionId;
}
else
{
wprintf(L"Couldn't resolve method name\n");
}
After this, use the profiler with a test application that calls the given
method at least twice, with some other call in between.
The clr then deadlocks and via the debugger you can see that
_CallDescrWorker has a problem with two stubs jumping to the beginning of
each other infinitelly.
With the Rotor-Code, I could get an Idea of what's wrong, but I'm not sure,
since the prestubs seem to be different in the .NET CLR implementation. I
suspect that there is a bug inside BOOL IJitManager::ForceReJIT(MethodDesc
*pFunction) and the provided code doesn't correctly set the method to
unjited.
If needed, I'll supply more information about this problem.
Patrick
David Gutierrez[MSFT] - 23 Sep 2003 23:41 GMT
I've forwarded your mail on to some JIT experts here at Microsoft so they
can tak e look.
Thanks,
David
Patrick Grawehr - 24 Sep 2003 12:56 GMT
Hi!
Good news! I've found the bug.
[Remark: I'm presenting code from the rotor sources, but the bug applies to
the "default" clr too]
In codeman.cpp IJITManager::ForceReJIT, line 200.
const BYTE *pAddrOfCode = pFunction->GetAddrofCode();
if (UpdateableMethodStubManager::CheckIsStub(pAddrOfCode, NULL))
{
// Restore the RVA for the JIT.
ULONG dwRVA;
pFunction->GetMDImport()->GetMethodImplProps(pFunction->GetMemberDef(),
&dwRVA, NULL);
pFunction->SetRVA(dwRVA);
// reset any flags relevant to the old code
pFunction->ClearFlagsOnUpdate();
// make our stub just jump to the prestub to force rejit
UpdateableMethodStubManager::UpdateStub( //<<--- HERE.
(Stub*)pAddrOfCode, pFunction->GetPreStubAddr());
}
This call to UpdateMethodStubManager::UpdateStub causes an infinite loop,
because pAddrOfCode contains the same as pFunction->GetPreStubAddr().
What actually needs to be done is to get pFunction->GetPreStubAddr() and put
there a *call* to the *global* prestub.
The global prestub address is stored in the variable g_preStub.
So we need to patch in 0xe8 and the four-byte relative address of g_preStub.
Patrick
> I've forwarded your mail on to some JIT experts here at Microsoft so they
> can tak e look.
> Thanks,
> David
Patrick Grawehr - 24 Sep 2003 14:11 GMT
> I've forwarded your mail on to some JIT experts here at Microsoft so
> they can tak e look.
> Thanks,
> David
Hi!
Good news! I've found the bug.
[Remark: I'm presenting code from the rotor sources, but the bug applies
to the "default" clr too]
In codeman.cpp IJITManager::ForceReJIT, line 200.
const BYTE *pAddrOfCode = pFunction->GetAddrofCode();
if (UpdateableMethodStubManager::CheckIsStu(pAddrOfCode, NULL))
{
// Restore the RVA for the JIT.
ULONG dwRVA;
pFunction->GetMDImport()->GetMethodImplProps(pFunction->
GetMemberDef(),
&dwRVA, NULL);
pFunction->SetRVA(dwRVA);
// reset any flags relevant to the old code
pFunction->ClearFlagsOnUpdate();
// make our stub just jump to the prestub to force rejit
UpdateableMethodStubManager::UpdateStub( //<<--- HERE.
(Stub*)pAddrOfCode, pFunction->GetPreStubAddr());
}
This call to UpdateMethodStubManager::UpdateStub causes an infinite loop,
because pAddrOfCode contains the same as pFunction->GetPreStubAddr().
What actually needs to be done is to get pFunction->GetPreStubAddr() and
put
there a *call* [not a jmp] to the *global* prestub, not the one of the
function.
The global prestub address is stored in the variable g_preStub.
So we need to patch in 0xe8 and the four-byte relative address of
g_preStub.
Greetings.
Patrick
David Gutierrez[MSFT] - 02 Oct 2003 22:53 GMT
This will definitely help speed things along. Thanks!
David