hisham hm

Java: if you have trouble declaring a static hashmap…

Java (as of version 6, aka 1.6) does not allow you to declare a static HashMap as conveniently as an array. Still, you have the alternative of using a static block in your class to add fields. Take this example:

import java.util.HashMap;

public class StaticHashMapTest {
	private final static HashMap constants = new HashMap();
	static
	{
		constants.put("A", "The Letter A");
		constants.put("B", "The Letter B");
		constants.put("C", "The Letter C");
	}
	/* Rest of your class that needs to know the consts */
}

This works fine. But then you want to map something a little more complex than a string to another string. And I don't mean something very complex... just, say, a string to a string and an integer (yes, you'd like to use some kind of "pair object", but it looks like Java does not have it).

So you go and try to do things The Java Way (tm) and create a tiny class just to hold your two values:

import java.util.HashMap;

public class StaticHashMapTest {

	private class Pair {
		final String name;
		final int number;
		public Pair(String name, int number) {
			this.name = name;
			this.number = number;
		}
	}

	private final static HashMap constants = new HashMap();
	static
	{
		constants.put("A", new Pair("The Letter A", 123));
		constants.put("B", new Pair("The Letter B", 456));
		constants.put("C", new Pair("The Letter C", 789));
	}
	/* Rest of your class that needs to know the consts */
}

This should suffice, right? I even made the Pair class private to my class, to ensure good information hiding (that's what Java is all about, right?). Turns out this fails to compile:

StaticHashMapTest.java:18: non-static variable this cannot be referenced from a static context
      constants.put("A", new Pair("The Letter A", 123));
                         ^
StaticHashMapTest.java:19: non-static variable this cannot be referenced from a static context
      constants.put("B", new Pair("The Letter B", 456));
                         ^
StaticHashMapTest.java:20: non-static variable this cannot be referenced from a static context
      constants.put("C", new Pair("The Letter C", 789));
                         ^
3 errors

The error messages say that my "new" operators are failing due to the use of the "this" variable, which is not there at all! But hey, we can call "new" from a static context, can't we? We just did that when declaring the HashMap itself.

It turns out that the problem is that we're using an inner class. Objects from inner classes hold a "this" reference to their parent object (yes, as in myInnerObject.this.myParentAttribute... go figure), hence the trouble with the implicit "this" reference.

You have to make it a static inner class, which means it doesn't know anything about the enclosing class. Yes, that's yet another meaning for the word "static" in programming. Due to this peculiar meaning, inner classes are the only context where you can use the "static" qualifier to a class declaration in Java.

This, therefore, works:

import java.util.HashMap;

public class StaticHashMapTest {

	private static class Pair {
		final String name;
		final int number;
		public Pair(String name, int number) {
			this.name = name;
			this.number = number;
		}
	}
	private final static HashMap constants = new HashMap();
	static
	{
		constants.put("A", new Pair("The Letter A", 123));
		constants.put("B", new Pair("The Letter B", 456));
		constants.put("C", new Pair("The Letter C", 789));
	}
	/* Rest of your class that needs to know the consts */
}

And that's Java for you.


Tethering no Linux com celular Nokia, conectando no 3G da TIM

E eis que consegui conectar o notebook à internet usando o celular Nokia (N85) e um cabo USB. (Ok, tive que catar instruções lendo no browserzinho do celular, mas rolou!)

Condensando aqui as instruções para facilitar minha vida da próxima vez:

1) Configurar o celular para conectar em modo “PC Suite” (normalmente eu deixo em “Mass storage”). No celular: Tools → Settings → Connection→ USB → USB Connection Mode: “PC Suite”

2) Plugar o celular na USB. Devem aparecer linhas mais ou menos assim no log do kernel (/var/log/kernel, dmesg)

usb 2-1.2: new high speed USB device using ehci_hcd and address 5
usb 2-1.2: New USB device found, idVendor=0421, idProduct=0094
usb 2-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 2-1.2: Product: N85
usb 2-1.2: Manufacturer: Nokia
usb 2-1.2: SerialNumber: 356808020426058
usb 2-1.2: configuration #1 chosen from 1 choice
cdc_acm 2-1.2:1.1: ttyACM0: USB ACM device
usbcore: registered new interface driver cdc_acm
cdc_acm: v0.26:USB Abstract Control Model driver for USB modems and ISDN adapters
NET: Registered protocol family 35
cdc_phonet: probe of 2-1.2:1.12 failed with error -22
usbcore: registered new interface driver cdc_phonet
usbcore: registered new interface driver cdc_ether
usb 2-1.2: bad CDC descriptors
usbcore: registered new interface driver rndis_host
usb 2-1.2: bad CDC descriptors
usbcore: registered new interface driver rndis_wlan

O principal ali é o “ttyACM0”, que indica que criou o device. Conferir que existe agora um arquivo /dev/ttyACM0

3) Arquivo /etc/ppp/peers/tether:

ttyACM0 460800 crtscts
connect '/usr/sbin/chat -v -f /etc/ppp/chat-tether'
noauth

O nome “tether” é arbitrário, pode ser qualquer um (será usado abaixo), mas o pppd parece realmente querer os arquivos no diretório /etc/ppp/peers.

4) Arquivo /etc/ppp/chat-tether:

TIMEOUT 5
ECHO ON
ABORT BUSY
ABORT ERROR
ABORT 'NO CARRIER'
ABORT VOICE
ABORT 'NO DIALTONE'
ABORT 'NO DIAL TONE'
ABORT 'NO ANSWER'
ABORT DELAYED
TIMEOUT 12
'' ATZ
OK AT+CGDCONT=1,"IP","tim.br"
OK ATD*99#
CONNECT ''

Note a APN da operadora TIM ali. Para outras operadoras, mudar ali de acordo (gprs.oi.com.br, claro.com.br, zap.vivo.com.br).

O nome do arquivo deve ser o mesmo referenciado dentro do arquivo /etc/ppp/peers/tether acima.

5) Rodar: sudo pppd call tether

Se tudo for bem, aparecerá algo assim no /var/log/messages:

Jul 16 21:24:32 (none) pppd[4746]: pppd 2.4.4 started by root, uid 0
Jul 16 21:24:33 (none) chat[4747]: timeout set to 5 seconds
Jul 16 21:24:33 (none) chat[4747]: abort on (BUSY)
Jul 16 21:24:34 (none) chat[4747]: abort on (ERROR)
Jul 16 21:24:34 (none) chat[4747]: abort on (NO CARRIER)
Jul 16 21:24:34 (none) chat[4747]: abort on (VOICE)
Jul 16 21:24:34 (none) chat[4747]: abort on (NO DIALTONE)
Jul 16 21:24:34 (none) chat[4747]: abort on (NO DIAL TONE)
Jul 16 21:24:34 (none) chat[4747]: abort on (NO ANSWER)
Jul 16 21:24:34 (none) chat[4747]: abort on (DELAYED)
Jul 16 21:24:34 (none) chat[4747]: timeout set to 12 seconds
Jul 16 21:24:34 (none) chat[4747]: send (ATZ^M)
Jul 16 21:24:34 (none) chat[4747]: expect (OK)
Jul 16 21:24:34 (none) chat[4747]: ATZ^M^M
Jul 16 21:24:34 (none) chat[4747]: OK
Jul 16 21:24:34 (none) chat[4747]:  -- got it
Jul 16 21:24:34 (none) chat[4747]: send (AT+CGDCONT=1,"IP","tom.br"^M)
Jul 16 21:24:34 (none) chat[4747]: expect (OK)
Jul 16 21:24:34 (none) chat[4747]: ^M
Jul 16 21:24:34 (none) chat[4747]: AT+CGDCONT=1,"IP","tom.br"^M^M
Jul 16 21:24:34 (none) chat[4747]: OK
Jul 16 21:24:34 (none) chat[4747]:  -- got it
Jul 16 21:24:34 (none) chat[4747]: send (ATD*99#^M)
Jul 16 21:24:34 (none) chat[4747]: expect (CONNECT)
Jul 16 21:24:34 (none) chat[4747]: ^M
Jul 16 21:24:34 (none) chat[4747]: ATD*99#^M^M
Jul 16 21:24:34 (none) chat[4747]: CONNECT
Jul 16 21:24:34 (none) chat[4747]:  -- got it
Jul 16 21:24:34 (none) chat[4747]: send (^M)
Jul 16 21:24:34 (none) pppd[4746]: Serial connection established.
Jul 16 21:24:34 (none) pppd[4746]: Using interface ppp0
Jul 16 21:24:34 (none) pppd[4746]: Connect: ppp0 <--> /dev/ttyACM0
Jul 16 21:24:37 (none) pppd[4746]: local  IP address 189.67.100.239
Jul 16 21:24:37 (none) pppd[4746]: remote IP address 10.6.6.6

E deve aparecer um ppp0 no ifconfig:

ppp0      Link encap:Point-to-Point Protocol
          inet addr:189.67.100.239  P-t-P:10.6.6.6  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:8938 errors:0 dropped:0 overruns:0 frame:0
          TX packets:7998 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3
          RX bytes:7405264 (7.0 Mb)  TX bytes:1018670 (994.7 Kb)

6) Estabelecer o default gateway: sudo route add default gw 10.6.6.6

Conferir com o comando route:

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.6.6.6        *               255.255.255.255 UH    0      0        0 ppp0
default         10.6.6.6        0.0.0.0         UG    0      0        0 ppp0

7) Configurar o DNS (por exemplo, com o DNS público do Google). Arquivo /etc/resolv.conf:

nameserver 8.8.8.8

Feito isso, o acesso à internet deve estar funcionando normalmente no computador!


Crash-course sobre gdb: decifrando segmentation faults

Como não ficar no escuro em caso de segmentation fault:

1) Compile o programa com símbolos de depuração

Use a flag “-g” do gcc para compilar o programa com símbolos de depuração. Certifique-se que todos os objetos estão compilados com “-g”. Você pode precisar dar “make clean”.

2) Seu ambiente deve estar com “core dump” habilitado

Ao ocorrer um “segmentation fault”, o Linux pode gerar um arquivo que é imagem da memória do programa na hora que ele “deu pau”, o chamado “core dump” (curiosidade: o “core dump” tem esse nome por causa da memória de ferrite dos anos 1950-1970, que era chamada de “magnetic-core memory“, ou simplesmente “core”). Com o arquivo de “core dump”, podemos fazer a análise post-mortem do processo e descobrir por que ele explodiu.

Digite “ulimit -c” no seu shell. Se ele retornar “0”, o sistema não irá gerar os arquivos “core”. Digite “ulimit -c unlimited” para remover a restrição de tamanho aos arquivos core. Esta é uma configuração por processo, que é herdada pelos processos filhos (portanto, só vale no terminal onde foi digitada). Para torná-la “permanente”, você pode configurar isso no seu .bash_profile, .zshrc ou equivalente.

3) Rodando o gdb

Com o binário e o core dump na mão, rode o gdb:

gdb ./meu_programa_bugado core

(Dependendo da distro, o arquivo core pode ter o número do processo no seu nome, como “core.1234”)

4) Comandos básicos do gdb

Com esses comandos básicos, podemos analisar os dados de um core dump:

Há muito mais que se pode aprender sobre o gdb — é um debugger completo capaz de depurar o programa enquanto roda (gdb -p pid), lidar com threads, etc… mas esse conjunto pequeno de comandos apresentado aqui já é uma mão na roda para decifrar os “segmentation faults”!


Git Cheat Sheet

I have a hard time memorizing certain unfrequent tasks on Git. I’ll write them down here as I learn them so I can look it up later.

Undo a commit

If you just made a commit (perhaps typing git commit -a too eagerly) and realize you made a mistake, use this to uncommit the modified code, so it shows as “modified:” in git status again and appears again in git diff:

git reset HEAD~

I even added an alias git undo-commit, like this:

git config --global alias.undo-commit 'reset HEAD~'

Getting the last commit from another branch

When working in an alternative branch, you can retrieve the latest commit from another branch with this:

git cherry-pick other-branch

Example:

$ git checkout anotherBranch
$ git branch
* anotherBranch
  master
$ edit some files...
$ git commit -a
$ git checkout master
$ git branch
  anotherBranch
* master
$ git cherry-pick anotherBranch

Interactively select which edits should be committed

After making several edits across files, sometimes you want to add them as separate commits. Committing files separately is easy, but sometimes various edits in the same file should be different commits (or some changes to a file are ready to be committed but others are not). To commit part of the changes to a file, use:

git add -p

From the manual:

“Interactively choose hunks of patch between the index and the work tree and add them to the index. This gives the user a chance to review the difference before adding modified contents to the index. This effectively runs add –interactive, but bypasses the initial command menu and directly jumps to the patch subcommand.”


Debugging a stack corruption with gdb

Today I had to debug a crash to my application that happened in a really weird place. I examined the core dump and I was getting a segmentation fault at the time() function, which is part of the standard library.

Evidently, the chances that there was a bug in time() were pretty slim, so the problem must have been elsewhere, and merely manifested itself as a crash in time().

Opening the core in gdb, the GNU Debugger, and checking the backtrace command showed that the stack was corrupted: instead of getting a nice backtrace leading all the way to main (or to the clone() call that created my thread), I had about 6 levels of proper stack and then over 900 levels of “??” below that. Another obvious hint was that the hexadecimal addresses of functions in those invalid stack levels were completely different from the numbers seen in proper stack levels.

A stack corruption can only mean one thing: someone wrote something over the stack and filled the stack pointer address with garbage instead.

I then proceeded to look at the stack contents, hoping to find from which point did values start to look odd. In gdb, I ran the backtrace full command. This shows all local variables as well.

From there on, it was easy to spot a char[] buffer at the lowermost valid stack level that was being updated by functions higher up in the stack. If that buffer had overflowed, it would certainly make everything from there on in the stack invalid.

There were other pointers in that stack level right after the suspicious buffer. Using the up command, I went up, up, up until I reached that stack level, and then I could check the pointers using the print command. Indeed, gdb replied “cannot reach memory address” for their values — the pointers were invalid.

With the down command I went down the stack, right to the function that was manipulating that buffer. A quick look at the code, combined with checking the values of local variables with print confirmed my suspicions. An off-by-one error made my loop go beyond the end of the buffer, corrupting the stack and causing the crash.

As for time()? It didn’t really crash there. Its return value was being assigned to an address that was made invalid by stack corruption, and gdb couldn’t tell the difference between the crash happening at time() or at its return value, probably due to compiler optimizations.