Vericação de Conformidade entre Código e
Modelo para Programas Concorrentes
Feira de Santana 2010
Vericação de Conformidade entre Código e
Modelo para Programas Concorrentes
Monograa apresentada à Coordenação do Curso de Engenharia de Computação da Uni-versidade Estadual de Feira de Santana como requisito parcial à obtenção do grau de Ba-charel em Engenharia de Computação.
Orientador:
Prof. Me. José Amancio Macedo Santos
Universidade Estadual de Feira de Santana
Feira de Santana 2010
Aluno(a): Lidiany Cerqueira Santos
Monograa apresentada e julgada em 25 de Janeiro de 2010 perante a banca examinadora:
José Amancio Macedo Santos
(Universidade Estadual de Feira de Santana - DTEC/UEFS)
Daniel Gouveia Costa
(Universidade Estadual de Feira de Santana - DTEC/UEFS)
Angelo Amâncio Duarte
(Universidade Estadual de Feira de Santana - DTEC/UEFS)
SUPLENTE
Antonio Augusto Teixeira Ribeiro Coutinho
(Universidade Estadual de Feira de Santana - DTEC/UEFS)
Coordenador do Colegiado do Curso de Engenharia de Computação
Agradecimentos
Apesar de serem poucas linhas e muitas pessoas para expressar meu sentimento de gratidão, não poderia me eximir de tentar diante da oportunidade.
Em primeiro lugar, gostaria de agradecer a minha mãe, Lucidalva, por tudo que conquistei até o momento, pelo amor e carinho, pelos ensinamentos e por todas as minhas conquistas futuras.
À minha grande irmã, Liziany, pelo carinho e pela força. Ao meu pai, Bispo, pela sua presença.
Ao meu noivo, Leandro, por ter estado ao meu lado em cada momento, mesmo nos piores, pela ajuda sempre bem-vinda e pelo seu amor incondicional. Honey, obrigada por tudo, te amo!
Gostaria de oferecer um agradecimento especial ao meu orientador José Amancio, pelo incentivo, apoio, paciência e orientação neste trabalho. E também à professora Gabriela Rezende por ser uma ótima "tutora"e grande amiga.
Aos meus colegas e amigos do curso, especialmente João e Mateus, sem os quais com toda certeza não estaria aqui!
Finalmente agradeço aos meus familiares, amigos e a todos os bons professores que me ajudaram e contribuíram nessa conquista.
scars.
Resumo
A utilização de modelos para abstração da complexidade do software tem sido cada vez mais necessária. Esta importância torna-se ainda mais visível no desenvolvimento de sistemas concorrentes, onde a complexidade é maior, devido a sua natureza não determi-nística. Para auxiliar o projeto e compreensão deste tipo de software, modelos em Redes de Petri são largamente utilizados. Porém, durante o desenvolvimento, a inserção de no-vos requisitos e restrições força a realização de alterações no projeto que frequentemente não são reetidas no modelo. A falta de manutenção dos modelos durante a evolução do código acaba gerando inconformidades no projeto que prejudicam a qualidade do soft-ware. Além disso, o custo e esforço necessário para a manutenção desse tipo de artefato é bastante alto, o que contribui para o abandono desta tarefa ao longo do projeto. Este trabalho apresenta uma ferramenta para vericação automática de conformidade entre um modelo em Rede de Petri e um sistema concorrente em tempo de execução. Esta ferramenta detecta e informa a existência de inconformidades, caso o código não esteja de acordo com a sua especicação. Dessa forma, auxilia o desenvolvedor na sincronização dos modelos à medida que o código é alterado e também na detecção e correção de erros no projeto. Com isso, os desenvolvedores passam a contar com um recurso de baixo custo para auxiliar no projeto e manutenção de qualidade em sistemas concorrentes.
Palavras-chave: Programas concorrentes. Modelos. Rede de Petri. Vericação de Conformidade. Ferramenta.
Abstract
The use of models as an abstraction to software complexity has been increasingly necessary. This importance becomes even more visible in the development of concurrent systems, where complexity is higher due to its nondeterminism. To assist the design and the understanding of this type of software, Petri net models are widely used. However, during development, the integration of new requirements and restrictions forces imple-mentation changes that often are not reected in the model. The lack of maintenance of the models during evolution of the code ends up generating nonconformities in design that reduces the software quality. Moreover, the cost and eort required to maintain this type of artifact is quite high, which contributes to the abandonment of this task throughout the project. This work presents a tool for automatic verication of conformity between a Petri net model and a concurrent system at runtime. This tool detects and reports the existence of nonconformities if the code does not match with its specication. So it helps the developer in the synchronization of the models as the code is changed as well as it supports in detection and correction of design errors. As a result, developers have now a low-cost resource to assist in the design and in the quality maintenance in concurrent systems.
Keywords: Concurrent Programs. Models. Petri nets. Conformity Verication. Tool.
Lista de Figuras
1 Diagrama de classes de um editor de guras (ELRAD et al., 2001). . . p. 18
2 Implementação do editor de guras utilizando POO. . . p. 19 3 Implementação de um Aspecto para modularização da atualização de tela. p. 20 4 Representação gráca de uma Rede de Petri. . . p. 21 5 Situação de sequência entre as transições T1 e T2. . . p. 22 6 Conito entre as transições T1 e T2. . . p. 23 7 Concorrência entre as transições T0 e T9, e T1 e T2. . . p. 23 8 Métodos produce() e consume() da classe LimitedBuer. . . p. 24 9 Estado da rede e trace gerado depois da produção de duas chas. . . . p. 25 10 Estado da rede e trace gerado depois do consumo de uma cha. . . p. 25 11 Anotação @TestCase. . . p. 26 12 Jarp: ferramenta utilizada neste trabalho. . . p. 31 13 Marcação de código proposta por Andrade (2008). . . p. 33 14 Anotação @FireTransition. . . p. 34 15 Marcação do método produce e transição correspondente no modelo. . p. 35 16 Aspecto para interceptar as marcações. . . p. 36 17 Inconsistência nos resultados da vericação com um advice o tipo before. p. 36 18 Utilização do advice after returning para resolver o problema de
in-consistência. . . p. 37 19 Resultados obtidos após a modicação do Aspecto. . . p. 37 20 Identicação do objeto que dispara uma transição (ANDRADE, 2008). . p. 38 21 Versão nal de AspectTransition, responsável pela interceptação do
código marcado. . . p. 39 22 Aplicação da ferramenta, destaque para o painel com o trace gerado. . . p. 40 23 Especicação do sistema de ferrovias. . . p. 41 24 Modelo em Rede de Petri para o sistema de trens. . . p. 42 25 Modelo em Rede de Petri para o sistema de trens após a simplicação. p. 44 26 Marcação das instruções na classe Train. . . p. 45 27 Programa para simulação das ferrovias em execução. . . p. 45 28 Aplicação da ferramenta de vericação no sistema de ferrovias. . . p. 46
30 Vericação de conformidade do sistema de ferrovias. . . p. 47 31 Alteração na ferrovia. . . p. 48 32 Modelo em Rede de Petri para a segunda versão do sistema. . . p. 49 33 Simulação da segunda versão do sistema de ferrovias. . . p. 50 34 Vericação de conformidade da segunda versão do sistema de ferrovias. p. 50
Lista de Tabelas
1 Componentes das ferramentas analisadas. . . p. 29 2 Tipos de Redes de Petri suportadas pelas ferramentas analisadas. . . . p. 30 3 Comparação entre as ferramentas. . . p. 30 4 Lista de estados para o modelo da ferrovia . . . p. 43 5 Lista de transições para o modelo da ferrovia . . . p. 43
Lista de Siglas
API Application Programming Interface GPL GNU General Public License JVM Java Virtual Machine
MOP Monitoring-Oriented Programming PNML Petri Net Markup Language
POA Programação Orientada a Aspectos POO Programação Orientada a Objetos RP Redes de Petri
Sumário
1 Introdução p. 13
2 Fundamentação Teórica p. 16
2.1 Vericação de Software . . . p. 16 2.2 Programação Orientada a Aspectos . . . p. 17 2.3 Redes de Petri . . . p. 20 2.4 Técnica de Vericação . . . p. 23 2.5 Anotações Java . . . p. 26
3 Metodologia p. 28
3.1 Revisão bibliográca . . . p. 28 3.2 Análise e avaliação da técnica proposta por Andrade (2008) . . . p. 28 3.3 Levantamento de ferramentas para modelagem de Redes de Petri . . . . p. 28 3.4 Projeto e desenvolvimento . . . p. 31 3.5 Aplicação e análise da ferramenta . . . p. 32 4 Desenvolvimento da ferramenta para vericação de conformidade p. 33 4.1 Marcação de instruções no código . . . p. 33 4.2 Utilização de POA para captura das marcações . . . p. 35 4.3 Necessidade de identicação de instâncias em execução . . . p. 37 4.4 Apresentação dos resultados da vericação . . . p. 38 4.5 Utilização do Jarp . . . p. 39 4.6 Vericação de conformidade através da ferramenta . . . p. 40 5 Aplicação e Análise da Ferramenta p. 41 5.1 Descrição da aplicação . . . p. 41 5.2 Modelagem da aplicação . . . p. 42 5.3 Desenvolvimento e marcação . . . p. 44 5.3.1 Alteração no projeto do sistema . . . p. 47 5.3.2 Resultados observados . . . p. 48
1 Introdução
A abstração da complexidade do software através da utilização de modelos é uma es-tratégia importante para auxiliar no projeto, desenvolvimento e manutenção de software, especialmente quando este apresenta características concorrentes (BASS; CLEMENTS; KAZ-MAN, 1998). Neste tipo de software a existência de vários uxos de execução implica em um comportamento não determinístico, sendo possível obter diversos resultados a partir de sua execução. Esta característica diculta a compreensão do comportamento desses sistemas e consequentemente torna ainda mais interessante a utilização de artefatos para especicação formal como por exemplo, os modelos.
Para utilizar modelos como artefatos para especicação de software é necessário ga-rantir a sua conformidade em relação ao código. A conformidade caracteriza-se pela corretude na relação entre o código desenvolvido e os modelos utilizados para especicar o software. Problemas de conformidade podem ter consequências graves para o desenvol-vimento de um sistema, levando a entrega de produtos com defeitos, baixa qualidade e custos elevados. Esses problemas podem tornar necessária a reconstrução de um software inteiro e, segundo Gurp e Bosch (2002), o esforço e custo necessários para reconstruir um sistema de alta complexidade podem ser fatais para uma empresa. Para Gueheneuc et al. (2002), um problema recorrente no desenvolvimento de software é a lacuna existente entre a modelagem, a implementação e a manutenção do software, pois as constantes alterações que ocorrem durante o desenvolvimento do software acabam distanciando os modelos do código.
Diante da necessidade de assegurar a conformidade em um projeto, os desenvolvedores podem utilizar técnicas de vericação. Estas possibilitam a detecção de problemas difíceis de serem encontrados pelo desenvolvedor (COLLOFELLO, 1988). Na vericação é feita a
análise da conformidade entre o sistema e alguma especicação existente, que pode ser um modelo. As técnicas de vericação permitem identicar grande parte dos defeitos, auxiliando na manutenção de sistemas, e consequentemente oferecendo um meio para assegurar a qualidade do software. Entre as técnicas existentes, Katoen (1999) destaca a simulação, os testes e a vericação de modelos. Na vericação por simulação é realizada a
simulação e análise sobre um modelo abstrato, enquanto na vericação através de testes são realizados experimentos com a execução do código (MCGREGOR; SYKES, 2001). Estas
são técnicas consolidadas e ecientes na vericação de software, contudo, é certamente inviável utilizá-las devido a diculdade em cobrir todo o comportamento de um sistema concorrente. A vericação de modelos (model checking) é realizada através da comparação entre as propriedades dos modelos e as especicações do sistema que estes representam. Esta é uma técnica adequada para a vericação de sistemas concorrentes. Porém, a sua utilização pode ser inecaz, caso os modelos não estejam em conformidade com o sistema. Existem alguns trabalhos como a Programação Orientada a Monitoração (Monitoring-Oriented Programming - MOP) que é uma técnica para desenvolvimento e análise de sistemas de software (CHEN; ROsU, 2007) e a técnica para vericação de conformidade
apresentada por Oliveira (2006), que auxiliam na manutenção de conformidade em um projeto. Na MOP são usados métodos formais para vericação da conformidade entre uma implementação e uma especicação em tempo de execução. São utilizadas ano-tações de código para especicar formalmente os requisitos em pontos do código-fonte dos sistemas alvo e para monitorar o comportamento dos sistemas durante a execução. Esta técnica possibilita a vericação de conformidade em sistemas concorrentes, porém mostra-se insuciente para vericação de algumas propriedades inerentes a este tipo de sistema. O trabalho proposto por Oliveira (2006) consiste em uma técnica para verica-ção de conformidade que informa automaticamente se uma implementaverica-ção não obedece à arquitetura estabelecida. Esta técnica mostra-se adequada para aplicação em sistemas concorrentes, contudo sua abordagem tem como objetivo principal assegurar a conformi-dade entre código e design arquitetural em um projeto, não sendo aplicável à vericação de características comportamentais.
Andrade (2008) apresenta uma técnica cujo objetivo principal é permitir ao desen-volvedor vericar a conformidade entre código e modelo de um software concorrente em tempo de execução. Essa técnica consiste basicamente na utilização de marcações inseri-das em pontos-chave de um sistema que são usainseri-das para mapear, no código, instruções correspondentes a eventos do modelo. Durante a execução do programa, são gerados traces correspondentes aos pontos onde foram inseridas as marcações. Esses são utiliza-dos para comparar o uxo de execução do sistema e a simulação do modelo. A aplicação desta técnica permite confrontar modelo e código, para vericação de seu comportamento, possibilitando um controle direto do sistema em um nível de abstração mais alto. Além disso, permite uma sincronização constante entre modelo e código, auxiliando na análise de softwares complexos, como os sistemas concorrentes. No trabalho realizado por An-drade (2008), a aplicação da técnica supracitada deve ser realizada manualmente, uma
vez que o desenvolvedor precisa inserir as marcações no código fonte do sistema e ainda realizar a comparação entre o modelo e os traces gerados pela execução do código.
Este trabalho tem como objetivo principal a automatização da técnica, através do desenvolvimento de uma ferramenta para vericação de conformidade, reduzindo-se a intervenção do desenvolvedor no processo de marcação e vericação. A ferramenta im-plementada permite a vericação de conformidade entre um modelo em Rede de Petri (RP) (MURATA, 1989) e um sistema concorrente, em tempo de execução. Com a
reali-zação deste trabalho pretende-se minimizar os custos e esforços inerentes a vericação de conformidade entre modelo e código em sistemas concorrentes.
2 Fundamentação Teórica
2.1 Vericação de Software
Vericação se refere ao processo de determinar se produtos de uma determinada fase do processo de desenvolvimento de software preenchem os requisitos estabelecidos pela fase anterior, como por exemplo, a correspondência entre a fase de especicação e imple-mentação de um software.
A vericação é uma atividade importante no processo de desenvolvimento, pois através dela é possível identicar grande parte dos defeitos e evidenciar a corretude do produto de software. A importância do uso de técnicas de vericação tem aumentado devido a necessidade de manter a conformidade do software e de cumprir exigências de qualidade. É possível dividir as técnicas de vericação de software em vericação estática e veri-cação dinâmica. A veriveri-cação estática é realizada sem que o código seja executado através de inspeções, análise do código e vericação formal. A vericação dinâmica é realizada em tempo de execução, engloba técnicas como testes, avaliação de asserções e simulação. Katoen (1999) destaca as seguintes técnicas: simulação, testes, vericação formal e de modelos (model checking). Na vericação por simulação é feita uma análise sobre um modelo abstrato. Na vericação através de testes são realizados experimentos durante a execução do código (MCGREGOR; SYKES, 2001). É possível também realizar testes de
conformidade a partir dos modelos. Nesse tipo de técnica é feita uma análise do modelo e denidos os casos de teste, avaliando-se a conformidade entre a implementação e o mo-delo especicado (FERNANDEZ; MOUNIER; PACHON, 2005). Testes de software, unidade,
regressão, integração e testes formais são bastante utilizados. Tanto a simulação, quanto os testes permitem demonstrar a presença de falhas. Porém, devido ao grande número de possibilidades de execução em um sistema concorrente, é difícil avaliar todos os casos. Assim, utilizando-se as técnicas supracitadas é difícil garantir a ausência de falhas em um software concorrente. A vericação formal é feita através de provas matemáticas para garantir as especicações de um sistema. Porém, a sua aplicação implica na necessidade de prossionais com alto grau de especialização o que aumenta os custos da vericação.
destaca-se:
• a vericação estática, em que são analisadas as propriedades do sistema a partir de um modelo (KATOEN, 1999). A vericação é realizada a partir de um modelo
formal, de onde são derivados todos os comportamentos possíveis do sistema. Esses comportamentos são especicados e modelo e sistema são submetidos à vericação (PELED, 1994). Essa técnica é suciente para indicar a corretude de um sistema
con-corrente, sem a necessidade de prossionais especialistas, o que diminui seus custos em comparação aos métodos formais. Contudo, é difícil analisar estaticamente todas as características do comportamento do software. Além disso, durante a evolução, sistemas complexos tendem a apresentar divergências de seu modelo, de forma que este pode não representar corretamente o sistema (SEFIKA; SANE; CAMPBELL, 1996);
• a vericação dinâmica, onde é realizada a análise do comportamento através da execução de um sistema (SCHMERL et al., 2006). Esta abordagem permite que o
uxo de execução de um programa seja monitorado e confrontado com o modelo. Pelo fato de possibilitar a observação do comportamento do software quando este é executado, esta técnica é especialmente útil para a vericação de sistemas concor-rentes, pois permite que sejam monitorados inclusive softwares com comportamento não determinístico.
Diante das diculdades citadas para garantir a conformidade em programas concor-rentes, Andrade (2008) apresenta uma técnica para vericação dinâmica de conformidade entre a implementação e o modelo do software especicado. Na aplicação desta técnica é realizada a análise em tempo de execução através do confronto entre os comportamentos do sistema e a correspondência com o modelo.
2.2 Programação Orientada a Aspectos
A Programação Orientada a Objetos (POO) permite uma boa modularização dos requisitos de um sistema porém, algumas funcionalidades secundárias não respeitam de forma adequada essa modularização e tendem a se espalhar por todo o código. Esses requisitos são denominados requisitos transversais (cross-cutting concerns) (KICZALES et al., 1997).
Com a proposta de modularizar e encapsular corretamente este tipo de código, Kicza-les et al. (1997) apresenta a Programação Orientada a Aspetos (POA). Na POA são uti-lizados Aspectos, como uma forma de modularizar os interesses transversais, tratando-os
como uma dimensão distinta dos demais requisitos do sistema. Através da sua integra-ção à POO, é possível eliminar os interesses transversais e consequentemente melhorar o projeto e facilitar a manutenção do software.
Os principais elementos da POA são:
• Aspect: unidade de programação, captura a funcionalidade que corta a aplicação; • Joinpoint: dene os pontos onde os aspectos irão interceptar a execução do
pro-grama;
• Pointcut: responsável por capturar os joinpoints de acordo com um conjunto de critérios;
• Advice: trechos de código executados quando uma interceptação é realizada. Podem assumir os seguintes comportamentos:
before: executa antes do Joinpoint; after: executa depois do Joinpoint;
around: pode substituir o código interceptado pelo joinpoint.
Para exemplicação dos conceitos relacionados à POA, Elrad et al. (2001) apresenta um editor de guras. Este possui duas classes concretas de elementos de gura: pontos e linhas. Sempre que uma gura for movimentada a tela do editor deve ser atualizada. Um diagrama de classes do editor é apresentado na Figura 1. Uma análise deste diagrama permite observar que a atualização da tela constitui um requisito transversal, pois sua implementação não é responsabilidade de nenhuma das classes Point e Line (ELRAD et al., 2001).
Na Figura 2 é apresentada a implementação orientada a objetos do editor de guras, onde é possível vericar que o código responsável pela atualização da tela está espalhado pelas classes Point e Line.
1 public class Point { 2
3 private int x ; 4 private int y ; 5
6 public setX ( int x ) { 7 this . x = x ;
8 Display . a t u a l i z a ( ) ;
9 }
10
11 public setY ( int y ) { 12 this . y = y ; 13 Display . a t u a l i z a ( ) ; 14 } 15 } 16 17
18 public class Line ( ) { 19
20 private Point p1 ; 21 private Point p2 ; 22
23 public setP1 ( Point p1 ) { 24 this . p1 = p1 ; 25 Display . a t u a l i z a ( ) ;
26 }
27
28 public setP2 ( Point p2 ) { 29 this . p2 = p2 ;
30 Display . a t u a l i z a ( ) ;
31 }
32 }
Figura 2: Implementação do editor de guras utilizando POO.
Uma proposta para trabalhar com a POA é a extensão orientada a aspectos da lin-guagem Java: AspectJ. Esta linlin-guagem oferece suporte para a implementação modular de interesses transversais (KICZALES et al., 2001). Os Aspectos constituem a unidade básica da linguagem e são denidos por declarações similares as declarações de Classes em Java.
AspectJ suporta dois tipos de implementações transversais: dinâmica e estática. A primeira permite que sejam denidas execuções adicionais em pontos que podem ser in-terceptados durante a execução do programa. A implementação estática permite denir novas operações sobre tipos existentes, através de especicações de classes e comporta-mentos que podem ser utilizados pelos interesses dinâmicos (LADDAD, 2003).
utilizando-se AspectJ. Na Figura 3 é apreutilizando-sentada a implementação de um Aspecto, DisplayUpdate, cuja nalidade é modularizar os requisitos transversais concernentes à mudança da posição das guras na tela. A nova implementação possibilita a retirada destes requisitos das classes Point e Line. Neste Aspecto foram utilizados joinpoints para interceptar as chamadas de qualquer um dos métodos que alteram a posição das guras. Os joinpoints foram agrupados em um pointcut para gerenciar os eventos relacionados ao movimento das guras. Além disso, foi denido um advice para realizar a atualização da tela após a execução de qualquer método interceptado no pointcut.
1 aspect DisplayUpdate {
2 pointcut move ( ) :
3 c a l l ( Line . setP1 ( Point ) ) | | c a l l ( Line . setP2 ( Point ) ) | | 4 c a l l ( Point . setX ( int ) ) | | c a l l ( Point . setPY ( int ) ) ; 5
6 a f t e r ( ) r e t u r n i n g move ( ) { 7 Display . update ( ) ;
8 }
9 }
Figura 3: Implementação de um Aspecto para modularização da atualização de tela.
Este exemplo demonstra como a POA permite separar de forma eciente característi-cas semelhantes em um sistema. A sua adoção possibilita um aumento no grau de reuso de código e auxilia no desenvolvimento e manutenção de software.
2.3 Redes de Petri
Redes de Petri (RP) constituem uma poderosa linguagem para modelagem gráca e matemática de vários tipos de sistemas. Utilizando-se RP é possível modelar sistemas concorrentes, assíncronos, paralelos e distribuídos. Assim as RP são uma ótima ferramenta para auxiliar a comunicação visual na representação de sistemas através de modelos, semelhantemente a uxogramas e diagramas de blocos. Ademais, é possível realizar a simulação de Redes de Petri, e consequentemente dos sistemas que estas representam. Como uma ferramenta matemática, é possível utilizar equações algébricas que denam formalmente o comportamento de sistemas (MURATA, 1989).
Uma RP é composta por lugares, chas ou tokens, transições e arcos direcionados. Os lugares representam uma condição ou uma atividade; as chas, marcas ou tokens representam o estado de um sistema; as transições representam um evento e os arcos (elementos da relação)indicam os lugares de entrada ou saída para as transições. Com esse conjunto de elementos, é possível usar as Redes de Petri para o projeto, a especicação, a simulação e vericação de sistemas.
• os lugares são representados por círculos; • as transições são representadas por retângulos; • os arcos são representados por setas.
Figura 4: Representação gráca de uma Rede de Petri.
Uma RP pode ser denida formalmente como uma 5-tupla R = (P, T, F, W, Mo) (MURATA, 1989), onde:
• P = (P1, P2, ..., Pm) é um conjunto nito de lugares, • T = (T1, T2, ..., Tn) é um conjunto nito de transições, • F ⊆ (P × T ) ∪ (T × P )é o conjunto dos arcos,
• W: F → (1, 2, 3...) é o peso dos arcos, • M0: P → (0, 1, 2, 3...) é a marcação inicial,
• P ∩ T = ∅ ∧ P ∪ T 6= ∅os conjuntos P e T são disjuntos e não vazios.
Existem várias maneiras de classicar as Redes de Petri. Uma delas é a subdivisão em redes de baixo nível e redes de alto nível. Nas redes de baixo nível as marcas não são diferenciadas, considerando-se apenas a existência ou não de marcas em um lugar. Em uma rede de alto-nível as marcas possuem valores, o que as torna diferenciáveis entre si. Essa característica confere as RP de alto nível maior poder de expressão. Nesse trabalho serão tratadas apenas as redes denominadas lugar-transição, um dos tipos de redes de baixo nível. Em uma RP do tipo lugar-transição, os lugares podem acumular mais de uma cha e os arcos podem ser valorados (MURATA, 1989).
• uma transição é habilitada se em cada um dos seus lugares de entrada existe pelo menos um número n de chas, onde n é o peso do respectivo arco que vai de cada lugar de entrada à transição;
• uma transição habilitada pode ou não disparar;
• ao disparar uma transição, são removidas n chas de cada um dos lugares de entrada da transição e são adicionadas m chas em cada um dos lugares de saída, onde n é o peso de cada arco de entrada e m é o peso de cada arco de saída da transição. Algumas situações particulares podem ocorrer nos relacionamentos entre as transições (MURATA, 1989). Entre essas situações destaca-se:
Sequência: apresentada na Figura 5, entre as transições T1 e T2. A transição T1 pode ocorrer e T2 não pode, e T2 é habilitada somente após o disparo de T1.
Figura 5: Situação de sequência entre as transições T1 e T2.
Conito: apresentada na Figura 6. O disparo de T0 faz com que uma cha seja retirada de P0 para P1, habilitando as duas transições T1 e T2. Neste caso, apenas uma das transições pode ser executada e não é possível armar qual das duas será disparada primeiro. Essa situação demonstra um conito entre as transições T1 e T2. O disparo de T1 habilita a transição T3, já o disparo de T2 habilita a transição T4.
Concorrência: ocorre quando duas transições T1 e T2 são independentes entre si, de modo que, a execução de uma não interfere na execução da outra, sendo possível dispará-las concorrentemente. Na Figura 7, as transições T0 e T9, assim como T1 e T2 são exemplos de transições concorrentes.
A modelagem de sistemas concorrentes é uma das aplicações de RP, sendo possível a simulação de tais modelos para efeitos de vericação. A utilização de modelos permite re-tratar a ocorrência de conitos, sincronização, concorrência e compartilhamento, algumas das características de um sistema concorrente.
Figura 6: Conito entre as transições T1 e T2.
Figura 7: Concorrência entre as transições T0 e T9, e T1 e T2.
A adoção de RP na técnica proposta por Andrade (2008) mostra-se adequada, pois esta é uma poderosa linguagem para a modelagem de sistemas. Ademais existe uma extensa teoria envolvendo o assunto, uma base em matemática formal que dá sustentação a linguagem e diversas ferramentas para construção e simulação de modelos. Essa escolha se justica também pela adequação e facilidade na modelagem de sistemas concorrentes.
2.4 Técnica de Vericação
Nesta seção serão apresentados os aspectos teóricos que caracterizam a técnica pro-posta por Andrade (2008), uma vez que esta constitui a base para a realização deste trabalho.
Na técnica supracitada, o desenvolvedor insere marcações em pontos chave do código fonte de um sistema concorrente com a nalidade de mapear o seu comportamento. Nos pontos onde ocorrem mudanças de estados, isto é, onde foram inseridas as marcações, é gerado um trace correspondente aos eventos visíveis no modelo. A vericação é feita a partir da execução do código, utilizando-se os traces para confrontar o uxo de execução
do sistema e os eventos do modelo.
A estratégia adotada para mapear as marcações foi a utilização da POA. O uso de Aspectos é justicado para o desenvolvimento da técnica, uma vez que as marcações -cariam dispersas pelo código - o que é contra os princípios da orientação a objetos. A implementação do Aspecto para captura dos eventos foi feita com a utilização de AspectJ (KICZALES et al., 2001), sendo utilizados joinpoints para interceptar os pontos-chave
de-signados pelo desenvolvedor no momento em que estes são executados. Para cada ponto interceptado pelos joinpoints é gerada uma linha no trace correspondente ao uxo de execução do código.
Na Figura 8 é apresentada uma implementação em Java do problema do buer li-mitado (TANENBAUM, 2007). Este problema consiste em dois processos, um produtor e
um consumidor, que compartilham um buer de n elementos. O processo produtor é res-ponsável por produzir recursos e disponibilizá-los no buer para serem consumidos pelo processo consumidor. Deve-se ter o cuidado para que o produtor não produza nenhum recurso quando o buer já estiver cheio, a m de evitar a perda dos recursos produzidos anteriormente e que ainda não foram consumidos. É necessário evitar, também, que o consumidor tente consumir algum recurso enquanto o buer estiver vazio.
1 public synchronized void produce { 2 while ( spaces==capacity ) { 3 try { 4 wait ( ) ; 5 } catch ( InterruptedException e ) { 6 e . printStackTrace ( ) ; 7 } 8 } 9 ++spaces ;
10 T ra n s it io n . f i r e T r a n s i t i o n ( "Uma ficha disponível!" ) ; 11 n o t i f y A l l ( ) ;
12 }
13 public synchronized void consume ( ) { 14 while ( spaces==0){ 15 try { 16 wait ( ) ; 17 } catch ( InterruptedException e ) { 18 e . printStackTrace ( ) ; 19 } 20 } 21 −−spaces ;
22 T r a n s i t io n . f i r e T r a n s i t i o n ( "Uma ficha consumida!" ) ; 23 n o t i f y A l l ( ) ;
24 }
Figura 8: Métodos produce() e consume() da classe LimitedBuer.
As marcações inseridas no código apresentado na Figura 8 (linhas 10 e 23) indicam os pontos chave do sistema, quando são produzidas ou consumidas chas no buer.
Nas guras 9 e 10 é possível observar o modelo, construído para representar o pro-blema do buer limitado com duas posições. As chas são produzidas através do disparo da transição produzir e são consumidas através do disparo da transição consumir. Ob-servando o modelo, os instantes em que ocorre uma mudança de estado são quando se produz ou consome uma cha. Dessa forma, as marcações foram inseridas nos métodos produce() e consume() da classe que representa o buer.
Quando uma cha é retirada do lugar Esperando para produzir é criada uma cha em Esperando para Consumir. Na Figura 9 apresenta-se o trace gerado pela execução do código e o estado correspondente em que o modelo se encontra após a produção de duas chas. A transição consumir é habilitada após a produção de uma cha.
Figura 9: Estado da rede e trace gerado depois da produção de duas chas.
Do mesmo modo, a Figura 10 mostra o trace gerado e o estado do modelo, após o consumo de uma cha do buer. As linhas geradas pelo trace nas guras 9 e 10 são
Figura 10: Estado da rede e trace gerado depois do consumo de uma cha.
resultantes das marcações em destaque na Figura 8. Observando-se as guras 9 e 10 é possível confrontar o trace gerado e o modelo do sistema e notar a conformidade existente, pois o resultado da execução corresponde à simulação do modelo.
Através do problema apresentado nesta seção é possível entender o funcionamento da técnica e como é possível utilizá-la para vericar a conformidade entre modelo e código em um sistema concorrente. É possível observar também a necessidade de aplicar a técnica manualmente o que diculta a vericação em sistema mais complexos.
2.5 Anotações Java
Anotações foram introduzidas na versão 5.0 da linguagem Java e oferecem um me-canismo especial para a inserção de metadados no código fonte Java. Diferentemente de marcações para ns de documentação do tipo JavaDoc, as anotações Java podem ser re-exivas, ou seja, podem ser embutidas nos arquivos de classes gerados pelo compilador e capturadas pela Máquina Virtual Java (Java Virtual Machine - JVM ) em tempo de exe-cução. As anotações podem ser utilizadas em: classes, interfaces, construtores, variáveis, métodos, atributos de classes e parâmetros. As anotações possuem políticas de retenção, que determinam quando as mesmas podem ser utilizadas (HORSTMANN; CORNELL, 2008): • source: não permanecem no arquivo class gerado pelo compilador, são lidas apenas
durante o processo de compilação;
• class: a política de retenção padrão, as informações permanecem tanto em tempo de compilação quanto no class gerado, mas não precisam ser lidas pela JVM; • runtime: são lidas pelo compilador, permanecem no arquivo class e podem ser
obtidas em tempo de execução pela JVM.
É possível utilizar anotações para obter informações em tempo de execução, caso a política de retenção das mesmas permitam.Na Figura 11 é possível observar como é denida uma anotação (HORSTMANN; CORNELL, 2008):
1 import java . lang . annotation . ∗ ; 2
3 @Target ( ElementType .METHOD)
4 @Retention ( RetentionPolicy .RUNTIME) 5 public @ i n t e r f a c e TestCase {
6 S t r i n g id ( ) default"[none]" ; 7 }
Figura 11: Anotação @TestCase.
A meta anotação @Target dene o tipo de dado onde a anotação @TestCase pode ser utilizada, neste caso em métodos e @Retention dene o tipo da política de retenção.
É comum a utilização de anotações com o objetivo de tornar o desenvolvimento mais simples e claro para o desenvolvedor, substituindo-se práticas como o uso de arquivos de
descrição e conguração. Além disso, as anotações são frequentemente usadas na geração de código para testes e logging.
3 Metodologia
A metodologia adotada para a realização deste trabalho compreendeu as seguintes etapas principais:
3.1 Revisão bibliográca
Nesta etapa foi iniciado o trabalho com uma revisão na área de engenharia de software e modelagem de sistemas concorrentes, a m de levantar o estado da arte na área de vericação de conformidade em sistemas concorrentes. Esta etapa abrangeu também um estudo sobre as tecnologias usadas neste trabalho, como Programação Orientada a Aspectos e AspectJ. Além de um estudo sobre a modelagem de sistemas concorrentes utilizando Redes de Petri.
3.2 Análise e avaliação da técnica proposta por
An-drade (2008)
Na segunda etapa foram realizados experimentos utilizando-se alguns problemas clás-sicos de concorrência com a nalidade de compreender a referida técnica e de avaliar seu funcionamento.
3.3 Levantamento de ferramentas para modelagem de
Redes de Petri
Esta etapa compreendeu uma pesquisa e avaliação de ferramentas existentes para criação e simulação de Redes de Petri.
As atividades desta fase foram realizadas com o intuito de levantar o estado da arte em relação às ferramentas existentes. Também foi feita uma avaliação da possibilidade de usar uma das ferramentas analisadas no desenvolvimento deste trabalho.
Dentre as ferramentas para criação e simulação de Redes de Petri disponíveis sob licenças livres, foram analisadas: CPN Tools (RATZER et al., 2003), JARP (SANGOI, 2009),
Jfern (NOWOSTAWSKI, 2009) e Renew (KUMMER et al., 2004).
Na Tabela 1 é apresentada uma listagem das ferramentas analisadas e os componentes que cada uma possui. Estes componentes indicam as funções que cada ferramenta suporta. • o editor gráco permite construir modelos utilizando-se a notação gráca para
re-presentação de RP;
• a animação de tokens permite que a simulação do modelo seja visualizada graca-mente;
• a simulação rápida permite maximizar o desempenho da simulação, pois realiza a simulação do modelo sem animação gráca;
• através da análise de performance é possível avaliar um modelo, sendo possível otimizar uma RP;
• por último Interchange File Format indica que as ferramentas podem importar e exportar arquivos com um formato padrão, sendo possível usar um mesmo modelo em várias ferramentas. O formato adotado atualmente pelas ferramentas é o Petri Net Markup Language (PNML), baseado em eXtensible Markup Language (XML), e utiliza uma sintaxe padrão para a representação de modelos.
Observando a Tabela 1 é possível armar que as ferramentas CPN e Renew possuem a maior quantidade de componentes, oferecendo mais funcionalidades.
Tabela 1: Componentes das ferramentas analisadas.
Componentes/
Ferramentas Editor grá-co Animaçãode tokens Simulaçãorápida Análiseperformancede InterchangeFile Format
CPN • • • • •
Renew • • • •
JFern • • • • •
Jarp • • •
Na Tabela 2 são apresentados os tipos de Redes de Petri suportadas pelas ferramentas analisadas.
Na Tabela 3 é apresentada uma comparação entre as ferramentas, utilizando-se os seguintes critérios: interface gráca simples e intuitiva, existência de erros, licença livre, código aberto e a plataforma de execução das ferramentas. Estes critérios foram adotados com base em uma análise pessoal, uma vez que uma das ferramentas analisadas seria adotada em algumas etapas deste trabalho. A avaliação da interface gráca foi realizada de
Tabela 2: Tipos de Redes de Petri suportadas pelas ferramentas analisadas.
Tipo de RP/
Ferramentas Orientada a Objetos Alto nível Lugar Transição Temporizadas
CPN • • •
Renew • • • •
JFern • • • •
Jarp •
forma subjetiva, considerando-se a simplicidade na utilização da ferramenta. Os critérios licença livre e código aberto foram adotados devido a importância da permissão para realizar modicações no código da ferramenta, caso fosse necessário. A existência de erros foi considerada com a nalidade de que o trabalho não fosse prejudicado por erros presentes na ferramenta. O critério relativo a plataforma também foi analisado de forma subjetiva, uma vez que foram priorizadas as ferramentas independentes por serem mais exíveis.
Tabela 3: Comparação entre as ferramentas.
Itens /
Fer-ramentas InterfaceGráca Intuitiva
Erros Licença
livre Códigoaberto Plataforma
CPN • Windows
-Linux
Renew • • Independente
JFern • • • • Independente
Jarp • • • Independente
Com base nos itens apresentados nas Tabelas 1, 2, 3 foi realizada a seguinte análise: • CPN Tools é uma ferramenta amplamente utilizada para a edição, simulação e
aná-lise de Redes de Petri de alto nível. Possui versões para os Sistemas Operacionais Microsoft Windows e Linux. Apesar de ser possível utilizá-la livremente para ns não-comerciais, não é possível realizar modicações no seu código fonte. Esta ferra-menta possui um grande conjunto de funcionalidades, porém a sua interface gráca é bastante complexa, com isso necessita-se de tempo e prática para entender o seu funcionamento. Não poderia ser usada neste trabalho, uma vez que seu código fonte não é disponibilizado e a sua licença não permite modicações.
• Renew é um editor e simulador de Redes de Petri de alto nível. Permite a integração entre Redes de Petri e a linguagem de programação Java. Também possui um
grande conjunto de componentes/ funcionalidades e uma interface gráca bastante complexa.
• JFern possui uma interface gráca simples e intuitiva, tornando-a fácil de utilizar, contudo a versão 3.0.1, usada durante a pesquisa, apresentou alguns problemas durante a execução dos testes: não foi possível interromper a simulação de um modelo e muitas vezes a ferramenta lançou exceções inesperadas.
• Foi analisada a versão 1.1 da ferramenta Jarp. Esta foi desenvolvida em Java, e também possui uma interface simples e intuitiva. Oferece suporte para redes do tipo lugar-transição. Permite a exportação de arquivos para o formato padronizado PNML. Oferece suporte para as línguas inglesa, francesa e portuguesa. O Jarp foi utilizado para construir e simular os modelos usados neste trabalho.
• As ferramentas Renew e JFern oferecem suporte para mais tipos de RP.
A partir da análise realizada foi escolhida a ferramenta Jarp, apresentada na Figura 12. O Jarp está disponível sob a licença GNU General Public License (GPL), possibilitando o seu uso e alteração, desde que as modicações sejam liberadas. Parte do seu código fonte foi utilizado para implementar a ferramenta de vericação de conformidade desenvolvida neste trabalho.
Figura 12: Jarp: ferramenta utilizada neste trabalho.
3.4 Projeto e desenvolvimento
A partir dos resultados obtidos com o estudo e análise da técnica proposta por An-drade (2008), bem como do levantamento apresentado na seção anterior, foi desenvolvida a ferramenta para vericação automática de conformidade, objetivo principal deste tra-balho. No próximo capítulo são apresentados os detalhes relativos a esta etapa.
3.5 Aplicação e análise da ferramenta
Na última fase deste trabalho foi realizado um experimento que permitiu a utilização e análise da ferramenta de vericação de conformidade. Este experimento consistiu no desenvolvimento de um sistema concorrente, implementando-se um problema simples, mas real. Esta escolha se deve ao fato de que os detalhes de um sistema de grande complexidade não fazem parte do escopo deste trabalho. Além disso, optou-se por implementar um software, em lugar de se utilizar um sistema previamente desenvolvido, permitindo-se acompanhar todas as fases do projeto e implementação do problema escolhido. O objetivo do experimento foi identicar os benefícios e limitações deste trabalho. Mais detalhes sobre a sua realização serão apresentados posteriormente no capítulo intitulado Análise e Aplicação da Ferramenta.
4 Desenvolvimento da ferramenta
para vericação de conformidade
Neste capítulo são apresentados os detalhes referentes à implementação da ferramenta para vericação automática de conformidade.
4.1 Marcação de instruções no código
No trabalho proposto por (ANDRADE, 2008), o código fonte a ser analisado deve
conter marcações em pontos chave denidos pelo desenvolvedor. Essas marcações são usadas para gerar um trace que informam a ordem em que as instruções marcadas foram executadas.
Para realizar a marcação do código Andrade (2008) insere, nos métodos selecionados, uma instrução (Figura 13 linha 10) que identica os métodos executados. Para interceptar essa instrução é utilizado um Aspecto que captura o evento disparado e informa a sua ocorrência, adicionando uma linha ao trace. Uma análise da Figura 13 permite observar que este procedimento obriga o desenvolvedor a inserir uma instrução que não faz parte do código do sistema em análise. A necessidade de inserir esta instrução faz com que a aplicação da técnica interra de forma inapropriada no sistema em análise.
1 public synchronized void produce { 2 while ( spaces==capacity ) { 3 try { 4 wait ( ) ; 5 } catch ( InterruptedException e ) { 6 e . printStackTrace ( ) ; 7 } 8 } 9 ++spaces ;
10 T r a n s i t io n . f i r e T r a n s i t i o n ( "Uma ficha disponível!" ) ; 11 n o t i f y A l l ( ) ;
12 }
Figura 13: Marcação de código proposta por Andrade (2008).
Durante o desenvolvimento da ferramenta se percebeu a necessidade de reduzir a interferência da aplicação da técnica. Uma das possibilidades estudadas para realizar as
marcações foi a criação de uma linguagem própria da ferramenta. Porém, esta deveria ser intuitiva para o desenvolvedor, de forma que não dicultasse a vericação. Com isso, foram identicadas duas restrições: a necessidade de alterar as marcações para minimizar o acoplamento entre o código da ferramenta e do sistema a ser vericado e de adotar uma linguagem de marcação simples e intuitiva para o desenvolvedor. A solução escolhida para este problema foi utilizar o mecanismo de Anotações da linguagem Java combinado a POA. O uso de anotações é interessante para esse propósito principalmente por:
• possuírem uma sintaxe conhecida para os desenvolvedores, o que facilita a sua uti-lização;
• permitirem a inserção de metadados no código Java que podem ser usados em tempo execução;
• e também por facilitarem a atualização das marcações juntamente com mudanças realizadas no código fonte, semelhantemente as anotações do Javadoc.
Na Figura 14 é apresentada a anotação @FireTransition que foi desenvolvida para a ferramenta. Para interceptar as marcações em tempo de execução é necessá-rio que essa anotação seja mantida. Por isso, a política de retenção denida foi a RetentionPolicy.RUNTIME, o que garante que a anotação poderá ser lida durante a execução de um programa marcado.
1 import java . lang . annotation . ElementType ; 2 import java . lang . annotation . Retention ;
3 import java . lang . annotation . RetentionPolicy ; 4 import java . lang . annotation . Target ;
5 6
7 @Retention ( RetentionPolicy .RUNTIME) 8 @Target ( ElementType .METHOD)
9 public @ i n t e r f a c e F i r e T r a n s i t i o n { 10
11 }
Figura 14: Anotação @FireTransition.
No trabalho realizado por Andrade (2008), o código responsável pelo disparo das transições estava diretamente acoplado ao sistema em análise. Porém, a ferramenta precisa identicar a instrução marcada de forma genérica, permitindo que seja possível aplicá-la em qualquer sistema concorrente. Com esta nalidade, primeiramente a anotação foi restringida a métodos, através do target: denido @Target(ElementType.METHOD). Em seguida, deniu-se que a assinatura de um método com esta anotação deve corresponder
a transição do modelo que ele irá disparar. Assim, durante a execução, qualquer método anotado com @FireTransition é interceptado e a sua assinatura é usada para disparar uma transição correspondente no modelo.
Na Figura 15 é apresentada a marcação no método produce da classe LimitedBuffer e o modelo correspondente. Esta classe foi usada na implementação do problema do buer limitado. Uma análise da Figura 15 permite armar que a interferência da técnica no código foi reduzida e que a relação entre modelo e código tornou-se mais próxima, devido ao padrão adotado para mapear as transições. Além disso, como as marcações são inseridas de forma simples e com uma linguagem conhecida pelos desenvolvedores, acredita-se que estes fatores simpliquem a utilização da ferramenta.
1 @FireTransition
2 public synchronized void produce { 3 while ( spaces==capacity ) { 4 try { 5 wait ( ) ; 6 } catch ( InterruptedException e ) { 7 e . printStackTrace ( ) ; 8 } 9 } 10 ++spaces ; 11 n o t i f y A l l ( ) ; 12 }
Figura 15: Marcação do método produce e transição correspondente no modelo.
4.2 Utilização de POA para captura das marcações
Para interceptar os pontos marcados e disparar as transições correspondentes foi uti-lizada a POA. A abordagem usada para trabalhar com esse paradigma foi a linguagem AspectJ. Foi desenvolvido um Aspecto responsável pela captura dos métodos marcados com a anotação @FireTransition. Esse Aspecto, apresentado na Figura 16, possui um pointcut formado por dois joinpoints que interceptam a execução de qualquer método que
contenha a anotação supracitada.
1 public aspect AspectTransition {
2 b e f o r e ( ) : execution (∗ ∗ . ∗ ( . . ) ) &&
3 @annotation ( simulator . annotations . F i r e T r a n s i t i o n ) { 4 T ra ns it io n . f i r e T r a n s i t i o n ( t r a n s i t i o n ) ;
5 }
6 }
Figura 16: Aspecto para interceptar as marcações.
Assim como no trabalho de Andrade (2008) foi utilizado um advice do tipo before. Porém durante os testes com a ferramenta foram identicadas inconsistências na verica-ção. Na gura 17 apresenta-se os resultados de uma vericação realizada na aplicação que simula o buer limitado. Após a realização de testes com o código, foi identicado que a causa do problema estava no tipo de advice utilizado. Mesmo quando uma das threads era suspensa o advice era executado e uma transição que não estava habilitada no momento era disparada. Assim, durante a execução parecia que o produtor tentava inserir chas no buer onde não havia mais espaços, porém, na verdade a thread produtora havia sido suspensa.
Figura 17: Inconsistência nos resultados da vericação com um advice o tipo before.
Para resolver este problema, foi alterado o tipo do advice de before para after returning, solução apresentada na Figura 18. Esta construção da linguagem AspectJ faz com que o advice seja executado somente após o retorno com sucesso do método interceptado. Desta forma, caso um método seja capturado e interrompido, o Aspecto também é suspenso até que o evento seja habilitado.
1 public aspect AspectTransition { 2
3 a f t e r ( ) r e t u r n i n g ( ) : execution (∗ ∗ . ∗ ( . . ) ) && 4 @annotation ( simulator . annotations . F i r e T r a n s i t i o n ) {
5 S t r i n g t r a n s i t i o n = t h i s J o i n P o i n t . g e t S ig na t u r e ( ) . getName ( ) ; 6 T r a n s it io n . f i r e T r a n s i t i o n ( t r a n s i t i o n ) ; 7 8 } 9 }
Figura 18: Utilização do advice after returning para resolver o problema de inconsistên-cia.
Com o tipo after returning, tanto o advice quanto o método marcado são executa-dos atomicamente, o que garante a consistência nos resultaexecuta-dos forneciexecuta-dos pela ferramenta. Na Figura 19 é possível observar que os resultados da vericação são consistentes após a mudança descrita.
Figura 19: Resultados obtidos após a modicação do Aspecto.
4.3 Necessidade de identicação de instâncias em
exe-cução
Considerando-se ainda o exemplo do buer limitado, caso existissem dois consumi-dores e dois produtores, seriam acrescentadas mais transições para inserir e retirar chas do buer. Na aplicação seria necessário instanciar mais processos para produzir e consu-mir. Porém, a marcação do código seria comprometida, pois em uma classe seus métodos
seriam responsáveis por disparar várias transições iguais.
Andrade (2008) solucionou este problema através de uma identicação passada ao método responsável por gerar o trace. Esta solução é apresentada na Figura 20, onde é possível observar que nas linhas 3 e 10 é identicado o objeto responsável pelo evento.
1 public synchronized void put ( int p h i l ) { 2 taken=f a l s e ;
3 T r a n s i t io n . f i r e T r a n s i t i o n ( "Filósofo " + p h i l + " devolve garfo " +
i d e n t i t y ) ;
4 n o t i f y ( ) ;
5 }
6
7 public synchronized void get ( int p h i l ) throws InterruptedException { 8 while ( taken ) wait ( ) ;
9 taken=true ;
10 Tr a n s i t i o n . f i r e T r a n s i t i o n ( "Filósofo " + p h i l + " pega garfo " +
i d e n t i t y ) ;
11 }
Figura 20: Identicação do objeto que dispara uma transição (ANDRADE, 2008).
Esta solução, apesar de simples, não seria aplicável ao projeto da ferramenta. Marcar o código desta forma iria provocar uma grande interferência da ferramenta sobre o código. Além de torná-la dependente do sistema em análise. Para resolver este problema, foi necessário criar uma forma genérica de identicar o objeto responsável pela execução dos métodos. Para isso, foi criada uma interface Java Simulable. Esta possui um método getId cuja nalidade é permitir que o Aspecto possa identicar a instância da classe cujo método será disparado. Assim, cada objeto deve possuir uma identicação única. Esse procedimento permite que o Aspecto capture a identicação do objeto e o método executado e faça o disparo das transições correspondentes. Para utilizá-lo, foi feita uma nova alteração no advice, inserindo-se um joinpoint, que restringe as classes capturadas ao tipo Simulable. Esta alteração, mostrada na Figura 21, permite que apenas as classes que implementam esta interface sejam capturadas pelo Aspecto.
A solução escolhida causou uma interferência no código a ser analisado. Todavia, possibilitou a aplicação da ferramenta em sistemas que contém várias instâncias de uma mesma classe, permitindo que a ferramenta seja aplicável a projetos com maior nível de complexidade.
4.4 Apresentação dos resultados da vericação
Andrade (2008) utiliza o resultado da execução de um programa, obtido através do trace gerado, para confrontar manualmente a simulação do modelo e o comportamento do programa analisado. O trace resultante é impresso no console onde o programa é
execu-1 public aspect AspectTransition { 2
3
4 a f t e r ( Simulable se ) r e t u r n i n g ( ) : execution (∗ ∗ . ∗ ( . . ) ) &&
@annotation ( simulator . annotations . F i r e T r a n s i t i o n ) && t a r g e t ( se ) { 5 S t r i n g t r a n s i t i o n = se . getId ( )+ "." + t h i s J o i n P o i n t . g e t S ig na t u r e ( ) . getName ( ) ; 6 T r a ns it io n . f i r e T r a n s i t i o n ( t r a n s i t i o n ) ; 7 8 } 9 }
Figura 21: Versão nal de AspectTransition, responsável pela interceptação do código marcado.
tado. O desenvolvedor precisa ler os resultados do trace e compará-los a uma simulação do modelo para vericar a existência de inconformidades. Este procedimento torna-se cansativo e demanda tempo e disposição do desenvolvedor, principalmente se for consi-derada a análise de um sistema de grande complexidade. Uma alternativa encontrada para este problema foi utilizar um painel na interface gráca da ferramenta de vericação. Este painel é usado para mostrar os resultados obtidos através do trace. Desta forma, além de visualizar a simulação automática do modelo, o desenvolvedor pode acompanhar em tempo real o uxo de execução do programa. Além disso, foi alterado o mecanismo de geração dos traces, de modo que agora estes informam se uma transição disparada estava habilitada ou não. Assim, quando um método dispara uma transição do modelo, é vericado se esta foi habilitada. Em caso positivo, é feita alteração no estado do modelo e impresso um trace da instrução executada. Caso este não esteja habilitada no momento, o estado do modelo não é alterado, mas informa-se através do trace que uma transição não pôde ser disparada. Na Figura 22 é apresentada uma vericação realizada com a versão nal da ferramenta onde foi inserida uma inconformidade proposital.
Esta implementação tem por objetivo informar automaticamente a ocorrência de in-conformidades, permitindo que o desenvolvedor acompanhe o resultado da vericação em tempo real. Além disso, o desenvolvedor pode visualizar simultaneamente modelo e có-digo em execução. Com isso, permite-se que na ocorrência de uma inconformidade seja possível identicar o estado do modelo e consequentemente do sistema no momento em que esta for detectada, o que facilita a correção dos erros encontrados.
4.5 Utilização do Jarp
Para a simulação e exibição dos modelos foi utilizado o analisador de Redes de Petri Jarp. Este oferece os recursos necessários para a simulação interativa e a composição
Figura 22: Aplicação da ferramenta, destaque para o painel com o trace gerado.
visual de Redes de Petri. Para implementar a parte da ferramenta referente a simulação e exibição dos modelos durante a vericação o Jarp foi integrado ao projeto. Além disso, como o Jarp oferece suporte para o formato de arquivos PNML a sua integração permite que o modelo de uma RP do tipo lugar transição construída em qualquer ferramenta seja importado e utilizado na ferramenta.
4.6 Vericação de conformidade através da ferramenta
Os seguintes passos são necessários para utilizar a ferramenta: • importá-la no projeto a ser analisado;
• escolher os pontos chave para vericação;
• escolher as classes que irão implementar a interface Simulable;
5 Aplicação e Análise da
Ferramenta
Com o propósito de avaliar a ferramenta foi denida a realização de um experimento que permitisse apontar seus benefícios e limitações. O objetivo desse experimento era simular um caso típico, através da implementação de um problema simples mas real, onde fosse possível desenvolver um sistema a partir de um especicação em Rede de Petri. Nesta seção os resultados e discussões a respeito deste experimento serão apresentados.
5.1 Descrição da aplicação
O problema escolhido envolve o desenvolvimento de um sistema para simulação de uma ferrovia. A sua nalidade é permitir a simulação de dois trens percorrendo um trilho em sentido horário. Como pode ser observado na Figura 23 os trens percorrem um trilho em comum.
Figura 23: Especicação do sistema de ferrovias.
A m de evitar acidentes, apenas um trem pode utilizar o trilho compartilhado em um determinado momento, criando uma condição de disputa (race condition). Esse tipo de situação ocorre quando dois ou mais processos leem ou escrevem em algum dado com-partilhado e o resultado nal depende da ordem de execução dos mesmos (TANENBAUM, 2007). O trilho compartilhado constitui uma região crítica (critical section) acessada
pe-los dois processos referentes aos trens. Assim, o sistema deve tratar a sincronização dos trens para garantir que não haja colisão no trilho compartilhado. Para isso, é preciso fazer com que os dois processos nunca entrem em suas regiões criticas ao mesmo tempo. Quando um trem entrar no trilho compartilhado o outro deve esperar que o trilho seja liberado, garantindo-se a segurança de ambos os trens.
Uma solução existente para resolver problemas de compartilhamento de recursos é o uso de semáforos. Um semáforo pode ser representado por uma variável inteira não negativa que é manipulada por instruções de down e up (TANENBAUM, 2007). O semáforo
indica quando o recurso associado a ele é utilizado por um dos processos concorrentes: • S > 0: nenhum processo está utilizando o recurso;
• S = 0: processo impedido de utilizar o recurso.
5.2 Modelagem da aplicação
Como a ferramenta utiliza modelos em Rede de Petri, foi construída uma especicação para o problema descrito, apresentada na Figura 24. Uma análise desta permite observar a situação de conito existente entre as transições T3 e T7, que reetem a disputa pelo trilho compartilhado.
Figura 24: Modelo em Rede de Petri para o sistema de trens.
Na Tabela 4 são apresentados todos os estados referentes ao modelo da Figura 24. O trilho compartilhado é a região crítica RC, os processos pertinentes a simulação são os trens (Ta e Tb) e o "Semáforo" que representa o semáforo responsável pelo controle do
acesso ao trilho. Os estados P4 e P9 não podem ser alcançados ao mesmo tempo, pois estes representam os trens Ta e Tb acessando respectivamente o trilho compartilhado.
Tabela 4: Lista de estados para o modelo da ferrovia
ESTADOS
P1 - Ta espera para iniciar P2 - Ta correndo
P3 - Ta espera para acessar a RC P4 - Ta correndo na RC
P5 - Semáforo
P6 - Tb espera para iniciar P7 - Tb correndo
P8 - Tb espera para acessar a RC P9 - Tb correndo na RC
As transições referentes ao modelo da Figura 24 são apresentadas na Tabela 5. As transições T3 e T7 não podem ser disparadas ao mesmo tempo, devido a existência do conito relativo ao semáforo. Para disparar T3 ou T7 é preciso que haja uma cha no estado P5, ou seja, no "Semáforo", o que indica que o acesso ao trilho está liberado.
Tabela 5: Lista de transições para o modelo da ferrovia
AÇÕES T1 - Ta inicia T2 - Ta solicita acesso a RC T3 libera RC para Ta T4 - Ta deixa a RC T5 Tb inicia T6 Tb solicita acesso a RC T7 libera RC para Tb T8 Tb deixa a RC
A partir da especicação apresentada na Figura 24 foi desenvolvida uma aplicação que simula os dois trens, respeitando-se as condições de integridade descritas anteriormente. Porém, durante a realização das marcações para posterior vericação do sistema desenvol-vido, observou-se a possibilidade de reduzir esta especicação. Esta tarefa permitira uma simplicação do modelo, preservando-se as propriedades a serem analisadas. Uma versão simplicada do modelo foi obtida, onde conservaram-se os estados e transições ligados às condições de disputa, pois estes constituem as propriedades do sistema que se desejava estudar. É possível identicar que permaneceram no modelo apenas as transições refe-rentes à entrada e saída dos trens do trilho compartilhado. Alguns estados como P1 e P6
foram descartados, pois a sua análise não era necessária para a vericação de corretude do sistema.
Figura 25: Modelo em Rede de Petri para o sistema de trens após a simplicação.
O modelo resultante deste processo, apresentado na Figura 25, mesmo que obtido empiricamente, conserva as propriedades necessárias para especicação do sistema. Mu-rata (1989) destaca a utilização de técnicas para simplicação de Redes de Petri com o objetivo de facilitar a análise de grandes sistemas. Esta etapa do experimento possibi-litou a observação de um aspecto relevante para o trabalho, uma vez que demonstrou a sua capacidade em auxiliar na simplicação de Redes de Petri, facilitando a análise dos modelos.
5.3 Desenvolvimento e marcação
A partir da especicação apresentada na seção anterior foi desenvolvido em Java, um programa concorrente para a simulação da ferrovia. Cada trem foi implementado através de uma thread, o que permite que os dois executem independentemente. O controle do acesso ao trilho compartilhado, foi implementado com um semáforo. Durante a simula-ção os processos devem solicitar ao semáforo o acesso ao trilho. Para implementá-lo foi utilizada a classe Semaphore do pacote java.util.concurrent.
Após o desenvolvimento, foram mapeados os pontos-chave do código. As marcações, apresentadas na Figura 26, foram inseridas nos métodos enterRC e leaveRC na classe Train. Estes correspondem aos eventos de entrada e saída dos trens do trilho comparti-lhado.
Em seguida a marcação, foi realizada a simulação do sistema apresentada na Figura 27. A vericação de conformidade entre o modelo da Figura 25 e o sistema, é apresentada na Figura 28. Nesta destacam-se os traces gerados durante a execução. Observando os resultados da vericação é possível notar que a ferramenta apontou inconformidades. Porém, através de um acompanhamento da simulação gráca do sistema, entendeu-se
1 @FireTransition
2 private synchronized void enterCR ( ) throws InterruptedException { 3 semaphore . a c q u i r e ( ) ;
4 }
5
6 @FireTransition
7 private synchronized void leaveCR ( ) { 8 semaphore . r e l e a s e ( ) ;
9 }
Figura 26: Marcação das instruções na classe Train.
que o mesmo estava correto. A partir disso, foram analisadas as marcações e percebeu-se que estas foram inpercebeu-seridas incorretamente. Assim, espercebeu-ses resultados foram produzidos devido à falta de compreensão da correspondência entre código e modelo, de modo que as propriedades do modelo não foram corretamente reetidas pela marcação realizada.
Figura 27: Programa para simulação das ferrovias em execução.
Após encontrar este problema, foi realizada uma nova análise do sistema com o auxílio do modelo, a m de identicar os pontos que deveriam ser marcados. Nesta análise buscou-se as instruções que deveriam buscou-ser responsáveis pelo disparo das transições prebuscou-sentes na rede, constituindo os pontos-chave para a vericação do sistema. Observando o modelo, percebeu-se que o responsável pela modicação dos estados era o semáforo e não os trens como se supôs anteriormente. A partir deste ponto foi necessário remarcar o código para possibilitar uma análise correta do sistema.
A principal modicação necessária foi colocar as marcações nos métodos referentes às mudanças no semáforo. Porém, a técnica de vericação possui uma restrição de atomi-cidade que não permite a marcação em classes de bibliotecas externas (ANDRADE, 2008).
Neste projeto, havia sido utilizada a classe Semaphore pertencente à Application Pro-gramming Interface (API) Java. Assim, foi necessário criar uma classe para implementar
Figura 28: Aplicação da ferramenta de vericação no sistema de ferrovias.
um semáforo e realizar a marcação dos métodos, apresentada na Figura 29.
1 @FireTransition
2 public synchronized void down( S t r i n g id ) { 3 4 i f ( f r e e ) { 5 f r e e = f a l s e ; 6 } else { 7 try { 8 wait ( ) ; 9 f r e e = f a l s e ; 10 this . id=id ; 11 } catch ( InterruptedException e ) { 12 } 13 } 14 } 15 16 @FireTransition
17 public synchronized void up ( S t r i n g id ) { 18 this . id=id ;
19 f r e e = true ; 20 n o t i f y A l l ( ) ;
21 }
Na Figura 30 apresentam-se os resultados da vericação após as mudanças nas mar-cações. É possível notar que foi identicada a conformidade entre o modelo construído e o sistema implementado.
Figura 30: Vericação de conformidade do sistema de ferrovias.
Os resultados obtidos com as marcações incorretas permitiram observar que a uti-lização da técnica força o desenvolvedor a uma compreensão correta do sistema. Na primeira simulação, foram obtidos resultados inconsistentes, pois os métodos escolhidos para marcação não correspondiam aos pontos-chave do sistema.
5.3.1 Alteração no projeto do sistema
Com a nalidade de continuar o experimento, foi introduzido um novo requisito no sistema de ferrovias. Foram adicionados um novo trem e um novo trilho. Na Figura 31 é apresentada a nova especicação para o sistema. De acordo com esta, foi incluída uma nova região crítica, pois o novo trem compartilha um trilho com um dos trens denidos an-teriormente. Assim, um dos trens percorre dois trilhos compartilhados. Para garantir que não haja colisões é preciso inserir um novo semáforo, possibilitando o compartilhamento seguro dos trilhos.
Figura 31: Alteração na ferrovia.
Primeiramente foram realizadas as modicações no modelo, seguindo-se as novas espe-cicações. A versão simplicada, obtida durante a fase anterior do experimento, foi usada como base para inserir o novo requisito. Concluiu-se que seria desnecessário construir uma especicação completa do sistema, como o modelo apresentado na Figura 24. Assim, a quantidade de alterações necessárias no modelo foi diminuída consideravelmente, já que a etapa de construção/simplicação não foi realizada. Com isso, a extensão do modelo foi facilitada, reduzindo o trabalho do desenvolvedor durante a alteração do projeto.
A existência de duas regiões críticas nesta nova versão do sistema gerou um aumento na sua complexidade. Na Figura 32 apresenta-se o modelo que especica esta versão. É possível observar o aumento da sua complexidade, o que reete as alterações no projeto. Nesta versão do sistema existem duas regiões críticas. Desta forma, realizar uma análise manual de conformidade entre modelo e código seria ainda mais difícil. No entanto, a vericação realizada através da ferramenta permanece com a mesma complexidade. Como o código já havia sido marcado e o modelo contém as mesmas especicações usadas no experimento anterior, não foi necessária nenhuma modicação adicional para realizar a vericação.
A execução do programa é apresentada na Figura 33. Na Figura 34 o resultado da vericação de conformidade é apresentado. É possível observar que a conformidade entre sistema e modelo foi mantida após a realização das alterações.
5.3.2 Resultados observados
Nesta seção apresenta-se um resumo das observações sobre o experimento realizado. É interessante ressaltar que os benefícios obtidos com o experimento podem ser ainda
Figura 32: Modelo em Rede de Petri para a segunda versão do sistema.
maiores uma vez que foi adotado um sistema relativamente simples e que podem ser potencializados se forem considerados casos de sistemas complexos ou reais. Dentre os benefícios obtidos com o experimento realizado destacam-se:
• a utilização da ferramenta faz com que o desenvolvedor precise conhecer mais deta-lhes e tenha uma visão mais clara dos pontos críticos do sistema. Assim, a aplicação da ferramenta pode ajudá-lo na implementação e manutenção de um projeto de soft-ware;
altera-Figura 33: Simulação da segunda versão do sistema de ferrovias.
Figura 34: Vericação de conformidade da segunda versão do sistema de ferrovias.
ções em um projeto é simplicado. No experimento foram inseridos novos requisitos no sistema implementado, o que reete condições reais de trabalho, onde este tipo de mudança ocorre frequentemente. Porém, não foi necessária nenhuma modica-ção adicional para realizar novamente a vericamodica-ção. Esta observamodica-ção permite inferir que a simplicidade na manutenção de conformidade entre modelo e código ajuda o