Pergunta

Temos uma base de código com vários anos e todos os desenvolvedores originais já se foram.Ele usa muitos, muitos threads, mas sem nenhum design aparente ou princípios arquitetônicos comuns.Cada desenvolvedor tinha seu próprio estilo de programação multithread, então alguns threads se comunicam entre si usando filas, alguns bloqueiam dados com mutexes, alguns bloqueiam com semáforos, alguns usam mecanismos IPC do sistema operacional para comunicações intraprocessos.Não há documentação de design e os comentários são escassos.É uma bagunça e parece que sempre que tentamos refatorar o código ou adicionar novas funcionalidades, introduzimos impasses ou outros problemas.

Então, alguém conhece alguma ferramenta ou técnica que ajudaria a analisar e documentar todas as interações entre threads?FWIW, a base de código é C++ no Linux, mas estou interessado em ouvir sobre ferramentas para outros ambientes.


Atualizar

Agradeço as respostas recebidas até agora, mas esperava algo mais sofisticado ou sistemático do que conselhos que é essencialmente "adicione mensagens de log, descubra o que está acontecendo e conserte -o". Existem muitas ferramentas por aí para analisar e documentar o fluxo de controle em programas de thread único;não há nada disponível para programas multithread?


Veja também Depurando aplicativos multithread

Foi útil?

Solução

Invista em uma cópia do Intel VTune e suas ferramentas de perfil de thread.Ele lhe dará uma visão do sistema e do nível de origem do comportamento do thread.Certamente não irá documentar automaticamente a coisa para você, mas deve ser uma ajuda real, pelo menos, para visualizar o que está acontecendo em diferentes circunstâncias.

Acho que existe uma versão de teste que você pode baixar, então pode valer a pena tentar.Usei apenas a versão Windows, mas olhando a página do VTune também tem uma versão Linux.

Outras dicas

Como ponto de partida, ficaria tentado a adicionar mensagens de log de rastreamento em pontos estratégicos do seu aplicativo.Isso permitirá que você analise como seus threads estão interagindo sem o perigo de que o ato de observar os threads altere seu comportamento (como poderia ser o caso da depuração passo a passo).Minha experiência é com a plataforma .NET e minha ferramenta de registro preferida seria o log4net, pois é gratuito, tem amplas opções de configuração e, se você for sensato na forma como implementa seu registro, isso não prejudicará visivelmente o desempenho do seu aplicativo.Como alternativa, existe a classe Debug (ou Trace) integrada do .NET no namespace System.Diagnostics.

Eu me concentraria primeiro nos bloqueios de memória compartilhada (os mutexes e semáforos), pois eles têm maior probabilidade de causar problemas.Observe qual estado está sendo protegido por bloqueios e então determine qual estado está sob a proteção de vários bloqueios.Isso lhe dará uma noção de conflitos potenciais.Veja situações em que o código que contém um bloqueio chama métodos (não se esqueça dos métodos virtuais).Tente eliminar essas chamadas sempre que possível (reduzindo o tempo de bloqueio).

Dada a lista de mutexes mantidos e uma ideia aproximada do estado que eles protegem, atribua uma ordem de bloqueio (ou seja, o mutex A deve sempre ser obtido antes do mutex B).Tente impor isso no código.

Veja se você pode combinar vários bloqueios em um se a simultaneidade não for afetada negativamente.Por exemplo, se parecer que os mutex A e B podem ter conflitos e um esquema de ordenação não for fácil de executar, combine-os inicialmente em um bloqueio.

Não vai ser fácil, mas sou a favor de simplificar o código em detrimento da simultaneidade para resolver o problema.

Este é um problema realmente difícil para ferramentas automatizadas.Você pode querer investigar verificação de modelo seu código.Não espere resultados mágicos:os verificadores de modelo são muito limitados na quantidade de código e no número de threads que podem verificar com eficácia.

Uma ferramenta que pode funcionar para você é XADREZ (embora infelizmente seja apenas para Windows). EXPLOSÃO é outra ferramenta bastante poderosa, mas é muito difícil de usar e pode não lidar com C++.A Wikipédia também lista Vapor, do qual nunca ouvi falar antes, mas parece que pode funcionar para você:

StEAM é um verificador de modelo para C++.Ele detecta deadlocks, falhas de segmentação, variáveis ​​fora de faixa e loops sem terminação.

Alternativamente, provavelmente ajudaria muito tentar convergir o código para um pequeno número de esquemas de sincronização bem definidos (e, de preferência, de alto nível).Misturar bloqueios, semáforos e monitores na mesma base de código é causar problemas.

Uma coisa a ter em mente ao usar o log4net ou uma ferramenta semelhante é que eles alteram o tempo do aplicativo e muitas vezes podem ocultar as condições de corrida subjacentes.Tínhamos alguns códigos mal escritos para depurar e introduzimos o log e isso realmente removeu condições de corrida e impasses (ou reduziu bastante sua frequência).

Em Java, você tem opções como FindBugs (para análise de bytecode estático) para encontrar certos tipos de sincronização inconsistente ou muitos analisadores de threads dinâmicos de empresas como Coverity, JProbe, OptimizeIt, etc.

A UML não pode ajudá-lo aqui?

Se você fizer engenharia reversa de sua base de código em UML, então você poderá desenhar diagramas de classes que mostram os relacionamentos entre suas classes.Começando pelas classes cujos métodos são os pontos de entrada do thread, você pode ver qual thread usa qual classe.Com base na minha experiência com Rosa Racional, isso pode ser conseguido usando arrastar e soltar;se não houver relacionamento entre a classe adicionada e as anteriores, então a classe adicionada não será usada diretamente pelo thread que iniciou com o método com o qual você iniciou o diagrama.Isso deve fornecer dicas sobre a função de cada thread.

Isso também mostrará os "objetos de dados" que são compartilhados e os objetos específicos do thread.

Se você desenhar um grande diagrama de classes e remover todos os "objetos de dados", deverá ser capaz de organizar esse diagrama como nuvens, cada nuvem sendo um thread - ou um grupo de threads, a menos que o acoplamento e a coesão da base de código sejam horrível.

Isso lhe dará apenas uma parte do quebra-cabeça, mas pode ser útil;Só espero que sua base de código não seja muito turva ou muito "procedimental", nesse caso...

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top