Análise Estática com LLVM

LLVM é uma coleção de tecnologias de compiladores (ou algo assim, é que eles usam para se autodefinir), mas esse não é o ponto mais interessante – apesar de existirem diversos subprojetos bastante interessantes, a parte mais legal do LLVM é o subprojeto Clang Static Analyzer – ok, ele é parte do subprojeto Clang na verdade. O Clang utiliza as tecnologias oferecidas pelo LLVM para implementar um compilador completo, com algumas vantagens é extremamente rápido e dá ótimas mensagens de erro.

Meu interessante por análise estática surgiu primeiro quando eu ainda brincava de descompilar programas e entender o que eles faziam, afinal, quando se lê um código sem executá-lo estamos fazendo uma análise. Mas cansei de ler ASM, passei a programar mais e fui atrás de algo que fizesse algo parecido com o que eu fazia. Descobri várias ferramentas que fazem algum tipo de análise estática: lint, sparse e o clang. Sim, lint aquele programa velhão que verificava se um código C era bem formado fazia nada mais do que uma análise estática do código – ok, sem grandes interpretações do que o código fazia. O Sparse é um parser semântico para C, faz mais ou menos a mesma coisa que o lint fazia, mas é um código mais novo, originalmente escrito pelo Linus Torvalds. E finalmente há o Clang…

Clang Static Analyzer

Primeiro passo para testar e usar o Clang é obter os arquivos fonte e compilá-lo, pelo menos não achei uma versão para download (ok, há uma versão para Mac OSX). Há um bom passo-a-passo aqui. Em linhas gerais:

cd tools
svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
cd llvm/tools
svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
cd ..
./configure
make
make install

Atualmente o Clang não instala os scripts scan-build e scan-view, então é necessário copiá-los manualmente. Instalei os scripts em $HOME/bin/, pois já tinha esse caminho no meu path. scan-build é a ferramenta responsável por criar o ambiente necessário para executar o Clang. Com a ferramenta instalada é hora de utilizá-la!

Ah, ele também tem um suporte bem legal a Objective-C, tanto para programas para Mac quanto para aplicativos de iPhone / iPad! Mas, ainda não testei essa funcionalidade.

Usando o Clang

Precisamos de um código com alguns problemas para testar o clang. Resolvi reutilizar o código do post sobre valgrind. Ok, não é o melhor código para demonstrar as funcionalidades do clang, mas é suficientemente bom. Há quatro bugs naquele código, todos foram corretamente identificados pelo valgrind. Quantos desses erros o Clang conseguirá identificar? Usando o scan-build, habilitando verificações experimentais, obtemos:

pedro@urubu:~/code$ scan-build --experimental-checks gcc -g -O1 -o valtest valtest.c
valtest.c:13:2: warning: Access out-of-bound array element (buffer overflow)
        x[5] = 'a';
        ^~~~
valtest.c:20:2: warning: Value stored to 'x' is never read
        x = malloc(5 * sizeof(char));
        ^   ~~~~~~~~~~~~~~~~~~~~~~~~
valtest.c:20:4: warning: Allocated memory never released. Potential memory leak.
        x = malloc(5 * sizeof(char));
        ~~^~~~~~~~~~~~~~~~~~~~~~~~~~
3 warnings generated.
scan-build: 3 bugs found.
scan-build: Run 'scan-view /tmp/scan-build-2010-06-01-4' to examine bug reports.

Dentro de /tmp/scan-build-2010-06-01-4 há um relatório em html com cada erro encontrado, basicamente o código fonte anotado. Um dos erros acima gerou o seguinte relatório:

clang-report

Exemplo de relatório gerado

Resultado, o Clang encontrou três problemas no código, sendo que dois são na mesma linha de código – gerados pelo mesmo problema. Então dos quatro bugs existentes no código a ferramenta identificou dois… nada mal para uma ferramenta que só analizou o código fonte! Claro, há outros bugs que a ferramenta irá identificar e o valgrind não. There is no silver bullet, mas várias ferramentas utilizadas em conjunto conseguem aumentar bastante a qualidade do código escrito.

Duas coisas para terminar este post, não utilizei o clang para gerar código, somente para fazer análise do código fonte, mas isso é perfeitamente viável. E para completar, se formos utilizar o Clang junto a um projeto com autotools, fariamos:

pedro@urubu:~/code/myproject$ scan-build ./configure
pedro@urubu:~/code/myproject$ scan-build make