Chromium Chronicle n.o 13: Depuración de viaje en el tiempo con RR

Episodio 13: de Christian Biesinger en Madison, WI (marzo de 2020)
Episodios anteriores

¿Ejecutas la misma prueba una y otra vez en el depurador para intentar averiguar cómo entró en mal estado el código? Tenemos una herramienta para ti. Es fácil de instalar y configurar, registra un seguimiento de ejecución y eso le otorga nuevos poderes mágicos a gdb. Retrocede, ejecuta hacia atrás y consulta dónde cambiaron su valor las variables o cuándo se llamó por última vez a una función en un objeto (mediante puntos de interrupción condicionales).

En Linux, puedes usar rr. Instálalo con sudo apt-get install rr o desde https://rr-project.org/.

No se admite oficialmente, pero es muy útil. La forma en que funciona rr es que primero registras un seguimiento y, luego, lo vuelves a reproducir.

rr record .../content_shell --no-sandbox  --disable-hang-monitor --single-process
# record the trace. --single-process is optional, see below. The other flags are required.
rr replay # This will replay the last trace
(gdb)       # rr uses GDB to let you replay traces

De manera conveniente, la sincronización y las direcciones del puntero se mantienen iguales cada vez que vuelves a reproducir el mismo seguimiento. Los registros se pueden convertir en portátiles con rr pack, de modo que puedas copiarlos en otra máquina y volver a reproducirlos allí o reproducirlos incluso después de volver a compilarlos. Ejecuta el programa con continue. Puedes usar todos los comandos de GDB normales -b, next, watch, etc. Sin embargo, también puedes usar reverse-next (rn), reverse-cont (rc), reverse-step (rs) y reverse-fin.

Estas seguirán respetando los puntos de interrupción que hayas establecido. Por ejemplo:

(gdb) c  # Execute to the end
(gdb) break blink::LayoutFlexibleBox::UpdateLayout
(gdb) rc # Run back to the last layout call
Thread 5 hit Breakpoint 1, blink::LayoutBlock::UpdateLayout (
    this=0x121672224010)
(gdb) # Inspect anything you want here. To find the previous Layout call on this object:
(gdb) cond 1 this == 0x121672224010
(gdb) rc
Thread 5 hit Breakpoint 1, blink::LayoutBlock::UpdateLayout (
    this=0x121672224010)
(gdb) watch -l style_.ptr_ # Or find the last time the style_ was changed
(gdb) rc
Thread 5 hit Hardware watchpoint 2: -location style_.ptr_

Old value = (const blink::ComputedStyle *) 0x1631ad3dbb0
New value = (const blink::ComputedStyle *) 0x0
0x00007f68cabcf78e in std::__Cr::swap<blink::ComputedStyle const*> (

En este ejemplo, usé --single-process para simplificar, pero no es necesario. RR puede hacer un seguimiento de varios procesos. Después del registro, puedes ver una lista con rr ps y elegir uno para volver a reproducirlo con rr replay -f PID.

RR puede ser útil de muchas maneras. Hay otros comandos que puedes usar, como cuándo averiguar el número de evento en el que te encuentras o rr replay -M para anotar stdout con un ID de proceso y un número de evento para cada línea. Consulta el sitio web y la documentación de RR para obtener más detalles.