Testando condições de corrida multi-thread

52

Lendo os comentários para esta resposta , especificamente:

Just because you can't write a test doesn't mean it's not broken. Undefined behaviour which usually happens to work as expected (C and C++ are full of that), race conditions, potential reordering due to a weak memory model... – CodesInChaos 7 hours ago

@CodesInChaos if it cant be reproduced then the code written to 'fix' cant be tested either. And putting untested code into live is a worse crime in my opinion – RhysW 5 hours ago

... me pergunto se há boas maneiras gerais de se acionar consistentemente ocorrendo com pouca freqüência em problemas de produção causados por condições de corrida no caso de teste.

    
por Dan Neely 25.04.2013 / 16:25
fonte

6 respostas

80
Depois de ter estado nesse negócio louco desde 1978, tendo passado quase todo esse tempo em computação em tempo real incorporada, trabalhando multitarefa, multithreaded, multi-qualquer sistema, às vezes com vários processadores físicos, tendo perseguido mais do que a minha feira partilha das condições de corrida, a minha opinião é que a resposta à sua pergunta é bastante simples.

Não.

Não há uma boa maneira geral de acionar uma condição de corrida nos testes.

Sua ÚNICA esperança é projetá-los completamente fora de seu sistema.

Quando e se você achar que alguém já colocou um, você deve colocá-lo em um formigueiro, e depois reprojetar para eliminá-lo. Depois de ter projetado o seu faux pas (pronuncia-se f *** up) fora do seu sistema, você pode liberá-lo das formigas. (Se as formigas já o consumiram, deixando apenas ossos, coloque uma placa dizendo "Isto é o que acontece com as pessoas que colocam as condições de corrida no projeto XYZ!" E DEIXE-O LÁ).

    
por 25.04.2013 / 16:42
fonte
16

Se você estiver na cadeia de ferramentas ms. Ms pesquisa criou uma ferramenta que irá forçar novas interlevings para cada execução e pode recriado falhou executa seu chamado xadrez .

aqui está um vídeo mostrando-o em uso.

    
por 25.04.2013 / 17:21
fonte
15

A melhor ferramenta que eu conheço para esse tipo de problema é uma extensão do Valgrind chamado Helgrind .

Basicamente Valgrind simula um processador virtual e executa seu binário (não modificado) em cima dele, para que ele possa verificar cada acesso à memória. Usando essa estrutura, Helgrind observa as chamadas do sistema para inferir quando um acesso a uma variável compartilhada não está adequadamente protegido por um mecanismo de exclusão mútua. Dessa forma, ele pode detectar uma condição de corrida teórica, mesmo que isso não tenha acontecido.

A Intel vende uma ferramenta muito semelhante chamada Intel Inspector .

Essas ferramentas oferecem ótimos resultados, mas seu programa será consideravelmente mais lento durante a análise.

    
por 25.04.2013 / 21:25
fonte
6

Expor um bug multi-threading requer forçar diferentes threads de execução para executar seus passos em uma ordem intercalada particular. Normalmente, isso é difícil de fazer sem depurar manualmente ou manipular o código para obter algum tipo de "controle" para controlar essa intercalação. Mas a mudança de código que se comporta de maneira imprevisível muitas vezes influencia essa imprevisibilidade, portanto, é difícil automatizá-lo.

Um bom truque é descrito por Jaroslav Tulach em Projeto Prático de API : se você tiver instruções de registro no código sob questão, manipule o consumidor dessas declarações de registro (por exemplo, um pseudo-terminal injetado) para que ele aceite as mensagens de registro individuais em uma ordem específica com base em seu conteúdo. Isso permite controlar a intercalação de etapas em segmentos diferentes sem precisar adicionar nada ao código de produção que ainda não existe.

    
por 25.04.2013 / 16:37
fonte
6

Não há como ter absoluta certeza de que vários tipos de comportamento indefinido (em particular condições de corrida) não existem.

No entanto, existem várias ferramentas que mostram um bom número dessas situações. Você pode provar que um problema existe atualmente com essas ferramentas, mesmo que você não possa provar que sua correção é válida.

Algumas ferramentas interessantes para esse propósito:

Valgrind é um verificador de memória. Ele encontra vazamentos de memória, leituras de memória não inicializada, uso de ponteiros pendentes e acessos fora dos limites.

O Helgrind é um verificador de segurança de threads. Encontra condições de corrida.

Ambos funcionam por instrumentação dinâmica, ou seja, eles levam seu programa como está e o executam em um ambiente virtualizado. Isso os torna não intrusivos, mas lentos.

O UBSan é um verificador de comportamento indefinido. Ele encontra vários casos de comportamento indefinido em C e C ++, como transbordamentos de inteiros, deslocamentos fora do intervalo e coisas semelhantes.

MSan é um verificador de memória. Tem objetivos semelhantes aos de Valgrind.

O TSan é um verificador de segurança de threads. Tem objetivos semelhantes aos de Helgrind.

Estes três são construídos no compilador Clang e geram código em tempo de compilação. Isso significa que você precisa integrá-los em seu processo de compilação (em particular, você deve compilar com o Clang), o que os torna muito mais difíceis de configurar do que o * grind, mas por outro lado eles têm uma sobrecarga de tempo de execução muito menor.

Todas as ferramentas que listei funcionam no Linux e algumas delas no MacOS. Eu não acho que qualquer trabalho no Windows de forma confiável ainda.

    
por 26.04.2013 / 19:23
fonte
0

Parece que a maioria das respostas aqui confundem essa pergunta com "como eu automaticamente detecto condições de corrida?" quando a questão é realmente "como eu reproduzo condições de corrida em testes quando as encontro?"

A maneira de fazer isso é introduzir a sincronização em seu código que é usada apenas para teste. Por exemplo, se uma condição de corrida ocorrer quando o Evento X acontece entre o Evento A e o Evento B, então, para testar seu aplicativo, escreva algum código que aguarde o Evento X acontecer após o Evento A acontecer. Você provavelmente precisará de alguma forma para seus testes falarem com seu aplicativo para informá-lo ("Ei, estou testando essa coisa, então espere por este evento neste local").

Estou usando o node.js e o mongo, em que algumas ações envolvem a criação de dados consistentes em várias coleções. Nesses casos, meus testes de unidade farão uma chamada para o aplicativo para informar "configure uma espera pelo Evento X" e, depois que o aplicativo for configurado, o teste para o evento X será executado e os testes posteriormente informarão o aplicativo ("Estou pronto com a espera pelo Evento X") para que o restante dos testes seja executado normalmente.

A resposta aqui explica esse tipo de coisa em detalhes no contexto do python: neste código python-confiável">

    
por 14.05.2015 / 22:06
fonte