3

I am using /Gh and /GH compiler option of visual studio to profile a bunch of code. Two methods used are _penter and _pexit which are called when a function is entered or exited in the code being profiled. Since I need specific functions being profiled/debugged, I use an already defined array FuncTable which contains the addresses of the functions I need to instrument with their names as string. So when a function is entered, the pStack[0], which basically contains the register contents, contains the address to current line of the code being executed. Similarly when a function is exited, pStack[0] contains the address of last line of the code.

THE ISSUE: When a function is entered (_penter is called), I get the address of first line of the function in pStack[0] and hence I can get the function's address by subtracting a constant(-5) and save that to my list to be retrieved later in the _pexit function. But since in _pexit I am getting address to the last line of the function, I need to find the size of the function in order to subtract that size from the address in pStack[0] to reach the starting address of the function and then compare that address to the ones saved in my list. Pasted below is the code.

void _stdcall EnterFunc0(unsigned * pStack)
{
    void      * pCaller;
    pCaller = (void *)(pStack[0] - 5); // pStack[0] is first line, -5 for function address
    Signature * funct = FuncTable;

    while (funct->function)
    {
        const BYTE * func = (const BYTE *)funct->function;
        if ((func == (const BYTE *)pCaller) || ((*func == 0xE9) && ((func + *(DWORD *)(func + 1) + 5) == (const BYTE *)pCaller)))
        {
            Stack_Push(funct->name, funct->returnType, true, pCaller);          
        }
        funct++;
    }

}

extern "C" __declspec(naked) void __cdecl _penter()
{
    _asm
    {
        pushad              // save all general purpose registers
        mov    eax, esp     // current stack pointer
        add    eax, 32      // stack pointer before pushad
        push   eax          // push pointer to return address as parameter to EnterFunc0

        call   EnterFunc0

        popad               // restore general purpose registers
        ret                 // start executing original function
    }
}
void _stdcall ExitFunc0(unsigned * pStack)
{
    if (startRecording) 
    {
        StackEntry * start = top;
        while (start != NULL)
        {
            //**HERE I NEED TO COMPARE THE ADDRESS OF THE FUNCTION WITH THE ONE ALREADY IN MY STACK**
                            if ((void *)(pStack[0] - sizeOfTheFunction) == start->Address)
            {
                OutputDebugString("Function Found\n");
            }
            start = start->next;
        }
    }

}
extern "C" __declspec(naked) void __cdecl _pexit()
{
    _asm
    {
        pushad              // save all general purpose registers
        mov    eax, esp     // current stack pointer
        add    eax, 32      // stack pointer before pushad
        push   eax          // push pointer to return address as parameter to EnterFunc0

        call   ExitFunc0

        popad               // restore general purpose registers
        ret                 // start executing original function
    }
}
Taha Rehman Siddiqui
  • 2,441
  • 5
  • 32
  • 58

4 Answers4

2

You already know the address in your _pexit() function, it was handed to you in the _penter() function. All you have to do is support nested function calls. An std::stack<> is good for that. Use push() to save the address in _penter, retrieve it with top() in your _pexit function and call pop().

No need to know the function body size at all anymore.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
0

Since the compiler ensures that _penter and _pexit are called at the start and end of every function, you can be certain that upon a call to _pexit, the function pointer on top of the stack created by _penter always points to the current function. There is no need to search for it.

(This should be true unless you call one of the functions manually, which you shouldn't do, or have a multi-threaded program. In the latter case you should create a private stack per thread. Of course, you'll also have to add a Stack_Pop invocation to _pexit, but I assume that you planned on doing that anyway.)

Phillip
  • 13,448
  • 29
  • 41
  • The problem with this approach is that not every function is traced, and since I am only tracing specific functions in _penter, in _pexit I need to know if it is indeed the function that I traced in _penter. Unfortunately, I cant create a private stack per thread, the library is supposed to work be linked with applications that can be single or multi threaded. And I dont think I have access to the threads of the process library is linked to. – Taha Rehman Siddiqui Jul 12 '16 at 18:57
  • @TahaRehmanSiddiqui That does not prohibit to use a (second) stack to trace the stack (pun intended). Also, I don't see why you [couldn't use TLS](https://msdn.microsoft.com/en-us/library/windows/desktop/ms686997(v=vs.85).aspx). – Phillip Jul 13 '16 at 06:01
0

It turns out I was looking at the problem the wrong way. I solved the problem by getting symbol information using SymFromAddress method, from dbghelp.lib. Once I got the name of the method, I was able to compare it with the information that I stored in _penter.

SYMBOL_INFO  * mysymbol;
HANDLE         process;
char           temp[MAX_TEMP_LENGTH] = "                                                                       ";

process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);

mysymbol = (SYMBOL_INFO *)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
mysymbol->MaxNameLen = 255;
mysymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
SymFromAddr(process, (DWORD64)((void *)pStack[0]), 0, mysymbol);
OutputDebugString(mysymbol->Name);
OutputDebugString("\n");
Taha Rehman Siddiqui
  • 2,441
  • 5
  • 32
  • 58
  • Be aware that the documentation [explicitly states that `SymFromAddr` is not thread-safe](https://msdn.microsoft.com/en-us/library/windows/desktop/ms681323(v=vs.85).aspx). – Phillip Jul 13 '16 at 06:02
  • Well, I just tested it for a piece of code (simulator). It is working with multithreaded code (may be because I am using it in a library that is linked to a multithreaded process). However, it is slowing the execution to an unbearable extent. So I am still looking for a way. @Phillip, for some reason the stack, if I use one, is going empty when it is supposed to have some entries. Just to confirm, with stack you mean I push in _penter and pop in _pexit? – Taha Rehman Siddiqui Jul 13 '16 at 18:41
  • Exactly. As long as you do this unconditionally, never call any of the two functions manually, and make sure that you don't have threading issues, this shouldn't happen. – Phillip Jul 14 '16 at 09:18
  • 1
    You could cache the result of `SymFromAddr` and only look up the symbol when you get a new address. This should avoid most of your performance hit. You should also use a lock around calls to `SymFromAddr` since that isn't thread safe (just because it seems to work with multiple threads doesn't prove that it is safe to do so - you'd need two threads actively in the function to maybe see a problem). – 1201ProgramAlarm Jul 15 '16 at 01:01
0

Configuration Properties > C/C++ > Command Line

Add compiler option to Additional Options box

Like so example settings

Add flag /Gh for _penter hook
Add flag /GH for _pexit hook

Code I use for tracing / logging

#include <intrin.h>

extern "C"  void __declspec(naked) __cdecl _penter(void) {
    __asm {
        push ebp;               // standard prolog
        mov ebp, esp;
        sub esp, __LOCAL_SIZE
        pushad;                 // save registers
    }
    // _ReturnAddress always returns the address directly after the call, but that is not the start of the function!
    PBYTE addr;
    addr = (PBYTE)_ReturnAddress() - 5;

    SYMBOL_INFO* mysymbol;
    HANDLE       process;
    process = GetCurrentProcess();
    SymInitialize(process, NULL, TRUE);
    mysymbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
    mysymbol->MaxNameLen = 255;
    mysymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    SymFromAddr(process, (DWORD64)((void*)addr), 0, mysymbol);
    myprintf("Entered Function: %s [0x%X]\n", mysymbol->Name, addr);

    _asm {
        popad;              // restore regs
        mov esp, ebp;       // standard epilog
        pop ebp;
        ret;
    }
}

extern "C"  void __declspec(naked) __cdecl _pexit(void) {
    __asm {
        push ebp;               // standard prolog
        mov ebp, esp;
        sub esp, __LOCAL_SIZE
        pushad;                 // save registers
    }
    // _ReturnAddress always returns the address directly after the call, but that is not the start of the function!
    PBYTE addr;
    addr = (PBYTE)_ReturnAddress() - 5;

    SYMBOL_INFO* mysymbol;
    HANDLE       process;
    process = GetCurrentProcess();
    SymInitialize(process, NULL, TRUE);
    mysymbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
    mysymbol->MaxNameLen = 255;
    mysymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    SymFromAddr(process, (DWORD64)((void*)addr), 0, mysymbol);
    myprintf("Exit Function: %s [0x%X]\n", mysymbol->Name, addr);

    _asm {
        popad;              // restore regs
        mov esp, ebp;       // standard epilog
        pop ebp;
        ret;
    }
}
SSpoke
  • 5,656
  • 10
  • 72
  • 124