Como depurar um programa MPI?

129

Eu tenho um programa MPI que compila e executa, mas eu gostaria de percorrê-lo para garantir que nada de estranho esteja acontecendo. Idealmente, eu gostaria de uma maneira simples de anexar o GDB a qualquer processo específico, mas não tenho muita certeza se isso é possível ou como fazê-lo. Uma alternativa seria fazer com que cada processo grave a saída de depuração em um arquivo de log separado, mas isso realmente não oferece a mesma liberdade que um depurador.

Existem abordagens melhores? Como você depura programas MPI?

Jay Conrod
fonte

Respostas:

62

Como alguém disse, o TotalView é o padrão para isso. Mas custará um braço e uma perna.

O site do OpenMPI tem uma ótima FAQ sobre depuração de MPI . O item 6 da FAQ descreve como anexar o GDB aos processos MPI. Leia a coisa toda, existem ótimas dicas.

Porém, se você achar que possui muitos processos para acompanhar, confira a STAT (Stack Trace Analysis Tool) . Usamos isso na Livermore para coletar rastreamentos de pilha de centenas de milhares de processos em execução e representá-los de forma inteligente para os usuários. Não é um depurador com todos os recursos (um depurador com todos os recursos nunca aumentaria para 208k núcleos), mas informará quais grupos de processos estão fazendo a mesma coisa. Você pode percorrer um representante de cada grupo em um depurador padrão.

Todd Gamblin
fonte
14
A partir de 2010 Allinea DDT é um depurador de full-featured que as escalas para mais de 208k núcleos
Marcar
1
Então, eu vou em frente e voto a resposta de @ Mark aqui. DDT é legal. Experimente também. O TotalView também se integra ao STAT agora, portanto, se o seu site tiver uma instalação do TotalView, você também poderá tentar. O LLNL mantém o TotalView e o DDT por perto, e é bom que o TotalView finalmente tenha uma forte concorrência.
Todd Gamblin
Gostaria de destacar o link para as Perguntas frequentes sobre depuração de MPI ( open-mpi.org/faq/?category=debugging#serial-debuggers ). Especificamente, o marcador 6 é uma maneira boa, rápida e fácil (o suficiente para mim!) Entender uma maneira de pelo menos depurar um processo individual.
Jeff
As etapas da seção 6 da página de perguntas frequentes funcionaram muito bem para mim e me ajudaram a descobrir o meu problema. Muito obrigado por isso.
precisa
86

Eu achei o gdb bastante útil. Eu uso isso como

mpirun -np <NP> xterm -e gdb ./program 

Isso lança janelas xterm nas quais eu posso fazer

run <arg1> <arg2> ... <argN>

geralmente funciona bem

Você também pode empacotar esses comandos usando:

mpirun -n <NP> xterm -hold -e gdb -ex run --args ./program [arg1] [arg2] [...]
messenjah
fonte
Como posso enviar a mesma entrada para todos os xterms de NP gdb? Por exemplo, quero adicionar dois pontos de interrupção a cada processo, e há 16 processos. Existe alguma alternativa ao xterm para fazer isso? Podemos conectar sessões em uma única instância de screen, tmux ou Terminator de Chris Jones?
Osgx
@osgx Você pode fazer isso salvando os comandos ("break xxx", "break yyy", "run") <file>e passando -x <file>para o gdb.
Eush77 # 22/16
mas encontro um erro, a mensagem de erro é "erro execvp no arquivo xterm (não
existe
quando eu tento isso com jdb e OpenMPI, ele não funciona, ou seja, cada instância do jdb vê num_ranks de 1 em vez do que é dado ao argumento -np. alguma idéia do porquê?
Michel Müller
26

Muitas das postagens aqui são sobre o GDB, mas não mencione como se conectar a um processo desde a inicialização. Obviamente, você pode anexar a todos os processos:

mpiexec -n X gdb ./a.out

Mas isso é extremamente ineficaz, pois você terá que se movimentar para iniciar todos os seus processos. Se você quiser depurar apenas um (ou um pequeno número de) processos MPI, adicione-o como um executável separado na linha de comando usando o :operador:

mpiexec -n 1 gdb ./a.out : -n X-1 ./a.out

Agora, apenas um dos seus processos receberá o GDB.

Wesley Bland
fonte
Posso usar "mpiexec -n X gdb ./a.out", mas existe alguma maneira de usar o modo gdb -tui?
hitwlh
16

Como outros já mencionaram, se você estiver trabalhando apenas com alguns processos MPI, poderá tentar usar várias sessões gdb , a redobrável valgrind ou criar sua própria solução de impressão / registro.

Se você estiver usando mais processos do que isso, realmente começará a precisar de um depurador adequado. As perguntas frequentes do OpenMPI recomendam o Allinea DDT e o TotalView .

Eu trabalho no Allinea DDT . É um depurador de código-fonte gráfico completo e com recursos; portanto, você pode:

  • Depurar ou anexar a (mais de 200k) processos MPI
  • Passo e pause-os em grupos ou individualmente
  • Adicionar pontos de interrupção, relógios e pontos de rastreamento
  • Capture erros de memória e vazamentos

...e assim por diante. Se você já usou o Eclipse ou o Visual Studio, estará em casa.

Adicionamos alguns recursos interessantes especificamente para depuração de código paralelo (seja MPI, multiencadeado ou CUDA):

  • Variáveis ​​escalares são comparadas automaticamente em todos os processos: (fonte: allinea.com )Sparklines mostrando valores entre processos

  • Você também pode rastrear e filtrar os valores de variáveis ​​e expressões por processos e tempo: Pontos de rastreamento registram valores ao longo do tempo

É amplamente utilizado entre os 500 principais sites de HPC, como ORNL , NCSA , LLNL , Jülich et. al.

A interface é bem rápida; cronometramos o passo e a fusão das pilhas e variáveis ​​de 220.000 processos a 0,1s como parte do teste de aceitação no cluster Jaguar de Oak Ridge.

O @tgamblin mencionou o excelente STAT , que se integra ao Allinea DDT , assim como vários outros projetos populares de código aberto.

Marca
fonte
7

Se você é um tmuxusuário, você se sentirá muito confortável usando o script de Benedikt Morbach :tmpi

Fonte original: https://github.com/moben/scripts/blob/master/tmpi

Forquilha: https://github.com/Azrael3000/tmpi

Com ele, você tem vários painéis (número de processos) todos sincronizados (todos os comandos são copiados em todos os painéis ou processos ao mesmo tempo, para economizar muito tempo comparando com a xterm -eabordagem). Além disso, você pode conhecer os valores das variáveis ​​no processo que deseja fazer printsem precisar mover para outro painel; isso imprimirá em cada painel os valores da variável para cada processo.

Se você não é um tmuxusuário, recomendo fortemente que tente e veja.

GG1991
fonte
2
Como o tmpi é realmente fantástico e exatamente o que eu estava procurando, coloquei-o na minha conta do github: github.com/Azrael3000/tmpi desde que o autor original o removeu
Azrael3000 26/18
6

http://github.com/jimktrains/pgdb/tree/master é um utilitário que escrevi para fazer exatamente isso. Existem alguns documentos e sinta-se à vontade para enviar perguntas para mim.

Basicamente, você chama um programa perl que agrupa o GDB e direciona seu IO para um servidor central. Isso permite que o GDB esteja em execução em cada host e você possa acessá-lo em cada host no terminal.

Jim Keener
fonte
Obrigado! Definitivamente vou verificar isso na próxima vez que estiver trabalhando no MPI.
Jay Conrod
5

O uso em screenconjunto com gdbpara depurar aplicativos MPI funciona bem, especialmente se xtermnão estiver disponível ou você estiver lidando com mais do que alguns processadores. Houve muitas armadilhas ao longo do caminho nas pesquisas de fluxo de pilha que acompanham, portanto, reproduzirei minha solução por completo.

Primeiro, adicione o código após MPI_Init para imprimir o PID e interrompa o programa para aguardar a conexão. A solução padrão parece ser um loop infinito; Acabei decidindo raise(SIGSTOP);, o que requer uma chamada extra continuepara escapar dentro do gdb.

}
    int i, id, nid;
    MPI_Comm_rank(MPI_COMM_WORLD,&id);
    MPI_Comm_size(MPI_COMM_WORLD,&nid);
    for (i=0; i<nid; i++) {
        MPI_Barrier(MPI_COMM_WORLD);
        if (i==id) {
            fprintf(stderr,"PID %d rank %d\n",getpid(),id);
        }
        MPI_Barrier(MPI_COMM_WORLD);
    }
    raise(SIGSTOP);
}

Após a compilação, execute o executável em segundo plano e pegue o stderr. Você pode então grepo arquivo stderr para alguma palavra-chave (aqui literal PID) para obter o PID e a classificação de cada processo.

MDRUN_EXE=../../Your/Path/To/bin/executable
MDRUN_ARG="-a arg1 -f file1 -e etc"

mpiexec -n 1 $MDRUN_EXE $MDRUN_ARG >> output 2>> error &

sleep 2

PIDFILE=pid.dat
grep PID error > $PIDFILE
PIDs=(`awk '{print $2}' $PIDFILE`)
RANKs=(`awk '{print $4}' $PIDFILE`)

Uma sessão gdb pode ser anexada a cada processo com gdb $MDRUN_EXE $PID. Fazer isso em uma sessão de tela permite fácil acesso a qualquer sessão de GDB. -d -minicia a tela no modo desanexado, -S "P$RANK"permite que você nomeie a tela para facilitar o acesso posteriormente, e a -lopção bash a inicia no modo interativo e evita que o gdb saia imediatamente.

for i in `awk 'BEGIN {for (i=0;i<'${#PIDs[@]}';i++) {print i}}'`
do
    PID=${PIDs[$i]}
    RANK=${RANKs[$i]}
    screen -d -m -S "P$RANK" bash -l -c "gdb $MDRUN_EXE $PID"
done

Uma vez iniciado o gdb nas telas, você pode inserir a entrada de scripts nas telas (para que você não precise entrar em todas as telas e digitar a mesma coisa) usando o -X stuffcomando da tela . Uma nova linha é necessária no final do comando. Aqui as telas são acessadas -S "P$i"usando os nomes dados anteriormente. A -p 0opção é crítica, caso contrário, o comando falhará de forma intermitente (com base no fato de você estar ou não conectado anteriormente à tela).

for i in `awk 'BEGIN {for (i=0;i<'${#PIDs[@]}';i++) {print i}}'`
do
    screen -S "P$i" -p 0 -X stuff "set logging file debug.$i.log
"
    screen -S "P$i" -p 0 -X stuff "set logging overwrite on
"
    screen -S "P$i" -p 0 -X stuff "set logging on
"
    screen -S "P$i" -p 0 -X stuff "source debug.init
"
done

Neste ponto, você pode conectar a qualquer tela usando screen -rS "P$i"e desconectando usando Ctrl+A+D. Os comandos podem ser enviados para todas as sessões gdb em analogia com a seção anterior do código.

user3788566
fonte
3

Existe também minha ferramenta de código aberto, padb, que visa ajudar na programação paralela. Eu chamo isso de "Ferramenta de inspeção de trabalho", pois funciona não apenas como um depurador, mas também por exemplo como um programa paralelo de topo. Executar no modo "Relatório completo", mostrará a pilha de rastreamentos de todos os processos em seu aplicativo, juntamente com variáveis ​​locais para todas as funções em todas as classificações (supondo que você tenha compilado com -g). Também mostrará as "filas de mensagens MPI", que são a lista de envios e recebimentos pendentes para cada classificação no trabalho.

Além de mostrar o relatório completo, também é possível solicitar ao padb para aumentar o zoom de bits individuais de informações no trabalho, há uma infinidade de opções e itens de configuração para controlar quais informações são exibidas, consulte a página da web para obter mais detalhes.

Padb


fonte
3

A maneira "padrão" de depurar programas MPI é usando um depurador que suporte esse modelo de execução.

No UNIX, o TotalView é considerado um bom suporte para o MPI.

Comunidade
fonte
2

Eu uso esse pequeno método homebrewn para anexar o depurador aos processos MPI - chame a seguinte função, DebugWait (), logo após MPI_Init () no seu código. Agora, enquanto os processos aguardam a entrada do teclado, você tem o tempo todo para anexar o depurador a eles e adicionar pontos de interrupção. Quando terminar, forneça uma entrada de caractere único e você estará pronto para começar.

static void DebugWait(int rank) {
    char    a;

    if(rank == 0) {
        scanf("%c", &a);
        printf("%d: Starting now\n", rank);
    } 

    MPI_Bcast(&a, 1, MPI_BYTE, 0, MPI_COMM_WORLD);
    printf("%d: Starting now\n", rank);
}

Obviamente, você deseja compilar essa função apenas para compilações de depuração.


fonte
O MPI exigiu a maioria das instruções de depuração que eu já escrevi para um código simples. (risos) Isso pode ser muito útil.
Troggy 02/07/2009
3
Esta solução é semelhante ao marcador 6 aqui ( open-mpi.org/faq/?category=debugging#serial-debuggers ). Você pode melhorar um pouco o seu código adicionando gethostname(hostname, sizeof(hostname)); printf("PID %d on host %s ready for attach\n", getpid(), hostname);. Em seguida, você se anexa ao processo digitando rsh <hostname_from_print_statement>finalmente gdb --pid=<PID_from_print_statement>.
19412 Jeff
2

O comando para anexar gdb a um processo mpi está incompleto, deve ser

mpirun -np <NP> xterm -e gdb ./program 

Uma breve discussão sobre mpi e gdb pode ser encontrada aqui

akintayo
fonte
2

Uma maneira bastante simples de depurar um programa MPI.

Na função main (), adicione sleep (some_seconds)

Execute o programa como de costume

$ mpirun -np <num_of_proc> <prog> <prog_args>

O programa começará e entrará no sono.

Então você terá alguns segundos para encontrar seus processos pelo ps, execute o gdb e anexe-os.

Se você usa algum editor como o QtCreator, pode usar

Depurar-> Iniciar depuração-> Anexar ao aplicativo em execução

e encontre seus processos lá.

desconhecido
fonte
1

Eu faço alguma depuração relacionada ao MPI com rastreamentos de log, mas você também pode executar o gdb se estiver usando mpich2: MPICH2 e gdb . Essa técnica é uma boa prática em geral quando você está lidando com um processo difícil de iniciar a partir de um depurador.

Jim Hunziker
fonte
Alterado para outro link que não está quebrado, adicionou alguns comentários.
Jim Hunziker
0

Outra solução é executar seu código no SMPI, o MPI simulado. Esse é um projeto de código aberto no qual estou envolvido. Cada classificação MPI será convertida em threads do mesmo processo UNIX. Você pode facilmente usar o gdb para subir nas classificações MPI.

O SMPI propõe outras vantagens ao estudo de aplicativos MPI: clarividência (você pode observar todas as partes do sistema), reprodutibilidade (várias execuções levam ao mesmo comportamento, a menos que você especifique isso), ausência de heisenbugs (como a plataforma simulada é diferente do host), etc.

Para mais informações, consulte esta apresentação ou a resposta relacionada .

Martin Quinson
fonte