The tool I'm developing will eventually be able to perform various types
of analysis including code coverage checks. To track what areas of the
code are being executed, I plan to instrument the IL code by inserting
probes, i.e. calls to a tracking routine in the profiler itself. Right
now, I'm just trying to get the basic functionality working, though.
After studying the various types of calls and modes available, I've
found that the simplest way to do what I want appears to be to define
the tracking routine as an unmanaged void function, taking a DWORD as
argument. The DWORD argument can then be generated by the
instrumentation logic as a unique value identifying the code location to
be tracked.
So, what we have is
STDCALL void ProbeFunc(DWORD D)
{
... do something to record that location D was executed
}
(You'll have to excuse me if the above declaration is slightly off - I
don't use C for this but Delphi)
Anyway,
As far as I've been able to deduct from the documentation, the
instrumentation stub to be inserted in the existing code should then
look like this:
// push location id
ldc.i4 <4-byte location id> // e.g 0x20 0x12345678
// push unmanaged pointer to probe function
ldc.i4 <absolute address of ProbeFunc> // e.g. 0x20 0xAAA12400
// call unmanaged function pointer on stack
calli unmanaged void(int32) // e.g. 0x29 0x11000001
Right now, all I'm doing is I'm trying to modify a small routine in my
test client (C# app). I do this in response to the JITCompilationStarted
event for this particular routine (only) by copying the code to a new
location but expanded and relocated by 15 bytes to accommodate the chunk
of code just shown. The code is patched in between the method header and
the existing code, and the method header is updated to reflect the new
size. Well, I actually convert the header, which happens to be a thin
header, to its fat alternative. I do this in preparation for
instrumenting thin header routines in the future that become too large.
This is not the problem, however, because...
All of this works except for the calli itself. That is to say, if I
replace the calli instruction with a pop, pop, nop, nop, nop sequence
(to get rid of the two dwords just pushed, and padded to 5 bytes), the
code works - except that it doesn't actually call my probe function, of
course. With the calli in place, however, an exception of type
"2345678" - whatever that is - gets raised by mscorjit.dll.
There seems to be (at least) two likely explanations for the failure,
but I'm not sure how to figure out which one is the right one and what
to do about it:
1) calli may not be expecting an absolute address but a RVA, or
something.
2) the SIG for the calli is somehow incorrect.
As for 1), I tried to subtract the image base from the absolute address,
but that made no difference.
As for 2), the token I used was obtained via
IMetaDataEmit::GetTokenFromSig. The IMetaDataEmit instance was obtained
from iCorProfilerInfo for the current module. GetTokenFromSig reports
success. The BLOB passed to GetTokenFromSig looks like this:
0x2 0x1 0x1 0x9, which should translate to:
0: 0x2 = IMAGE_CEE_UNMANAGED_CALLCONV_STDCALL
1: 0x1 = argument count
2: 0x1 = return type (ELEMENT_TYPE_VOID)
3: 0x9 = argument type (ELEMENT_TYPE_U4)
This is correct as far as I understand the specs.
The actual token, however, invariably comes back as 0x11000001. I
suppose it may be correct, but while pool identifier is obviously
correct (0x11 = the stand-alone SIG pool), the sequence number of 1
seems a bit odd to me. This is a Forms app - could this really be the
first stand-alone sig in the assembly?
For meta data scopes opened like this, however, there does not appear to
be a way to save or 'bake' the modified meta data.
--
So, this is where it stands. The calli instruction causes the JIT'er to
crash. I'd appreciate if somebody could tell me how to fix this code, or
let me know if this is entirely the wrong way to go about what I'm
trying to do, and set me straight.
Some sample code - however crude - would probably help too.
TIA
Per Larsen
TurboPower Software
JIT is using 12345678 for all internal exceptions. If you
continue after the exception is thrown, the exception can
materialize either as VerificationException or
InvalidProgramException. This information may give you a
clue on what's more likely wrong.
If it is VerificationException, it is most likely caused
by the fact that the running of the unverifiable code is
not allowed. There may be some problem with security
settings on your machine, or there may be some security
attributes on the module you are trying to instrument.
Try to "ildasm" the module you are trying to instrument,
add the instrumentation to one place, and "ilasm" it
again. Then run it in your test program, but with
instrumentation turned off. Note that the address of your
callback is probably not changing between runs, so you can
simply hardcode the address of your callback. The probe in
il should be something like:
ldc.i4 0x12345678
ldc.i4 0x44358792
calli unmanaged stdcall void(int32)
Does it work this way or not?
If it works, capture the byte represantion of the
instrumented method and go back to your instrumentation.
Is your instrumentation producing the exactly same bytes?
Do the buffers have right sizes, are you updating the size
of the code after instrumentation everywhere it needs to
be updated?
I don't see anything wrong with the fact that your
signature is the very first standalone signature in the
module. You can double check whether this is the case by
running "ildasm foo.exe /metadata=HEX /OUT:a.il". The
standalone signatures show up as:
// Signature #1 (0x11000001)
// -------------------------------------------------------
// CallCnvntn: [STDCALL]
// ReturnType: Void
// 1 Arguments
// Argument #1: I4
// Signature : 02 01 01 08
If none of this helps, any chance you can put together a
small repro of the problem?
-Jan
This posting is provided "AS IS" with no warranties, and
confers no rights.
>.... do something to record that location D was executed
>.
>
> JIT is using 12345678 for all internal exceptions. If you
> continue after the exception is thrown, the exception can
> materialize either as VerificationException or
> InvalidProgramException. This information may give you a
> clue on what's more likely wrong.
Okay - I can't actually do that in my current context since I'm using a
custom shell, and its debugging support isn't that well developed yet.
The shell provides some info to the profiling object (like the list of
routines to monitor) in shared memory buffers, so I can't currently run
the COM object as it stands outside the shell. Looks like I may have to
change that to be able to properly debug this.
> Try to "ildasm" the module you are trying to instrument,
> add the instrumentation to one place, and "ilasm" it
> again. Then run it in your test program, but with
> instrumentation turned off. Note that the address of your
> callback is probably not changing between runs, so you can
> simply hardcode the address of your callback. The probe in
> il should be something like:
>
> ldc.i4 0x12345678
> ldc.i4 0x44358792
> calli unmanaged stdcall void(int32)
>
> Does it work this way or not?
Yes, it works, but only once !?! I have a couple of OutputDebugString
calls in the probe (and nothing else), and the first time I invoke the
instrumented routine, the probe spits out both an entry confirmation,
the passed argument value (0x12345678), and an exit confirmation. I can
do other things in the app that don't involve the instrumented routine,
but when I try to invoke the routine a second time, I get an access
violation at 0x7C0013BC - somewhere outside my code, it appears. To
eliminate the possibility that the probe itself was screwing up the
stack, or something, I reduced it to just an empty shell:
0AAC955Ch push ebp
0AAC955Dh mov ebp, esp
0AAC955Fh pop ebp
0AAC9560h ret 04h
but it still AVs the second time it's called.
My profiler shell can capture the binary code emitted by the JIT'er for
the routine, and it looks like this:
083F9578h push esi
083F9579h mov esi, ecx
083F957Bh mov ecx, 012345678h
083F9580h mov eax, 0AAC955Ch
083F9585h push 01766F0h
083F958Ah call 080A018h
083F958Fh mov [07FB85A8h], esi
083F9595h pop esi
083F9596h ret
Apparently, the JIT'er uses a common stub at 080A018h to generate the
actual call to my routine. I can't say whether there's anything wrong
with this code (because I don't know exactly what is at 080A018h). I can
say, though, that it's not as efficient as I would have liked - but I
guess that's a different discussion.
For comparison, this is the code without the instrumentation probe:
mov [07FB85A8h], ecx
ret
and here's the IL:
.method /*0600000D*/ public hidebysig static
void InstrStub(int32 x) cil managed
// SIG: 00 01 01 08
{
// Method begins at RVA 0x25c4
// Code size 22 (0x16)
.maxstack 8
IL_0000: /* 20 | 78563412 */ ldc.i4 0x12345678
IL_0005: /* 20 | 5C95AC0A */ ldc.i4 0xaac955c
IL_000a: /* 29 | 05000011 */ calli unmanaged
stdcall void(int32) /*11000005*/
IL_000f: /* 02 | */ ldarg.0
IL_0010: /* 80 | (04)00000D */ stsfld int32
JITtest1.Form1/* 02000002 */::y /* 0400000D */
IL_0015: /* 2A | */ ret
} // end of method Form1::InstrStub
All the code does (the uninstrumented version) is it stores an int
argument in a static field.
> If it works, capture the byte represantion of the
> instrumented method and go back to your instrumentation.
> Is your instrumentation producing the exactly same bytes?
> Do the buffers have right sizes, are you updating the size
> of the code after instrumentation everywhere it needs to
> be updated?
I'm not aware of anywhere except the method header that the size should
be updated. Other than that, things appear to check out okay.
> I don't see anything wrong with the fact that your
> signature is the very first standalone signature in the
> module. You can double check whether this is the case by ...
Well, there was actually something wrong: I was using the wrong
ModuleID - that's why I got a signature offset of 1. In fact, there were
several other sigs already present in the module I'm instrumenting.
Unfortunately, fixing that so that the right ModuleID is used didn't
solve the problem. Despite the fact that my instrumented stream is
exactly identical to the one I get by disassembling the manually
constructed probe thunk, the automatic version invariably crashes
immediately whereas the manual one works at least the one time, as just
mentioned. Well, there is one small difference in the code: The
disassembly of the manual assembly shows 5 stand-alone signatures - the
STDCALL one I defined being number 5. When I add that sig dynamically to
the original module, however, it comes back as 0x11000009. I'm not sure
where those extra 4 sigs have come from - I'm only emitting one. I use
the token that I get back from the metadata interface in my code stream.
I've also changed the blob to use int32 rather than uint32 to make it
exactly the same in case that had anything to do with anything.
> If none of this helps, any chance you can put together a
> small repro of the problem?
Yes, it looks like I'm going to have to unless something pops out at you
from my walkthrough here. I imagine you would like it to be in C++. I'll
have to write one from scratch - or rather, based on one of the samples,
probably. Where should I put/send it?
- Per
For the effectivity of the generated code, the code you
are seeing is an unoptimized code. You are most likely
looking at the code when running under debugger - the JIT
always generates unoptimized code under the debugger so
you can get a good line number information. The optimized
code for the simple calli like this should be the actual
call surrounded by dozen or so instructions that erects
the frame for the managed-unmanaged transition. An easiest
way to see the real optimized code is to start the process
outside the debugger and then attach.
If you have a small repro for the problem, you can send it
directly to me via email.
-Jan
>.
>
I ripped out all the shell dependencies and all unnecessary code in
preparation for sending the stuff to you, and somewhere along the way I
managed to fix whatever it was that made the static version fail on all
but the first call. Perhaps I did something bad in one of the profiler
callback - I don't know. The precompiled version now works consistently,
so the basic logic is okay. The dynamically modified version still
fails.
I modified the profiler to dump the binary IL image of the routine to
debug output, and when I compare the static one with the one I create
dynamically, there is only one byte of difference, namely the sequence
number of the meta tags for the calli sig. The tag value is 0x11000006,
which is one above the last signature if I disassemble the module, so
that part checks out okay now too.
Since replacing the calli with pops and nops makes it, um, not crash,
the only thing left as the source of the problem is the sig token. To
verify this, I made yet another custom version along the lines you gave.
This time I didn't add the probe calling code to the test routine but
instead dropped a "calli unmanaged stdcall void(int32)" instruction in
an unrelated buttonclick handler. I did this to make sure the sig was
available in the assembly. After assembling, I disassembled to discover
the offset of this sig. I then changed the instrumentation logic to that
value as a hard-coded token in the calli instruction.
It works! My dynamically modified routine can now call my probe function
repeatedly with no apparent problems.
So, the question is, why isn't the JIT'er able to refer to the sig I've
just emitted? The actual exception I get, btw, turns out to be a type
load exception. This is consistent with an invalid token, which,
apparently, it is according to the JIT'er. I've tried to emit the sig
both in response to the ModuleLoadFinished event and right before I
replace the routine in the JITCompilationStarted event. It makes no
difference. Here's the actual code I use to emit the sig (the ODS bits
are wrappers for OutputDebugString):
ODS('Trying to get a meta data dispenser... ');
if Succeeded(CorProfilerInfo.GetModuleMetaData(moduleID, ofWrite,
IID_IMetaDataEmit, MetaDataEmit)) then begin
ODS('Success!');
ODSLF;
ODS('Trying to define SIG blob for module ');
ODSH(moduleID);
CreateStubToken; // this builds a 4-byte sequence = 2, 1, 1, 9 in
Blob
if Succeeded(MetaDataEmit.GetTokenFromSig(Blob, BlobSize,
ThunkSigToken)) then begin
ODS('Success!');
ODS('token = ');
ODSH(DWord(ThunkSigToken));
end else
ODS('Failure!');
// MetaDataEmit := nil;
end else
ODS('Failure!');
ODSLF;
...
As previously mentioned, I'm not aware of any way to save or 'bake' the
modified metadata, or otherwise notify the .NET core that I've changed
it.
What gives?
- Per
"Jan Kotas [MSFT]" <jko...@online.microsoft.com> wrote in message
news:53ca01c2bce1$a2459d90$d2f82ecf@TK2MSFTNGXA09...
Then I have compiled the following simple C# program, run
profiling_on.bat from the sample and started the program.
using System;
class My {
static void Main() {
Console.WriteLine("a");
}
}
It all worked for me - my callback printed "Magic:
12345678" and the program gracefully exited.
I was doing exactly what you are saying does not work for
you. Can you please try that this works on your machine?
-Jan
This posting is provided "AS IS" with no warranties, and
confers no rights.
-----------------------------------------
void __stdcall MyCallback(int x) {
fprintf(stderr, "Magic: %d\n", x);
}
void Check(HRESULT hr) {
if (FAILED(hr)) {
fprintf(stderr, "FAILED\n");
exit(1);
}
}
HRESULT CProfilerCallback::JITCompilationStarted(UINT
functionId,
BOOL
fIsSafeToBlock)
{
wchar_t wszClass[512];
wchar_t wszMethod[512];
if ( GetMethodNameFromFunctionId( functionId,
wszClass, wszMethod ) )
{
if (wcscmp(L"My", wszClass) == 0 && wcscmp
(L"Main", wszMethod) == 0) {
ClassID classId = 0;
ModuleID moduleId = 0;
mdToken tkMethod = 0;
IMetaDataEmit* pMetaDataEmit = NULL;
mdSignature msig = 0;
IMethodMalloc* pMalloc = NULL;
PVOID pMethod;
Check(m_pICorProfilerInfo->GetFunctionInfo(
functionId, &classId, &moduleId, &tkMethod ));
Check(m_pICorProfilerInfo->GetModuleMetaData(
moduleId, ofRead | ofWrite,
IID_IMetaDataEmit,
(IUnknown** )
&pMetaDataEmit ));
char sigblob[] = {
0x2, //
IMAGE_CEE_UNMANAGED_CALLCONV_STDCALL
0x1, // argument count
0x1, // ret = ELEMENT_TYPE_VOID
0x8 // arg1 = ELEMENT_TYPE_I4
};
Check(pMetaDataEmit->GetTokenFromSig
((PCCOR_SIGNATURE)sigblob, sizeof(sigblob), &msig));
#pragma pack(push, 1)
struct {
char Flags_CodeSize;
char ldci4_1; int val_1;
char ldci4_2; int val_2;
char calli; int sig;
char ret;
char pad1, pad2, pad3;
} ilcode = {
0x42,
0x20, 12345678,
0x20, (int)(size_t)MyCallback,
0x29, msig,
0x2A, 0, 0, 0
};
#pragma pack(pop)
Check(m_pICorProfilerInfo-
>GetILFunctionBodyAllocator( moduleId, &pMalloc ));
pMethod = pMalloc->Alloc(sizeof(ilcode));
memcpy(pMethod, &ilcode, sizeof(ilcode));
Check(m_pICorProfilerInfo->SetILFunctionBody(
moduleId, tkMethod, (LPCBYTE)pMethod));
pMalloc->Release();
pMetaDataEmit->Release();
fprintf(stderr, "Replaced\n");
}
ProfilerPrintf("JITCompilationStarted: %ls::%
ls\n",wszClass,wszMethod);
}
else
{
ProfilerPrintf( "JITCompilationStarted\n" );
}
ChangeNestingLevel( 1 );
return S_OK;
}
Yes, this works for me too.
Hmm - I can't find any obvious difference between this and what I'm
doing, but there obviously has to be some small difference somewhere.
I'll try to expand this sample step by step towards what my failing
version was (meant to be) doing.
Thanks for posting it. I'll let you know if I ever figure out what the
problem is with my version.
- Per
"Jan Kotas [MSFT]" <jko...@online.microsoft.com> wrote in message
news:761f01c2be00$89ca9e30$8af82ecf@TK2MSFTNGXA03...
Have you already succedded in connecting with Delphi to the interface
ICorDebugMannagedCallback (with COM obviously)?
I search desperaterely somebody with this experience.
Thanks,
Eddy POULLET
Brussels
> Have you already succedded in connecting with Delphi to the interface
> ICorDebugMannagedCallback (with COM obviously)?
Yes. The key is to use a pre-D7 version for the TLB import. D7 can't
handle it properly. I can't just give you mine, I'm afraid, since it
doesn't belong to me (but the company I work for).
Note to lurkers: This bit is off-topic for the thread.
- Per
Thanks for the reply. I can connect to the ICorDebugManagedCallback but
I have an AV in mscordbi.dll after the NameChange and before
CreateThread. In Visual C++, it works correctly. I don't believe that
the problem is in the _TLB - I have corrected it manually. Althought, I
have tried D5 and D6 version but the generation is also wrong (except
that the ghostmethods are not added) e.g. the WCHAR type is translated
in Word in LogMessage and LogSwitch.
Probably, I will create a DLL to do the interface. A little pity...
Cheers,
Eddy POULLET
If you post the declarations for the call-backs you're having problems
with I'll compare them to what I have.
I should point out that I haven't yet tested every nook and cranny of
the debugging stuff myself, though.
- Per
"Eddy POULLET" <e.po...@win.be> wrote in message
news:MPG.18967d0cf...@msnews.microsoft.com...
Thanks to try to help me. I can "attach" to the process without problem
in the CreateAppDomain function. The program uses the functions below
correctly (for me :! ).
function LoadModule(const pAppDomain: ICorDebugAppDomain; const
pModule: ICorDebugModule): HResult; stdcall;
function CreateAppDomain(const pProcess: ICorDebugProcess; const
pAppDomain: ICorDebugAppDomain): HResult; stdcall;
function LoadAssembly(const pAppDomain: ICorDebugAppDomain; const
pAssembly: ICorDebugAssembly): HResult; stdcall;
function NameChange(const pAppDomain: ICorDebugAppDomain; const
pThread: ICorDebugThread): HResult; stdcall;
but after NameChange normally the function CreateThread is called (I can
mirror this with the c++ program - CLRDebugging from M Pellegrino).
Hélas ! AV in the DLL.
> I should point out that I haven't yet tested every nook and cranny of
> the debugging stuff myself, though.
For me, with this stopper, it's not possible to go forward (breakpoint,
stepping, evaluate & modify variable, IMetaData,...)if I stay blocked to
this point :).
Thanks,
Eddy POULLET
Brussels
Here excerpt fromthe _TLB.pas I use
ICorDebugManagedCallback = interface(IUnknown)
['{3D6F5F60-7538-11D3-8D5B-00104B35E7EF}']
function Breakpoint(const pAppDomain: ICorDebugAppDomain; const
pThread: ICorDebugThread;
const pBreakpoint: ICorDebugBreakpoint):
HResult; stdcall;
function StepComplete(const pAppDomain: ICorDebugAppDomain; const
pThread: ICorDebugThread;
const pStepper: ICorDebugStepper; reason:
CorDebugStepReason): HResult; stdcall;
function Break(const pAppDomain: ICorDebugAppDomain; const thread:
ICorDebugThread): HResult; stdcall;
function Exception(const pAppDomain: ICorDebugAppDomain; const
pThread: ICorDebugThread;
unhandled: Integer): HResult; stdcall;
function EvalComplete(const pAppDomain: ICorDebugAppDomain; const
pThread: ICorDebugThread;
const pEval: ICorDebugEval): HResult; stdcall;
function EvalException(const pAppDomain: ICorDebugAppDomain; const
pThread: ICorDebugThread;
const pEval: ICorDebugEval): HResult;
stdcall;
function CreateProcess(const pProcess: ICorDebugProcess): HResult;
stdcall;
function ExitProcess(const pProcess: ICorDebugProcess): HResult;
stdcall;
function CreateThread(const pAppDomain: ICorDebugAppDomain; const
thread: ICorDebugThread): HResult; stdcall;
function ExitThread(const pAppDomain: ICorDebugAppDomain; const
thread: ICorDebugThread): HResult; stdcall;
function LoadModule(const pAppDomain: ICorDebugAppDomain; const
pModule: ICorDebugModule): HResult; stdcall;
function UnloadModule(const pAppDomain: ICorDebugAppDomain; const
pModule: ICorDebugModule): HResult; stdcall;
function LoadClass(const pAppDomain: ICorDebugAppDomain; const c:
ICorDebugClass): HResult; stdcall;
function UnloadClass(const pAppDomain: ICorDebugAppDomain; const c:
ICorDebugClass): HResult; stdcall;
function DebuggerError(const pProcess: ICorDebugProcess; errorHR:
HResult; errorCode: LongWord): HResult; stdcall;
function LogMessage(const pAppDomain: ICorDebugAppDomain; const
pThread: ICorDebugThread;
lLevel: Integer; var pLogSwitchName: Word; var
pMessage: Word): HResult; stdcall;
function LogSwitch(const pAppDomain: ICorDebugAppDomain; const
pThread: ICorDebugThread;
lLevel: Integer; ulReason: LongWord; var
pLogSwitchName: Word;
var pParentName: Word): HResult; stdcall;
function CreateAppDomain(const pProcess: ICorDebugProcess; const
pAppDomain: ICorDebugAppDomain): HResult; stdcall;
function ExitAppDomain(const pProcess: ICorDebugProcess; const
pAppDomain: ICorDebugAppDomain): HResult; stdcall;
function LoadAssembly(const pAppDomain: ICorDebugAppDomain; const
pAssembly: ICorDebugAssembly): HResult; stdcall;
function UnloadAssembly(const pAppDomain: ICorDebugAppDomain; const
pAssembly: ICorDebugAssembly): HResult; stdcall;
function ControlCTrap(const pProcess: ICorDebugProcess): HResult;
stdcall;
function NameChange(const pAppDomain: ICorDebugAppDomain; const
pThread: ICorDebugThread): HResult; stdcall;
function UpdateModuleSymbols(const pAppDomain: ICorDebugAppDomain;
const pModule: ICorDebugModule; const
pSymbolStream: IStream): HResult; stdcall;
function EditAndContinueRemap(const pAppDomain: ICorDebugAppDomain;
const pThread: ICorDebugThread;
const pFunction: ICorDebugFunction;
fAccurate: Integer): HResult; stdcall;
function BreakpointSetError(const pAppDomain: ICorDebugAppDomain;
const pThread: ICorDebugThread;
const pBreakpoint: ICorDebugBreakpoint;
dwError: LongWord): HResult; stdcall;
end;
ICorDebug = interface(IUnknown)
['{3D6F5F61-7538-11D3-8D5B-00104B35E7EF}']
function Initialize: HResult; stdcall;
function Terminate: HResult; stdcall;
function SetManagedHandler(const pCallback:
ICorDebugManagedCallback): HResult; stdcall;
function SetUnmanagedHandler(const pCallback:
ICorDebugUnmanagedCallback): HResult; stdcall;
function CreateProcess(lpApplicationName: PWideChar; lpCommandLine:
PWideChar;
lpProcessAttributes: PUserType1;
lpThreadAttributes: PUserType1;
bInheritHandles: BOOL;
dwCreationFlags: LongWord;
lpEnvironment: Pointer;
lpCurrentDirectory: PWideChar;
lpStartupInfo: ULONG_PTR;
lpProcessInformation: ULONG_PTR;
debuggingFlags: CorDebugCreateProcessFlags;
out ppProcess: ICorDebugProcess): HResult;
stdcall;
function DebugActiveProcess(id: LongWord; win32Attach: Integer; out
ppProcess: ICorDebugProcess): HResult; stdcall;
function EnumerateProcesses(out ppProcess: ICorDebugProcessEnum):
HResult; stdcall;
function GetProcess(dwProcessId: LongWord; out ppProcess:
ICorDebugProcess): HResult; stdcall;
function CanLaunchOrAttach(dwProcessId: LongWord;
win32DebuggingEnabled: Integer): HResult; stdcall;
end;
Here the .H version (in C++).
ICorDebugManagedCallback : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE Breakpoint(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *pThread,
/* [in] */ ICorDebugBreakpoint *pBreakpoint) = 0;
virtual HRESULT STDMETHODCALLTYPE StepComplete(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *pThread,
/* [in] */ ICorDebugStepper *pStepper,
/* [in] */ CorDebugStepReason reason) = 0;
virtual HRESULT STDMETHODCALLTYPE Break(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *thread) = 0;
virtual HRESULT STDMETHODCALLTYPE Exception(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *pThread,
/* [in] */ BOOL unhandled) = 0;
virtual HRESULT STDMETHODCALLTYPE EvalComplete(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *pThread,
/* [in] */ ICorDebugEval *pEval) = 0;
virtual HRESULT STDMETHODCALLTYPE EvalException(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *pThread,
/* [in] */ ICorDebugEval *pEval) = 0;
virtual HRESULT STDMETHODCALLTYPE CreateProcess(
/* [in] */ ICorDebugProcess *pProcess) = 0;
virtual HRESULT STDMETHODCALLTYPE ExitProcess(
/* [in] */ ICorDebugProcess *pProcess) = 0;
virtual HRESULT STDMETHODCALLTYPE CreateThread(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *thread) = 0;
virtual HRESULT STDMETHODCALLTYPE ExitThread(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *thread) = 0;
virtual HRESULT STDMETHODCALLTYPE LoadModule(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugModule *pModule) = 0;
virtual HRESULT STDMETHODCALLTYPE UnloadModule(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugModule *pModule) = 0;
virtual HRESULT STDMETHODCALLTYPE LoadClass(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugClass *c) = 0;
virtual HRESULT STDMETHODCALLTYPE UnloadClass(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugClass *c) = 0;
virtual HRESULT STDMETHODCALLTYPE DebuggerError(
/* [in] */ ICorDebugProcess *pProcess,
/* [in] */ HRESULT errorHR,
/* [in] */ DWORD errorCode) = 0;
virtual HRESULT STDMETHODCALLTYPE LogMessage(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *pThread,
/* [in] */ LONG lLevel,
/* [in] */ WCHAR *pLogSwitchName,
/* [in] */ WCHAR *pMessage) = 0;
virtual HRESULT STDMETHODCALLTYPE LogSwitch(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *pThread,
/* [in] */ LONG lLevel,
/* [in] */ ULONG ulReason,
/* [in] */ WCHAR *pLogSwitchName,
/* [in] */ WCHAR *pParentName) = 0;
virtual HRESULT STDMETHODCALLTYPE CreateAppDomain(
/* [in] */ ICorDebugProcess *pProcess,
/* [in] */ ICorDebugAppDomain *pAppDomain) = 0;
virtual HRESULT STDMETHODCALLTYPE ExitAppDomain(
/* [in] */ ICorDebugProcess *pProcess,
/* [in] */ ICorDebugAppDomain *pAppDomain) = 0;
virtual HRESULT STDMETHODCALLTYPE LoadAssembly(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugAssembly *pAssembly) = 0;
virtual HRESULT STDMETHODCALLTYPE UnloadAssembly(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugAssembly *pAssembly) = 0;
virtual HRESULT STDMETHODCALLTYPE ControlCTrap(
/* [in] */ ICorDebugProcess *pProcess) = 0;
virtual HRESULT STDMETHODCALLTYPE NameChange(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *pThread) = 0;
virtual HRESULT STDMETHODCALLTYPE UpdateModuleSymbols(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugModule *pModule,
/* [in] */ IStream *pSymbolStream) = 0;
virtual HRESULT STDMETHODCALLTYPE EditAndContinueRemap(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *pThread,
/* [in] */ ICorDebugFunction *pFunction,
/* [in] */ BOOL fAccurate) = 0;
virtual HRESULT STDMETHODCALLTYPE BreakpointSetError(
/* [in] */ ICorDebugAppDomain *pAppDomain,
/* [in] */ ICorDebugThread *pThread,
/* [in] */ ICorDebugBreakpoint *pBreakpoint,
/* [in] */ DWORD dwError) = 0;
};
MIDL_INTERFACE("3d6f5f61-7538-11d3-8d5b-00104b35e7ef")
ICorDebug : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE Initialize( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Terminate( void) = 0;
virtual HRESULT STDMETHODCALLTYPE SetManagedHandler(
/* [in] */ ICorDebugManagedCallback *pCallback) = 0;
virtual HRESULT STDMETHODCALLTYPE SetUnmanagedHandler(
/* [in] */ ICorDebugUnmanagedCallback *pCallback) = 0;
virtual HRESULT STDMETHODCALLTYPE CreateProcess(
/* [in] */ LPCWSTR lpApplicationName,
/* [in] */ LPWSTR lpCommandLine,
/* [in] */ LPSECURITY_ATTRIBUTES lpProcessAttributes,
/* [in] */ LPSECURITY_ATTRIBUTES lpThreadAttributes,
/* [in] */ BOOL bInheritHandles,
/* [in] */ DWORD dwCreationFlags,
/* [in] */ PVOID lpEnvironment,
/* [in] */ LPCWSTR lpCurrentDirectory,
/* [in] */ LPSTARTUPINFOW lpStartupInfo,
/* [in] */ LPPROCESS_INFORMATION lpProcessInformation,
/* [in] */ CorDebugCreateProcessFlags debuggingFlags,
/* [out] */ ICorDebugProcess **ppProcess) = 0;
virtual HRESULT STDMETHODCALLTYPE DebugActiveProcess(
/* [in] */ DWORD id,
/* [in] */ BOOL win32Attach,
/* [out] */ ICorDebugProcess **ppProcess) = 0;
virtual HRESULT STDMETHODCALLTYPE EnumerateProcesses(
/* [out] */ ICorDebugProcessEnum **ppProcess) = 0;
virtual HRESULT STDMETHODCALLTYPE GetProcess(
/* [in] */ DWORD dwProcessId,
/* [out] */ ICorDebugProcess **ppProcess) = 0;
virtual HRESULT STDMETHODCALLTYPE CanLaunchOrAttach(
/* [in] */ DWORD dwProcessId,
/* [in] */ BOOL win32DebuggingEnabled) = 0;
};
FWIW, the Delphi headers you quote here appear to be exactly identical
to mine.
> Thanks to try to help me. I can "attach" to the process without
problem
> in the CreateAppDomain function. The program uses the functions below
> correctly (for me :! ).
Okay - I'm not actually attaching to a running process but launching a
new one via the CorDebug.CreateProcess function, and I handle both
managed and unmanaged debugging events. Do you do that too?
> but after NameChange normally the function CreateThread is called (I
can
> mirror this with the c++ program - CLRDebugging from M Pellegrino).
> Hélas ! AV in the DLL.
Right. I can past that point, though. Between the first NameChange and
the first CreateThread event, though, I get an unmanaged
LOAD_DLL_DEBUG_EVENT. Do you get that too?
Also, where exactly does the AV occur?
- Per
> > Thanks to try to help me. I can "attach" to the process without
> > problem
> > in the CreateAppDomain function. The program uses the functions below
> > correctly (for me :! ).
> Okay - I'm not actually attaching to a running process but launching a
> new one via the CorDebug.CreateProcess function,
Me too
> and I handle both
> managed and unmanaged debugging events. Do you do that too?
No only managed Code
Event sequence :
// create com server (in C#) to compile the code
// because it is not possible to call managed code from unmanagedcode
itfCompile := CodebuggerCompiler.Create;
iCoreDebuggerProvider := CoCorDebug.Create;
OleCheck(iCoreDebuggerProvider.Initialize);
// create the callback
iManagedCallback := oManagedCallback as ICorDebugManagedCallback;
// Now register the callback with the debug service provider
OleCheck(iCoreDebuggerProvider.SetManagedHandler(iManagedCallback));
// Create a binder used for symbol management
iSymbolBinder := CoCorSymBinder.Create;
// compile the code
itfCompile := CodebuggerCompiler.Create;
b := itfCompile.Compile(codeToDebug, fileToDebug);
WaitForProcessToExit; // reset process event
wStr := ProcessToDebug;
OleCheck(iCoreDebuggerProvider.CreateProcess(PWideChar(wStr), //
lpApplicationName
nil, // lpCommandLine
nil, // lpProcessAttributes
nil, // lpThreadAttributes
True, // bInheritHandles
0, // dwCreationFlags
nil, // lpEnvironment
nil, // lpCurrentDirectory
Cardinal(@processStartupInfo), // lpStartupInfo
Cardinal(@processInfo), // lpProcessInformation,
DEBUG_NO_SPECIAL_OPTIONS, // debuggingFlags
iProcessBeingDebugged // ppProcess
));
// Clean up resources handed back to us
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
function TManagedCallback.CreateProcess( const pProcess:
ICorDebugProcess): HResult;
begin
...
//create Process event
CreateEvent(nil, // lpEventAttributes
TRUE, // bManualReset
FALSE, // bInitialState
nil // lpName
);
DebuggerServices.ContinueFromCallback(pProcess);
...
end;
function TManagedCallback.CreateAppDomain(const pProcess:
ICorDebugProcess;
const pAppDomain: ICorDebugAppDomain): HResult;
begin
...
hr := pAppDomain.Attach;
DebuggerServices.ContinueFromCallback(pProcess) ;
// ContinueFromCallback == pProcess.Continue( FALSE );
/ Note that we pass in FALSE because we're continuing from a managed
callback.
...
end;
function TManagedCallback.LoadAssembly(
const pAppDomain: ICorDebugAppDomain;
const pAssembly: ICorDebugAssembly): HResult;
function TManagedCallback.LoadModule(const pAppDomain:
ICorDebugAppDomain;
const pModule: ICorDebugModule): HResult;
function TManagedCallback.NameChange(const pAppDomain:
ICorDebugAppDomain;
const pThread: ICorDebugThread): HResult;
Now AVF Debugger Fault Notification --------------------------->
Project DDebugger.exe faulted with message: 'access violation at
0x7911eaed: write of address 0x000000a0'.
> Right. I can past that point, though. Between the first NameChange and
> the first CreateThread event, though, I get an unmanaged
> LOAD_DLL_DEBUG_EVENT. Do you get that too?
No, I will debug only managed code. I use no LOAD_DLL_DEBUG_EVENT event.
> Also, where exactly does the AV occur?
I have tried to trace the error location in the Visual C++ debugger with
the corlibdbi.pdb loaded but it is in a ThreadProc routine.
In fact, the program is a Delphi copy of CLRDebugging from M Pellegrino
http://msdn.microsoft.com/msdnmag/issues/02/11/clrdebugging/default.aspx
(but until now a not working copy...) Only reference I have found until
now....It's not to write a profiler, only to debug program in Net
languages.
I believe that some details in Delphi is not working but where ???
Thanks,
Eddy POULLET
FWIW, I don't think this is a Delphi specific issue, but I'd need to be
able to debug all your code to find out for sure.
- Per
"Eddy POULLET" <e.po...@win.be> wrote in message
news:MPG.18976e4a9...@msnews.microsoft.com...
> FWIW, I don't think this is a Delphi specific issue, but I'd need to be
> able to debug all your code to find out for sure.
>
> - Per
Yes, it's probably in my code but the code is very simple.
I can send it to your e_mail if you are curious.
The ICorPublish works apparently.
Eddy
Per
Are you agreed that I send you the (very simple) source project itself
or not ?
Thanks
Eddy POULLET
Brussels
PS : Sorry if I do a wrongly interpretation of your message.
Yes, go ahead and send it to me, and I'll take a look when I can find a
moment.
- Per
"Eddy POULLET" <e.po...@win.be> wrote in message
news:MPG.1897bed79...@msnews.microsoft.com...
Eddy