it-swarm-es.com

¿Cómo pruebo un código multiproceso unitario?

¿Hay formas de probar su código de subprocesos múltiples en busca de condiciones de carrera y puntos muertos?

Para ver si se están desempeñando como deberían ser ...

34
Tamara Wijsman

AJEDREZ , un proyecto de Microsoft Research. Citando su sitio:

AJEDREZ es una herramienta para encontrar y reproducir Heisenbugs en programas concurrentes. CHESS ejecuta repetidamente una prueba concurrente asegurando que cada ejecución tome un entrelazado diferente. Si un intercalado resulta en un error, CHESS puede reproducir el intercalado para mejorar la depuración. CHESS está disponible para programas administrados y nativos.

Actualización (23/09/2015): Para C, C++ y Go, puede usar ThreadSanitizer .

19
Josh Kelley

Valgrind tiene Helgrind que realmente ayuda. No solo ayuda a señalar las razas que podrían conducir a la inanición o el punto muerto, sino que la ligera desaceleración de tener el programa perfilado a veces expone razas que de otra manera no se verían.

Entonces, incluso si usa un comando con algún tipo de método sin bloqueo, aún ayuda :)

Sin embargo, está centrado en POSIX. Se entrega con encabezados que facilitan la creación de bibliotecas de pruebas unitarias simples como TAP, conscientes de que se está ejecutando, lo que también es realmente útil. Por ejemplo, podría tener un hilo que normalmente no se bloquearía al intentar adquirir un bloqueo, continúe y bloquee (quizás al azar), solo para simular el hambre.

10
Tim Post

No recuerdo los detalles exactamente, pero esta es la idea general. Y solo lo hice una vez, pero lo que hice fue separar el código entrante del código que realizaba la tarea, usando una interfaz para poder burlarse de la clase de tarea.

Luego diseñé mi maqueta para poder bloquear una llamada de modo que sepa que el hilo está en la sección crítica, y luego vuelva a llamarlo y verifique que esté esperando, antes de liberar el primer hilo y terminar limpiamente.

Algo como eso.

No estoy seguro de que funcione para escenarios más complejos, pero ayuda a preservar el comportamiento durante las refactorizaciones.

5

En JAOO/GOTO este año vi esta presentación:

http://gotocon.com/aarhus-2010/presentation/Testing%20Asynchronous%20Behaviour%20in%20an%20Instant%20Messaging%20Server

El truco es que modela lo que se supone que debe hacer tu aplicación de hairball, en términos de pasos de invocación , así como las operaciones reales en tu aplicación. El software John Hughes intenta sistemáticamente muchas permutaciones de los pasos de invocación repetidamente en paralelo y comprueba luego que el estado de la aplicación corresponde con el estado del modelo. Si se encuentra un error, el software sabe cómo reducir los pasos al caso mínimo que produce el error.

Él demostró en vivo cómo detectar varios errores en las bibliotecas principales de Erlang que habían estado al acecho durante 15 años y ocasionalmente informaban, pero nadie podía averiguar de dónde venían y, por lo tanto, cómo solucionarlo. Con los casos mínimos reportados por el software, el encargado de la biblioteca pudo corregir cada error dentro de un día .

Fue SO impresionante).

John Hughes vende este software a través de su empresa.

2
user1249
  1. Las pruebas con resultados no reproducibles son inútiles. Eso descarta las pruebas completamente aleatorias, pero deja en las pruebas generadas a partir de secuencias pseudoaleatorias.
  2. Cada actor en un entorno concurrente tiene componentes algorítmicos o de otro tipo que no son de concurrencia que se pueden probar por medios convencionales. Una vez probados, cualquier falla restante debe estar en la lógica de concurrencia.
  3. Los eventos en un sistema concurrente siempre son, de hecho, una secuencia lineal de eventos. Si se usa suficiente precisión para medir el tiempo, entonces no hay eventos que sucedan "al mismo tiempo". Eso significa que los actores en un sistema concurrente se pueden probar generando eventos secuencialmente. Capturar la secuencia de eventos en el momento del fallo de un sistema concurrente proporciona los casos de prueba requeridos.
  4. El código que proporciona vivacidad (hilos) a los actores es proporcionado por el sistema operativo o por las bibliotecas del sistema. Es seguro asumir que dicho código no necesita ser probado. El código a cargo de la comunicación y sincronización normalmente lo escribe el programador de aplicaciones. Ese código se puede probar sin invocar el código del sistema, es decir, sin iniciar ningún subproceso.
  5. Las condiciones de contorno en el código algorítmico (cola vacía) a menudo requieren manejo en el código de sincronización, y ese es un buen objetivo para las pruebas.
  6. La definición de proxies en torno al código del sistema (t.wait ()) permite el uso de stubs/simulacros de la funcionalidad durante las pruebas.
2
Apalala

Puedes probar mi Detector de raza relacy . Está diseñado para verificar cuidadosamente y con precisión los algoritmos de sincronización, como las colas productor-consumidor y los contenedores concurrentes, pero no es muy adecuado para la verificación de programas completos. Sin embargo, tal vez sea una buena idea distribuir la sincronización y las mutexes para asignar un programa de todos modos, sino concentrar la sincronización en componentes especializados (que se pueden verificar con Relacy).

1
Dmitry Vyukov

No es fácil, pero básicamente la única forma es llamar al código multiproceso simultáneamente de múltiples hilos y cambiar el tiempo y el orden al azar jugando con el azar Thread.sleep() y Thread.yield() llamadas (suponiendo Java).

También hay herramientas listas disponibles (como TestNG) que hacen algo como se describió anteriormente, pero aún no son muy maduras, que yo sepa.

0
Joonas Pulakka

No es una prueba unitaria estricta, sino una verificación de tiempo de ejecución que me ha ayudado con algunas pruebas fallidas intermitentes. Es rápido y sucio, pero funcionó.

Cuando se otorga un mutex, mantengo un registro de qué hilo lo tiene. Todas las solicitudes de mutex tienen un tiempo de espera de treinta segundos, después de lo cual gritan un punto muerto.

Entonces puedo usar la lista de mutexes otorgados para ver qué hilo contiene el mutex de bloqueo y por qué durante tanto tiempo. En mis casos hasta ahora es porque estaba bloqueado en otra cosa, por lo que puedo solucionar esa situación.

Esto funcionó para mí porque mis mutexes tienen una clase de contenedor multiplataforma que hace que sea fácil inyectar el mantenimiento de registros y el tiempo de espera. También sabía lo suficiente sobre la aplicación para saber que nunca debería estar bloqueando un mutex durante 30 segundos.

Puede que no sea un propósito completamente general, pero ahorra mucha depuración para un esfuerzo de programación de aproximadamente un par de horas. La sobrecarga es insignificante y solo se puede depurar-construir.

Voy a ver cómo extenderlo para grabar secuencias de solicitud de mutex anidadas, y ver si algunas son potencialmente inductoras de bloqueo (por ejemplo, un hilo bloquea A y luego B y otro bloquea B y luego A) en lugar de simplemente inducir bloqueo, pero hasta ahora Ha sido un gran beneficio para el esfuerzo trivial.

0
Andy Krouwel