• Nenhum resultado encontrado

Minerando exceções Runtime não documentadas em bibliotecas Java a partir do GitHub: um estudo exploratório

N/A
N/A
Protected

Academic year: 2021

Share "Minerando exceções Runtime não documentadas em bibliotecas Java a partir do GitHub: um estudo exploratório"

Copied!
80
0
0

Texto

(1)

Departmento de Informática e Matemática Aplicada Programa de Pós-Graduação em Sistemas e Computação

Mestrado Acadêmico em Sistemas e Computação

Minerando Exceções Runtime não

Documentadas em Bibliotecas Java a partir do

GitHub: Um Estudo Exploratório

Lucas Mariano Galdino de Almeida

Natal-RN Agosto de 2018

(2)

Minerando Exceções

Runtime não Documentadas em

Bibliotecas Java a partir do GitHub: Um Estudo

Exploratório

Dissertação de Mestrado apresentada ao Pro-grama de Pós-Graduação em Sistemas e Computação do Departamento de Informá-tica e MatemáInformá-tica Aplicada da Universidade Federal do Rio Grande do Norte como requi-sito para a obtenção do grau de Mestre em Sistemas e Computação.

Linha de pesquisa: Engenharia de Software

Orientadora

Prof

a

Dr

a

Roberta de Souza Coelho

PPgSC – Programa de Pós-Graduação em Sistemas e Computação DIMAp – Departamento de Informática e Matemática Aplicada

CCET – Centro de Ciências Exatas e da Terra UFRN – Universidade Federal do Rio Grande do Norte

Natal-RN Agosto de 2018

(3)

Universidade Federal do Rio Grande do Norte - UFRN Sistema de Bibliotecas - SISBI

Catalogação de Publicação na Fonte. UFRN - Biblioteca Setorial Prof. Ronaldo Xavier de Arruda - CCET

Almeida, Lucas Mariano Galdino de.

Minerando exceções Runtime não documentadas em bibliotecas Java a partir do GitHub: um estudo exploratório / Lucas Mariano Galdino de Almeida. - 2018.

78f.: il.

Dissertação (mestrado) - Universidade Federal do Rio Grande do Norte, Centro de Ciências Exatas e da Terra, Departamento de Informática e Matemática Aplicada, Programa de Pós-Graduação em Sistemas e Computação. Natal, 2018.

Orientadora: Roberta de Souza Coelho.

1. Engenharia de software - Dissertação. 2. Exceção - Dissertação. 3. Relatório de erros - Dissertação. 4.

Conhecimento da multidão - Dissertação. 5. Java - Dissertação. 6. Documentação excepcional - Dissertação. I. Coelho, Roberta de Souza. II. Título.

RN/UF/CCET CDU 004.41

(4)

JavaapartirdoGitHub:UmEstudo Exploratório

ãBTP çMBBIOTPV)N áNM AZUcPGP PGI:ZPGP .POP P NóTIRV)N GN T¥TZUN GI FIBTOI IF íMBTIFPB I NF.ZTPV)N I P.ONLPGP IF BZP áNOFP áMRPU .IUN bONcOPFP GI b=Bt oOPGZPV)N IF íMBTIFPB I NF.ZTPV)N GN çI.POTPFIRTN GI jRáNOF(TM-P I dPTIF(TM-P e.UM-PGP GP SRMLIOBMGPGI IGIOPU GN nMN oOPRGI GN qNOTIa

bOIBMGIRTID çOPa nprãnme çã ípSve pãgfp Epnjãqmeçpne t S nqs

çOa rnSqp dpmme çã enCegfp E NNOGIRPGNO GN bONcOPFPs

Adbtd R^duxbdf:ld

(5)

Agradecimentos

Inicialmente gostaria de agradecer a Deus por ter me possibilitado que chegasse até aqui, pela família que tenho e pelos amigos que ganhei.

Gostaria de agradecer (e dedicar este trabalho) aos meus pais (Erivaldo e Marlene) e ao meu grande irmão (Felipe), que sempre me apoiaram e me incentivaram em tudo. À minha avó (Teresinha), minhas tias, primos e primas por todo o incentivo.

Agradeço em especial à minha esposa Rosemary, por ter aturado todo o stress (que não foi pouco) durante esse período, uma vez que nós dois estávamos cursando simulta-neamente o mestrado, em cidades distantes, e em paralelo não podíamos deixar de cuidar e de dar todo o amor ao nosso querido filho Nicolas. Como se não bastasse, talvez como forma de encerrar um ciclo de nossas vidas e iniciar um novo, nessa reta final de nossos trabalhos fomos agraciados com a chegada do mais novo integrante da família, o risonho Vinicius.

Aos professores com quem tive a oportunidade de aprender bastante durante essa etapa, em especial Roberta Coelho (minha orientadora), por toda paciência, atenção e respeito com que ela trata seus orientandos. Ela foi grande responsável por eu ter chegado até aqui, me instigando a investigar e ao mesmo tempo pedindo para dedicar tempo à família, sempre que necessário. Levo várias lições para o resto da vida.

Também agradeço aos amigos do Laboratório LETS pelo convívio, pelas trocas de experiências e os bate-papos diários. Um grupo muito alto astral. Em especial, à De-móstenes Sena, Eduardo Nascimento, Erica Miranda, Fábio Penha, Gabriela Trindade, Hugo Melo e João Carlos, por todas as discussões realizadas sobre os mais variados temas, dentre eles, esporadicamente, sobre nossos temas de trabalho. Todos contribuíram, direta ou indiretamente para a concretização deste trabalho. Esse grupo também acompanhou minhas emoções de pai, e toda gestação do Vinicius. Foi durante uma de nossas conversas que surgiu a frase que vem me seguindo, principalmente na reta final do mestrado: "Com pouca emoção não vale. (Erica Miranda)".

E finalmente, agradeço ao IFRN por possibilitar o meu afastamento das atividades docentes para que eu pudesse me dedicar às tarefas do mestrado.

(6)
(7)

Bibliotecas Java a partir do GitHub: Um Estudo

Exploratório

Autor: Lucas Mariano Galdino de Almeida Orientador(a): Profa Dra Roberta de Souza Coelho

Resumo

Exceções uncaught não são um cenário excepcional nos sistemas atuais. Estima-se que as exceções uncaught representem até dois terços dos crashes em sistemas Java. Tais exce-ções podem ser lançadas por problemas no código da aplicação, no hardware subjacente ou até mesmo por uma biblioteca utilizada. Muito frequentemente, a documentação sobre as exceções sinalizadas pelos métodos de bibliotecas está ausente ou incompleta. Como consequência, o desenvolvedor geralmente descobre essas exceções quando elas ocorrem no ambiente de produção, causando uma parada abrupta da aplicação. Alternativamente, ferramentas de análise estática podem ser utilizadas para identificar tais exceções, porém essas ferramentas possuem alto custo computacional e podem ser imprecisas. Nesse cená-rio, este trabalho relata um estudo exploratório que extraiu as stack traces inseridas em issues do GitHub para descobrir as exceções não checadas e não documentadas de biblio-tecas. No geral, foram mineradas as issues de 2.970 projetos Java hospedados no GitHub, das quais foram extraídas 66.118 stack traces. Neste estudo, as interfaces excepcionais das 85 bibliotecas mais populares do Maven foram investigadas. Os resultados mostram que a análise estática captura apenas uma pequena parte das exceções que foram reportadas com stack traces em issues do GitHub, e que as informações disponíveis nessas stack tra-ces podem, de fato, ser usadas para descobrir exceções não documentadas lançadas pelos métodos das bibliotecas, enriquecendo assim, as informações obtidas com ferramentas de análise estática.

Palavras-chave: exceção, relatório de erro, conhecimento da multidão, Java, documentação excepcional.

(8)

Author: Lucas Mariano Galdino de Almeida Supervisor: Profa Dra Roberta de Souza Coelho

Abstract

Uncaught exceptions are not an exceptional scenario in today’s systems. Uncaught excep-tions are estimated to account for up to two-thirds of the crashes on Java systems. Such exceptions may be thrown by problems in the application code, the underlying hardware, or even by a library used. Very often, documentation about exceptions flagged by library methods is missing or incomplete. As a consequence, the developer usually discovers these exceptions when they occur in the production environment, causing the application crash. Alternatively, static analysis tools can be used to identify such exceptions. However, these tools have a high computational cost and may be imprecise. In this scenario, this paper reports an exploratory study that extracted the stack traces inserted in issues of GitHub to discover the unchecked and undocumented exceptions of libraries. Overall, the issues of 2,970 Java projects hosted on GitHub were mined, of which 66,118 stack traces were extracted. In this study, the outstanding interfaces of the 85 most popular Maven libraries were investigated. The results show that static analysis captures only a small part of the exceptions that have been reported with GitHub’s stack traces in issues, and that the information available in these stack traces can, in fact, be used to discover undocumen-ted exceptions released by library methods, thus enriching the data obtained with static analysis tools.

(9)

Lista de figuras

1 Relação entre API, biblioteca e framework. . . p. 21 2 Hierarquia de classes de exceção da linguagem Java. . . p. 23

3 Estrutura básica para captura e tratamento de exceções em Java. . . . p. 24

4 Identificação dos elementos que compõem uma stack trace Java. . . p. 26

5 Distribuição por modificador de acesso, dos métodos das bibliotecas

ana-lisadas. . . p. 32

6 Documentação dos métodos públicos das bibliotecas analisadas. . . p. 33

7 Ocorrências de exceções documentadas, das bibliotecas analisadas,

cate-gorizadas por supertipo. . . p. 33 8 Visão geral do estudo. . . p. 34 9 Modelo relacional da base de stack traces. . . p. 35

10 Modelo relacional da base de dados de documentação excepcional. . . . p. 38

11 Método org.my.corporation.utils.FileUtil.fileToList. . . p. 39

12 Principais ferramentas que compõem a suite ExMinerSuite. . . p. 41

13 Bibliotecas identificadas com análise de stack traces. . . p. 47 14 Bibliotecas identificadas com interfaces excepcionais descobertas. . . p. 47 15 Bibliotecas agrupadas de acordo com as issues. . . p. 51 16 Código que exemplifica dois pares método-exceção. . . p. 53 17 Exceções descobertas por cada abordagem, para o conjunto de bibliotecas

alvo. . . p. 54

18 Método channelRead da classe ReadTimeoutHandler. . . p. 55

19 Distribuição por modificador de visibilidade, dos métodos que compõem

(10)

das por supertipo. . . p. 72 22 Bibliotecas identificadas com análise de stack traces. . . p. 73 23 Bibliotecas identificadas com exceções descobertas. . . p. 74 24 Libs agrupadas de acordo com as issues. . . p. 77 25 Exceções descobertas por cada abordagem de análise. . . p. 78

(11)

Lista de tabelas

1 Hardware utilizado para executar a PLEA. . . p. 39

2 Top 10 exceções mais recorrentes das stack traces, considerando apenas

exceções runtime. . . p. 43

3 Exceções lançadas em caso de terminação abrupta durante avaliação de

uma expressão. . . p. 44

4 Top 10 exceções mais recorrentes das stack traces, considerando apenas

exceções runtime, não lançadas implicitamente. . . p. 44 5 Top 10 exceções não Java, mais recorrentes das stack traces, considerando

apenas exceções runtime. . . p. 45

6 Top 10 métodos presentes na base do GitHub, que mais geraram exceções

runtime, desconsiderando exceções implícitas do Java. . . p. 45

7 Top 10 métodos presentes na base do GitHub, que não são da API Java,

que mais geraram exceções runtime, desconsiderando exceções implícitas

do Java. . . p. 46 8 Top 10 bibliotecas mais recorrentes das stack traces, considerando apenas

exceções descobertas, do conjunto de bibliotecas alvo. . . p. 48

9 Top 10 bibliotecas com mais exceções descobertas por método. . . p. 49

10 Top 10 exceções mais recorrentes, descobertas em stack traces, para as

bibliotecas do conjunto alvo. . . p. 49 11 Top 10 bibliotecas que mais tiveram issues com stack traces relacionadas

com exceções descobertas. . . p. 50

12 Métodos que mais impactaram em cada uma das Top 10 bibliotecas alvo. p. 51

13 Top 10 bibliotecas mais recorrentes das stack traces, considerando apenas

(12)

15 Top 10 exceções mais recorrentes, descobertas em stack traces, para as

bibliotecas do conjunto. . . p. 76 16 Top 10 bibliotecas que mais tiveram issues com stack traces relacionadas

com exceções descobertas. . . p. 76

(13)

Lista de abreviaturas e siglas

LETS – Laboratório de Especificação e Teste de Software

IFRN – Instituto Federal de Educação, Ciência e Tecnologia do Rio Grande do Norte API – Application Programming Interface

JVM – Java Virtual Machine URL – Uniform Resource Locator

JavaSE – Java Plataform, Standard Edition JavaEE – Java Plataform, Enterprise Edition POM – Project Object Model

JDK – Java Development Kit

PLEA – Product Line Exception Analyzer WALA – T. J. Watson Libraries for Analysis

(14)

1 Introdução p. 15 1.1 Contexto e Motivação . . . p. 15 1.2 Limitações de Trabalhos Existentes . . . p. 17 1.3 Objetivos . . . p. 18 1.4 Metodologia . . . p. 18 1.5 Organização do trabalho . . . p. 19

2 Fundamentação Teórica p. 20

2.1 API, Biblioteca e Framework . . . p. 20 2.2 Mecanismos de Tratamento de Exceções . . . p. 21

2.2.1 Mecanismo de Tratamento de Exceções em Java . . . p. 22

2.2.2 Interface Excepcional . . . p. 24

2.2.3 Análise Estática para Descoberta de Interface Excepcional . . . p. 25

2.3 Stack traces . . . p. 26 2.4 Análise de Stack traces . . . p. 27 2.5 GitHub . . . p. 27 3 Minerando Exceções Runtime não Documentadas a partir do GitHub:

Um Estudo Exploratório p. 29

3.1 Objetivo e Questões de Pesquisa . . . p. 29 3.2 Bibliotecas Alvo . . . p. 31

3.3 O Processo de Mineração de Exceções a Partir do GitHub . . . p. 33

(15)

3.3.3 Passo 3: Download dos Artefatos Alvo da Análise . . . p. 35 3.3.4 Passo 4: Identificação das Exceções das Bibliotecas . . . p. 36

3.3.5 Passo 5: Extração da Documentação Excepcional . . . p. 36

3.3.6 Passo 6: Execução da Ferramenta PLEA . . . p. 38

3.3.7 Passo 7: Identificação dos Métodos das Bibliotecas a partir das

Stacks . . . p. 39 3.3.8 Passo 8: Descoberta de Exceções Runtime não Documentadas a

partir das Stacks . . . p. 40 3.4 Apoio Ferramental . . . p. 40 3.5 Dataset utilizado . . . p. 43 3.6 Respondendo às Questões de Pesquisa . . . p. 45

3.6.1 (RQ1) Quantas das bibliotecas alvo do estudo puderam ser

iden-tificadas nas stack traces? . . . p. 46 3.6.2 (RQ2) Quantas das bibliotecas alvo do estudo puderam ter

exce-ções descobertas através da análise das stack traces? . . . p. 47 3.6.3 (RQ3) Quais as características das exceções descobertas através

da análise de stack traces? . . . p. 48

3.6.4 (RQ4) Qual o impacto das exceções descobertas? . . . p. 50

3.6.5 Disponibilizando os Resultados do Estudo . . . p. 52

3.7 Comparando Análise Estática com Mineração de stack traces . . . p. 52

3.7.1 Exceções Mineradas e não Detectadas Estaticamente . . . p. 54

3.8 Ameaças à Validade . . . p. 55 3.9 Considerações Finais . . . p. 56

4 Trabalhos Relacionados p. 58

4.1 Análise de Stack Traces . . . p. 58 4.2 Estudos que Mineram Informações sobre APIs . . . p. 61

(16)

5 Conclusões p. 65 5.1 Trabalhos Futuros . . . p. 66

Referências p. 67

Apêndice A -- Outro conjunto de bibliotecas p. 71

A.1 Bibliotecas Alvo . . . p. 71 A.2 Respondendo às Questões de Pesquisa . . . p. 73

A.2.1 (RQ1) Quantas das bibliotecas alvo do estudo puderam ser

iden-tificadas nas stack traces? . . . p. 73 A.2.2 (RQ2) Quantas das bibliotecas alvo do estudo puderam ter

exce-ções descobertas através da análise das stack traces? . . . p. 74 A.2.3 (RQ3) Quais as características das exceções descobertas através

da análise de stack traces? . . . p. 75

A.2.4 (RQ4) Qual o impacto das exceções descobertas? . . . p. 76

(17)

1

Introdução

1.1 Contexto e Motivação

A inoperância ou até mesmo o encerramento abrupto (do inglês, crash) de aplicações Java, causados por problemas no código de tratamento de exceções, não são situações excepcionais. Estima-se que aproximadamente até dois terços do total dos crashes de aplicações Java sejam causados por exceções não capturadas (do inglês, uncaught excepti-ons)(MAXION; OLSZEWSKI, 1998). Tais exceções podem ser lançadas na própria aplicação,

pelo sistema ou hardware utilizado, ou mesmo por bibliotecas e interfaces de programa-ção de aplicações - APIs (do inglês, Application Programming Interface) de terceiros que estejam em uso.

Bibliotecas de software (do inglês, library ou simplesmente lib) fornecem APIs que permitem sua utilização por outras aplicações, porém podem causar falhas em aplicações

cliente devido a: (i) um uso inadequado das APIs (WANG; GODFREY, 2013)(

LINARES-VÁSQUEZ et al., 2014), (ii) erros de programação na própria biblioteca (THOMAS, 2002), e (iii) exceções de tempo de execução (do inglês, runtime exceptions) não documentadas, lançadas pelos métodos (COELHO et al., 2015)(KECHAGIA; SPINELLIS, 2014)(SENA et al.,

2016). Tradicionalmente, muitos tipos de documentação de software, como documentação de APIs, são geradas por poucas pessoas e têm um público alvo muito maior. A documen-tação resultante, quando existente, geralmente tem baixa qualidade e carece de exemplos e explicações (PARNIN et al., 2012).

Segundo Joshua Bloch (2008), se uma biblioteca ou API for utilizável, ela deve ser documentada, e uma descrição das exceções lançadas por um método é uma parte im-portante da documentação necessária para utilização correta da mesma. O conjunto das exceções propagadas por um método é denominado interface excepcional do método ( MIL-LER; TRIPATHI, 1997). Como na maioria das vezes, a documentação de software referente

à interface excepcional dos métodos está ausente ou incompleta (KECHAGIA; SPINELLIS,

(18)

implementar códigos tolerantes a falhas, pois simplesmente eles não têm como saber que elas podem ocorrer. Tratando-se de exceções não checadas (do inglês, unchecked excep-tion) que não estão documentadas, muitas vezes só são manifestadas quando estas causam crashes no sistema em ambiente de produção. Um dos estudos recentes realizados por Ke-chagia e Spinellis (2014) apontou que 19% dos crashes identificados em 1,8 mil aplicações Android foram causados por exceções não checadas e não documentadas.

Por padrão, em aplicações Java, quando ocorre algum crash causado por uma exceção não capturada, a máquina virtual Java (JVM - Java Virtual Machine) imprime um rastreamento da pilha (do inglês, stack trace). As informações contidas nas stack traces geralmente são usadas pelos desenvolvedores para rastrear a causa da falha e encontrar possíveis soluções. Geralmente estas stack traces são adicionadas em bug reports, sendo muito úteis para entender e solucionar as causas de crashes (BETTENBURG et al., 2008). Além disso, frequentemente, o entendimento da causa e as possíveis soluções são obtidos a partir do conhecimento disseminado pela comunidade de desenvolvedores em diversas plataformas, como fóruns de discussão, blogs, sites de perguntas e respostas e ferramentas especializadas.

Diante deste cenário surge a seguinte questão: Podemos utilizar informações de crash de outras aplicações para prevenir crashes similares? Em outras palavras, podemos apren-der algo com os crashes ocorridos em outras aplicações, como por exemplo descobrir exceções runtime e não documentadas, para com isso tornarmos nossas aplicações mais robustas?

Os repositórios de código e issues (do inglês, source code repositories e issue trackers) estão sendo cada vez mais utilizados e merecem destaque. Equipes de desenvolvimento costumam utilizar estes repositórios para, além de gerenciar o código e as mudanças, reportar erros, sugerir melhorias, contribuindo assim para a construção e consolidação de uma base de conhecimento comum ao contexto do sistema desenvolvido. Dessa maneira, podemos observar que ferramentas de issue trackers são promissoras fontes de informação sobre crashes já ocorridos em sistemas open source.

Considerando esse contexto, este trabalho aborda o problema da falta de documenta-ção excepcional relacionada às exceções não checadas lançadas por métodos de bibliotecas java. Mais precisamente abordaremos o conjunto das exceções runtime, ou seja, aquelas exceções que herdam direta ou indiretamente da classe java.lang.RuntimeException. Como apresentado em trabalhos anteriores, a falta de documentação acerca de tais exce-ções pode fazer com que o desenvolvedor não esteja ciente de que elas podem ocorrer. Isso

(19)

pode impactar diretamente na implementação de códigos capazes em lidar com condições excepcionais inesperadas, o que poderá resultar em exceções uncaught e possivelmente em interrupções abruptas durantes a execução (i.e. crashes) das aplicações.

Essa investigação se faz importante, uma vez que atualmente os desenvolvedores dis-põem apenas da documentação das bibliotecas para se informarem a respeito de exceções que podem ser lançadas por alguma biblioteca em uso. Alternativamente podem ser utili-zadas ferramentas de análise estática para realizar a identificação de tais exceções, porém essas ferramentas encontram-se atualmente apenas em ambientes acadêmicos, são compu-tacionalmente bastante custosas e ainda podem ser imprecisas. Por outro lado, a análise dinâmica é mais precisa, uma vez que verifica as propriedades em tempo de execução (BALL, 1999), mas também pode ser cara de ser obtida, justamente por essa necessidade

de execução do sistema analisado.

1.2 Limitações de Trabalhos Existentes

Algumas soluções foram propostas até o momento com o objetivo de descobrir interfa-ces excepcionais de programas Java (PÁDUA; SHANG, 2017;SENA et al., 2016;FERNANDES,

2017).

Duas destas abordagens se baseiam em análise estática de código para identificar fluxos de exceções, porém esta técnica pode sofrer com limitações quando combinada com características de linguagens modernas (e.g. herança, polimorfismo, chamadas virtuais e reflexão), que induzem a falsos positivos. Outra característica dessas técnica é a relação entre custo computacional e precisão da informação gerada - quanto maior a precisão maior o custo computacional. Diante desse cenário, as mesmas duas abordagens apontam para uma possível complementariedade entre as abordagens de análise estática e dinâmica. Durante nossas pesquisas, não identificamos trabalhos que comparem essas abordagens.

Alguns trabalhos também utilizam a mineração de informações de API (PARNIN et

al., 2012; SOUZA; CAMPOS; MAIA, 2014a;CAMPOS et al., 2014), porém dois deles utilizam

apenas o Stackoverflow como fonte de dados. Apenas o trabalho de Campos et al. (2014) faz uso do GitHub, porém este consome apenas as issues referentes ao projeto em que o desenvolvedor está envolvido, desconsiderando toda a base de stack traces formulada por toda a rede de usuários da plataforma.

(20)

1.3 Objetivos

O objetivo primário deste trabalho consiste em investigar o potencial da mineração de stack traces em repositórios de código, mais precisamente as que são postadas em issues no GitHub, como fonte de informação capaz de auxiliar os desenvolvedores em identificar exceções runtime que não estão documentadas em bibliotecas java, que potencialmente podem fluir destas. O segundo objetivo é aferir se, e até que ponto, a análise das stack traces pode de alguma forma enriquecer e baratear o custo computacional da análise estática.

Para atingir os objetivos deste trabalho, foram definidos como objetivos específicos: i. Verificar se é possível identificar bibliotecas java a partir de stack traces reportadas

no GitHub;

ii. Verificar se é possível descobrir, através da análise das stack traces, exceções runtime não documentadas que fluem por bibliotecas java;

iii. Investigar se as stack traces analisadas podem enriquecer as informações obtidas através de técnicas de análise estática;

1.4 Metodologia

Para atingir aos objetivos elencados acima, este trabalho foi conduzido nas seguintes etapas:

i. Criamos uma base de dados para armazenar as stack traces obtidas do GitHub; ii. Criamos uma segunda base de dados para armazenar as exceções extraídas da

do-cumentação Java;

iii. Baixamos o código fonte das bibliotecas alvo;

iv. Extraímos e armazenamos as exceções identificadas através da análise do código fonte das bibliotecas alvo;

v. Criamos uma terceira base de dados para armazenar o resultado da extração da documentação excepcional;

(21)

vi. Identificamos os métodos das bibliotecas e descobrimos as interfaces excepcionais não documentadas; e

vii. Comparamos os resultados da mineração com os resultados obtidos através da exe-cução de uma ferramenta de análise estática de fluxos excepcionais.

1.5 Organização do trabalho

Além deste capítulo introdutório, o restante do trabalho está organizado em outros quatro capítulos descritos asseguir. O Capítulo 2 norteia o leitor, através de uma breve fundamentação, quanto aos principais conceitos utilizados, com o objetivo de promover um melhor entendimento do trabalho. O Capítulo 3 apresenta o estudo realizado para minerar as interfaces excepcionais utilizando as stack traces embutidas nas issues criadas no sistema de issue tracker do GitHub. Dando continuidade, o Capítulo 4 apresenta uma série de trabalhos que estão, de alguma forma, relacionado com este estudo. Por fim, no Capítulo 5 apresentamos nossas conclusões e alguns trabalhos futuros que vislumbramos como desdobramentos do atual.

(22)

2

Fundamentação Teórica

O tratamento de exceções há muito é estudado com o intuito de prover e aperfeiçoar mecanismos para identificação e tratamento de ocorrência de condições excepcionais de software. Tais condições alteram o fluxo normal de execução do programa. O objetivo deste capítulo é nortear o leitor, através de uma breve fundamentação, quanto aos principais conceitos utilizados no trabalho, que estão relacionados com a temática abordada. Para isso, serão apresentados conceitos de API, biblioteca e framework (Seção 2.1), mecanismos de tratamento de exceções (Seção 2.2), adentrando um pouco mais, será apresentada especificamente o mecanismo de tratamento de exceções da linguagem Java (Seção 2.2.1). Prosseguindo, são apresentados os conceitos de interface excepcional (Seção 2.2.2), stack traces (Seção 2.3), análise de stack traces (Seção 2.4) e por fim abordamos GitHub (Seção 2.5).

2.1 API, Biblioteca e

Framework

Uma API (acrônimo que vem do inglês, Application Programming Interface) ou in-terface de programação de aplicações, é a inin-terface que determina o modo como dois elementos de software interagem entre si. Também pode ser entendida como uma inter-face que orienta os desenvolvedores em como acessar e utilizar os recursos, funções e métodos de uma biblioteca ou serviço, não entrando nos detalhes de como tais métodos e funções estão implementados.

Biblioteca de software (do inglês, library ou simplesmente lib), é um conjunto de re-cursos, funções e métodos, geralmente organizados em classes, responsável por auxiliar na realização de uma determinada tarefa. Bibliotecas são construídas e distribuídas de tal forma que possibilite o reaproveitamento das funcionalidades implementadas. São ampla-mente utilizadas no universo de desenvolvimento de software, uma vez que o reuso pode aumentar a produtividade dos desenvolvedores. As aplicações cliente, fazem chamadas aos recursos, funções ou métodos das bibliotecas para realizarem ou auxiliarem na realização

(23)

de sua tarefa.

Para o contexto deste trabalho, consideramos framework como uma espécie de mo-delo, arcabouço, ou plataforma de desenvolvimento, que possue pontos claros de extensão, onde o desenvolvedor deve acoplar suas aplicações. Geralmente os frameworks são capazes de gerenciar a execução de uma aplicação, para isso eles fazem uso de diversas bibliotecas para fornecerem as mais variadas funcionalidades. Por estas características, a implemen-tação dos frameworks são transparentes para as aplicações clientes, cabendo a esses a responsabilidade de invocar o código da aplicação cliente.

A relação entre API, biblioteca e framework é apresentada graficamente na Figura 1.

Figura 1: Relação entre API, biblioteca e framework.

2.2 Mecanismos de Tratamento de Exceções

Tratar uma exceção é o ato de tomar medidas corretivas a fim de recuperar o estado de um sistema após detectar-se a ocorrência de uma exceção (GARCIA et al., 2001). O

tratamento de exceções (GARCIA et al., 2001; GOODENOUGH, 1975) é uma característica

de grande parte das linguagens de programação que adotam o paradigma da orientação a objeto.

Mecanismos de tratamento de exceções são modelos usados para estruturar o fluxo excepcional de um módulo de software através da detecção e sinalização da ocorrência de

exceções (GOODENOUGH, 1975). Estes mecanismos são a forma mais comuns para lidar

(24)

Um dos objetivos dos mecanismos de tratamento de exceções é permitir que desenvol-vedores de software implementem sistemas mais robustos e tolerantes a falhas. Enquanto que, uma das vantagens dos mecanismos de tratamento de exceções é prover uma melhor separação entre a implementação da lógica relacionada ao provimento do serviço oferecido pelo sistema e a lógica responsável por recuperar o sistema para um estado correto, caso

uma exceção ocorra durante a sua execução (GOODENOUGH, 1975).

Linguagens como Java, C#, Python e Swift, dentre outras, trazem mecanismos de tratamento de exceções embutidos. Esse recurso é frequentemente utilizado pelos desen-volvedores, para implementar soluções de software mais confiáveis e tolerantes a falhas. Entretanto, estes recursos que foram idealizados para aumentar a robustez dos sistemas são muitas vezes fonte de falhas (COELHO et al., 2015, 2008;BARBOSA; GARCIA; BARBOSA,

2014). Após a análise dos mecanismos de tratamento de exceção de diversas linguagens orientadas a objeto, realizada por Garcia et al. (GARCIA et al., 2001), foi possível observar

que o modelo proposto por Goodenough (GOODENOUGH, 1975) continua bastante aceito.

Os mecanismos de tratamento de exceções proveem suporte à detecção da ocorrência de condições excepcionais e à implementação de medidas corretivas. O objetivo dessas medidas é, permitir que depois de detectada a ocorrência de uma exceção, o sistema possa ser restaurado para um estado em que possa continuar provendo o seu serviço corretamente. O mecanismo de tratamento de exceções é responsável por interromper o fluxo de execução normal do sistema para o fluxo excepcional quando uma exceção é sinalizada. Após a exceção ser tratada, o mecanismo de tratamento de exceções retoma o fluxo de execução normal.

2.2.1 Mecanismo de Tratamento de Exceções em Java

Java é uma linguagem orientada a objetos, e por tanto, suas exceções também são representadas por objetos. Tais objetos são instâncias de classes pertencentes a uma hie-rarquia bem definida. Toda classe de exceção em Java herda direta ou indiretamente da classe Throwable. A Figura 2 apresenta a organização hierárquica das classes de exceções do Java, segundo sua especificação1.

As subclasses Error e Exception são utilizadas para distinguir exceções que nor-malmente são irrecuperáveis das que podem ser recuperáveis, respectivamente. Qualquer exceção que herde da classe Error ou de uma classe descendente dela, deve representar

(25)

Figura 2: Hierarquia de classes de exceção da linguagem Java.

uma condição da qual não se espera recuperação. Tais exceções geralmente são detec-tadas no ambiente de tempo de execução, como resultado da detecção de deficiência de recursos ou outras condições, das quais o software não pode se recuperar (GOSLING et al.,

2014; BLOCH, 2008). Já as exceções da família Exception devem representar condições

das quais o sistema pode querer se recuperar.

RuntimeException é uma subclasse direta de Exception. Ela é a superclasse de todas as exceções que podem ser lançadas por diversas razões durante a avaliação de uma expressão, mas da qual a recuperação ainda é possível. RuntimeException e todas as suas subclasses são conhecidas como exceções de tempo de execução (do inglês, runtime exceptions).

Em seu mecanismo de tratamento de exceção, a linguagem Java também faz uma separação clara entre exceções verificadas (do inglês, checked exceptions) e exceções não verificadas (do inglês, unchecked exceptions). Para as exceções verificadas, a linguagem analisa, em tempo de compilação, a existência de tratadores para cada uma das exceções declaradas. Compõem o conjunto das exceções não verificadas, as classes de exceção de tempo de execução, a classe Error e suas subclasses. As classes de exceção verificadas são as demais classes, que não integram o conjunto de exceções não verificadas.

A linguagem Java fornece uma série de estruturas para lidar com exceções. Os prin-cipais termos da sintaxe utilizada para tratar exceções em Java são apresentados na Fi-gura 3.

Uma região de código é denominada protegida, se estiver envolvida por um bloco try. Para cada região protegida, um ou mais blocos catch podem ser vinculados, definindo qual exceção deverá ser capturada e qual a ação a ser realizada em cada ocorrência. Quando

(26)

Figura 3: Estrutura básica para captura e tratamento de exceções em Java.

uma exceção é lançada dentro de uma região protegida, o fluxo normal de execução do programa é interrompido exatamente na linha de código em que a exceção ocorre, e em seguida transferido para o bloco de tratamento adequado. O bloco responsável por capturar e tratar a exceção será o primeiro bloco catch, atrelado à região, que contiver como parâmetro o mesmo tipo (ou superclasse – classe de nível superior na hierarquia) da exceção lançada.

2.2.2 Interface Excepcional

Quando o desenvolvedor deseja fazer uma chamada a um método, seja este desen-volvido por ele próprio ou por terceiros, é importante conhecer, além do nome, quais são seus parâmetros e seus respectivos tipos. O conjunto dessas informações é conhecido como assinatura do método (GOSLING et al., 2014; BLOCH, 2008).

Algumas linguagens, como por exemplo Java, permitem associar a assinatura de um método à uma lista de possíveis exceções que esse método pode lançar. O conjunto das exceções propagadas por um método é denominado interface excepcional do método ( MIL-LER; TRIPATHI, 1997). Esse conjunto é composto por exceções verificadas (do inglês,

checked exceptions), explicitamente vinculadas à assinatura do método, e exceções não verificadas (do inglês, unchecked exceptions).

Durante a execução, o método pode deparar-se com situações excepcionais. Para que o desenvolvedor possa tratar adequadamente tais situações, é importante também ter ciência de quais exceções podem ser lançadas. Por esse motivo, tão importante quanto conhecer a assinatura do método, é conhecer sua interface excepcional.

Além de fornecer informações importantes para os usuários do método, algumas lin-guagens permitem que a interface excepcional seja verificada em tempo de compilação. Isto, visa garantir que manipuladores tenham sido definidos para cada uma das exceções. Idealmente, a interface excepcional deve fornecer informações completas e precisas. Na

(27)

maioria das vezes, no entanto, estas informações não são completas e, muito menos, pre-cisas (CABRAL; MARQUES, 2007), porque linguagens, como Java, oferecem uma forma de

ignorar esse mecanismo, através do lançamento de um tipo específico de exceção.

As exceções não verificadas ou ainda exceções de tempo de execução (inglês, runtime exception), não exigem nenhuma declaração associada à assinatura do método, como também não exigem que sejam capturadas e tratadas. Devido a incompletude e imprecisão de muitas interfaces excepcionais, alguns trabalhos (COELHO et al., 2008; FERNANDES,

2017) convencionaram classificar tais interfaces em duas categorias:

i. Interface excepcional explícita – refere-se às exceções que estão associadas à assinatura do método, através da lista de possíveis exceções lançadas pelo método, ou explicitamente sinalizadas no corpo do método; e

ii. Interface excepcional completa – que engloba toda a interface excepcional ex-plícita, e também as exceções não verificadas que não estão contidas na lista de exceções associadas à assinatura do método.

Com o objetivo de ser o mais abrangente e completo possível, ao longo deste trabalho, a menos que seja explicitamente mencionado, a expressão “interface excepcional” será usada como referência a interface excepcional completa.

2.2.3 Análise Estática para Descoberta de Interface Excepcional

Alguns trabalhos foram propostos com o objetivo de identificar a interface excepcional de métodos Java através de análise estática de código (SENA et al., 2016; FERNANDES,

2017; PÁDUA; SHANG, 2017). A análise estática consiste de um conjunto de técnicas que

extraem informação do código de uma aplicação (COOPER; TORCZON, 2011). É o processo

de analisar o código enquanto ele não está em execução (LEVINSON; NELSON, 2006). Nesse

tipo de abordagem, o software é analisado e as informações das propriedades investigadas são extraídas sem a necessidade da execução, bastando para isso, a análise do código da aplicação, por exemplo, bytecode e/ou do código fonte (NIELSON; NIELSON; HANKIN, 2015; CHESS; WEST, 2007).

Existem diversas técnicas de análise estática, dentre elas, as categorizadas como in-trapocedurais e interprocedurais. Na análise intraprocedural, as instruções e fluxos de controle que compõem um método são analisados, ao passo que na análise

(28)

interprocedu-ral são analisadas as interações (fluxo de chamadas) entre os métodos (NIELSON; NIELSON; HANKIN, 2015).

A geração de grafo de chamadas (do inglês, call graph) com técnicas de análise estática está suscetível à incompletude, uma vez que essa abordagem precisa lidar com o problema de custo computacional versus precisão, como apresentado por Tip e Palsberg (2000). Devido a natureza da análise estática, esta técnica pode não ser completamente precisa, podendo apresentar limitações quando combinada com características de linguagens mo-dernas (por exemplo, polimorfismo, sobrecarga de métodos não resolvidos, reflexão ou até mesmo configurações dinâmicas através de arquivos ou características do ambiente de execução). Nesse sentido, os trabalhos (PÁDUA; SHANG, 2017; SENA, 2017) que fazem uso

desse tipo de análise para identificar as interfaces excepcionais esbarram no custo compu-tacional e na precisão da informação gerada - podendo gerar falsos positivos e negativos.

2.3

Stack traces

Quando uma aplicação termina inesperadamente, o sistema produz um relatório da falha podendo conter um rastreamento da pilha (do inglês, stack trace) e metadados de tempo de execução (KECHAGIA; SPINELLIS, 2014). Em Java, a stack trace resultante é uma

cadeia de quadros (do inglês, frames) de chamadas de método, que leva a uma exceção e fornece informações sobre a causa de falha.

Figura 4: Identificação dos elementos que compõem uma stack trace Java.

A Figura 4 ilustra um exemplo de stack trace em Java, realçando cada um dos ele-mentos, que a compõem. No exemplo apresentado, é possível visualizar que a exceção lançada foi java.lang.NullPointerException. Esta exceção foi sinalizada pelo método

(29)

addCoverage durante a execução da linha 378, do arquivo FileDate.java. Até ser lan-çada pelo método sinalizador, a exceção ainda fluiu pelos demais métodos listados na stack trace.

Na mesma figura, o primeiro frame está destacado para apresentar a estrutura comum de um frame em uma stack trace. Cada frame pode fornecer as informações da classe, método, arquivo e linha do código relacionadas com a exceção que está sendo sinalizada.

2.4 Análise de

Stack traces

A análise dinâmica é uma classe de técnicas que verificam as propriedades dos sistemas em tempo de execução, assim, extraindo informações mais precisas dos comportamentos a serem observados que outras classes de técnicas de análises (BALL, 1999).

O fato de ser necessário executar a aplicação analisada, pode caracterizar uma limi-tação desta abordagem, uma vez que as propriedades a serem observadas precisam ser exercitadas com um conjunto de dados de entrada mais abrangente possível. Por este motivo, caso uma propriedade não seja completamente exercitada, poderá acarretar em falso-negativo.

As stack traces são resultantes de términos abruptos de algum fluxo de execução. Elas registram fluxos de execução de um programa até que haja uma interrupção súbita. Stack traces reportadas em bug reports representam execuções nos mais diversos domínios de aplicações, nos mais variados ambientes de execução e por diferentes indivíduos, reduzindo assim a limitação inerente a esta abordagem.

2.5 GitHub

O GitHub2 é uma plataforma de hospedagem de projetos, que devido características

amigáveis aos desenvolvedores de software, obteve uma adoção generalizada (GOUSIOS; SPINELLIS, 2012). Com aproximadamente 26 milhões de usuários e mais de 74 milhões de

projetos hospedados, GitHub é atualmente a plataforma de hospedagem de projetos mais popular3.

A plataforma baseia-se no sistema de gerenciamento de versão Git (LOELIGER; MC-CULLOUGH, 2012) para gerenciar todos os arquivos do projeto. A plataforma também

2https://github.com

(30)

facilita as contribuições por parte dos interessados que não são membros da equipe atra-vés de recursos como a bifurcação (do inglês, fork). Com este recurso, o desenvolvedor cria uma cópia privada de um repositório Git. Ao concluir sua contribuição, pode ser utilizado o recurso de solicitação de integração, chamado pull request. Além disso, o GitHub oferece uma gama de ferramentas que auxiliam as atividades de desenvolvimento de software, como um issue tracker, um wiki e ferramentas de comunicação para desenvolvedores.

(31)

3

Minerando Exceções Runtime não

Documentadas a partir do

GitHub: Um Estudo Exploratório

Neste capítulo, é apresentado o estudo realizado para minerar as exceções runtime não documentadas que fluíram por métodos de bibliotecas java, utilizando para isso, as stack traces embutidas nas issues criadas no sistema de issue tracker do GitHub. Ao final do estudo, investigamos se as exceções identificadas através da análise das stack traces podem enriquecer e baratear o custo computacional necessário atualmente, para identificar tais exceções através de técnicas de análise estática.

Nosso estudo inicia com a apresentação do objetivo e das questões de pesquisa que nos nortearam (Seção 3.1), em seguida apresentamos o conjunto de biblioteca alvo de nosso estudo (Seção 3.2), logo após descrevemos o processo de mineração que utilizamos (Seção 3.3), o conjunto de ferramentas desenvolvidas e utilizadas no processo de minera-ção é apresentado na seminera-ção de Apoio Ferramental (Seminera-ção 3.4), a base de dados de stack traces utilizada no estudo é caracterizada na seção Dataset (Seção 3.5), em seguida res-pondemos às questões de pesquisa (Seção 3.6), mostramos a comparação da identificação de exceções através da mineração de stack traces e uma ferramenta de análise estática (Seção 3.7), apresentamos as ameaças à validade envolvidas em nosso estudo (Seção 3.8), e para concluir o capítulo, apresentamos nossas considerações finais (Seção 3.9).

3.1 Objetivo e Questões de Pesquisa

O objetivo de nosso estudo de mineração foi explorar o conhecimento embutido em repositórios de aplicações open-source, para investigar se esse conhecimento seria capaz de auxiliar os desenvolvedores em identificar exceções runtime que não estão documentadas em bibliotecas java, e que potencialmente possam fluir para suas aplicações. Mais especifi-camente, analisamos stack traces postadas em issues no GitHub, para extrair as exceções

(32)

que de fato ocorreram durante a execução de alguma aplicação. Uma vez identificadas as exceções runtime não documentadas, as confrontamos com as exceções obtidas através de uma abordagem baseada em análise estática de código. Essa comparação é importante para verificarmos se de fato, a mineração de stack traces foi capaz de identificar as mesmas identificadas pela ferramenta de análise estática, ou mesmo alguma outra não identificada, podendo assim enriquecer as informações providas por esta.

O estudo de mineração foi guiado pela seguinte questão de pesquisa geral:

RQ: Stack traces contidas em issues do GitHub podem enriquecer as in-formações obtidas por análise estática para identificar exceções runtime não documentadas em bibliotecas java?

Para auxiliar no entendimento, e assim podermos analisar com mais detalhes os dados observados, a questão principal foi refinada e dela derivamos as subquestões:

RQ1: Quantas das bibliotecas alvo do estudo puderam ser identificadas nas stack traces?

A primeira subquestão de pesquisa busca verificar se é possível identificar bibliotecas java a partir de stack traces. Nesse sentido, ela é respondida após analisarmos o código fonte de um conjunto de bibliotecas alvo (ver Seção 3.2) e confrontarmos suas classes e métodos com os frames que compõem as stack traces.

RQ2: Quantas das bibliotecas alvo do estudo puderam ter exceções desco-bertas através da análise das stack traces?

Essa subquestão de pesquisa é utilizada para mostrar o quanto a análise de stack traces pode contribuir para a identificação de exceções runtime não documentadas, dado um determinado conjunto de bibliotecas. Nesse sentido, ela é respondida após analisarmos e confrontarmos as classes e métodos de cada biblioteca alvo com os frames das stack traces. Com isso, podemos identificar as interfaces excepcionais compostas por exceções checadas ou não checadas e ainda as documentadas ou não documentadas.

RQ3: Quais as características das exceções descobertas através da análise de stack traces?

A terceira subquestão de pesquisa foi derivada para investigar as características das exceções runtime não documentadas que foram descobertas. O objetivo é verificar se existe alguma característica comum às exceções descobertas através da análise de stack traces.

(33)

Esta questão busca medir o impacto causado aos desenvolvedores, em decorrência da ausência de informações sobre as exceções descobertas. Medimos o "impacto"a partir do número de issues que possuem stack traces que fornecem informações sobre exceções run-time não documentadas que fluem por métodos das bibliotecas alvo - em outras palavras, quantas stack traces podem ter sido causadas por exceções runtime que não estavam do-cumentadas - e quantos projetos foram impactados por tais exceções, ou seja em quantos repositórios diferentes estas stack traces foram reportadas.

3.2 Bibliotecas Alvo

Para a realização deste trabalho selecionamos um conjunto de bibliotecas alvo para serem analisadas. Essas bibliotecas foram extraídas da listagem dos 100 projetos que compõem o ranking4 dos projetos mais populares existentes no repositório central do

Maven em 30/05/2018. A popularidade de uma biblioteca no repositório Maven é medida de acordo com a quantidade de artefatos indexados, que a utiliza como dependência.

Optamos por esse conjunto por dois motivos: (i) o Maven é uma ferramenta de ge-renciamento de dependências amplamente adotada em projetos Java (MILEVA et al., 2009; MAPLE; SHELAJEV, 2016); e (ii) por serem as mais populares, possivelmente também sejam

bastante utilizadas pelos desenvolvedores que adotam o Maven. Também analisamos um outro conjunto de bibliotecas (ver A), porém como os achados foram bastante similares, optamos por mantermos apenas um deles no corpo desta dissertação.

Das bibliotecas disponibilizadas no ranking, julgamos candidatos aptos para análise, os artefatos estáveis mais atuais de cada biblioteca, disponíveis no momento da extração. Adotamos como versões estáveis aquelas cujo os nomes não possuíam as denominações alpha, beta, M1, M2, Snap ou Snapshot. Além disso, consideramos aptas para nosso estudo as bibliotecas que:

• disponibilizaram seus artefatos no repositório central do Maven; • possuíam artefatos com a extensão .jar ;

• disponibilizaram um artefato com o código fonte;

• possuíam no mínimo um arquivo com a extensão .java nos artefatos de código fonte; e

(34)

• possuíam conteúdo em seus arquivos de extensão .java.

Após a aplicação dos critérios de seleção, uma biblioteca foi desconsiderada por não disponibilizar um artefato com seu código fonte; três bibliotecas foram descartadas por não estarem acessíveis no repositório central e outras nove por não possuírem arquivos com a extensão .java nos artefatos de código fonte. Ao final, resultaram 87 bibliotecas para análise. Inspecionando manualmente cada uma delas, observamos que duas não possuíam métodos com "corpo ou conteúdo"em seus arquivos de código fonte, contendo apenas interfaces, anotações ou enumerações. Optamos por desconsiderá-las de nossas análises, ficando assim o conjunto, com 85 bibliotecas válidas para nosso estudo. As URLs para todas bibliotecas analisadas estão disponíveis em https://github.com/lucasmgaldino/ ExceptionalInterfaceFromGitHubStudy.

Analisando o código fonte de cada biblioteca, observamos que elas forneceram um total de 275.452 métodos distintos, sendo destes, 199.060 (72,27%) públicos. A distribuição dos métodos identificados, categorizados de acordo com seus modificadores de visibilidade, é apresentada na Figura 5.

Figura 5: Distribuição por modificador de acesso, dos métodos das bibliotecas analisadas. Ao longo deste trabalho, a menos que seja explicitamente mencionado, consideramos apenas os métodos públicos em nossas análises. Esta decisão foi adotada por considerarmos que os desenvolvedores não interagem diretamente com métodos privados de bibliotecas, por tanto, a presença ou não de documentação de tais métodos torna-se indiferente para a atividade de codificação. Da mesma forma, os métodos com tal visibilidade por padrão não estão presentes em documentações on-line das bibliotecas. Por fim, essa decisão também foi adotada trabalhos anteriores (SENA, 2017; KECHAGIA; SPINELLIS, 2014).

Ao analisarmos a documentação JavaDoc dos métodos das bibliotecas, observamos que um número significativo (57,41%) dos métodos públicos não possuem qualquer do-cumentação JavaDoc. Da parcela que possui JavaDoc (42,59%), descobrimos que apenas 15,02% possui alguma exceção documentada, como apresentado na Figura 6. Observamos que mais da metade (55,71%) das documentações excepcionais são referentes a exceções não verificadas (do inglês, unchecked exceptions), ou seja, são exceções que herdam

(35)

di-reta ou indidi-retamente da classe java.lang.RuntimeException, como pode ser visto na Figura 7.

Figura 6: Documentação dos métodos públicos das bibliotecas analisadas.

Figura 7: Ocorrências de exceções documentadas, das bibliotecas analisadas, categorizadas por supertipo.

3.3 O Processo de Mineração de Exceções a Partir do

GitHub

O estudo foi composto por várias etapas, que englobam desde a extração de stack traces a partir de issues do GitHub, seleção das bibliotecas a serem utilizadas na inves-tigação, extrações de documentação Javadoc destas bibliotecas, até a identificação das documentações e interfaces excepcionais dos métodos de cada biblioteca. A Figura 8 ilus-tra uma visão geral do estudo. A Figura 8 (a) apresenta a etapa de coleta de dados para a realização das análises listadas na Figura 8 (b).

Inicialmente, criamos uma base de dados para armazenar as stack traces obtidas do GitHub (1), detalhada na Seção 3.3.1. Uma segunda base foi criada para armazenar as exceções extraídas da documentação Java (2), apresentada da Seção 3.3.2. Em seguida, o código fonte das bibliotecas alvo foram baixados (3), como descrito na Seção 3.3.3. A mesma base de exceções também foi utilizada para armazenar as exceções identificadas através da análise do código fonte das bibliotecas alvo (4), mostrado na Seção 3.3.4. Cria-mos uma terceira base de dados para armazenar o resultado da extração da documentação

(36)

Figura 8: Visão geral do estudo.

excepcional (5), etapa detalhada na Seção 3.3.5. Posteriormente identificamos os méto-dos das bibliotecas (7), descrito na Seção 3.3.7 e descobrimos as interfaces excepcionais não documentadas (8), como apresentado na Seção 3.3.8. Por fim, geramos uma outra base de dados com os resultados da execução da ferramenta PLEA (6), apresentada na Seção 3.3.6. Ao final de toda a fase de coleta, os dados extraídos e armazenados foram cruzados para realizar as análises, descritas na Seção 3.5.

3.3.1 Passo 1: Obtenção das Stack Traces

Para a realização do estudo utilizamos uma base de dados de stack traces que gera-mos para um estudo anterior (COELHO et al., 2015). Essa base foi populada através da

ferramenta ExceptionMiner que também desenvolvemos para o estudo. Da mesma forma que em outros trabalhos (FERNANDES, 2017), (COELHO et al., 2015) e (KECHAGIA; SPI-NELLIS, 2014), também utilizamos um conjunto de expressões regulares para identificar as stack traces quando presentes nas issues. Com essa ferramenta consultamos o GHTor-rent5 (GOUSIOS, 2013), um espelho off-line dos dados oferecidos através da API GitHub

e extraímos mais de 66 mil stack traces de mais de 2,9 mil projetos java open-source dife-rentes, totalizando mais de 2,1 mil exceções distintas. O modelo relacional utilizado para armazenar os dados está apresentado na Figura 9.

3.3.2 Passo 2: Identificação das Exceções da API Java

O segundo passo da etapa de coleta de dados foi o de identificação das exceções da API Java. Para isso, desenvolvemos um rastreador web (do inglês, web crawler) com o auxílio

(37)

Figura 9: Modelo relacional da base de stack traces.

da biblioteca jsoup6, que chamamos JavaAPICrawler (ver Seção 3.4). Este rastreador

navegou por todas as páginas do JavaDoc oficial das edições JavaSE e JavaEE , ambas nas versões 57,8, 69,10, 711,12, e 813,14, percorrendo cada página da documentação em busca

de classes de exceção.

A identificação das classes de exceção foi feita a partir da análise da hierarquia da classe, presente em cada página da documentação. Ao identificar a presença da classe java.lang.Throwable na hierarquia, a classe foi considerada como sendo de exceção. Ao localizar uma classe de exceção, seu nome completo e supertipo foram extraídos e persistidos na base de dados de exceções para serem utilizados em análises posteriores.

3.3.3 Passo 3: Download dos Artefatos Alvo da Análise

Realizar a pesquisa e download dos artefatos para análise de forma manual é bastante custoso e pode inviabilizar a replicação do estudo para um número maior de bibliotecas. Para solucionar esse problema, automatizamos o processo de localização e download dos artefatos através da API fornecida pelo mecanismo de pesquisa15para o repositório central

do Maven - que chamamos de MavenArtifactFinder (ver Seção 3.4).

A partir de uma lista com os nomes e versões dos artefatos desejados, o mecanismo de pesquisa do Maven é invocado, retornando uma listagem com todos os artefatos que

6https://jsoup.org/ 7https://docs.oracle.com/javase/1.5.0/docs/api/allclasses-frame.html 8https://docs.oracle.com/javaee/5/api/allclasses-noframe.html 9https://docs.oracle.com/javase/6/docs/api/allclasses-frame.html 10https://docs.oracle.com/javaee/6/api/allclasses-noframe.html 11https://docs.oracle.com/javase/7/docs/api/allclasses-frame.html 12https://docs.oracle.com/javaee/7/api/allclasses-noframe.html 13https://docs.oracle.com/javase/8/docs/api/allclasses-frame.html 14https://docs.oracle.com/javaee/8/api/allclasses-noframe.html 15https://search.maven.org/

(38)

atendem aos critérios da pesquisa por nome e versão de artefato. Uma vez recuperada a lista de artefatos com o nome e versão desejados, é baixado o primeiro artefato que disponibilizar além do artefato desejado, o arquivo com a extensão .pom, e um artefato com o código fonte da biblioteca.

3.3.4 Passo 4: Identificação das Exceções das Bibliotecas

Para podermos realizar quaisquer análises que necessitem de alguma identificação ou categorização das classes de exceções fornecidas pelas bibliotecas investigadas é funda-mental que as conheçamos. Para atender à essa necessidade, automatizamos o processo de identificação das exceções das bibliotecas - chamamos esta ferramenta de ExceptionFinder (ver Seção 3.4).

Inicialmente a ferramenta resolve todas as dependências das bibliotecas, definidas em seus arquivos descritores POM (project object model), para isso a ferramenta utiliza a API Apache Maven Invoker16. Em seguida, com o auxílio do framework JavaParser17, o

código fonte de cada biblioteca é percorrido em busca das classes de exceção, isto é, classes que herdam direta ou indiretamente da classe java.lang.Throwable. Uma vez localizada uma classe de exceção, seu supertipo é extraído e armazenado juntamente com o nome da classe na base de dados de exceções.

3.3.5 Passo 5: Extração da Documentação Excepcional

O processo de extração da documentação excepcional dos métodos foi automatizado. Para isso, implementamos uma ferramenta que chamamos de DocExceptionInterfaceFin-der (ver Seção 3.4), responsável por realizar a identificação das documentações excepcio-nais.

Ao executar a ferramenta, inicialmente são baixados do repositório Central do Maven, os artefatos com o código fonte das bibliotecas. Posteriormente os artefatos transferidos são descompactados. No passo seguinte, arquivos com a extensão .java são percorridos com o auxílio do framework JavaParser. Nesse momento, as informações como nome da classe e do método, assinatura e documentação JavaDoc do método, são coletadas e persistidas na base de dados. Quando identificada a presença JavaDoc em um método, a documentação é investigada em busca da presença de exceções documentadas.

16https://maven.apache.org/shared/maven-invoker/ 17https://www.javaparser.org/

(39)

O modelo relacional da base de dados utilizada para armazenar as informações re-ferentes à documentação excepcional dos métodos está apresentado na Figura 10 e as principais tabelas são descritas a seguir:

• bibliotecas – Responsável por armazenar informações referentes às bibliotecas ana-lisadas, como por exemplo o nome da biblioteca, endereços de URL de artefatos e a data em que a biblioteca foi analisada;

• classes – Armazena informações pertencentes às classes java analisadas, como por exemplo o nome e o pacote classe;

• metodos – Responsável por armazenar as informações de cada método da biblio-teca, como os nomes simples e completos dos métodos, assinatura do método (i.e.: nome, tipo de retorno e parâmetros do método), um indicador (flag) se possui ou não documentação javadoc, e todo o conteúdo da documentação, quando ela existir; • excecoes – Armazena os nomes das classes de exceção identificadas nas assinaturas dos métodos ou nas documentações JavaDoc, ou seja, armazenar todas as classes que herdam direta ou indiretamente da classe java.lang.Exception que estejam declaradas em uma cláusula throws ou nas anotações @throws ou @exception. Como cada biblioteca pode possuir mais de uma classe, o relacionamento entre as tabelas bibliotecas e classes é do tipo um para muitos. Por entendermos que podem haver métodos literalmente duplicados em alguma biblioteca, optamos por utilizar o relaciona-mento do tipo muitos para muitos entre as tabelas classes e metodos. Em virtude dessa de-cisão, foi criada a tabela intermediária classe_metodo, responsável única e exclusivamente por armazenar as chaves estrangeiras envolvidas no relacionamento. De forma similar que a decisão anterior, optamos por criar uma tabela intermediária entre as tabelas metodos e excecoes. Isso se deu em razão de um método poder sinalizar ou documentar mais de uma exceção. Diante disso, criamos a tabela metodo_excecao, responsável por armazenar as chaves estrangeiras das duas tabelas envolvidas no relacionamento, bem como indicadores (flags) de que a exceção registrada está declarada em uma cláusula throws ou presente na documentação JavaDoc.

Alguns trabalhos (KECHAGIA; SPINELLIS, 2014; SENA et al., 2016; FERNANDES, 2017;

SENA, 2017) identificaram documentação excepcional de métodos através da busca de

ocorrências da anotação @throws em seus comentários JavaDoc. Para complementar a identificação realizada por esses trabalhos, incluímos também em nossas inspeções a busca pela anotação @exception.

(40)

Figura 10: Modelo relacional da base de dados de documentação excepcional. A razão para adotar tanto a anotação @exception quanto a @throws é que segundo o guia de referência e convenções da documentação JavaDoc (ORACLE, 2011), ambas são

sinônimas. A primeira foi originalmente concebida na versão 1.0 do Java Development Kit (JDK ), enquanto que a segunda foi incluída apenas na versão 1.2 do kit.

3.3.6 Passo 6: Execução da Ferramenta PLEA

O sexto passo do nosso estudo consistiu em comparar as interfaces excepcionais desco-bertas a partir da mineração de repositórios com as interfaces excepcionais que podem ser descobertas através de análise estática de código. Para isto executamos a PLEA (Product Line Exception Analyzer) para obtermos as interfaces excepcionais identificadas através da análise estática. A PLEA (MELO et al., 2013) é uma ferramenta que utiliza técnicas de

análise estática para calcular os fluxos excepcionais de uma aplicação. A PLEA utiliza o framework WALA 18 (T. J. Watson Libraries for Analysis) para a construção dos grafos

de chamada dos métodos. Para isso, configuramos a ferramenta para fazer uso do algo-ritmo de geração de grafo de chamadas k-CFA, por ser o algoalgo-ritmo com maior precisão disponível na ferramenta.

Como já mencionado anteriormente, a geração de grafos de chamada com técnicas de análise estática é um processo computacionalmente custoso. Utilizando 5 computadores pessoais para executar a PLEA sobre as bibliotecas alvo de nosso trabalho, foram neces-sárias aproximadamente 100hs (um pouco mais de 4 dias) ininterruptas de execução. A Tabela 1 apresenta o hardware utilizado durante a execução da PLEA.

(41)

Tabela 1: Hardware utilizado para executar a PLEA.

#Máquinas Processador RAM Armazenamento

1 Core i7 4Ghz 16Gb SSD

1 Core i5 2,5Ghz 16Gb SSD

2 Core i7 2,5Ghz 8Gb SSD

1 Core i5 2,3Ghz 8Gb SSD

3.3.7 Passo 7: Identificação dos Métodos das Bibliotecas a partir

das Stacks

Após a extração dos dados, realizamos a identificação dos métodos presentes nas stack traces, que eram pertencentes à cada biblioteca. Nesse momento utilizamos como heurís-tica a comparação dos nomes totalmente qualificados dos métodos (do inglês, fully qualified name). Consideramos como nome totalmente qualificado de um método, a concatenação entre o pacote da classe, a classe detentora do método e o método. Ao longo deste traba-lho, utilizaremos o termo ’nome completo do método’ como sinônimo de ’nome totalmente qualificado do método’. A Figura 11 ilustra o método fileToList que pertence à classe FileUtil que está contida no pacote org.my.corporation.utils. Para o exemplo apre-sentado na imagem, consideramos org.my.corporation.utils.FileUtil.fileToList como sendo o nome completo do método.

Figura 11: Método org.my.corporation.utils.FileUtil.fileToList.

Cada um dos frames, que compunham as stack traces, tiveram seus métodos compa-rados com os métodos de cada biblioteca. Ao identificar métodos coincidentes, tal método foi considerado como pertencente a lib em questão.

(42)

Todos os passos mostrados até aqui foram responsáveis por construir a base de dados necessária para que pudéssemos descobrir a interface excepcional não documentada dos métodos de bibliotecas descrita a seguir.

3.3.8 Passo 8: Descoberta de Exceções Runtime não

Documen-tadas a partir das Stacks

A descoberta de exceções não documentadas que fluem pelos métodos das bibliotecas alvo foi realizada com o cruzamento da base de dados das stack traces com a base de do-cumentação excepcional. Utilizamos também a base de dados de exceções para filtrarmos e considerarmos apenas as exceções runtime. Para o contexto deste trabalho, optamos por priorizar as exceções que são reportadas mais frequentemente pelos desenvolvedores. Desse modo, adotamos uma heurística que chamamos de frame gerador.

Nossa heurística do frame gerador consiste em computar apenas as exceções e os métodos que compõem as stack traces que possuem o mesmo frame gerador e foram reportadas em no mínimo 3 repositórios diferentes. Esta escolha foi baseada no padrão "regra de três"(do inglês, rule of three). De acordo com Martin, Riehle e Buschmann (MARTIN; RIEHLE; BUSCHMANN, 1997), um padrão não é um padrão a menos que haja

pelo menos três fatos independentes e observáveis que ilustrem o padrão que está sendo usado. Desta forma buscamos minimizar a inclusão de exceções inexpressivas ou pouco relevantes para os desenvolvedores, de acordo com o contexto deste trabalho.

Consideramos como sendo o frame gerador de uma stack trace, aquele que está no topo da impressão da pilha. Para exemplificar, o frame gerador da stack trace apresentada na Figura 4 da Seção 2.3, é o representado pelo método jscover.report.FileData.addCoverage.

3.4 Apoio Ferramental

Como descrito anteriormente todos os passos do processo de mineração puderam ser total ou parcialmente automatizados. As ferramentas citadas anteriormente, apoiando cada um dos passos, compõem a suite de ferramentas que chamamos ExMinerSuite, ex-tensão da ferramenta ExMiner utilizada em um estudo anterior, e que foi desenvolvida para apoiar o presente estudo. A Figura abaixo apresenta as principais ferramentas que compõem esta suite.

(43)

do-Figura 12: Principais ferramentas que compõem a suite ExMinerSuite.

cumentação Javadoc nas versões 5, 6, 7 e 8 das edições SE e EE do Java, em busca de classes de exceção. Recebe como entrada a URL da página inicial da documentação da edição e versão do Java desejadas, e a partir daí, com o auxílio da biblioteca jsoup, visita cada uma das páginas. O Quadro 3.1 apresenta o pseudocódigo de como a classificação dos tipos excepcionais Java foi realizada. Ao final da execução, o resultado será uma base de dados populada com todas as classes de exceções do Java.

A ferramenta MavenArtifactFinder é responsável por automatizar o processo de localização e download dos artefatos adequados, a partir do repositório Maven. Recebe como entrada o nome e versão do artefato desejado, seguindo o padrão de nomenclatura de artefatos Maven. Fazendo uso do serviço de pesquisa do Maven, os artefatos são trans-feridos. Para cada artefato desejado, uma requisição ao serviço de pesquisa do Maven é realizada. O serviço retorna uma lista contendo todos os artefatos que atendem aos critérios da busca. A lista resultante é percorrida em busca do primeiro artefato que dis-ponibilizar além do artefato desejado, o arquivo com a extensão .pom, e um artefato com o código fonte da biblioteca.

ExceptionFinder é a ferramenta responsável por automatizar o processo de

iden-tificação das classes de exceção definidas por cada biblioteca alvo. Da mesma forma que a MavenArtifactFinder, recebe como entrada o nome e versão do artefato desejado, seguindo o padrão de nomenclatura de artefatos Maven. A execução inicia invocando o MavenArtifactFinder para recuperar o artefato de código fonte e o arquivo .pom de cada biblioteca. Com o auxílio da API Apache Maven Invoker, todas as dependências definidas no arquivo .pom são resolvidas. Em seguida, o arquivo contendo o código fonte é

(44)

des-Quadro 3.1 Algoritmo para Identificar o Tipo das Exceções Java.

Entrada: Página inicial da edição e versão desejada, contendo a listagem de todas as classes documentadas;

1. Percorre a página e armazena temporariamente todos os links da página inicial; 2. Para cada link, executa as seguintes ações:

2.1. Carrega a página do link;

2.2. Percorre a página em busca de uma seção que defina a hierarquia da classe; 2.3. Ao localizar uma seção de hierarquia, extrai cada um dos elementos,

armazenando-os em uma coleção temporária;

2.4. Verifica se existe a classe java.lang.Throwable na coleção de classes da hie-rarquia;

2.5. Se existir a classe java.lang.Throwable, caracteriza-se uma classe de exceção, portanto executa:

2.5.1. Verifica se existe a classe java.lang.Error na coleção de classes da hie-rarquia;

2.5.1.1. Se existir, classifica a exceção analisada como uma subclasse desta; 2.5.2. Verifica se existe a classe java.lang.RuntimeException na coleção de

classes da hierarquia;

2.5.2.1. Se existir, classifica a exceção analisada como uma subclasse desta; 3. Retorna todas as exceções Java encontradas na documentação e com suas respectivas

super classes.

compactado e com o auxílio do framework JavaParser, todos os arquivo com a extensão .java são percorridos em busca das classes de exceção, isto é, classes que herdam direta ou indiretamente da classe java.lang.Throwable. Uma vez localizada uma classe de ex-ceção, seu supertipo é extraído e armazenado juntamente com o nome da classe na base de dados de exceções.

A quarta ferramenta, chamada de DocExceptionInterfaceFinder, é responsável por realizar a identificação de toda documentação excepcional das bibliotecas alvo. Da mesma forma que as duas ferramentas apresentadas anteriormente, também recebe o nome e versão do artefato desejado, seguindo o padrão de nomenclatura de artefatos Maven. A execução invoca o MavenArtifactFinder para recuperar o artefato de código fonte de cada biblioteca e em seguida o descompacta. No passo seguinte, arquivos com a extensão .java são percorridos com o auxílio do framework JavaParser. À medida que os arquivos são percorridos, informações como nome da classe e do método, assinatura e documentação JavaDoc, são coletadas e persistidas na base de dados. Com o auxílio de expressões

Referências

Documentos relacionados

Nessa situação temos claramente a relação de tecnovívio apresentado por Dubatti (2012) operando, visto que nessa experiência ambos os atores tra- çam um diálogo que não se dá

As resistências desses grupos se encontram não apenas na performatividade de seus corpos ao ocuparem as ruas e se manifestarem, mas na articulação micropolítica com outros

Frondes fasciculadas, não adpressas ao substrato, levemente dimórficas; as estéreis com 16-27 cm de comprimento e 9,0-12 cm de largura; pecíolo com 6,0-10,0 cm de

A descrição das atividades que podem vir a ser desempenhadas pelo AVISA, além de se fundamentar na perspectiva da integralidade da atenção à saúde, envolvendo desde as ações

Indices (1) to verify the agreement between soil depth values where penetration resistance is equal to 1.5 MPa, based on the proposed penetrometer (Hatô) and the

Avaliar os efeitos do enriquecimento ambiental em animais submetidos a privação maternal sobre comportamentos de ansiedade, bem como avaliar os efeitos dessas condições

diglicéridos, ácido cítrico e SAPP: Batatas, mono e diglicéridos, pirofosfato ácido de sódio, ácido cítrico e BHA • Código Nav 102555 Sacos embalados de kraft revestidos

O objetivo principal deste estudo de caso era verificar o grau de valorização da medicina popular de plantas entre o próprio povo, tendo por base uma comunidade com diferentes