¿Cómo obtengo el valor y el tipo de la excepción actual en C++ usando gdb?

6 minutos de lectura

gdb permite detectar excepciones cuando se generan y cuando se detectan. Pero a veces, la línea en la que se lanza una excepción no tiene símbolos, o se activa un punto de interrupción durante el manejo de excepciones. ¿Cómo inspecciono el valor de la excepción actual?

avatar de usuario
tom tromey

Las respuestas anteriores eran correctas cuando se escribieron (en 2013), pero desde entonces gdb y libstdc++ han cambiado.

libstdc++ ahora tiene algunos ganchos que permiten que gdb interactúe mejor con el sistema de excepciones. En particular, ahora hay suficiente información expuesta para que gdb proporcione una $_exception variable de conveniencia para el usuario. Esta variable contiene la excepción que se lanza. Solo es válido exactamente en el lugar donde se detecta la excepción; que puedes dejar de usar catch catch.

Ver la pagina del manual para detalles.

avatar de usuario
UpAndAdam

Actualizado


Aquí hay algo de información del Manual de GDB

Actualmente existen algunas limitaciones para el manejo de excepciones de C++ (catch throw y catch catch) en gdb:

Si llama a una función de forma interactiva, gdb normalmente le devuelve el control cuando la función ha terminado de ejecutarse. Sin embargo, si la llamada genera una excepción, la llamada puede pasar por alto el mecanismo que le devuelve el control y hacer que su programa aborte o simplemente continúe ejecutándose hasta que llegue a un punto de interrupción, capte una señal que gdb está escuchando o salga. Este es el caso incluso si establece un punto de captura para la excepción; los puntos de captura en las excepciones están deshabilitados dentro de las llamadas interactivas. No puede generar una excepción de forma interactiva. No puede instalar un controlador de excepciones de forma interactiva. A veces, catch no es la mejor manera de depurar el manejo de excepciones: si necesita saber exactamente dónde se genera una excepción, es mejor detenerse antes de que se llame al controlador de excepciones, ya que de esa manera puede ver la pila antes de que se produzca cualquier desenredado. Si establece un punto de interrupción en un controlador de excepciones, puede que no sea fácil averiguar dónde se generó la excepción.

Para detenerse justo antes de que se llame a un controlador de excepciones, necesita algún conocimiento de la implementación. En el caso de gnu C++, las excepciones se generan llamando a una función de biblioteca llamada __raise_exception que tiene la siguiente interfaz ANSI C:

     /* addr is where the exception identifier is stored.
        id is the exception identifier.  */
     void __raise_exception (void **addr, void *id); To make the debugger catch all exceptions before any stack unwinding takes place,

establezca un punto de interrupción en __raise_exception (consulte Puntos de interrupción, Puntos de observación y Excepciones).


Eso dijo

Depende del código y de dónde se encuentre en la pila. Si realmente atrapó la excepción como en:

try { .... } catch (std::exception &e) {
   //do stuff
}

Probablemente podría intentar imprimir e.what(), o mire los miembros de la excepción. Si solo lo atrapaste como (…) entonces no estoy seguro de lo que podrías recopilar.

Otra cosa de manejo que podrías hacer es atrapar ‘lanzar’ en gdb y atrapar ‘atrapar’ también si realmente quieres seguir todo el flujo.

gdb> catch catch  
gdb> catch throw

De esta manera, obtendrá puntos de interrupción justo antes de que se generen las excepciones y, justo cuando se detecten, podría recorrer la pila para obtener más información sobre lo que estaba sucediendo. Incluso si está en otro punto de interrupción, debería poder subir la pila (usando arriba o abajo) para obtener el marco en el que se ve la excepción.

Respuesta corta: no puede porque la mayor parte del trabajo de manejo de excepciones se realiza fuera de su programa y, por lo tanto, está fuera del alcance de gdb.

Respuesta explicada:

a veces la línea que se lanza una excepción no tiene símbolos

Si el binario que está depurando no tiene símbolos de depuración, entonces el binario probablemente esté eliminado y no podrá encontrar mucho sobre los tipos/valores de nada.

¿Cómo inspecciono el valor de la excepción actual?

Creo que está asumiendo aquí que una excepción es una característica del idioma que gdb puede inspeccionar; de hecho, una excepción en C++ es una combinación de características de C++ como lenguaje, de libc++ y ABI. E incluso podría haber más de una sola excepción actual activa.

Como señala UpAndAdam, puede establecer un punto de interrupción en un bloque catch con un especificador de tipo y luego inspeccionar ese elemento, pero sospecho que su problema es en los casos en que encuentra un “catch (…)”. En esos casos, no podrá aprender mucho sobre la excepción actual a menos que profundice en la implementación del manejo de excepciones.

Con una descripción muy corta e incompleta podríamos decir que para lanzar una excepción:

  1. Su programa llamará a libc++ para generar una excepción
  2. libc++ llamará a “desenrollar” en glibc para comenzar a desenrollar la pila
  3. unwind devolverá una “función de personalidad” de libc ++ para cada marco de pila (básicamente, cada llamada de función en la pila)
  4. la función de personalidad de alguna manera decidirá si el marco de pila actual puede o no manejar esta excepción
  5. si se puede manejar la excepción, se ejecutará el bloque catch

Ahora, es difícil hablar sobre los detalles porque gran parte del manejo de excepciones depende de su cadena de herramientas (compilador, plataforma, arquitectura, libc++, etc.), pero en la mayoría de los casos, una “captura (…)” ni siquiera recibirá la excepción original. como argumento. En cualquier caso, para responder de alguna manera a su pregunta: en gcc con libc ++ de gnu, podría intentar algo como esto:

  1. Obtenga una libc++ con símbolos de depuración
  2. Establezca un punto de interrupción en __gxx_personality_v0 (que se llama la función de personalidad). Se llamará a esta función para determinar si un marco de pila (una llamada de función, básicamente) tiene un bloque catch adecuado para manejar la excepción
  3. En la función de personalidad, podrá encontrar un puntero a _Unwind_Exception, que es un contenedor para su excepción real.
  4. Obtenga la información de tipo para su excepción de esta manera: __cxa_exception *exception_header = (__cxa_exception*)(unwind_exception+1)-1; std::type_info *thrown_exception_type =Exception_header->exceptionType;
  5. Obtendrá un tipo de excepción que luego puede buscar con el resto del RTTI definido para su código

En cualquier caso, es probable que deba dedicar bastante tiempo a tratar de comprender cómo se implementa el manejo de excepciones en su plataforma. Si desea leer un poco más sobre el manejo de excepciones, pasé un tiempo en el pasado escribiendo sobre el tema @ http://monoinfinito.wordpress.com/series/exception-handling-in-c/. No es una fuente oficial, pero tiene enlaces a las especificaciones de cada parte involucrada en el manejo de una excepción.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad