c++ - onlinegdb - online gdb python
¿Cómo interrumpirse cuando se lanza un tipo de excepción específico en GDB? (7)
De acuerdo con la documentación, puedo interrumpir un tipo de excepción específico utilizando puntos de interrupción condicionales. Sin embargo, la sintaxis de la condición no es muy clara para mí:
condition bnum <expression>
Mirando la sintaxis de la expresión, creo que este es el patrón que necesito:
{type} addr
Sin embargo, no sé qué debo aprobar para el argumento addr
. Intenté lo siguiente:
(gdb) catch throw
(gdb) condition 1 boost::bad_function_call *
Pero no funciona (gdb se rompe en todos los tipos de excepción).
¿Alguien puede ayudar?
Actualizar
También probé la sugerencia de @ Adam, pero da como resultado un mensaje de error:(gdb) catch throw boost::bad_function_call
Junk at end of arguments.
Sin boost::
espacio de nombres:
(gdb) catch throw bad_function_call
Junk at end of arguments.
Solución
Romper en el constructor debad_function_call
funciona. Como otros ya han mencionado, esta funcionalidad no funciona en la práctica. Pero como solución puede poner condiciones en el catch throw
. Cuando se lanza la excepción, llegamos a la función __cxa_throw
. Tiene varios parámetros que apuntan a la clase de excepción, por lo que podemos establecer la condición en uno de ellos. En la sesión gdb de muestra a continuación, puse condición en el parámetro dest
de __cxa_throw
. El único problema es que el valor de dest
(0x80486ec en este caso) se desconoce de antemano. Se puede conocer, por ejemplo, ejecutando primero gdb sin condiciones en el punto de interrupción.
[root@localhost ~]#
[root@localhost ~]# gdb ./a.out
GNU gdb (GDB) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/a.out...done.
(gdb) catch throw
Catchpoint 1 (throw)
(gdb) condition 1 dest==0x80486ec
No symbol "dest" in current context.
(gdb) r
warning: failed to reevaluate condition for breakpoint 1: No symbol "dest" in current context.
warning: failed to reevaluate condition for breakpoint 1: No symbol "dest" in current context.
warning: failed to reevaluate condition for breakpoint 1: No symbol "dest" in current context.
Catchpoint 1 (exception thrown), __cxxabiv1::__cxa_throw (obj=0x804a080, tinfo=0x8049ca0, dest=0x80486ec <_ZNSt13runtime_errorD1Ev@plt>) at ../../../../gcc-4.4.3/libstdc++-v3/libsupc++/eh_throw.cc:68
68 ../../../../gcc-4.4.3/libstdc++-v3/libsupc++/eh_throw.cc: No such file or directory.
in ../../../../gcc-4.4.3/libstdc++-v3/libsupc++/eh_throw.cc
(gdb) bt
#0 __cxxabiv1::__cxa_throw (obj=0x804a080, tinfo=0x8049ca0, dest=0x80486ec <_ZNSt13runtime_errorD1Ev@plt>) at ../../../../gcc-4.4.3/libstdc++-v3/libsupc++/eh_throw.cc:68
#1 0x08048940 in main () at test.cpp:14
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep y 0x008d9ddb exception throw
stop only if dest==0x80486ec
breakpoint already hit 1 time
(gdb)
Actualizar
También debe cargar la información de depuración para libstdc ++ para que esta solución funcione.
Creo que puedo responder a la parte sobre el establecimiento de descansos condicionales. No responderé a la pregunta sobre las excepciones, ya que __raise_exception parece no existir en g ++ 4.5.2 (?)
Supongamos que tiene el siguiente código (uso void para obtener algo similar a __raise_exception de gdb doc)
void foo(void* x) {
}
int main() {
foo((void*)1);
foo((void*)2);
}
para romper en foo (2) usas los siguientes comandos
(gdb) break foo
Breakpoint 1 at 0x804851c: file q.cpp, line 20.
(gdb) condition 1 x == 2
Si corres con
(gdb) r
Verás que se detiene en la segunda llamada foo, pero no en la primera.
Creo que lo que querían decir en los documentos es que establece la interrupción en la función __raise_exception (depende de la implementación)
/* addr is where the exception identifier is stored
id is the exception identifier. */
void __raise_exception (void **addr, void *id);
y luego configure la ruptura condicional en la identificación como se describió anteriormente (tiene que determinar de alguna manera cuál es la identificación para su tipo de excepción).
Desafortunadamente
(gdb) break __raise_exception
resultados con (g ++ 4.5.2)
Function "__raise_exception" not defined.
Cuando el comando gdb ''catch throw'' falla, intente esta solución:
(probado con Linux g ++ 4.4.5 / gdb 6.6)
1 / Agregue este código en cualquier parte del programa para depurar:
#include <stdexcept>
#include <exception>
#include <typeinfo>
struct __cxa_exception {
std::type_info *inf;
};
struct __cxa_eh_globals {
__cxa_exception *exc;
};
extern "C" __cxa_eh_globals* __cxa_get_globals();
const char* what_exc() {
__cxa_eh_globals* eh = __cxa_get_globals();
if (eh && eh->exc && eh->exc->inf)
return eh->exc->inf->name();
return NULL;
}
2 / En gdb entonces podrás filtrar excepciones con:
(gdb) break __cxa_begin_catch
(gdb) cond N (what_exc()?strstr(what_exc(),"exception_name"):0!=0)
donde N es el número de punto de interrupción, y nombre_excepción es el nombre de excepción para el que deseamos interrumpir.
Otro enfoque es confiar en el argumento tinfo
disponible cuando se activa el punto de captura, que es un puntero al objeto devuelto por typeid(type)
.
Entonces, si quiero detectar la excepción std::bad_alloc
está std::bad_alloc
, podría hacer:
> p &typeid(std::bad_alloc)
> $1 = (__cxxabiv1::__si_class_type_info *) 0x8c6db60 <typeinfo for std::bad_alloc>
> catch throw if tinfo == 0x8c6db60
Por lo que he entendido de la pregunta aquí, desea interrumpirse cuando se lance una aplicación específica boost::bad_function_call
en su aplicación.
$> gdb /path/to/binary
(gdb) break boost::bad_function_call::bad_function_call()
(gdb) run --some-cli-options
Entonces, cuando el objeto temporal boost::bad_function_call
se construye en preparación para el throw
; gdb estallará!
He probado esto y funciona. Si conoce con precisión la forma en que se está construyendo el objeto de excepción, puede establecer un punto de interrupción en el constructor específico; de lo contrario, como se muestra en el siguiente ejemplo, puede omitir la lista de prototipos de argumentos, y gdb
establecerá puntos de ruptura en todos los sabores diferentes de constructor.
$ gdb /path/to/binary
(gdb) break boost::bad_function_call::bad_function_call
Breakpoint 1 at 0x850f7bf: boost::bad_function_call::bad_function_call. (4 locations)
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y <MULTIPLE>
1.1 y 0x0850f7bf in boost::bad_function_call::bad_function_call() at /usr/include/boost/function/function_base.hpp:742
1.2 y 0x0850fdd5 in boost::bad_function_call::bad_function_call(boost::bad_function_call const&) at /usr/include/boost/function/function_base.hpp:739
1.3 y 0x0863b7d2 <boost::bad_function_call::bad_function_call()+4>
1.4 y 0x086490ee <boost::bad_function_call::bad_function_call(boost::bad_function_call const&)+6>
Supongamos que tiene el siguiente code.cpp con un subproceso que produce una excepción:
#include <iostream>
#include <thread>
void thr()
{
while (true) {
new int[1000000000000ul];
}
}
int main(int argc, char* argv[]) {
std::thread t(thr);
t.join();
std::cout << "Hello, World!" << std::endl;
return 0;
}
Compílalo usando el siguiente CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(tutorial)
set(CMAKE_CXX_STANDARD 11)
add_executable(test_exceptions main.cpp)
target_link_libraries(test stdc++ pthread)
Ahora puedes jugar con, ejecutarlo te dará un aborto debido a bad_alloc. Antes de continuar, es mejor si instala los símbolos de depuración de libstd, sudo apt-get install libstdc++6-5-dbg
o la versión que tenga.
Compilación de depuración
Si está compilando en Debug
, puede seguir esta respuesta https://.com/a/12434170/5639395 porque los constructores generalmente están definidos.
Compilación de lanzamiento
Si está compilando en DebWithRelInfo
es posible que no pueda encontrar un constructor adecuado donde colocar su punto de interrupción debido a la optimización del compilador. En este caso, tienes otras opciones. Continuemos.
Solución de cambio de código fuente
Si puede cambiar el código fuente fácilmente, esto funcionará https://.com/a/9363680/5639395
Gdb captura fácil solución
Si no desea cambiar el código, puede intentar ver si funciona el catch throw bad_alloc
o, en general, catch throw exception_name
.
Solución de tiro de captura de Gdb
Construiré sobre esta respuesta https://.com/a/6849989/5639395 __cxxabiv1::__cxa_throw
un punto de interrupción en gdb en la función __cxxabiv1::__cxa_throw
. Esta función toma un parámetro llamado tinfo
que tiene la información que necesitamos para verificar condicionalmente la excepción que nos importa.
Queremos algo como el lanzamiento de la captura si la excepción == bad_alloc , entonces, ¿cómo encontrar la comparación adecuada? Resulta que tinfo
es un puntero a una estructura que tiene una variable llamada __name
dentro. Esta variable tiene una cadena con el nombre mutilado del tipo de excepción.
Así que podemos hacer algo como: atrapar tiros si tinfo -> __ name == mangled_exception_name
¡Estamos casi alli!
Necesitamos una forma de hacer una comparación de cadenas, y resulta que gdb tiene una función integrada $ _streq (str1, str2) que hace exactamente lo que necesitamos. El nombre mutilado de la excepción es un poco más difícil de encontrar, pero puede intentar adivinarlo o consultar el Apéndice de esta respuesta. Supongamos que por ahora es "St9bad_alloc".
La instrucción final es:
catch throw if $_streq(tinfo->__name , "St9bad_alloc")
o equivalente
break __cxxabiv1::__cxa_throw if $_streq(tinfo->__name , "St9bad_alloc")
Cómo encontrar el nombre de su excepción
Tienes dos opciones
Busca el símbolo en la biblioteca.
Suponiendo que instaló los símbolos de depuración libstd, puede encontrar el nombre de la biblioteca así:
apt search libstd | grep dbg | grep installed
El nombre es algo así como libstdc++6-5-dbg
Ahora revisa los archivos instalados:
dpkg -L libstdc++6-5-dbg
Busque algo que tenga una depuración en la ruta y una extensión .so. En mi PC tengo /usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6.0.21
. Finalmente, busque la excepción que desea allí.
nm /usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6.0.21 | grep -i bad_alloc
O nm /usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6.0.21 | grep -i runtime_error
nm /usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6.0.21 | grep -i runtime_error
etc.
En mi caso, encontré algo como 00000000003a4b20 V _ZTISt9bad_alloc
que me sugirió usar "St9bad_alloc" como nombre.
Tirarlo en gdb e inspeccionar el nombre allí
Esto es fácil, solo inicia gdb, catch throw
todo y ejecuta el pequeño ejecutable que escribí antes. Cuando estés dentro de gdb, puedes emitir un p *tinfo
y buscar la descripción de __name
en gdb.
gdb -ex ''file test_exceptions'' -ex ''catch throw'' -ex ''run''
(gdb) p *tinfo $1 = {_vptr.type_info = 0x406260 <vtable for __cxxabiv1::__si_class_type_info+16>, __name = 0x7ffff7b8ae78 <typeinfo name for std::bad_alloc> "St9bad_alloc"}
EDITAR
La documentación sugiere que el catch throw <exceptname>
puede usarse para romper cuando se lanza una excepción de tipo <exceptname>
; Sin embargo, eso no parece funcionar en la práctica.
(gdb) help catch
Set catchpoints to catch events.
Raised signals may be caught:
catch signal - all signals
catch signal <signame> - a particular signal
Raised exceptions may be caught:
catch throw - all exceptions, when thrown
catch throw <exceptname> - a particular exception, when thrown
catch catch - all exceptions, when caught
catch catch <exceptname> - a particular exception, when caught
Thread or process events may be caught:
catch thread_start - any threads, just after creation
catch thread_exit - any threads, just before expiration
catch thread_join - any threads, just after joins
Process events may be caught:
catch start - any processes, just after creation
catch exit - any processes, just before expiration
catch fork - calls to fork()
catch vfork - calls to vfork()
catch exec - calls to exec()
Dynamically-linked library events may be caught:
catch load - loads of any library
catch load <libname> - loads of a particular library
catch unload - unloads of any library
catch unload <libname> - unloads of a particular library
The act of your program''s execution stopping may also be caught:
catch stop
C++ exceptions may be caught:
catch throw - all exceptions, when thrown
catch catch - all exceptions, when caught
Ada exceptions may be caught:
catch exception - all exceptions, when raised
catch exception <name> - a particular exception, when raised
catch exception unhandled - all unhandled exceptions, when raised
catch assert - all failed assertions, when raised
Do "help set follow-fork-mode" for info on debugging your program
after a fork or vfork is caught.
Do "help breakpoints" for info on other commands dealing with breakpoints.