blog do Zé

30/7 de 2010

A abominável strtok()

Categoria: C — jfonseca @ 2:24 am

A função strtok acompanha todos os compiladores ANSI C, e serve para dividir uma string em diversas cadeias menores. Um exemplo seria separar os campos de um registro no formato “campo a:campo b:campo c”, uma linha de dados tendo colunas divididas pelo caractere “:”.

Existem diversos motivos para evitar esta função a qualquer custo. Tive problemas recentes com o strtok(), e decidí compartilhar aí pelo menos 3 razões para evitar strtok(). São os chamados “efeitos colaterais” de funções “legadas” da biblioteca C.

Lá vão, 3 motivos para você evitar strtok():

1) A função não é reentrante. Você a chama a primeira vez utilizando a cadeia de caracteres que deseja dividir, e as chamadas seguintes passando um ponteiro nulo. Se alguma outra parte de seu programa utilizar a função strtok, a sequência inicial será comprometida.

2) strtok() alter a cadeia de caracteres que está sendo dividida – sem te avisar. Este motivo justifica não utilizar strtok() em hipótese alguma.

3) A string de origem, sendo dividida, não pode ser constante, deve ser um bloco de memória alocado e que comporte as alterações que a função efetua na cadeia original.

Os dois seguintes trechos ilustram na prática estes 3 defeitos. Aliás, talvez este seja um post inútil, visto que você não deve usar strtok(). No entanto é um exemplo de função padrão mal implementada, presente em 100% das bibliotecas C do mundo, o que provavelmente representa praticamente 100% de todos os computadores em funcionamento no mundo hoje*.

Este programa gera um Bus Error ou Segmentation Fault, dependendo de sua implementação:

/*
 ============================================================================
 Name        : strtok.c
 Copyleft  : ZeFonseca.com
 Description : Demonstra efeitos colaterais de strtok()
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {

	char *tok = NULL;

        // temp é uma cadeia estatica, e o espaço alocado em memória tem
        // apenas os caracteres necessários para
        // armazenar todos os caracteres, mais um caractere nulo não visível na mesma
	char *temp = "name:address:telephone:email";
       // somamos um para acomodar o caractere nulo ao final da cadeia
	int dll = strlen(temp) + 1; 

	tok = (char*)calloc(dll, 1);

	if ( tok == NULL ) {
		fprintf(stderr, "Unable to allocate %u bytes for token.\n", dll);
		return EXIT_FAILURE;
	}

       // primeira chamada de strtok() gera Bus Error, o programa não passará daqui
	tok = strtok(temp, ":");
        // a linha acima ilustra os problemas descritos nos itens 2 e 3 acima
	printf("%s\n", tok);
	fflush(stdout);

	while ( (tok = strtok(NULL, ":")) !=  NULL ) {
                // strtok é chamada com um ponteiro nulo a partir da 2a chamada
                // assim vemos que, caso qualquer outra função
                // chame strtok() em outra thread
                // a proxima iteracao deste loop retornara um valor sem sentido
                // aqui ilustramos o ponto 1 dos problemas de strtok()
		printf("%s\n", tok);
		fflush(stdout);
	}

	return EXIT_SUCCESS;
}

Ao tentar manipular a string temp, strtok gera um Bus Error ou Segmentation Fault. O motivo da falha pode não ser óbvia de inicio: strtok() está tentando manipular a cadeia original, e não uma cópia dela! strtok() insere caracteres nulos nos locais onde encontra a divisão da cadeia procurada. A cadeia original não é uma área dinamicamente alocada, e sim uma área estática de memória. Quando strtok() tenta alterá-la, o sistema operacional encerra o processo.

A seguinte versão corrige este problema, alocando um espaço muito maior que o necessário e trabalhando com uma cópia da cadeia original. Como podemos perceber, este programa está longe de ser eficiente, terminamos com 3 cópias da mesma informação ao final do processo: a string original, uma cópia para trabalho, e as substrings contendo os trechos buscados, caso existam(somadas formam a cadeia original).

/*
 ============================================================================
 Name        : strtok2.c
 Copyleft  : ZeFonseca.com
 Description : Desvia de efeitos colaterais de strtok() criando um frankenstein
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STR_BUFF_SIZE 1024

int main(void) {
        // copiaremos a string para ca e dividiremos esta copia atraves de strtok()
	char *dataline  = NULL;
        // guardaremos a versao original para comparar posteriormente
	char *dataline2 = NULL;
	char *tok = NULL; // armazenara cada token obtido
	char *temp = "name:address:telephone:email"; // cadeia original para testes
        // comprimento da string + 1 para acomodar o byte nulo \0
	int dll = strlen(temp) + 1; 

	dataline = (char *)calloc(STR_BUFF_SIZE, 1);
	memcpy(dataline, temp, dll);

	dataline2 = (char *)malloc(STR_BUFF_SIZE);
	tok = (char*)calloc(STR_BUFF_SIZE, 1);

	if ( tok == NULL ) {
		fprintf(stderr, "Unable to allocate %u bytes for token.\n", dll);
		return EXIT_FAILURE;
	}

	memset(dataline2,0,dll);
	memcpy(dataline2, dataline, dll);

	printf("string 1 before: %s\n", dataline);
	printf("string 2 before: %s\n", dataline2);
        // strtok() nao causa Bus Error, pois dataline
        // tem espaço suficiente para acomodar
        // os efeitos colaterais de strtok()
	tok = strtok(dataline, ":"); 

	printf("%s\n", tok);
	fflush(stdout);

	while ( (tok = strtok(NULL, ":")) !=  NULL ) {
		printf("%s\n", tok);
		fflush(stdout);
	}

        // como strtok insere nulos na string,
        // este printf mostrara apenas o primeiro campo
	printf("string 1 after: %s\n", dataline);
        // a string original sera totalmente impressa
	printf("string 2 after: %s\n", dataline2); 

	return EXIT_SUCCESS;
}

Qual a alternativa? Diversos frameworks e bibliotecas fornecem alternativas seguras à strtok(). Procure evitar implementações da Microsoft(strtok_s) e outras variações como strtok_r. Em vez disso, use uma biblioteca bem implementada e bem testada como a glib.

* Praticamente todos os sistemas operacionais de ampla utilização são escritos em C e vão ligados à biblioteca padrão.


 

22/7 de 2010

Como visualizar quais Virtual Hosts o Apache está atendendo no momento

Categoria: Linux,Perl,UNIX — jfonseca @ 12:19 am

Caso você administre um servidor Apache com dezenas ou centenas de Virtual Hosts, possivelmente já terá se deparado com picos de visitação que, normalmente, testam os limites do servidor. Caso possua apenas um site hospedado por servidor, esqueça o resto deste artigo. No entanto, na grande maioria dos ambientes de hospedagem, um servidor dedicado terá inúmeros hosts virtuais configurados, e é praticamente impossível saber, desconhecendo a origem do pico de visitação, qual é o responsável pela sobrecarga.

Para resolver este problema criei o seguinte script Perl que, combinado ao tcpdump, analisa o tráfego de rede atual, extraindo os cabeçalhos “Host:” e contando quantas vezes cada Host surge no tráfego capturado. Um handler do sinal SIGALRM imprime ao STDOUT as estatísticas atuais a cada 5 segundos, em ordem decrescente de demanda.

Assim, é possível ver, em tempo real, qual Virtual Host está gerando a maior demanda no servidor naquele instante.

Copie o seguinte script e salve como tcpd_host_filter.pl

#!/usr/bin/perl

# http://zefonseca.com/blogs/ze/

use strict;
use warnings;

our %hosts = ();
our $match_count = 0;

$SIG{ALRM} = \&dump_stats;

alarm(5);

while (<>) {
        if ( m{Host\:\s+(\S+)}gms ) {
                $hosts{$1}++;
                $match_count++;
        }
}

sub dump_stats {
        print "\n\nACTIVE HOSTS\n";
        foreach my $host ( reverse sort { $hosts{$a} <=> $hosts{$b} } keys %hosts ) {
                my $ratio = $hosts{$host} / $match_count;
                printf "%-5d %-32s %.2f %% \n", $hosts{$host}, $host, $ratio*100;
        }
        alarm(5);
}

Torne o script executavel:

chmod 755 tcpd_host_filter.pl

Agora rode o tcpdump com as opções -s 65535, que determina o tamanho máximo de captura, e -w – que escreve todo o conteúdo do pacote capturado à saída padrão.

tcpdump -s 65535 -w - | ./tcpd_host_filter.pl

O script lerá a captura em tempo real, no entanto só imprimirá as estatísticas a cada 5 segundos, para evitar uma inundação de dados na tela.

Exemplo de saída:

ACTIVE HOSTS
120   site1.com             30.61 %
116   site2.com                            29.59 %
74    zefonseca.com                    18.88 %
29    site3.com                        7.40 %
9     site4.com                 2.30 %
8     site5.com               2.04 %
7     site6.com                 1.79 %
7     site7.com                 1.79 %
[ ... ]

 

21/7 de 2010

Gorillas.JS: Versão do clássico GORILLAS.BAS em Javascript + HTML 5

Categoria: Games,Javascript,Programação — jfonseca @ 4:39 pm

Talvez algum leitor se lembre de um programa chamado QBasic que acompanhava os últimos DOS? Corre o boato de que Bill Gates sempre foi apaixonado pela linguagem BASIC e, por este motivo, foi a linguagem adotada pela Microsoft para tantos de seus produtos(Visual Basic, VBA, VBScript, etc). Talvez seja pelo mesmo motivo que sempre houve um interpretador de BASIC embutido nos sistemas operacionais Microsoft. Desde o GW-BASIC dos IBM PC’s, passando pelo QBasic nos últimos DOS e o Windows Scripting Host nos Windows mais recentes.

Um dos programas de demonstração do BASIC, que costumavam acompanhar o DOS, era o GORILLAS.BAS – um jogo onde 2 primatas sobem em edifícios de uma cidade e travam uma guerra de bananas explosivas. Os jogadores devem inserir a velocidade e angulo de lançamento das bananas de acordo com a ventania no momento e, assim, tentar acertar o oponente.

O jogo, bastante simples, marcou uma época no mundo dos PC’s. A primeira versão, com direitos autorais da IBM, foi lançada em 1991. Logo mais, surgiram versões com efeitos especiais e mais variáveis, como a possibilidade de alterar a gravidade, ou de jogar com gorilas “atômicos” em um cenário esquisito.

Ao fim de Junho de 2009, sofrí um acidente e, não tendo nada melhor para fazer, preso à uma cama, decidí tentar reescrever o clássico Gorillas.BAS em Javascript. Mantive o máximo da originalidade, inclusive as cores dos prédios, janelas. O algoritmo do movimento da banana é baseado em meus experimentos com balística.

Portanto, fica aí para quem desejar lembrar do velho DOS: Jogue GORILLAS.JS – Versão de 2010 do clássico de 1991


 

13/7 de 2010

Brincando com HTML 5 e Canvas

Categoria: Dicas para Webmasters,Programação — jfonseca @ 11:58 pm

Buscando aprender mais sobre o HTML 5 e o componente Canvas, realizei um pequeno experimento com trajetórias balísticas.

O script é bastante simples. Após inicializar um canvas com 800×400 pixels, o Prototype dispara o evento dom:loaded. O handler deste evento prepara o canvas com um degradê inteiramente feito via Javascript, e liga o botão “Shoot” em um handler para desenhar no Canvas a trajetória balística de um projétil com a velocidade e o ângulo desejado.

Não há tempo real, portanto não inserí unidades. A velocidade seria m/s na vida real, porém não tendo referência poderiamos criar qualquer unidade. O movimento também recebeu ampliação de 2x no sentido horizontal, de modo a deixar mais harmônico com o canvas 800×400. A variável de proporção pode ser alterada apenas ajustando a largura e altura do canvas no código Javascript. Estas medidas poderiam ser lidas diretamente do canvas, sem serem hard coded no Javascript – fica aí como sugestão.


 

12/7 de 2010

Sybase: SQL Server no Linux em 1998

Categoria: Programação,Velharia — jfonseca @ 2:54 pm

O Sybase SQL Server foi um RDBMS robusto e muito utilizado por empresas à partir da segunda metade dos anos 1980. O produto foi atualizado e hoje leva o nome Adaptive Server

Parte do código-fonte deste RDBMS foi desenvolvido em parceria com a Microsoft. Na segunda metade dos anos 1990, as empresas romperam o contrato de cooperação e seguiram rumos independentes. Dizem os boatos que a Microsoft apenas aderiu à Sybase para obter inteligência na área de bancos de dados empresariais, produto que faltava em sua linha Enterprise.

O código herdado pela Microsoft neste acordo sofreu algumas poucas mudanças e o Sybase foi rebatizado e lançado como MS SQL Server.

De fato, em 1998/99, um “truque” que eu empregava consistia em utilizar drivers Sybase para conectar sistemas UNIX a bancos MS-SQL Server! Nesta época, sistemas Linux não possuiam drivers nativos para MS-SQL. Alguns analistas me perguntavam como isso era possível, e eu explicava o fato do código fonte dos dois sistemas serem praticamente idênticos.

O Sybase era, na minha concepção, um MS SQL-Server pra UNIX e assim o utilizei durante um bom tempo para desenvolver soluções compatíveis com SQL Server – no Linux. Na era pré-virtualização isso tinha gande valor prático.

Hoje a Sybase tem 4000 funcionários e receita de mais de U$ 1 bilhão anuais. O Sybase SQL Server foi desenvolvido e expandido para tornar-se um sistema completo de inteligência nos negócios, tomada de decisões, data warehouse e outras aplicações de bancos de dados.


 

Buscando uma implementação JDO

Categoria: Java,Programação — jfonseca @ 9:38 am

Ao iniciar um novo projeto, é preciso determinar qual será a estratégia para implementação de persistência de dados. Esta decisão é crucial, pois ditará a qualificação e pré-requisitos dos desenvolvedores que o gerente deverá contratar. O sistema escolhido também terá grande influência sobre a produtividade da equipe, o custo total do projeto e, já na parte técnica, esta escolha trará muitas outras consequências que podem determinar o sucesso ou fracasso de um produto de software.

Caso a empresa tenha investido milhões de Reais em licenças de um certo sistema de bancos de dados, caberia perguntar se o driver do framework está sendo mantido contra falhas de segurança, bugs e possuindo todas as atualizações devidas. O framework de persistência está em desenvolvimento ativo? Há suficiente oferta de profissionais especializados neste framework no mercado?

JDO
Eis que, para um determinado sistema ainda no estágio de planejamento, minha escolha foi o JDO para implementar a camada de persistência de objetos.

Sempre trabalhei com Hibernate, no entanto tenho notado maior empenho da Sun(agora Oracle) em favorecer padrões independentes de terceiros. Não tenho qualquer crítica à JBoss, pelo contrário, nunca tive qualquer problema com o Hibernate e continuo fã deste ORM.

Como citado acima, esta escolha terá consequências remotas, tanto na manutenção do sistema quanto no desenvolvimento de versões futuras. Assim, acredito que, neste momento, o JDO é mais seguro em termos de “adoção institucional” pela Oracle, digamos assim.

O grande medo de qualquer gerente de projeto é inserir um COBOL no meio do sistema, um framework ou linguagem que, daqui a 5 ou 10 anos, só será compreendido por uma elite de programadores de alto custo.

Google App Engine
Um fator decisivo na escolha do JDO ao invés de Hibernate ou JPA foi a possibilidade de executar o aplicativo finalizado no Google App Engine.

Gerentes experientes no Brasil e no exterior concordam: atualmente não há melhor relação de custo/benefício que o Google App Engine em termos de computação sob demanda.

Um dos prerequisitos colocados pelo meu cliente é que o sistema deva poder executar transparentemente, ou com poucas modificações, no Google App Engine. Portanto mais um fator à favor do JDO.

Implementações JDO
Tendo escolhido JDO como API de abstração de dados, passo a procurar uma implementação deste padrão que propicie persistência via RDBMS. A implementação da Apache JDO é apenas demonstrativa, e neste momento suporta apenas arquivos comuns como fontes de dados. Não possui, portanto, suporte a RDBMS.

DataNucleus AccessPlatform 2.0
DataNucleus AccessPlatform 2.0Apesar do nome comercial, a implementação DataNucleus adota a licença Apache 2.0, livre de quaisquer amarras legais e autorizada para uso comercial. A implementação aparenta ser bem completa, incluindo suporte para incontáveis fontes de dados, entre arquivos, RDBMS, XML e outros.

Como já era esperado, a lista de dependências do AccessPlatform é imensa. Trata-se de um sistema completo, e complexo, de relação objeto-relacional(ORM). Como este tipo de aplicativo costuma ser empregado em estações de trabalho ou servidores com capacidade de sobra, a lista de pre-requisitos não é um problema. No entanto, se precisar uma solução para dispositivos móveis ou smartphones, esqueça o AccessPlatform.

Datastores
Nesta lista você encontra todas as fontes de dados que o AccessPlatform é capaz de utilizar. Vale ressaltar que o sistema suporta Google BigTable(do Google App Engine) e MongoDB – um sistema de banco de dados “noSQL”, ambos fortes candidatos a adoção em novas gerações de apps para WWW.

Instalação
Primeiramente, é preciso baixar uma versão do AccessPlatform. A versão 2.1 encontra-se em desenvolvimento na data deste post, portanto baixaremos a última versão 2.0 disponível.

Baixe Aqui – Este endereço possui link atualizado para o SourceForge.

Caso leia este post algum tempo depois, verifique a situação de versões posteriores, visto que a 2.1 estava praticamente pronta para lançamento nesta data.

O seguinte endereço possui diversos links para iniciantes, além de explicação do processo de desenvolvimento utilizando o JDO.

Basicamente, o processo de trabalho se resume a dois itens fundamentais:

1) Determinar que classes você irá armazenar permanentemente. Isto é trivial, basicamente é a parte do sistema que reflete o modelo de dados.
2) Utilizar o JDO para obter objetos do banco de dados, modificar objetos, salvá-los novamente, sempre através de um PersistenceManager fornecido pela implementação JDO(que é o “trabalho bruto” do AccessPlatform)

Cada ítem, é claro, possui detalhes técnicos. Como buscar objetos(utilizando JDOQL, a linguagem de consulta do JDO), como armazenar objetos, e por aí vai.

Sendo uma implementação 100% de acordo com o padrão JDO, a documentação da Oracle do JDO é válida para o AccessPlatform. Assim, não faltam fontes para estudo desta tecnologia online. O único problema é que a maior parte do material está em Inglês, e isso pode limitar o acesso de alguns.

Links
Como usar a JDO com o Google App Engine – Em Português
Iniciando com JDO – Oracle
Iniciando com JDO – DataNucleus, desenvolvedora do AccessPlatform
JDO definido na Wikipedia
Página principal do JDO na Oracle


 

11/7 de 2010

Combine vários JARs em um só usando NetBeans

Categoria: Java — jfonseca @ 1:03 pm

Este artigo dá uma dica interessante de como combinar vários JARs em um só utilizando o NetBeans. (A tradução automática é um pouco sofrível, melhor se ler o original. )


 

The C Book – Livro de linguagem C aberto, grátis para todos

Categoria: Programação,Revistas / Livros — jfonseca @ 12:05 am

A edição não cobre o padrão C99, portanto a editora Addison Wesley decidiu abrir mão dos direitos autorais.

Acesse aqui


 

7/7 de 2010

Oracle promete manter apoio tanto ao Eclipse quanto à Netbeans IDE

Categoria: Java — jfonseca @ 1:35 am

Boa notícia para a comunidade Java: a Oracle evitará estabelecer um clima de competição entre o Netbeans IDE e o Eclipse.

Leia matéria traduzida pelo Google translate.

Ou a matéria original em Inglês


 

6/7 de 2010

No Go?

Categoria: Linguagem Go,Programação — jfonseca @ 1:53 am

A seção sobre a linguagem Go ficou congelada um bom tempo. O motivo foi a falta de tempo e, confesso, um certo “desencanto” com a linguagem. Trata-se de um projeto experimental do Google, e ainda precisa amadurecer bastante.

No ritmo de trabalho típico de um desenvolvedor, torna-se impossível acompanhar novas linguagens experimentais e, ao mesmo tempo, produzir no ritmo exigido pelos projetos em andamento.

Espero poder voltar a estudar Go em breve, e que esta interessante linguagem encontre um nicho onde seja aproveitada. Por enquanto, esta seção do blog ficará congelada.


  Proxima Pagina »