try tipos programacion manejo lanzar excepciones excepcion errores como catch capturar c++ visual-studio winapi exception visual-c++

tipos - ¿Encontrar la fuente de una excepción en C++ después de que se detecta?



tipos de excepciones programacion (13)

¿Otros idiomas? Bueno, en Java llamas a e.printStackTrace (); No es mucho más simple que eso.

Estoy buscando una respuesta en MS VC ++.

Al depurar una aplicación grande de C ++, que desafortunadamente tiene un uso muy extenso de excepciones de C ++. A veces encuentro una excepción un poco más tarde de lo que realmente quiero.

Ejemplo en pseudo código:

FunctionB() { ... throw e; ... } FunctionA() { ... FunctionB() ... } try { Function A() } catch(e) { (<--- breakpoint) ... }

Puedo detectar la excepción con un punto de interrupción cuando se depura. Pero no puedo rastrear si la excepción ocurrió en FunctionA() o FunctionB() , o alguna otra función. (Suponiendo un uso extensivo de excepciones y una gran versión del ejemplo anterior).

Una solución a mi problema es determinar y guardar la pila de llamadas en el constructor de excepciones (es decir, antes de que se capture). Pero esto requeriría que obtenga todas las excepciones de esta clase de excepción base. También requeriría una gran cantidad de código, y tal vez ralentizar mi programa.

¿Hay alguna manera más fácil que requiera menos trabajo? Sin tener que cambiar mi gran base de código?

¿Hay mejores soluciones para este problema en otros idiomas?


Así es como lo hago en C ++ usando las bibliotecas de GCC:

#include <execinfo.h> // Backtrace #include <cxxabi.h> // Demangling vector<Str> backtrace(size_t numskip) { vector<Str> result; std::vector<void*> bt(100); bt.resize(backtrace(&(*bt.begin()), bt.size())); char **btsyms = backtrace_symbols(&(*bt.begin()), bt.size()); if (btsyms) { for (size_t i = numskip; i < bt.size(); i++) { Aiss in(btsyms[i]); int idx = 0; Astr nt, addr, mangled; in >> idx >> nt >> addr >> mangled; if (mangled == "start") break; int status = 0; char *demangled = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status); Str frame = (status==0) ? Str(demangled, demangled+strlen(demangled)) : Str(mangled.begin(), mangled.end()); result.push_back(frame); free(demangled); } free(btsyms); } return result; }

El constructor de su excepción simplemente puede llamar a esta función y almacenar el rastreo de la pila. Toma param numskip porque me gusta cortar el constructor de la excepción de mis rastreos de pila.


En caso de que alguien esté interesado, un compañero de trabajo me respondió esta pregunta por correo electrónico:

Artem escribió:

Hay una bandera para MiniDumpWriteDump () que puede hacer mejores volcados de bloqueos que permitirán ver el estado completo del programa, con todas las variables globales, etc. En cuanto a las pilas de llamadas, dudo que puedan ser mejores debido a las optimizaciones ... a menos que gire ( tal vez algunas) optimizaciones desactivadas.

Además, creo que la desactivación de las funciones en línea y la optimización de todo el programa ayudarán bastante.

De hecho, hay muchos tipos de volcado, tal vez podría elegir uno lo suficientemente pequeño pero aún así tener más información http://msdn.microsoft.com/en-us/library/ms680519(VS.85).aspx

Sin embargo, esos tipos no ayudarán con la pila de llamadas, solo afectan la cantidad de variables que podrá ver.

Noté que algunos de esos tipos de volcado no son compatibles con la versión 5.1 de dbghelp.dll que usamos. Sin embargo, podríamos actualizarlo a la versión más nueva, 6.9. Acabo de consultar el EULA para herramientas de depuración de MS: el dbghelp.dll más nuevo todavía está en condiciones de redistribuirse.


Hay un excelente libro escrito por John Robbins que aborda muchas preguntas difíciles de depuración. El libro se llama Aplicaciones de depuración para Microsoft .NET y Microsoft Windows . A pesar del título, el libro contiene una gran cantidad de información sobre la depuración de aplicaciones nativas de C ++.

En este libro, hay una larga sección sobre cómo obtener la pila de llamadas para las excepciones que se lanzan. Si recuerdo correctamente, algunos de sus consejos implican el uso de manejo estructurado de excepciones (SEH) en lugar de (o además de) las excepciones de C ++. Realmente no puedo recomendar el libro lo suficiente.


No hay una forma estándar de hacer esto.

Además, la pila de llamadas debe registrarse normalmente en el momento en que se lanza la excepción; una vez que se ha capturado, la pila se ha desenrollado, por lo que ya no se sabe lo que estaba sucediendo en el momento de ser arrojado.

En VC ++ en Win32 / Win64, puede obtener resultados utilizables, registrando el valor del compilador _ReturnAddress () intrínseco y asegurándose de que el constructor de la clase de excepción sea __declspec (noinline). Junto con la biblioteca de símbolos de depuración, creo que probablemente pueda obtener el nombre de la función (y el número de línea, si su .pdb lo contiene) que corresponde a la dirección de retorno utilizando SymGetLineFromAddr64.


Señaló un punto de interrupción en el código. Como está en el depurador, puede establecer un punto de interrupción en el constructor de la clase de excepción o establecer el depurador de Visual Studio para romper todas las excepciones lanzadas (Depurar-> Excepciones Hacer clic en excepciones de C ++, seleccionar opciones lanzadas y no detectadas)


Yo uso mis propias excepciones. Puede manejarlos bastante simple, también contienen texto. Yo uso el formato:

throw Exception( "comms::serial::serial( )", "Something failed!" );

También tengo un segundo formato de excepción:

throw Exception( "comms::serial::serial( )", ::GetLastError( ) );

Que luego se convierte de un valor DWORD al mensaje real usando FormatMessage. Usando el formato de dónde / qué le mostrará qué sucedió y en qué función.


En el código nativo, puede obtener una oportunidad para recorrer la pila de llamadas instalando un controlador de Excepción Vectored . VC ++ implementa excepciones de C ++ además de las excepciones de SEH y un manejador de excepciones vectorizadas recibe el primer disparo antes que cualquier controlador basado en tramas. Sin embargo, tenga mucho cuidado, los problemas introducidos por el manejo de excepciones vectorizadas pueden ser difíciles de diagnosticar.

También Mike Stall tiene algunas advertencias sobre su uso en una aplicación que tiene código administrado. Finalmente, lea el artículo de Matt Pietrek y asegúrese de entender SEH y el manejo de excepciones vectorizadas antes de intentar esto. (Nada se siente tan mal como rastrear un problema crítico para codificar la ayuda agregada para rastrear problemas críticos).


Ponga un punto de interrupción en el constructor de objeto de excepción. Obtendrá su punto de interrupción antes de que se produzca la excepción.


Si está depurando desde el IDE, vaya a Depurar-> Excepciones, haga clic en Lanzado para excepciones de C ++.


No hay forma de averiguar el origen de una excepción después de que se detecta, a menos que incluya esa información cuando se lanza. Para cuando atrape la excepción, la pila ya está desenrollada, y no hay forma de reconstruir el estado previo de la pila.

Su sugerencia de incluir el seguimiento de la pila en el constructor es su mejor opción. Sí, cuesta tiempo durante la construcción, pero es probable que no deba arrojar excepciones con la suficiente frecuencia como para preocuparse. Hacer que todas sus excepciones hereden de una nueva base también puede ser más de lo que necesita. Simplemente podría heredar las excepciones relevantes (gracias, herencia múltiple) y tener una captura separada para esas.

Puede usar la función StackTrace64 para construir la traza (creo que también hay otras formas). Mira este artículo para ver el código de ejemplo.


Si solo está interesado en la procedencia de la excepción, puede escribir una macro simple como

#define throwException(message) / { / std::ostringstream oss; / oss << __FILE __ << " " << __LINE__ << " " / << __FUNC__ << " " << message; / throw std::exception(oss.str().c_str()); / }

que agregará el nombre del archivo, el número de línea y el nombre de la función al texto de excepción (si el compilador proporciona las macros respectivas).

Luego lanza excepciones usando

throwException("An unknown enum value has been passed!");


Creo que MSDev te permite establecer puntos de ruptura cuando se lanza una excepción.

Alternativamente, ponga el punto de corte en el constructor de su objeto de excepción.