Instrumenting virtual function calls

I’m attempting to instrument virtual function calls in my code. After each virtual call I’m calling my own registerMethod function, with an integer marking the location of the call and a pointer to the function that was called.

However, and this is where I get confused, the function pointer doesn’t match any of the functions in my module. I’d hoped to call ExecutionEngine::getGlobalValueAtAddress to get a Function* for the virtual function, but ExecutionEngine::getGlobalValueAtAddress returns null.

If I look up the virtual function that is getting called (with ExeuctionEngine::getPointerToFunction) it doesn’t match the arguments being passed to my instrumentation. What’s a little strange is that the pointers are somewhat close: getPointerToFunction returns 0x47829a0, but my instrumentation gets 0x477fc10.

What am I missing?

Lines starting with “!” are the instrumented lines.

define double @Return() {
entry:
%this = load %“struct.Q::BinaryOperation<bool,bool,bool,Q::AddOperator>”** @q_constant2 ; <%“struct.Q::BinaryOperation<bool,bool,bool,Q::AddOperator>”> [#uses=2]
%tmp9.i = getelementptr %“struct.Q::BinaryOperation<bool,bool,bool,Q::AddOperator>”
%this, i32 0, i32 2, i32 0 ; <%“struct.Q::Function”> [#uses=1]
%tmp10.i = load %“struct.Q::Function”
%tmp9.i, align 4 ; <%“struct.Q::Function”> [#uses=2]
%tmp17.i = getelementptr %“struct.Q::BinaryOperation<bool,bool,bool,Q::AddOperator>”
%this, i32 0, i32 1, i32 0 ; <%“struct.Q::Function”> [#uses=1]
%tmp18.i = load %“struct.Q::Function”
%tmp17.i, align 4 ; <%“struct.Q::Function”> [#uses=2]
%tmp33.i = getelementptr %“struct.Q::Function”
%tmp18.i, i32 0, i32 0, i32 0, i32 0, i32 0 ; <i32 (…)> [#uses=1]
%tmp34.i = load i32 (…)
%tmp33.i, align 4 ; <i32 (…)> [#uses=1]
%tmp35.i = getelementptr i32 (…)
%tmp34.i, i32 9 ; <i32 (…)> [#uses=1]
%tmp36.i = load i32 (…)
%tmp35.i, align 4 ; <i32 (…)> [#uses=1]
%tmp3637.i = bitcast i32 (…)
%tmp36.i to double (%“struct.Q::Function”) ; <double (%“struct.Q::Function”)> [#uses=2]
%tmp39.i = call double %tmp3637.i( %“struct.Q::Function”* %tmp18.i ) ; [#uses=1]
! bitcast double (%“struct.Q::Function”) %tmp3637.i to i8* ; <i8*>:0 [#uses=1]
! call void @registerMethod( i64 73846672, i8* %0 )
%tmp49.i = getelementptr %“struct.Q::Function”* %tmp10.i, i32 0, i32 0, i32 0, i32 0, i32 0 ; <i32 (…)> [#uses=1]
%tmp50.i = load i32 (…)
%tmp49.i, align 4 ; <i32 (…)> [#uses=1]
%tmp51.i = getelementptr i32 (…)
%tmp50.i, i32 9 ; <i32 (…)> [#uses=1]
%tmp52.i = load i32 (…)
%tmp51.i, align 4 ; <i32 (…)> [#uses=1]
%tmp5253.i = bitcast i32 (…)
%tmp52.i to i32 (%“struct.Q::Function”) ; <i32 (%“struct.Q::Function”)> [#uses=2]
%tmp55.i = call i32 %tmp5253.i( %“struct.Q::Function”* %tmp10.i ) ; [#uses=1]
! bitcast i32 (%“struct.Q::Function”) %tmp5253.i to i8* ; <i8*>:1 [#uses=1]
! call void @registerMethod( i64 73865808, i8* %1 )
%tmp5859.i = sitofp i32 %tmp55.i to double ; [#uses=1]
%tmp61.i = add double %tmp39.i, %tmp5859.i ; [#uses=1]
ret double %tmp61.i
}

Robert

This should basically work. You'll have to walk through the various code that populates the maps. It could be that the start of the function is actually a constant pool or jump table or something, not the first instruction of the function.

-Chris

After hacking away at it for a bit, it looks like the mystery function is actually a stub function. The function pointer is coming from a vtable, which gets filled in with pointers to stub functions.

Is there any way to do the round trip for a stub function? Two possible solutions come to mind:
  1) Modify getGlobalValueAtAddress to work for pointers to stub functions
  2) Add a getStubAtAddress

Any other suggestions?

Robert

After hacking away at it for a bit, it looks like the mystery function is actually a stub function.

You know, I had this lengthy email written to cover all the details and I decided not to send it as I wasn't sure if that was what you were hitting a stub and I didn't want to confuse the issue if it wasn't due to stubs...

:frowning:

Oh well...

Is there any way to do the round trip for a stub function?

Not that I know of. I think you're down to template checking (check the whole thing for strict equality please).

Two possible solutions come to mind:
  1) Modify getGlobalValueAtAddress to work for pointers to stub functions

I think I like this better. I think others might want to, need to do this same thing and I suspect they don't want to learn and they'll appreciate it just working.

Do you care about JIT laziness? You could just call getPointerToFunction on every function in the module before your code starts up.

-Chris

I thought about it --- that's actually how I figured out that I was getting the stub functions --- but the laziness is nice for my application. The bitcode module I'm working with is about 3.8 megabytes. I've been assuming it would take a while to compile each function, a chunk of which I probably won't need (but won't be able to tell until I get my input.)

Robert

Here is a patch that will do just that.

The patch brings up three questions:
  Is it ok for ExecutionEngine::getGlobalValueAtAddress to be virtual?
  Did I handle the locking properly in my changes to JITEmitter.
  Are there other places that have to be changed for this to work right?

getGlobalValueForStubs.txt (2.77 KB)

Here is a patch that will do just that.

The patch brings up three questions:
  Is it ok for ExecutionEngine::getGlobalValueAtAddress to be virtual?
  Did I handle the locking properly in my changes to JITEmitter.
  Are there other places that have to be changed for this to work right?

getGlobalValueForStubs.txt (2.77 KB)