sábado, 31 de enero de 2009

Usando valgrind + memcheck como debugger

Linus Torvalds declaró en este post su postura contraria al uso de debuggers en el kernel, argumentando que son herramientas usadas por los malos desarrolladores que, lejos de aprender de sus errores, se concentran en resolver el problema sin profundizar en la causa real del error. Y por tanto, prefiere mantenerlos lejos del desarrollo del kernel. Desde luego que tiene mucha razón (palabra de Linus).

Mi postura, no tan radical, aunque totalmente de acuerdo con sus argumentos. Es deseable desarrollar con sumo cuidado e intentar evitar todos los fallos durante la codificación, pues detectarlos a posteriori en ocasiones puede ser complicado. Sin embargo, puede que tengamos que analizar algun core de código que no hemos escrito nosotros. Y en una línea que nunca en la vida debería fallar. ¿Que esta ocurriendo?. ¿Que hacemos ahora? Es en estos casos cuando necesitamos ayuda de lo que yo llamo "las fuerzas especiales".


En este post os voy a explicar como usar el debugger valgrind con la herramienta memcheck para detectar 5 errores comunes en la programación de C/C++. Lo vemos en estos 5 test:

Test 1: Detección de una perdida de memoria:

03 int main()
04 {
05 char *x = malloc(100); /* memory leak */
06 return 0;
07 }
$ gcc -g test1.c -o test1
$ valgrind --tool=memcheck --leak-check=yes ./test1
==8458== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==8458== at 0x4C265AE: malloc (vg_replace_malloc.c:207)
==8458== by 0x40051D: main (test1.c:5)


Test 2: Detectar escrituras fuera de los limites de la memoria reservada:

03 int main()
04 {
05 char *x = malloc(10);
06 x[10] = 'a'; /* write out of bounds */
07 return 0;
08 }
$ gcc -g test2.c -o test2
$ valgrind --tool=memcheck --leak-check=yes ./test2
==17039== Invalid write of size 1
==17039== at 0x40052A: main (test2.c:6)
==17039== Address 0x519d03a is 0 bytes after a block of size 10 alloc'd
==17039== at 0x4C265AE: malloc (vg_replace_malloc.c:207)
==17039== by 0x40051D: main (test2.c:5)


Test 3: Detectar el uso de variables sin inicializar:

03 int main()
04 {
05 int x;
06 if (x == 0) /* uninitialised variable */
07 printf("x is zero");
08 return 0;
09 }
$ gcc -g test3.c -o test3
$ valgrind --tool=memcheck --leak-check=yes ./test3
==17070== Conditional jump or move depends on uninitialised value(s)
==17070== at 0x400518: main (test3.c:6)


Test 4: Intentar liberar una zona de memoria que no ha sido reservada:

03 int main(void)
04 {
05 char * str;
06 free(str); /* free not allocated */
07 return 0;
08 }
$ gcc -g test4.c -o test4
$ valgrind --tool=memcheck --leak-check=yes ./test4
==17092== Conditional jump or move depends on uninitialised value(s)
==17092== at 0x4C25265: free (vg_replace_malloc.c:323)
==17092== by 0x40051C: main (test4.c:6)


Test 5: delete en C++ mal hecho:

01 int main(void)
02 {
03 char * str = new char[10];
04 delete str; // instead of: delete [] v;
05 return 0;
06 }
$ g++ -g test5.c -o test5
$ valgrind --tool=memcheck --leak-check=yes ./test5
==17116== Mismatched free() / delete / delete []
==17116== at 0x4C24DAD: operator delete(void*) (vg_replace_malloc.c:342)
==17116== by 0x40067A: main (test5.cpp:4)


Hemos visto que valgrind es una herramienta de mucha ayuda para un desarrollador de C/C++ que sin duda nos ayudará a resolver muchos bugs. No obstante, tambien tiene sus limitaciones, por ejemplo que memcheck no comprueba los limites en los arrays estaticos, y por tanto no detecta este error:

01 int main()
02 {
03 char x[10];
04 x[11] = 'a'; /* not detected! */
05 return 0;
06 }


No obstante, debemos recordar las palabras de Linus. Los debugger son para los malos programadores. Y nosotros queremos ser buenos programadores. Asi que debemos aprender de nuestros errores e intentar que no vuelvan a repetirse en el futuro. Si seguis estos consejos, los debuggers tienen los días contados :-) En todo caso, para mi, el mejor debugger que ha existido y que siempre existirá es el printf (en C) o el cout (en C++).

viernes, 23 de enero de 2009

Controla tu tarifa plana de Orange

Todos debemos (deberíamos) llevar un control exhaustivo del gasto mensual que hacemos en la factura de nuestro teléfono movil, y mas en plena época de crisis en la que siempre viene tan bien ahorrarse unas pelillas. Lo primero sin duda es elegir una tarifa que se adapte a vuestras necesidades.

En mi caso, tengo la tarifa plana de Orange: pago 22 euros al mes y hablo 1000 minutos gratis. Eso siempre que las llamadas sean dentro de la franja horaria de 18-08h. Los SMS y las llamadas fuera de la franja horaria se pagan aparte. El problema de la tarifa plana es que Orange no proporciona ningún servicio para controlar los minutos consumidos (ya me informé en su momento, muy mal hecho señores de Orange) y cuando (como en mi caso) apuras el consumo mensual hasta el último suspiro, a veces puedes llevarte sorpresas desagradables si no controlas el tiempo.

Este post explica como controlar los minutos que todavía os quedan disponibles para seguir llamando, y así poder apurar al máximo los minutos gratuitos. Para ello necesitas dos cosas: el listado de llamadas del periodo de facturación actual (en formato .txt) y un bash shell script en Linux que desarrollé hace algun tiempo y que extrae la información de esa factura.

El bash shell script lo teneis disponible aqui.

http://socios.aditel.org/~icastell/develex/bash/orangeTarifaPlanaActual.sh

Esta licenciado bajo GPL, asi que podeis usarlo y modificarlo a vuestro antojo, siempre que respeteis la licencia.

Para conseguir el listado de llamadas del periodo de facturación actual, hay que darse de alta en la página de Orange, enviando un SMS (gratuito) desde vuestro movil Orange al 222, con el texto CLAVE seguido de un espacio y la clave entre 6 y 8 dígitos, por ejemplo: CLAVE 12345678. Después os autentificais en el área de clientes de su página web http://www.orange.es, introduciendo vuestro número de teléfono y el password que hayais elegido en el SMS:
En el menú que aparece a la izquierda, hay que seleccionar dentro del apartado "mi factura y consumo", la opción "listado de llamadas" e insertar en el calendario el periodo de facturación en el que estais interesados:
En pantalla aparece un listado de todas las llamadas realizadas en el periodo de facturación indicado en el paso anterior. Lo siguiente es pulsar el boton que aparece al pie de la pagina "Imprimir todas las paginas", tal y como muestra la siguiente imagen:
Al pulsar ese boton aparece una ventana emergente para iniciar la impresión. Esa ventana no será necesaria, debes cerrarla. En pantalla tendrás el listado de todas las llamadas realizadas. Debes seleccionarlo al completo desde la primera llamada hasta la última, y usar la función de copiar/pegar para guardarlo en un fichero de texto llamado factura.txt (en realidad el nombre es irrelevante).

Ya tienes el listado de llamadas. Ese listado lo vas a usar como input del script que ya debes haber descargado. Para probarlo, basta con que ejecutes esto en linea de comandos del bash:

$ orangeTarifaPlanaActual.sh factura.txt

Los resultados del script son concluyentes:

Gasto en llamadas sin descuentos = 234.1776 (euros)
Gasto en llamadas con descuentos = 6.5448 (euros)
Minutos gratis consumidos = 994.26666666666666666666
Minutos gratis restantes = 5.73333333333333333334
Total factura a pagar este mes = 33.111968 (euros)

Este mes nos han sobrado poco mas de 5 minutos. Podríamos haber hecho una llamadita mas. Pero mas de 200 euros de ahorro en llamadas, ¡no estan nada mal!

domingo, 4 de enero de 2009

El cubo de Rubik

Papa Noel me trajo estas Navidades un juguete nuevo muy chulo: un cubo de Rubik de 3x3. De pequeño ya tuve uno, pero me causó una gran frustración no poder resolverlo nunca y finalmente opté por despegar todas las pegatinas y pegarlas en su posición correcta. ¿Lo resolví o no? jeje. Pues si, pero a partir de entonces el cubo quedo algo dañádo, por decirlo de alguna manera, y algunas pegatinas se caian de cuando en cuando... Aquella azaña no me dejó un buen sabor de boca. Asi que estas vacaciones me puse manos a la obra, leyendo algunos manuales de Internet, para sacarme la espinita.

El cubo viene resuelto de fábrica, asi que lo primero que hay que hacer es enredarlo todo lo que podais. De eso se encargó mi hermano antes de regalarmelo... ¡ya te apañaras! me dijo. ¡Que cabron! jeje Pense yo. Pero no os preocupeis porque puede resolverse desde cualquier posición. Nuestra posicion de partida del cubo es la siguiente:


Para empezar hay que tomar una cara de referencia como cara superior del cubo. Sirve cualquiera, asi que vamos a tomar la blanca que me gusta mas. Debes hacer una cruz en la cara blanca. Ademas, todas sus caras laterales deben tener el color de su celda central del mismo color que la celda que hay por encima:


Una vez has conseguido esto, debes completar toda la primera fila de cada lateral de su color, lo que a su vez completará la cara superior de color blanco:


Ahora debes completar la segunda fila de cada lado:


En este punto, debes girar el cubo, tomando como base la cara blanca y como cara superior la cara amarilla (todavia sin completar):


Igual que hiciste con la cara blanca, debes hacer una cruz en la cara amarilla, por supuesto sin estropear la cara blanca ni las dos filas inferiores que ya tenemos en su posición correcta:


Ahora todas las caras laterales deben tener el color de su celda central del mismo color que la celda que hay por encima, por supuesto respetando la cruz de color amarillo:


En el siguiente paso debes situar las fichas de las 4 esquinas que todavía faltan por completar en su posicion, aunque sus colores no esten bien encarados:


Por útimo solo queda encarar con sus colores las fichas que todavia no lo esten. Y por fin, el cubo queda resuelto:


¡Objetivo cumplido!

Los algoritmos necesarios para conseguir la secuencia descrita no los he incluido aqui porque es bastante mas complicado de explicar que de entender. Hay multitud de manuales en Internet y por supuesto, podeis preguntarme aqui si teneis cualquier duda.

Ha sido muy divertido aprender a resolverlo y me lo he pasado muy bien. Pero en realidad no es trivial, y desde luego no es un juguete pensado para un niño de 8 años que no reciba ninguna ayuda para resolverlo. El cubo 3x3 tiene 43 trillones de posiciones y sin una guia que te explique los pasos es prácticamente impensable que alguien pueda resolverlo por su cuenta.

No obstante, en este mundo hay gente para todo, y el estadounidense Shotaro Makisumi ostenta el record mundial de velocidad en 12,11 segundos, y a ciegas en 3 minutos y 37 segundos. Desde luego que no voy a ser yo quien se lo quite...

Visitas:

Seguidores