Helgrind e Eclipse IDE no desenvolvimento de aplicações C multi-thread no Linux

Um dos grandes desafios da programação multi-core em C/C++ em ambiente Linux é o gerenciamento do uso de Pthreads para evitar conflitos de recursos, como memória e I/O. Dentre diversas ferramentas para ajudar nesta tarefa, uma comumente usada pela comunidade Linux é o Helgrind, que faz parte do framework Valgrind, a qual é disponível nas distribuições Linux mais usadas atualmente. Outro desafio é como utilizar tal ferramenta em um projeto com diversos arquivos fonte, onde é comum o uso de uma IDE para auxiliar no desenvolvimento. Neste artigo explora-se o uso da ferramenta Helgrind dentro do Eclipse IDE, através da extensão Linuxtools, para o profiling de aplicações multi-core C/C++ como uma alternativa para superar estes desafios.

Daniel Henrique Barboza , Engenheiro de Software Pleno, IBM

Daniel Henrique BarbozaDaniel Henrique Barboza é Engenheiro de Software no Linux Technology Center (LTC) da IBM Brasil. Seu trabalho no LTC consiste em interações com diversas comunidades de código aberto Linux e desenvolvimento de extensões da plataforma Eclipse IDE usando a linguagem Java. É formado pela Univerdade de Campinas (Unicamp) no curso de Engenharia de Computação e possui mestrado pela mesma universidade.



01/Ago/2012

Introdução

Aplicações multi-thread possuem diversas vantagens em relação a aplicações monothread: um mesmo código pode executar múltiplas vezes simultaneamente, utilizando os mesmos recursos e, em caso de falha, somente a instância que falhou é interrompida. Em arquiteturas multi-core, o uso de múltiplas threads aumenta drasticamente a performance da aplicação.

A desvantagem é a maior complexidade no desenvolvimento. Como várias instâncias são executadas ao mesmo tempo, alguns recursos podem ser acessados simultaneamente por mais de uma instância, causando conflito. Um exemplo seria uma thread que abre um arquivo do sistema: a thread A está acessando o arquivo, modificando seu conteúdo, e a thread B tenta acessar o mesmo arquivo. Existem várias técnicas de regrar esse acesso aos recursos (semáforos, por exemplo), porém nem sempre estas técnicas são suficientes para garantir que não existirão conflitos.

No ambiente Linux, o Helgrind [1] é uma ferramenta do framework Valgrind [2] que tem como objetivo detectar conflitos e erros de sincronização em aplicações C, C++ e Fortran que utilizem threads POSIX¹ (ou pthreads como são mais comumente chamadas). A ferramenta recebe um arquivo executável como parâmetro e devolve um relatório de potenciais problemas relacionados ao uso de pthreads neste executável. As distribuições Linux atuais possuem o framework Valgrind instalado, então para utilizar o Helgrind tudo que você precisa é um terminal para utilizar a ferramenta via linha de comando.

Uma das maneiras de utilizar o Helgrind é através de linha de comando, a qual não é tão conveniente quando estamos trabalhando em um projeto maior e utilizando uma IDE para o desenvolvimento. Para cada erro ou observação encontrado é necessário encontrar o erro manualmente na IDE, corrigir e voltar a linha de comando.

Utilizando-se o Eclipse IDE [3] é possível utilizar o Helgrind de maneira integrada através da extensão Linuxtools [4], projeto open-source que implementa plug-ins de Eclipse para utilização do framework Valgrind e outras ferramentas de análise do Linux (como OProfile e Perf). O Linuxtools permite o uso do Helgrind dentro do Eclipse, onde é possível usufruir de vantagens as quais não existem usando a mesma ferramenta via linha de comando do Linux.

A seguir é demonstrado o uso do Helgrind, por linha de comando, em um simples programa C que utiliza pthreads. Em seguida o mesmo experimento é feito porém dentro do Eclipse IDE com o Linuxtools. Por fim é apresentado algumas conclusões sobre esta investigação.

____________________________
¹ POSIX é a sigla para Portable Operating System Interface. Trata-se de um padrão IEEE que visa manter uma mínima compatibilidade entre sistemas operacionais. Sem esse padrão, cada sistema operacional/hardware teria sua própria implementação de threads, o que dificultaria a portabilidade de aplicações multi-thread.


Exemplo de uso: Helgrind via linha de comando do Linux

Nesta seção é demonstrado como utilizar a ferramenta Helgrind via linha de comando no Linux, partindo de uma simples aplicação em C que utiliza POSIX threads (pthreads), como segue abaixo:


#include <pthread.h> 
 
 int var = 0; 
 
 void* cria_thread ( void* arg ) { 
   var++; 
   return NULL; 
 } 
 
 int main ( void ) { 
   pthread_t child; 
   pthread_create(&child, NULL, cria_thread, NULL); 
   var++; 
   pthread_join(child, NULL); 
   return 0; 
 } 

Neste exemplo, a variável global "var" começa com o valor 0 e no bloco principal (main) é criada uma nova thread a partir da função pthread_create, através da rotina cria_thread. Incrementa-se o valor da variável global "var" no bloco principal e dentro da função cria_thread. Esta condição é um exemplo simples de concorrência, chamado data race, onde mais de uma thread tenta acessar o mesmo recurso, neste caso a variável global "var".

Este arquivo C é salvo com o nome thread.c e é compilado usando o gcc para criar o binário. A linha de comando para compilar o arquivo segue abaixo:

$ gcc -pthread -g -o thread thread.c

A seguir utiliza-se o Helgrind para analisar este binário gerado. Primeiro, é necessário verificar se o framework Valgrind está instalado no sistema. Isto é possível tentando executá-lo no terminal:

$ valgrindvalgrind: no program specifiedvalgrind: Use --help for more information.

Este resultado indica que o framework está instalado corretamente e pronto para uso. Caso retorne "Command not found" ou qualquer outro erro, pode ser que o framework não esteja instalado ou não esteja presente no PATH do Linux. O Valgrind pode ser instalado através do pacote chamado "valgrind"² ou compilado e instalado a partir do código fonte [5].

____________________________
² Pacote existente nas distros Linux Fedora/Red Hat, OpenSuse/Suse, Ubuntu/Debian e outras.

O Valgrind é um framework que possui várias ferramentas de análise, não só o Helgrind. Para usar especificamente o Helgrind, usa-se o parâmetro "--tool=helgrind" na linha de comando do Valgrind. Usando junto com o parâmetro "--help" é possível ver uma lista de opções da ferramenta, como é mostrado abaixo (comprimido em algumas seções):


  $ valgrind --tool=helgrind --help 
  usage: valgrind [options] prog-and-args 
  
  tool-selection option, with default in [ ]: 
  --tool=<name>             use the Valgrind tool named <name> [memcheck] 
    
    basic user options for all Valgrind tools, with defaults in [ ]: 
    -h --help                 show this message 
    (...)
    
    user options for Valgrind tools that report errors: 
    --xml=yes                 emit error output in XML (some tools only) 
    (...)
    user options for Valgrind tools that replace malloc: 
    --alignment=<number>      set minimum alignment of heap allocations [16] 
      
      uncommon user options for all Valgrind tools: 
      --smc-check=none|stack|all  checks for self-modifying code: none, 
      only for code found in stacks, or all [stack] 
      (...)
      
      user options for Helgrind: 
      --track-lockorders=no|yes show lock ordering errors? [yes] 
      --history-level=none|approx|full [full] 
      full:   show both stack traces for a data race (can be very slow) 
      approx: full trace for one thread, approx for the other (faster) 
      none:   only show trace for one thread in a race (fastest) 
      --conflict-cache-size=N   size of 'full' history cache [1000000] 
      
      Extra options read from ~/.valgrindrc, $VALGRIND_OPTS, ./.valgrindrc 
      
      (...)  

Na seção "user options for Helgrind" desta lista encontram-se opções que são específicas da ferramenta Helgrind. No fim de cada opção existe um valor entre colchetes que indica qual é o valor default do parâmetro caso nenhum seja especificado. Por exemplo, na opção "--history-level" do Helgrind, o valor default é "full" se nenhum outro valor for especificado*.

* Para mais detalhes sobre o que representa e como utilizar cada opção do Helgrind acessar
http://valgrind.org/docs/manual/hg-manual.html#hg-manual.options .

Verificado a existência do Valgrind, executa-se o Helgrind no binário gerado anteriormente:

 $ valgrind --tool=helgrind ./thread 

==7391== Helgrind, a thread error detector 
==7391== Copyright (C) 2007-2009, and GNU GPL'd, by OpenWorks LLP et al. 
==7391== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info 
==7391== Command: ./thread 
==7391== 
==7391== Thread #2 was created 
==7391==    at 0x38CC0E0BEE: clone (in /lib64/libc-2.13.so) 
==7391==    by 0x38CC405D9F: do_clone.clone.2 (in /lib64/libpthread-2.13.so) 
==7391==    by 0x38CC40731A: pthread_create@@GLIBC_2.2.5 (in /lib64/libpthread-2.13.so) 
==7391==    by 0x4A069C8: pthread_create_WRK (hg_intercepts.c:229) 
==7391==    by 0x4A06AFA: pthread_create@* (hg_intercepts.c:256) 
==7391==    by 0x4005B4: main (thread.c:12) 
==7391== 
==7391== Thread #1 is the program's root thread 
==7391== 
==7391== Possible data race during write of size 8 at 0x7feffff00 by thread #2 
==7391==    at 0x4A06AD6: mythread_wrapper (hg_intercepts.c:199) 
==7391==  This conflicts with a previous read of size 8 by thread #1 
==7391==    at 0x4A069DC: pthread_create_WRK (hg_intercepts.c:234) 
==7391==    by 0x4A06AFA: pthread_create@* (hg_intercepts.c:256) 
==7391==    by 0x4005B4: main (thread.c:12) 
==7391== 
==7391== Possible data race during read of size 4 at 0x6009c0 by thread #1 
==7391==    at 0x4005B5: main (thread.c:13) 
==7391==  This conflicts with a previous write of size 4 by thread #2 
==7391==    at 0x400585: cria_thread (thread.c:6) 
==7391== 
==7391== Possible data race during write of size 4 at 0x6009c0 by thread #1 
==7391==    at 0x4005BE: main (thread.c:13) 
==7391==  This conflicts with a previous write of size 4 by thread #2 
==7391==    at 0x400585: cria_thread (thread.c:6) 
==7391== 
==7391== 
==7391== For counts of detected and suppressed errors, rerun with: -v 
==7391== Use --history-level=approx or =none to gain increased speed, at 
==7391== the cost of reduced accuracy of conflicting-access information 
==7391== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 1 from 1)

No fim de cada observação de erro é mostrado o arquivo fonte e a linha que gerou o mesmo, no formato arquivo:linha. Neste caso, o Helgrind apontou um possível data race nas linhas 6 e 13 do arquivo thread.c, linhas as quais a variável global "var" é incrementada, caracterizando o data race.

É possível observar que executar o Helgrind via linha de comando é uma alternativa viável para aplicações simples, com poucos arquivos fonte. Em aplicações compostas por diversos arquivos fonte e múltiplos erros seria necessário abrir todos os arquivos envolvidos em editores de texto e procurar manualmente os erros encontrados.


Exemplo de uso: Helgrind usando o Eclipse IDE

A seguir é realizado o mesmo experimento usando o Eclipse IDE. Para instalar o Eclipse basta entrar no site [3], sessão Downloads e escolher a versão/formato desejado. Neste exemplo utilizamos a nova versão do Eclipse, codinome Juno.

Para desenvolvimento de aplicações C/C++ é necessário instalar um conjunto de plug-ins chamado CDT (C/C++ Development Tools). Para instalar o CDT :

  • Dentro do Eclipse IDE, menu Help – Install New Software
  • Na janela que segue é necessário escolher qual o repositório a ser utilizado no campo "Work with". Escolhe-se o repositório oficial, neste caso chamado Juno.
  • No campo logo abaixo escreve-se "C/C++". É uma maneira rápida de procurar um software dentro dos diversos que estão disponíveis para o Eclipse.
  • Selecinona-se "C/C++ Development Tools", como na figura a seguir:

Figura 1: Procurando e instalando o CDT no Eclipse

  • A partir deste ponto pressiona-se "Next" para continuar o processo de instalação, rever os termos de licença deste novo software, "Next" novamente e, por fim, "Finish". A instalação ocorrerá e uma nova janela irá pedir para reiniciar o Eclipse.
  • O Eclipse IDE trabalha com várias perspectivas, onde a aparência da IDE muda conforme o tipo de projeto. Para mudar para a perspectiva "C/C++", após o reboot do Eclipse, pode-se ir para o menu "Window", logo depois "Open perspective" e "Other". Na janela que abrirá, "C/C++" e logo após "OK".

Criando, compilando e executando um projeto C/C++ no Eclipse com o CDT

Após instalar o CDT no Eclipse é necessário criar um projeto C que consiste no mesmo arquivo threads.c usado para testar o Helgrind via linha de comando. Para criar o projeto:

  • Menu "File", logo depois "New" e "C Project"
  • Na nova janela, escolhe-se um nome do projeto, por exemplo "helgrind". Existem algumas opções de tipo de projeto C, como biblioteca estática, compartilhada e projeto de Makefile. Neste exemplo usa-se o tipo "Executable" com "Empty Project", como mostra a figura abaixo. A opção "Toolchains" permite escolher qual conjunto de compiladores usar no projeto. Pressione "Finish" para criar o projeto.

Figura 2: Criando um projeto C no Eclipse CDT

O próximo passo é criar um arquivo fonte C chamado threads.c como o anterior dentro do projeto recém-criado:

  • Abrindo o menu clicando com o botão direito do mouse no projeto, escolhe-se "New" e depois "Source File"
  • Na tela que se segue, "threads.c" no campo "Source file".
  • O arquivo será criado, mas estará vazio. Usa-se o mesmo o código fonte do arquivo threads.c que foi usado anteriormente no editor do Eclipse e pressiona-se CTRL+S para salvar.

Para compilar projetos C/C++ no Eclipse usa-se a opção "Build Project" do menu "Project". Para este exemplo, entretanto, é preciso configurar o Eclipse para utilizar a flag "-pthread" na compilação do projeto:

  • Clicando com o botão direito no mouse no nome do projeto, escolhe-se "Properties"
  • Na janela que se segue, "C/C++ Build" e escolhe-se "Settings". Do lado direito da janela, dentro do submenu "GCC C Linker", escolhe-se "Libraries" . Pressionando o botão "Add..." e inserindo o valor "pthread" na janela que se abre, logo depois pressionando "OK", conclui-se esse processo. A figura abaixo tenta ilustrar estes passos:

Figura 3: Configurando a flag "pthread" no build do projeto

O projeto deverá ser compilando (clique direito do mouse no projeto e "Build Project") sem erros. Para rodar o executável gerado usa-se o menu "Run", opção "Run". O executável deverá ser executado sem erros.

O próximo passo é instalar o Linuxtools, o qual adicionará suporte ao Valgrind dentro do Eclipse IDE.

Instalando o Linuxtools e executando o Helgrind

Para instalar o Linuxtools o procedimento é similar com o usado para instalar o CDT, porém inserindo a palavra chave "Valgrind" no campo de busca e escolhendo o item "Valgrind Tools Integration" para ser instalado, como na figura abaixo.

Figura 4: Instalando o suporte a Valgrind no Eclipse

Após o processo de instalação ("Next", aceitar os termos de instalação e "Next", "Finish" e logo após reiniciar0 o Eclipse quando solicitado), o Valgrind é acessível via o menu "Run", opção "Profile configurations ...". Clicando duas vezes no item "Profile with Valgrind" cria-se uma configuração de profiling³ com o Valgrind usando o projeto C recém-criado:

____________________________
³Profiling é o termo usado para a ação de rodar uma aplicação usando uma ferramenta de análise com o objetivo de avaliar qual a performance da aplicação, seja em velocidade de execução, recursos utilizados, uso de memória e outros parâmetros.

Figura 5: Criando uma configuração do Valgrind para o projeto

Existem 6 abas com configurações para este profiling. Neste exemplo, será necessário configurar somente as opções da aba "Valgrind Options":

Figura 6: Aba de configuração do Valgrind

O ca2mpo "Tool to run" é usado para determinar qual ferramenta do Valgrind será utilizada. Clicando na aba "Helgrind Options" as opções específicas para o Helgrind são exi bidas, as quais são as mesmas disponíveis na linha de comando do Linux:

Figura 7: Opções do Helgrind disponíveis via linha de comando

Figura 8: Opções do Helgrind dentro do plug-in do Eclipse

Uma das vantagens de utilizar o Helgrind via Eclipse IDE ao invés da linha de comando é a visão geral de todos os parâmetros e seus possíveis valores, como é possível perceber na figura acima.

Para prosseguir para o profiling do projeto com o Helgrind aperta-se o botão "Profile":

 
 

Figura 9: Resultado do profiling do projeto usando o Helgrind
Visualizar imagem ampliada

Algumas observações a respeito do resultado:

  • O resultado foi o mesmo obtido através da linha de comando, o que era esperado, dado que nos dois casos foram utilizados as opções padrão.
  • Destaque da linha onde aconteceu o aviso no editor: outra vantagem que não é possível obter via linha de comando. No resultado do profiling é possível clicar nos avisos do Helgrind e a linha referenciada é automaticamente destacada no editor do Eclipse.Ao selecionar a observação referente a linha 6 do arquivo thread.c, automaticamente a linha 6 do arquivo threads.c no editor foi selecionada. Em projetos onde existe mais de um arquivo fonte, o Eclipse abrirá o editor com o arquivo mencionado e destacará a linha indicada pelo Helgrind.
  • Expandir/contrair a saída. Na figura acima todos os itens foram expandidos para mostrar que o resultado foi similar ao encontrado na linha de comando, mas pode-se expandir somente o que for de interesse, como na figura a seguir. Em casos onde o Helgrind fornecerá centenas de linhas de resultado, esta funcionalidade facilita a análise dos pontos de maior interesse.

Figura 10: Resultado do Helgrind expandindo somente alguns pontos de interesse


Conclusão

Este artigo analisou brevemente os desafios da programação multi-threading em Linux e como que ferramentas de análise como o Helgrind podem ajudar nesta tarefa. A partir de um exemplo de uma simples aplicação em C que utiliza múltiplas threads, analisou-se como seria usar o Helgrind em linha de comando e quais as limitações deste uso. Logo após realizou-se o mesmo procedimento usando o Eclipse IDE.

Como foi demonstrado todo o processo de instalação do Eclipse IDE, com todos os componentes necessários (CDT e Linuxtools), o esforço foi maior do que criar o arquivo no editor de texto, compilar na linha de comando via GCC e logo depois fazer o profiling com o Helgrind. Porém todo esse procedimento de instalação é feito somente na primeira vez, então não devemos contabilizá-lo nesta análise. Para qualquer outra aplicação futura, usar o Eclipse com CDT é mais veloz e mais fácil do que usar a linha de comando e este fato fica ainda mais evidente quando partimos para aplicações maiores.

Utilizar o Helgrind a partir do Eclipse IDE gera os mesmos resultados do que utilizar via linha de comando do Linux, o que é esperado. Porém a partir do Eclipse IDE tem-se vantagens, como highlight de linha no editor e formato de output, que auxiliam o desenvolvedor na depuração da aplicação.

Conclu-se que usar o Helgrind, junto com o Eclipse IDE, é uma boa combinação para programadores de aplicações multi-thread C/C++ em ambiente Linux.


Referências

[1]http://valgrind.org/docs/manual/hg-manual.html

[2]http://valgrind.org/

[3]http://www.eclipse.org/

[4]http://www.eclipse.org/linuxtools/

[5]http://valgrind.org/downloads/current.html

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Linux
ArticleID=828485
ArticleTitle=Helgrind e Eclipse IDE no desenvolvimento de aplicações C multi-thread no Linux
publish-date=08012012