Sarah Raquel da Rocha Silva
BTS: Uma ferramenta de suporte ao desenvolvimento
de sistemas confiáveis baseados em componentes
Natal/RN
2013
Sarah Raquel da Rocha Silva
BTS: Uma ferramenta de suporte ao desenvolvimento
de sistemas confiáveis baseados em componentes
Dissertação apresentada ao Programa de
Pós-graduação em Sistemas e Computação
do Departamento de Informática e Matemática Aplicada da Universidade Federal do Rio
Grande do Norte, como requisito parcial para
a obtenção do grau de Mestre em Ciência da
Computação.
Orientador: Prof. Dr. Marcel Vinícius Medeiros Oliveira
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/RN
2013
Catalogação na fonte.
Ficha catalográfica elaborada pela Bibliotecária
Aline Nascimento Silva CRB 15/711
Silva, Sarah Raquel da Rocha.
BTS: uma ferramenta de suporte ao desenvolvimento de sistemas confiáveis
baseados em componentes / Sarah Raquel da Rocha. – Natal, RN, 2013.
81 f. : il.
Orientador: Dr. Marcel Vinícius Medeiros Oliveira
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.
1. Métodos formais – Dissertação. 2. Desenvolvimento baseado em
componentes – Dissertação. 3. Concorrência – Dissertação. 4. Verificação de
modelos – Dissertação. I. Oliveira, Marcel Vinícius Medeiros. II. Universidade
Federal do Rio Grande do Norte. III. Título
CDU: 004.4`2
BTS: Uma ferramenta de suporte ao desenvolvimento
de sistemas conáveis baseados em componentes
Sarah Raquel da Rocha Silva
Dissertação de Mestrado aprovada em 13 de dezembro de 2013 pela banca examinadora
composta pelos seguintes membros:
Prof. Dr. Marcel Vinícius Medeiros Oliveira (orientador) . . . . . . . . . . . . . . . DIMAp/UFRN
Profa. Dra. Anamaria Martins Moreira . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . DIMAp/UFRN
Prof. Dr. Alexandre Cabral Mota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . UFPE
iii
Agradecimentos
A Deus, por ter me dado saúde e força para superar as dificuldades.
Ao professor Marcel, pela orientação e confiança.
Ao meu amigo, companheiro de trabalho e irmão na amizade Dalay, pelo carinho,
ajuda pessoal, psicológica e pela amizade que vai continuar presente em minha vida.
A Igor, por ter me dado apoio para entrar no mestrado, e por todo incentivo e carinho
para enfrentar e superar as dificuldades.
Aos meus pais (Rita e Francisco) e meus irmãos (Sérgio e Filipe), pelo amor, incentivo
e apoio incondicional. Por serem meu porto seguro, por sempre me apoiarem e por me
mostrarem que nenhum de nós nasce pronto, mas somos moldados pelas nossas escolhas.
A todos os amigos que me ajudaram direta ou indiretamente. A minha amiga de
infância Luciana, por ser sempre minha amiga de todas as horas. A meus companheiros
de laboratório Viviane, Ernesto, Sidney e Hélio, que fizeram parte dessa fase ao qual crie
laços que espero levar por toda minha vida.
A secretaria de pós-graduação da UFRN, que por intermédio da professora Fernanda
Nervo Raffin, me forneceu ajuda financeira durante essa fase.
A todos vocês, o meu muito obrigada.
iv
Resumo
O desenvolvimento de sistemas baseados em componentes revolucionou o processo de
desenvolvimento de software, facilitando a manutenção, trazendo mais confiabilidade e
reutilização. Porém, mesmo com todas as vantagens atribuídas ao componente, é necessário uma análise detalhada de sua composição. Realizar verificação a partir de testes
de software não é o suficiente para se ter uma composição segura, pois esses não são
baseados em modelos semânticos formais nos quais podemos descrever precisamente o
comportamento do sistema. Nesse contexto, os métodos formais oferecem a possibilidade de especificarmos sistemas de forma precisa, através de notações com forte base
matemática, trazendo, entre outros benefícios, mais segurança. O método formal CSP
possibilita a especificação de sistemas concorrentes e verificação de propriedades inerentes
a tais sistemas, bem como refinamento entre diferentes modelos. Existem abordagens que
aplicam restrições usando CSP, para verificar o comportamento da composição entre componentes, auxiliando a verificação desses componentes antecipadamente. Visando auxiliar
esse processo, tendo em vista que o mercado de software busca cada vez mais automação,
minimizando trabalhos e trazendo agilidade nos negócios, este trabalho apresenta uma ferramenta que automatiza a verificação da composição entre componentes, onde o conjunto
de verificações CSP impostas é gerado e verificado internamente, oculto para o usuário.
Dessa forma, através de uma interface simples, a ferramenta BTS (BRIC-Tool-Suport)
ajuda a criar e compor componentes, prevendo, com antecedência, comportamentos indesejáveis no sistema, como deadlocks.
Keywords: Métodos Formais, Desenvolvimento Baseado em Componentes, Concorrência, Verificação de Modelos.
v
Abstract
The component-based development of systems revolutionized the software development
process, facilitating the maintenance, providing more confiability and reuse. Nevertheless,
even with all the advantages of the development of components, their composition is an
important concern. The verification through informal tests is not enough to achieve a
safe composition, because they are not based on formal semantic models with which we
are able to describe precisally a system’s behaviour. In this context, formal methods
provide ways to accurately specify systems through mathematical notations providing,
among other benefits, more safety. The formal method CSP enables the specification
of concurrent systems and verification of properties intrinsic to them, as well as the
refinement among different models. Some approaches apply constraints using CSP,
to check the behavior of composition between components, assisting in the verification
of those components in advance. Hence, aiming to assist this process, considering
that the software market increasingly requires more automation, reducing work and
providing agility in business, this work presents a tool that automatizes the verification
of composition among components, in which all complexity of formal language is kept
hidden from users. Thus, through a simple interface, the tool BST (BRIC-Tool-Suport)
helps to create and compose components, predicting, in advance, undesirable behaviors in
the system, such as deadlocks.
Keywords: Formal Methods, Component-Based Development, competition, Model
Checking.
vi
Sumário
Lista de Figuras
p. viii
Lista de Tabelas
p. xi
1 Introdução
p. 1
1.1
Visão Geral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 1
1.2
Motivação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 2
1.2.1
Proposta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 3
1.2.2
Trabalhos Relacionados . . . . . . . . . . . . . . . . . . . . . . .
p. 4
Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 5
1.3.1
Objetivos Gerais . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 5
1.3.2
Objetivos Específicos . . . . . . . . . . . . . . . . . . . . . . . .
p. 5
Estrutura da Dissertação . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 6
1.3
1.4
2 Fundamentação Teórica
p. 7
2.1
Sistemas Baseados em Componentes . . . . . . . . . . . . . . . . . . .
p. 7
2.2
CSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 9
2.3
2.2.1
Semântica CSP . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 13
2.2.2
Ferramentas para CSP . . . . . . . . . . . . . . . . . . . . . . .
p. 18
Abordagem para o desenvolvimento sistemático de sistemas confiáveis .
p. 22
3 Uma verificação formal para desenvolvimento de sistemas confiáveis baseados em
componentes
p. 36
3.1
p. 36
Análise CSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Sumário
vii
4 BTS: Ferramenta de suporte ao desenvolvimento de sistemas confiáveis baseados
em componentes
p. 46
4.1
Detalhes Técnicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 46
4.2
Ferramenta BTS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 47
4.2.1
Aplicação prática da estratégia pela BTS . . . . . . . . . . . . .
p. 55
4.2.2
Considerações Finais . . . . . . . . . . . . . . . . . . . . . . . .
p. 64
p. 65
5 Estudo de Caso
5.1
Buffer Circular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 65
5.2
Aplicação do Buffer Circular na BTS . . . . . . . . . . . . . . . . . . .
p. 68
5.2.1
Contrato BRIC de Cell e Controller
. . . . . . . . . . . . . . .
p. 68
5.2.2
Composição . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 70
Análise do Estudo de Caso . . . . . . . . . . . . . . . . . . . . . . . . .
p. 74
5.3
6 Considerações finais
6.1
Trabalhos Futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Referências Bibliográficas
p. 76
p. 77
p. 78
viii
Lista de Figuras
2.1
Atividades Para o Desenvolvimento com Componentes . . . . . . . . .
p. 8
2.2
Exemplo de especificação CSP . . . . . . . . . . . . . . . . . . . . . . .
p. 10
2.3
Máquina de estado do processo PARQUE criada pelo FDR2 . . . . . . .
p. 15
2.4
Processos PORTAO1 e PORTAO2 com seus estados e suas recusas . . .
p. 16
2.5
Processos divergentes . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 17
2.6
Interface da FDR2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 20
2.7
Interface da ProBe . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 20
2.8
Interface da ProB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 21
2.9
Modelo de componente . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 22
2.10 Exemplo de um Cliente-Servidor . . . . . . . . . . . . . . . . . . . . . .
p. 23
2.11 Descrição dos tipos e canais . . . . . . . . . . . . . . . . . . . . . . . .
p. 23
2.12 Descrição do comportamento do componente Cliente . . . . . . . . . .
p. 23
2.13 Descrição do comportamento do componente Máquina . . . . . . . . .
p. 23
2.14 Protocolo do Cliente para o canal maq . . . . . . . . . . . . . . . . . .
p. 26
2.15 Protocolo da Maquina para o canal maq . . . . . . . . . . . . . . . . .
p. 27
2.16 Dual-protocol de Prot_Cliente . . . . . . . . . . . . . . . . . . . . . . .
p. 28
2.17 Reutilização de componente . . . . . . . . . . . . . . . . . . . . . . . .
p. 28
2.18 Criando um novo componente Máquina . . . . . . . . . . . . . . . . . .
p. 29
2.19 Exemplo de comunicação via Buffer . . . . . . . . . . . . . . . . . . . .
p. 29
2.20 Canais desacoplados . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 31
2.21 Compatibilidade de Auto-Injeção com Buffer . . . . . . . . . . . . . . .
p. 31
2.22 Exemplo de três regras de composição . . . . . . . . . . . . . . . . . . .
p. 31
Lista de Figuras
ix
4.1
Diagrama de Caso de Uso da BTS . . . . . . . . . . . . . . . . . . . . .
p. 47
4.2
Diagrama de sequência da criação de tipos . . . . . . . . . . . . . . . .
p. 48
4.3
Interface para criação de tipos . . . . . . . . . . . . . . . . . . . . . . .
p. 49
4.4
Interface para criação de canais . . . . . . . . . . . . . . . . . . . . . .
p. 49
4.5
Diagrama de sequência da criação de contrato . . . . . . . . . . . . . .
p. 50
4.6
Interface para criação de contrato . . . . . . . . . . . . . . . . . . . . .
p. 51
4.7
Tela de resultado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 51
4.8
Tela de instanciação . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 52
4.9
Diagrama de sequência da composição . . . . . . . . . . . . . . . . . .
p. 53
4.10 Tela de Composição . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 53
4.11 Estrutura da BTS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 54
4.12 Entradas e saídas da BTS . . . . . . . . . . . . . . . . . . . . . . . . .
p. 54
4.13 Interface BTS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 55
4.14 Exemplo de comportamento do componente . . . . . . . . . . . . . . .
p. 57
4.15 Interface de saída do TypeChecker . . . . . . . . . . . . . . . . . . . . .
p. 57
5.1
Buffer Circular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 66
5.2
Representação do componente Cell . . . . . . . . . . . . . . . . . . . .
p. 66
5.3
Representação do componente Controller . . . . . . . . . . . . . . . . .
p. 66
5.4
Interface para descrição de eventos de entrada e saída . . . . . . . . . .
p. 69
5.5
Interface para descrição protocolo e dual-protocols . . . . . . . . . . . .
p. 71
5.6
Tela de composição com o exemplo da composição em interleave . . . .
p. 71
5.7
Composição em interleave de Cell1 e Cell2 . . . . . . . . . . . . . . . .
p. 72
5.8
Composição em interleave de Cell1, Cell2 e Cell3 . . . . . . . . . . . .
p. 72
5.9
Composição em Communication do Controller com as Células . . . . .
p. 73
5.10 Composição em feedback do componente resultante das células com o
Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 73
Lista de Figuras
x
5.11 Composição em reflexive do componente resultante da composição em
feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p. 74
xi
Lista de Tabelas
2.1
Operadores da linguagem CSP . . . . . . . . . . . . . . . . . . . . . . .
p. 11
3.1
Teste de Caracterização da Composição em Interleave . . . . . . . . . .
p. 37
1
1
Introdução
Neste capítulo apresentaremos uma visão geral e os fatores motivadores desta dissertação, além da proposta e objetivos que buscamos alcançar. Por fim, apresentaremos a
estrutura que segue este documento, descrevendo o que será visto em cada capítulo.
1.1
Visão Geral
A engenharia de software surgiu como uma forma de organizar e gerenciar o desenvolvimento de software [WIRTH, 2008]. Esta preocupa-se com todos os aspectos de produção
do sistema, desde a especificação inicial, passando pela implementação, implantação e manutenção.
Cada vez mais buscam-se características que melhorem a qualidade do software, como
maior facilidade de manutenção, confiabilidade e reutilização. Uma grande ideia que
revolucionou o processo de desenvolvimento foi compor o sistema em pequenos pedaços
que chamamos de componentes [MCILROY, 1968], descritos em [SZYPERSKI, 2002] da
seguinte forma: "Um componente de software é uma unidade de composição com interfaces
especificadas através de contratos e dependências de contexto explícitas. Um componente
de software pode ser distribuído independentemente e está sujeito a composição com outras
partes".
O desenvolvimento de sistemas baseados em componentes (SBC)[SPAGNOLI; BECKER, 2003] estabelece uma integração entre os componentes de software, que facilitam o
acréscimo de novas funcionalidades, a manutenção e a substituição dos componentes existentes. Porém, mesmo com todas as boas características atribuídas ao desenvolvimento
baseado em componentes, estes devem ser compostos de forma segura, principalmente
quando lidamos com sistemas críticos. Um sistema de aviação, por exemplo, deve ter alta
confiabilidade para que em caso de falha, continue operando normalmente [MOTA et al.,
2010].
1.2 Motivação
2
Uma característica importante para obter sucesso no desenvolvimento de um componente é verificar adequadamente suas funcionalidades. Hoje, a prática mais utilizada
para essa verificação ainda é o teste de software. Porém, garantir a qualidade do software
apenas com inspeções informais é muito difícil, principalmente quando lidamos com software complexos. Nesse contexto, surgem os métodos formais, os quais constituem outro
grande avanço em direção à construção de software de qualidade. Através de linguagens
formais, as quais possuem uma base matemática, os desenvolvedores especificam o sistema mantendo a precisão da especificação. Além disso, estas linguagens possuem uma
semântica formal, através da qual é possível realizar provas matemáticas para verificar
características como, por exemplo, a ausência de deadlocks.
CSP [HOARE, 1985] é um método formal que permite a modelagem e verificação
formal de sistemas concorrentes. Ele possui um conjunto de ferramentas e alguns trabalhos na literatura [HOARE, 1985] [ROSCOE et al., 1997] [SCHNEIDER, 1999]. A
linguagem CSP é baseada em processos que se comunicam através de eventos (ações) e
esses representam o fluxo do seu comportamento. A linguagem também possui modelos
semânticos, os quais permitem a verificação e comparação entre processos. As ferramentas que dão suporte a CSP auxiliam tanto no processo de modelagem e verificação, como
na visualização gráfica do comportamento do processo. São exemplos dessas ferramentas,
o FDR2 [BROWNLEE, 2000], proBE [PROBE. . . , 2003] e TypeChecker 1 . Além dessas,
CSP ainda dispõe do provador de teorema CSP-Prover [ISOBE; ROGGENBACH, ]. Este
é uma integração das teorias de CSP com o provador de teorema Isabelle [NIPKOW et
al., 2002].
Por todas essas características e suporte de ferramentas, CSP tem ganho muita importância nos últimos tempos, com grandes aplicações na indústria [NOGUEIRA et al.,
2008] [FEITOSA et al., 2008] [PERES; MOTA, 2007] [NETO, 2007]. Atualmente, seus
benefícios vêm sendo explorados junto à área de testes para uma melhor geração e automação dos testes de software [BERTOLINI; MOTA, 2008] [CAVALCANTI; GAUDEL,
2007] [NOGUEIRA et al., 2008] [NETO, 2007].
1.2
Motivação
No processo de desenvolvimento baseado em componentes é importante uma análise
detalhada de como esses se integram no sistema, não bastando apenas verificar o compo1 http://www.fsel.com/software.html
1.2 Motivação
3
nente, mas também sua composição. A falta de uma verificação adequada da composição
entre esses componentes pode gerar problemas como deadlocks, os quais ocorrem quando
os componentes ficam impedidos de continuar suas execuções, pois cada um fica bloqueado
aguardando o outro. Nesse sentido, é preciso fazer com que os componentes se integrem
de forma que consigam se comunicar adequadamente. Uma abordagem ideal deveria prever esses comportamentos com antecedência. Linguagens formais, como CSP, podem ser
utilizadas com esta finalidade.
Em [RAMOS, 2011] é descrita uma estratégia que propõe a composição sistemática
e segura de componentes baseada na linguagem formal CSP. A estratégia define quatro
regras de composição: interleave, communication, feedback e reflexive, as quais asseguram
a ausência de deadlocks no componente resultante da composição.
A abordagem, a ser descrita mais detalhadamente no Capítulo 2, baseia-se em um
modelo de componentes que impõe as restrições necessárias para uma interação segura. As
regras oferecem um método sistematizado para preservar propriedades comportamentais
por construção. Com isso é possível identificar, através de propriedades conhecidas do
sistema, comportamentos da composição entre componentes antes de sua implementação.
1.2.1
Proposta
A abordagem proposta em [RAMOS, 2011] apresenta características de grande importância para verificação da composição entre componentes. Porém, o mercado de software
busca automação de processos [GROOVER, 2007]. Assim sendo, a aplicação de um método, como o proposto em [RAMOS, 2011], na prática, exige ferramentas que facilitem
sua utilização, auxiliando o usuário e trazendo agilidade ao desenvolvimento.
Este trabalho apresenta o desenvolvimento de uma ferramenta de apoio à estratégia proposta em [RAMOS, 2011], visando facilitar sua utilização. A ideia é abstrair a
complexidade imposta pelos modelos formais. Como benefícios, a ferramenta trará um
método de verificação prático, com agilidade no processo de desenvolvimento. Para isso,
a avaliamos através de um estudo de caso.
A seguir apresentaremos algumas abordagens e ferramentas voltadas para o desenvolvimento baseado em componentes.
1.2 Motivação
1.2.2
4
Trabalhos Relacionados
São muitos os trabalhos publicados que apresentam modelos de desenvolvimento baseado em componentes [LAU; NTALAMAGKAS, 2009] [LAU et al., 2011] [LAU et al.,
2005] [BARN; BROWN, 1998]. Existem alguns voltados para a verificação de componentes, como o proposto em [MARCILON, 2012] que apresenta a descrição de um processo de
derivação e verificação de propriedades de componentes nas nuvens. O processo envolve
a especificação formal do comportamento do componente por meio de contratos descritos
pela linguagem Circus [WOODCOCK; CAVALCANTI, 2002]. Esse contrato é refinado e
traduzido, chegando à implementação de um componente. Essa proposta tem em comum
com a abordagem descrita em [RAMOS, 2011] o fato que ambos conseguem verificar com
antecedência o comportamento do componente, devido à utilização de modelos formais.
Existem algumas ferramentas voltadas para verificação funcional automática de componentes. Em [BARBOSA, 2005] é descrita uma ferramenta que gera, executa e analisa
os resultados de testes funcionais para componentes de software a partir de especificações UML [RUMBAUGH et al., 2004] e restrições OCL [WARMER; KLEPPE, 2003].
Em nossa ferramenta, essa verificação é voltada para análise formal da composição entre
componentes.
Podemos encontrar em [EASSA; ABULNAJA, 2001] a proposta de um conjunto de
sub-ferramentas que auxiliam na construção de sistemas baseados em componentes, através da descrição de casos de uso em UML [RUMBAUGH et al., 2004]. Em nossa ferramenta, o desenvolvimento dos componentes é baseado na descrição de um modelo de
componente usando características da linguagem formal CSP.
O conjunto de ferramentas VEST [STANKOVIC et al., 2003] apoia o desenvolvimento
distribuído de sistemas embarcados baseados em componentes, cujo objetivo é fornecer
um conjunto de verificação de dependências baseado nos conceitos de aspecto [KICZALES
et al., 2001] para lidar com problemas não funcionais. Nossa ferramenta lida com a
verificação de segurança para problemas de deadlocks, baseado em características dos
componentes.
O projeto CESAR [JOLLIFFE, 2010] também investiu em ferramentas para o desenvolvimento de sistemas baseados em componentes. A ferramenta X-MAN [LAU; TRAN,
2012] é uma ferramenta industrial para o desenvolvimento de sistemas embarcados, automotivos e aviônicos. A ferramenta utiliza uma engenharia dirigida por modelo (MDE)
através da ferramenta GME (Ambiente de modelo genérico) [LEDECZI et al., 2001].
1.3 Objetivos
5
Em [MATSUMOTO; FUTATSUGI, 2000b] é proposta uma ferramenta que assegura
uma alta confiabilidade no desenvolvimento de sistemas baseados em componentes. O
desenvolvimento da ferramenta foi baseado na arquitetura em árvore [MATSUMOTO;
FUTATSUGI, 2000a]. Ela recebe como entrada uma especificação dos requisitos do software e uma especificação refinada de como combinar os componentes, que é descrita através da linguagem de especificação CafeOBJ [DIACONESCU; FUTATSUGI, 1998]. Além
disso, são especificados os componentes javaBeans (componentes descritos através da linguagem Java). Como saída a ferramenta apresenta um componente que é resultado dos
componentes descritos na entrada, juntamente com os conectores. A alta confiabilidade é
garantida através da verificação dos requisitos e geração da conexão entre os componentes de forma automática. Esse trabalho possui características bem parecidas com nossa
proposta, levando-se em consideração a confiabilidade imposta através da verificação realizada a partir de linguagens formais. Nessa ferramenta, a conexão entre os componentes é
gerada automaticamente, de acordo com a especificação descrita na entrada. Já na nossa
esse comportamento é invertido, onde a descrição da conexão é realizada manualmente,
bem como o comportamento do componente, e ela gera automaticamente o conjunto asserções de verificação.
1.3
Objetivos
1.3.1
Objetivos Gerais
O objetivo geral desse trabalho consiste no desenvolvimento de um ambiente automatizado para verificação da composição entre componentes. Esse ambiente auxiliará
o usuário na verificação e prevenção de comportamentos indesejáveis da interação entre
componentes.
1.3.2
Objetivos Específicos
São objetivos específicos deste trabalho:
• Investigar a existência de ferramentas para verificação de componentes. Fizemos
um levantamento na literatura em busca de ferramentas voltadas para verificação
do componente e de sua composição. A maioria dos trabalhos encontrados trata
da criação e verificação do componente em si, e poucas utilizam modelos formais.
Nossa ferramenta se preocupa não só com a descrição e criação do componente, mas,
1.4 Estrutura da Dissertação
6
principalmente, da verificação de sua composição a partir de várias propriedades
descritas através da especificação formal CSP.
• Estudar a verificação formal da abordagem proposta. Em [OLIVEIRA, 2012b] foi
realizada uma análise formal a partir da implementação das regras e restrições proposta em [RAMOS, 2011]. Nesse estudo, conseguimos identificar comportamentos
e extrair características necessárias para a implementação de nossa ferramenta.
• Desenvolver ferramenta de apoio. Nessa etapa foi desenvolvida a ferramenta que
automatiza a abordagem proposta em [RAMOS, 2011]. Para implementação utilizamos a linguagem java e a IDE NetBeans. Além disso, a fim de realizar as verificações necessárias nos scripts CSP gerados, foram integradas outras duas ferramentas
de suporte CSP: TypeChecker e FDR2.
• Validar a ferramenta. Através da aplicação da estratégia, utilizando a ferramenta
em um estudo de caso proposto em [OLIVEIRA, 2012b]. Nesta aplicação, analisamos se, de fato, a especificação descrita manualmente no estudo de caso é gerada
automaticamente e corretamente pela ferramenta, sendo possível extrair características como: interface amigável, melhor entendimento, facilidade de uso e redução
da complexidade.
1.4
Estrutura da Dissertação
No Capítulo 2 é feita uma breve introdução ao desenvolvimento baseado em componentes. Em seguida, apresentamos a linguagem CSP, onde descrevemos sua sintaxe, seus
modelos semânticos e apresentamos as ferramentas de suporte ao método. Finalizamos
com uma descrição da estratégia proposta em [RAMOS, 2011], onde apresentamos as
regras e restrições necessárias para se obter composições seguras. No Capítulo 3 apresentamos uma análise CSP da estratégia proposta em [RAMOS, 2011], através da ferramenta
FDR2, que constitui a base para construção de nossa ferramenta. No Capítulo 4, apresentamos toda estruturação de nossa ferramenta, como detalhes técnicos e funcionalidade.
O Capítulo 5 apresenta o estudo de caso que foi desenvolvido no decorrer desse trabalho
de dissertação. Concluímos esse trabalho com as considerações finais e trabalhos futuros.
7
2
Fundamentação Teórica
Neste capítulo serão apresentados os conceitos necessários para o entendimento desse
trabalho. Começaremos explicando os conceitos fundamentais de sistemas baseados em
componentes. Logo em seguida serão apresentados os conceitos da linguagem CSP, seus
modelos semânticos clássicos e as ferramentas de apoio à linguagem. Finalizaremos com
a descrição da abordagem utilizada como base nesse trabalho.
2.1
Sistemas Baseados em Componentes
O desenvolvimento de sistemas baseados em componentes surgiu da necessidade de se
melhorar o processo de desenvolvimento de software, tendo como motivação a busca pelo
aumento da produtividade, da qualidade e da redução nos custos do software, trazendo
com isso novas perspectivas para o desenvolvimento [VINCENZI et al., 2005].
Existe um grande interesse no desenvolvimento de software através da integração planejada de componentes existentes [BROWN; WALLNAU, 1998]. Com o desenvolvimento
baseado em componentes, sistemas que antes eram construídos em um único módulo,
agora são divididos em várias partes, onde cada uma representa uma funcionalidade do
sistema. Esse processo possui muitas vantagens, entre elas podemos citar a reutilização e
manutenção de componentes [VINCENZI et al., 2005].
A ideia de componente de software não é novidade. Em 1968 McIlroy já acreditava no futuro da indústria de componentes de software reutilizáveis [MCILROY, 1968].
Na literatura podemos encontrar várias definições de componentes. A definição que mais
assemelha-se a nosso trabalho foi proposta em [SZYPERSKI, 2002] e define o componente
de software como uma unidade de composição com interfaces contratualmente especificadas, podendo ser usado independentemente e combinado com outras partes.
No desenvolvimento de sistemas baseados em componentes, podemos considerar duas
características: o desenvolvimento de componentes e o desenvolvimento com componentes.
2.1 Sistemas Baseados em Componentes
8
O desenvolvimento de componentes representa a sua implementação. Já no desenvolvimento com componentes, considera-se a existência destes e os relacionam as atividades
necessárias para sua composição no sistema.
Segundo [BROWN; SHORT, 1997] existem as seguintes atividades essenciais para o
desenvolvimento com componentes: 1) encontrar componentes; 2) selecionar componentes; 3) realizar adaptações; 4) realizar a composição dos componentes; e 5) atualizar os
componentes. A Figura 2.1 ilustra essas atividades.
Figura 2.1: Atividades Para o Desenvolvimento com Componentes
Na primeira atividade são selecionados componentes com potencial para serem usados
no desenvolvimento do sistema. A atividade de seleção deve garantir que o componente
selecionado execute as funcionalidades necessárias para a aplicação. A adaptação consiste
em corrigir potenciais fontes de conflitos entre componentes selecionados para compor o
sistema. A atividade de composição utiliza uma infraestrutura comum para a realização
da composição dos componentes. Por último, a atividade de atualização consiste na substituição de versões antigas por novos componentes. Esta tem um impacto muito grande,
pois uma alteração no componente sem um planejamento pode acarretar comportamentos
inesperados em outros componentes do sistema.
Nierstrasz e Dami definem a composição de componentes como “o processo de construir aplicações por interconectar componentes de softwares através de seus plugins” [NIERSTRASZ; DAMI, 1995]. Esses plugins definem o meio de interação e comunicação entre
os componentes. Em [RAMOS, 2011], essa interação é descrita no modelo de componente,
o qual chamamos de contrato. Um contrato de componente descreve o comportamento
que o mesmo deve ter para se comportar de maneira confiável. Nesse sentido, métodos
formais tornam-se uma ferramenta importante, visto que eles permitem a formalização
e verificação de consistência dos componentes e seus contratos, trazendo assim um alto
grau de confiabilidade ao componente resultante.
A geração de contrato não está apenas associada à confiabilidade de um componente,
2.2 CSP
9
mas também à confiabilidade da composição desses componentes. O trabalho proposto
em [RAMOS, 2011], que está sendo abordado nesta proposta de dissertação, define regras
para compor componentes de forma segura, que são baseadas em um modelo de contrato.
Ainda nesse capítulo, você encontrará mais informações sobre essa abordagem.
2.2
CSP
CSP [HOARE, 1978] é uma linguagem formal que descreve o sistema em termos
de processos. Esses processos operam independentemente e interagem com os outros
através de ações (eventos). CSP é suportada por uma teoria matemática, um conjunto
de ferramentas de prova e alguns trabalhos literários [HOARE, 1985] [ROSCOE et al.,
1997] [SCHNEIDER, 1999]. Sua abordagem tem sido utilizada para especificação, análise
e verificação de sistemas concorrentes e em tempo real [SCHNEIDER, 1999]. Nessa seção
apresentaremos algumas características e operadores da linguagem, mais especificamente,
aqueles que fazem parte do contexto desse trabalho.
A Figura 2.2 apresenta um exemplo de especificação utilizando uma notação funcional
chamada CSPM , o qual tomaremos como base para apresentar os conceitos da linguagem.
A especificação descreve o comportamento de entrada em um parque de diversões. O
parque possui os clientes e um CAIXA que recebe o dinheiro, entrega o bilhete e o troco.
Outros comportamentos serão apresentados de acordo com a descrição da linguagem.
Iniciamos os conceitos de CSP apresentando a descrição de tipos e canais. Na linguagem, tipos compreendem conjuntos de valores e operações que podem ser realizados
sobre estes valores. Estes podem ser descritos, por exemplo, como inteiros, intervalos,
booleanos, entre outros. Na especificação da Figura 2.2 temos o exemplo da declaração de um tipo intervalo que representa o valor a ser pago na entrada do parque. CSP
também permite a criação de novos tipos através da palavra reservada datatype, onde
seus conjuntos de valores não fazem parte dos valores já definidos pela linguagem e são
representados como tipos enumerados ou uma união disjunta. Em nossa especificação
definimos o tipo ALFA que é representado por três valores (datatype ALFA = a | b
| c), onde a, b e c representam os clientes do sistema.
Os canais representam as portas de comunicação ou simplesmente a ação do processo, a qual damos o nome de evento. Na Figura 2.2 podemos encontrar a declaração
de alguns canais como dinheiro, bilhete e troco que definem ações no comportamento do processo CAIXA. Estes comunicam valores do tipo ALFA, tal que dinheiro.a
2.2 CSP
10
Valor ={0..10}
datatype ALFA = a | b | c
channel entrar, brincar, sair
channel dinheiro, bilhete, troco, moeda, passe : ALFA
channel entrada : Valor
PARQUE = entrar -> brincar -> sair -> PARQUE
CAIXA = dinheiro?clt -> bilhete!clt -> troco!clt -> CAIXA
USUARIOS = USUARIO(a) ||| USUARIO(b) ||| USUARIO(c)
USUARIO(clt) =
((entrar -> moeda!clt ->
(passe.clt -> troco.clt -> SKIP
[] troco.clt -> passe.clt -> SKIP)); brincar -> sair -> USUARIO(clt))
|~|
((moeda!clt -> entrar ->
(passe.clt -> troco.clt -> SKIP
[] troco.clt -> passe.clt -> SKIP)); brincar -> sair -> USUARIO(clt))
ENTRADA_PARQUE =
entrada?x ->
if (x==8) then SISTEMA_PARQUE else ENTRADA_PARQUE
MAQUINA = CAIXA [[dinheiro <- moeda, bilhete <- passe]]
SISTEMA_PARQUE = (USUARIOS [|{|moeda,passe,troco|}|]
(PARQUE ||| MAQUINA)) \ {|moeda,passe,troco|}
Figura 2.2: Exemplo de especificação CSP
2.2 CSP
11
e bilhete.a são possíveis eventos do processo CAIXA, que representa a bilheteria do
parque.
Em CSP, também é possível definir comportamentos de entrada e saída. Um processo
pode receber ou fornecer um valor. Esses são descritos através dos operadores ? (para
receber) e ! (para fornecer). Na Figura 2.2 o comportamento do processo CAIXA é
descrito inicialmente pelo recebimento do dinheiro de um cliente (dinheiro?clt), em
seguida tem duas ações de saída (bilhete!clt e troco!clt).
Além dos conceitos apresentados anteriormente, CSPM contém alguns operadores.
Esses operadores são considerados padrão para as ferramentas existentes e são utilizados para relacionar eventos e processos. A tabela 2.1 apresenta alguns operadores da
linguagem. Os outros operadores serão omitidos por não fazerem parte do escopo desse
trabalho.
STOP
SKIP
a -> Processo
Processo [] Processo
Processo |˜| Processo
Processo ; Processo
if <condiçao> then Processo else Processo
<condicão> & Processo
Processo ||| Processo
Processo [| cs |] Processo
Processo \ <cs>
Processo [[ <lista_renomeação> ]]
deadlock
finalização com sucesso
prefixação
Escolha externa
Escolha interna
composição sequencial de processos
escolha condicional
processo guardado
Intercalação de processos
Paralelismo
ocultação de canais
renomeação
Tabela 2.1: Operadores da linguagem CSP
Iniciamos apresentando os processos primitivos da linguagem, que têm comportamento básico, como o STOP que não faz nada (representa o deadlock) e o SKIP que
representa terminação com sucesso.
O prefixo é o operador mais básico da linguagem: a -> P denota um processo que
inicialmente comunica o evento a e comporta-se como P após esta comunicação. Podemos ilustrar esse operador através do processo CAIXA (Fig. 2.2), ele inicialmente oferece o evento dinheiro?clt. Após esta comunicação, o processo oferece os eventos
bilhete.clt e troco.clt, nesta ordem, voltando novamente a seu estado inicial
após ambas as comunicações ocorrerem.
Em CSP existem dois tipos de escolha, a escolha externa e a escolha interna, denotados
pelos operadores [] e |˜|, respectivamente. A escolha externa é determinada pela
ação do ambiente, ou seja, o ambiente tem controle sobre as escolhas das opções de
comportamento. Já a escolha interna tem o comportamento não determinístico, pois esse
é realizado pelo processo, sem a interferência do ambiente.
2.2 CSP
12
Na especificação (Fig. 2.2), o processo USUARIO decide internamente (|˜|) se inicialmente o cliente entra, ou só depois que efetuar pagamento. No mesmo processo existe
uma escolha externa ([]), onde o processo decide, de forma perceptível ao ambiente, se
emite primeiramente o bilhete seguido do troco ou vice-versa, .
O operador de composição (;) combina dois processos: P;Q de tal forma que executa
inicialmente o processo P e caso esse termine com sucesso, Q é executado. Podemos
observar esse comportamento no processo USUARIO, que independente das escolhas, ao
finalizá-lo, realiza sequencialmente as ações de brincar e sair levando ao estado inicial
de USUARIO.
As escolhas condicionais são recursos da linguagem CSP para determinar o fluxo
de um processo através de expressões lógicas.
Na especificação (Fig.
2.2) a esco-
lha condicional é usada no processo ENTRADA_PARQUE, onde a expressão if x ==
8 then SISTEMA_PARQUE else ENTRADA_PARQUE representa um comportamento
onde, caso a condição x == 8 seja atendida, o processo SISTEMA_PARQUE é executado,
senão ele volta a seu estado inicial (ENTRADA_PARQUE). Essa condicional se assemelha às
das linguagens de programação. Podemos também definir uma condicional através da condição de processo guardado, onde o comportamento (x == 8) & SISTEMA_PARQUE
define que PARQUE é executado se a condição x == 8 for verdadeira, caso contrário, o
processo é bloqueado.
Na composição em interleave, dois processos P ||| Q não se comunicam entre si,
apenas com o ambiente. O processo USUARIOS (Fig. 2.2) é descrito através de uma
composição entrelaçada (do Inglês, interleaving) entre de três clientes (USUARIO(a)
||| USUARIO(b) ||| USUARIO(c)), onde cada um é executado independentemente
do outro.
A expressão P [| cs |] Q denota um paralelismo, tal que P e Q são processos e
cs é o conjunto de canais de sincronização. Na especificação da Figura 2.2 encontramos
o uso desse operador no processo SISTEMA_PARQUE. O processo executa paralelamente
USUARIOS e CAIXA, que se comunicam através da sincronização nos seguintes eventos:
dinheiro, bilhete, troco. Além disso, essas ações são escondidas do ambiente
utilizando-se do operador de abstração (\). A ocultação de canais é descrita através
da expressão P \ cs, tal que o processo se comporta como P, exceto que os eventos
no conjunto cs não são visíveis pelo ambiente externo ao processo. Nesse contexto, os
eventos visíveis do processo SISTEMA_PARQUE são apenas entrar, brincar e sair.
O operador de renomeação P[[a <- b]] comporta-se como P , exceto que a
2.2 CSP
13
ocorrência do evento a será substituída pelo evento b.
quando se fala em reuso.
A renomeação é essencial
Através deste operador é possível obter novos processos
apenas renomeando seus eventos, ou seja, pode-se reaproveitar descrições específicas
do comportamento de um processo sem a necessidade de reescrevê-lo. Como exemplo, podemos citar o comportamento do processo CAIXA (Fig.
2.2) que é descrito
como o seguinte: dinheiro?clt -> bilhete!clt -> troco!clt -> CAIXA.
Caso desejemos reaproveitar esse processo para criar uma máquina com comportamento similar, mas que trabalhe em um conjunto de canais diferente, podemos utilizar o operador de renomeação. Por exemplo, MAQUINA é representado da seguinte
forma: MAQUINA = CAIXA [[dinheiro <- moeda, bilhete <- passe]], recebe o comportamento de CAIXA e a seta <- representa a renomeação do canal da esquerda pelo da direita.
Em FDR2 [BROWNLEE, 2000], podemos também fazer uso de uma linguagem funcional que possui diversas funções sobre conjuntos, sequências, tuplas, assim como casamento
de padrões. Por exemplo, union, inter e diff representam as operações de união,
interseção e diferença entre conjuntos, respectivamente. O member(x, cs) verifica se
o evento x faz parte do conjunto de eventos cs. Por exemplo, podemos verificar se o
evento brincar faz parte do conjunto de eventos J = {entrar, brincar, sair}
analisando se member(brincar, J). Finalizando, extension(c) retorna o conjunto
de valores que ”completam” o canal c. Por exemplo, na especificação da figura 2.2, são
extension(dinheiro) os valores a, b e c.
Como as interfaces são usualmente definidas em termos de canais, uma notação que
especial é fornecida para a escrita do conjunto de eventos de um canal. A notação {|c|}
expande o conjunto de eventos a partir de c, ou seja, todas as possíveis comunicações ao
longo do canal c. Por exemplo, o conjunto de eventos do canal entrada é descrito como
o seguinte: {|entrada|} = {entrada.1,entrada.2,...,entrada.10}.
A seguir apresentaremos a descrição dos modelos semânticos da linguagem CSP.
2.2.1
Semântica CSP
Na literatura podemos encontrar três modelos semânticos para CSP: operacional,
algébrica e denotacional [ROSCOE ET al., 1997]. Na semântica operacional, o processo é
um sistema em transição (ou máquina de estados), e a ocorrência de um evento representa
uma transição. Na semântica algébrica, as propriedades do processo podem ser deduzidas
por transformações sintáticas, seguindo um conjunto de leis algébricas. Na semântica
2.2 CSP
14
denotacional, CSP pode ser interpretada em três níveis: modelo de traces, falhas e falhadivergência [SCHNEIDER, 1999], que serão apresentadas a seguir.
Modelo de traces
Um aspecto importante do comportamento de um processo está relacionado a ocorrência dos eventos na ordem de seus acontecimentos. No modelo de traces, o processo
é descrito através (do conjunto) das possíveis sequências de eventos que ele pode vir a
executar. O conjunto de todos os possíveis traces de um processo P é chamado traces(P )
e define a semântica de um processo.
Um trace pode ser representado explicitamente pela ordem da listagem de
seus elementos.
Para entendermos melhor, vamos considerar o seguinte processo:
PARQUE = entrar -> brincar -> sair -> SKIP. Podemos extrair as seguintes sequências em cada execução de PARQUE:
entrar -> brincar -> sair -> SKIP
↓entrar
→ hentrari
brincar -> sair -> SKIP
↓brincar
→ hentrar, brincari
sair -> SKIP
↓sair
→ hentrar, brincar, sairi
SKIP
↓√
→ hentrar, brincar, sair, ticki
Como podemos observar, cada sequência de ações realizada por um processo representa
um trace, tal que, por exemplo, hai descreve uma sequência de execuções do processo e
ha, bi consiste nos eventos a seguido por b, que descreve outra sequência. Logo, levando-se
em consideração que a sequência vazia hi sempre fará parte do conjunto de traces do
processo, os traces de PARQUE são representados como:
traces(P ARQU E) = {hi, hentrari, hentrar, brincari, hentrar, brincar, sairi,
hentrar, brincar, sair, ticki}
O processo PARQUE poderia ter um conjunto infinito de traces caso ele fosse recursivo:
PARQUE = entrar -> brincar -> sair -> PARQUE. Neste caso, seu modelo de
traces será representado da seguinte forma:
traces(P ARQU E) = {hi, hentrari, hentrar, brincari, hentrar, brincar, sairi,
hentrar, brincar, sair, entrari...}
2.2 CSP
15
e seu comportamento sempre leva ao estado inicial. Porém, seu espaço de estados é
finito, ou seja, o processo executa infinitamente, mas cada ação do seu comportamento é
executada finitamente. como descrito na Figura 2.3.
Figura 2.3: Máquina de estado do processo PARQUE criada pelo FDR2
Por se tratar de uma sequência, diversas operações podem ser utilizadas para compor
traces. A operação de concatenação é uma delas, onde seq1 ˆseq2 representa a sequência
de elementos de seq1 seguida pelos de seq2 . Com esse operador é possível gerar um novo
trace através da junção de outros dois. Por exemplo, no processo PARQUE a concatenação
dos traces hentrar, brincar, sairi ˆ hentrari gera o trace hentrar, brincar, sair, entrari.
A operação #seq define o tamanho de uma sequência, retornando o número de
elementos contidos nela.
Por exemplo, #hentrar, bricar, sairi = 3.
A projeção de
uma sequência seq sobre os elementos de um conjunto A (seq A) representa uma
subsequência de todos os elementos de seq que estão nesse conjunto. Por exemplo,
hentrar, brincar, sair, entrari {entrar, brincar} = hentrar, brincar, entrari.
Se seq é uma sequência não vazia, ela pode ser descrita como hs0 i ˆs1 , onde
s0 é o primeiro elemento e s1 é o resto da sequência.
Nesse contexto, duas fun-
ções são definidas: head(seq) = s0 e tail(seq) = s1 . Por exemplo, para a sequência
hentrar, brincar, sair, entrari : s0 = entrar e s1 = hbrincar, sair, entrari.
A semântica de traces de um processo CSP é descrita através de um conjunto de regras,
as quais são descritas de acordo com os operadores da linguagem. A seguir apresentaremos
algumas delas:
√
traces(SKIP ) = {hi, h i}
traces(ST OP ) = {hi}
traces(a → P ) = {hi} ∪ {hai ˆtr | tr ∈ traces(P )}
traces(P []Q) = traces(P ) ∪ traces(Q)
traces(P |˜| Q) = traces(P ) ∪ traces(Q)
Como podemos observar nas regras descritas acima, os operadores de escolha externa
2.2 CSP
16
([]) e interna (|˜|) possuem os mesmos conjuntos de traces, pois este modelo preocupa-se
apenas com a sequência de eventos que um processo pode executar, definindo uma visão
apropriada para as propriedades de segurança. No entanto, não é possível definir com
esse modelo onde os processos podem falhar.
Para tal, a fim de diferenciar o não-determinismo, é preciso estender o modelo de
traces com informações adicionais, o que é feito pelo modelo que apresentamos a seguir.
Modelo de falhas
O modelo de falhas descreve o que um processo pode fazer e quais conjuntos de eventos
podem ser recusados ao executar cada ação do processo, ou seja, esse modelo determina
quais eventos podem não ser aceitos pelo processo após uma ação dele. Essa falha é
definida através do par (t, X), tal que t ∈ traces(P ) e X representa todos os valores que
podem ser recusados por P após a execução de t. Com isso, o conjunto de todas as
possíveis falhas de P é chamado de f ailures(P ).
Esse modelo permite verificar se um processo é determinístico ou não, o que não é
possível no modelo de traces. Um processo é dito determinístico se toda vez que seu comportamento é observado a partir de um estado conhecido/determinado (mesma entrada),
o resultado é sempre o mesmo.
Para
tes
exemplificar
processos:
o
modelo
de
falhas,
vamos
considerar
os
seguin-
PORTAO1 = entrar -> STOP [] sair -> STOP
PORTAO2 = entrar -> STOP |˜| sair -> STOP.
e
Os processos PORTAO1
e PORTAO2 possuem os mesmos traces, porém não são semanticamente equivalentes,
considerando os conjuntos de eventos que podem não ser aceitos em cada transição. A
Figura 2.4 ilustra o gráfico de transição desses processos no modelo de falha.
Figura 2.4: Processos PORTAO1 e PORTAO2 com seus estados e suas recusas
2.2 CSP
17
Observando a transição de estados do PORTAO1, ao executar qualquer ação, o processo
recusa ambos eventos. Por exemplo, para as duas opções que o processo tem, caso ele
execute entrar, depois dessa ação, ele pode recusar entrar e sair. Já o processo
PORTAO2, devido a escolha interna (representada pelo símbolo τ ), possui uma transição
que permite executar um evento e recusar o outro.
Essa abordagem é válida para processos que não divergem. Quando há necessidade
de analisar esse comportamento, devemos usar o modelo a seguir.
Modelo de falha-divergência
O modelo de falhas/divergência tem sido considerado padrão para CSP, pois inclui os
dois modelos anteriores. Este permite investigar o que um processo pode fazer, identificar
suas falhas e verificar suas divergências.
A divergência é um dos piores comportamentos que um componente do sistema pode
apresentar, levando-se em consideração que outros componentes podem ficar na espera,
consumindo recursos computacionais, enquanto aguardam que em algum momento o processo consiga sincronizar [SCHNEIDER, 1999].
Quando um processo realiza uma sequência infinita de eventos internos, não há garantias de que em algum momento o processo vai alcançar um estado estável. A Figura 2.5
ilustra o exemplo de dois processos divergentes.
Figura 2.5: Processos divergentes
Os
processos
e
PORTAO3 = entrar -> PORTAO3
PORTAO4 = entrar -> PORTAO4 [] sair -> STOP
através das maquinas de estado da seguinte forma:
que
são
representados
PORTAO3 \ {entrar}
e
PORTAO4 \ {entrar} , têm execuções divergentes, devido à ocultação de seus
canais.
Na modelagem de falha e divergência, o conjunto de falhas é estendido para
capturar a ideia de um processo poder falhar após ter divergido.
Sua se-
mântica é definida para cada processo pelo par (f ailures⊥ (P ), divergences(P )),
onde divergences(P ) é o conjunto de traces s em que P pode divergir, e
2.2 CSP
18
f ailures⊥ (P ) = f ailures(P ) ∪ {(s, X)|s ∈ divergences(P )}, ou seja, o conjunto de falhas
do processo após divergir. Esse comportamento é diferente das f ailures(P ), que representa o conjunto de falhas do processo após a execução de cada trace.
Refinamento
Refinar um sistema significa criar uma nova modelagem ou implementação (sair do
abstrato para algo mais próximo da implementação) de modo que o comportamento seja
compatível. Em CSP, todos os três modelos semânticos apresentados anteriormente (traces, falhas e falhas-divergência) possuem suas formas de refinamento que são possíveis
verificar através da ferramenta FDR2.
O modelo de traces está relacionado a sequência de eventos que um processo pode
executar. Nesse modelo, um processo P é refinado por um processo Q (P vT Q) se todas
as possíveis sequências de eventos de Q são possíveis em P (traces(Q) ⊆ traces(P )).
O modelo de falhas indica o que um processo pode recusar após a execução de um
trace. Nesse modelo, um processo P é refinado por um processo Q (P vF Q) se Q não
introduz nenhuma falha quando substitui P , além de não introduzir nova sequência de
comunicação traces(Q) ⊆ traces(P ) e f ailures(Q) ⊆ f ailures(P ).
O modelo de falhas e divergências é o único capaz de investigar se o processo refinado não introduz mais divergências.
Um processo P
é refinado
por Q se as possíveis sequências em que Q diverge e seu conjunto de falhas
ao divergir, são possíveis em P .
Seu refinamento é definido como seguinte:
P vF D Q ≡ f ailures(Q)⊥ ⊆ f ailures(P )⊥ ∧ divergences(Q) ⊆ divergences(P ).
Baseado nesses modelos semânticos, foram implementadas diversas ferramentas de
suporte a CSP. A seguir, apresentaremos algumas delas.
2.2.2
Ferramentas para CSP
CSP possui algumas ferramentas que auxiliam tanto no processo de modelagem e
verificação, como na visualização gráfica do comportamento dos processos. A seguir apresentamos quatro dessas ferramentas: FDR2, ProBE, ProB e TypeChecker.
FDR2
O FDR2 (Failures-Divergence Refinement) [BROWNLEE, 2000] é uma ferramenta que
se fundamenta na teoria de concorrência e realiza verificações para os modelos semânticos
de especificações CSP.
2.2 CSP
19
Sendo um produto da Formal System 1 , FDR2 foi a primeira ferramenta comercial
disponível para CSP e desempenhou um papel importante na sua evolução. O FDR2 está
disponível para as plataformas Linux, Solaris e Mac OS.
Usando FDR2, é possível verificar refinamento de processos nos três modelos descritos
anteriormente. Além disso, é possível a utilização do FDR2 sem interface gráfica. Em
nosso trabalho, usamos a interface ’batch’ para executar as asserções do script gerado pela
BTS. Essas asserções são usadas para afirmar propriedades e refinamento, e são definidas
da seguinte forma:
assert Abstract [X= Concrete
onde Abstract e Concrete são processos e o X indica o tipo de comparação: T para
traces, F para falhas e FD para falha e divergência.
FDR2 também permite a verificação de várias condições de exatidão, como ausência
de deadlocks e livelocks, podendo ser investigado os motivos quando essas não são satisfeitas, e nos auxilia na verificação de comportamentos determinísticos para os modelos
apresentados anteriormente. A seguir, temos alguns exemplos de asserções que podem ser
aplicadas na especificação da Figura 2.2.
assert PARQUE :[ deterministic [FD] ]
assert USUARIOS :[ deadlock free [F] ]
assert SISTEMA_PARQUE :[ divergence free ]
assert SISTEMA_PARQUE [T= PARQUE
Como podemos observar, além da verificação de refinamento, o FDR2 também permite
avaliar diretamente se um processo é determinístico, livre de deadlocks e divergência.
Na Figura 2.6 podemos visualizar a interface da ferramenta FDR2, bem como a ilustração da verificação dessas asserções na parte central. O resultado da verificação dessas
√
asserções, válido ou não, é representado por um
verde e um χ vermelho, respectivamente.
Uma característica importante do FDR2 são os contraexemplos. Quando uma asserção
é selecionada para depuração, é criada uma nova janela com informações de contraexemplos que foram encontrados (caso haja) enquanto a asserção era analisada. Sendo possível
explorar as razões das asserções falsas.
1 http://www.fsel.com/software.html
2.2 CSP
20
Figura 2.6: Interface da FDR2
ProBe
ProBe [PROBE. . . , 2003] é um animador para CSP que permite ao usuário explorar
o comportamento dos modelos interativamente. A ferramenta exibe uma lista com as
possíveis ações e estados que um processo pode executar. Assim o usuário pode verificar
um dado comportamento do processo e detectar problemas.
O ProBe é gratuito e possui versões para Windows, Linux, FreeBSD e Solaris. Além
de ser muito simples de usar, é notável sua importância para o ensino de CSP, pois é
possível visualizar o comportamento dos processos de forma interativa, como podemos
observar na Figura 2.7.
Figura 2.7: Interface da ProBe
2.2 CSP
21
ProB
Assim como o ProBe, o ProB 2 (Fig. 2.8) é outro animador e model-checker [CLARKE
JR. et al., 1999] que oferece suporte a CSP e outras linguagens formais, como método B
[SCHNEIDER, 2001], Z [SMITH, 2000], Event-B [ABRIAL et al., 2010] e TLA [LAMPORT, 1994]. Esta permite que as especificações sejam verificadas de forma automática
e animada, ajudando na detecção de erros.
ProB é gratuito e possui versões Windows, Linux e OS X.
Figura 2.8: Interface da ProB
Na Figura 2.8 podemos visualizar, na parte superior, uma especificação CSP e na parte
inferior central é possível explorarmos o comportamento dos processos CSP, clicando sobre
eles.
TypeChecker
TypeChecker é um verificador de tipos da linguagem CSP, ou seja, ele é utilizado basicamente para verificar se a especificação foi definida corretamente, no tempo de tradução
do programa (ou tempo de compilação). O TypeChecker não possui interface gráfica e é
executado através de linhas de comando.
Em nossa ferramenta utilizamos o verificador de tipo para analisar os comportamentos
2 http://www.stups.uni-duesseldorf.de/ProB/index.php5/Main_Page
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
22
externos, descritos pelo usuário.
2.3
Abordagem para o desenvolvimento sistemático e
confiável de sistemas baseados em componentes
O trabalho desenvolvido em [RAMOS, 2011] apresenta uma abordagem que propõe
a composição sistemática e segura de componentes. Essa descreve um modelo de componente que se concentra em seus conectores e impõe restrições necessárias para uma
interação segura. A estratégia define quatro regras de composição: interleave, communication, feedback e reflexive. Estas asseguram corretude na composição, como a ausência
de deadlocks.
Iniciaremos a apresentação da abordagem descrevendo o modelo de componente proposto.
Modelo de componente
Um contrato descreve o comportamento que um componente deve ter para obter confiança. Nesse sentido, [RAMOS, 2011] definiu um modelo de componente que representa
o componente, seus conectores e sua interação semântica, formando a base para uma
composição segura.
Na abordagem, componentes e conectores (Figura 2.9) são representados basicamente
por elementos caixa-preta, descritos através de um processo CSP, com seus canais e tipos
específicos, que definem um comportamento externo, seus pontos de interação (ou portas)
e suas interfaces, respectivamente.
Figura 2.9: Modelo de componente
Para ilustrar melhor, vamos considerar um exemplo clássico de um cliente-servidor
(Figura 2.10). O exemplo consiste em um sistema de caixa eletrônico, onde o Cliente e a
Maquina representam componentes no sistema.
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
23
Figura 2.10: Exemplo de um Cliente-Servidor
Utilizaremos esse exemplo durante toda essa seção para apresentar as regras e propriedades da abordagem. O comportamento dos componentes, bem como seus tipos e canais
são descritos a seguir.
NUM = {0..20}
datatype DADOSMQ = rt.NUM | requisitaSaldo | ackRt.Bool | recSaldo.NUM
datatype DADOSCL = insereCartao.NUM | digitaSenha.NUM | retiraDin.NUM |
saldo | retiraCartao | recebeDin | mostraSaldo
channel cl : DADOSCL
channel maq : DADOSMQ
Figura 2.11: Descrição dos tipos e canais
Cliente = cl.insereCartao?num -> cl.digitaSenha?num ->
(SAQUE [] SALDO) ; cl!retiraCartao -> Cliente
SAQUE = cl.retiraDin? val -> maq.rt.val ->
maq.ackRt?a -> cl!recebeDin -> SKIP
SALDO = cl.saldo -> maq.requisitaSaldo ->
maq.recSaldo?x -> cl!mostraSaldo -> SKIP
Figura 2.12: Descrição do comportamento do componente Cliente
Maquina =
|~| a: Bool @ maq.rt?x -> maq.ackRt.a -> Maquina
[]
|~| y: NUM @ maq.requisitaSaldo -> maq!recSaldo.y -> Maquina
Figura 2.13: Descrição do comportamento do componente Máquina
A especificação inicialmente apresenta os tipos e canais (Fig. 2.11) utilizados pelos
componentes. Em seguida, temos o comportamento de Cliente (Fig. 2.12) e Maquina (Fig.
2.13). O processo Cliente recebe do usuário, através do canal cl, o número do cartão
seguido da senha. Logo após, é oferecida um escolha entre sacar dinheiro (SAQUE) e
verificar saldo (SALDO). Já o processo Maquina tem o comportamento de retirar dinheiro
ou verificar saldo, de acordo com a escolha do Cliente. Por exemplo, maq.retirar.val
representa um valor de saída que vai ser recebido em maq.retirar?x no processo Maquina.
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
24
Nesse processo existe uma escolha externa entre retirar dinheiro ou requisitar saldo. O
operador @ representa uma separação entre o valor que está sendo recebido internamente
e a execução dos eventos.
Como podemos observar, o comportamento do componente está relacionado à ação
de interação, não se preocupando com o que ocorre internamente. Ou seja, relaciona-se
com o que ele pode receber e fornecer para outro componente. Por isso as ações de seu
comportamento são externas.
Nesse contexto, a interface de comunicação do componente é representada por dois
conjuntos de eventos CSP: eventos de entrada e eventos de saída, onde os valores de saída
de um componente são recebidos na entrada de outro e essa comunicação pode representar
um par request-response. O comportamento da comunicação entre dois componentes
(representado pelo elemento central da Fig. 2.10 (CON )) será explicado mais a frente. A
seguir apresentaremos a definição do modelo de componente.
Definição 1 (Contrato de Componente). U m contrato de componente Ctr : hB, R, I, Ci
é descrito através de um comportamento B, um conjunto de canais (portas) de
comunicação C, um conjunto de interf aces I e uma f unção total entre canais e
interf aces R : C → I, tal que :
• B é um processo de entrada e saída (I/O P rocess).
• R representa a relação entre canais e o conjunto de tipos, cujo dom(R) = C ∧
img(R) = I.
• Os eventos de todos os seus canais C devem estar associados a dois conjuntos disjuntos: inputs e outputs.
O comportamento desses componentes é representado por um I/O P rocess (processo
de entrada e saída), que em CSP é definido por um processo P que deve satisfazer a cinco
condições:
1. Canais de entrada e saída: Os eventos de um canal fazem parte das ações de entrada
ou saída de um processo. Por exemplo, no sistema Cliente-servidor as entradas e
saídas para o Cliente são definidas através das funções inputs e outputs, como o
seguinte:
inputs(maq, Cliente) = { maq.ack, maq.recSaldo}
outputs(maq, Cliente) = { maq.retirar, maq.requisitaSaldo}
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
25
onde a interseção de inputs e outputs é vazia e a sua união é subconjunto de {|maq|}.
Como podemos observar, o processo Cliente satisfaz essa condição, pois não possui
eventos em comum nas entradas e saídas.
2. Traces infinitos: P deve ter traces infinitos (mas espaço de estado finito).
3. Ausência de divergência: P é livre de divergência.
4. Entrada Determinística: Se um conjunto de eventos de entrada em P é oferecido
pelo ambiente, nenhum desses pode ser recusado. Esse determinismo é muito importante, pois o processo nunca vai recusar um serviço oferecido por outro processo
(componente). Essa condição é baseada na semântica de falhas que garante que se
P executa um trace s ˆ hc.ai, tal que hc.ai é uma entrada, então esse evento não
pode ser recusado por P após s ((s, {c.a}) ∈
/ f ailures(P )).
Nos processos Cliente e servidor essa propriedade é satisfeita, visto que não há
escolha interna entre os eventos de entrada.
5. Saída Fortemente Decisiva: todas as escolhas entre eventos de saída de um determinado canal em P são internas. Essas decisões são baseadas nas ações de entrada do
processo, o que gera um não determinismo na saída. Na saída decisiva, dado o trace
s ˆ hc.bi tal que c.b pertence à saída de P , toda saída em c será recusada, exceto c.b
- representado por (s, outputs(c) \ hc.bi) ∈ f ailures(P ). Com isso, é notável que o
processo sempre irá oferecer uma saída nesse canal.
Os processos Cliente e servidor satisfazem essa condição, pois não há escolha nas
suas saídas e quando essa é oferecida, existe apenas uma opção de saída.
Os comportamentos de Entrada Determinística e Saída Decisiva são propriedades que
indicam como um componente deve aceitar e enviar informações.
Esse modelo de componente é chamado de BRIC (Behavior, relation, interfaces, channels) e descreve as condições que devem ser atendidas para se obter um componente confiável. No sistema Cliente-servidor (Fig. 2.10), o contrato de componente de Cliente e
Maquina são definidos como o seguinte:
CtrCliente = hCliente, {cl 7→ DADOSCL, maq 7→ DADOSM Q}, {DADOSCL, DADOSM Q}, {cl, maq}i
CtrM aquina = hM aquina, {maq 7→ DADOSM Q}, {DADOSM Q}, {maq}i
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
26
Como podemos observar, as definições descritas até agora observam o componente
e seu comportamento. Essas características são fundamentais para que as propriedades
e restrições dos componentes sejam garantidas e a aplicação das regras de composição
tenham o resultado desejado. Por isso, uma das principais características da abordagem proposta em [RAMOS, 2011] é resolver os problemas esperados antes de integrar os
componentes.
Descreveremos a seguir algumas propriedades importantes relacionadas a verificação
da interação segura desses componentes.
Protocolos de comunicação
Outra importante característica do componente é o protocolo de comunicação. Esse
está relacionado a interação de um componente com outro no sistema.
Os protocolos são importantes para realizar verificações mais específicas sobre a interação dos componentes, analisando a comunicação através de um canal. A seguir apresentaremos algumas características do protocolo de comunicação.
• Implementação:
cesso
P
é
A implementação do protocolo de um canal ch do pro-
descrita
como
todo
comportamento
do
processo
sobre
ch
(P rotIM P (P, ch) ≡F P {ch}), tal que P rotIM P deve ter os mesmos traces e falhas do processo sobre a projeção desse canal. Ou seja, os traces e falhas desses
devem ser equivalentes (≡F ). A projeção é toda execução de ch em P .
No exemplo do Cliente-servidor o canal maq representa a interação de cada componente. Existe o protocolo a partir do ponto de vista do Cliente e da Maquina.
Estes são definidos a partir do comportamento do processo. Por exemplo, definido
o comportamento do componente Cliente, seu protocolo representa todo comportamento de Cliente no canal maq, como descrito na Figura 2.14. Da mesma forma,
para a Maquina, seu protocolo é representado por todo comportamento do processo
Maquina sobre o canal maq, como descrito na Figura 2.15.
Prot_Cliente =
|~|x: NUM @ maq!rt.x -> maq.ackRt?a -> Prot_Cliente
[]
maq.requisitaSaldo -> maq.recSaldo?x -> Prot_Cliente
Figura 2.14: Protocolo do Cliente para o canal maq
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
27
Prot_Maquina =
|~| a: Bool @ maq.rt?x -> maq.ackRt.a -> Prot_Maquina
[]
|~|x: NUM @ maq.requisitaSaldo -> maq!recSaldo.x -> Prot_Maquina
Figura 2.15: Protocolo da Maquina para o canal maq
Os protocolos de comunicação descritos na Figura 2.14 e 2.15 são referentes a todas as ações do canal de interação maq, tanto para o componente Cliente como a
Maquina.
Nesse exemplo, não é implementado protocolo de comunicação para o canal cl, pois
este não interage com nenhum componente. Se quisermos tratar o ambiente (usuário) que utiliza esse serviço como outro componente, consequentemente teremos uma
interação deste com o componente Cliente.
• Satisfação: Um protocolo é válido quando ele satisfaz o seguinte refinamento: (Q vF
P rotIM P (P, ch)), onde Q corresponde a um protocolo que deve ser refinado pela
comunicação do processo P sobre o canal ch.
No exemplo do Cliente-Servidor, para analisar se o protocolo Prot_Cliente satisfaz
o processo Cliente é necessário realizar uma verificação através do refinamento CSP,
ou seja, verificar se o conjunto de traces e falhas da projeção de Cliente sobre o
canal maq refina Prot_Cliente (P rot_Cliente vF P rotIM P (Cliente, maq)).
Esses protocolos de comunicação são descritos para analisar a relação entre a comunicação de dois processos, focando unicamente em seus protocolos. Verificar a compatibilidade desses é uma forma de garantir que a comunicação entre dois processos, que são
I/O processes, é livre de deadlocks. A propriedade de compatibilidade de protocolo é
descrita a seguir.
• Protocolos fortemente Compatíveis: a saída de um processo sempre será aceita
pelo outro processo. Esse tipo de verificação é de extrema importância para o sistema, pois consegue garantir que nenhuma informação gerada (saída) pelo componente será perdida.
Analisar a compatibilidade entre dois protocolos através de refinamento CSP pode ser
definido através do processo chamado dual protocol [VALLECILLO et al., 2006]. O dual
de um protocolo Q é definido como um processo do qual seu conjunto de entrada e saída
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
28
é igual as outputs(Q) e inputs(Q), respectivamente, e que tem os mesmos conjuntos de
traces, ou seja, os eventos de entrada de Q correspondem a saída de seu dual e os eventos
de saída de Q são correspondidos na entrada de seu dual.
Nesse contexto, podemos definir o dual protocol do Prot_Cliente como descrito na
Figura 2.16.
DUAL_Prot_Cliente =
|~|a: Bool @ maq.rt?x -> maq.ackRt.a -> DUAL_Prot_Cliente
[]
|~|x: NUM @ maq.requisitaSaldo -> maq.recSaldo!x -> DUAL_Prot_Cliente
Figura 2.16: Dual-protocol de Prot_Cliente
Para ser compatível, o DUAL_Prot_Cliente deve ser refinado pelo protocolo do outro
componente, em nosso exemplo, Prot_Maquina, cuja condição é satisfeita.
Instância de Componente
Uma das vantagens do desenvolvimento baseado em componente é a reutilização.
Normalmente um componente é definido uma vez e reusado múltiplas vezes, e em vários
contextos diferentes. Nessa abordagem, o contexto é representado por um conjunto de
canais (que representam os pontos de interação) e é usado para comunicar um único
componente no ambiente. Nesse sentido, a substituição dos canais de um componente
por outros, significa que ele supostamente poderá interagir com outro ambiente. Essa
substituição é representada pela renomeação de um conjunto de canais de um componente
em novos. Nesse contexto, a reutilização é feita através da atribuição de nova identificação
ao componente que se deseja usar.
Figura 2.17: Reutilização de componente
A Figura 2.17 apresenta esse processo, onde é definido um novo contrato de componente cujo nome e canais são derivados da renomeação do anterior. O comportamento
do novo componente é o mesmo, exceto que ele utiliza um novo conjunto de pontos de
interação. Esse novo componente é descrito como uma instância do anterior.
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
29
Maquina1 = Maquina [[ maq <- maq_1 ]]
Figura 2.18: Criando um novo componente Máquina
No exemplo do Cliente-servidor, uma instância do componente Maquina pode ser definido como na Figura 2.18, onde o componente Maquina1 possui o mesmo comportamento
de Maquina, porém, com seu nome e canal renomeado.
A instância de componente é útil quando desejamos construir sistemas cujos componentes possuem características idênticas.
Composição
Nessa abordagem, a comunicação entre componentes é assíncrona. Para representar
essa comunicação um buffer é apresentado como elemento intermediário da composição
(Figura 2.19). Ele copia as informações de um canal para outro, ignorando se o outro
componente está ativo para receber entradas.
Apresentaremos a seguir como a comunicação assíncrona dos processos que são I/O
P rocess é especificada via buffer.
A definição de buffer em CSP é descrita como um processo com dois canais, sendo
um de entrada e um de saída, onde eles possuem o mesmo tipo. O buffer copia as
informações do seu canal de entrada para o seu canal de saída de modo que nenhuma
produção é perdida.
Figura 2.19: Exemplo de comunicação via Buffer
Na abordagem de [RAMOS, 2011], o mapeamento entre entrada e saída é traduzida
como uma renomeação entre as interfaces. Considerando isso, foi definida uma função
BU F FIO que recebe um par de canais e renomeia a interface de entrada pela interface
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
30
a→b = {a.x 7→ b.x}, tal que
de saída. A renomeação usada no buffer é descrita como: RIO
a→b substitui saídas em a por
x é uma extensions(a) e a.x ∈ outputs. Desta forma, RIO
entradas correspondentes em b.
Para sistemas modelados por meio de máquinas de estado e especificados via buffer,
existem duas propriedades que permitem o mecanismo de verificação, sem explosão dos
estados:
• Entrada e Saída Confluente: Se um processo possui ações de canais distintos a
serem executadas, uma não impede a outra de ser executada, a menos que seja no
mesmo canal.
Os processos Cliente e Maquina são I/O Confluente. Eles são o exemplo mais simples, onde não há escolha entre os eventos de diferentes canais, nem entre entradas
e saídas.
• Propriedade de Saída Finita: Como o comportamento de I/O process é livre
de divergência, a ausência de divergência ao ocultar a saída de um canal garante
que o processo sempre comunica um número finito de saídas possíveis (ou seja, o
tipo do canal é finito).
Nesse sentido, um processo Cliente, por exemplo, tem propriedade de saída finita se
para todo canal c pertencente ao conjunto de canais de Cliente, o comportamento
Cliente \ outputs(c, Cliente) é livre de divergência.
Como descrito anteriormente, a compatibilidade de protocolo é usada para verificar
a comunicação entre dois processos. Porém, é interessante verificar a relação entre diferentes canais de um mesmo componente para preservar propriedades comportamentais na
composição. Apresentaremos a seguir duas propriedades que comparam a comunicação
entre eventos de um mesmo processo.
• Canais Desacoplados: A comunicação do canal de um processo não interfere na
comunicação do outro. Na Figura 2.20, a comunicação do canal c1 é independente
de c2. Nesse contexto, os canais são desacoplados porque não introduzem nenhum
ciclo de dependência.
• Compatibilidade de Auto-Injeção com Buffer: permite a injeção de informações entre dois canais de um processo (Figura 2.21), através de buffers internos à
composição, sem introduzir deadlocks.
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
31
Figura 2.20: Canais desacoplados
Figura 2.21: Compatibilidade de Auto-Injeção com Buffer
Regras de Composição
As regras de composição introduzidas em [RAMOS, 2011] são apresentadas em uma
ordem incremental de complexidades e focam em cenários específicos de composição. As
quatro regras são interleave, communication, feedback e reflexive. Estas podem prever a
ausência de deadlocks e cada composição criam um novo componente que inclui os dois
anteriores.
A Figura 2.22 apresenta a estrutura de três dessas regras de composição. As composições em interleave e communication são binárias (comunicam canais de dois componentes).
Já as composições em feedback e reflexive são unárias, pois estão relacionadas a comunicação de diferentes canais em um mesmo componente. As regras de composição unária
têm a mesma estrutura, sendo diferenciadas apenas pelas condições impostas.
Figura 2.22: Exemplo de três regras de composição
Na composição por interleave, dois componentes, mesmo após a composição, não se
comunicam entre si, apenas com o ambiente, sem haver interferência entre eles. Para
que esse tipo de composição ocorra é necessário que os componentes não compartilhem
nenhum canal de comunicação. A definição da composição por interleave é descrita a
seguir.
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
32
Definição 2 (Composição em Interleave). Se P e Q são dois contratos de componentes,
tal que P e Q não possuem canais em comum (CP ∩ CQ = ∅, onde CP e CQ representam
o conjunto de canais dos processos P e Q, respectivamente), então, a composição em
interleave de P e Q é dada por:
P [k|]Q = Phi hi Q
O operador de composição representa uma interação assíncrona entre duas sequências de canais, mediada pela ideia de buffers infinitos. Na definição acima, a composição
não envolve comunicação, o que indica não haver qualquer tipo de interferência de um
sobre o outro. Tomando como exemplo o sistema Cliente-servidor (Fig. 2.10), para adicionarmos dois componentes Cliente no sistema, não é necessário existir comunicação entre
eles, apenas deles com a Maquina. Para isso, podemos compor esses componentes da
seguinte forma:
Cliente1 = Cliente[[cl < −cl1, maq < −maq1 ]]
Cliente2 = Cliente[[cl < −cl2, maq < −maq2 ]]
Cliente1[k|]Cliente2 = Cliente1hi hi Cliente2
onde inicialmente dois componentes Cliente são instanciados e posteriormente são colocados em interleave.
A segunda regra de composição é o caso tradicional, ou seja, caminho mais comum
para comunicar canais de diferentes componentes, como no exemplo da comunicação entre
o Cliente e a Maquina.
Definição 3 (Composição por Comunicação). Se P e Q são dois contratos de componentes
e ic e oc dois canais de comunicação, tal que
• ic ∈ CP ∧ oc ∈ CQ ,
• CP ∩ CQ = ∅
ic→oc ]] e P rot
oc→ic ]]
e os protocolos de comunicação P rotIM P (P, ic) [[RIO
IM P (Q, oc) [[RIO
• Possuem Entrada e saída confluente
• Têm Forte compatibilidade
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
33
• E satisfazem a propriedade de saída finita
A composição em comunicação de P e Q através de ic e oc é definida como o seguinte:
P [ic ↔ oc]Q = Phici hoci Q
Os componentes têm diferentes pontos de interação e sua comunicação é assíncrona.
Além disso, outras restrições devem ser aplicadas para se ter um processo livre de deadlocks, como a implementação dos protocolos dos canais que fazem parte da comunicação (P rotIM P ). Estas restrições aplicam-se a uma versão renomeada destes protocolos:
oc→ic ]] que substitui a saída de oc pela entrada de ic em P . Além disso, esses proP [[RIO
tocolos devem atender três propriedades que garantem a confiabilidade da composição.
A Entrada e saída confluente e a Forte compatibilidade permitem o mecanismo de verificação do sistema, sem explosão dos estados e a propriedade de saída finita garante a
interoperabilidade de dois componentes.
Na construção do sistema, um conjunto de componentes compostos sempre representará um único componente. Compor as saídas e entradas desse conjunto de componentes
permite introduzir ciclos na estrutura. Devido à existência desses ciclos, novas condições devem ser levadas em consideração para preservar propriedades comportamentais na
composição. Este tema está relacionado com o estudo de abordagens mais gerais para
garantir sistemas livres de deadlocks. Esse identifica que deadlocks surgem em topologias
complexas (gráficos) pela presença de ciclos indesejáveis.
A terceira regra de composição foi baseada no conceito de canais desacoplados. De
acordo com essa noção, a comunicação em um canal não interfere na comunicação do
outro (essa é intercalada).
Definição 4 (Composição em Feedback).
Se P é um contrato de componente, e
ic→oc ]] e
ic e oc dois canais de comunicação, tal que os protocolos P rotIM P (P, ic) [[RIO
oc→ic ]]
P rotIM P (P, oc) [[RIO
• Possuem Entrada e saída confluente
• Têm Forte compatibilidade
• Satisfazem a propriedade de saída finita
• {ic, oc} ⊆ CP
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
34
• ic e oc são desacoplados em P
A composição em feedback de oc em ic é definida como o seguinte:
P [oc ,→ ic] = P |
hici
hoci
Esta regra impõe algumas condições que são similares a regra de composição de comunição, exceto que adicionalmente aplica a restrição que os canais sejam desacoplados.
A última regra de composição é mais geral que o feedback. Essa é para topologias
mais complexas, onde são verificados problemas de dependência sobre a comunicação dos
eventos no sistema.
Definição 5 (Composição Reflexiva). Se P é um contrato de componente, e ic e oc dois
canais de comunicação, tal que
• {ic, oc} ⊆ CP
• P {| ic, oc |} satisfaz uma propriedade de saída finita.
• E P {|ic, oc|} é Compatibilidade de Auto-Injeção com Buffer
Assim, a composição reflexiva de P de oc em ic é definida como o seguinte:
¯ ic] = P |
P [oc ,→
hici
hoci
Considerações Finais
A abordagem de [RAMOS, 2011] apresenta características de grande importância
para verificação da composição entre componentes. Porém, o mercado de software busca
automação dos processos. Assim sendo, a aplicação de métodos, como o proposto em
RAMOS, na prática exigem ferramentas que facilitem sua utilização, auxiliando o usuário
e trazendo agilidade ao desenvolvimento.
Esta dissertação tem como principal objetivo o desenvolvimento e validação de uma
ferramenta que consiga prever a composição entre componentes utilizando todo conjunto
de propriedades e regras proposto nesse capítulo.
2.3 Abordagem para o desenvolvimento sistemático de sistemas confiáveis
35
Buscamos o máximo de automação possível, reduzindo a complexidade através da
ocultação dessas regras e propriedades. Além disso, com a finalidade de facilitar a usabilidade do usuário, buscamos o mínimo de dificuldade através da descrição de uma interface
amigável.
Para entendermos como funcionará a ocultação de todas as regras e propriedades
apresentadas nesse capítulo, o próximo capítulo apresenta o trabalho proposto em [OLIVEIRA, 2012b] que descreve a implementação dessas, aplicadas a um estudo de caso,
através de asserções FDR2.
36
3
Uma verificação formal para
desenvolvimento de sistemas confiáveis
baseados em componentes
No capítulo anterior conhecemos o trabalho descrito em [RAMOS, 2011] que propõe
a composição sistemática e confiável de componentes. Nele, algumas condições foram
expressas em termos de refinamentos que podem ser verificados automaticamente em
FDR2. No entanto, muitas das condições foram descritas diretamente na semântica de
CSP. A prova destas condições requer uma interação com provadores de teoremas que
dificultariam a aplicação prática dessa estratégia. Nesse contexto, em [OLIVEIRA, 2012b]
foi proposto um conjunto de asserções FDR2 que correspondem às condições originais de
[RAMOS, 2011]. Utilizando-se deste trabalho, é possível verificar automaticamente em
FDR2 todas as condições que validam os contratos e a composição entre eles.
A seguir, descrevemos as asserções apresentadas em [OLIVEIRA, 2012b], as quais são
utilizadas por nossa ferramenta, BRIC-Tool-Suport (BTS), como base para a validação
dos contratos e suas composições.
3.1
Análise CSP
Inicialmente, como descrito no capítulo anterior, um componente deve ter o comportamento de um processo de entrada e saída (I/O process). Para isso, ele deve atender às
cinco condições descritas pelas asserções apresentadas na Tabela 3.1.
A primeira asserção diz respeito à condição de Canais de entrada e saída, onde para
um processo P, é verificado se a interseção (inter) dos seus eventos de entrada com
seus eventos de saída é vazia. Para essa verificação é necessária a definição de duas funções: inputs(P) e outputs(P), as quais retornam os eventos de entrada e de saída
de um componente, respectivamente. Na definição da asserção, foram criados os proces-
3.1 Análise CSP
Canais de entrada e saída
Traces infinitos
Ausência de divergência
Entrada determinística
Saída fortemente decisiva
37
assert not Test(inter(inputs(P),outputs(P)) == {})
[T= ERROR
assert not HideAll(P): [divergence free [FD]]
assert P: [divergence free [FD]]
assert LHS_InputDet_A(P) [F= RHS_InputDet(P)
assert LHS_OutputDec_A(P) [F= RHS_OutputDec_A(P)
assert LHS_OutputDec_B(P, c1) [F= RHS_OutputDec_B(P, c1)
assert LHS_OutputDec_B(P, c2) [F= RHS_OutputDec_B(P, c2)
Tabela 3.1: Teste de Caracterização da Composição em Interleave
sos ERROR e Test, com os respectivos comportamentos: ERROR = error -> SKIP e
Test(c) = not c & ERROR. Nessa verificação, inicialmente Test recebe uma condição booleana, cujo valor é representado pela verificação da interseção entre as inputs
e outputs ser vazia ou não. Caso o valor de c seja verdadeiro, o processo Test nega
a condição e bloqueia o processo, fazendo com que Test não refine ERROR. Porém, essa
condição é negada, o que torna a verificação verdadeira.
A verificação que determina se um processo é livre de divergência, está incluído no
próprio FDR2. Já para a verificação de traces infinitos, é usada uma função chamada
Hideall que recebe um processo e esconde todos os seus eventos de entrada (inputs)
e saída (outputs). Como a ocultação dos eventos de um processo que possui traces
infinitos gera divergência, a afirmação da asserção é dada através de sua negação.
As verificações de entrada determinística e saída decisiva exigem um nível de complexidade bem maior em sua implementação, como descrito a seguir.
A entrada determinística é teoricamente definida como o seguinte:
Entrada Determinística. Um processo P possui entrada determinística se
∀s ˆ hc.ai : traces(P ) | c.a : inputs(c, P ) • (s, {c.a}) 6∈ f ailures(P )
Informalmente, isso significa que se um conjunto de eventos de entrada em P é oferecido ao ambiente, nenhum desses deve ser recusado.
A ideia geral dessa propriedade é verificar se o comportamento dos eventos de entrada
de um processo é determinístico. Um processo é dito determinístico se, ao executarmos
duas cópias do mesmo processo, ele comporta-se exatamente da mesma forma.
A solução para essa definição foi verificar o refinamento de falha entre dois processos:
LHS_InputDet_A e RHS_InputDet, os quais descreveremos a seguir. Inicialmente,
são executadas duas cópias de um processo, que sincronizam em um evento chamado
clunk. Este evento sincroniza as cópias do processo depois de cada evento. Inicialmente,
3.1 Análise CSP
38
isto é obtido pela execução do processo em paralelo com o outro, que força a produção
do clunk depois de executar um evento.
Channel clunk
AllButClunk = diff(Events, clunk)
onde AllButClunk é o conjunto de todos os eventos de P com exceção do clunk.
Clunking(P) = P [| AllButClunk |] Clunker
Clunker = [] x:
AllButClunk @ x -> clunk -> Clunker
Podemos observar que Clunking comporta-se exatamente como P, exceto que ele comunica clunk entre cada sincronização de eventos. Depois são executadas cópias do
processo em paralelo, sincronizando unicamente no clunk.
(Clunking(P) [| clunk |] Clunking(P)) \
clunk
Nesse caso, ambas as cópias dos processos executam independentemente, mas suas sequências de traces nunca terão tamanho cuja diferença seja maior do que um, pois sempre que
um dos processos executar um evento, esse aguardará a execução do outro para que ambos
possam sincronizar no evento clunk.
Se P é um processo determinístico e uma cópia de P executa um evento, a outra não
pode recusar. Nesse contexto, ambas as cópias devem ter o mesmo trace.
RHS_InputDet(P) =
(Clunking(P) [|cluck|] Clunking(P)) \
clunck
[|AllButClunck|]
Repeat
Repeat = [] x:
AllButClunck @ x -> x -> Repeat
Dessa forma, o processo RHS_InputDet força as duas cópias do processo a executarem o mesmo trace, por sincronizá-los com o processo Repeat e esse faz com que o evento
sincronizado seja executado duas vezes. Com isso, nunca teremos o comportamento de
deadlocks, já que esse só ocorre se, depois da execução de traces da forma ha, a...d, di, onde
um processo P executa ha, ..., di, uma cópia de P aceita um evento e a outra recusa.
Voltando à Tabela 3.1, podemos comprovar o determinismo no modelo de falhas verificando se RHS_InputDet(P) refina o seguinte processo sobre F (falhas).
3.1 Análise CSP
39
Deterministic(S) =
STOP
|˜|
([] x:AllButClunk @ x ->
(if member(x,S) then
x -> Deterministic(S)
else
(STOP |˜| x -> Deterministic(S))))
LHS_InputDet(P) = Deterministic(inputs(P))
assert LHS_InputDet(P) [F= RHS_InputDet(P)
O processo LHS_inputDet(P) especifica um comportamento determinístico do conjunto de eventos de entradas de um dado processo (inputs(P)), tal que a função
Deterministic executa um evento pertencente a AllButClunk, caso esse faça parte
das entradas de P, é executada em seguida outra cópia desse evento. Como estamos focando apenas no comportamento determinístico para os eventos de entrada, em qualquer
outro momento o processo pode travar.
Finalizando o conjunto de asserções da Tabela 3.1, apresentaremos a seguir a verificação da saída fortemente decisiva, a qual é definida da seguinte maneira:
Saída fortemente decisiva. Um processo P possui saída fortemente decisiva se:
∀s ˆ hc.bi : traces(P ) | c.b : outputs(c, P )•
(s, outputs(c, P )) 6∈ f ailures(P ) ∧ (s, outputs(c, P ) \ {c.b}) ∈ f ailures(P )
Isso significa que todas as escolhas entre eventos de saídas de um determinado canal
em P são internas. Esse não determinismo na saída fornece ao processo a capacidade
de fazer decisões internas baseadas nas entradas. Em um processo com saída fortemente
decisiva, dado o trace s ˆ hc.bi tal que c.b pertence a saída de P , toda saída em c será
recusada, exceto c.b - representado por (s, outputs(c) \ {|c.b|}) ∈ f ailures(P ). Com isso,
é nota-se que o processo sempre irá oferecer uma saída nesse canal.
A solução para Saída fortemente decisiva é dividida em duas partes: a primeira condição verifica se depois da execução de uma trace s ˆ hc.xi, o processo não pode recusar
todos os eventos em {|c|}, o qual representa o conjuntos de todos os eventos através do
canal c). A primeira verificação é feita para todo o processo de uma só vez.
3.1 Análise CSP
40
Considerando o processo Clunker(P) descrito anteriormente, temos duas cópias que
sincronizam no evento clunk e em todos os outros, exceto os valores de outputs(P)
que representam o conjunto de eventos de saída de todos os canais de um processo.
(Clunking(P) [| diff(Events, outputs(P))|] Clunking(P)) \ { clunk }
O processo One2Many executa os eventos de um processo e que não fazem parte dos
canais de saída. Caso ocorra uma saída, é forçada outra comunicação neste canal.
One2Many(S) =
([] x:diff(Events,union(S,{ clunk })) @ x -> One2Many(S))
[] ([] c:S @ [] x:
{|c|} @ x -> One2Many’(S,c,x))
One2Many’(S,c,x) =
[] y:{|c|} @ y -> if x==y then One2Many(S) else STOP
Colocando esse processo em paralelo com os descritos acima, temos o primeiro lado da
asserção apresentado a seguir:
RHS_OutputDec_A(P) =
(Clunking(P) [|diff(Events, outputs(P))|] Clunking(P)) \ {clunck}
[| AllButClunk |]
One2Many(outputs(P))
O processo RHS_OutputDec_A(P) sincroniza as cópias do processo em todos os
eventos, menos os de saída. Quando ocorrer um evento de saída, uma cópia irá sincronizar
com o processo One2Many que em seguida aguardará o evento de saída da outra cópia.
O teste só continuará sendo executado se ambas as cópias realizarem o mesmo evento.
Assim, todas as vezes que o teste carregar, ambas as cópias de P devem realizar o mesmo
trace. O resultado dessa implementação é testada contra a especificação a seguir:
LHS_OutputDec_A(P) =
STOP
|˜|
([] x:diff(Events,union(outputs(P),{ clunck }))
@ x -> LHS_OutputDec_A(P))
[]
([] x:outputs(P) @ x -> (|˜| y:chan(x,P)
@ y -> LHS_OutputDec_A(P)))
onde
3.1 Análise CSP
41
chan(ev,P) =
inter(outputs(P),
{| c | c <- GET_CHANNELS(P), member(ev,{|c|})|})
e a função GET_CHANNELS(P) retorna todos os canais do processo.
No processo LHS_OutputDec_A os eventos de P são executados normalmente até
ocorrer um evento pertencente a saída, que força a execução de outro evento no mesmo
canal, através da função chan. Além disso, é considerado que o processo pode a qualquer
momento travar, por isso existe uma escolha interna entre executar o comportamento
descrito por LHS_OutputDec_A ou STOP.
Assim, o refinamento LHS_OutputDec_A [F= RHS_OutputDec_A(P) checa que
depois de todos os traces t do processo P, a ocorrência de uma saída em um canal é sempre
acompanhada por um evento desse mesmo canal na cópia. Ou seja, essa asserção verifica
que caso um determinado trace t possa ser seguido de uma saída no canal c, o processo
não pode recusar todas as saídas em c. No entanto, esse refinamento não verifica se um
processo é não determinístico para um dado canal de saída. Sendo assim, é necessária
uma verificação adicional para garantir uma escolha não determinística nesse canal.
A segunda condição para se ter uma saída fortemente decisiva é verificar que depois
do trace sˆhc.xi, o processo pode recusar todos os eventos em {|c|} \ {c.x}. Assim, o
processo é não-determinístico para as saídas no canal.
A asserção a seguir verifica individualmente cada canal no processo. Por exemplo,
supondo o alfabeto de P igual a hc1, c2i, são necessárias as seguintes asserções:
assert LHS_OutputDec_B(P,c1) [F= RHS_OutputDec_B(P,c1)
assert LHS_OutputDec_B(P,c2) [F= RHS_OutputDec_B(P,c2)
Nessa parte da verificação, o refinamento garante que em cada saída de P que for
bloqueada, o deadlock deve ocorrer sempre no mesmo trace.
Para mais detalhes sobre a verificação formal de entrada determinística e saída fortemente decisiva, consulte [OLIVEIRA, 2012a].
Além da análise descrita na Tabela 3.1, que caracteriza toda as condições que um
processo deve atender para poder ser composto de forma segura, foram definidos outros
conjuntos de asserções voltados para verificação dos processos, auxiliando nas regras de
composição.
3.1 Análise CSP
42
Análise de Composição
A composição em interleave é a mais simples de todas.
Nessa regra, para dois
processos P e Q, não deve existir eventos em comum entre eles.
Para isso, é utili-
zado o processo RUN que executa todos os eventos da interseção entre dois processos
(inter(events(P),events(Q)), tal que events é uma função que recebe um processo e realiza a união (union) entre os seus eventos de entrada e seus eventos de saída).
Para que essa condição seja válida, a interseção entre os processos deve ser vazia e consequentemente, seus traces serão vazios. Nesse contexto, seu resultado deve refinar os traces
de STOP. Como apresentado na asserção a seguir.
assert STOP [T= RUN(inter(events(P),events(Q)))
As próximas regras apresentam um nível de complexidade maior, já que lidam com a
interação entre os componentes. Como discutido no capítulo anterior, a composição em
comunicação requer que o protocolo tenha entrada e saída confluente, forte compatibilidade e satisfaça a propriedade de saída finita. A seguir apresentaremos o conjunto de
asserções que verificam essas propriedades.
As asserções descritas em [OLIVEIRA, 2012b] foram definidas para um estudo de caso
manual, que gerou e analisou toda especificação em um único módulo, ou seja, de forma
genérica, onde, através da função “apply”, do FDR2, pega vários processos de uma só vez
e aplica a verificação. Logo, algumas alterações foram necessárias, como a retirada dessa
função. Em nossa ferramenta a descrição e análise são realizadas de forma específica,
ou seja, para processos (contratos) individuais descritos na BTS, que será apresentado
no próximo Capítulo. Tomando como base esse exemplo, acrescentamos que todas as
modificações realizadas foram em nível de código CSP, tentando buscar a adaptação do
conjunto de verificações a o que propõe a ferramenta. Essas verificações representam a
implementação das propriedades descritas na Seção 2.3 que validam a composição entre
componentes.
Inicialmente, apresentaremos o grupo de asserções que validam a propriedade de entradas e saídas confluentes.
Todas as verificações descritas a partir desse ponto são voltadas para os protocolos
dos processos.
Iniciamos verificando se o protocolo do processo P é livre de divergência, tal que
PROT_IMP_P representa a implementação do processo sobre um canal.
3.1 Análise CSP
43
assert PROT_IMP_P :[divergence free [FD]]
Logo em seguida é verificado se este de fato é um protocolo válido para um determinado canal no processo P, ou seja, ele deve ser refinado pela projeção de P sobre um
canal. Além disso, também é verificado se o protocolo é um refinamento dessa projeção.
Essas duas verificações garantem que protocolo do processo P possui os mesmos traces de
sua projeção sobre um canal.
assert PROT_IMP_P [F= PROT_IMP_def(P, ip)
assert PROT_IMP_def(P,ip) [FD= PROT_IMP_P
Foram definidas asserções para verificar se todos os eventos da entrada ou da saída
de um protocolo fazem parte das produções de um único canal. Para isso é realizada
uma interseção entre os eventos de entrada do processo P com os eventos do canal ip
(inputs_PROT_IMP(P, ip)). Logo em seguida, através da função subseteq é verificado se todos esses eventos de entrada do processo fazem parte das produções de um
único canal. O resultado é analisado através dos dois processos utilizados na verificação
de Canais de entrada e saída (Tabela 3.1) Test e ERROR.
assert not Test(subseteq(inputs_PROT_IMP(P, ip), {|ip|})) [T= ERROR
assert not Test(subseteq(outputs_PROT_IMP(P, ip), {|ip|})) [T= ERROR
A última asserção que valida a condição de entrada e saída confluente verifica se uma
renomeação do protocolo é determinística.
assert InBufferProt(PROT_IMP_R(P,ip ,R_IO(P,ip,oq))):[deterministic[F]]
tal que R_IO(P,ip,oq) é uma função que recebe dois canais (ip, oq) e renomeia
os dados de oq pelos dados de saída de ip. Por exemplo, para o evento ip.x ∈
outputs(ip) será descrito o evento oq.x. Isso significa que o canal de entrada de
um processo será renomeado com os dados do canal de saída do outro. Em seguida, a
função PROT_IMP_R substitui o canal renomeado oq no protocolo de ip, verificando se
esse tem o comportamento determinístico. Como os canais são representados pela saída
de P e a entrada do outro processo, respectivamente, o resultado dessa substituição sendo
determinístico, implica dizer que no envio de informação para o outro processo não terá
problema no recebimento.
O segundo conjunto de verificações para a composição em comunicação analisa a compatibilidade de protocolo. Essa análise é referente a versão renomeada desses protocolos
(PROT_IMP_R), descrita na verificação de entrada e saída confluente.
3.1 Análise CSP
44
A primeira asserção verifica se o protocolo PROT_IMP_R é livre de deadlocks. Em
seguida, é verificado se ele é um protocolo de comunicação, tanto na entrada como na
saída, igualmente as asserções anteriores, porém essas verificam o comportamento de
PROT_IMP_R. Essas verificações são realizadas para validar a descrição dos protocolos
renomeados.
assert PROT_IMP_R(P, ip,R_IO(P,ip,oq)):[deadlock free [FD]]
assert not Test(subseteq(inputs(PROT_IMP_R(P, ip,R_IO(P,ip,oq)), {|ip|}))
[T= ERROR
assert not Test(subseteq(outputs(PROT_IMP_R(P, ip,R_IO(P,ip,oq)),{|ip|}))
[T= ERROR
Nessa parte, analisamos também o comportamento do Dual-protocol. Inicialmente,
verificamos se o conjunto de eventos de entrada de um protocolo é igual ao conjunto de
eventos de saída do seu dual-protocol.
assert not Test(inputs(PROT_IMP_R(P, ip,R_IO(P,ip,oq))) ==
outputs(DUAL_PROT_IMP_R(P, ip,R_IO(P,ip,oq)))) [T= ERROR
Para uma análise mais segura, também é verificado se o protocolo e seu dual possuem
traces equivalentes.
assert DUAL_PROT_IMP_R(P, ip,R_IO(P,ip,oq)) [T= PROT_IMP_R(P,
ip,R_IO(P,ip,oq))
Finalizando, a analise de protocolo fortemente compatível pode ser definida por verificar se o dual-protocol de um protocolo P é refinado pelo protocolo Q.
assert DUAL_PROT_IMP_R(P, ip,R_IO(P,ip,oq)) [F= PROT_IMP_R(Q,
iq,R_IO(P,ip,oq))
Verificada todas as condições acima, a propriedade de saída finita é caracterizada
pela asserção que exclui todos os eventos de saída de um protocolo, mas não introduz
divergência, como apresentado a seguir:
assert PROT_IMP_R(P, ip,R_IO(P,ip,oq)) \ allOutputs:[divergence free [FD]]
A composição por feedback impõe as mesmas restrições da composição por comunicação descritas anteriormente, exceto que adicionalmente analisa a condição de que canais
sejam desacoplados, como descrito a seguir.
assert INTER_PROT_IMP(P, ip,oq) [F= PROJECTION(P, ip, oq)
3.1 Análise CSP
45
assert PROJECTION(P, ip,oq) [FD= INTER_PROT_IMP(P, ip, oq)
A análise para a propriedade de canais desacoplados recebe dois canais de um processo e realiza uma verificação através de um refinamento bidirecional entre a projeção de P sobre os canais e a intercalação da implementação dos protocolos, tal que
(INTER_PROT_IMP) gera um interleave entre os protocolos dos canais descritos na entrada e PROJECTION retorna a projeção de dois canais sobre o processo P.
Concluindo, a composição reflexiva possui, adicionalmente, a verificação de Compatibilidade de Auto-Injeção com Buffer, que estabelece comunicação entre canais de um
mesmo processo via buffer sem introduzir deadlocks, como descrito a seguir:
assert not PROJECTION(P,ip, oq) [| {| ip, oq |} |] BUFF_IO_1(P_LR1, P_LR2)
:[deadlock free [F]]
onde a PROJECTION do processo P sobre dois canais é colocado em paralelo com o
processo BUFF_IO_1 que recebe dois conjuntos de eventos de comunicação P_LR1 e
P_LR2, renomeia seus canais e os coloca em interleave. O interleave desses sincronizando
em paralelo com a projeção de um processo sobre esses dois canais deve ser livre de
deadlock.
Utilizando esse conjunto de verificações foi possível aplicar a abordagem para o desenvolvimento sistemático e confiável em uma verificação automática através da ferramenta
apresentada no próximo capítulo.
46
4
BTS: Ferramenta de suporte ao
desenvolvimento de sistemas confiáveis
baseados em componentes
Nessa seção apresentaremos todo processo de construção da ferramenta BTS (BRICTool-Support). Começaremos apresentando os detalhes técnicos, como linguagem de programação e ferramentas utilizadas. Em seguida, apresentaremos a estrutura da ferramenta, bem como sua funcionalidade. Por fim, será apresentada a aplicação prática da
estratégia pela BTS.
4.1
Detalhes Técnicos
A ferramenta foi implementada utilizando a linguagem de programação Java e a IDE
NetBeans. Além disso, a fim de realizar as verificações necessárias nos scripts CSP gerados, BTS foi integrada a duas ferramentas de suporte CSP:
• TypeChecker (verificador de tipos da linguagem CSP) é utilizado para verificar se
o comportamento CSP descrito pelo usuário está tipado corretamente.
• FDR2 (Failures-Divergence Refinement) é utilizado através do controle direto, sem interface gráfica. Para isso utilizamos a interface Batch [BROWNLEE,
2000] para executar o FDR2 através de linhas de comando. Este é responsável pela
verificação das asserções dos componentes do sistema, tanto na criação, como na
composição. Em caso de falha na verificação, as razões da falha são retornadas por
essa ferramenta.
4.2 Ferramenta BTS
4.2
47
Ferramenta BTS
A ferramenta foi desenvolvida para auxiliar o usuário na criação e verificação do
componente e de sua composição. O diagrama de caso de uso descrito na Figura 4.1
apresenta as ações que podem ser realizadas no sistema para completar o processo da
BTS.
Figura 4.1: Diagrama de Caso de Uso da BTS
A Figura mostra o usuário e sua interação com o sistema que é descrito através dos
seguintes casos de uso:
1. Usuário cria as interfaces do componente.
2. Usuário cria porta de comunicação do componente.
3. Usuário cria contrato de componente.
4. Define instancias a partir do contrato criado
5. Realiza a verificação da composição entre componentes.
4.2 Ferramenta BTS
48
Cada caso de uso, na ordem descrita, sempre inclui o anterior. Além disso, podemos
observar uma interação do sistema com a ferramenta FDR2.
Para entendermos a sequência de eventos que determina o comportamento desses casos
de uso, a Figura 4.2 apresenta o diagrama de sequência para o caso de uso criação de
interface de componente. Como podemos observar, através da interface de tipos (Fig. 4.3),
o usuário descreve tipos como intervalo, booleanos e datatype. Internamente a ferramenta
cria o tipo e adiciona em uma lista.
Figura 4.2: Diagrama de sequência da criação de tipos
A mesma sequência de passos ocorre na criação de canais. A figura 4.4 apresenta sua
interface de interação com o usuário. Nela, podemos observa a respectiva lista de tipos
criada anteriormente, que representarão os valores comunicados pelo canal.
Na figura 4.5 podemos visualizar a sequência de eventos que determina o comportamento do caso de uso criar contrato de componente. Inicialmente o usuário descreve as
informações do contrato, como nome, comportamento e canais. Ao descrever o comportamento (representado por um processo CSP), é criado o objeto CheckerBehavior que
executa o TypeChecker e verifica se o comportamento está tipado corretamente, pega a
√
saída e apresenta ao usuário através do
verde, para as saídas corretas (como visto na
Figura 4.6 - A) e um χ vermelho, caso contrário.
Logo após, através da interação do usuário (Fig. 4.6 - B), a ferramenta verifica se
4.2 Ferramenta BTS
49
Figura 4.3: Interface para criação de tipos
Figura 4.4: Interface para criação de canais
o contrato atende as condições e propriedades apresentadas em [RAMOS, 2011] (I/O
process), através do objeto VerificationContract, que cria um arquivo CSP a partir dos
valores de entrada e gera as asserções. Em seguida comunica-se com a ferramenta FDR2,
que faz a verificação e registra o resultado em um arquivo de log. A ferramenta ler esse
4.2 Ferramenta BTS
Figura 4.5: Diagrama de sequência da criação de contrato
50
4.2 Ferramenta BTS
51
Figura 4.6: Interface para criação de contrato
resultado e faz uma análise. O resultado é apresentado ao usuário através da interface
descrita na Figura 4.7. Depois o contrato é criado e salvo em uma lista.
Figura 4.7: Tela de resultado
A sequência de passos para a instanciação difere do contrato apenas na entrada de
dados, onde na instância o usuário apenas renomeia o nome do contrato e dos seus respectivos canais (Fig. 4.8). Internamente são seguidos os mesmos passos que o contrato.
Na verificação da composição, o usuário seleciona os componentes e a regra de verificação a partir da interface descrita na Figura 4.10. Como observado no diagrama de
sequência da Figura 4.9, a BTS cria o objeto CSPVerificationRule que gera o arquivo
4.2 Ferramenta BTS
52
Figura 4.8: Tela de instanciação
com a especificação CSP, de acordo com a regra selecionada. Assim como na criação do
contrato, comunica-se com a ferramenta FDR2, faz a verificação salvando o resultado em
um arquivo. A BTS analisa esse resultado e caso a composição seja válida, é gerado um
novo componente de acordo com as entradas e a regra de composição, e o resultado é
apresentado ao usuário.
Estrutura da ferramenta de suporte
A BTS foi construída para receber dados, gerar especificação CSP automaticamente,
verificar e apresentar ao usuário o resultado da verificação (Figura 4.11). As especificações
CSP são geradas de acordo com a descrição inicial do usuário, bem como as regras e
restrições descritas em [RAMOS, 2011].
Do ponto de vista do usuário podemos dizer que nossa ferramenta é dividida em duas
etapas: Contrato de componente e composição (Fig. 4.12). Na etapa de contrato de componente, a ferramenta de suporte recebe como entrada os tipos, canais e comportamento
CSP. A saída da ferramenta apresenta o resultado da verificação das condições impostas
na Seção 2.3 e gera o componente que será adicionado a uma lista.
Na etapa de composição, a ferramenta recebe um ou dois componentes que foram
definidos na etapa anterior. Sua saída é o resultado das verificações realizadas de acordo
4.2 Ferramenta BTS
53
Figura 4.9: Diagrama de sequência da composição
Figura 4.10: Tela de Composição
4.2 Ferramenta BTS
54
Figura 4.11: Estrutura da BTS
Figura 4.12: Entradas e saídas da BTS
com a regra de composição selecionada e as restrições impostas para essa regra. Além
disso, a composição gera um novo componente.
A ferramenta garante confiabilidade através da geração interna da especificação CSP
e verificação através do FDR2.
Funcionamento da ferramenta de suporte
A ferramenta BTS possui uma interface amigável e esconde todo conjunto de verificações apresentado no Capítulo 3, ou seja, o usuário não precisa ter conhecimento sobre
a ferramenta FDR2. No entanto, a necessidade de conhecimento em CSP ainda se faz
necessária. A remoção dessa necessidade será motivação para trabalhos futuros interessantes. A Figura 4.13 apresenta a interface inicial da BTS. Esta apresenta quatro opções:
1) Criação de tipos, 2) Criação de canais, 3) Criação de contrato, 4) Criação de instâncias. Inicialmente são definidos os tipos, que representam a interface do contrato. Os
canais representam as portas de comunicação do contrato, que são associados aos tipos
de dados. Após a definição de tipos e canais, podemos definir nosso modelo de contrato
e posteriormente, instanciar esse contrato para ser composto.
Na tela de descrição de tipos o usuário pode definir tipos CSP automaticamente. Por
4.2 Ferramenta BTS
55
Figura 4.13: Interface BTS
exemplo, a criação de um intervalo é realizada através da definição de um nome e seu
valor mínimo e máximo (Fig. 4.3). Já a tela de criação de canais apresenta uma lista
com todos os tipos criados na tela anterior. O usuário define um nome para o canal e o
relaciona a um tipo ou mais (Fig. 4.4).
A tela de contrato recebe um nome (que representará o nome do componente) e o comportamento CSP. Além disso, para cada canal, o usuário deve definir quais eventos fazem
parte do conjunto de entrada e quais fazem parte do conjunto de saída do componente.
Sendo também necessário definir para esses canais o protocolo e o seu dual-protocol.
Finalizando, após a definição de um contrato de componente, é possível instanciá-lo
gerando novos componentes.
4.2.1
Aplicação prática da estratégia pela BTS
Como vimos na seção anterior, internamente a BTS gera arquivos CSP que são verificados pelo FDR2. Nessa seção apresentaremos a estruturação desses arquivos e também
apresentaremos a utilidade do typeChecker.
Todo conjunto de entrada é especificado na BTS, menos o comportamento do com-
4.2 Ferramenta BTS
56
ponente que é um processo CSP descrito pelo usuário. Quando a ferramenta recebe esse
comportamento, antes de ser integrado ao resto da especificação para que as asserções
sejam analisadas, é necessário fazer uma verificação, visto que esse é suscetível a erros.
A Figura 4.14 mostra um exemplo de como o comportamento especificado pelo usuário
é gerado para verificação. Utilizaremos o exemplo do Cliente-servidor descrito no Capítulo
2. Apresentaremos a seguir o comportamento do componente cliente.
Inicialmente, a BTS declara os tipos, canais e adiciona o comportamento CSP. Em seguida, ela comunica com TypeChecker que faz a verificação de tipagem. O resultado é
√
apresentado ao usuário através da apresentação dos ícones e χ, como descrito na Figura
4.6 – A. A saída do TypeChecker pode ser visualizada através do click sobre esses ícones,
que é descrita na Figura 4.15.
Realizada a verificação pelo TypeChecker a ferramenta está preparada para gerar e
verificar as condições descritas na seção 2.3.
Verificando as condições de I/O Process
A verificação de I/O process é fundamental para que as propriedades e restrições
do componente sejam garantidas e posteriormente a aplicação das regras de composição
tenham o resultado desejado. O arquivo CSP gerado nessa fase é descrito através de um
modelo de contrato que possui as seguintes características:
• Nome
• Comportamento
• Tipos
• Canais
• Eventos de entrada e saída
A partir das características descritas acima, o corpo da especificação (gerada automaticamente) para verificação do comportamento de I/O process, do componente Cliente,
possui a seguinte estrutura:
4.2 Ferramenta BTS
57
NUM = {0..20}
datatype DADOSMQ = rt.NUM | requisitaSaldo | ackRt.Bool | recSaldo.NUM
datatype DADOSCL = insereCartao.NUM | digitaSenha.NUM | retiraDin.NUM |
saldo | retiraCartao | recebeDin | mostraSaldo
channel cl : DADOSCL
channel maq : DADOSMQ
Cliente = cl.insereCartao?num -> cl.digitaSenha?num ->
(SAQUE [] SALDO) ; cl!retiraCartao -> Cliente
SAQUE = cl.retiraDin? val -> maq!rt.val ->
maq.ackRt?a -> cl!recebeDin -> SKIP
SALDO = cl.saldo -> maq!requisitaSaldo ->
maq.recSaldo?x -> cl!mostraSaldo -> SKIP
Figura 4.14: Exemplo de comportamento do componente
Figura 4.15: Interface de saída do TypeChecker
• Declaração de tipos e canais. Estes são descritos através das interfaces das Figuras
4.3 e 4.4. Internamente a BTS transforma em código CSP, como apresentado a
seguir:
NUM = {0..20}
datatype DADOSMQ = rt.NUM | requisitaSaldo | ackRt.Bool | recSaldo.NUM
datatype DADOSCL = insereCartao.NUM | digitaSenha.NUM | retiraDin.NUM |
saldo | retiraCartao | recebeDin | mostraSaldo
channel cl : DADOSCL
channel maq : DADOSMQ
• Processo (Comportamento): descrito pelo usuário.
Cliente = cl.insereCartao?num -> cl.digitaSenha?num ->
(SAQUE [] SALDO) ; cl!retiraCartao -> Cliente
SAQUE = cl.retiraDin? val -> maq!rt.val ->
4.2 Ferramenta BTS
58
maq.ackRt?a -> cl!recebeDin -> SKIP
SALDO = cl.saldo -> maq!requisitaSaldo ->
maq.recSaldo?x -> cl!mostraSaldo -> SKIP
• GET_CHANNEL: conjunto de canais do processo. Essa função é necessária para a
verificação das saídas fortemente decisivas.
GET_CHANNELS(P) =
let f =
<
(Cliente, { cl, maq })
>
within apply(f,P )
• inputs: eventos de entrada do componente.
inputs(P) =
let f =
<
( Cliente, {| maq.ackRt, maq.recSaldo |})
>
within apply(f, P )
• outputs: eventos de saída do componente.
outputs(P) =
let f =
<
( Cliente, {| maq.rt, maq.requisitaSaldo |})
>
within apply(f,P)
• Condições de verificação, apresentadas na seção anterior. Estas representam as cinco
condições que garantes ao componente o comportamento de I/O process.
assert not Test(inter(inputs(Cliente),outputs(Cliente)) == {}) [T= ERROR
assert not HideAll(Cliente):[divergence free [FD]]
assert Cliente:[divergence free [FD]]
4.2 Ferramenta BTS
59
assert LHS_InputDet(Cliente) [F= RHS_InputDet(Cliente)
assert LHS_OutputDec_A(Cliente) [F= RHS_OutputDec_A(Cliente)
assert LHS_OutputDec_B(Cliente,maq) [F= RHS_OutputDec_B(BricRingCell,maq)
assert LHS_OutputDec_B(Cliente,cl) [F= RHS_OutputDec_B(Cliente,cl)
Além disso, o arquivo gerado inclui, na primeira linha, um arquivo ("auxiliar.csp").
Este tem as funções necessárias para realizar as verificações do comportamento de I/O
process descritos em [OLIVEIRA, 2012b], juntamente com as modificações realizadas,
apresentado no Capítulo 3.
Utilizamos o FDR2 para realizar a verificação dessas asserções. O resultado é salvo
em um arquivo que é analisado e repassado para o usuário de maneira bem simples, como
descrito na Figura 4.7. Nela, o resultado de todas as asserções é realizado com sucesso,
√
descrito através do verde. Caso alguma condição não seja atendida, seu resultado será
apresentado como um χ no lugar do ícone verde. Além disso, ainda é possível visualizar
a saída do FDR2 através da opção details.
Para realizar a verificação da composição, que tem os componentes selecionados através da interface descrita na Figura 4.10, antes é necessário instanciar os componentes
(Fig. 4.8) que se deseja compor. Para gerar uma instância de contrato, basta existir um
contrato implementado e verificado. Esse contrato tem o nome e canais renomeados e
internamente esse comportamento é representado pela função rename de CSP.
Verificando a composição
Como descrito no Capítulo 2 existem quatro regras de composição que podem ser
aplicadas entre os componentes e cada uma gera seu conjunto de verificação.
Nessa parte, a BTS recebe um ou dois componentes que são I/O process e define a
seguinte estrutura:
• Corpo do documento. Construído igualmente ao documento que verifica o comportamento de I/O process, exceto a função GET_CHANNEL, que não é necessária
nessa fase, e o conjunto de verificações que é diferente.
• Conjunto de asserções de acordo com regra selecionada. Para cada regra, existe um
conjunto de verificações diferentes, como descrito a seguir:
4.2 Ferramenta BTS
60
Inteleave:
Para duas instâncias do processo Cliente (Cliente1 e Cliente2 ) temos os seguintes
conjuntos de asserções:
--INTERLEAVING COMPOSITION
Cliente1_Cliente2_INTER = INTER(Cliente1,Cliente2)
assert STOP [T= RUN(inter(events(Cliente1),events(Cliente2)))
A composição em interleave é realizada através da função IN T ER(P 1, P 2), tal que
P1 e P2 são processos. A função IN T ER está implementada no arquivo rule.csp, o qual
o arquivo deve importar.
Nessa regra de composição, considerando-se que um componente só pode ser composto caso atenda a condição de I/O process, verificado anteriormente, a única restrição
imposta é que os componentes não devem possuir conjuntos de eventos em comum. A
verificação da composição também é realizada através da ferramenta FDR2 e seu resultado apresentado para o usuário. Além disso, a BTS gera um novo componente representado pelo processo Cliente1_Cliente2_INTER que tem como comportamento a
INTER(Cliente1,Cliente2).
Communication:
Para exemplificar a regra de composição em comunicação, apresentaremos a composição do Cliente1 com a Máquina e os respectivos canais maq1 e maq. Nessa regra o
conjunto de verificação é maior e mais complexo. Com isso, temos as seguintes asserções:
1. maq1 está no alfabeto de Cliente1
assert not Cliente1 \ {|maq1|} [T= Cliente1
2. maq está no alfabeto de Maquina
assert not Maquina \ {|maq|} [T= Maquina
3. Verificação de I/O confluente para o primeiro componente
Nessa fase, as verificações são voltadas para os protocolos dos processos. Inicialmente é verificado se o protocolo do processo Cliente1 sobre o canal maq1 é livre
de divergência. Em seguida verifica se este de fato é um protocolo válido, ou seja,
ele deve ser refinado pela projeção de Cliente1 sobre o canal maq1. Além disso,
também é verificado se o protocolo é um refinamento dessa projeção.
4.2 Ferramenta BTS
61
(a) Ele é livre de divergência
assert PROT_IMP_Cliente1_maq1 :[divergence free [FD]]
(b) Ele é refinado pela projeção sobre um canal
assert PROT_IMP_Cliente1_maq1 [F= PROT_IMP_def(Cliente1,maq1)
(c) Ele é refinamento da projeção sobre um canal
assert PROT_IMP_def(Cliente1,maq1) [FD= PROT_IMP_Cliente1_maq1
(d) Ele é um port-protocol (communication protocol)
Aqui é verificado se todos os eventos da entrada ou da saída de Cliente1
fazem parte das produções do canal maq1. Para isso é realizada uma interseção entre os eventos de entrada de Cliente1 com os eventos do canal
(inputs_PROT_IMP(Cliente1, maq1)). Logo em seguida, através da
função subseteq é verificado se todos esses eventos de entrada do processo
fazem parte das produções de um único canal.
i. Entrada
assert not Test(subseteq(inputs_PROT_IMP(Cliente1,maq1),
{|maq1|})) [T= ERROR
ii. Saída
assert not Test(subseteq(outputs_PROT_IMP(Cliente1,maq1),
{|maq1|})) [T= ERROR
(e) A versão renomeada é I/O Confluente
Essa definição é baseada em [RAMOS, 2011] a qual afirma que um processo
é I/O confluente se possuir ações de canais distintos a serem executadas, e
uma não impedir a outra de ser executada, a menos que seja no mesmo canal.
Esse comportamento foi implementado no trabalho de [OLIVEIRA, 2012b],
através da asserção InBufferProt que verifica se o comportamento de P [[R]]
(PROT_IMP_Cliente1_maq1_R_IO_maq), que renomeia os dados de todos
os canais do componente, mas preserva sua direção, é determinístico.
assert InBufferProt(PROT_IMP_Cliente1_maq1_R_IO_maq, maq1)
:[deterministic [F]]
4. Verificação de I/O confluente para o segundo componente
O conjunto de verificações para o segundo componente são semelhante as do item
3.
4.2 Ferramenta BTS
62
5. Protocolos são fortemente compatíveis
Conjunto de verificações a seguir analisa a compatibilidade de protocolo. Essa
análise é referente a versão renomeada, descrita na verificação de entrada e saída
confluente, apresentada anteriormente. As asserções a seguir verificam se o protocolo
renomeado dos dois componentes é livre de deadlocks.
(a) Protocolos são deadlock-free
i. Na esquerda
assert PROT_IMP_Cliente1_maq1_R_IO_maq
:[deadlock free [FD]]
ii. Na direita
assert PROT_IMP_Maquina_maq_R_IO_maq1
:[deadlock free [FD]]
(b) Protocolos são protocolos de comunicação
Aqui é verificado se os protocolos dos componentes são protocolos de comunicação, tanto na entrada como na saída, igualmente as asserções anteriores,
porém essas verificam o comportamento do protocolo renomeado.
i. Entrada esquerda
assert not Test(subseteq(inputs(
PROT_IMP_Cliente1_maq1_R_IO_maq),{| maq1|})) [T= ERROR
ii. Saída esquerda
assert not Test(subseteq(outputs(
PROT_IMP_Cliente1_maq1_R_IO_maq),{|maq|})) [T= ERROR
iii. Entrada direita
assert not Test(subseteq(inputs(
PROT_IMP_Maquina_maq_R_IO_maq1),{| maq|})) [T= ERROR
iv. Saída direita
assert not Test(subseteq(outputs(
PROT_IMP_Maquina_maq_R_IO_maq1),{| maq1|})) [T= ERROR
(c) Ele é um Dual Protocol
Nessa parte analisamos o comportamento do Dual-protocol para os dois componentes, verificando se o conjunto de eventos de entrada de um protocolo é
igual ao conjunto de eventos de saída do seu dual-protocol.
i. Entrada e saída
assert not Test(inputs(PROT_IMP_Cliente1_maq1_R_IO_maq) ==
outputs(DUAL_PROT_IMP_Cliente1_maq1_R_IO_maq)) [T= ERROR
4.2 Ferramenta BTS
63
ii. Entrada e saída
assert not Test(outputs(PROT_IMP_Maquina_maq_R_IO_maq1) ==
inputs(DUAL_PROT_IMP_Cliente1_maq1_R_IO_maq)) [T= ERROR
iii. O protocolo e seu dual possuem traces equivalentes
A. Esquerda
assert DUAL_PROT_IMP_Cliente1_maq1_R_IO_maq [T=
PROT_IMP_Cliente1_maq1_R_IO_maq
B. Direita
assert PROT_IMP_Cliente1_maq1_R_IO_maq [T=
DUAL_PROT_IMP_Cliente1_maq1_R_IO_maq
A analise de protocolo fortemente compatível é definida através da verificação
do dual-protocol de Cliente1 ser refinado pelo protocolo Maquina, devendo
também ser a verificação contrária verdade.
(d) assert DUAL_PROT_IMP_Cliente1_maq1_R_IO_maq [F=
PROT_IMP_Maquina_maq_R_IO_maq1
(e) Correspondência de compatibilidade
assert PROT_IMP_Maquina_maq_R_IO_maq1 [F=
DUAL_PROT_IMP_Cliente1_maq1_R_IO_maq
6. Protocolo têm propriedade de saída finita
A propriedade de saída finita é caracterizada pela asserção que exclui todos os
eventos de saída de um protocolo, mas não introduz divergência, como apresentado
a seguir:
(a) Esquerda
assert PROT_IMP_Cliente1_maq1_R_IO_maq \
outputs(PROT_IMP_Cliente1_maq1_R_IO_maq)
:[divergence free [FD]]
(b) Direita
assert PROT_IMP_Maquina_maq_R_IO_maq1 \
outputs(PROT_IMP_Maquina_maq_R_IO_maq1)
:[divergence free [FD]]
Todo conjunto de verificações aqui apresentados estão descritos no Capítulo 3.
A geração dos arquivos CSPs para as regras de feedback e reflexive possuem a mesma
estrutura, porém, nessas regras se trata apenas um componente e possuem algumas propriedades de verificação diferentes.
4.2 Ferramenta BTS
4.2.2
64
Considerações Finais
Nesse capítulo apresentamos a BTS, ferramenta desenvolvida durante esse trabalho de
dissertação. Foi feita uma apresentação de sua implementação, estrutura e funcionalidade,
além de mostrarmos alguns exemplos práticos de sua utilização e alguns arquivos gerados
por ela.
A ferramenta faz uma verificação prévia da composição de componentes. Esta implementa a abordagem proposta em [RAMOS, 2011] que apresenta todas as verificações e
validações através do método formal CSP.
A ideia desse trabalho foi buscar o máximo de automação para garantir agilidade na
abordagem de verificação segura. Porém, ainda não foi possível ter um processo totalmente automatizado, devido à descrição do comportamento do componente ser implementado manualmente. Mesmo assim, a BTS já traz grandes benefícios à utilização da
abordagem de Ramos [RAMOS, 2011]. A ferramenta desenvolvida é capaz de, a partir da
definição de um modelo de componente, verificar se sua comunicação com outros componentes é segura. Essa verificação é feita através das características definidas pelo usuário,
como comportamento, portas e interface. Internamente a ferramenta gera arquivos CSP
e realiza a verificação automaticamente. O resultado dessa verificação é transmitido diretamente para o usuário.
Foi realizado um estudo de caso para validar nossa ferramenta, onde foram gerados,
testados e compostos os componentes a partir do exemplo clássico de buffer circular. Este
será apresentado no próximo capítulo.
65
5
Estudo de Caso
Nesse capítulo faremos uma validação inicial de nossa ferramenta para o desenvolvimento sistemático e confiável de componentes. O estudo de caso consiste no exemplo do
buffer circular.
O principal objetivo desse estudo de caso é validar nossa ferramenta utilizando o exemplo descrito em [OLIVEIRA, 2012b] que avaliou o desenvolvimento sistemático e confiável manualmente. Nossa proposta é demonstrar a geração e composição de componentes
sendo gerada automaticamente pela BTS. Além disso, esta reduz toda complexidade da
abordagem descrita na Seção 2.3.
5.1
Buffer Circular
Um buffer consiste em uma região de memória temporária, cujo objetivo é a escrita
e leitura de dados no mesmo. Um buffer circular consiste na utilização de buffers de
tamanho fixo, como se estivessem ligados ponta a ponta.
Em nosso exemplo, o buffer circular (Fig. 5.1) é composto por um anel de células
com um controlador central e uma cache. Cada célula tem sua identificação e é capaz de
armazenar um valor. O controlador é responsável por receber entradas e fornecer saídas
solicitadas pelo ambiente e interagir de acordo com o armazenamento das células.
O anel é modelado como uma sequência de células, com suas extremidades unidas. Já
o controlador recebe os seguintes valores: uma cache, o tamanho do buffer e dois índices
para o anel circular: top e bottom. Caso os valores top e bottom coincidam, não é possível
distinguir se o anel atingiu sua capacidade máxima de armazenamento ou se está vazio.
Por isso, também precisamos do tamanho do buffer.
Na aplicação do estudo de caso definimos os seguintes componentes: Cell, que representa o comportamento de uma célula do buffer e um Controller. A Célula (Fig. 5.2)
tem como comportamento ler ou escrever informações. A descrição CSP de Cell, que
5.1 Buffer Circular
66
Figura 5.1: Buffer Circular
representa o B do contrato BRIC, é apresentada a seguir:
Figura 5.2: Representação do componente Cell
Cell =
let CellState(val) =
rd.req?dumb -> rd.ack!val -> CellState(val)
[]
wrt.req?x -> wrt.ack.x -> CellState(x)
within
CellState(0)
O Controller (Fig. 5.3) é responsável por controlar o recebimento de informações que
serão armazenas e lidas das células. Seu comportamento CSP é descrito a seguir:
Figura 5.3: Representação do componente Controller
5.1 Buffer Circular
67
As ações de entrada do Controller são executadas enquanto o buffer não atingir
seu tamanho máximo (maxbuff). Caso o tamanho do buffer seja vazio, a entrada é
armazenada em cache, o buffer é incrementado e top e bot não mudam. Ou seja, o cache
representa a primeira célula que armazena o dado recebido pelo Controller. Caso o buffer
não esteja vazio, o Controller envia o valor de entrada para o anel, juntamente com a
posição de armazenamento. Essa comunicação é realizada através do canal de escrita.
Os índices e o tamanho do anel são alterados. O maxring representa o limite do anel
(definido por maxbuff - 1), como apresentado nas ações a seguir:
InputController(cache,size,top,bot) =
size < maxbuff & input?x ->
(size == 0 & ControllerState(x,1,top,bot)
[]
size > 0 & write.top.req!x -> write.top.ack?dumb ->
ControllerState(cache,size+1,(top%maxring)+1,bot))
Nas ações de saída, que só serão executadas se o buffer não estiver vazio, o valor
registrado em cache será sempre o valor que está sendo comunicado. Caso o buffer tenha
um único elemento, este será comunicado e seu tamanho atualizado. Se existem elementos
armazenados em qualquer célula, o valor de x é lido, o cache é atualizado com esse valor
e bot é incrementado. Essas ações do comportamento do Controller são apresentadas a
seguir:
OutputController(cache,size,top,bot) =
size > 0 & output!cache ->
(size > 1 & read.bot.req?dumb -> read.bot.ack?x ->
ControllerState(x,size-1,top,(bot%maxring)+1)
(|~| dumb:Value @ read.bot.req.dumb -> read.bot.ack?x ->
ControllerState(x,size-1,top,(bot%maxring)+1))
[]
size == 1 & ControllerState(cache,0,top,bot))
O comportamento de Controller inicia com a cache e o tamanho do buffer vazios e top
e bot na primeira posição. Depois de inicializado, a opção de escolha entre InputController
e OutputController é oferecida.
5.2 Aplicação do Buffer Circular na BTS
68
ControllerState(0,0,1,1)
InputController(cache,size,top,bot)
[]
OutputController(cache,size,top,bot)
5.2
Aplicação do Buffer Circular na BTS
Para verificar o desenvolvimento sistemático de componentes através da BTS, primeiramente devemos considerar as seguintes características:
• Cada célula do buffer utiliza dois canais de comunicação: wrt para escrita e rd para
leitura.
• O Controller utiliza os seguintes canais de comunicação: write para escrita e read
para leitura, input e output para receber e fornecer dados do ambiente externo.
• Toda comunicação da célula com o controlador tem duas direções: (req) que representa uma requisição de leitura ou escrita e (ack) como uma confirmação.
Essas características representam os tipos e canais CSP que são descritos através das
interfaces nas figuras 4.3 e 4.4, respectivamente.
Na criação de tipos, o usuário define a interface que será utilizada na construção do
componente. Com isso, descrevemos o conjunto de interfaces (I) do contrato BRIC.
O conjunto de canais (C) e sua relação com a interface (R = C → I) é definido na
Figura 4.4, onde o usuário cria o canal e seleciona os tipos que farão parte da comunicação.
Por exemplo, nessa tela temos a criação de um canal wrt que tem como interfaces uma
direção (Direction) e um valor (Value).
5.2.1
Contrato BRIC de Cell e Controller
Como apresentado na Seção 2.3, um contrato de componente possui um comportamento (processo CSP), portas (canais) e interface (tipos). Os contratos que representam
a Cell e o Controller, descritos nas Figuras 5.2 e 5.3 são definidos a seguir e representam
os valores de entrada da ferramenta:
*



rd 7→ Direction x V alue, 
+
CtrCell = Cell, 
, {Direction x V alue}, {rd, wrt}
wrt 7→ Direction x V alue 
5.2 Aplicação do Buffer Circular na BTS


*
69

read 7→ CellId x Direction x V alue, 
CtrController = Controller, 
,
write 7→ CellId x Direction x V alue 
{ CellId x Direction x Value}, {read, write} i
Para demonstrarmos comos esses dados são descritos na BTS, apresentaremos a criação do contrato CtrCell . A Figura 4.6 nos apresenta a tela de contrato, onde definimos
o comportamento B e a lista de canais, que possuem os tipos associados representando a
relação de um para o outro.
Além do conjunto de entradas que representam o contrato BRIC, a descrição dos
eventos de entrada e saída, apresentadas a seguir, são definidos através da interface na
Figura 5.4, onde o usuário seleciona o canal e define manualmente o canal de comunicação,
seja para a entrada ou para a saída. No exemplo da Figura, está sendo definido o valor
de inputs para o canal rd.
Figura 5.4: Interface para descrição de eventos de entrada e saída
inputs(rd, CtrCell ) = {|rd.req|}
inputs(wrt, CtrCell ) = {|wrt.req|}
outputs(rd, CtrCell ) = {|rd.ack|}
outputs(wrt, CtrCell ) = {|wrt.ack|}
inputs(read.1, CtrController ) = {|read.1.ack|}
inputs(read.2, CtrController ) = {|read.2.ack|}
inputs(read.3, CtrController ) = {|read.3.ack|}
inputs(write.1, CtrController ) = {|write.1.ack|}
inputs(write.2, CtrController ) = {|write.2.ack|}
inputs(write.3, CtrController ) = {|write.3.ack|}
inputs(input, CtrController ) = {|input|}
outputs(read.1, CtrController ) = {|read.1.req|}
5.2 Aplicação do Buffer Circular na BTS
70
outputs(read.2, CtrController ) = {|read.2.req|}
outputs(read.3, CtrController ) = {|read.3.req|}
outputs(write.1, CtrController ) = {|write.1.req|}
outputs(write.2, CtrController ) = {|write.2.req|}
outputs(write.3, CtrController ) = {|write.3.req|}
outputs(output, CtrController ) = {|output|}
Descrito esse conjunto de definições de entrada, todas as outras características, como
verificação e composição são gerados automaticamente. Inicialmente, para cada contrato
descrito, a ferramenta verifica se esse possui o comportamento de I/O process. Para isso,
internamente é gerado o arquivo CSP que possui toda descrição do contrato juntamente
com o conjunto de verificações que é analisado pelo FDR2.
Concluindo que CtrCell e CtrController são contratos válidos, agora é possível compor
esses componentes. O contrato de cada célula descrita na Figura 5.1 é definido como uma
instância de CtrCell .
Cell1 = Inst(CtrCell , {rd 7→ rd_i.1, wrt 7→ wrt_i.1})
Cell2 = Inst(CtrCell , {rd 7→ rd_i.2, wrt 7→ wrt_i.2})
Cell3 = Inst(CtrCell , {rd 7→ rd_i.3, wrt 7→ wrt_i.3})
onde Cell1, Cell2 e Cell3 possuem o mesmo comportamento que Cell, porém, com os
canais renomeados e cada instância representa um contrato de componente válido.
5.2.2
Composição
Para realizar a verificação da composição é necessário gerar o protocolo para cada
canal do componente. Na descrição do contrato existe a opção, para cada canal, de
adicionar seu protocolo e dual-protocol, que são representados por processos CSP, como
podemos observar na Figura 5.5. Na Figura, encontramos a definição do protocolo e dual
de Cell sobre o canal rd, estes estão representados como uma chamada de função, pois os
processos que os representam foram definidos anteriormente em "auxiliar process", opção
que a BTS fornece ao usuário, para definir processos que ele deseja inserir no arquivo da
especificação final. Em nosso estudo de caso, temos os seguintes protocolos e protocolos
dual para Cell e Controller:
P rotCell(e) =|˜| v2 : V alue @ e.req?v1 → e.ack.v2 → P rotCell(e)
5.2 Aplicação do Buffer Circular na BTS
71
Figura 5.5: Interface para descrição protocolo e dual-protocols
Dual_P rot_Cell(e) = |˜| v1 : V alue @ e.req.v1 → e.ack?v2 → Dual_P rotCell(e)
P rotController(e) =|˜|v1 : V alue @ e.req.v1 → e.ack?v2 → P rotController(e)
Dual_P rotController(e) = |˜| v2 : V alue @ e.req?v1 → e.ack.v2 → Dual_P rotController(e)
Para analisar o comportamento das composições, a BTS nos fornece a interface descrita na Figura 5.6, onde no exemplo da Figura temos a seleção de dois componentes
(Cell1 e Cell2 ) e da regra de interleaving. Como nessa regra não são necessários os canais
(port), estes não precisam ser descritos. Apresentaremos a seguir a ordem da aplicação
das regras pela BTS.
Figura 5.6: Tela de composição com o exemplo da composição em interleave
5.2 Aplicação do Buffer Circular na BTS
72
Interleave
Nesse estudo de caso, a composição em interleave consiste em validar a comunicação
entre as três células que formam o anel de buffer (Fig. 5.1). Primeiramente, compomos
em interleave Cell1 e Cell2.
Figura 5.7: Composição em interleave de Cell1 e Cell2
Cell1_Cell2 = Cell1 [|||] Cell2
Para que a aplicação da regra seja válida, basta que seus conjuntos de eventos, o que é
verificado pela BTS, não sejam iguais (como observado na instanciação desses processos).
O resultado dessa composição é o processo Cell1_Cell2 (Fig. 5.7) que pode então ser
composto com Cell3.
Figura 5.8: Composição em interleave de Cell1, Cell2 e Cell3
Cell1_Cell2_Cell3 = Cell1_Cell2 [|||] Cell3
A composição desses componentes (Fig. 5.8) completam a composição das células em
nosso exemplo, as quais não têm comunicação entre si, mas apenas com o ambiente.
Communication
Em nosso exemplo do Buffer Circular, a composição em comunicação consiste em
verificar a interação entre os componentes, ou seja, essa regra faz a verificação da comunicação do Controller com as células e requer um conjunto de análise mais complexo. Na
ferramenta, compomos o Controller com o resultado da composição entre as três células
5.2 Aplicação do Buffer Circular na BTS
73
(Fig. 5.9). Nesse estudo de caso, realizamos a composição em comunicação com entre os
canais wrt_i.1 e write.1. A análise da composição entre esses componentes é realizada a
partir da comunicação entre esses dois canais.
Figura 5.9: Composição em Communication do Controller com as Células
Feedback
Na composição por feedback são selecionados dois canais do componente, gerado a
partir da composição entre o Controller e as células. Essa regra verifica se a comunicação
em um canal não interfere na comunicação do outro. Para nosso estudo de caso, realizamos
três composições em feedback: compomos os canais rd_i.1 e read.1 ; wrt_i.2 e write.2 ;
rd_i.2 e read.2.
Figura 5.10: Composição em feedback do componente resultante das células com o Controller
Reflexive
A última composição verificada é a reflexiva (Fig. 5.11) entre dois canais do componente resultante da composição em feedback. Essa regra verifica se existem problemas
5.3 Análise do Estudo de Caso
74
de dependência sobre a comunicação dos eventos no sistema. A composição foi realizada
entre os canais wrt_i.3 e write.3 ; rd_i.3 e read.3.
Figura 5.11: Composição em reflexive do componente resultante da composição em feedback
5.3
Análise do Estudo de Caso
Assim como descrito no processo original, nossa ferramenta foi capaz de gerar e verificar toda especificação do estudo de caso apresentado em [OLIVEIRA, 2012b]. Esse veio
validar nossa ferramenta, em relação a abordagem manual, quanto à facilidade, redução
da complexidade, tempo de resposta e confiabilidade.
Todo conjunto de regras e leis são definidos internamente pela ferramenta, de forma
automática, através de asserções FDR2. A tabela a seguir apresenta o conjunto de asserções gerado automaticamente em cada fase do estudo de caso. Os testes foram realizados
em uma máquina Pentium(R) Dual-Core CPU T4400 @ 2.20GHz, com 4Gb RAM e sistema operacional Ubuntu 12.04.
I/O process de Cell (3)
Asserções geradas e
Chamadas a proces-
Total de linhas no
verificadas
21
sos externos
48
script CSP
705
tempo de execução
2s07ms
I/O process de Controller (1)
9
16
261
14s.
Composições em Interleave (2)
2
4
508
1s8ms
Composição em
43
62
372
3s64ms
Composições em feedback (3)
135
192
1122
7m58s
Composições em reflexive (2)
88
132
782
4m77s
Total
298
454
3750
17m21s
communication (1)
Como podemos observar na tabela, cada coluna representa um uma fase de verificação,
como por exemplo, o resultado da verificação de I/O process das três celulas.
5.3 Análise do Estudo de Caso
75
Na primeira linha da tabela podemos observar a quantidade de asserções geradas e
verificadas. Ou seja, para a verificação de I/O process das três células, por exemplo, temos
a totalidade de 21 asserções geradas. Já as três composições em feedback totalizaram 135
asserções geradas automaticamente.
A BTS também inclui alguns arquivos externos que possuem a implementação de
processos usados na verificação das asserções, como descrito na segunda linha da tabela.
Esta representa a quantidade de chamadas a esses processos na execução dos arquivos
gerados. Além disso, a tabela apresenta a quantidade de linhas de código gerada e o
tempo de processamento em cada fase. Esse tempo foi medido manualmente e representa
desde o momento de solicitação para aquela verificação, até a apresentação do resultado
pela BTS ao usuário.
Uma das grandes contribuições da aplicação deste estudo de caso é a geração e verificação automática de 298 asserções de forma oculta ao usuário, além das 454 chamadas à
processos, que implementam as propriedades e regras descritas em [RAMOS, 2011], sendo
estas, através do FDR2, capazes de avaliar e validar a composição de componentes no
sistema. Além disso, os scripts CSP gerados em cada fase totalizaram 3750 linhas de
código.
76
6
Considerações finais
A automatização do processo de desenvolvimento de software é de extrema importância nos dias atuais, principalmente quando tratamos de características que são indispensáveis nesse processo, mas que requerem muito tempo e um alto custo.
Como dito anteriormente, o desenvolvimento de componentes tem ganhado destaque
nos últimos anos, devido à facilidade de manutenção, reutilização e confiabilidade, a
qual deve estar associada não só aos componentes, mas também à forma como esses são
compostos no sistema.
Em nosso trabalho propomos a implementação de uma ferramenta que faz uma verificação prévia da composição de forma automatizada. Esta implementa a abordagem
proposta em [RAMOS, 2011] que apresenta todas as verificações e validações através do
método formal CSP.
A abordagem foi baseada em um modelo de componentes e possui quatro formas de
composição. A composição é validada através da verificação de determinadas condições.
Uma vez satisfeitas as condições de aplicação da composição, a abordagem garante que o
sistema resultante é livre de deadlocks.
Iniciamos nosso trabalho com um estudo da abordagem utilizada. Logo em seguida,
visando um melhor entendimento e uma melhor aprofundamento no tema, fizemos uma
avaliação do estudo de caso proposto em [OLIVEIRA, 2012b] que aplica a abordagem a
um estudo de caso através do FDR2. A partir dessa análise, iniciamos a implementação de
nossa ferramenta utilizando a linguagem de programação Java e outras duas ferramentas
de apoio: o typeChecker e FDR2.
A ferramenta desenvolvida é capaz de, a partir da definição de um modelo de componente, verificar se sua comunicação com outros componentes é segura. Essa verificação
é feita através das características definidas pelo usuário, como comportamento, portas,
interface. Internamente a ferramenta gera arquivos CSP e realiza a verificação automaticamente. O resultado dessa verificação é transmitido diretamente para o usuário.
6.1 Trabalhos Futuros
77
Para avaliar nossa ferramenta realizamos um estudo de caso, onde geramos e testando
os componentes e sua composição a partir do exemplo clássico de buffer circular. Através
desse estudo de caso conseguimos avaliar características como redução da complexidade,
onde o usuário precisaria desenvolver e aplicar todas as regras e propriedades através da
linguagem CSP. Agora tudo é implementado internamente pela ferramenta e não é preciso
ter conhecimento sobre o FDR2.
A ferramenta apresenta ganho em relação a geração das condições de verificação automaticamente e a comunicação direta com o FDR2 sem conhecimento do usuário. Com
isso, abstraímos aos olhos do utilizador toda implementação das regras e propriedades
impostas em [RAMOS, 2011]. Porém, é necessário que o usuário tenha conhecimento
sobre a abordagem, para que, caso a BTS apresente alguma condição como inválida, seja
possível identificar o erro e consertar na descrição do modelo de componente.
6.1
Trabalhos Futuros
Nessa versão da ferramenta ainda não é possível ter um processo totalmente automatizado, por a descrição do comportamento do componente ser implementado manualmente.
Nesse contexto, como trabalhos futuros, pretende-se utilizar a plataforma Spoofax 1 , onde
é possível descrever linguagens como CSP utilizando um alto nível de formalismo, como
por exemplo, a linguagem de modelagem UML 2 . Com isso, temos a possibilidade de
omitir a linguagem CSP do usuário.
Ainda no trabalho de [RAMOS, 2011], foi proposto um estilo arquitetural chamado
BRICK. Nele, os contratos de componentes são enriquecidos com metadados que executam informações adicionais úteis na verificação da composição. Esses metadados da
composição são derivados dos metadados dos componentes que a constitui. Como trabalho futuro, pretendemos estender a BTS para o modelo de contrato de componente
enriquecido com metadados.
1
http://strategoxt.org/Spoofax/
2 http://www.uml.org/
78
Referências Bibliográficas
ABRIAL, J.-R. et al. Rodin: an open toolset for modelling and reasoning in event-b.
Int. J. Softw. Tools Technol. Transf., Springer-Verlag, Berlin, Heidelberg, v. 12, n. 6, p.
447–466, nov. 2010. ISSN 1433-2779. Disponível em: <http://dx.doi.org/10.1007/s10009010-0145-y>.
BARBOSA, D. Um Método Automático de Teste Funcional para a Verificação de Componentes. Dissertação (Mestrado) — Departamento de Computação - Universidade Federal
de Campina Grande, 2005.
BARN, B.; BROWN, A. W. Methods and tools for component based development. In:
Proceedings of the Technology of Object-Oriented Languages and Systems. Washington,
DC, USA: IEEE Computer Society, 1998. (TOOLS ’98), p. 384–. ISBN 0-8186-8482-8.
Disponível em: <http://dl.acm.org/citation.cfm?id=832254.832826>.
BERTOLINI, C.; MOTA, A. Ideas08: An strategy for automatic conformance testing in
embedded systems. Latin America Transactions, IEEE (Revista IEEE America Latina),
v. 6, n. 3, p. 290–297, 2008. ISSN 1548-0992.
BROWN, A. W.; SHORT, K. On components and objects: The foundations of componentbased development. In: Proceedings of the 5th International Symposium on Assessment
of Software Tools (SAST ’97). Washington, DC, USA: IEEE Computer Society, 1997.
(SAST ’97).
BROWN, A. W.; WALLNAU, K. C. The current state of cbse. IEEE Software, v. 15, n. 5,
p. 37–46, 1998.
BROWNLEE, N. Formal systems (europe) ltd. failures-divergence refinement. fdr2
user manual. available at http://www.formal.demon.co.uk/fdr2manual/index.html.
In:
Blount MetraTech Corp. Accounting Attributes and Record Formats
http://www.ietf.org/rfc/rfc2924.txt. [S.l.: s.n.], 2000.
CAVALCANTI, A.; GAUDEL, M.-C. Testing for refinement in csp. In: BUTLER, M.;
HINCHEY, M. G.; LARRONDO-PETRIE, M. M. (Ed.). Formal Methods and Software
Engineering, 9th International Conference on Formal Engineering Methods, ICFEM 2007,
Boca Raton, FL, USA, November 14-15, 2007, Proceedings. [S.l.]: Springer, 2007. (Lecture
Notes in Computer Science, v. 4789), p. 151–170. ISBN 978-3-540-76648-3.
CLARKE JR., E. M.; GRUMBERG, O.; PELED, D. A. Model checking. Cambridge, MA,
USA: MIT Press, 1999. ISBN 0-262-03270-8.
DIACONESCU, R.; FUTATSUGI, K. CafeOBJ report : the language, proof techniques,
and methodologies for object-oriented algebraic specification. Singapore, River Edge, NJ,
London: World scientific, 1998. (AMAST series in computing). ISBN 981-02-3513-5. Disponível em: <http://opac.inria.fr/record=b1104496>.
Referências Bibliográficas
79
EASSA, F. E.; ABULNAJA, O. A. Building uml-based use case sub-tool for componentbased software development. In: Proceedings of the ACS/IEEE International Conference
on Computer Systems and Applications. Washington, DC, USA: IEEE Computer Society,
2001. (AICCSA ’01), p. 454–. ISBN 0-7695-1165-1. Disponível em: <http://dl.acm.org/citation.cfm?id=872017.872219>.
FEITOSA, C.; PERES, G.; MOTA, A. Unifying models of test cases and requirements.
In: CIbSE. [S.l.: s.n.], 2008. p. 113–126.
GRIESKAMP, W. et al. Generating finite state machines from abstract state machines.
In: in Proceedings of International Symposium on Software Testing and Analysis (ISSTA.
[S.l.: s.n.], 2002.
GROOVER, M. P. Automation, Production Systems, and Computer-Integrated Manufacturing. 3rd. ed. Upper Saddle River, NJ, USA: Prentice Hall Press, 2007. ISBN
0132393212.
HOARE, C. A. R. Communicating sequential processes. Commun. ACM, ACM, New
York, NY, USA, v. 21, n. 8, p. 666–677, ago. 1978. ISSN 0001-0782. Disponível em:
<http://doi.acm.org/10.1145/359576.359585>.
HOARE, C. A. R. Communicating Sequential Processes. [S.l.]: Prentice-Hall, 1985. ISBN
0-13-153271-5.
ISOBE, Y.; ROGGENBACH, M. Webpage on CSP-Prover. http://staff.aist.go.
jp/y-isobe/CSP-Prover/CSP-Prover.html.
JOLLIFFE, G. Cost-efficient methods and processes for safety relevant embedded systems
(cesar) - an objective overview. In: SSS. [S.l.: s.n.], 2010. p. 37–50.
KICZALES, G. et al. Getting started with aspectj. Communications of the ACM, v. 44,
p. 59–65, 2001.
LAMPORT, L. The temporal logic of actions. ACM Trans. Program. Lang. Syst., ACM,
New York, NY, USA, v. 16, n. 3, p. 872–923, maio 1994. ISSN 0164-0925. Disponível em:
<http://doi.acm.org/10.1145/177492.177726>.
LAU, K.-K.; NTALAMAGKAS, I. Component-based construction of concurrent systems
with active components. In: Proc. 35th EUROMICRO Conference on Software Engineering and Advanced Applications (SEAA 2009). [S.l.]: IEEE, 2009. p. 497–502.
LAU, K.-K.; TAWEEL, F.; TRAN, C. The W Model for component-based software development. In: Proc. 37th EUROMICRO Conference on Software Engineering and Advanced
Applications. [S.l.]: IEEE, 2011. p. 47–50.
LAU, K.-K.; TRAN, C. M. X-man: An mde tool for component-based system development. 2010 36th EUROMICRO Conference on Software Engineering and Advanced
Applications, IEEE Computer Society, Los Alamitos, CA, USA, v. 0, p. 158–165, 2012.
LAU, K.-K.; Velasco Elizondo, P.; WANG, Z. Exogenous connectors for software components. In: Proc. 8th Int. SIGSOFT Symp. on Component-based Software Engineering,
LNCS 3489. [S.l.: s.n.], 2005. p. 90–106.
Referências Bibliográficas
80
LEDECZI, A. et al. The generic modeling environment. In: Workshop on Intelligent Signal
Processing. [S.l.: s.n.], 2001.
MARCILON, T. Contratos Formais para Derivação e Verificação de Componentes Paralelos. Dissertação (Mestrado) — Departamento de Computação - Universidade Federal do
Ceará, 2012.
MATSUMOTO, M.; FUTATSUGI, K. Highly reliable component-based software development by using algebraic behavioral specification. In: Proceedings of the 3rd IEEE International Conference on Formal Engineering Methods. Washington, DC, USA: IEEE Computer Society, 2000. (ICFEM ’00), p. 35–. ISBN 0-7695-0822-7. Disponível em: <http://dl.acm.org/citation.cfm?id=857176.857382>.
MATSUMOTO, M.; FUTATSUGI, K. The support tool for highly reliable componentbased software development. In: Software Engineering Conference, 2000. APSEC 2000.
Proceedings. Seventh Asia-Pacific. [S.l.: s.n.], 2000. p. 172–179.
MCILROY, M. D. Mass-produced software components. Proc. NATO Conf. on Software
Engineering, Garmisch, Germany, Springer-Verlag, 1968.
MOTA, A. et al. Evolving a safe system design iteratively. In: Proceedings of the 29th
international conference on Computer safety, reliability, and security. Berlin, Heidelberg:
Springer-Verlag, 2010. (SAFECOMP’10), p. 361–374. ISBN 3-642-15650-9, 978-3-64215650-2. Disponível em: <http://dl.acm.org/citation.cfm?id=1886301% -.1886337>.
NETO, E. Abordagem para Geração Automática de Código para Framework de Automação
de Testes. Dissertação (Mestrado) — Universidade Federal de Pernambuco, Centro de
Informática, 2007.
NIERSTRASZ, O.; DAMI, L. Component-Oriented Software Technology. 1995.
NIPKOW, T.; PAULSON, L. C.; WENZEL, M. Isabelle/HOL — A Proof Assistant for
Higher-Order Logic. [S.l.]: Springer, 2002. (LNCS, v. 2283).
NOGUEIRA, S.; SAMPAIO, A.; MOTA, A. Guided test generation from csp models.
In: FITZGERALD, J. S.; HAXTHAUSEN, A. E.; YENIGüN, H. (Ed.). ICTAC. [S.l.]:
Springer, 2008. (Lecture Notes in Computer Science, v. 5160), p. 258–273. ISBN 978-3540-85761-7.
OLIVEIRA, M. research report, An Exercise on Strong Output Decisiveness and Input
Determinism. Berlin, Germany: [s.n.], 2012.
OLIVEIRA, M. research report, Systematic Development of Constructive Componentbased Systems: a Quantitative Analysis. Berlin, Germany: [s.n.], 2012.
PERES, G.; MOTA, A. A tool to translate CSP models into english requirements. In: II
Encontro Brasileiro de Teste de Software. Recife: In: II Encontro Brasileiro de Teste de
Software, 2007.
PROBE User Manual. 2003. http://www.fsel.com/documentation/probe/
probe-doc-html/html/index.html.
Referências Bibliográficas
81
RAMOS, R. Systematic Development of Trustworthy Component-based Systems. Dissertação (Doutorado) — Universidade Federal de Pernambuco, Centro de Informática, 2011.
ROSCOE, A. W.; HOARE, C. A. R.; BIRD, R. The Theory and Practice of Concurrency.
Upper Saddle River, NJ, USA: Prentice Hall PTR, 1997. ISBN 0136744095.
RUMBAUGH, J.; JACOBSON, I.; BOOCH, G. Unified Modeling Language Reference
Manual, The (2nd Edition). [S.l.]: Pearson Higher Education, 2004. ISBN 0321245628.
SCHNEIDER, S. Concurrent and Real Time Systems: The CSP Approach. 1st. ed. New
York, NY, USA: John Wiley & Sons, Inc., 1999. ISBN 0471623733.
SCHNEIDER, S. The B-Method: An Introduction. [S.l.]: Palgrave Macmillan, 2001. (Cornerstones of Computing).
SMITH, G. The Object-Z Specification Language. [S.l.]: Kluwer Academic Publishers,
2000. 160 p.
SPAGNOLI, L. de A.; BECKER, K. UM ESTUDO SOBRE O DESENVOLVIMENTO
BASEADO EM COMPONENTES. [S.l.], Maio 2003.
STANKOVIC, J. A. et al. VEST: An Aspect-Based Composition Tool for Real-Time Systems. 2003.
SZYPERSKI, C. Component Software: Beyond Object-Oriented Programming. 2nd.
ed. Boston, MA, USA: Addison-Wesley Longman Publishing Co., Inc., 2002. ISBN
0201745720.
VALLECILLO, A.; VASCONCELOS, V. T.; RAVARA, A. Typing the behavior of software components using session types. Fundam. Inf., IOS Press, Amsterdam, The Netherlands, The Netherlands, v. 73, n. 4, p. 583–598, set. 2006. ISSN 0169-2968. Disponível
em: <http://dl.acm.org/citation.cfm?id=1231191% -.1231198>.
VINCENZI, A. M. R. et al. Desenvolvimento baseado em componentes: Conceitos e
. [S.l.]: Editora Ciência Moderna Ltda., 2005. cap. Software Baseado
técnicas. In:
em Componentes: Uma Revisão sobre Teste.
WARMER, J.; KLEPPE, A. The Object Constraint Language: Getting Your Models Ready
for MDA. 2. ed. Boston, MA, USA: Addison-Wesley Longman Publishing Co., Inc., 2003.
ISBN 0321179366.
WIRTH, N. A brief history of software engineering. IEEE Annals of the History of Computing, v. 30, n. 3, p. 32–39, 2008.
WOODCOCK, J. C. P.; CAVALCANTI, A. L. C. The semantics of circus. In: BERT, D.
et al. (Ed.). ZB 2002: Formal Specification and Development in Z and B. [S.l.]: SpringerVerlag, 2002. (Lecture Notes in Computer Science, v. 2272), p. 184—203.
Download

BTS: Uma ferramenta de suporte ao desenvolvimento de