Discussione:
Segnalare errore nella malloc
(troppo vecchio per rispondere)
arkkimede
2014-04-12 16:21:20 UTC
Permalink
Ciao a tutti.
Avrei il seguente problema:

ogni qual volta devo utilizzare l'istruzione malloc per riservare un blocco di memoria, mi preoccupo sempre di verificare che il puntatore non sia NULL.

Questo modo di programmare pero' mi appesantisce un po' il codice.

Per snellirlo allora ho deciso di utilizzare una subroutine (malloc_chk) che si preoccupa di verificare se c'e' stato un problema.

Mi resta pero' ancora un grattacapo, come segnalare dove e' accaduto il problema.

Al momentosto sto utilizzando degli id numerici i quali dovrebbero essere tutti
diversi per garantirmi l'univocita' della eventuale segnalazione. Scrivendo il codice pero' mi sono reso conto che questo metodo non e' decisamente il massimo.

Ogni tanto devo ripassarmi tutto il sorgente per risistemare gli id.

Non avreste una soluzione alternativa?

Grazie
Archaeopteryx
2014-04-12 16:52:28 UTC
Permalink
Post by arkkimede
Non avreste una soluzione alternativa?
SE ho capito il tuo problema, io faccio una cosa
orribilmente zozza, ovvero passare a routines di questo
genere anche il nomefile e numero di linea, almeno
rintraccio dove sta il probema. Ci sono macro già definite
e puoi fare qualcosa del genere:

void *myalloc (__FILE__,__LINE__,size, etc);

adesso sarò lapidato, lo so :D

Certo che se la velocità è un parametro critico, mettere
altri due parametri sullo stack non è il massimo... e ci
vorranno soluzioni più sofisticate, per esempio una
compilazione condizionale per sostituire la routine di
allocazione con una versione "release".
--
Un corpo immerso in un liquido si bagna.
Un corpo immerso nel cemento sapeva troppo.
Un corpo immerso in un altro corpo si diverte.
Un corpo immerso in due corpi è un'orgia.
enoquick
2014-04-12 17:57:38 UTC
Permalink
Post by Archaeopteryx
Post by arkkimede
Non avreste una soluzione alternativa?
SE ho capito il tuo problema, io faccio una cosa
orribilmente zozza, ovvero passare a routines di questo
genere anche il nomefile e numero di linea, almeno
rintraccio dove sta il probema. Ci sono macro già definite
void *myalloc (__FILE__,__LINE__,size, etc);
adesso sarò lapidato, lo so :D
Certo che se la velocità è un parametro critico, mettere
altri due parametri sullo stack non è il massimo... e ci
vorranno soluzioni più sofisticate, per esempio una
compilazione condizionale per sostituire la routine di
allocazione con una versione "release".
Basta trasformare myalloc in macro e il client non deve specificare
__FILE__ e __LINE__
Inoltre non soffre di una ulteriore chiamata a funzioni
enoquick
2014-04-12 18:01:29 UTC
Permalink
Post by arkkimede
Ciao a tutti.
[CUT]

Se l' esito dell' allocazione non deve essere
gestita in nessun modo dal client
basta fare un abort in caso di allocazione non riuscita
Per il resto basta una macro dove usi __FILE__ e __LINE__
Massimo Soricetti
2014-04-12 21:42:52 UTC
Permalink
Post by arkkimede
Per snellirlo allora ho deciso di utilizzare una subroutine (malloc_chk) che si preoccupa di verificare se c'e' stato un problema.
Mi resta pero' ancora un grattacapo, come segnalare dove e' accaduto il problema.
Già ti dissero.

Aggiungo che invece di appesantire la chiamata a malloc con __FILE__ e
__LINE__ potresti sollevare un'eccezione e lasciare che sia l'handler a
segnalare dove e quando malloc è fallita. Già che c'è, un handler
potrebbe pure fare qualche check diagnostico e dare altre info, dato che
a quel punto il tempo di esecuzione non è più un fattore critico :-)
enoquick
2014-04-12 22:18:55 UTC
Permalink
Post by Massimo Soricetti
Post by arkkimede
Per snellirlo allora ho deciso di utilizzare una subroutine (malloc_chk) che si preoccupa di verificare se c'e' stato un problema.
Mi resta pero' ancora un grattacapo, come segnalare dove e' accaduto il problema.
Già ti dissero.
Aggiungo che invece di appesantire la chiamata a malloc con __FILE__ e
__LINE__ potresti sollevare un'eccezione e lasciare che sia l'handler a
segnalare dove e quando malloc è fallita. Già che c'è, un handler
potrebbe pure fare qualche check diagnostico e dare altre info, dato che
a quel punto il tempo di esecuzione non è più un fattore critico :-)
In C non esiste una vera gestione delle eccezioni
E' comunque possibile fare qualcosa con l' accoppiata setjmp/longjmp

Un esempio:

http://www.on-time.com/ddj0011.htm
Massimo Soricetti
2014-04-14 00:49:22 UTC
Permalink
Post by enoquick
In C non esiste una vera gestione delle eccezioni
Vero, ma uno può pure affidarsi al sistema operatyivo, x esempio soto
windows si possono installare gestori usando le API SEH... sotto linux
non so, ma suppongo che qualcosa ci sia pure lì
enoquick
2014-04-14 01:53:40 UTC
Permalink
Post by Massimo Soricetti
Post by enoquick
In C non esiste una vera gestione delle eccezioni
Vero, ma uno può pure affidarsi al sistema operatyivo, x esempio soto
windows si possono installare gestori usando le API SEH... sotto linux
non so, ma suppongo che qualcosa ci sia pure lì
Ma non è portabile
Sotto linux, essendo unix, non esistono le eccezioni tipo SEH
Al massimo esistono i segnali
Massimo Soricetti
2014-04-14 11:58:31 UTC
Permalink
Post by enoquick
Ma non è portabile
E da quando in qua è un problema?

#ifdef _WIN32

// codice windows

#else

// codice linux

#endif

Ammesso poi che sia necessario... tanti programmi esistono solo per win
o solo per linux e nessuno si lamenta.
Post by enoquick
Sotto linux, essendo unix, non esistono le eccezioni tipo SEH
Al massimo esistono i segnali
Che assolvono gli stessi compiti. Quindi, di nuovo, problemi zero.
enoquick
2014-04-14 12:54:01 UTC
Permalink
Post by Massimo Soricetti
Post by enoquick
Ma non è portabile
E da quando in qua è un problema?
#ifdef _WIN32
// codice windows
#else
// codice linux
#endif
Ammesso poi che sia necessario... tanti programmi esistono solo per win
o solo per linux e nessuno si lamenta.
Post by enoquick
Sotto linux, essendo unix, non esistono le eccezioni tipo SEH
Al massimo esistono i segnali
Che assolvono gli stessi compiti. Quindi, di nuovo, problemi zero.
I segnali sono pochi e non portabili, in piu non portano informazioni se
non il numero del segnale stesso
Meglio una accoppiata setjmp/longjmp, non hanno limiti di uso e sono
portabili
Avevo già portato un url con esempi che riporto:

http://www.on-time.com/ddj0011.htm
f***@gmail.com
2014-04-13 07:41:29 UTC
Permalink
Post by arkkimede
Ciao a tutti.
ogni qual volta devo utilizzare l'istruzione malloc per riservare un blocco
di memoria, mi preoccupo sempre di verificare che il puntatore non sia NULL.
Giusto, come fanno tutti.
Post by arkkimede
Questo modo di programmare pero' mi appesantisce un po' il codice.
Solo se fai miliardi d'allocazioni, per miliardi di cose diverse, cosa che
dubito fortemente. Solitamente le allocazioni sono solo in poche funzioni,
nel core, fossi in te organizzerei meglio il codice.
Post by arkkimede
Per snellirlo allora ho deciso di utilizzare una subroutine (malloc_chk) che
si preoccupa di verificare se c'e' stato un problema.
Mi resta pero' ancora un grattacapo, come segnalare dove e' accaduto il problema.
Al momentosto sto utilizzando degli id numerici i quali dovrebbero essere
tutti diversi per garantirmi l'univocita' della eventuale segnalazione.
Scrivendo il codice pero' mi sono reso conto che questo metodo non e'
decisamente il massimo.
Ogni tanto devo ripassarmi tutto il sorgente per risistemare gli id.
Non avreste una soluzione alternativa?
Come ti hanno già detto puoi wrappare la malloc ma, io la butto lì, il tuo
problema non è la malloc, ma la gestione del flow degli errori. Se hai
un errore che riguarda un altra cosa che fai? Non puoi usare lo stesso flow
per gli errori sulle funzioni di allocazione?

Ciao!
arkkimede
2014-04-13 10:33:44 UTC
Permalink
Post by f***@gmail.com
Post by arkkimede
Ciao a tutti.
ogni qual volta devo utilizzare l'istruzione malloc per riservare un blocco
di memoria, mi preoccupo sempre di verificare che il puntatore non sia NULL.
Giusto, come fanno tutti.
Post by arkkimede
Questo modo di programmare pero' mi appesantisce un po' il codice.
Solo se fai miliardi d'allocazioni, per miliardi di cose diverse, cosa che
dubito fortemente. Solitamente le allocazioni sono solo in poche funzioni,
nel core, fossi in te organizzerei meglio il codice.
Post by arkkimede
Per snellirlo allora ho deciso di utilizzare una subroutine (malloc_chk) che
si preoccupa di verificare se c'e' stato un problema.
Mi resta pero' ancora un grattacapo, come segnalare dove e' accaduto il
problema.
Al momentosto sto utilizzando degli id numerici i quali dovrebbero essere
tutti diversi per garantirmi l'univocita' della eventuale segnalazione.
Scrivendo il codice pero' mi sono reso conto che questo metodo non e'
decisamente il massimo.
Ogni tanto devo ripassarmi tutto il sorgente per risistemare gli id.
Non avreste una soluzione alternativa?
Come ti hanno già detto puoi wrappare la malloc ma, io la butto lì, il tuo
problema non è la malloc, ma la gestione del flow degli errori. Se hai
un errore che riguarda un altra cosa che fai? Non puoi usare lo stesso flow
per gli errori sulle funzioni di allocazione?
Ciao!
Prima di tutto un doveroso GRAZIE A TUTTI.
Credo che la soluzione ottimale per me sia la macro __LINE__ di cui ignoravo la conoscenza (e probabilmente ci saranno ancora tante altre cose che ignoro).
Usando quasi sempre un unico file la macro __FILE__ restiruirebbe sempre il medesimo nome del sorgente (differente sarebbe se le varie subroutine fossero in file separati. In ogni caso ho scoperto la presenza di __func__ che restituisce il valore della funzione).

Per cultura personale mi piacerebbe anche capire cosa sono e come funzionano gli handler di cui parla Massimo Soricetti. Potreste indicarmi un esempio valido?

Grazie ancora.
f***@gmail.com
2014-04-14 07:57:46 UTC
Permalink
Post by arkkimede
Credo che la soluzione ottimale per me sia la macro __LINE__ di cui ignoravo
la conoscenza (e probabilmente ci saranno ancora tante altre cose che
ignoro).
Usando quasi sempre un unico file la macro __FILE__ restiruirebbe sempre il
medesimo nome del sorgente (differente sarebbe se le varie subroutine fossero
in file separati. In ogni caso ho scoperto la presenza di __func__ che
restituisce il valore della funzione).
Continuo a dire che hai un problema d'organizzazione del codice.
Spezzetta prima in funzioncine piccole, poi in file, poi ti accorgi che
controllare due/tre volte se un'allocazione ritorna NULL è una roba che non
da fastidio.

Ciao!
Massimo Soricetti
2014-04-14 12:20:06 UTC
Permalink
Post by arkkimede
mi piacerebbe anche capire cosa sono e come funzionano gli handler
Un handler (per esteso exception handler; gestore di eccezioni, tradotto
in italiano) è un pezzo di codice a cui il tuo programma salta
automaticamente quando si verifica un'eccezione di un certo tipo (quale
handler deve trattare quale eccezione lo decidi tu). Lo scopo degli
handler è quello di metterci una pezza, se possono, altrimenti terminare
il programma in modo un pò meno cinofallico.
Esempio di eventi che generano eccezioni: divisione per zero, underflow,
overflow.

Puoi definire tu delle eccezioni tue oltre a quelle definite dal
sistema, se ti fa comodo: io suggerivo proprio di creare un'eccezione
apposta per l'out of memory della malloc e chiamarla da software (in
gergo "lanciare un'eccezione", "sollevare un'eccezione").

In C++ la gestione delle eccezioni è più completa, in C normale invece è
molto primitiva.

Anche i sistemi operativi moderni, a prescindere dal linguaggio di
programmazione quindi, forniscono meccanismi per la gestione delle
eccezioni: sotto Windows ci sono gli Structured Exception Handler, sotto
Linux c'è il meccanismo dei segnali (SIG_TERM, SIG_HUP ecc. ecc.).
Chiaro che sono sistemi non portabili: se decidi di usarli, quando fai
il porting sono 'azzi tuoi, come diceva enoquick
m***@gmail.com
2014-04-17 17:35:57 UTC
Permalink
Post by arkkimede
Ciao a tutti.
ogni qual volta devo utilizzare l'istruzione malloc per riservare un blocco di memoria, mi preoccupo sempre di verificare che il puntatore non sia NULL.
Per fortuna.
Post by arkkimede
Questo modo di programmare pero' mi appesantisce un po' il codice.
Rassegnati, in C si fa così.
E credimi se ti dico che, fatto in modo coerente, non appesantisce.
Vedi il codice di grossi progetti come Nginx, per citarne uno scitto bene.

L'unica cosa che ti consiglio è di scriverti un wrapper che loggi i dettagli dell'eventuale errore; ad esempio

void *my_malloc(...)
{
void *p = malloc(...)
if (!p) {
my_log("malloc() failed")
}

return p;
}


dove my_log, ad esempio, è una macro che in caso di debug stampa su stderr anche __FILE__ e __LINE__,
Post by arkkimede
Per snellirlo allora ho deciso di utilizzare una subroutine (malloc_chk) che si preoccupa di verificare se c'e' stato un problema.
Mi resta pero' ancora un grattacapo, come segnalare dove e' accaduto il problema.
Restituire NULL :)
Oppure se ritieni che l'errore sia ingestibile, puoi chiamare abort.
Ma di solito è meglio non farlo, altrimenti poi inizi ad usare abort ovinque.
Post by arkkimede
Al momentosto sto utilizzando degli id numerici i quali dovrebbero essere tutti
diversi per garantirmi l'univocita' della eventuale segnalazione. Scrivendo il codice pero' mi sono reso conto che questo metodo non e' decisamente il massimo.
Come detto, chiama la tua macri di log com un messaggio descrittivo come "malloc() failed", e fai gestire alla macro e alla funzione sottostante i vari dettagli.
Post by arkkimede
[...]
P.S.: scusate per il quoting, sto usando google groups

Ciao Manlio
f***@gmail.com
2014-04-17 22:11:53 UTC
Permalink
Post by m***@gmail.com
[...]
+1
Post by m***@gmail.com
P.S.: scusate per il quoting, sto usando google groups
Pur'io, ma cancello le linee in più ;)

Ciao!
arkkimede
2014-04-18 05:02:05 UTC
Permalink
Grazie
m***@gmail.com
2014-04-18 12:34:22 UTC
Permalink
Post by f***@gmail.com
Post by m***@gmail.com
P.S.: scusate per il quoting, sto usando google groups
Pur'io, ma cancello le linee in più ;)
Non solo. Dalla textarea per l'editing dei messaggi, le linee lunghe avevano il quoting rotto, ma sembra che il testo inviato sia corretto.


Ciao Manlio
LutherBlissett
2014-05-09 07:07:57 UTC
Permalink
Post by m***@gmail.com
L'unica cosa che ti consiglio è di scriverti un wrapper
che loggi i dettagli dell'eventuale errore; ad esempio
void *my_malloc(...)
{
void *p = malloc(...)
if (!p) {
my_log("malloc() failed")
}
return p;
}
dove my_log, ad esempio, è una macro che in caso di debug stampa su stderr anche __FILE__ e __LINE__,
Cosi' pero' se my_log e' una macro e viene espansa dentro il wrapper il
risultato sara' che verrano loggati sempre gli stessi valori di
__FILE__ e __LINE__ (quelli corrispondenti alla riga dentro il wrapper).
E' il wrapper che dovrebbe prevedere i paramentri di input per
__FILE__ e __LINE__ e poi si dovrebbe definire una macro per il
wrapper...
m***@gmail.com
2014-05-09 08:09:29 UTC
Permalink
Post by LutherBlissett
L'unica cosa che ti consiglio � di scriverti un wrapper
che loggi i dettagli dell'eventuale errore; ad esempio
void *my_malloc(...)
{
void *p = malloc(...)
if (!p) {
my_log("malloc() failed")
}
return p;
}
dove my_log, ad esempio, � una macro che in caso di debug stampa su stderr anche __FILE__ e __LINE__,
Cosi' pero' se my_log e' una macro e viene espansa dentro il wrapper il
risultato sara' che verrano loggati sempre gli stessi valori di
__FILE__ e __LINE__ (quelli corrispondenti alla riga dentro il wrapper).
E' il wrapper che dovrebbe prevedere i paramentri di input per
__FILE__ e __LINE__ e poi si dovrebbe definire una macro per il
wrapper...
#define my_log(msg) my_log_core(__FILE__, __LINE__, msg)

Mi sembrava ovvio.


Ciao Manlio
Giacomo Degli Esposti
2014-05-09 08:32:26 UTC
Permalink
Post by m***@gmail.com
Post by LutherBlissett
L'unica cosa che ti consiglio � di scriverti un wrapper
che loggi i dettagli dell'eventuale errore; ad esempio
void *my_malloc(...)
{
void *p = malloc(...)
if (!p) {
my_log("malloc() failed")
}
return p;
}
dove my_log, ad esempio, � una macro che in caso di debug stampa su stderr anche __FILE__ e __LINE__,
Cosi' pero' se my_log e' una macro e viene espansa dentro il wrapper il
risultato sara' che verrano loggati sempre gli stessi valori di
__FILE__ e __LINE__ (quelli corrispondenti alla riga dentro il wrapper).
E' il wrapper che dovrebbe prevedere i paramentri di input per
__FILE__ e __LINE__ e poi si dovrebbe definire una macro per il
wrapper...
#define my_log(msg) my_log_core(__FILE__, __LINE__, msg)
Mi sembrava ovvio.
Credo che si riferisca al fatto che anche definendo my_log in quel
modo i valori di __FILE__ e __LINE__ si riferiscono a file e riga della
funzione my_malloc quindi sono sempre gli stessi, mentre bisognerebbe
vedere file e riga della *chiamata* a my_malloc().

Probabilmente la soluzione e' di definire anche my_malloc come macro
invece che come funzione.

ciao
Giacomo
enoquick
2014-05-09 12:21:20 UTC
Permalink
Post by Giacomo Degli Esposti
Post by m***@gmail.com
Post by LutherBlissett
L'unica cosa che ti consiglio � di scriverti un wrapper
che loggi i dettagli dell'eventuale errore; ad esempio
void *my_malloc(...)
{
void *p = malloc(...)
if (!p) {
my_log("malloc() failed")
}
return p;
}
dove my_log, ad esempio, � una macro che in caso di debug stampa su stderr anche __FILE__ e __LINE__,
Cosi' pero' se my_log e' una macro e viene espansa dentro il wrapper il
risultato sara' che verrano loggati sempre gli stessi valori di
__FILE__ e __LINE__ (quelli corrispondenti alla riga dentro il wrapper).
E' il wrapper che dovrebbe prevedere i paramentri di input per
__FILE__ e __LINE__ e poi si dovrebbe definire una macro per il
wrapper...
#define my_log(msg) my_log_core(__FILE__, __LINE__, msg)
Mi sembrava ovvio.
Credo che si riferisca al fatto che anche definendo my_log in quel
modo i valori di __FILE__ e __LINE__ si riferiscono a file e riga della
funzione my_malloc quindi sono sempre gli stessi, mentre bisognerebbe
vedere file e riga della *chiamata* a my_malloc().
Probabilmente la soluzione e' di definire anche my_malloc come macro
invece che come funzione.
ciao
Giacomo
E poi si vuole sapere dove è stata chiamata quella funzione e via dicendo
Uno stack delle chiamate (stack frame)
Non esiste un modo portabile per avere lo stack frame
Meglio un abort, al massimo abilitare la produzione del core file e se
proprio si vuole vedere lo stack delle chiamate lasciare i simboli di
debug ed usare il debugger per verificare lo stack frame
m***@gmail.com
2014-05-10 18:09:51 UTC
Permalink
Post by Giacomo Degli Esposti
[...]
Post by m***@gmail.com
#define my_log(msg) my_log_core(__FILE__, __LINE__, msg)
Mi sembrava ovvio.
Credo che si riferisca al fatto che anche definendo my_log in quel
modo i valori di __FILE__ e __LINE__ si riferiscono a file e riga della
funzione my_malloc quindi sono sempre gli stessi, mentre bisognerebbe
vedere file e riga della *chiamata* a my_malloc().
Probabilmente la soluzione e' di definire anche my_malloc come macro
invece che come funzione.
Vero, my_log era definita come funzione generica
Per fare il debug di ciascuna chiamata al sistema va deciso caso per caso.
Post by Giacomo Degli Esposti
[...]
Ciao Manlio
LutherBlissett
2014-05-12 07:14:30 UTC
Permalink
Post by m***@gmail.com
Mi sembrava ovvio.
Anche a me... :-)
ulisse
2014-09-11 08:11:23 UTC
Permalink
Post by arkkimede
Ciao a tutti.
ogni qual volta devo utilizzare l'istruzione malloc per riservare un blocco di memoria, mi preoccupo sempre di verificare che il puntatore non sia NULL.
Questo modo di programmare pero' mi appesantisce un po' il codice.
parecchi anni fa su Macintosh usavo quanto illustrato qui:

http://www.mactech.com/articles/develop/issue_11/Parent_final.html

da qualche parte in rete si dovrebbe trovare ancora Exceptions.h

U


----Android NewsGroup Reader----
http://www.piaohong.tk/newsgroup

Loading...