Pregunta

Sé que no existe una función C estándar para hacer esto.Me preguntaba ¿cuáles son las técnicas para esto en Windows y *nix?(Windows XP es mi sistema operativo más importante para hacer esto en este momento).

¿Fue útil?

Solución

Hemos utilizado esto para nuestros proyectos:

https://www.codeproject.com/kb/threads/stackwalker.aspx

El código es un poco complicado en mi humilde opinión, pero funciona bien.Sólo Windows.

Otros consejos

glibc proporciona la función backtrace().

http://www.gnu.org/software/libc/manual/html_node/Backtraces.html

Hay backtrace() y backtrace_symbols():

Desde la página de manual:

     #include <execinfo.h>
     #include <stdio.h>
     ...
     void* callstack[128];
     int i, frames = backtrace(callstack, 128);
     char** strs = backtrace_symbols(callstack, frames);
     for (i = 0; i < frames; ++i) {
         printf("%s\n", strs[i]);
     }
     free(strs);
     ...

Una forma de usar esto de una manera más conveniente/OOP es guardar el resultado de backtrace_symbols() en un constructor de clase de excepción.Por lo tanto, cada vez que lanza ese tipo de excepción, tiene el seguimiento de la pila.Luego, simplemente proporcione una función para imprimirlo.Por ejemplo:


class MyException : public std::exception {

    char ** strs;
    MyException( const std::string & message ) {
         int i, frames = backtrace(callstack, 128);
         strs = backtrace_symbols(callstack, frames);
    }

    void printStackTrace() {
        for (i = 0; i 

...


try {
   throw MyException("Oops!");
} catch ( MyException e ) {
    e.printStackTrace();
}

¡Ta da!

Nota:habilitar indicadores de optimización puede hacer que el seguimiento de la pila resultante sea inexacto.Lo ideal sería utilizar esta capacidad con los indicadores de depuración activados y los indicadores de optimización desactivados.

Para Windows, consulte la API StackWalk64() (también en Windows de 32 bits).Para UNIX, debe utilizar la forma nativa del sistema operativo para hacerlo, o recurrir a backtrace() de glibc, si está disponible.

Sin embargo, tenga en cuenta que tomar un Stacktrace en código nativo rara vez es una buena idea, no porque no sea posible, sino porque normalmente intenta lograr algo incorrecto.

La mayoría de las veces la gente intenta obtener un seguimiento de pila en, por ejemplo, una circunstancia excepcional, como cuando se detecta una excepción, falla una afirmación o, lo peor y más equivocado de todos, cuando se obtiene una "excepción" fatal o una señal como una violación de segmentación.

Teniendo en cuenta el último problema, la mayoría de las API requerirán que asigne memoria explícitamente o pueden hacerlo internamente.Hacerlo en el estado frágil en el que se encuentra actualmente su programa puede empeorar aún más las cosas.Por ejemplo, el informe de fallo (o coredump) no reflejará la causa real del problema, sino su intento fallido de solucionarlo).

Supongo que estás intentando lograr ese manejo de errores fatales, ya que la mayoría de la gente parece intentarlo cuando se trata de obtener un seguimiento de pila.Si es así, confiaría en el depurador (durante el desarrollo) y dejaría que el proceso se realice en producción (o minivolcado en Windows).Junto con una gestión adecuada de los símbolos, no debería tener problemas para descubrir la instrucción causante post-mortem.

No existe una forma independiente de la plataforma para hacerlo.

Lo más parecido que puedes hacer es ejecutar el código sin optimizaciones.De esa manera, puede conectarse al proceso (usando el depurador de Visual C++ o GDB) y obtener un seguimiento de la pila utilizable.

Para ventanas, CaptureStackBackTrace() También es una opción, que requiere menos código de preparación por parte del usuario que StackWalk64() hace.(Además, para un escenario similar que tuve, CaptureStackBackTrace() terminó funcionando mejor (más confiablemente) que StackWalk64().)

Deberías estar usando el biblioteca para relajarse.

unw_cursor_t cursor; unw_context_t uc;
unw_word_t ip, sp;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
unsigned long a[100];
int ctr = 0;

while (unw_step(&cursor) > 0) {
  unw_get_reg(&cursor, UNW_REG_IP, &ip);
  unw_get_reg(&cursor, UNW_REG_SP, &sp);
  if (ctr >= 10) break;
  a[ctr++] = ip;
}

Su enfoque también funcionaría bien a menos que realice una llamada desde una biblioteca compartida.

Puedes usar el addr2line comando en Linux para obtener la función fuente/número de línea de la PC correspondiente.

Solaris tiene la pstack comando, que también se copió en Linux.

¿Puedo señalarle mi artículo?Son sólo unas pocas líneas de código.

Depuración post mortem

Aunque actualmente tengo problemas con el Implementación x64 de este.

Durante los últimos años he estado usando libbacktrace de Ian Lance Taylor.Es mucho más limpio que las funciones de la biblioteca GNU C que requieren exportar todos los símbolos.Proporciona más utilidad para la generación de seguimientos que libunwind.Y por último, pero no menos importante, ASLR no lo derrota como lo son los enfoques que requieren herramientas externas como addr2line.

Libbacktrace fue inicialmente parte de la distribución GCC, pero ahora el autor la pone a disposición como una biblioteca independiente bajo una licencia BSD:

https://github.com/ianlancetaylor/libbacktrace

Al momento de escribir este artículo, no usaría nada más a menos que necesite generar seguimientos en una plataforma que no sea compatible con libbacktrace.

Puedes hacerlo caminando la pila hacia atrás.Sin embargo, en realidad, con frecuencia es más fácil agregar un identificador a una pila de llamadas al comienzo de cada función y colocarlo al final, y luego simplemente imprimir el contenido.Es un poco PITA, pero funciona bien y al final le ahorrará tiempo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top