• Programmazione Android
  • CORSI ONLINE
  • Web Agency

Logo

Corsi di programmazione web e mobile online
Navigation
  • Home
  • CORSI ONLINE
  • Tutorial Pratici
  • GUIDE COMPLETE
    • Corso completo di C
    • Corso videogame con Cocos2d
    • Programmazione Cocoa Touch
  • Sezioni
    • Libri e manuali
    • Tips & Tricks
    • Risorse utili
    • Strumenti di Sviluppo
    • Materiale OpenSource
    • Framework
    • Guide Teoriche
    • Guide varie
    • Grafica e Design
    • iPad
    • News
    • Video Tutorial
    • Windows Phone
  • Pubblicità
  • About
    • Chi siamo
    • Pubblicazioni
    • Collabora
    • Sostieni devAPP

GDB (GNU Project Debugger): Debug del linguaggio Assembly

By Junior B. | on 19 Aprile 2011 | 6 Comments
Senza categoria

Assembly? Ma il linguaggio di iOS/Mac OS non è l’Objective-C? Si, certo, ma a volte conviene sfruttare qualcosa che va ancora più a basso livello per risolvere i nostri problemi, in questi casi parliamo dell’Assembly. In questi anni d’esperienza ho incontrato moltissimi sviluppatori, credo di averne conosciuti più di 200 (sviluppatori che vantano applicazioni come Delibar, Delicious Library, BBEdit, ecc…), ma pochissimi di loro (forse li riesco a contare sulle dita di una mano) mi hanno detto di aver sfruttato appieno le potenzialità di GDB (GNU Project Debugger). Ecco perchè ho deciso di scrivere questo piccolo quickstart, che non sarà altro che un punto di partenza nel caso in cui siate interessati ad approfondire la questione, ma veniamo al dunque.

Quando occorre scendere ad un livello più basso?

Ci sono volte in cui capita di scrivere metodi (magari noiosi) e poi ci si tritrova bloccati per un problema di memoria che non riusciamo a risolvere. Spesso si tratta di errori banali, altre volte di errori un po’ più complessi, altre volte ancora si è talmente sicuri del proprio codice che si arriva a pensare che sia Apple ad aver commesso un errore (mai commettere questo sbaglio :P).

In ognuno di questi casi, saper sfruttare GDB non è un male, anzi, ci si mette un po’ di tempo a prendere confidenza, ma una volta appreso il funzionamento, non si riesce più a farne a meno.

Per questa guida sfrutteremo un piccolo esempio pratico, di cui trovate il sorgente disponibile per il download a fondo articolo. Si tratta di un banalissimo programma per iPhone che mostra in una tabella gli URL di 10 technotes presenti sul sito Apple (che consiglio di leggere, male certamente non farà :D), prelevati da un semplicissimo array. Al tap su di una cella (row), verrà inviato un messaggio all’applicazione di aprire Safari Mobile aprendo l’URL associato. Insomma, un programmino davvero banale e di facile comprensione.

Vediamo il codice del nostro programma di esempio

Iniziamo a vedere il codice del nostro esempio partendo dal file di intestazione (.h) del nostro RootViewController:


GDB-GNU-Project-Debugger-Debug-del-linguaggio-Assembly-01

Nel file di implementazione (.m) ho quindi istanziato l’array che contiene gli URL che andremo a mostrare e ad aprire direttamente in Safari:


GDB-GNU-Project-Debugger-Debug-del-linguaggio-Assembly-02

(I più attenti molto probabilmente si saranno già accorti dell’errore, ma andiamo avanti ;))

Mentre i metodi che si occuperanno di avviare Safari sono definiti nel seguente modo:


GDB-GNU-Project-Debugger-Debug-del-linguaggio-Assembly-03

Alcuni di voi si chiederanno perchè ho fatto tutta questa strada per aprire un semplice URL in Safari? La risposta è complessa, cercherò comunque di non complicare troppo le cose cercando si spegare in modo chiaro: in pratica ho bisogno di diverse iterazioni affinchè l’errore si verifichi nel simulatore. Infatti, il simulatore, dispone di tantissima memoria (quella libera nei nostri sistemi) evidentemente molto più rispetto a quella disponibile normalmente nei nostri iPhone.

Ok, il codice del nostro piccolo esempio è tutto qui. Facciamo partire la nostra applicazione, e noteremo che questa funzionerà benissimo… apparentemente!


GDB-GNU-Project-Debugger-Debug-del-linguaggio-Assembly-04

Se premiamo su una qualsiasi riga della tabella, si aprirà Safari senza nessun problema. A questo punto non mi resta che provare a fare lo scroll e siamo a posto: metto il dito, trascino la mia tabella e magicamente… CRASH!

A primo impatto, penso che non sia possibile, il codice è giusto! Riavvio l’applicazione e riprovo! Risultato: stesso comportamento, quando scrollo l’applicazione questa va in crash, ma stavolta è avvenuto in un punto diverso rispetto al precedente.. brutto segnale. Mi rimbocco le maniche e mi preparo a cercare di capire dove sta il problema.


GDB-GNU-Project-Debugger-Debug-del-linguaggio-Assembly-05

Come potete vedere dall’immagine qui sopra, il nostro caro GDB non da nessun errore se non un (fastidiosissimo) EXC_BAD_ACCESS.

Siccome io so che l’array è popolato, e lo capiamo dal fatto che i miei URL sono correttamente stampati nella tabella, provo a cercare di capire cosa non sta effettivamente funzionando e piazzo un bel breakpoint durante il runtime (subito dopo aver fatto partire l’applicazione), nella linea dove prima l’applicazione è andata in crash, quindi riprovo ad eseguire uno scroll. Faccio uno “StepIn” così da entrare nel metodo:

-(NSString *) getFileNameForIndex:(NSInteger)index

Una volta al suo interno ricevo di nuovo un segnale di errore, ma questa volta si tratta di un SIGABRT:


GDB-GNU-Project-Debugger-Debug-del-linguaggio-Assembly-06

A questo punto decido quindi di stampare il codice Assembly generato ed ecco il risultato:


GDB-GNU-Project-Debugger-Debug-del-linguaggio-Assembly-07

Nelle architetture ARM, le funzioni vengono gestite nel seguente modo:

  • primi 4 parametri passati via registro (gli altri vanno nello stack e diventa più complessa la gestione) con i seguenti nomi: $r0, $r1, $r2, $r3
  • l’indirizzo di ritorno viene passato dal registro: $lr
  • il risultato viene passato nel registro: $eax

NOTA: ci sono delle eccezioni, piuttosto rare, ma ci sono 😉 ma tutto sommato, come linea guida, questa è più che sufficiente per i nostri scopi.

Sono curioso di capire cosa ritorna la mia funziona, per logica stampando con il comando ‘po $eax’ dovrei ottenere un oggetto stringa, invece ottengo il seguente messaggio:

Can’t print the description of a NIL object.

Che è in linea con quanto XCode mi segnala, ovvero un errore di memoria. Continuo a curiosare ed a questo punto ho una soluzione, spezzettare la funzione:

-(NSString *) getFileNameForIndex:(NSInteger)index

in modo tale da avere più pezzetti da controllare, via sicuramente più semplice, ma prima, per scaramanzia, stampo il contenuto della variabile dataArray, giusto per essere sicuri che sia tutto ok:


GDB-GNU-Project-Debugger-Debug-del-linguaggio-Assembly-08

Come potete vedere.. non è tutto ok… dataArray dovrebbe essere un array, mentre in realtà, in questo caso, è un NSBundle con retainCount uguale a 4:


GDB-GNU-Project-Debugger-Debug-del-linguaggio-Assembly-09

A questo punto è chiaro che il problema è proprio con la variabile dataArray, aggiungo un breakpoint quando creo il dataArray e faccio ripartire l’applicazione.

Mi fermo subito dopo l’init dell’array e so già che, visto il funzionamento iniziale della app, il suo contenuto fino a questo punto è corretto, ma deve esserci qualcosa che rilascia l’array, decido di stampare lo stack di autorelease:


GDB-GNU-Project-Debugger-Debug-del-linguaggio-Assembly-10

Noto che al top dello stack (cioè appena uscirò dalla funzione, ‘viewDidLoad’ sarà rilasciato) c’è un NSArrayI, per curiosità stampo il suo contenuto:


GDB-GNU-Project-Debugger-Debug-del-linguaggio-Assembly-11

ED ECCO IL NOSTRO ARRAY! E l’errore è veramente banale: ‘NSArray arrayWithObjects:’ crea un array autorelease che verrà rilasciato appena il pool sarà “drainato”, cioè all’uscita della funzione.

L’applicazione funziona correttamente subito dopo il lancio, perchè i registri non vengono puliti immediatamente e nel simulatore, con memoria di 2, 4, 6, 8 o più giga, non è sempre necessaria questa procedura, ecco perchè spesso ci sono problemi che sorgono solo sui device e non nei simulatori. Ecco quindi che il problema si verifica solo in seconda istanza.

Sostituisco il metodo con [[NSArray alloc] init…] e tutto funziona a meraviglia!

Se volete approfondire l’argomento, vi consiglio di leggere la nota tecnica TN2239 sul sito della Apple, mentre se avete già qualche base di GDB, allora una bella Reference Card per GDB sicuramente non guasta. 😉

Eccovi il sorgente del progetto di esempio utilizzato nella nostra guida: AssemblyTest

Share this story:
  • tweet

Tags: debug applicazioni iphoneEXC_BAD_ACCESSGDBgestione memoria iPhoneGNU Project DebuggerGuide varieSIGABRT

Recent Posts

  • Parte il percorso programmatori iOS in Swift su devACADEMY.it

    20 Dicembre 2017 - 0 Comment
  • Android, crittografare dati velocemente con Encryption

    24 Settembre 2018 - 0 Comment
  • Sql2o, accesso immediato ai database tramite Java

    3 Settembre 2018 - 0 Comment
  • Okio, libreria per ottimizzare l’input/output in Java

    27 Agosto 2018 - 0 Comment

Related Posts

  • Chisel: il coltellino svizzero del debug iOS e OS X

    15 Ottobre 2014 - 0 Comment
  • Xcode 4: scopriamo come possiamo usare l’analizzatore statico per trovare alcuni bug nel nostro codice

    27 Maggio 2011 - 6 Comments
  • L#016 – Gestione della memoria: UIViewController, loadView e viewDidUnload

    29 Marzo 2011 - 1 Comment

Author Description

Nato in Brasile, ho vissuto buona parte della mia vita in Svizzera, ho sempre avuto la passione per l'elettronica e l'informatica. Mi sono diplomato come Elettronico Multimediale per poi frequentare la Scuola Universitaria Professionale della Svizzera Italiana (SUPSI) in Ingegneria Informatica laureandomi nel 2007. Dal 2007 sviluppo su iPhone (fin dal primo SDK ufficiale) e web (Java, Scala e PHP). Da Settembre 2010 vivo e lavoro a Londra (Portable Pixels) come iOS developer a tempo pieno. Ho anche un blog personale sul mondo Apple ed Internet in generale chiamato: ThinkDiff.ch.

6 Responses to “GDB (GNU Project Debugger): Debug del linguaggio Assembly”

  1. 19 Aprile 2011

    cikpis

    Potevi anche mettere un retain alla fine cioè:

    dataArray = [[NSArray arrayWithObject:Object 1…..Object n-1, nil] retain];

  2. 19 Aprile 2011

    gian

    Bellissimo articolo,
    anche io cerco di utilizzare al massimo il GDB anche se a volte è decisamente ostico…

    Posso chiederti una cosa riguardo l’AutoreleasePool?! Nell’articolo scrivi che la funzione drain verrà richiamata appena uscirai dalla funzione “viewDidLoad”. E’ sistematico questo comporamento?!
    Sapevo che la funzione drain veniva chiamata in modo arbitrario dal SO in base ad eventi casuali (come l’interazione dell’utente, etc. etc.).

    Grazie mille e complimenti ancora per l’articolo..
    G.

  3. 19 Aprile 2011

    ignazioc

    grande articolo! potessi leggerne ogni giorno di articoli così!! complimenti…devo smetterla di fare debug a forza di printf! 🙂

  4. 19 Aprile 2011

    Xfight

    Ottimo articolo !

  5. 21 Aprile 2011

    Junior B.


    gian:

    Bellissimo articolo,
    anche io cerco di utilizzare al massimo il GDB anche se a volte è decisamente ostico…

    Posso chiederti una cosa riguardo l’AutoreleasePool?! Nell’articolo scrivi che la funzione drain verrà richiamata appena uscirai dalla funzione “viewDidLoad”. E’ sistematico questo comporamento?!
    Sapevo che la funzione drain veniva chiamata in modo arbitrario dal SO in base ad eventi casuali (come l’interazione dell’utente, etc. etc.).

    Grazie mille e complimenti ancora per l’articolo..
    G.

    é buona norma pensarlo… diciamo che non é esattamente così, il comportamento dell’autorelease pool iniziare é arbitrario, ecco perché l’applicazione funziona in prima istanza, dopo il lancio e non crasha subito… bisognerebbe anche tenere in considerazione i registri, ma vabbeh… diciamo che é buona norma pensare che dopo l’uscita di un loop o la fine di una funzione, l’autorelease pool rilascia tutti gli oggetti che devono essere rilasciati. Non esiste un modo di sapere quando, ma pensarla così aiuta molto, anche perché poi si giocherebbe con il fuoco 😉

  6. 21 Aprile 2011

    Junior B.


    cikpis:

    Potevi anche mettere un retain alla fine cioè:

    dataArray = [[NSArray arrayWithObject:Object 1…..Object n-1, nil] retain];

    anche, ma non sono un gran fan di questa dicitura ed il motivo é che il codice diventa meno leggibile… se ho un [[NSArray alloc… so che poi dovrà per forza esserci un release da qualche parte, ma se scrivo un ‘retain’ dopo un ‘arrayWith…’, questo potrebbe anche scapparmi… é questione di gusto, ma preferisco tenere una certa ‘grammatica’, anche perché lavorando in team semplifica molto la vita quando si cercano i bug 😀

Leave a Reply

Your email address will not be published. Required fields are marked *


*
*

Corso online di programmazione android e java

SEZIONI

  • Android
  • Comunicazioni
  • Contest
  • Corsi ed Eventi
  • Corso completo di C
  • Corso programmazione videogiochi
  • Framework
  • Grafica e Design
  • Guida rapida alla programmazione Cocoa Touch
  • Guide Teoriche
  • Guide varie
  • iPad
  • Le nostre applicazioni
  • Libri e manuali
  • Materiale OpenSource
  • News
  • Pillole di C++
  • Progetti completi
  • Risorse utili
  • Strumenti di Sviluppo
  • Swift
  • Tips & Tricks
  • Tutorial Pratici
  • Video Tutorial
  • Windows Phone

Siti Amici

  • Adrirobot
  • Allmobileworld
  • Apple Notizie
  • Apple Tribù
  • Avvocato360
  • Blog informatico 360°
  • bubi devs
  • fotogriPhone
  • GiovaTech
  • iApp-Mac
  • iOS Developer Program
  • iPodMania
  • MelaRumors
  • Meritocracy
  • SoloTablet
  • TecnoUser
  • Privacy & Cookie Policy
©2009-2018 devAPP - All Rights Reserved | Contattaci
devAPP.it è un progetto di DEVAPP S.R.L. - Web & Mobile Agency di Torino
Str. Volpiano, 54 - 10040 Leini (TO) - C.F. e P.IVA 11263180017 - REA TO1199665 - Cap. Soc. € 10.000,00 i.v.

devACADEMY.it

Vuoi imparare a programmare?

Iscriviti e accedi a TUTTI i corsi con un’unica iscrizione.
Oltre 70 corsi e migliaia di videolezioni online e in italiano a tua disposizione.

ISCRIVITI SUBITO