A plataforma Eclipse provê um ambiente de desenvolvimento gráfico para aplicações de várias naturezas, possui uma série de plug-ins para o desenvolvimento em outras linguagens de programação, como C e C++, por meio do projeto Eclipse CDT (C/C++ Development Tools). Aliado a este tem-se o projeto do Eclipse Linux Tools, cujo o objetivo é disponibilizar uma interface simples para facilitar a análise e otimização de aplicações nas linguagens C e C++.

O Eclipse Linux Tools é composto de uma série de plug-ins que fazem interface com as ferramentas de linha de comando, que são usualmente utilizadas pelos desenvolvedores em Linux. São elas:

  • Valgrind: um framework de análise de programa em tempo de execução que faz uso de um mecanismo de instrumentação dinâmica. É a base para construção das ferramentas: Helgrind, Cachegrind, Massif e Memcheck.
  • Oprofile e Perf: ferramentas de coleta e análise de informações provenientes da PMU (Performance Monitor Unit), a qual possui unidades especiais responsáveis por coletar eventos de hardware nos processadores.
  • Gprof: o GNU Profiler é uma ferramenta de profiling de chamada de métodos e execução de funções.
  • Gcov: o GNU Coverage é uma ferramenta de cobertura de código.

Este artigo faz um tour por essas ferramentas e mostra como elas são usadas dentro do Eclipse no desenvolvimento de aplicações C e C++ no sistema operacional Linux. Serão mostradas as principais funcionalidades de cada ferramenta e exemplos dos relatórios gerados.

No artigo foram usadas as seguintes versões dos componentes: Eclipse 4.5 (Mars), Linux Tools 4.0 e CDT 8.7, todos lançados no dia 24 de junho de 2015.

Considerações iniciais

Para usar as ferramentas do Linux Tools descritas nesse artigo, algumas condições são previamente requeridas:

  1. Ter o seguinte ambiente de desenvolvimento instalado: Eclipse Mars e os plug-ins do CDT, além do Linux Tools;
  2. Ter instalado o compilador GCC para compilar aplicações C, ou G++ para compilar aplicações C++;
  3. Ter um projeto C ou C++ importado e compilado no workspace do Eclipse. Mais informações podem ser encontradas na seção "C/C++ Development User Guide" (vide seção Links);
  4. Ter instalado os binários referente aos plug-ins usados. Na maioria das distribuições Linux, os binários das ferramentas Gcov e Gprof são instalados junto com o GCC. Já as outras ferramentas Perf, Oprofile e Valgrind estão em pacotes separados que podem ser identificados pelo mesmo nome da ferramenta na maioria das distribuições Linux como, por exemplo, no Fedora.

Instalação do ambiente de desenvolvimento

A primeira coisa que precisamos fazer é baixar e extrair o pacote "Eclipse IDE for C/C++ Developers" (vide seção Links), que já contém o Eclipse, CDT e Linux Tools. Para iniciar uma nova instância do IDE execute o arquivo "eclipse".

Para utilizar os plug-ins do Linux Tools é preciso criar uma configuração de profile para cada um, sendo que cada configuração é associada a um projeto específico. A vantagem de utilizar configurações específicas por projeto é que cada um pode ter uma ou mais configurações para cada plug-in, ou seja, cada configuração pode ter parâmetros e opções diferentes.

Atualmente, os plug-ins são divididos em três categorias de profile: Code Coverage, Memory e Timing.

A categoria Code Coverage engloba o plug-in Gcov, a categoria Memory engloba três funcionalidades do plug-in Valgrind, sendo elas: Memcheck, Helgrind e Massif. E por último, a categoria Timing contém os plug-ins Oprofile, Perf e Gprof e também a última funcionalidade do Valgrind, chamada de CacheGrind.

Criação e execução de Profiles

Para visualizar as configurações de profile disponíveis, clique com o botão direito sobre o projeto, selecione a opção "Profiling Tools" e depois "Profiling Tools Configuration". Uma nova janela será aberta contendo as ferramentas de profile do Linux Tools. Para criar uma nova configuração do Oprofile, por exemplo, clique duas vezes sobre o item "Profile Timing". Após o item de configuração ser criado mude para a aba "Timing" e no primeiro campo mude para utilizar o Oprofile. Para criar a configuração de profile de outros plug-ins realize os mesmos passos, selecionando a categoria e o plug-in desejado.

Outras abas relevantes para a configuração do profile são "Main" e "Arguments".

A aba "Main" possui o campo "C/C++ Application", que permite escolher qual binário será usado para o profile. Se a aplicação tiver um único binário, esse campo será automaticamente preenchido, caso contrário, selecione o binário desejado clicando no botão "Search Project". Já a aba "Arguments" permite que parâmetros sejam passados para o binário caso precise de alguma entrada para ser executado.

Com a configuração criada e o binário selecionado clique no botão "Profile" para iniciar o profile da aplicação.

Para que os plug-ins obtenham a maior quantidade de informações da aplicação é altamente recomendado que a flag de debug (-g) seja usada durante a compilação. Para algumas ferramentas específicas, como o Gcov e Gprof, além da flag de debug, é preciso que outras sejam usadas, como veremos a seguir.

Ferramentas de Profiles

Valgrind

Várias ferramentas de propósito específico foram construídas a partir da plataforma, dentre as principais temos o Memcheck, Cachegrind, Massif e Helgrind, cuja finalidades são, respectivamente: a depuração e análise de memória, uso das memórias cache do processador, análise de alocação de memória no heap da aplicação e comportamento de programas multi-thread com POSIX threads.

O Valgrind é executado pela linha de comando valgrind, e qualquer uma de suas sub-ferramentas é chamada utilizando a opção --tool. Ao reportar os erros encontrados, as ferramentas fornecem informações do código fonte e a linha onde estão os problemas.

Memcheck é a ferramenta padrão do Valgrind, isto é, se a opção --tool não for usada. Ele é um detector de erros de memória capaz de encontrar problemas de gestão de memória ao nível do programa, que incluem:

  • Utilização de memória não inicializada;
  • Erros na alocação e desalocação de memória;
  • Vazamento de memória (memory leak);
  • Leitura e escrita em áreas impróprias da memória.

Cachegrind simula como uma aplicação interage com a cache e o preditor de saltos (branch predictor) do processador, conseguindo gerar uma descrição detalhada do comportamento das caches de instruções e dados nos seus vários níveis hierárquicos do processador. Ela é muito útil na análise de performance de algoritmos sensíveis à cache do sistema. A ferramenta disponibiliza uma tabela com snapshots onde são mostrados:

  • O número de cache read e read misses de instruções;
  • O número de cache read/writes e read/writes misses de dados nos níveis L1 e Ln (último nível);
  • Referências de memórias;
  • Número de saltos (branches) condicionais e indiretos executados e mispredicted.

Massif é um analisador de utilização da heap feita pela a aplicação, ou seja, ele mostra a quantidade de memória alocada e desalocada ao longo do tempo, sendo muito útil para desenvolvedores acharem hotspots na alocação de dados pela aplicação, possibilitando uma análise detalhada que visa a redução da quantidade de memória usada. Ao executar o Massif, ele gera um gráfico mostrando a utilização da heap ao longo da execução da aplicação e inclui informações sobre quais partes são responsáveis pelas maiores alocações de memória.

E, por último, o Helgrind é utilizado para detectar uma grande lista de problemas em aplicações que utilizam POSIX threads (também chamadas de Pthreads), como deadlocks de threads.

O plug-in Valgrind do Linux Tools integra as ferramentas apresentadas com a interface gráfica do Eclipse. Para utilizá-lo, crie uma nova configuração de Profile Memory (conforme mostrado no tópico Criação e execução de Profiles) e escolha uma das ferramentas do Valgrind. No artigo utilizaremos a funcionalidade Memory Check como exemplo.

Na aba "Memory" é possível customizar as opções usadas pelo Valgrind durante o profiling da aplicação, como mostra a Figura 1. Após escolher as opções desejadas, clique no botão "Profile".

Tela com aba de opções globais do plug-in Valgrind

Figura 1. Tela com aba de opções globais do plug-in Valgrind.

A Figura 2 mostra a tela do Valgrind com o resultado da análise de uma aplicação utilizando o Memcheck, que continha problemas na hora de desalocar memória. Um ponteiro foi inicializado com o operador new e no final do programa a memória foi desalocada com o operador free, ao invés de delete. Nesse caso, o Memcheck marcará o problema na linha de código onde a desalocação de memória foi feita de maneira equivocada e irá sugerir uma troca do operador free por delete para solucionar o problema.

Tela de exibição de dados do plug-in Valgrind

Figura 2. Tela de exibição de dados do plug-in Valgrind.

Gcov

É um programa de cobertura de código que gera contagens exatas do número de vezes que cada instrução de um programa é executada. Atualmente, o Gcov é distribuído como utilitário padrão da suíte do GCC (GNU Compiler Collection).

A utilização do Gcov ajuda a criar programas mais eficientes e descobre quais partes do código estão sendo menos usadas e quais não são usadas em nenhum momento. Com essas informações fica mais fácil saber em quais partes da aplicação deve-se investir maior esforço para ter um retorno significativo na performance.

Ao executar o plug-in Gcov do Linux Tools, as seguintes informações sobre a aplicação são mostradas:

  • Com qual frequência cada linha do código é executada;
  • Quais funções são usadas;
  • Qual a porcentagem do código fonte está sendo realmente utilizado.

Para que Gcov funcione corretamente é necessário compilar a aplicação usando as flags -ftest-coverage e -fprofile-arcs do GCC.

A flag -ftest-coverage adiciona instruções no binário para contagem do número de vezes que cada linha de código é executada. Já a flag -fprofile-arcs adiciona código instrumentado para cada ramificação da aplicação executada. Além da utilização de ambas, para utilizar o Gcov é preciso compilar a aplicação sem opções de otimização (por exemplo, -O3, -Ofast, etc.), pois esta pode não fornecer informações necessárias para a análise de partes do programa que estão consumindo grande quantidade de tempo.

Para utilizar o plug-in Gcov crie uma nova configuração de Profile Code Coverage e escolha o Gcov como ferramenta. Como a tela de configuração do Gcov não possui nenhuma opção para configurar, como mostra a Figura 3, clique no botão "Profile".

Tela com aba de opções globais do plug-in Gcov

Figura 3. Tela com aba de opções globais do plug-in Gcov

A Figura 4 mostra o resultado do profile de uma aplicação com três funções: main, sum_add e sum_sub. As duas primeiras foram usadas no decorrer da execução do programa, porém, a função sum_sub não é utilizada em nenhum momento, por isso a coluna Coverage mostra o valor de 0% de cobertura para aquela função específica e zero linhas executadas. Caso esta aplicação precise ser otimizada, o desenvolvedor não precisaria gastar esforços otimizando a função sum_sub e poderia até analisar se realmente precisa mantê-la no código.

Tela de exibição de dados do plug-in Gcov

Figura 4. Tela de exibição de dados do plug-in Gcov.

Gprof

Gprof é uma ferramenta de análise de performance que mostra o quanto de recurso de processamento é utilizado em cada trecho de código. Atualmente, essa ferramenta do projeto GNU faz parte do conjunto de ferramentas binárias GNU Binutils.

A sua utilização permite a análise dinâmica de aplicações através da coleta de informações e identificação de trechos de código mais executados, além do tempo consumido por cada função. Este tipo de informação permite analisar a quantidade de funções existentes no código, a quantidade de vezes que cada função é usada e a porcentagem de tempo gasto para executar cada função.

O plug-in Gprof do Linux Tools mostra as seguintes informações sobre a aplicação:

  • A quantidade de chamadas que cada função recebeu;
  • O tempo gasto em cada chamada;
  • A porcentagem do tempo gasto em uma determinada função em relação a execução total de aplicação.

Para que o Gprof funcione corretamente é necessário compilar a aplicação usando a flag -pg do GCC para coletar as informações necessárias da aplicação.

Para utilizá-lo crie uma nova configuração de Profile Timing e escolha o Gprof como ferramenta. Como a sua tela de configuração não possui nenhuma opção, como mostra a Figura 5, clique no botão "Profile".

Tela com aba de opções globais do plug-in Gprof

Figura 5. Tela com aba de opções globais do plug-in Gprof

A Figura 6 mostra o mesmo exemplo usado na seção do Gcov, porém com a função sum_sub sendo utilizada. Na tela é mostrada a quantidade de vezes em que cada função foi chamada e a porcentagem de tempo utilizado por elas em relação ao tempo de execução total, que ocorreu em quase todo o tempo na função main (90,96% do tempo). Assim, caso o desenvolvedor queira melhorar a performance desta aplicação, ele deverá investir um maior esforço nesta função.

Ao expandir cada função na tela do Gprof é mostrado a quantidade de tempo gasto em cada linha de código, por exemplo, sum_add (main.c:112) refere-se a linha 112 do arquivo main.c. Para ir até esta linha no código fonte basta clicar duas vezes sobre esse item na tela.

Figura 6. Tela de exibição de dados do plug-in Gprof

Oprofile

É uma ferramenta de profiling muito utilizada por desenvolvedores no Linux para acessar informações geradas por um hardware dedicado chamado de PMU (Performance Monitor Unit), que hoje se encontra na maioria dos processadores de uso geral. É responsável por registrar eventos da execução e performance do processador, por exemplo, memórias referenciadas, acessos a memória cache do processador, número de interrupções de hardware e ciclos de CPU gastos na execução das instruções. Estes eventos podem ser usados no cálculo de métricas de desempenho de aplicações tais como cache misses, mispredição de saltos condicionais, acesso a memória e stalls de instruções nas várias unidades de processamento da CPU. Também, a partir da observação de alguns eventos, é possível descobrir áreas no código mais executadas (ou que gastam maior tempo de execução) e gargalos que provocam uma má performance da aplicação. Esta ferramenta deve ser executada em dois passos: o primeiro é coletar as informações de eventos da PMU, para depois gerar um relatório para visualização dos dados. O plug-in OProfile do Linux Tools integra esta ferramenta de profiling na interface gráfica do Eclipse, facilitando a utilização por desenvolvedores de vários níveis de experiência.

Para utilizar o plug-in crie uma nova configuração de Profile Timing e escolha o Oprofile como ferramenta. Por padrão, o plug-in irá produzir dados sobre os ciclos de processamento gastos na aplicação e mostra os valores em hierarquia por métodos. Mude as opções padrões caso seja necessário:

  • Na aba "Global" (veja Figura 7) pode-se escolher entre utilizar o operf (que é atualmente a opção padrão) ou o opcontrol. Ambos são comandos usados para coletar informações sobre os eventos de hardware, porém, o desenvolvimento do opcontrol foi encerrado pela comunidade e, então, não é recomendado o seu uso. Pode-se executar mais de uma vez a aplicação, de forma que o resultado apresentado seja a soma das rodadas.
  • Na aba "Events" (veja Figura 8) é possível personalizar quais eventos serão monitorados durante a execução da aplicação. Para modificar desmarque a opção "Use default events", selecione a opção "Enabled" e selecione os eventos desejados.

Após terminar a configuração do profile, clique no botão "Profile" para iniciar a análise da aplicação.

Tela com aba de opções globais do plug-in Oprofile

Figura 7. Tela com aba de opções globais do plug-in Oprofile

Tela de seleção de eventos do plug-in Oprofile

Figura 8. Tela de seleção de eventos do plug-in Oprofile

Ao final do processamento é aberta uma tela contendo os dados coletados, como mostra a Figura 9. O resultado é exibido por porcentagem da ocorrência do evento e é dividido em dois blocos principais: ocorrência no executável da aplicação e ocorrência no kernel do Linux e em bibliotecas compartilhadas do sistema. Essa porcentagem de ocorrências é detalhada em mais três níveis: por função, arquivo fonte (ou binário) e por linha do código (ou símbolo do binário).

No exemplo da Figura 9, 78.06% do evento CPU_CLK_UNHALTED (ciclos de relógio do processador) ocorreu enquanto uma instrução do executável era processada e o restante no Kernel e em bibliotecas do sistema. Sendo que 76.04% do evento aconteceu dentro da função BZ2_bzDecompress (no código fonte bzlib.c). A captura de tela não mostra, mas expandindo o BZ2_bzDecompress é mostrado uma lista de linhas onde aconteceram esses eventos, sendo que a linha 618 representa 54.75% deles. Percebe-se também que apenas 2.15% está relacionada com chamadas para a Lib C (/usr/lib64/libc.2.20.so) e outros 19.78% com chamadas do sistema (system calls). Ou seja, se existe um gargalo de execução, ele está em uma única função (BZ2_bzDecompress).

Tela de exibição de dados do plug-in Oprofile

Figura 9. Tela de exibição de dados do plug-in Oprofile

Perf

Da mesma forma que o Oprofile, a ferramenta de linha de comando Perf também utiliza eventos de hardware para a análise de desempenho da aplicação. Ele foi desenvolvido juntamente com o código fonte do Kernel do Linux e faz uso do seu subsistema de performance (desde a versão 2.6) e de eventos de software e tracepoints gerados pelo Kernel. O resultado do profile é bem preciso e rápido por não fazer a instrumentação do código.

Para utilizar o plug-in, crie uma nova configuração de Profile Timing e escolha o Perf como ferramenta.

Para customizar as opções que serão usadas por ele para fazer o profile da aplicação, clique na aba "Perf Options". A Figura 10 mostra as opções que vem marcadas por padrão, dentre as quais merecem atenção:

  • Com a opção "Show source Disassembly view" habilitada, após a execução do profile será mostrada a tela "Perf Source Disassembly", onde é exibido o código assembly do executável da aplicação.
  • Com a opção "Show Stat View" habilitada será usado o comando perf stat para fazer a contagem estatística da ocorrência dos eventos, isto é, não serão coletadas informações de correspondência entre os eventos e os símbolos, funções e linhas de código da aplicação. Ao final da execução do profile será mostrada a tela "Perf Statistics". Ao fazer o profile da aplicação duas ou mais vezes usando a opção perf stat, é possível fazer uma comparação entre os resultados das últimas duas execuções clicando em "view menu" na tela do "Perf Statistics" e selecione a opção "Compare Latest".

Tela de opções do plug-in Perf

Figura 10. Tela de opções do plug-in Perf

Além de personalizar as opções gerais que serão usadas pelo Perf, é possível modificar quais eventos serão analisados. Para isso, mude para a aba "Perf Events" e desmarque a opção "Default Event", como é mostrado na Figura 11. Nesta aba é possível navegar entre as várias categorias de eventos, tais como Kernel PMU Events, Hardware Events, Hardware cache event.

Tela para seleção de eventos no plug-in Perf

Figura 11. Tela para seleção de eventos no plug-in Perf

Após terminar a configuração do profile, clique no botão "Profile" para iniciar a análise da aplicação.

A Figura 12 mostra a tela de resultados de uma sessão de profile com o plug-in Perf. Nela é exibida a quantidade de amostras coletadas dos eventos e a porcentagem de ocorrências, tanto no executável da aplicação quanto nas bibliotecas de sistema e Kernel do Linux. Esta tela de resultados é similar à do plug-in Oprofile, que vimos na Figura 9.

Tela de exibição de dados do plug-in Perf

Figura 12. Tela de exibição de dados do plug-in Perf

Podemos concluir que o uso dessas ferramentas ajuda muito no desenvolvimento e na aplicação de boas práticas, pois como elas mostram os possíveis problemas de desempenho do sistema, fica fácil saber onde podemos melhorar o código.

Até a próxima!

Links

Projeto C/C++
http://help.eclipse.org/mars/index.jsp.

Eclipse IDE for C/C++ Developers
https://eclipse.org/downloads

Valgrind
http://valgrind.org

Gcov
https://gcc.gnu.org/onlinedocs/gcc/Gcov.html

Gprof
https://sourceware.org/binutils/docs/gprof

OProfile
http://oprofile.sourceforge.net

Perf
https://perf.wiki.kernel.org/index.php/Main_Page