Hace unos días escribí sobre Rewind VM, una máquina virtual determinista donde cada ejecución de una construcción Nix es una función pura de sus entradas, incluida la programación de hilos. He estado usando para encontrar, reproducir y resolver muchas condiciones de carrera en construcciones Nix, pero me está haciendo sentir un poco loco. ¿Qué es esta superpoder? ¿Y por qué nadie más la usa?
La herramienta ha crecido rápidamente con un panel de código fuente, marcos de pila, marcadores, una pestaña "Compare", mucho más soporte para GDB, carriles de hilos que muestran quién tuvo la CPU en cada paso, y "Check from here", que ayuda a encontrar el paso exacto donde ocurre una condición de carrera. Entré en cada nueva función pensando que sería un gran levantamiento de código, pero continuamente encontré lo mismo: la parte difícil de cada función ya estaba hecha, y Nix ya lo había hecho.
Por ejemplo, un depurador necesita las entradas exactas del programa, sus símbolos de depuración, su código fuente, el código fuente de todas las bibliotecas debajo de él, y una manera de que alguien más pueda obtener todo eso en su máquina. Eso es exactamente lo que hace una derivación, y Nix hace que sea fácil.
Dos cajeros, una cuenta
Un ejemplo clásico simple de una condición de carrera es dos hilos depositando en una cuenta bancaria. Cada depósito lee el balance, escribe una línea en el libro mayor, luego almacena el balance más el depósito. Si un cajero se ejecuta entre la lectura y el almacenamiento del otro, escribe un balance obsoleto sobre los depósitos del otro.
static void deposit(int teller, long amount){long seen = balance;char line[64];int n = snprintf(line, sizeof line,"teller %d: %ld + %ld\n",teller, seen, amount);if (write(1, line, n) != n)return;balance = seen + amount;}
Un cajero que se ejecuta entre la lectura y el almacenamiento del otro escribe un balance obsoleto sobre los depósitos del otro.
11
En mi portátil de 16 núcleos, el banco perdió dinero en 396 de 1.000 ejecuciones. Fijado a un solo núcleo con taskset, no perdió dinero en ninguna de 1.000: un solo núcleo rara vez cambia hilos en medio de un depósito, por eso Rewind perturba el horario.
La VM de Rewind tiene una sola CPU, así que su primera ejecución pasa demasiado bien. rewind check vuelve a ejecutar la construcción bajo horarios perturbados, cada uno pidiendo al kernel del guest que reprograma en pasos diferentes, y reduce el primer fallo a un solo paso:
$ rewind check --where github:fzakaria/rewindvm#bank...step 3237 decide: una reprogramación allí hace que la ejecución falle
passing: run 12feb5f832205a72, schedule 0
failing: run c2f1cfac0c288f3e, schedule 1 over steps 3237..3238
Las dos ejecuciones son la misma máquina, salida por salida, hasta el paso 3237, donde solo el que falla recibe una reprogramación.
Cosas gratis uno: las entradas
El argumento de check es una derivación y puede ser una referencia flake. La belleza de Nix es que conoce las entradas de una derivación, por eso es todo lo que necesitamos para asegurar que la ejecución es reproducible. Las entradas de la derivación son la fuente, el compilador, las bibliotecas, el kernel, y la configuración de la VM. Rewind nix realiza las entradas de la derivación, empaqueta el cierre en una imagen erofs de solo lectura, y arranca la VM en ella. El ID de la ejecución es un hash de sus entradas, de la misma manera que una ruta de store, y rewind show imprime el comando que la vuelve a hacer:
$ rewind show 12feb5f8
rewind nix /nix/store/cr8rl40rjb9cmmcd9sxn9mpdrhvp53jj-bank-0.1.0.drv --epoch --clock branches --name bank-0.1.0 # 12feb5f832205a72
Cuando la construcción tiene éxito, el guest informa el hash NAR de cada salida, y rewind nix lo compara con la copia del host y con cada caché binaria de sustitución de Nix, fetchando solo el .narinfo:
$ rewind nix github:fzakaria/rewindvm#bank.../nix/store/27a4q7w90jzp4wg5kaaajvb64zll6yqg-bank-0.1.0 dbf0df3de84b7a84 matches your store
Esto nos ayuda a validate que la construcción dentro de la VM es la misma que en mi portátil, y que la ejecución de la VM es reproducible por Nix.
Fuente: Hacker News · Resumido por HeadlinesBriefing