2008-08-30 10 views
11

Estoy buscando una respuesta en MS VC++.Averiguar el origen de una excepción en C++ después de que se detecta?

Al depurar una aplicación grande de C++, que desafortunadamente tiene un uso muy extenso de las 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) 
    ... 
} 

I puede detectar la excepción con un punto de interrupción al depurar. Pero no puedo rastrear si ocurrió la excepción 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 quede atrapada). 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 a este problema en otros idiomas?

Respuesta

11

Si están interesados ​​sólo en donde la excepción viene, usted podría 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, número de línea y el nombre de función al texto de la excepción (si el el compilador proporciona las macros respectivas).

Entonces lanzar excepciones usando

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

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

1

No hay una forma estándar de hacerlo.

Además, la pila de llamadas debe registrarse normalmente en el momento de la excepción que es lanzada; una vez que ha sido atrapado la pila se ha desenrollado, por lo que ya no sabe lo que estaba sucediendo en el punto de lanzamiento.

En VC++ en Win32/Win64, que puede obtener resultados utilizables suficientemente registrando el valor intrínseco de la _ReturnAddress compilador() y la garantía de que su constructor de clase de excepción es __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.

4

No hay forma de averiguar el origen de una excepción después de se detecta a, 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 generar la traza (creo que también hay otras formas). Consulte this article para obtener un código de ejemplo.

+0

realmente la pila no se desenrolla hasta que se hayan invocado todos los controladores de captura. (El modelo de excepción C++, implementado encima de la ventana SEH, realiza algunos pasos a través de la cadena de excepción SEH). y es perfectamente posible llegar al origen de la excepción (en el depurador, por ejemplo) estando dentro de esta captura. – deemok

6

Hay un excelente libro escrito por John Robbins que aborda muchas preguntas difíciles de depuración. El libro se llama Debugging Applications for Microsoft .NET and 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 mal no recuerdo, algunos de sus consejos incluyen el uso de manejo de excepciones estructuradas (SEH) en lugar de (o además de) las excepciones de C++. Realmente no puedo recomendar el libro lo suficiente.

+0

Incluso más allá del voto extra que acabo de dar, quiero enfatizar nuevamente que el libro de John Robbins es un EXCELENTE LIBRO. Yo también lo recomiendo. – pestophagous

0

En caso de que a alguien le interesa, un compañero de trabajo respondió a esta pregunta a mí a través de correo electrónico:

Artem escribió:

Hay una bandera a MiniDumpWriteDump() que puede hacerlo mejor volcados que la voluntad Permitir 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 desactives (quizás algunas) optimizaciones.

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 usted 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

Esos tipos no ayudará con la pila de llamadas, sin embargo, que sólo afectan a la cantidad de variables que se Podré ver.

Me di cuenta de que algunos de esos tipos de volcado no son compatibles con dbghelp.dll versión 5.1 que utilizamos. 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.

12

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 C++, seleccionar opciones lanzadas y no detectadas)

+0

duh, ¿por qué no pensé en eso - guardado nuevamente por Stack Overflow? – paquetp

2

Así es como lo hago en C++ usando bibliotecas CCG: constructor de

#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; 
} 

Tu de excepción puede simplemente llamar a esta función y almacenar lejos del seguimiento de la pila. Toma el parámetro numskip porque me gusta cortar el constructor de la excepción de mis rastreos de pila.

0

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 excepción:

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

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

1

En el código nativo, puede obtener una oportunidad para caminar la pila de llamadas instalando un Vectored Exception handler. 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 has some warnings para usarlo en una aplicación que tenga código administrado.Finalmente, lea Matt Pietrek's article 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.)

1

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

4

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

+1

Esto funciona particularmente bien si todas sus excepciones se derivan de una única clase base. –

1

creo MSDev le permite establecer puntos de interrupción cuando se produce una excepción.

Alternativamente poner el punto de quiebre en el constructor de su objeto de excepción.

Cuestiones relacionadas