it-swarm.it

Come posso testare il codice multi-thread?

Ci sono modi per testare l'unità del tuo codice multi-thread per condizioni di gara e deadlock?

Per vedere se si comportano come dovrebbero essere ...

34
Tamara Wijsman

CHESS , un progetto di Microsoft Research. Citando il loro sito:

CHESS è uno strumento per trovare e riprodurre Heisenbugs in programmi simultanei. CHESS esegue ripetutamente un test simultaneo assicurando che ogni esecuzione abbia un diverso interfogliamento. Se un interleaving provoca un errore, CHESS può riprodurre l'interleaving per migliorare il debug. CHESS è disponibile sia per i programmi gestiti che per quelli nativi.

Aggiornamento (23/09/2015): per C, C++ e Go, puoi usare ThreadSanitizer .

19
Josh Kelley

Valgrind ha Helgrind che aiuta davvero. Non solo aiuta a sottolineare le razze che potrebbero portare alla fame o allo stallo, ma il leggero rallentamento della profilazione del programma a volte espone razze che altrimenti non potrebbero essere viste.

Quindi, anche se vai a commando con una sorta di metodo di blocco libero, aiuta ancora :)

È centrato su POSIX, tuttavia. Viene fornito con intestazioni che rendono facilmente semplici librerie di unit test come TAP che è in esecuzione, il che è anche molto utile. Ad esempio, potresti avere un thread che normalmente non si bloccherebbe quando provi ad acquisire un blocco vai avanti e blocchi (forse in modo casuale), solo per simulare la fame.

10
Tim Post

Non ricordo esattamente i dettagli, ma questa è l'idea generale. E l'ho fatto solo una volta, ma quello che ho fatto è stato separare il codice di rientro dal codice che esegue l'attività, usando un'interfaccia per essere in grado di deridere la classe di attività.

Quindi ho progettato il mio mock-up per poter bloccare una chiamata in modo da sapere che il thread si trova nella sezione critica, quindi richiamarlo e verificare che sia in attesa, prima di rilasciare il primo thread e terminare in modo pulito.

Qualcosa del genere.

Non sono sicuro che funzionerebbe per scenari più complessi, ma aiuta a preservare il comportamento durante i refactoring.

5

A JAOO/GOTO quest'anno ho visto questa presentazione:

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

Il trucco sta nel modellare ciò che la tua applicazione di hairball dovrebbe fare, in termini di passaggi di invocazione nonché delle operazioni effettive sulla tua applicazione. Il software John Hughes quindi prova sistematicamente molte permutazioni delle fasi di invocazione ripetutamente in parallelo e verifica successivamente che lo stato dell'applicazione corrisponda allo stato del modello. Se viene rilevato un errore, il software sa come ridurre i passaggi al minimo caso producendo l'errore.

Dimostrò dal vivo come catturare diversi bug nelle biblioteche di base di Erlang che erano in agguato da 15 anni e che occasionalmente riferivano, ma nessuno poteva capire da dove venissero e quindi come risolvere. Con i casi minimi segnalati dal software, il manutentore della libreria è stato in grado di correggere ogni bug entro un giorno .

Era SO impressionante.

John Hughes vende questo software attraverso la sua azienda.

2
user1249
  1. I test con risultati non riproducibili sono inutili. Ciò esclude i test completamente casuali, ma lascia nei test generati da sequenze pseudo-casuali.
  2. Ogni attore in un ambiente concorrente ha componenti algoritmici o altrimenti non concorrenti che possono essere testati con mezzi convenzionali. Dopo averli testati, tutti gli errori rimanenti devono risiedere nella logica della concorrenza.
  3. Gli eventi in un sistema concorrente sono sempre in realtà una sequenza lineare di eventi. Se si utilizza una precisione sufficiente per misurare il tempo, allora non si verificano eventi "contemporaneamente". Ciò significa che gli attori di un sistema concorrente possono essere testati generando eventi in sequenza. Catturare la sequenza di eventi attorno al momento del fallimento di un sistema concorrente fornisce i casi di test richiesti.
  4. Il codice che fornisce agli attori vivacità (thread) è il più delle volte fornito dal sistema operativo o dalle librerie di sistema. È lecito ritenere che detto codice non debba essere testato. Il codice responsabile della comunicazione e della sincronizzazione è normalmente scritto dal programmatore delle applicazioni. Quel codice può essere testato senza invocare il codice di sistema, cioè senza avviare alcun thread.
  5. Le condizioni al contorno nel codice algoritmico (coda vuota) richiedono spesso la gestione nel codice di sincronizzazione e questo è un buon obiettivo per i test.
  6. La definizione di proxy attorno al codice di sistema (t.wait ()) consente l'uso di stub/mock della funzionalità durante i test.
2
Apalala

Puoi provare il mio Relacy Race Detector . È progettato per verificare con cura e precisione algoritmi di sincronizzazione come code produttore-consumatore e contenitori simultanei, ma non molto adatto per la verifica di interi programmi. Tuttavia, forse è una buona idea diffondere la sincronizzazione e mutex allo stesso modo in un programma, ma concentrare invece la sincronizzazione in componenti specializzati (che possono essere verificati con Relacy).

1
Dmitry Vyukov

Non è facile, ma sostanzialmente l'unico modo è chiamare contemporaneamente il codice multi-thread da più thread e cambiare i tempi e l'ordinamento in modo casuale giocando con Thread.sleep() e Thread.yield() chiama (presupponendo Java).

Ci sono anche strumenti pronti disponibili (come TestNG) che fanno qualcosa come descritto sopra, ma non sono ancora molto maturi, per quanto ne so.

0
Joonas Pulakka

Non un test unitario rigoroso, ma un controllo di runtime che mi ha aiutato con alcuni test falliti a intermittenza. È veloce e sporco, ma ha funzionato.

Quando viene concesso un mutex tengo traccia di quale thread lo ha. Tutte le richieste di mutex hanno un timeout di trenta secondi, dopo di che urlano deadlock.

Posso quindi utilizzare l'elenco dei mutex concessi per vedere quale thread contiene il mutex di blocco e perché per così tanto tempo. Nei miei casi finora è perché era bloccato su qualcos'altro, quindi posso quindi risolvere quella situazione.

Questo ha funzionato per me perché i miei mutex hanno una classe wrapper multipiattaforma che rende facile iniettare la registrazione e il timeout. Sapevo anche abbastanza dell'applicazione da sapere che non avrebbe mai dovuto essere bloccato su un mutex per 30 secondi.

Potrebbe non essere uno scopo completamente generale, ma consente di risparmiare un sacco di debug per circa un paio di ore di programmazione. L'overhead è trascurabile e può essere solo di debug-build.

Ho intenzione di estenderlo per registrare sequenze di richieste di mutex nidificate e vedere se qualcuno sta potenzialmente inducendo deadlock (ad es. Un thread blocca A quindi B e un altro blocca B quindi A) piuttosto che indurre solo deadlock, ma finora è stato un grande vantaggio per uno sforzo insignificante.

0
Andy Krouwel