domingo, 25 de março de 2012

Usando o GDB

olá pessoal, hoje vamos tentar usar o GDB, uma ferramenta de debug muito útil no desenvolvimento.
Para demostrar o seu uso, vamos fazer um pequeno programa e uma função para somar todos os elementos de uma matriz.

Nosso código fonte:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. int main()
  4. {
  5. int matriz[5] = {1,2,5,3};
  6. int * result;
  7. somaArray(matriz,5,result);
  8. printf("\n\tResult: %d\n", *result);
  9. return 1;
  10. }
  11. void somaArray(int * m, int size, int * result)
  12. {
  13. int acul=0;
  14. int x;
  15. for(x=0;x<size;x++)
  16. {
  17. acul+=m[x];
  18. }
  19. *result = acul;
  20. }

Bom, obviamente esse código tem um erro, não inicializamos o ponteiro “result” assim quando formos escrever nele vamos receber um “segmentation fault”

Bom mas por enquanto vamos fazer de conta que não tem problema e vamos compilar nosso código fonte com o seguinte comando:

eduardo@debian:~/Documents/programas/gdb$ gcc -o gdbTest gdbTest.c

Vamos executar:

eduardo@debian:~/Documents/programas/gdb$ ./gdbTest

E vamos ver o resultado da soma!!!!!
Segmentation fault

É, ainda não foi dessa vez. Bom, vamos pedir ajuda ao gdb. Aqui eu já estou com o gdb instalado. Se você não tem ele instalado você pode rodar o comando:
apt-get install gdb

Mas não vamos cobrir aqui detalhes da instalação. Nosso objetivo, agora, é usar.

Primeiro passo: vamos compilar nosso programa novamente, com a opção “-g”, assim, o gdb teerá melhores condições de analisar nosso código e nos permitirá, por exemplo imprimir a linha do SIGSEGV.

Vamos lá:
eduardo@debian:~/Documents/programas/gdb$ gcc -g -o gdbTest gdbTest.c

Segundo passo: rodar com o gdb

eduardo@debian:~/Documents/programas/gdb$ gdb ./gdbTest
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/eduardo/Documents/programas/gdb/gdbTest...done.
(gdb)

Vai ficar piscando o cursor. Apenas digitamos: run

(gdb) run
Starting program: /home/eduardo/Documents/programas/gdb/gdbTest

Program received signal SIGSEGV, Segmentation fault.
0x000000000040059e in somaArray (m=0x7fffffffe280, size=5, result=0x0)
at gdbTest.c:23
23 *result = acul;
(gdb)

Já recebemos o nosso “SIGSEGV”, bom veja que ele já nos informou a linha que isso ocorreu – linha 23 – e em qual função – somaArray – veja que ele nos passou os argumentos da função, veja que o primeiro parâmetro é um ponteiro, e realmente tem um endereço ali. O segundo parâmetro é um int, e realmente tem o valor 5 que estamos passando, mas e o ponteiro result? Veja que ele é nulo, e na linha 23 estamos tentando escrever nele!!! então achamos a causa do sigsegv.

Se você desejar ver quem chamou a função somaArray, você pode digital o comando bt (backtrace). Imagine que você poderia ter no main várias chamadas para essa função no main. Veja que é na linha 23 que o problema acontece, mas essa linha está certa, o problema é que já estamos recebendo o ponteiro nulo!!!

então:
(gdb) bt
#0 0x000000000040059e in somaArray (m=0x7fffffffe280, size=5, result=0x0)
at gdbTest.c:23
#1 0x0000000000400539 in main () at gdbTest.c:7
(gdb)

Agora vamos no main na linha 7 e ver como é a chamada.
somaArray(matriz,5,result);

bom a variável matriz está inicializada corretamente. O parâmetro size já é 5, então está certo. O result, logo acima, na linha 6 foi declarado:
int * result;

Mas não foi inicializado!!! e logo abaixo já o usamos.

Bom, achamos o problema, agora corrigimos:

Vamos alterar a linha 6 para:
int * result = (int *) malloc(sizeof(int));

vamos compilar novamente, pode ser sem a flag “-g”.

eduardo@debian:~/Documents/programas/gdb$ gcc -o gdbTest gdbTest.c

uardo@debian:~/Documents/programas/gdb$ ./gdbTest

Result: 11
eduardo@debian:~/Documents/programas/gdb$

Resolvido!!!!

sábado, 25 de fevereiro de 2012

Comunicação entre processos – PIPES em C++

Sistema operacional utilizado: Linux debian 2.6.32-5-amd64

O nosso caso de exemplo será com o que chamamos de "Named PIPES" também conhecidas como FIFO. Onde a utilizaremos para comunicação entre dois processos. Basicamente, teremos um servidor, que criará uma PIPE/FIFO onde o cliente escreverá um comando. O Servidor lerá essa mensagem, e escreverá na PIPE/FIFO do cliente a resposta do comando. Ou seja, o cliente também terá que criar sua PIPE para que o servidor possa escrever. Então, utilizaremos duas PIPES, uma criada pelo Server e a outra criada pelo cliente.

A Pipe criada pelo servidor deve ser conhecida antes de rodarmos o cliente. O cliente irá criar sua Pipe pelo PID do processo e irá transmitir o nome da Pipe para o servidor através da PIPE do server, assim o servidor será capaz de abrir a Pipe do cliente e escrever o resultado nela.

No nosso exemplo vamos usar três arquivos de codificação.

Main.cpp – Esse arquivo conterá a codificação para o nosso servidor
client.cpp – Esse arquivo conterá a codificação para o nosso cliente
header_fifo.h – Esse arquivo conterá a implementação de nossa classe.

Teremos tamém um quarto arquivo, um Makefile, que será responsável por compilar nosso exemplo.

Teremos duas opções de compilação, uma para Debug, onde você poderá ver as mensagens sendo trocadas entre cliente e servidor, o que será útil para modificar ou entender melhor o processo de comunicação.

Então vamos ao código de nosso servidor:


#include "fifo_header.h"

int main(int argc, char *argv[])
{
    int processID;
    if(argv[1]!=NULL)
    {
        if(strcmp("stop",argv[1]) ==0)
        {
                system("killall PipeServer");
                return 1;
        }
        else
        {
            if(strcmp("Debug",argv[1]) !=0)
            {

                processID = fork();

                if (processID < 0)
                {
                    exit(EXIT_FAILURE);
                }
                else
                {


                    if (processID > 0)
                    {
                        exit(EXIT_SUCCESS);
                    }
                }
            }
        }
    }

    FIFO myFifo, clientFifo;
    int PipeToWrite, PipeToRead, returnedValue, ready = 0;
    data buffer;
    myFifo.setName(argv[1]);
    myFifo.setPath(strcat(strcat(argv[2],"/"),argv[1]));
#ifdef DEBUG
    cout << "Name of FIFO: " << myFifo.getName() << endl;
    cout << "Path of FIFO: " <<myFifo.getPath() << endl;
    cout.flush();
#endif

    /*
    Vamos iniciar a criação de nossa PIPE
    */
    mknod(myFifo.getName(), S_IFIFO | 0666, 0);

    /*
    Vamos abrir a nossa PIPE que usaremos para ler do cliente
    */

    returnedValue = open(myFifo.getName(), O_RDONLY | O_NDELAY);
    if(returnedValue == -1)
    {
        printf("\n\tError in open PIPE\n");
        perror(myFifo.getMessage());
        return 1;
    }
    myFifo.pipeDescriptor = returnedValue;

    while (true)
    {

        returnedValue = myFifo.readFromPipe(clientFifo);

        if(returnedValue > 0)
        {
#ifdef DEBUG
        cout <<"retuned value from read: "<<returnedValue <<endl;
        cout <<"What Client said is: " <<myFifo.getMessage()<<endl;
#endif
        ready=0;
        do
        {
            returnedValue = open(clientFifo.getName(), O_WRONLY/*|O_NDELAY*/);
                if(returnedValue == -1)
                {
                    perror(clientFifo.getName());
                    return 1;
                }
#ifdef DEBUG
            cout <<"Pipe client Name: "<< clientFifo.getName() <<endl;
#endif
            clientFifo.pipeDescriptor = returnedValue;
            myFifo.writeToPipe(clientFifo);
            ready=1;


            }while(!ready);
            clientFifo.closePipe();
        }
    }

    return 1;
}

Agora o código de nosso cliente

#include "fifo_header.h"

int main(int argc, char *argv[])
{
    /*
    argv deve ser o mesmo do servidor.
    Assim termos como informar o nome da PIPE e o local onde o servidor
    a criou
    */
    FIFO myFifo, serverFifo;
    data buffer;
    int numberOfBytes, returnedValue, readOK;
    serverFifo.setName(argv[1]);
    serverFifo.setPath(strcat(strcat(argv[2],"/"),argv[1]));

#ifdef DEBUG
    cout << "Name of FIFO: " << myFifo.getName() << endl;
    cout << "Path of FIFO: " <<myFifo.getPath() << endl;
    cout.flush();
#endif
    sprintf(buffer.name, "/home/eduardo/Documents/programas/FIFO/%d", getpid( ));
    myFifo.setName(buffer.name);

    if (mknod(myFifo.getName(), S_IFIFO | 0666, 0) < 0)
    {
        perror(myFifo.getName());
        return 1;
    }
    if ((returnedValue = open(serverFifo.getName(), O_WRONLY)) == -1)
    {
        perror(serverFifo.getName());
        return 1;
    }
    serverFifo.pipeDescriptor = returnedValue;
    while(true)
    {

        cout << "\ncmd" <<endl;
#ifdef DEBUG
        cout <<"PIPE of server: " << serverFifo.getName() <<endl;
#endif
        numberOfBytes = read(fileno(stdin), buffer.message, PIPE_BUF);
#ifdef DEBUG
        cout <<"what you write: "<<buffer.message<<endl;
#endif
        if (!strncmp("exit", buffer.message, numberOfBytes - 1))
            break;

        write(serverFifo.pipeDescriptor,(char *) &buffer, sizeof(buffer));

        returnedValue = open(myFifo.getName(), O_RDONLY | O_NDELAY);

        if(returnedValue == -1)
        {
            perror(myFifo.getName());
            return 1;
        }
        myFifo.pipeDescriptor = returnedValue;
        readOK = 0;
        while (readOK != 1)
        {
            if((numberOfBytes = read(myFifo.pipeDescriptor, buffer.message, PIPE_BUF)) > 0)
            {
                write(fileno(stdout), buffer.message, numberOfBytes);
                memset(buffer.message,0x0,PIPE_BUF);
                readOK=1;
            }


        }
        myFifo.closePipe();


    }

    serverFifo.closePipe();
    return 1;
}


Agora a implementação de nossa classe


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <linux/limits.h>
#include <stdlib.h>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <errno.h>
using namespace std;

typedef struct dataSet
{
        char name[255];
        char path[255];
        char message[PIPE_BUF];
}data;

class FIFO
{
    private:
        data info;
    public:
        int pipeDescriptor;
        char* getName();
        void setName(char*);
        char* getPath();
        void setPath(char*);
        char* getMessage();
        void writeMessage(char*);
        void closePipe();
        int writeToPipe(FIFO&);
        int readFromPipe(FIFO&);

};

char* FIFO::getName()
{
    return info.name;
}

void FIFO::setName(char * nameToSet)
{
    strcpy(info.name,nameToSet);
}

char* FIFO::getPath()
{
    return info.path;
}

void FIFO::setPath(char *pathToSet)
{
    strcpy(info.path, pathToSet);
}


char* FIFO::getMessage()
{
    return info.message;
}

void FIFO::writeMessage(char *messageToWrite)
{
   strcpy(info.message, messageToWrite);
}

int FIFO::writeToPipe(FIFO& pipeToWrite)
{
    FILE * result;
    int readFromFile, numberOfBytes, popenIsOk;
    char buffer[PIPE_BUF], stringBuff[PIPE_BUF];
#ifdef DEBUG
    cout <<"what will be the command to sheel: " << getMessage()<<endl;
#endif
    result = popen(getMessage(), "r");
    if(!result)
    {
#ifdef DEBUG
        cout <<"result is null" << endl;
        perror("errot in open file");
#endif
        strcpy(stringBuff,"Command not found");
        write(pipeToWrite.pipeDescriptor,stringBuff,strlen(stringBuff)+1);
        return -1;
    }


    popenIsOk=0;
    while(popenIsOk!=1)
    {
        if ((numberOfBytes = read(fileno(result), buffer, PIPE_BUF))>0)
        {
            strcpy(stringBuff,buffer);
            write(pipeToWrite.pipeDescriptor,stringBuff,strlen(stringBuff)+1);
            memset(buffer, 0x0, PIPE_BUF);
#ifdef DEBUG
            cout <<"what will be written to client: " << stringBuff << endl;
#endif
            popenIsOk=1;

        }
    }

    pclose(result);

}

int FIFO::readFromPipe(FIFO& pipe)
{

    int returnedValue;
    data buffer;
    returnedValue = read(pipeDescriptor, (char *) &buffer, sizeof(buffer));
    if(returnedValue <=0 )
        return returnedValue;
    else
    {
        writeMessage(buffer.message);
        pipe.setName(buffer.name);
    }
    return returnedValue;
}

void FIFO::closePipe()
{
    close(pipeDescriptor);
}

Agora vamos ao nosso Makefile


CC:=g++

all:PipeServer Client

PipeServer:main.cpp fifo_header.h
$(CC) -o PipeServer main.cpp

Client:client.cpp fifo_header.h
$(CC) -o Client client.cpp

Debug: main.cpp client.cpp fifo_header.h
$(CC) -D DEBUG -o PipeServer main.cpp
$(CC) -D DEBUG -o Client client.cpp

Comandos necessários para compilar e rodar:
Para compilar os dois programas em modo normal: 
Comando: make all

Para executar:
Comando para Rodar o Server: ./PipeServer FIFO /home/eduardo/Documents/programas/FIFO
Onde: 
  1. FIFO é o Nome da PIPE a ser Criada
  2.  /home/eduardo/Documents/programas/FIFO: Caminho da Pasta onde ela vai ser criada
Comando para Rodar o cliente: ./client FIFO /home/eduardo/Documents/programas/FIFO
  1. FIFO é o Nome da PIPE criada pelo server
  2.  /home/eduardo/Documents/programas/FIFO: Caminho da Pasta onde ela foi  criada
Como o Server roda em BackGround, se você desejar para o server dê o comando: ./PipeServer Stop
Para parar o cliente apenas digite exit

Para o dois programas em modo de DEBUG

comando para compilar: make Debug

Para rodar o server(agora ele não roda em BackGround Ctrl+C se desejar parar o server): ./PipeServer Debug /home/eduardo/Documents/programas/FIFO

Para rodar o cliente: ./client  Debug  /home/eduardo/Documents/programas/FIFO

Bons Testes!!!!


terça-feira, 31 de janeiro de 2012

Definindo Macros para diretivas de compilação

Hoje vamos aprender a usar algumas diretivas de compilação. As vezes é necessário fazermos um debug de nosso programa. Para isso ao invés de definirmo um #define em  nosso código, vamos incluir no momento da compilação.

Vamos ver um pequeno exemplo. Vamos criar um arquivo em c com o seguinte código:


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

int main()
{
    int num1, num2, result;
    printf("\n\tDigite o priemiro Numero: ");
    scanf("%d",&num1);
    printf("\n\tDigite o segundo Numero: ");
    scanf("%d", &num2);

    #ifdef DEBUG
    printf("\n\tOs valores que voce digitou: %d %d", num1, num2);
    #endif

    printf("\n\tResultado %d + %d = %d", num1, num2, num1 + num2);

    return 1;
}

Agora, vamos compilar o nosso código com a seguinte sintaxe: gcc -D DEBUG -o somaDoisN soma.c
Quando o programa for executado, ele vai mostrar na tela o printf entre o #ifdef e o #endif

Vamos mudar a compilação, vamos retirar a diretiva de compilação para DEBUG:
gcc  -o somaDoisN soma.c 

Agora a mensagem não vai mais ser mostrada!!!!


sábado, 7 de janeiro de 2012

Shared Libraries no Linux

Shared libraries são bibliotecas que são carregadas pelos programas quando eles iniciam.

Precisamos entender a diferença entre o nome da biblioteca, soname, e seu real name.
Toda shared library tem um nome especial chamado de "soname". O soname tem o prefixo "lib", o nome da biblioteca, o sufixo ".so" seguido de ponto e um número de versão que é incrementado sempre que a interface muda. Normalmenteo soname é um link simbólico para o real name da shared library.
Exemplo de soname: libctest.so.1

Toda a shareed libray possui um real name. Que é o nome do arquivo que contém o código da biblioteca. O real name adiciona ao soname um ponto, um número, que é o minor number, outro ponto e o número da versão.
Exemplo de real name: libctest.so.1.0

Além disso, há um nome usado pelo compilador para requisitar a library, que é simplesmente o soname sem nenhum número de versão.
Exemplo: libctest.so


Colocando em prática:

Primeiramente vamos ter dois arquivos que são:
operacoes.c – que vai conter nosso código fonte
operacoes.h – que vai conter apenas as definições

Código do arquivo operacoes.c

#include "operacoes.h"

int somaDeInterios (int n1, int n2)
{
return n1 + n2;
}

int multInteiros (int n1, int n2)
{
return n1 * n2;
}

Código do arquivo operacoes.h

#ifndef _OPERACOES
#define _OPERACOES

int somaDeInterios (int n1, int n2);
int multInteiros (int n1, int n2);

#endif


Agora vamos digitar o comandos:
gcc -fPIC -c operacoes.c
Veja que fio gerado o aquivo: operacoes.o

Agora, vamos gerar a nossa library propriamente dita:

gcc -shared -Wl,-soname,libcmatematica.so.1 -o libcmatematica.so.1.0 operacoes.o

Vamos copiar o arquivo gerado: libcmatematica.so.1.0 para dentro da pasta /usr/lib, talvez você deva estar logado como root para executar esse passo:

cp libcmatematica.so.1.0 /usr/lib

Agora vamos criar os links necessários, talvez tenha que estar logado com root:

ln -sf /usr/lib/libcmatematica.so.1.0 /usr/lib/libcmatematica.so
ln -sf /usr/lib/libcmatematica.so.1.0 /usr/lib/libcmatematica.so.1

Bom, agora, vamos criar um simples programa para verificar se a nossa biblioteca realmente funciona.

Vamos chamar nosso arquivo de main.c

Código do arquivo main.c

#include "operacoes.h"
#include <stdio.h>

int main()
{
printf("\n\tTeste da Shared library");
printf("\n\t5 + 2 = %d", somaDeInterios(5,2));
printf("\n\t5 * 2 = %d", multInteiros(5,2));
}

Para compilar:
gcc main.c -lcmatematica -o operacoesInteiras

Resultado:
Teste da Shared library
5 + 2 = 7
5 * 2 = 10

domingo, 18 de dezembro de 2011

Shell Script - Criando Script para trocar a extensão de vários arquivos

Olá pessoal,

hoje vamos falar um pouco sobre shell script. O objetivo é criar um script, muito simples, que seja capaz de alterar a extensão de nossos arquivos.

Para testar, vamos criar em nosso diretório de testes os seguintes arquivos:


ChangeExtension.sh  -> nosso script
First.txt -> arquivo para testes
Second.txt -> arquivo para testes

Bom, agora, vamos ao nosso script, que pega o nome os arquivos, e troca a sua extensão de acordo com os parâmetros passados a eles:


NUMBER_OF_ARGUMENTS=$#
ORIGINAL_EXTENSION=$1
NEW_EXTENSION=$2


if [ $NUMBER_OF_ARGUMENTS -ne 2 ]
then
    echo "you need to pass two extensions like: ChangeExtension .txt .teste"
    exit 1
fi

echo "Original Extension: "  $ORIGINAL_EXTENSION
echo "New Extension: " $NEW_EXTENSION




for file in $(ls | awk -F"." '{print $1}')
do
    if [ -e $file$ORIGINAL_EXTENSION ]
    then

        mv -v $file$ORIGINAL_EXTENSION $file$NEW_EXTENSION
    fi

done

O que é feito é basicamente o seguinte:
1° - pegamos os argumentos passados passados pela linha de comando: $1 é o primeiro $2 é o segundo. $# é o número de argumentos.
2º - Testamos se o número de argumentos é válido
3° - Para cada arquivo com a extensão informada, a substituímos pela nova extensão.

Vamos a um exemplo:
Vamos renomear nossos arquivos de .txt para .old.txt

Apenas digitamos o comando:  ./ChangeExtension.sh .txt .old.txt
Resultado:
ChangeExtension.sh  -> nosso script, inalterado
First.old.txt  Second.old.txt -> dois arquivos com a extensão alterada.

Bom, por hoje é isso




sábado, 10 de dezembro de 2011

Instalação do KDE no FreeBSD

Hoje vamos instalar a interface KDE no FreeBSD.

Versão do sistema: 8.2 RELEASE

Primeiro passo -> Instalação do X11:

  • Rodamos o comando: pkg_add -r xorg
Segundo passo -> Configuração:

Abra o arquivo /etc/rc.conf e adicione as seguintes linhas para auto detectar mouse/teclado e habilitar o KDE aos iniciar

  • hald_enable="YES"
  • dbus_enable="YES"
  • local_startup="${local_startup} /usr/local/kde4/etc/rc.d"
  • kdm4_enable="YES"
Terceiro passo -> Instalação do KDE:

  • pkg_add -r kde4
Quarto passo -> Vamos fazer o KDE startar automaticamente:


  • echo "exec /usr/local/kde4/bin/startkde" > ~/.xinitrc
Resultado:

domingo, 2 de outubro de 2011

Imprimindo variáveis de ambiente (Environment Variables) em seu programa C - Linux

Pessoal,
hoje vamos trabalhar um pouco com as variáveis de ambiente do sistema operacional Linux. Vamos imprimir as variáveis de ambiente na tela, um programa bem simples que vai fazer usado da função "getenv" que prcisa do include: "unistd.h" Essa função é responsável por procurar a variável informada em uma lista de variáveis de ambiente e nos retornar seu valor. Na verdade ele retorna o valor da primeira ocorrência encontrada. Em C para fazermos o acesso as variáveis de ambiente, podemos fazer através de um ponteiro externo, cujo nome é "environ" como pode ser visto no código abaixo:


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


void imprimeVariaveisDeAmbiente();
void imprimeVariavel(char *var);
int menuDeOpcoes();

extern char** environ;
int main()
{
    int op;
    char  varAmb[40];

    do
    {
        op = menuDeOpcoes();
        switch(op)
        {
            case 1: imprimeVariaveisDeAmbiente();
                    break;
            case 2: printf("\n\tInforme nome da variavel:\t");

                    scanf("%s",varAmb);
                    imprimeVariavel(varAmb);
                    break;
        }
    }while(op<3);

    return 0;
}

void imprimeVariaveisDeAmbiente()
{

    printf("\n\t----Imprimindo varia¡veis de ambiente!!!----");
    char ** pEnviron = environ;
    for(;*pEnviron;*pEnviron++)
    {
        printf("\n%s",*pEnviron);
    }
}

void imprimeVariavel(char *var)
{
    char * result;

    result = getenv(var);
    if(result != NULL)
        printf("\n\tVariavel:\n\t%s",result);
    else
        printf("\n\tNao encontrado!!!");
}

int menuDeOpcoes()
{
    int opcao;
    printf("\n\t1 - Imprimir todas as variaveis;");
    printf("\n\t2 - Inmprimir uma variavel;");
    printf("\n\t3 - Sair");
    printf("\n\tSelecione a opcao:\t");
    scanf("%d",&opcao);
    return opcao;
}