Domanda

Abbiamo una base di codice vecchia di diversi anni e tutti gli sviluppatori originali se ne sono andati da tempo.Utilizza molti, molti thread, ma senza un design apparente o principi architettonici comuni.Ogni sviluppatore aveva il proprio stile di programmazione multithread, quindi alcuni thread comunicano tra loro utilizzando le code, alcuni bloccano i dati con mutex, altri bloccano con semafori, alcuni utilizzano meccanismi IPC del sistema operativo per le comunicazioni intra-processo.Non esiste documentazione di progettazione e i commenti sono scarsi.È un disastro e sembra che ogni volta che proviamo a rifattorizzare il codice o ad aggiungere nuove funzionalità, introduciamo deadlock o altri problemi.

Quindi, qualcuno conosce strumenti o tecniche che potrebbero aiutare ad analizzare e documentare tutte le interazioni tra i thread?FWIW, la base del codice è C++ su Linux, ma sarei interessato a conoscere strumenti per altri ambienti.


Aggiornamento

Apprezzo le risposte ricevute finora, ma speravo in qualcosa di più sofisticato o sistematico dei consigli che sono essenzialmente "Aggiungi messaggi di registro, capire cosa sta succedendo e risolverlo". Ci sono molti strumenti là fuori per analizzare e documentare il flusso di controllo nei programmi a thread singolo;non c'è nulla disponibile per i programmi multi-thread?


Guarda anche Debug di applicazioni multithread

È stato utile?

Soluzione

Investi in una copia di Intel VTune e i suoi strumenti di profilazione del thread.Ti fornirà sia una visione a livello di sistema che di origine del comportamento del thread.Certamente non documenterà automaticamente la cosa per te, ma dovrebbe essere un vero aiuto almeno per visualizzare ciò che sta accadendo in diverse circostanze.

Penso che ci sia una versione di prova che puoi scaricare, quindi potrebbe valere la pena provarla.Ho usato solo la versione per Windows, ma guardando la pagina web di VTune c'è anche una versione per Linux.

Altri suggerimenti

Come punto di partenza, sarei tentato di aggiungere messaggi di registro di tracciamento in punti strategici all'interno dell'applicazione.Ciò ti consentirà di analizzare come i tuoi thread interagiscono senza il pericolo che l'atto di osservare i thread cambi il loro comportamento (come potrebbe essere il caso con il debug passo dopo passo).La mia esperienza è con la piattaforma .NET e il mio strumento di registrazione preferito sarebbe log4net poiché è gratuito, ha ampie opzioni di configurazione e, se sei attento nel modo in cui implementi la registrazione, non ostacolerà notevolmente le prestazioni della tua applicazione.In alternativa, è presente la classe Debug (o Trace) incorporata di .NET nello spazio dei nomi System.Diagnostics.

Mi concentrerei prima sui blocchi della memoria condivisa (i mutex e i semafori) poiché è molto probabile che causino problemi.Osserva quale stato è protetto dai blocchi e quindi determina quale stato è protetto da diversi blocchi.Questo ti darà un senso di potenziali conflitti.Guarda le situazioni in cui il codice che contiene un blocco richiama i metodi (non dimenticare i metodi virtuali).Cercare di eliminare queste chiamate ove possibile (riducendo il tempo di mantenimento della serratura).

Dato l'elenco dei mutex conservati e un'idea approssimativa dello stato che proteggono, assegnare un ordine di blocco (ovvero, il mutex A dovrebbe sempre essere preso prima del mutex B).Prova ad applicarlo nel codice.

Verifica se riesci a combinare diversi blocchi in uno solo se la concorrenza non verrà influenzata negativamente.Ad esempio, se i mutex A e B sembrano avere dei deadlock e uno schema di ordinamento non è facile da realizzare, combinali inizialmente in un unico lock.

Non sarà facile, ma sono favorevole a semplificare il codice a scapito della concorrenza per gestire il problema.

Questo è un problema davvero difficile per gli strumenti automatizzati.Potresti voler esaminare verifica del modello il tuo codice.Non aspettarti risultati magici:i controllori di modelli sono molto limitati nella quantità di codice e nel numero di thread che possono controllare efficacemente.

Uno strumento che potrebbe funzionare per te è SCACCHI (anche se sfortunatamente è solo per Windows). RAFFICA è un altro strumento abbastanza potente, ma è molto difficile da usare e potrebbe non gestire il C++.Wikipedia elenca anche Vapore, di cui non ho mai sentito parlare prima, ma sembra che potrebbe funzionare per te:

StEAM è un controllo modello per C++.Rileva deadlock, errori di segmentazione, variabili fuori intervallo e loop che non terminano.

In alternativa, probabilmente sarebbe di grande aiuto provare a far convergere il codice verso un numero limitato di schemi di sincronizzazione ben definiti (e, preferibilmente, di alto livello).Mescolare serrature, semafori e monitor nella stessa base di codice crea problemi.

Una cosa da tenere a mente quando si utilizza log4net o uno strumento simile è che modificano i tempi dell'applicazione e spesso possono nascondere le condizioni di gara sottostanti.Avevamo del codice scritto male per il debug e abbiamo introdotto il logging e questo ha effettivamente rimosso le condizioni di competizione e i deadlock (o ne ha ridotto notevolmente la frequenza).

In Java, hai scelte come FindBugs (per l'analisi statica del bytecode) per trovare determinati tipi di sincronizzazione incoerente o i numerosi analizzatori di thread dinamici di aziende come Coverity, JProbe, OptimizeIt, ecc.

UML non può aiutarti qui?

Se esegui il reverse engineering della tua base di codice in UML, dovresti essere in grado di disegnare diagrammi di classe che mostrano le relazioni tra le tue classi.Partendo dalle classi i cui metodi sono i punti di ingresso del thread, puoi vedere quale thread utilizza quale classe.In base alla mia esperienza con Rosa razionale, ciò potrebbe essere ottenuto utilizzando il trascinamento della selezione;se non esiste alcuna relazione tra la classe aggiunta e quelle precedenti, allora la classe aggiunta non viene utilizzata direttamente dal thread iniziato con il metodo con cui hai iniziato il diagramma.Questo dovrebbe darti suggerimenti sul ruolo di ciascun thread.

Ciò mostrerà anche gli "oggetti dati" condivisi e gli oggetti specifici del thread.

Se disegni un grande diagramma di classi e rimuovi tutti gli "oggetti dati", dovresti essere in grado di strutturare quel diagramma come nuvole, ciascuna nuvola essendo un thread - o un gruppo di thread, a meno che l'accoppiamento e la coesione della base di codice non siano Terribile.

Questo ti darà solo una parte del puzzle, ma potrebbe essere utile;Spero solo che la tua codebase non sia troppo confusa o troppo "procedurale", nel qual caso...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top