Debug in una macchina virtuale


Questo post contiene un estratto dell’esperienza che mi sono fatto nell’eseguire il debugging di E17 installato su OpenSolaris, in esecuzione in una VM VirtualBox.

Non entrerò nel merito dell’installazione, che alla fine mi è riuscita ma è stata un pò più dura del solito, richiedendo qualche intervento manuale e di traverso, per questo arriverà un post a breve :)


L’entusiasmo iniziale per aver portato a termine l’installazione di è smorzato di fronte ad un brutto crash del sistema, occorso cliccando con il tasto destro in una parte vuota (senza icone) della finestra del file manager interno.

Premetto che in E17, per quanto possa essere brutto un crash (lo si può paragonare al famoso Blue Screen of Death, BSOD, di Windows), frequentemente è possibile recuperare le applicazioni aperte prima del crash premendo F1 nella finestra che appare, senza perdere nemmeno un bit.

Tornando al mio problema, dopo aver ripetuto il crash più e più volte in modo da essere certo di poterlo riprodurre facilmente, sono passato alla fase di debugging.

La procedura che sono solito usare in questi casi è avviare una nuova istanza di E17 in una sessione Xnest (o Xephyr), e lanciare gdb attaccandolo al PID della nuova istanza: può sembrare complesso, ma in realtà è sufficiente eseguire lo script xnest.sh (xnest.sh –help per dettagli), ottenendo ciò che ho descritto.

Ma non sono stato assistito dalla fortuna, infatti lanciando la sessione di debug in questo modo l’istanza di E17 partiva, ma terminava senza specificare un errore appena aver mostrato il desktop.
L’alternativa rimasta era quella di accedere in qualche modo alla sessione E17 corrente nella VM, ma quest’ultima si connette al box reale tramite NAT, e l’unica interazione che si ottiene di default è un servizio RDP per accedere graficamente in remoto alla macchina; a me serviva invece una connessione SSH.

Così, Googlando, ho trovato questo documento, che spiega come creare un tunnel SSH tra host e VM.
Applicate le informazioni, sono riuscito ad accedere alla VM in questo modo:


massi@e-laptop:~$ ssh -p 2222 massi@localhost
Password:
Last login: Fri Apr 17 09:26:10 2009
Sun Microsystems Inc. SunOS 5.11 snv_101b November 2008
massi@opensolaris:~$

Bene, tramite il comando:

ps -A

sono riuscito ad ottenere il PID dell’istanza E17 in esecuzione, per poi attaccarlo al debugger:


massi@opensolaris:~$ gdb --pid=PID --se=/opt/e17/bin/enlightenment

La parte finale (–se) serve per indicare al debugger dove andare a prendere i simboli, in questo caso è stata necessaria, in tutte le altre sessioni di debug (su Linux) non mi è mai servita…

Comunque, questo è il log della sessione:


[Switching to Thread 1 (LWP 1)]
0xd0f10bb7 in __pollsys () from /lib/libc.so.1
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0xd0e83d80 in countbytes () from /lib/libc.so.1
(gdb) bt
#0 0xd0e83d80 in countbytes () from /lib/libc.so.1
#1 0xd0ed019c in _ndoprnt () from /lib/libc.so.1
#2 0xd0ed27fe in snprintf () from /lib/libc.so.1
#3 0x0813f223 in _e_fm2_icon_menu (ic=0x84605e8, obj=0x85cd530, timestamp=4856841) at e_fm.c:7881
#4 0x0813b410 in _e_fm2_cb_icon_mouse_down (data=0x84605e8, e=0x85cb808, obj=0x85dae08, event_info=0x80455b0) at e_fm.c:6330
#5 0xd0905614 in evas_object_event_callback_call (obj=0x85dae08, type=EVAS_CALLBACK_MOUSE_DOWN, event_info=0x80455b0) at evas_callbacks.c:116
#6 0xd09056e8 in evas_object_event_callback_call (obj=0x84705d0, type=EVAS_CALLBACK_MOUSE_DOWN, event_info=0x80455b0) at evas_callbacks.c:145
#7 0xd0907661 in evas_event_feed_mouse_down (e=0x85cb808, b=3, flags=EVAS_BUTTON_NONE, timestamp=4856841, data=0x0) at evas_events.c:238
#8 0xd1141bf9 in _ecore_event_evas_mouse_button (e=0x86666a8, press=ECORE_DOWN) at ecore_input.c:172
#9 0xd1141d37 in ecore_event_evas_mouse_button_down (data=0x0, type=21, event=0x86666a8) at ecore_input.c:217
#10 0xd05f8e0f in _ecore_event_call () at ecore_events.c:439
#11 0xd06049e4 in _ecore_main_loop_iterate_internal (once_only=0) at ecore_main.c:647
#12 0xd06039c9 in ecore_main_loop_begin () at ecore_main.c:96
#13 0x0808977e in main (argc=1, argv=0x8047b9c) at e_main.c:1062
(gdb) f 2
#2 0xd0ed27fe in snprintf () from /lib/libc.so.1
(gdb) list
122 // eina_stringshare_dump();
123 return 0;
124 }
125
126 /* externally accessible functions */
127 int
128 main(int argc, char **argv)
129 {
130 int i;
131 int nostartup = 0;
(gdb) f 3
#3 0x0813f223 in _e_fm2_icon_menu (ic=0x84605e8, obj=0x85cd530, timestamp=4856841) at e_fm.c:7881
7881 snprintf(buf, sizeof(buf), "*%s", strrchr(ic->info.file, '.'));
(gdb) p ic
$1 = (E_Fm2_Icon *) 0x84605e8
(gdb) p ic->info.file
$2 = 0x829a924 "Desktop"
(gdb) p buf
$3 = "*export/home/massi/Desktop\000\b\001\000\000\000�P\004\b�m\220���1\bU\024\225�\001\000\000\0004R\004\b\001\000\000\000�P\004\b�m\220��f3\b\r\000\225�p\233\234��R)\b\001\000\000\000\004Q\004\b�m\220��l3\b�m\220��\2041\b\001\000\000\000\001\000\000\000 Q\004\b�m\220�\030u3\b\001\000\000\0000Q\004\b�m\220��\2041\binfo.file, '.')
$4 = 0

Le parti evidenziate in grassetto sono quelle che ritengo più interessanti, andiamole a vedere.

La prima riga, così some tutte quelle che cominciano con (gdb), rappresenta un comando eseguito al prompt del debugger, in questo caso bt dice al debuggger di generare il backtrace, fondamentale per capire la causa dell’errore.

Le due linee successive in grassetto chiariscono il problema: la funzione snprintf ha causato il problema, chiamata internamente dalla funzione _e_fm2_icon_menu, presente nel file e_fm.c alla riga 7881. Ora si sa dove mettere le mani.

Scendendo più in basso, il comando f 3 dice al debugger di visualizzare il frame numero 3, visibile più su nella riga che comincia con # 3. Avendo compilato E17 e le librerie EFL con la opzione -g del compilatore, il debugger è in grado di mostrare il codice sorgente incriminato, permettendo di ispezionare le variabili tramite i nomi utilizzati nel codice sorgente stesso.

Ed infatti è quello che ho fatto, nelle righe successive; i dati sembravano coerenti, però mi sono accorto che il la funzione strrchr non avrebbe riportato niente, visto che di carattere punto non ce ne erano nella stringa di confronto. E quì il dubbio, cosa significa niente? La documentazione riporta

If the value is not found, the function returns a null pointer

Ecco il cuore del problema, evidentemente l’implementazione della snprintf in Linux e OpenSolaris è differente, e mentre Linux è in grado di gestire in qualche modo un NULL pointer, OpenSolaris si arrabbia platealmente!

Con un piccolo programma di test ho avuto la conferma definitiva, e sono stato in grado di fornire parecchie informazioni utili al core team (si, avrei potuto patchare io il codice, visto che ho il commit bit, ma la parte in questione è cruciale per il DE, e mi sembra più corretto parlare prima con gli sviluppatori senior…).

Questa esperienza è stata veramente interessante, ho avuto modo di apprezzare la comodità di utilizzo di una macchina virtuale, la qualità di OpenSolaris come sistema desktop, la difficoltà di creare del codice veramente portabile, e soprattutto la semplicità con la quale Enlightenment si adatta ai vari sistemi :)

 

Alla prox

 

[tags]debugging,virtualbox,opensolaris[/tags]