Upload
others
View
4
Download
0
Embed Size (px)
Citation preview
14/11/2016
1
Alessandro GarciaLES / DI / PUC-Rio
Novembro / 2016
Aula 17Tratamento de Exceções e
Assertivas
Sumário
• Definições básicas
– Como se encerram as funções?
– Problema: e quando funções não encerram corretamente?
• Tratamento de exceções
– Tipos de tratadores
– Como tratar exceções
• Em C
• Em linguagens de programação contemporâneas
1
14/11/2016
2
Referência
• Cap. 8 do livro Programação Modular
• Leituras complementares:
– Artigos
• “Discovering Faults in Idiom-Based Exception Handling”, Bruntink, M., van Deursen, A. and Tourwé, T. In Proceedings of 28th ICSE, 2006.
– Tutorial sobre tratamento de exceções em Java
• http://download.oracle.com/javase/tutorial/essential/exceptions/index.html
2
Relembrando - Definições básicas
• O que é uma função?
– Função é uma porção auto-contida de código que:
• possui um nome que pode ser chamado (ativado) de diversos lugares
• pode retornar zero ou mais valores
• pode depender de e alterar zero ou mais parâmetros
• pode alterar zero ou mais valores do estado do módulo– variáveis internas
– variáveis globais
• pode criar, ler e destruir arquivos, etc...
• pode terminar de duas formas: normalmente ou exceção
TIPO_RETORNO NOME_FUNCAO ( LISTA_PARAMETROS){
CORPO_FUNCAO}
3
14/11/2016
3
Definições básicas
• Encerrando a execução de uma função:
– Chegar ao fim de uma função void
– O comando return <VALUE>
• Encerra a execução de uma função imediatamente
• Se um valor de retorno é informado, a função chamada (callee) retorna este valor para a função chamadora (caller)
• A transferência de controle após o return passa da função chamada para a função chamadora
– O comando exit(int)
• Encerra a execução do programa1. Executa em ordem reversa todas as funções registradas pela função
int atexit( void (*func)(void) )
2. Todos arquivos são fechados, todos arquivos temporários são apagados
3. O controle de execução retorna ao ambiente-hospedeiro (host
enviornment) o valor inteiro passado como argumento
4
Abril 2009 5 /30© LES/DI/PUC-Rio
O que são assertivas?
• Assertivas são condições envolvendo dados e estados manipulados que devem ser satisfeitas em determinadas partes do algoritmo
– de preferência, deve ser código (ao invés de comentários)
• São definidas em vários níveis de abstração
– funções
• devem estar satisfeitas em determinados pontos do corpo da função
• usualmente assertivas de entrada e assertivas de saída– pré e pós condições
– na definição das pós-condições, assume-se que as pré-condições foram satisfeitas
– módulos
• devem estar satisfeitas ao entrar e ao retornar de todas funções
• assertivas invariantes: – assertivas estruturais: corretude das instâncias de estruturas de dados
14/11/2016
4
Exceptions
Exceções
Eventos inesperados que ocorrem na
execução do módulo, o impedindo de
continuar o fluxo de execução normal
Press any key to continue _
Exemplos de exceções
• Exemplos:
– Um valor de um dos parâmetros ou variáveis viola assertivas
de entrada, saída
• Ex. Um parâmetro de entrada ocasiona uma divisão por zero
– Uma das assertivas estruturais é violada
– Não há memória disponível quando você tenta alocar memória dinamicamente
– Hardware para de funcionar corretamente
7
14/11/2016
5
Tratamento de exceções
• Por que é importante tratar exceções?
– Você pode irritar o seu usuário
8
Tratamento de exceções
• Por que é importante tratar exceções?
– Robustez: reagir adequadamente a eventos anormais
– Usuário pode perder a confiança no seu sistema e deixar de usá-lo
– Segurança: exceções não tratadas representam vulnerabilidades de segurança
• Versões 4.0.4 .. 6: 132 vulnerabilidades de exceções não tratadas foram reveladas
• Podem levar a ataques resultando “Deniel of Service”
• Varios smartphones impactados: Google, Huawei, Lenovo, Samsung, LG, Motorola, HTC and Nubia
9
14/11/2016
6
Tratamento de exceções
• Sistemas atuais são cada vez maiores e mais complexos
– Condições excepcionais podem surgir por diversos motivos e serem até mesmo difíceis de serem previstas
• Sistemas robustos e confiáveis devem prover seus serviços mesmo sob condições excepcionais
– Sistemas críticos:
• Sistemas bancários
• Controladores de redes elétricas, vôo, usinas nucleares...
• Sistemas embarcados em aviões, submarinos, foguetes...
• O tratamento de exceções é parte central do desenvolvimento de sistemas robustos e confiáveis
10
Maio 2009 11 / 32Alessandro Garcia © LES/DI/PUC-Rio
Estrutura de uma função
• Conjunto de exceções sinalizadas na interface
– em C: condições de retorno excepcionais
– Exceções – exs.: nome de arquivo não existe, índice/chave fora do limite, conexão com servidor/banco-dados não pode ser estabelecida, etc...
Tratador
E1
Tratador
E2Tratador
E3Função
Chamadas
Chamadas
14/11/2016
7
Tratamento de exceções - Terminologia
• Exceção: Uma exceção é um evento que ocorre durante a execução de um programa (ou módulo) e que impede a sua execução normal
– Uma exceção indica que o estado interno do sistema está inconsistente e, por isso, não é possível prosseguir com sua execução normal
– Para restaurar a consistência interna do sistema, é necessário tomar ações corretivas, ou remediadoras
• Tratador: Um tratador é um conjunto de ações que visam corrigir ou remediar a ocorrência de uma exceção
• Mecanismo de tratamento de exceções: Um mecanismo de tratamento de exceções é um modelo que permite a desenvolvedores:
– Representar os tipos de ocorrências de exceção
– Indicar a ocorrência de uma exceção na interface de funções/módulos
– Detectar a ocorrência de uma exceção na execução da função
– Estruturar ações de tratamento de exceções
– Desviar o fluxo normal do programa para o fluxo excepcional12
Tratamento de exceções
• Exemplos de ações em tratadores:
– Ignora a exceção: identifica a ocorrência de uma exceção e não toma nenhuma ação corretiva. Má prática e deve ser evitada.
– Re-sinaliza a exceção: identifica um tipo de exceção e reporta para a função chamadora outro tipo de exceção. Geralmente é usada para preservar abstração e encapsulamento.
– Delegação de controle: delega o controle da execução para outro módulo do sistema mais apto a lidar com a exceção.
– Notificação ao usuário: notifica o usuário a ocorrência da exceção, com possibilidade de pedir novo input, caso seja possível. Preferencialmente, somente em casos que usuário deve ser envolvido.
– Armazenamento de erro: cria um registro da exceção e de informações adicionais em arquivo especial (log).
– Nova tentativa: a mesma função é simplesmente invocada novamente. (ex. exceções intermitentes do ambiente – rede WiFi)
13
14/11/2016
8
Tratamento de exceções
• Exemplos de ações em tratadores:
– Liberação de recursos: assegura a liberação de recursos alocados, como memória alocada dinamicamente, arquivos e conexões abertos, etc.
– Preparação para desligar: prepara o sistema para terminar sem causar efeitos colaterais. Geralmente é usada em situações
extremas. É necessário liberar todos os recursos alocados e reverter o sistema para um estado em que os dados estão consistentes.
– Recuperação por retrocesso: desfaz modificações no estado do sistema a fim de restaurá-lo a um estado válido. Comumente usado em sistemas de bancos de dados.
– Recuperação por avanço:
• Uso de redundância de software: ativa uma função com o mesmo propósito.
• Uso de redundância de dados: uso de elementos adicionais da estrutura que permitam navegar de outras formas na estrutura.
14
Como tratar exceções em módulos em C?
• A linguagem C não traz suporte específico para tratamento de exceções
• Fica sob responsabilidade do programador desenvolver uma forma de identificar e tratar exceções do programa
• Existem várias formas de realizar esta tarefa, cada um com seus prós e contras
• Usamos o método mais comum e mais simples de tratamento de exceções em linguagem C...
15
14/11/2016
9
Como tratar exceções em módulos em C?
• A fim de padronizar a identificação e o tratamento de exceções em C, usamos um idioma com padrões/convenções de tratamento de exceções:
– Tipos enum e código de retorno das funções para indicar sob qual condição a função encerrou sua execução
– Se necessário, define-se parâmetros adicionais passados por referência e modifica-se seus valores para informações extra do erro
• Neste idioma, as funções tem duas responsabilidades:
– A função chamada deve usar o comando return para indicar sob qual condição (normal | excepcional) sua execução encerrou
– A função chamadora deve testar o código retornado pela função chamada a fim de tomar ações corretivas, caso necessário
16
Usando o código de retorno das funções
typedef enum {
LIS_CondRetOK ,
/* Concluiu corretamente */
LIS_CondRetListaVazia ,
/* A lista não contém elementos */
LIS_CondRetFimLista ,
/* Foi atingido o fim de lista */
LIS_CondRetNaoAchou ,
/* Não encontrou o valor procurado */
LIS_CondRetFaltouMemoria
/* Faltou memória ao tentar criar um elemento de lista */
} LIS_tpCondRet ;
LIS_tpCondRet LIS_InserirElementoAntes
( LIS_tppLista pLista , void * pValor )
{
tpElemLista * pElem ;
pElem = CriarElemento( pLista , pValor ) ;
if ( pElem == NULL ) {
return LIS_CondRetFaltouMemoria ;
} /* if */
....
return LIS_CondRetOK ;
} /* Fim função: LIS &Excluir elemento */
17
14/11/2016
10
Usando o código de retorno das funções
int main(void){...LIS_tpCondRet condRet = InserirElementoAntes( lista, pValor );
switch( condRet ) {case LIS_CondRetFaltouMemoria:
...case LIS_CondRetOK:
...default :
printf(“Condição de retorno inesperada”);}
}
18
Usando parâmetro passado por referência
int main(void){...char *errorMsg;LIS_tpCondRet condRet = InserirElementoAntes( lista, pValor, &errorMsg );
switch( condRet ) {case LIS_CondRetFaltouMemoria:
printf( “%s”, errorMSG );case LIS_CondRetOK:
...default :
printf(“Condição de retorno inesperada”);}
}
19
14/11/2016
11
Usando parâmetro passado por referência
LIS_tpCondRet LIS_InserirElementoAntes
( LIS_tppLista pLista , void * pValor, char ** errorMsg )
{
tpElemLista * pElem ;
pElem = CriarElemento( pLista , pValor ) ;
if ( pElem == NULL ) {
char str[] = “Não foi possível alocar memória para um novo elemento”;
int size = strlen( str ) + 1;
(*errorMsg) = (char*)malloc( sizeof(char) * size );
memcpy( (*errorMsg), str, size );
return LIS_CondRetFaltouMemoria ;
} /* if */
....
return LIS_CondRetOK ;
} /* Fim função: LIS &Excluir elemento */
20
Usando variáveis globais ou parâmetros
• Este método é complementar ao método de condições de retorno
– Bastante usado na GLIBC, biblioteca padrão do sistema GNU/Linux
• Neste método, as funções tem as seguintes responsabilidades:
– A função chamada deve modificar variáveis globais ou parâmetros passados por referência para indicar sob qual condição (normal | excepcional) sua execução encerrou
– A função chamadora deve testar a variável global, ou o parâmetro passado por referência, a fim de tomar ações corretivas, caso necessário
21
14/11/2016
12
Limitações de C
• A sinalização de uma exceção não é explícita
– Usa-se o comando return, parâmetros passados por referência ou variáveis globais
– Não há distinção entre encerramento sob condição normal ou excepcional
• O sistema não é interrompido no momento em que a exceção ocorre
– Mesmo sabendo-se que o estado é inconsistente
• Como prover mais informações a respeito do problema / exceção?
– Ex.: Qual a severidade? Que condições levaram a esta
ocorrência?
– Uso de parâmetros encoraja quebra de abstração e encapsulamento
22
Limitações de C
• Há um overhead na criação de tipos enumerados para cada módulo
– Para cada módulo é definido um tipo enumerado, mesmo que
representem a mesma condição (EX.: Falta de memória)
• A associação entre as exceções descritas nos tipos enumerados e quais exceções que podem ser levantadas por uma função depende exclusivamente da especificação da função
– Difícil entender o acoplamento excepcional entre funções: quais exceções devem ser tratadas?
• Não há separação textual do código de tratamento de exceção
– Código torna-se rapidamente extenso, complexo e pouco compreensível
23
14/11/2016
13
Usando o código de retorno das funções
• Problemas:– Chamadas encadeadas
de funções podem resultar em uma estrutura muito aninhada que é difícil de compreender, testar e fazer a manutenção
ARQ_tpCondRet leArquivo(){
condRet = OK;
abreArquivo();
se( arquivoAberto() ){
determineTamanhoArquivo();
se( determinouTamanho() ){
aloqueMemoria();
se( alocouMemoria() ){
copieDados();
se( ! copiouDados() ){
condRet = ERRO_COPIAR_DADOS;
}
} senão {
condRet = ERRO_ALOCAR_MEM;
}
} senão {
condRet = ERRO_DET_TAM;
}
fecheArquivo();
se( ! fechouArquivo() ){
condRet = ERRO_FECHAR_ARQ;
}
} senão {
condRet = ERRO_ABRIR_ARQ;
}
}
24
25 / 32Alessandro Garcia © LES/DI/PUC-Rio
Mecanismo de Tratamento de Exceções
• Comportamento exceptional de funções
– diferente de C, Java, C# e C++ e provêem suporte explícito a tratamento de exceções
Facilidades / Mecanismos para
Tratamento de Exceções
ExceçõesTratadores
(Handlers)
Desvio do Fluxo e Busca
de Tratadores
Definição de “Regiões
Protegidas”Interfaces
Excepcionais
14/11/2016
14
Linguagens contemporâneas
• Linguagens como Java, JavaScript, C++, C#, Python ... proveem mecanismos de tratamento de exceções implementados na própria linguagem
• Elementos sintáticos específicos para tratamento de exceções:
– THROW – sinaliza a ocorrência de uma exceção
– TRY – define uma região protegida contra a ocorrência de exceções
– CATCH – define um tratador, i.e., um trecho de código que implementa um conjunto de ações de recuperação
26
Sinalização explícita de exceções
• Cláusula throw sinaliza a ocorrência de uma exceção
static void escreveArquivo(Arquivo a) ... {
...
Buffer bf = buscaArquivo( a );
if( bf == null )
throw new FileNotFoundException();
}
27
14/11/2016
15
Maio 2009 28 / 32Alessandro Garcia © LES/DI/PUC-Rio
Estrutura de uma função em Java/C++
• Exceções são levantadas/sinalizadas explicitamente
int funcaoX ( ...) {
// código desprotegido
try {
// código a ser tentado; região protegida
if (x > 10) {throw excecao};
}
catch (Excecao)
{
// código do tratador: a ser executado
// em caso de exceção
if (x > 10) {throw excecao2};
}
}
acoplamento:exceção
sinalizada
Linguagens contemporâneas
• Linguagens como Java, JavaScript, C++, C#, Python ... provêem mecanismos de tratamento de exceções implementados na própria linguagem
• Elementos sintáticos específicos para tratamento de exceções:
– TRY – define uma região protegida contra a ocorrência de exceções
– CATCH – define um tratador, i.e., um trecho de código que implementa um conjunto de ações de recuperação
– THROW – sinaliza a ocorrência de uma exceção
– THROWS – especifica na interface de uma função quais as possíveis exceções que podem ser ocasionadas durante a execução daquela função do módulo
29
14/11/2016
16
Acoplamento excepcional explícito
• Cláusula throws indica quais exceções podem ocorrer durante a execução de uma função
void escreveArquivo(Arquivo) throws
FileNotFoundException,
CharCodingException,
PermissionException;
30
Linguagens contemporâneas
• Linguagens como Java, JavaScript, C++, C#, Python ... provêem mecanismos de tratamento de exceções implementados na própria linguagem
• Elementos sintáticos específicos para tratamento de exceções:
– TRY – define uma região protegida contra a ocorrência de exceções
– CATCH – define um tratador, i.e., um trecho de código que implementa um conjunto de ações de recuperação
– THROW – sinaliza a ocorrência de uma exceção
– THROWS – especifica na interface de uma função quais as possíveis exceções que podem ser ocasionadas durante a execução daquela função do módulo
– FINALLY – define um trecho de código que sempre será executado, mesmo quando exceções ocorrerem
31
14/11/2016
17
Mecanismo para ““““código de limpeza””””
• O código definido no bloco finally sempre será executado, seja após a terminação normal, ou após a terminação excepcional, de um bloco try
– Usado especialmente para liberação de recursos, como memória, arquivos abertos, conexões abertas, etc
leArquivo(){try{
abreArquivo();determineTamanhoArquivo();aloqueMemoria();copieDados();
} catch( abrirErro ){...}catch( determinarTamanhoErro ) {...} catch( alocarMemoriaErro ) {...}catch( copiarDadosErro ) {...}finally {
try{fecheArquivo();
} catch( fecharArquivoErro ){...}}
}
32
Melhor separação textual
ARQ_tpCondRet leArquivo(){condRet = OK;abreArquivo();se( arquivoAberto() ){
determineTamanhoArquivo();se( determinouTamanho() ){
aloqueMemoria();se( alocouMemoria() ){
copieDados();se( ! copiouDados() ){condRet = ERRO_COPIAR_DADOS;
}} senão {
condRet = ERRO_ALOCAR_MEM;}
} senão {condRet = ERRO_DET_TAM;
}fecheArquivo();se( ! fechouArquivo() ){
condRet = ERRO_FECHAR_ARQ;}
} senão {condRet = ERRO_ABRIR_ARQ;
}}
leArquivo(){try{
abreArquivo();determineTamanhoArquivo();aloqueMemoria();copieDados();
} catch( abrirErro ){...}catch( determinarTamanhoErro ) {...} catch( alocarMemoriaErro ) {...}catch( copiarDadosErro ) {...}finally {
fecheArquivo();}
}
33
14/11/2016
18
Linguagens contemporâneas
• Principais vantagens em relação a C:
– Redução do aninhamento de estruturas if-then-else
– Melhor separação textual entre o código que implementa a lógica da aplicação e o código que implementa o tratamento de exceções
– Também há uma clara distinção entre o encerramento
normal de uma função ( comando return ) e o encerramento
excepcional ( comando throw )
– Tipos de exceção podem ser reutilizadas entre diferentes módulos
– Mecanismos que garantem a execução de determinados
trechos de código tanto em situações normais, quanto
em situações excepcionais
• Blocos finally
– Liberação de recursos
34
Linguagens contemporâneas
• Mesmo com os mecanismos de tratamento de exceções providos na própria linguagem, código de tratamento de exceções ainda é fonte de erros:
– Dificuldade em modificar métodos que tratam exceções
– Tratadores vazios (catch block vazio)
– Exceções não tratadas
• Erros no código de tratamento de exceções são de difícil diagnose
– Código pouco executado e pouco testado
• Implementar tratamento de exceções não é trivial e requer esforço extra dos desenvolvedores
35
14/11/2016
19
FIM
Alessandro GarciaLES / DI / PUC-Rio
Novembro / 2016
Aula 17Tratamento de Exceções e
Assertivas