Upload
others
View
2
Download
0
Embed Size (px)
Citation preview
Departamento de Informática
UM MECANISMO DE DEPURAÇÃO PARA SISTEMAS
DISTRIBUÍDOS
Aluno: Carla Galdino Wanderley
Orientador: Arndt von Staa
Introdução
O termo qualidade de software é relativo: considere um sistema web que apresente uma
falha por semana e esta provoque a sua reinicialização. Se este for um sistema de missão-
crítica ele será considerado de baixa qualidade, entretanto, se for um simples sistema de
gestão de informações a sua qualidade ainda poderá ser considerada como média ou até
mesmo alta.
Qualidade pode ser definida como um conjunto de propriedades e características de um
produto ou serviço a serem satisfeitas em determinado grau, e que lhe confere a capacidade de
satisfazer as necessidades explícitas e implícitas de seus usuários e clientes. No caso de
software a avaliação de qualidade pode ser realizada a partir de um conjunto de atributos
definidos de acordo com a norma [ISO 9126], que são: usabilidade, funcionalidade,
confiabilidade, eficiência, manutenibilidade e portabilidade.
Ainda que a qualidade seja uma propriedade multidimensional, dentro do contexto de
sistemas de missão-crítica, consideramos a confiabilidade um atributo chave, pois neste tipo
de sistema é esperado que o número de ocorrências de falhas seja ínfimo. Outro atributo
considerado neste contexto é a disponibilidade do software, que se refere à probabilidade de
um sistema funcionar satisfatoriamente sempre que solicitado.
Além disso, sabemos que é importante focar no desenvolvimento de software que se
aproxime a ser fidedigno por construção, pois cerca de 50% do software posto em uso contém
defeitos não triviais [Boehm and Basili 2001] e ainda dependemos excessivamente de
software que frequentemente falha de forma imprevisível [Pitac 1999]. Outro ponto a ser
considerado é que estas métricas são feitas considerando todo tipo de software. A qualidade
pode ser comprometida a despeito dos esforços dos desenvolvedores, pois foi verificado que
as bibliotecas de classes são frequentemente não confiáveis [Thomas 2002].
Considerando que o objetivo do nosso trabalho é produzir mecanismos para aumentar a
qualidade do software, optamos por investir em ferramentas que aumentem os atributos
confiabilidade e disponibilidade. Acreditamos que a partir da criação de mecanismos de
introspecção seja possível criar ferramentas para humanos (depuradores) e máquinas
(sistemas orientados à recuperação), em ambos os casos auxiliando a compreensão do sistema
e, consequentemente, reduzindo o tempo médio de reparo (MTTR) quando uma falha for
encontrada, logo contribuindo para a melhoria da qualidade em virtude da maior facilidade de
remoção de eventuais defeitos.
Este relatório está dividido da seguinte forma: na seção 1 introduzimos o assunto; na
seção 2 apresentamos as motivações para esta pesquisa; na seção 3 discutimos o estado da arte
atual; na seção 4 apresentaremos o mecanismo de depuração; na seção 5 discutimos as
contribuições esperadas; na seção 6 apresentamos um esboço da solução; e por fim, na seção
7 descrevemos o cronograma.
Departamento de Informática
Motivação
A geração de informações de introspecção, ou seja, a observação do funcionamento do
software feita pelo próprio software, é uma atividade bastante utilizada durante o
desenvolvimento de sistemas complexos. Esta atividade promove o aumento do volume de
informações relacionadas ao sistema, que podem ser utilizadas tanto por desenvolvedores
quanto administradores com o objetivo de diagnosticar eventuais falhas.
Uma técnica comum neste contexto é a produção de logs com mensagens que
descrevam o comportamento do sistema em tempo de execução, podendo conter valores
associados que auxiliem a contextualizar as informações. Esta técnica é bastante interessante
pois aumenta o conhecimento sobre o funcionamento do sistema e fornece informações com
muita riqueza de detalhes acerca do sistema em execução. Entretanto, a utilização desta
técnica demanda muito esforço por parte dos desenvolvedores do sistema e seus resultados
nem sempre são satisfatórios quando aplicados a sistemas reais, já que o volume de dados
gerados pelos logs tende a ser muito grande. Assim como o volume a organização dos logs é
um ponto importante a ser considerado, pois uma estruturação ruim de eventos de log pode
reduzir a visibilidade das informações relevantes para o problema.
Além disso, a dificuldade de utilização de logs cresce exponencialmente quando
tratamos de ambientes distribuídos, pois neste contexto, os logs ficam espalhados em
diferentes máquinas ou dispositivos. Isto faz com que seja necessário acessá-los um a um para
inspeção, acrescentando mais uma dificuldade ao processo inicial: a ordenação global de
todos os eventos capturados em todas as máquinas.
Como o conhecimento sobre o sistema é um ponto importante para a nossa solução,
consideramos a técnica de geração de logs adequada para a solução do problema. Dessa
forma, apresentamos um mecanismo de depuração para sistemas distribuídos composto por
uma biblioteca que auxilia a geração de informações de introspecção (tendo como base o log
de execução do sistema) e um sistema de visualização para estudar o comportamento do
sistema como um todo a partir deste log.
Estado da Arte
Existem várias abordagens relacionadas à depuração de sistemas distribuídos. Uma
delas é a verificação de modelos [Killian et al 2007], que utiliza verificadores baseados nas
especificações do sistema para encontrar estados inconsistentes. O trabalho de Killian [Killian
et al 2007] especificamente fornece mecanismos para identificar estados suspeitos do sistema
e uma ferramenta para reexecutá-lo de forma interativa. Esta ferramenta é baseada em uma
máquina virtual com controles para voltar ou avançar a execução, permitindo que o
desenvolvedor estude o comportamento do sistema em cada ponto da execução. Esta
abordagem pode ser poderosa para o processo de depuração, no entanto, para detectar estados
suspeitos, assume-se que a especificação seja corretamente implementada além dela própria
estar correta do ponto de vista formal. Outra desvantagem desta abordagem é a dependência
de um ambiente virtualizado para reproduzir a falha.
Uma segunda abordagem seria a verificação baseada em reprodução, que disponibiliza
ao programador a capacidade de reprodução da execução de um programa, mimetizando sua
ordem e ambiente. Como exemplo de trabalho baseado nesta abordagem, temos Liblog [Geels
et al 2007]. Esta biblioteca cria logs durante a execução de um sistema e promove um
“replay” deterministicamente. O grande benefício deste tipo de ferramenta é a capacidade de
reprodução de falhas de forma consistente, mas por outro lado, possui um custo bastante
elevado de registro e reprodução de uma execução inteira.
Outra abordagem possível é a criação de depuração e verificação automatizada. Esta
abordagem promove o uso de predicados e representação destes predicados em grafos de
fluxo de dados. Como exemplo desta abordagem, temos D³S [Liu 2008] e MaceODB [Dao et
Departamento de Informática
al 2009]. Ambos promovem extensões para a linguagem C++, mas D³S utiliza uma linguagem
de scripts além destas extensões. Esta abordagem pode ser eficaz, já que verifica
automaticamente estados de um sistema distribuído como um todo, mas diminui bastante o
desempenho das aplicações que a utilizam por conta do “overhead” referente às verificações.
Por fim, temos a depuração através de logs, categoria em que a solução aqui proposta
se encaixa. Este tipo de abordagem promove a geração de informações para a análise de
registros pós-execução. Um exemplo desta metodologia é Pip [Reynolds et al 2006]. Pip
possibilita aos programadores especificar expectativas acerca da estrutura do sistema, assim
como outras propriedades, gerando logs para o sistema e, além disso, fornece uma interface
visual para explorar comportamentos normais e anormais. O mecanismo apresentado difere de
Pip na estrutura de criação de logs, que é organizada de forma a facilitar a busca de
informações conforme o contexto desejado. Esta estrutura de logs também ajudou na criação
da ferramenta de visualização destes logs, visando auxiliar a disposição de dados relevantes.
A maioria das ferramentas de auxílio à analise de logs do sistema, como é o caso de Pip,
necessitam da definição de estados inconsistentes do sistema para que seja possível analisar
informações sobre a execução. No entanto, acreditamos que é importante disponibilizar estas
informações de uma forma fácil, visando aumentar o conhecimento sobre o sistema mesmo
sem a identificação de falhas.
O mecanismo de depuração
O mecanismo de depuração apresentado tem como objetivo principal auxiliar o
desenvolvimento de sistemas distribuídos, disponibilizando informações inerentes ao software
em execução, sendo estas produzidas por instrumentação contida no código ou mesmo
coletadas automaticamente, como por exemplo, dados disponíveis a partir de medições
realizadas na plataforma. Estas informações devem ser disponibilizadas permitindo uma
forma de visualização, criada especialmente para a depuração de sistemas distribuídos. É
importante que esta forma de visualização seja estruturada com base nas necessidades dos
desenvolvedores, facilitando o processo de busca e identificação de possíveis falhas.
Dessa forma, criamos um mecanismo de depuração que utiliza informações de introspecção
do sistema-alvo para apresentar o seu comportamento em tempo de execução sob a forma de
eventos ordenados cronologicamente, ainda possibilitando a sua seleção conforme as
necessidades de cada operação de depuração. Isso é feito através de filtros que possibilitam a
inclusão e exclusão de eventos, baseando-se nas suas meta-informações.
Arquitetura
O mecanismo de depuração apresentado é baseado em informações de introspecção
referentes aos componentes de um sistema distribuído. Portanto, é necessário que estas
informações sejam obtidas facilmente e que estejam disponíveis para que os desenvolvedores
as consultem.
Dessa forma, projetamos a arquitetura do mecanismo de uma forma abstrata, seguindo
o fluxo de informações. As informações são originadas em cada um dos componentes do
sistema distribuído e enviadas a um servidor central. O servidor realiza algumas rotinas de
tratamento nas informações recebidas e logo em seguida, as armazena em um banco de dados.
Neste momento, estas informações se tornam disponíveis para os usuários do mecanismo,
através de uma ferramenta de visualização. O fluxo de dados foi representado na figura a
seguir:
Departamento de Informática
Figura 1 - Fluxo de informações originado pela utilização do mecanismo de depuração
O fluxo de dados acima nos apresenta três pontos a serem considerados: (1) Como gerar
informações de contexto nos componentes do sistema; (2) Como armazenar o grande volume
de dados originados e (3) Como apresentar as informações de maneira adequada para o
processo de depuração.
Considerando os pontos apresentados, definimos uma solução que consiste na criação de uma
biblioteca de auxílio à coleta de informações de introspecção, um servidor de tratamento e
armazenamento das informações coletadas e uma ferramenta de apoio à inspeção.
Figura 2 – Desafios e soluções apresentadas para a depuração de sistemas distribuídos
Departamento de Informática
O diagrama arquitetural da solução pode ser visto a seguir:
Figura 3 - Arquitetura do mecanismo de depuração
O diagrama acima apresenta as classes existentes em cada um dos componentes do sistema.
Considerando a solução apresentada anteriormente, os dispositivos utilizam uma biblioteca de
apoio à geração de informações de contexto. A hierarquia de classes da biblioteca está
representada na caixa azul à esquerda do diagrama. Neste contexto são encontradas as classes
Tag, Event, Stack e Logger. A classe Tag é uma estrutura de dados que possui duas
informações principais: key e value. A classe Event é composta por um conjunto de elementos
do tipo Tag, que representam informações de contexto do software. A classe Logger possui
uma pilha de Tags representada pela classe Stack. A pilha de Tags presente na classe Logger
representa o contexto corrente de execução do software. A classe Logger é responsável pelo
controle da pilha de Tags e pela notificação de eventos, representados pelos seguintes
métodos respectivamente: pushTag, popTag e notifyEvent. O método notifyEvent é
responsável pelo envio de eventos de log para um servidor central, que está representado
como o componente central do diagrama.
O servidor central é responsável pela manipulação e armazenamento de eventos de
log. A hierarquia de classes deste componente está expressa na caixa azul central e apresenta
as classes Database e EventProcess. A classe Database fornece métodos que facilitam tanto a
inserção de eventos na base de dados quanto a execução de filtros para a obtenção de eventos.
A classe EventProcess é responsável pelo tratamento de informações. O método process_logs
recebe dados enviados pelos dispositivos através de requisições, faz algumas correções e os
insere no banco de dados na forma de eventos. Os métodos get_event_list_by_interval,
count_event_list_by_interval e get_event_list_by_filters são utilizados pelo componente
representado pela ferramenta de inspeção. Estes métodos promovem a obtenção de eventos do
banco de dados e realizam algumas alterações que facilitam sua apresentação na ferramenta.
O componente Ferramenta de inspeção se resume à interface gráfica de apresentação
de eventos. Como esta interface foi desenvolvida utilizando HTML [Graham 1995],
Javascript [Powell, T., Schneider, F., 2001], AJAX [Powell, T. 2008] e JSON [Crockford, D.
2012], ela não é orientada a objetos, mas possui estrutura procedural.
Departamento de Informática
Aplicação de Avaliação
Tendo em vista que a utilização de um mecanismo de depuração para sistemas distribuídos
torna-se interessante apenas quando pode ser avaliado, escolhemos uma solução de cardápio
digital implementada como um sistema distribuído para implantação do mecanismo
apresentado. O sistema de cardápio digital é um sistema real que utiliza tablets para
apresentação de dados e um sistema web para gerenciamento.
O aplicativo executado no tablet é baseado em uma linha de produto [Clements e Northrop
2002] e, durante a configuração de cada cliente, é produzido um layout customizado seguindo
a identidade visual da sua marca, de acordo com a escolha prévia dos ativos (assets) da linha
de produto. Além da escolha dos assets existe uma configuração fina dos mesmos, realizada
através de arquivos de configuração. O layout é produzido é sensível à escolha dos assets e
das configurações definidas. A aplicação web de gestão do conteúdo permite inserir, editar e
remover diferentes tipos de produtos, além de fornecer mecanismos para configurar o
aplicativo dos tablets.
O servidor de sincronização é responsável pela atualização incremental do conteúdo e das
configurações dos tablets.
A escolha deste sistema como estudo de caso se adéqua-se perfeitamente ao presente trabalho
devido às dificuldades identificadas em seu ambiente de produção, que se deve
principalmente ao trabalho concorrente de diferentes tipos de atores sobre as informações de
um mesmo cliente. De forma resumida, os atores do sistema são: um administrador
configurando a linha de produto de cada cliente segundo suas necessidades, uma equipe de
catalogação inserindo a ficha técnica de produtos requisitados pelos clientes e uma equipe de
designers produzindo o layout segundo as especificações de cada cliente. E importante deixar
claro que o trabalho realizado por estes atores não é restrito a etapa inicial de implantação do
sistema, mas persiste ao longo da sua utilização.
Neste contexto, podemos identificar as entidades do sistema descrito. Dessa forma, tanto a
instância do aplicativo de cartas digitais executada em cada iPad quanto as instâncias
resultantes da utilização do sistema de gerenciamento são componentes do sistema-alvo.
Portanto, ele se encaixa na definição de um sistema distribuído e pode ser considerado para a
avaliação do mecanismo apresentado.
Geração de informações de contexto
O mecanismo apresentado consiste na depuração de sistemas distribuídos com base em seu
contexto de execução. Um processo normalmente utilizado na depuração de sistemas
distribuídos é a utilização de logs, que permitam a inspeção de informações sobre a execução
de cada componente do sistema.
Esta abordagem pode funcionar bem em alguns casos, mas na maioria das vezes gera uma
quantidade de dados muito grande, com informações que intercalam semânticas diferentes a
respeito do sistema. Estes problemas acabam dificultando o processo de depuração, uma vez
que se torna necessário que o depurador aplique filtros sobre este conjuntos de dados,
tentando inferir algum padrão nos logs emitidos pelos componentes.
Departamento de Informática
Considerando os problemas apresentados, percebemos que a manipulação destes logs seria
muito mais eficaz caso os dados fossem estruturados a fim de facilitar a depuração. Como
consequência, projetamos uma estrutura para a geração de logs de sistema que tem como
unidade principal o evento.
Um evento é um conjunto de informações referentes a um momento da execução de um
sistema ou componente adicionadas a uma mensagem que descreve de forma textual o
procedimento que está sendo realizado. Dessa forma, um evento pode representar a
arquitetura do sistema, o método corrente, a pilha de execução e ao mesmo tempo, fornecer
informações de mais alto nível através de sua mensagem.
Biblioteca de apoio à introspecção
A estratégia escolhida para obtenção de informações de introspecção foi criar uma biblioteca
para cada linguagem a ser utilizada, que auxiliasse a geração e a notificação de eventos.
Projetamos uma solução que se assemelha a utilização de um log tradicional dispondo
mensagens textuais, porém, com a adição de informações do contexto de execução, que
obedecem um formato bem definido: uma lista de Tags com Chave e Valor. A técnica
utilizada permite que os logs sejam escritos no lugar de comentários, o que diminui o esforço
necessário para a instrumentar o código.
Adicionalmente, a própria biblioteca gera automaticamente outras informações de contexto, a
fim de complementar as informações já fornecidas. As informações geradas também são
dispostas como uma lista de Tags nome-valor. Como exemplo, apresentamos a seguir um
evento neste formato:
[dateTime:2012-05-25 10:15:23] [container:RobotAPI] [component:UTSensor]
[action:ReadSensorData] [failure] Não foi possível estabelecer a comunicação com a
eletrônica.
Este evento está expressando a mensagem Não foi possível estabelecer a comunicação com a
eletrônica, que está associada às Tags dateTime, container, componente, action e failure com
seus respectivos valores 2012-05-25 10:15:23, RobotAPI, UTSensor, ReadSensorData e sem
valor para a última Tag. A primeira Tag representa o momento exato em que a mensagem foi
gerada, a qual é embutida pela biblioteca. Já a segunda e terceira representam o contexto
arquitetural do sistema, a quarta representa o contexto em que ela foi emitida (nome de
método) e a última indica que ela foi uma falha. Neste caso, as quatro últimas seriam
informadas pelo desenvolvedor.
Existem algumas informações que são relevantes para expressar o contexto de aplicações em
geral, outras ainda são importantes para marcar eventos relacionados a estados inconsistentes
dos sistemas. Dessa forma, listamos algumas Tags que podem ser utilizadas no processo de
instrumentação:
- Error - Indica uma falha no sistema.
- Disaster – Indica uma falha que causa um dano irrecuperável.
Departamento de Informática
- Warning – Indica uma inconsistência dos dados ou uma condição suspeita.
- Environment – Define o ambiente de execução, que pode assumir valores como server ou
mobile.
- Function – Define o nome da função corrente.
- Action – Define a ação corrente dentro da execução, eu seja, a semântica da operação
corrente.
- CPU – Carga corrente de processamento.
- Memory – Memória ocupada pela aplicação.
Interface da biblioteca
Seguindo a especificação apresentada criamos uma interface sob forma de IDL [Auerbach
J.S.,Russell J.R.1994] que define os contratos a serem seguidos:
interface Tag {
attribute string key;
attribute string value;
}
typedef sequence<Tag> TagList;
interface TagDictionary {
attribute TagList tags;
}
module EventManager {
void notifyEvent(in string message);
void notifyEvent(
in string message,
in TagDictionary dict);
void pushTag(in string name);
Departamento de Informática
void pushTag(
in string name,
in string value);
void popTag();
}
Esta interface contém a definição de uma estrutura Tag, representada como um par chave-
valor, e uma estrutura TagList, que representa uma lista de estruturas do tipo Tag. A estrutura
TagList é utilizada como uma pilha, em que informações de contexto do software sob forma
de Tags são empilhadas e desempilhadas conforme a execução da aplicação.
Em seguida, apresentamos os métodos necessários para a geração de informações de contexto:
notifyEvent, pushTag e popTag.
Os métodos pushTag e popTag são responsáveis pela manipulação das informações de
contexto inseridas na pilha de Tags. Estes métodos devem ser utilizados em conjunto para que
não hajam inconsistências nos logs gerados.
O método notifyEvent é responsável pela criação e envio de eventos. Sua assinatura possui
alguns parâmetros, sendo o primeiro deles uma string que representa uma mensagem e o
segundo, um parâmetro opcional que representa um conjunto de Tags. Este método cria
eventos utilizando a mensagem fornecida pelo usuário e as informações de contexto contidas
na pilha de Tags, que podem ser acrescidas de outras Tags, fornecidas através do parâmetro
opcional.
Implementação
A geração de informações de contexto ocorre através de instrumentação do código do
sistema-alvo, utilizando chamadas de funções que recebem como parâmetros uma string,
representando uma mensagem e estruturas chave-valor a serem adicionadas no evento criado.
Considerando o contexto de avaliação do sistema apresentado anteriormente, podemos
verificar que os componentes apresentam conjuntos de características diferentes entre si. Uma
destas características é a linguagem utilizada por cada componente, o que influi diretamente
na criação da biblioteca de geração de informações de contexto.
Um dos principais problemas na geração de informações do contexto do software é a
dificuldade na instrumentação do código. Portanto, é importante considerar maneiras de
reduzir o esforço na execução desta tarefa. Dessa forma, optamos por gerar bibliotecas
diferentes para cada linguagem utilizada, o que nos possibilita utilizar as características de
cada linguagem para criar açúcares sintáticos que facilitem o processo de instrumentação do
código. Dessa maneira, implementamos a biblioteca apresentada para duas linguagens:
Objective-C [Cox B. 1986], e Python [Rossum, G. van 1991].
Departamento de Informática
Objective-c
Os componentes representados pelos iPads utilizam a plataforma iOS [Apple 2007] e tem
Objective-C [Cox B. 1986], como linguagem de desenvolvimento. Esta é uma linguagem
compilada que apresenta interoperabilidade com as linguagens C e C++.
Como dito anteriormente, a utilização dos métodos pushTag e popTag prevê cuidado
constante no processo de instrumentação, uma vez que se utilizados de forma assíncrona,
geram inconsistências nas informações de contexto que compõem os logs. Como solução para
este problema, optamos por utilizar a pré-compilação para o gerenciamento de informações de
contexto, criando macros responsáveis pelo controle da pilha de Tags.
A macro 'SCOPED_TAG' insere uma ou mais Tags na pilha de contexto e faz a remoção no
fim do escopo corrente. A utilização desta macro diminui o esforço de instrumentação do
código, uma vez que retira do programador a responsabilidade de remover as Tags no fim de
cada escopo. Como exemplo, a utilização desta macro em um método:
-(void) functionTest {
SCOPED_TAG('operation', 'test');
...
}
Como mencionado anteriormente, o método de notificação é responsável pela criação e envio
de eventos a um servidor central. No entanto, o processo de envio deve considerar algumas
características dos componentes do sistema, uma vez que optamos pela centralização de
eventos. Um ponto importante é como este processo pode ser estruturado na presença de
entidades ubíquas, como é o caso do aplicativo sendo executado nos iPads.
É importante considerar que sistemas ubíquos costumam ser mais suscetíveis a quedas de
energia. Este fato nos remete ao problema de manter eventos em memória esperando o
momento de transmissão, pois uma vez que haja uma queda de energia, as informações da
memória do dispositivo serão completamente perdidas.
Para solucionar estes problemas definimos uma política de envio de eventos a ser
implementada em bibliotecas de sistemas ubíquos. Esta política consiste no envio de
pequenos pacotes, lidos diretamente do disco em um modelo produtor-consumidor.
Cada evento criado é imediatamente gravado no arquivo corrente, e quando este arquivo
atinge um tamanho limite, é fechado e marcado para envio. Em caso de queda de luz, ao
reiniciar a aplicação, o log gerado continua sendo escrito no último arquivo. Outra
consequência desta abordagem é a garantia que em um desastre, como um acesso inválido de
memória, as informações da falha estão salvas antes da aplicação fechar.
Python
Diferentemente de Objective-C [Cox B. 1986], Python é uma linguagem interpretada,
portanto, não é possível utilizar diretivas de pré-compilação neste contexto. Porém,
Departamento de Informática
linguagens interpretadas fornecem outras formas de enriquecer o processo de utilização desta
biblioteca.
Assim como a implementação de Objective-C [Cox B. 1986], criamos uma forma de retirar a
responsabilidade dos programadores quanto à remoção de Tags do contexto de execução. Para
isso, utilizamos decorators de Python [Rossum, G. van 1991] que nos permitem que sejam
criados envelopes [Büchi, M., Weck W. 2000] para funções que inserem Tags de contexto
antes do método e as retiram depois dele. Dessa maneira, a chamada de uma função seria
escrita da seguinte forma:
@scoped_tag(['operation':'test'])
def functionTest:
…
O processo de notificação desta biblioteca é bastante simples, se comparada à solução tomada
em Objective-C [Cox B. 1986]. Uma vez que a biblioteca Python [Rossum, G. van 1991] será
utilizada em sistemas web, podemos considerar que elas possuem um alto grau de
disponibilidade. Partindo deste pressuposto, o processo de envio de informações de contexto é
realizado diretamente, pois não há necessidade de armazenamento intermediário. Assim
sendo, os eventos são enviados, um por vez, através de requisições.
Testes
As bibliotecas possuem testes automatizados. O processo de verificação da corretude consiste
na comparação entre eventos de log gerados e seus valores esperados, que são a forma
serializada dos respectivos eventos.
No caso da biblioteca para a linguagem Objective-C [Cox B. 1986], os testes automáticos não
possuem cobertura total por não considerarem o processo de envio de eventos. A criação de
testes para envio de eventos neste contexto seria difícil, uma vez que os dispositivos que
utilizam esta linguagem não possuem acesso de leitura ao servidor de armazenamento. Já a
biblioteca para a linguagem Python [Rossum, G. van 1991] pode ser testada automaticamente
em sua totalidade.
Manipulação de informações de contexto
O armazenamento de eventos é realizado em um servidor central responsável por interpretar
as informações recebidas e fazer alguns ajustes, como por exemplo, a normalização de
identificadores temporais. Considerando que cada máquina no sistema possui seu próprio
relógio, que eventualmente está diferente do relógio no servidor central, é necessário
converter os logs recebidos para o relógio do servidor central. Solucionamos este problema
enviando junto com os eventos o timestamp corrente de cada máquina, e a partir dele
calculamos o delta temporal ajustando a coerência dos relógios.
O armazenamento de eventos realizado no servidor central utiliza um banco de dados NoSQL
[Stonebraker, M. 2010] devido a forma como estruturamos o evento. Este tipo de banco de
dados armazena cada entrada em um formato de tuplas, semelhante a nossa representação de
eventos e utiliza algoritmos de busca otimizados para este tipo de estrutura. Além disso, este
Departamento de Informática
tipo de banco de dados pode ser distribuído, e, considerando o problema de volume de
eventos, este recurso combinado a estratégias de computação na nuvem podem resolver o
problema de uma forma simples e de baixo custo.
Inspeção do sistema-alvo
Uma vez que os eventos estejam armazenados, é necessário que possamos visualizá-los
facilmente. Porém, criar uma forma de visualização adequada pode não ser uma tarefa fácil,
uma vez que a quantidade de dados armazenados é muito grande. Portanto, é interessante que
se utilize o conhecimento prévio da estrutura das informações armazenadas de forma a propor
mecanismos de busca de informações que facilitem a vida do usuário. Seguindo esta idéia,
projetamos uma ferramenta de inspeção que permite que o usuário defina um contexto de
visualização de eventos através de conjuntos [Chave, Valor].
Ferramenta de inspeção
A ferramenta de inspeção possui um mecanismo de filtros com base na estrutura de eventos
que possibilita que sejam definidos dois conjuntos [Chave, Valor]: o conjunto de Tags que
devem estar presentes e o conjunto de Tags que não devem estar presentes em um evento para
que ele seja exibido. A ferramenta também possui os campos Desde e Até nos quais entramos
com limites para um intervalo de data e hora que funciona como um filtro temporal. Dessa
maneira, os dados são filtrados de acordo com as informações de contexto presentes nos
eventos e assim, apenas os eventos que satisfaçam ao contexto definido serão visualizados.
Assim como o número de eventos armazenados, as informações de contexto presentes nos
eventos também podem ser extensas, pois um mesmo evento pode conter informações
interessantes para diferentes perfis de depuração. Como exemplo, temos o seguinte evento:
[environment:mobile] [application:hello world] [cpu:300] [memory:1024] [flow:main]
[message:Main screen loaded]
Este evento pode ser interessante tanto para visualização de desempenho da aplicação, através
das Tags cpu e memory, quanto para a visualização do fluxo de acesso de telas do sistema.
Pensando nisso, criamos um mecanismo de configuração de visualização de Tags. Este
mecanismo nos permite definir se uma Tag será ou não visualizada ou mesmo se apenas sua
chave ou valor aparecerá. Este mecanismo permitirá que apenas as informações de contexto
relevantes para o problema em questão sejam visualizadas.
Além disso, a ferramenta de inspeção possui uma opção chamada Atualização automática que
ao ser marcada, faz com que a tela de visualização de eventos seja atualizada conforme a
chegada de novos eventos no servidor central. A atualização do conjunto de eventos
visualizados também segue o contexto de filtros definido pelos conjuntos de Tags, ou seja,
apenas eventos que chegam no servidor central e se enquadram no contexto definido pelos
filtros são adicionados à visualização. A seguir, uma imagem da ferramenta, apresentando as
funcionalidades listadas:
Departamento de Informática
Figura 4 - Ferramenta de apoio à inspeção
Casos de uso
A seguir, apresentamos o funcionamento da ferramenta de inspeção através de um diagrama
de casos de uso.
Figura 5 – Diagrama de casos de uso referente à ferramenta de inspeção.
Departamento de Informática
Funcionalidades
A seguir, forneceremos instruções para de cada componente da ferramenta da inspeção.
Filtro temporal
O filtro temporal é composto por dois campos que permitem definir limites temporais para os
eventos. A seguir, uma imagem deste filtro:
Figura 6 - Componente de filtro temporal
Data e hora devem ser fornecidas no formato yyyy-mm-dd HH:MM:SS, como pode ser visto
na imagem acima. Os botões à direta dos campos fornecem data e hora correntes nos
respectivos campos. Uma vez que as informações sejam alteradas, a aplicação do filtro deve
ser realizada com a tecla Enter.
Filtro de Tags
Este componente do sistema nos permite aplicar filtros que restringem os eventos a serem
apresentados na tela. Para utilizar este filtro é necessário fornecer uma Chave e,
opcionalmente, um valor. Em seguida, definimos se o filtro criado será para inclusão ou
exclusão de Tags através da seleção das opções Incluir ou Excluir. No caso da seleção da
opção Incluir, apenas eventos que possuem Tags com Chave e Valor fornecidos para este
filtro serão apresentadas. No caso da seleção de Excluir, eventos que possuírem Tags com
Chave e valor fornecidos serão removidas a apresentação. O campo Regex possibilita a
criação de filtros com expressões regulares para o campo Valor de Tags. Uma vez que o filtro
seja criado através do botão Inserir, ele será apresentado em uma lista de filtros logo abaixo e
poderá ser removido a qualquer momento, através do botão em forma de x que se encontra à
direta de cada elemento. A seguir, uma imagem do filtro de Tags:
Departamento de Informática
Figura 7 - Componente de filtro de Tags de contexto
Esta imagem apresenta a interface gráfica referente ao componente de filtro de Tags. Neste
caso, há um filtro definido com chave environment e sem valor, o que faz com sejam exibidos
apenas eventos que possuem Tags cuja chave é igual a environment.
Configurações de visualização
Este componente nos permite definir quais Tags serão apresentadas nos eventos ou mesmo se
apenas suas chaves ou valores serão apresentados. Para inserir uma configuração, basta
fornecer a chave que identifica as Tags que serão alteradas e selecionar o que se deseja
visualizar marcando apenas a opção Chave, apenas a opção Valor ou ambas. A seguir, uma
imagem do componente de configurações:
Figura 8 - Componente de configurações de visualização de Tags
Visualizador de eventos
Uma vez que filtro temporal e filtro de Tags estejam definidos, os resultados serão
apresentados em um componente de visualização de eventos. Como os eventos são
organizados através de Tags, este componente apresenta um esquema de cores que busca
associar Tags com mesma chave afim de facilitar o acompanhamento de informações de
contexto no tempo. Os eventos são organizados de forma temporal, do mais recente para o
mais antigo e seus identificadores temporais são apresentados à esquerda. À direita, temos a
mensagem de cada evento. Eventos que possuem informações adicionais possuem um botão +
à direita da mensagem, que dá acesso as informações que não estão presentes no contexto
atual. A seguir, uma imagem do componente de visualização:
Departamento de Informática
Figura 9 - Componente de apresentação de eventos.
Neste caso, um evento com Tag “Error” é apresentado e sua mensagem fornece informações
sobre o tipo de falha ocorrida. Este tipo de evento possuem a stackTrace como informação
adicional, que pode ser acessada através do botão à direita.
Implementação
A ferramenta de inspeção foi desenvolvida em um sistema web, portanto utilizou
conhecimentos de linguagens de estruturação e apresentação de informações, como HTML
[Graham 1995]. A dinâmica de apresentação de informações foi desenvolvida utilizando
Javascript [Powell, T., Schneider, F., 2001], AJAX [Powell, T. 2008] e Jquery [Paddock R.,
Petersen J. 2012]. O backEnd foi desenvolvido em Python [Rossum, G. van 1991] através de
um servidor Django [FORCIER, J. et al 2008] e utilizamos recursos de JSON [Crockford, D.
2012] para facilitar a transmissão de dados entre cliente e servidor.
Como estamos tratando de sistemas distribuídos que possuem componentes com baixo nível
de disponibilidade, devemos considerar que os eventos podem não estar presentes no banco
de dados quando estamos tentando identificar a causa de uma falha. Suponha que um usuário
reportou um erro e o desenvolvedor responsável pela manutenção do sistema está analisando
o contexto, mas os logs referentes a falha ainda não foram transmitidos devido a problemas de
conexão. Enquanto o desenvolvedor está estudando o comportamento do sistema, restrito
aquele cliente, a rede volta a funcionar e o dispositivo envia os logs dos seus últimos minutos
de execução. Neste momento, a visualização fica inconsistente, pois não expressa o que
realmente existe no banco de dados.
Para solucionar este problema, projetamos uma solução que consistia em refazer a busca de
eventos de acordo com os filtros configurados, para que a tela fosse reconstruída de tempos
em tempos. Entretanto, esta abordagem mostrou ser muito custosa, uma vez que além de
refazer as buscas, é necessário recriar todos os elementos da interface gráfica de acordo com
os eventos recebidos. Como consequência, projetamos uma solução de implementação
baseada na técnica Divisão e Conquista [CORMEN, T. H. et al 2000]. A solução consiste na
criação de conjuntos de eventos conforme um intervalo de tempo, cada conjunto armazena o
número de eventos que contém. A cada 5 segundos, fazemos uma requisição ao servidor de
banco de dados fornecendo os limites temporais de todos os conjuntos e obtemos como
resposta o número de eventos em cada intervalo. Neste momento, comparamos o número de
eventos do banco de dados com o número de eventos de cada intervalo apresentado na tela e
reconstruímos os intervalos inconsistentes.
Avaliação de resultados
O mecanismo de depuração desenvolvido foi implantado em um sistema real conforme
apresentado na seção Aplicação de Avaliação. Partindo deste cenário, medimos o esforço de
Departamento de Informática
implantação do método no sistema. O processo de instrumentação contou com dois
desenvolvedores que participaram da construção do sistema-alvo e durou cerca de dois dias.
A ferramenta de inspeção está sendo utilizada pela equipe há aproximadamente 2 semanas,
tanto para monitoramento do sistema quanto para a depuração de falhas. Até o presente
momento, apenas uma falha foi reportada para a equipe e pode ser identificada por um
desenvolvedor em aproximadamente 15 minutos. De acordo com o gerente desta mesma
equipe, era comum que falhas deste tipo necessitassem de pelo menos um dia de depuração
até que sua causa fosse identificada.
Além disso, estruturamos um estudo de caso para uma avaliação quantitativa da ferramenta.
[ARAÚJO, T. et al]
Considerações finais
Apresentamos neste trabalho um mecanismo de depuração para sistemas distribuídos baseado
em logs estruturados com informações de contexto. Estes logs são armazenados em um
servidor central e podem ser visualizados através de uma ferramenta de inspeção. O
mecanismo apresentado mostrou-se eficaz no apoio à depuração, uma vez que diminuiu o
tempo de identificação de uma falha em um sistema real.
Trabalhos futuros
Existem alguns aspectos relacionados ao trabalho apresentado que podem originar trabalhos
futuros.
O primeiro ponto a ser considerado é a distribuição do banco de dados utilizado. O banco de
dados MongoDB [MONGODB 2007] é estruturado visando sua distribuição, portanto prevê
sua organização de acordo com arquiteturas que podem não só fornecer maior confiabilidade
no armazenamento, mas também, aumentar a velocidade de acesso às informações
armazenadas.
Outro ponto a ser considerado é a implementação de mecanismos de interceptação de eventos
gerados pelo sistema operacional dos componentes, como é o caso de um acesso inválido à
memória. Este componente seria útil para o processo de instrumentação, uma vez que ajudaria
a fornecer mais informações acerca da falha ocorrida em sistemas ubíquos.
Referências
1 - ARAÚJO, T.; WANDERLEY, C.; STAA, A.V.; Um mecanismo de introspecção para depurar o
comportamento de sistemas distribuídos. In: 26º. Simpósio Brasileiro de Engenharia de Software, Natal, RN;
setembro 2012. Aceito para publicação.
2 - APPLE, (2007) iOS Disponível em: http://www.apple.com/ios/ Acesso em: 12 jun 2012
3 - AUERBACH J. S., RUSSELL J. R. (1994). The Concert signature representation: IDL as
intermediate language. In Proceedings of the workshop on Interface definition languages (IDL '94),
Jeanette M. Wing and Richard L. Wexelblat (Eds.). ACM, New York, NY, USA, 1-12.
DOI=10.1145/185084.185095 http://doi.acm.org/10.1145/185084.185095
Departamento de Informática
4 - BOEHM, B., BASILI, V. R. (2001). Software defect reduction top 10 list. Computer, 34:135–
137.
5 - BÜCHI, M., WECK W. (2000), ECOOP 2000 — Object-Oriented Programming – pg.201 - 225
6 - CLEMENTS, P.; NORTHROP, L.. Software Product
Lines: Practices and Patterns. Addison-Wesley, Boston, MA, EUA, (2002).
608 p.
7 - COX, J. B., NOVOBILSKI, A., (1991). Object-Oriented Programming; an Evolutionary
Approach (2nd ed.). Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA.
8 - CROCKFORD, D. (2002). Introducing JSON, Disponível em: http://www.json.org/ Acesso em:
12 jun 2012.
9 - DAO D., ALBRECHT J., KILLIAN C., VAHDAT A. (2009). Live Debugging of Distributed
Systems. Compiler Construction - pg. 94-108
10 - FORCIER, J.; BISSEX, P.; CHUN, W. (2008), Python Web Development with Django (1st
ed.), Addison-Wesley, p. 408, ISBN 0-13-235613-9, OCLC 213835556
11 - CORMEN, T. H., LEISERSON, C.E., RIVEST R. L. (2000), Introduction to Algorithms MIT
Press.
12 - GRAHAM, I. (1995). The HTML Sourcebook. John Wiley & Sons, Inc., New York, NY, USA
13 - GEELS, D., ALTEKAR, G., MANIATIS, P., ROSCOE, T., STOICA, I. (2007). Friday: Global
Comprehension for Distributed Replay. In: Proceedings of the ACM/USENIX Symposium on
Networked Systems Design and Implementation (NSDI)
14 - ISO 9126 (1991) Disponível em:
http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=22749 Acesso
em:12 jun 2012.
15 - KILLIAN, C.E., ANDERSON, J.W., JHALA, R., VAHDAT, A. (2007). Life, Death, and the
Critical Transition: Finding Liveness Bugs in Systems Code. In: Proceedings of the
ACM/USENIX Symposium on Networked Systems Design and Implementation (NSDI)
16 - LIU, X., GUO, Z., WANG, X., CHEN, F., LIAN, X., TANG, J., WU, M., KAASHOEK, M.F.,
ZHANG, Z. (2008). D3S: Debugging Deployed Distributed Systems. In: Proceedings of the
ACM/USENIX Symposium on Networked Systems Design and Implementation (NSDI)
17 - MONGODB (2007), Disponível em: http://www.mongodb.org/ Acesso em: 12 jun 2012.
18 - PADDOCK R., PETERSON J. (2012). Practical Jquery (1st ed.). Apress, Berkely, CA, USA.
19 - PITAC, P. I. (1999). Information Technology Research: Investing in Our Future. Report to
the President. Technical report.
20 - POWELL, T., SCHNEIDER, F., (2001). Javascript: The Complete Reference. McGraw-Hill
Professional.
Departamento de Informática
21 - POWELL, T. (2008). Ajax: The Complete Reference (1 ed.). McGraw-Hill, Inc., New York,
NY, USA.
22 - REYNOLDS, P., KILLIAN, C.E.,WIENER, J.L.,MOGUL, J.C., SHAH, M.A., VAHDAT, A.
(2006). Pip: Detecting the Unexpected in Distributed Systems. In: Proceedings of the
ACM/USENIX Symposium on Networked Systems Design and Implementation (NSDI)
23 - ROSSUM, G. VAN (1991). Python Disponível em: http://python.org/ Acesso em:12 jun 2012.
24 - STONEBRAKER, M. (2010). SQL databases v. NoSQL databases. Commun. ACM 53, 4 (April
2010), 10-11. DOI=10.1145/1721654.1721659 http://doi.acm.org/10.1145/1721654.1721659
25 - THOMAS, D. A. (2002). The deplorable state of class libraries. Journal of Object Technology,
1(1):21–27.