c++ - puntos - visual studio saltar propiedades y operadores
¿Cómo establecer un punto de interrupción en GDB donde se devuelve la función? (7)
Contrariamente a las respuestas hasta ahora, la mayoría de los compiladores crearán una única instrucción de conjunto de retorno, independientemente de cuántas declaraciones de return
en la función (es conveniente que el compilador haga eso, de modo que solo haya un lugar para realizar todo el marco de pila). limpiar).
Si desea detenerse en esa instrucción, todo lo que tiene que hacer es disas
y buscar retq
(o cualquiera que sea la instrucción de retorno para su procesador), y establecer un punto de interrupción en ella. Por ejemplo:
int foo(int x)
{
switch(x) {
case 1: return 2;
case 2: return 3;
default: return 42;
}
}
int main()
{
return foo(0);
}
(gdb) disas foo
Dump of assembler code for function foo:
0x0000000000400448 <+0>: push %rbp
0x0000000000400449 <+1>: mov %rsp,%rbp
0x000000000040044c <+4>: mov %edi,-0x4(%rbp)
0x000000000040044f <+7>: mov -0x4(%rbp),%eax
0x0000000000400452 <+10>: mov %eax,-0xc(%rbp)
0x0000000000400455 <+13>: cmpl $0x1,-0xc(%rbp)
0x0000000000400459 <+17>: je 0x400463 <foo+27>
0x000000000040045b <+19>: cmpl $0x2,-0xc(%rbp)
0x000000000040045f <+23>: je 0x40046c <foo+36>
0x0000000000400461 <+25>: jmp 0x400475 <foo+45>
0x0000000000400463 <+27>: movl $0x2,-0x8(%rbp)
0x000000000040046a <+34>: jmp 0x40047c <foo+52>
0x000000000040046c <+36>: movl $0x3,-0x8(%rbp)
0x0000000000400473 <+43>: jmp 0x40047c <foo+52>
0x0000000000400475 <+45>: movl $0x2a,-0x8(%rbp)
0x000000000040047c <+52>: mov -0x8(%rbp),%eax
0x000000000040047f <+55>: leaveq
0x0000000000400480 <+56>: retq
End of assembler dump.
(gdb) b *0x0000000000400480
Breakpoint 1 at 0x400480
(gdb) r
Breakpoint 1, 0x0000000000400480 in foo ()
(gdb) p $rax
$1 = 42
Tengo una función de C ++ que tiene muchas declaraciones de devolución en varios lugares. ¿Cómo establecer un punto de interrupción en la declaración de retorno donde la función realmente devuelve?
¿Y qué significa "romper" el comando sin argumento?
La ruptura sin argumento establece un punto de interrupción en la línea actual.
No hay forma de que un solo punto de interrupción capture todas las rutas de retorno. Establezca un punto de interrupción en la persona que llama inmediatamente después de que regrese, o rompa en todas las declaraciones de return
.
Dado que esto es C ++, supongo que podrías crear un objeto de centinela local y romper su destructor, sin embargo.
Puede utilizar la depuración inversa para averiguar dónde devuelve realmente la función. Termine de ejecutar el fotograma actual, realice el paso inverso y luego debe detenerse en la declaración que acaba de devolver.
(gdb) record
(gdb) fin
(gdb) reverse-step
Si puede cambiar el código fuente, puede usar algún truco sucio con el preprocesador:
void on_return() {
}
#define return return on_return(), /* If the function has a return value != void */
#define return return on_return() /* If the function has a return value == void */
/* <<<-- Insert your function here -->>> */
#undef return
A continuación, establezca un punto de interrupción en on_return
y on_return
un fotograma.
Atención: esto no funcionará si una función no regresa a través de una declaración de return
. Así que asegúrate de que la última línea sea una return
.
Ejemplo (copiado descaradamente del código C, pero también funcionará en C ++):
#include <stdio.h>
/* Dummy function to place the breakpoint */
void on_return(void) {
}
#define return return on_return()
void myfun1(int a) {
if (a > 10) return;
printf("<10/n");
return;
}
#undef return
#define return return on_return(),
int myfun2(int a) {
if (a < 0) return -1;
if (a > 0) return 1;
return 0;
}
#undef return
int main(void)
{
myfun1(1);
myfun2(2);
}
La primera macro cambiará
return;
a
return on_return();
Lo cual es válido, ya que on_return
también devuelve void
.
La segunda macro cambiará
return -1;
a
return on_return(), -1;
Lo que llamará a on_return()
y luego devolverá -1 (gracias a ,
-operator).
Este es un truco muy sucio, pero a pesar de usar pasos hacia atrás, también funcionará en entornos de subprocesos múltiples y funciones integradas.
rr
depuración inversa
Similar al record
GDB mencionado en https://.com/a/3649698/895245 , pero mucho más funcional a partir de GDB 7.11 vs rr
4.1.0 en Ubuntu 16.04.
En particular, se trata de AVX correctamente:
- .com/questions/2528918/…
- "target record-full" en gdb hace que el comando "n" falle en printf con "El registro de proceso no admite la instrucción 0xc5 en la dirección 0x7ffff7dee6e7"?
lo que le impide trabajar con las llamadas de biblioteca estándar por defecto.
Instalar Ubuntu 16.04.
sudo apt-get install rr linux-tools-common linux-tools-generic linux-cloud-tools-generic
sudo cpupower frequency-set -g performance
Pero también considera compilar desde la fuente para obtener las últimas actualizaciones, no fue difícil.
Programa de prueba:
int where_return(int i) {
if (i)
return 1;
else
return 0;
}
int main(void) {
where_return(0);
where_return(1);
}
compilar y ejecutar:
gcc -O0 -ggdb3 -o reverse.out -std=c89 -Wextra reverse.c
rr record ./reverse.out
rr replay
Ahora queda dentro de una sesión de GDB, y puede revertir adecuadamente la depuración:
(rr) break main
Breakpoint 1 at 0x56057c458619: file a.c, line 9.
(rr) continue
Continuing.
Breakpoint 1, main () at a.c:9
9 where_return(0);
(rr) step
where_return (i=0) at a.c:2
2 if (i)
(rr) finish
Run till exit from #0 where_return (i=0) at a.c:2
main () at a.c:10
10 where_return(1);
Value returned is $1 = 0
(rr) reverse-step
where_return (i=0) at a.c:6
6 }
(rr) reverse-step
5 return 0;
Ahora estamos en la línea de retorno correcta.
Romper en todos los retq de la función actual
Este comando de Python pone un punto de interrupción en cada instrucción retq
de la función actual:
class BreakReturn(gdb.Command):
def __init__(self):
super().__init__(
''break-return'',
gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE,
False
)
def invoke(self, arg, from_tty):
frame = gdb.selected_frame()
# TODO make this work if there is no debugging information, where .block() fails.
block = frame.block()
# Find the function block in case we are in an inner block.
while block:
if block.function:
break
block = block.superblock
start = block.start
end = block.end
arch = frame.architecture()
pc = gdb.selected_frame().pc()
instructions = arch.disassemble(start, end - 1)
for instruction in instructions:
if instruction[''asm''].startswith(''retq ''):
gdb.Breakpoint(''*{}''.format(instruction[''addr'']))
BreakReturn()
Fuente con:
source gdb.py
y usa el comando como:
break-return
continue
Ahora deberías estar en retq
.
Paso hasta retq
Solo por diversión, otra implementación que se detiene cuando se encuentra un retq
(menos eficiente porque no hay soporte de hardware):
class ContinueReturn(gdb.Command):
def __init__(self):
super().__init__(
''continue-return'',
gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE,
False
)
def invoke(self, arg, from_tty):
thread = gdb.inferiors()[0].threads()[0]
while thread.is_valid():
gdb.execute(''ni'', to_string=True)
frame = gdb.selected_frame()
arch = frame.architecture()
pc = gdb.selected_frame().pc()
instruction = arch.disassemble(pc)[0][''asm'']
if instruction.startswith(''retq ''):
break
ContinueReturn()
Esto ignorará tus otros puntos de ruptura. TODO: se puede evitar?
No estoy seguro si es más rápido o más lento que reverse-step
.
Una versión que se detiene en un código de operación determinado se puede encontrar en: https://.com/a/31249378/895245
romper sin argumentos detiene la ejecución en la siguiente instrucción en el marco de pila seleccionado actualmente. Selecciona marcos de strack mediante los comandos de frame
o up
y down
. Si desea depurar el punto en el que realmente está dejando la función actual, seleccione el siguiente marco exterior y rompa allí.