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:
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