ti-enxame.com

Como faço para testar o código multithread da unidade?

Existem maneiras de testar seu código multithread em condições de corrida e impasses?

Para ver se eles estão executando da maneira que deveriam ...

34
Tamara Wijsman

XADREZ , um projeto da Microsoft Research. Citando seu site:

O CHESS é uma ferramenta para encontrar e reproduzir Heisenbugs em programas concorrentes. O CHESS executa repetidamente um teste simultâneo, garantindo que cada execução faça uma intercalação diferente. Se uma intercalação resultar em um erro, o CHESS poderá reproduzir a intercalação para melhorar a depuração. O CHESS está disponível para programas gerenciados e nativos.

Atualização (23/09/2015): para C, C++ e Go, você pode usar ThreadSanitizer .

19
Josh Kelley

Valgrind possui Helgrind o que realmente ajuda. Além de ajudar a apontar corridas que podem levar à fome ou a um impasse, a ligeira desaceleração do perfil do programa às vezes expõe corridas que podem não ser vistas de outra maneira.

Portanto, mesmo se você usar um método sem bloqueio, ele ainda ajudará :)

É centrado no POSIX, no entanto. Ele é fornecido com cabeçalhos que facilmente tornam as bibliotecas simples de teste de unidade como a TAP cientes de que está sendo executado, o que também é realmente útil. Por exemplo, você pode ter um encadeamento que normalmente não bloqueia ao tentar obter um bloqueio, vá em frente e bloqueie (talvez aleatoriamente), apenas para simular a fome.

10
Tim Post

Não me lembro exatamente dos detalhes, mas essa é a ideia geral. E fiz isso apenas uma vez, mas o que fiz foi separar o código reentrante do código que executava a tarefa, usando uma interface para poder zombar da classe da tarefa.

Em seguida, projetei meu modelo para poder bloquear uma chamada, para que eu saiba que o encadeamento está na seção crítica e, em seguida, chame-o novamente e verifique se está aguardando, antes de liberar o primeiro encadeamento e concluir de forma limpa.

Algo parecido.

Não tenho certeza se isso funcionaria em cenários mais complexos, mas ajuda a preservar o comportamento durante as refatorações.

5
Roger C S Wernersson

No JAOO/GOTO deste ano, vi esta apresentação:

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

O truque é que você modele o que seu aplicativo hairball deve fazer, em termos de etapas de chamada , bem como das operações reais em seu aplicativo. O software John Hughes então tenta sistematicamente muitas permutações de etapas de chamada repetidamente em paralelo paralelo e verifica posteriormente se o estado do aplicativo corresponde ao estado do modelo. Se um erro for encontrado, o software sabe como reduzir as etapas para o mínimo caso que produz o erro.

Ele demonstrou ao vivo como capturar vários bugs nas principais bibliotecas Erlang, que estavam à espreita há 15 anos e ocasionalmente relatavam, mas ninguém conseguia descobrir de onde eles vinham e, portanto, como consertar. Com os casos mínimos relatados pelo software, o mantenedor da biblioteca conseguiu corrigir cada bug em um dia .

Foi SO impressionante.

John Hughes vende este software através de sua empresa.

2
user1249
  1. Testes com resultados não reproduzíveis são inúteis. Isso exclui testes completamente aleatórios, mas sai em testes gerados a partir de sequências pseudo-aleatórias.
  2. Todo ator em um ambiente simultâneo possui componentes algorítmicos ou não concorrentes que podem ser testados por meios convencionais. Após testá-los, todas as falhas restantes devem estar na lógica de simultaneidade.
  3. Os eventos em um sistema simultâneo são sempre de fato uma sequência linear de eventos. Se precisão suficiente é usada para medir o tempo, não há eventos acontecendo "ao mesmo tempo". Isso significa que os atores em um sistema simultâneo podem ser testados gerando eventos sequencialmente. A captura da sequência de eventos no momento da falha de um sistema simultâneo fornece os casos de teste necessários.
  4. O código que fornece aos atores agilidade (threads) é mais frequentemente do que não fornecido pelo sistema operacional ou pelas bibliotecas do sistema. É seguro supor que o referido código não precise ser testado. O código responsável pela comunicação e sincronização é normalmente escrito pelo programador de aplicativos. Esse código pode ser testado sem chamar o código do sistema, ou seja, sem ativar nenhum encadeamento.
  5. As condições de limite no código algorítmico (fila vazia) geralmente exigem manipulação no código de sincronização, e esse é um bom alvo para teste.
  6. A definição de proxies em torno do código do sistema (t.wait ()) permite o uso de stubs/zombarias da funcionalidade durante o teste.
2
Apalala

Você pode tentar o meu Relacy Race Detector . Ele foi projetado para verificar com cuidado e precisão os algoritmos de sincronização, como filas produtor-consumidor e contêineres simultâneos, mas não muito adequado para a verificação de programas inteiros. No entanto, talvez seja uma boa idéia espalhar a sincronização e os mutexes por todo o programa, mas concentre a sincronização em componentes especializados (que podem ser verificados com o Relacy).

1
Dmitry Vyukov

Não é fácil, mas basicamente a única maneira é chamar o código multithread simultaneamente de vários threads e alterar o tempo e a ordem aleatoriamente jogando com as funções aleatórias Thread.sleep() e Thread.yield() chama (assumindo Java).

Também existem ferramentas prontas disponíveis (como o TestNG) que fazem algo como o descrito acima, mas elas ainda não estão muito maduras, até onde eu sei.

0
Joonas Pulakka

Não é um teste de unidade rigoroso, mas uma verificação de tempo de execução que me ajudou com alguns testes com falhas intermitentes. É rápido e sujo, mas funcionou.

Quando um mutex é concedido, mantenho um registro de qual thread o possui. Todas as solicitações de mutex têm um tempo limite de trinta segundos, após o qual elas geram um impasse.

Posso usar a lista de mutexes concedidos para ver qual thread está mantendo o mutex de bloqueio e por quê por tanto tempo. Nos meus casos até agora, é porque estava em um impasse em outra coisa, para que eu possa resolver essa situação.

Isso funcionou para mim porque meus mutexes têm uma classe de wrapper de plataforma cruzada, facilitando a injeção de manutenção de registros e tempo limite. Eu também sabia o suficiente sobre o aplicativo para saber que ele nunca deveria estar bloqueando um mutex por 30 segundos.

Pode não ser de propósito geral, mas economiza muita depuração por cerca de duas horas no esforço de programação. A sobrecarga é insignificante e pode ser apenas de depuração.

Vou examiná-lo para registrar sequências de solicitação mutex aninhadas e ver se há alguma potencialmente induzindo um deadlock (por exemplo, um encadeamento bloqueia A, em seguida, B e outro bloqueia B, em seguida, A), em vez de apenas induzir o bloqueio, mas até agora tem sido um grande benefício para um esforço trivial.

0
Andy Krouwel