[Tip] Simple serializador para C

Simple Net Serializer

Simple Net Serializer

El otro día necesitaba serializar unos mensajes para una aplicación cliente/servidor que estaba desarrollando.

En un primer momento intenté buscar alguna librería que me diera lo que estaba buscando. Probé con GLib pero no tiene nada para serializar mensajes (se pueden serializar objectos con GObject).

Decidí hacer una implementación sencilla de lo que tenia mente.

Así fue como desarrolle el siguiente código:

// PUSH/POP Simple net serializer

#define PUSH_BEGIN(buff,l)       { int i = 0; int lm = (l); char* b = (char*) (buff);
#define PUSH_BYTE(b)                 assert(i+1 <= lm); b[i++] = (b);
#define PUSH_SHORT(s)                assert(i+sizeof(short) <= lm); { short* ps = (short*) &b[i]; *ps = htons((s)); i += sizeof(short); }
#define PUSH_INT(s)                  assert(i+sizeof(int) <= lm); { int* ps = (int*) &b[i]; *ps = htonl((s)); i += sizeof(int);}
#define PUSH_SZ(s)                  { int l = strlen(s); assert(i+l+sizeof(int) <= lm); PUSH_INT(l); memcpy(&b[i],s,l); i += l; }
#define PUSH_END(l)              (l) = i; }

#define POP_BEGIN(buff,l)        { int i = 0; int lm = (l); char* b = (char*) (buff);
#define POP_BYTE(b)              assert(i < lm); (b) = b[i]; i++;
#define POP_SHORT(s)             assert(i+sizeof(short) <= lm); (s) = ntohs(  *((short*) &b[i])     ); i += sizeof(short);
#define POP_INT(s)               assert(i+sizeof(int) <= lm); (s) = ntohl(  *((int*) &b[i])     ); i += sizeof(int); printf("val: %i\n",(s));
#define POP_SZ(s)                { int l = 0; POP_INT(l);  assert(i+l <= lm); (s) = (char*) malloc(l+1); memcpy(s,&b[i],l); s[l] = '\0'; i += l; }
#define POP_END()             }

A poco que examinéis el código comprobareis que se trata en realidad de una macro.

Esta implementación cumple mis requisitos:

  • Eficiente. Las macros se resuelven en el código y es equivalente a las funciones inline.
  • Sencillo. La nomenclatura de las macros auto-describe su propósito, PUSH_x para agregar y POP_x para extraer.
  • Poca sobrecarga. La mayoría de los serializadores incluyen el tipo de dato del dato agregado. En este caso el tipo está bien determinado e identifica unívocamente el tipo de mensaje.
  • Depuración. Gracias a los assert() puede comprobar que el tamaño de los bufferes para el envio/recepción de los mensajes es correcto durante la fase de pruebas.
  • Portable. El uso de ntoh/hton posibilita la independencia de arquitectura big/little endian.

Así por ejemplo si queremos mandar el siguiente mensaje: “bote de champú”,3,”suave”, éste seria el código resultante:


#define MAXBUFFER 1024

char buff[1024];
int len;

PUSH_BEGIN(buff,MAXBUFFER)
PUSH_SZ("bote de champu")
PUSH_INT(3)
PUSH_SZ("suave")
len= PUSH_END()

Seguro que no es la mejor implementación y es probable que tenga algun bug. Si ves algo que no te gusta o bugs, te animo a que lo modifiques y comentes. Libero el código bajo licencia BSD.

Translate to:English
MenefanteMenéame TwitterTwitter

[Tip] Detectar bucles infinitos

For Hang Problem

For Hung Problem

Hoy en la lista de correo de GoLang he descubierto una cosa muy curiosa e importante en el desarrollo de software:
¿Cómo detectar el punto de programa donde se ejecuta un bucle infinito?

Ian Lance Taylor escribió lo siguiente:

    You should be able to get a stack trace by sending a SIGSEGV signal
    (signal 11).

No es una cosa que suela ocurrir frecuentemente (si lo comparamos con los Segmentation faults) pero cuando pasa, ¡madre mía! para encontrar el error.

He hecho un pequeño ejemplo que puede servir como procedimiento para aplicar en casos reales donde esto ocurra.

  1. Supongamos el siguiente código en C:
    #include <stdio.h>
    
    int func1()
    {
      int x,y;
    
      return x*y;
    }
    int func2()
    {
       for (;;)
       {
           func1();
       }
    }
    
    int func3()
    {
       func2();
    
    }
    
    int main(void)
    {
    
       printf("forever for stack trace\n");
    
       func3();
    
    return 0;
    
    }
    
    
  2. Lo guardamos como “testStack.c” y lo compilamos:
    gcc -o testStack testStack.c
  3. Para poder ver la pila de invocaciones del programa es necesario poder generar un core. En muchas distribuciones, por seguridad, no se permite generar cores. Es necesario invocar a “ulimit” con un límite grande para el tamaño máximo de core generado:
     ulimit -c  10000000 
  4. Ejecutamos nuestro programa:
     ./testStack
    

    forever for stack trace

  5. Ahora viene lo interesante. Abrimos otra ventana de shell y mandamos un SIGSEGV a “testStack”:
    pkill -11 testStack 
  6. En la ventana de ejecución de “testStack”, vemos como aparece:
    Fallo de segmentación
  7. Para ver donde se ha quedado, basta con llamar a gdb y pedirle el backtrace:
    gdb ./testStack core
    
    GNU gdb 6.8-debian
    Copyright (C) 2008 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html&gt;
    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 “i486-linux-gnu”…
    warning: Can’t read pathname for load map: Input/output error.
    Reading symbols from /lib/tls/i686/cmov/libc.so.6…done.
    Loaded symbols for /lib/tls/i686/cmov/libc.so.6
    Reading symbols from /lib/ld-linux.so.2…done.
    Loaded symbols for /lib/ld-linux.so.2
    Core was generated by `./testStack’.
    Program terminated with signal 11, Segmentation fault.
    [New process 2024]
    #0  0x080483c4 in func1 ()
    (gdb) bt
    #0  0x080483c4 in func1 ()
    #1  0x080483db in func2 ()
    #2  0x080483e8 in func3 ()
    #3  0x0804840c in main ()
    Current language:  auto; currently asm
    (gdb)
  8. Como se puede apreciar, nuestro programa se encontraba ejecutando la func1() en el momento de recibir el SIGSEGV. ¡Ya sabemos que por alguna func{1,2,3} está el problema! En este caso es un poco inútil, pero en software real puede ahorrarnos muchos dolores de cabeza.

Espero que este tip os sirva. A mi desde luego que sí 🙂

Translate to:English
MenefanteMenéame TwitterTwitter