2011-02-14 11 views
6

He estado luchando por obtener una pila de llamadas en un ejecutable de Windows. He intentado varias formas diferentes de obtener la pila de llamadas. Los siguientes son algunos ejemplos. Tenga en cuenta que los modifiqué ligeramente y eliminé el manejo de errores para que sean fáciles de entender, por lo que es posible que no compilen como están. Creo que entiendes el punto.Problemas al obtener una pila de llamadas en una compilación de lanzamiento

La forma más sencilla:

const int max_entries = 10; 
void *entries[max_entries]; 
return CaptureStackBackTrace(0, max_entries, entries, 0); 

La manera de bajo nivel:

const int max_entries = 10; 
void *entries[max_entries]; 

void **frame = 0; 
__asm { mov frame, ebp } 
unsigned int i = 0; 
while(frame && i < max_entries) { 
    entries[i++] = frame[1]; 
    frame = (void **)frame[0]; 
} 

La manera compatible:

void *entries[max_entries]; 
CONTEXT context; 
RtlCaptureContext(&context); 
STACKFRAME64 stack_frame; 
ZeroMemory(&stack_frame, sizeof(STACKFRAME64)); 
stack_frame.AddrPC.Offset = context.Eip; 
stack_frame.AddrPC.Mode  = AddrModeFlat; 
stack_frame.AddrFrame.Offset = context.Ebp; 
stack_frame.AddrFrame.Mode = AddrModeFlat; 
stack_frame.AddrStack.Offset = context.Esp; 
stack_frame.AddrStack.Mode = AddrModeFlat; 

unsigned int num_frames = 0; 
while (true) { 
    if (!StackWalk64(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), 
     GetCurrentThread(), &stack_frame, &context, NULL, 
     SymFunctionTableAccess64, SymGetModuleBase64, NULL)) 
     break; 

    if (stack_frame.AddrPC.Offset == 0) 
     break; 

    entries[num_frames++] = reinterpret_cast<void *>(stack_frame.AddrPC.Offset); 
} 

Mi problema es que trabajan en una acumulación sin optimizar, pero no con optimización completa en. Lo que sucede es que obtengo una entrada rota y luego salen de sus bucles. En la depuración obtengo la pila de llamadas completa y cuando más tarde busco los símbolos, todo es correcto.

No entiendo cómo puede ser difícil hacer que esto funcione en todas las compilaciones cuando el depurador lo hace todo el tiempo. Puedo decir específicamente que los punteros de cuadro no se omiten en la generación de código. Primero compilo para la depuración y luego solo cambio la optimización de none a full optimization y recompé para reproducir la falla de la pila de llamadas.

Cualquier sugerencia para una solución será muy apreciada.

/Jonas

+0

No está compilando con la optimización del puntero de marco activada, ¿verdad? –

+0

Los punteros de cuadro no se omiten en la generación de código, si eso es lo que quiere decir. –

Respuesta

2

Obtuve este funcionamiento utilizando la "forma compatible" ahora. Yo uso el siguiente código para inicializar el contexto:

#define GET_CURRENT_CONTEXT(c, contextFlags) \ 
    do { \ 
     memset(&c, 0, sizeof(CONTEXT)); \ 
     c.ContextFlags = contextFlags; \ 
     __asm call x \ 
     __asm x: pop eax \ 
     __asm mov c.Eip, eax \ 
     __asm mov c.Ebp, ebp \ 
     __asm mov c.Esp, esp \ 
    } while(0); 

CONTEXT context; 
GET_CURRENT_CONTEXT(context, CONTEXT_FULL); 

y luego continuar a buscar a la pila usando StackWalk64 como antes.

void *entries[max_entries]; 
STACKFRAME64 stack_frame; 
ZeroMemory(&stack_frame, sizeof(STACKFRAME64)); 
stack_frame.AddrPC.Offset = context.Eip; 
stack_frame.AddrPC.Mode  = AddrModeFlat; 
stack_frame.AddrFrame.Offset = context.Ebp; 
stack_frame.AddrFrame.Mode = AddrModeFlat; 
stack_frame.AddrStack.Offset = context.Esp; 
stack_frame.AddrStack.Mode = AddrModeFlat; 

unsigned int num_frames = 0; 
while (true) { 
    if (!StackWalk64(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), 
     GetCurrentThread(), &stack_frame, &context, NULL, 
     SymFunctionTableAccess64, SymGetModuleBase64, NULL)) 
     break; 

    if (stack_frame.AddrPC.Offset == 0) 
     break; 

    entries[num_frames++] = reinterpret_cast<void *>(stack_frame.AddrPC.Offset); 
} 

Noté que se me olvidó borrar la estructura CONTEXTO antes de enviarlo a RtlCaptureContext por lo que trataron de hacerlo de esta manera (porque yo preferiría usar la función RtlCaptureContext).

CONTEXT context; 
memset(&context, 0, sizeof(CONTEXT)); 
context.ContextFlags = CONTEXT_FULL; 
RtlCaptureContext(&context); 

Ahora RtlCaptureContext se estrella, así que me fui de nuevo a usar la macro GET_CURRENT_CONTEXT.

+0

Hola Jonas, ¿es posible mapear las entradas recuperadas [] a los nombres de las funciones reales en compilaciones optimizadas? Obtengo un resultado incorrecto ¿Tienes algún ejemplo? –

0

Esta no es una respuesta, pero sólo un informe de "yo también" para aclarar las versiones específicas:

que estoy viendo choques esporádicos dentro RtlCaptureContext pero sólo en 32 bits versiones de depuración y no en la de 32 bits de lanzamiento construcciones, y no en compilaciones Debug o Release 64-bit. Estoy usando VS2008 SP1 con dbghelp.dll fileVersion 6.12.2.633 de Debugging Tools para Windows, descargado el 25 de abril de 2011, y que dbghelp.dll copió en el mismo directorio que mi EXE antes de la invocación.

Esto es con la compilación utilizando la misma versión del compilador VS1000 SP1 en la misma máquina con Windows XP de 64 bits SP2 (que compila aplicaciones nativas de 32 y 64 bits, ningún código administrado .NET está en absoluto en el mezcla).

La clave de arriba es naturaleza esporádica. No he determinado las condiciones por las cuales falla.

Cuestiones relacionadas