Sé que no hay una función C estándar para hacer esto. Me preguntaba cuáles son las técnicas para hacer esto en Windows y * nix. (Windows XP es mi sistema operativo más importante para hacer esto en este momento).
Sé que no hay una función C estándar para hacer esto. Me preguntaba cuáles son las técnicas para hacer esto en Windows y * nix. (Windows XP es mi sistema operativo más importante para hacer esto en este momento).
Respuestas:
glibc proporciona la función backtrace ().
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
char ** backtrace_symbols (void *const *buffer, int size)
void backtrace_symbols_fd(void *const *buffer, int size, int fd)
que enviar la salida directamente a stdout / err, por ejemplo.
backtrace_symbols()
apesta. Requiere exportar todos los símbolos y no admite símbolos DWARF (depuración). libbacktrace es una opción mucho mejor en muchos (la mayoría) de los casos.
Hay backtrace () y backtrace_symbols ():
Desde la página del 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, solo 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 < frames; ++i) {
printf("%s\n", strs[i]);
}
free(strs);
}
};
...
try {
throw MyException("Oops!");
} catch ( MyException e ) {
e.printStackTrace();
}
Ta da!
Nota: habilitar los indicadores de optimización puede hacer que el seguimiento de la pila resultante sea inexacto. Idealmente, se utilizaría 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 usar la forma nativa del sistema operativo para hacerlo, o recurrir al 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 generalmente está tratando de lograr lo incorrecto.
La mayoría de las veces, las personas intentan obtener un seguimiento de pila en, digamos, una circunstancia excepcional, como cuando se detecta una excepción, una afirmación falla o, lo peor y lo más incorrecto de todos, cuando obtiene una "excepción" fatal o una señal como violación de la segmentación.
Teniendo en cuenta el último problema, la mayoría de las API requerirán que asigne memoria explícitamente o puede que lo haga internamente. Si lo hace 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 fallos (o coredump) no reflejará la causa real del problema, sino su intento fallido de solucionarlo).
Supongo que está tratando de lograr esa cosa del manejo de errores fatales, ya que la mayoría de la gente parece intentar eso 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 volcara en producción (o mini volcado en Windows). Junto con una gestión adecuada de los símbolos, no debería tener problemas para determinar la instrucción que la causa después de la autopsia.
Debería utilizar la biblioteca de desenrollado .
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 addr2line
comando en Linux para obtener la función fuente / número de línea de la PC correspondiente.
No existe una forma independiente de la plataforma para hacerlo.
Lo más cercano que puede hacer es ejecutar el código sin optimizaciones. De esa manera, puede adjuntar al proceso (utilizando el depurador visual de c ++ o GDB) y obtener un seguimiento de pila utilizable.
Solaris tiene el comando pstack , que también se copió en Linux.
Puede hacerlo caminando la pila hacia atrás. En realidad, sin embargo, con frecuencia es más fácil agregar un identificador en una pila de llamadas al comienzo de cada función y colocarlo al final, luego simplemente caminar imprimiendo el contenido. Es un poco PITA, pero funciona bien y al final te ahorrará tiempo.
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 backtraces que libunwind. Y por último, pero no menos importante, ASLR no lo derrota, ya que los enfoques que requieren herramientas externas como addr2line
.
Libbacktrace fue inicialmente parte de la distribución de GCC, pero ahora el autor lo pone a disposición como una biblioteca independiente bajo una licencia BSD:
https://github.com/ianlancetaylor/libbacktrace
En el momento de escribir este artículo, no usaría nada más a menos que necesite generar backtraces en una plataforma que no es compatible con libbacktrace.