¿Cómo se puede agarrar un rastro de pila en C?


Sé que no hay 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.)

11 answers

Hemos usado esto para nuestros proyectos:

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

El código es un poco desordenado en mi humilde opinión, pero funciona bien. Solo Ventanas.

 22
Author: Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-12 21:12:28

Glibc proporciona la función backtrace ().

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

 77
Author: sanxiyn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-19 21:15:32

Hay backtrace (), y backtrace_symbols ():

De 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/OOP más conveniente 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 pila resultante inexacto. Idealmente, uno usaría esta capacidad con banderas de depuración activadas y banderas de optimización desactivadas.

 24
Author: Tom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-25 19:13:37

Para Windows compruebe la API StackWalk64 () (también en Windows de 32 bits). Para UNIX deberías usar 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 está tratando de lograr lo incorrecto.

La mayoría de las veces la gente trata de obtener un stacktrace en, por ejemplo, una circunstancia excepcional, como cuando se detecta una excepción, una afirmación falla o-peor y más mal de todos ellos-cuando obtienes una "excepción" fatal o 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 su programa puede estar actualmente, puede empeorar aún más las cosas. Por ejemplo, el informe de bloqueo (o coredump) no reflejará la causa real del problema, pero su intento fallido de manejar se).

Asumo que estás tratando de lograr esa cosa de manejo de errores fatales, ya que la mayoría de la gente parece intentarlo cuando se trata de obtener un stacktrace. Si es así, confiaría en el depurador (durante el desarrollo) y dejar que el proceso coredump en producción (o mini-dump en windows). Junto con la correcta gestión de símbolos, no debería tener problemas para calcular la instrucción causante post-mortem.

 20
Author: Christian.K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-24 13:07:58

Para Windows, CaptureStackBackTrace() también es una opción, que requiere menos código de preparación en el extremo del usuario que StackWalk64(). (También, para un escenario similar que tenía, CaptureStackBackTrace() terminó funcionando mejor (más confiablemente) que StackWalk64().)

 4
Author: Quasidart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 21:35:37

Deberías usar la biblioteca unwind.

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.

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

 4
Author: user262802,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-31 06:47:16

No existe una forma independiente de hacerlo.

Lo más cercano que puede hacer es ejecutar el código sin optimizaciones. De esta manera puede adjuntar al proceso (usando el depurador visual c++ o GDB) y obtener un seguimiento de pila utilizable.

 3
Author: Nils Pipenbrinck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-19 21:15:16

Solaris tiene el comando pstack, que también fue copiado en Linux.

 2
Author: wnoise,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-27 15:05:25

Puedo señalar a mi artículo. Son sólo unas pocas líneas de código.

Depuración post Mortem

Aunque actualmente tengo problemas con la implementación de x64 de este.

 2
Author: RED SOFT ADAIR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:00:10

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

Libbacktrace fue inicialmente parte de la distribución GCC, pero ahora está disponible por el autor como una biblioteca independiente bajo una licencia BSD:

Https://github.com/ianlancetaylor/libbacktrace

En el momento de escribir, no usaría nada más a menos que necesite generar backtraces en una plataforma que no sea compatible con libbacktrace.

 0
Author: Erwan Legrand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-08 11:05:34

Puedes hacerlo caminando la pila hacia atrás. En realidad, sin embargo, con frecuencia es más fácil agregar un identificador a una pila de llamadas al principio de cada función y pop al final, a continuación, simplemente caminar que la impresión de los contenidos. Es un poco de PITA, pero funciona bien y te ahorrará tiempo al final.

 -1
Author: Cody Brocious,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-19 21:15:13