Autore Topic: Embedded systems programmers worldwide earn failing grades in C  (Letto 10775 volte)

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #15 il: 05 Luglio 2013, 15:27:20 »
Ah, poi visto che tu sei infognato nel linux kernel, stai pur tranquillo che peggio di quel codice non si può scrivere. Credo che Linux contenga la quantità peggiore di porcate illegibili che un programmatore possa partorire. In generale un buon C scritto come si deve, senza macro e senza trick del kaiser, si lascia leggere molto facilmente. In C++ è ancora meglio, perchè ci vogliono meno righe per esprimere lo stesso algoritmo.

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #16 il: 05 Luglio 2013, 15:34:42 »
Ma vedi che stai prendendo un abbaglio.
Tutto il flusso si dirama in modo molto più prevedibile:

Codice: [Seleziona]
try
{

  // Flusso normale


}
catch (NetworkException &e)
{
 // Flusso per quando cade la linea
}
catch (FilesystemException &e)
{
// Flusso per quando scoppia il disco
}
catch (Exception &e)
{
// Flusso per eccezioni generiche
}

Non devi impazzire con flag booleani e if innestati. Anche la macchina può analizzare molto più facilmente queste situazioni.

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #17 il: 05 Luglio 2013, 15:38:28 »
Allora e' un fattore commerciale ? Il pacchetto "exceptions" per VectorCast(C++) costa un ulteriore esborso di 12.000 testoni.


Offline Z80Fan

  • Administrator
  • Guru
  • *****
  • Post: 1671
  • Karma: +13/-2
    • Mostra profilo
    • http://z80fan.altervista.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #18 il: 05 Luglio 2013, 15:53:51 »
Se il C è usatissimo in embedded un motivo c'è :P

Secondo me é per tradizione, perché come detto, c++ senza eccezioni e funzioni virtuali non é più pesante del C, e hai ancora il vantaggio di avere classi e controllo più stretto dei tipi.

@Z80Fan: sicuramente sarebbe interessante approfondire la questione, anche perchè l'exception handling è una funzione comoda che consente di scrivere codice più sicuro senza dilungarsi troppo con if innestati e relativi codici di controllo.

E anche valutare le alternative: dsar aveva linkato un paper che discuteva contro le eccezioni, forse in uno dei thread che sono esplosi. :(

Offline Nonefonow

  • Guru
  • *****
  • Post: 1979
  • Karma: +36/-3
    • Mostra profilo
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #19 il: 05 Luglio 2013, 17:27:00 »
1 - L'articolo e' stato scritto nel 2010 basato su uno studio del 2007 con aggiunte nel 2009, quindi ....

2 - Embedde system sono per definizione sistemi chiusi o semi chiusi (limitati) con operativi ben definiti e quindi a lungo tempo condizionano la mente del programmatore a seguire routine gia' prestabilite e a non re-creare la routa (per cosi dire).  Limitano la liberta' creativa e di sperimentazione.  Praticamente forzano i vari programmatoria diventare pigri.

3 - Bisogna tenere in considerationi i tempi di rilascio che generalmente dal punto di vista hardware per me sono abbastanza rilassati ma dal punto di vista software sono brutali.

4 - Per una discussione piu' recente su i sistemi operativi embedded e' anche interessante questo articolo.

http://www.embedded.com/design/operating-systems/4412423/Embedded-Linux-and-Android--Friends-or-Foes-

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #20 il: 06 Luglio 2013, 16:09:33 »
Brrr..... non mi piace proprio questo metodo di debugging. Per queste cose dovresti usare lo Unit Testing e piazzare dei "throw nome_eccezione()" in giro, per vedere come si comporta.
E' il metodo che usi che è inadatto ai nuovi strumenti. Com'è che dicevano? "You can write FORTRAN in any language" :D

Sicuramente non puoi pretendere di far andare con questo sistema un progetto software fatto di migliaia di classi, non è così che farei io e come me qualsiasi altro software engineer (io non sono formalmente un "engineer", ma è come se lo fossi per il tipo di lavoro che faccio).

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #21 il: 06 Luglio 2013, 16:24:48 »
E anche se fosse? Questo metodo va contro le tecniche di buona progettazione di un software robusto. Ci potrei scrivere un libro sul perchè non si debba usare questo metodo per testare il codice. Magari lo usi al volo in un momento di debug, ma subito dopo dovresti approntare un meccanismo migliore per poter ripetere il test a seguito di modifiche future del codice.

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #22 il: 06 Luglio 2013, 16:26:55 »
Voglio dire... metti di avere un software che comprende allocazioni in 100 posti diversi (quindi parliamo di un software molto piccolo), ad ogni modifica sostanziale dovresti piazzare 100 breakpoint e testare, potenzialmente, da un minimo di 100 ad un massimo di 2 elevato 100 possibili combinazioni di eventi PASS/FAIL sull'allocazione.
A mano.
Per ogni modifica importante.
-.-

Offline Z80Fan

  • Administrator
  • Guru
  • *****
  • Post: 1671
  • Karma: +13/-2
    • Mostra profilo
    • http://z80fan.altervista.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #23 il: 06 Luglio 2013, 18:48:22 »
Secondo me, per tenerti la possibilità di punzecchiare il sistema nel suo ambiente operativo, non usare new direttamente ma mettilo dentro a una funzione fatta da te.
Così hai un punto singolo dove avviene l'allocazione e li ci puoi mettere tutto il codice di distruzione che vuoi. (anche robe tipo if (rand()%100) throw badalloc(); ricordati solo di toglierlo nel codice definitivo! ;D)

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #24 il: 06 Luglio 2013, 19:06:39 »
Secondo me, per tenerti la possibilità di punzecchiare il sistema nel suo ambiente operativo, non usare new direttamente ma mettilo dentro a una funzione fatta da te.
Così hai un punto singolo dove avviene l'allocazione e li ci puoi mettere tutto il codice di distruzione che vuoi. (anche robe tipo if (rand()%100) throw badalloc(); ricordati solo di toglierlo nel codice definitivo! ;D)

ma anche no, questo è un anti-pattern orrendo, pari solo al "catch em all" ( vedi numero 2 qui http://www.codinghorror.com/blog/2012/07/new-programming-jargon.html)

@TheKaneB
tu usi Cantata ? O che altro software usi ?


No, non lo conosco. Uso sistemi custom per il testing, anche se dipende da progetto a progetto. Ultimamente mi sono un po' lasciato andare al coding orrendo, un po' per colpa mia, un po' per le pressioni dall'alto perchè ci sono sempre mille cose da fare e troppo poco tempo per farle. Tuttavia mi brucia sempre quando vedo cose fatte male (anche le mie) :p

Offline Z80Fan

  • Administrator
  • Guru
  • *****
  • Post: 1671
  • Karma: +13/-2
    • Mostra profilo
    • http://z80fan.altervista.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #25 il: 06 Luglio 2013, 20:29:13 »
ma anche no, questo è un anti-pattern orrendo, pari solo al "catch em all" ( vedi numero 2 qui http://www.codinghorror.com/blog/2012/07/new-programming-jargon.html)

Ma anche si; scusa eh, tralasciando l'if rand, mica é un antipattern racchiudere un new in una funzione equivalente ma customizzabile, pensa anche solo se vuoi avere delle statistiche o log a runtime:

Codice: [Seleziona]
int* myalloc(size_t s) {
    int* p = new[s];
    cout<<"allocati "<<s<<endl;
}

Se uno ha bisogno di monitorare tutte le allocazioni (senza un hypervisor di più basso livello), non vedo altri modi.
In PIÙ, gli va bene a lui perché può intercettare quella funzione e fargli lanciare un'eccezione per vedere cosa succede, anche nel sistema stesso; ricorda che si parla di sistemi embedded, e benché puoi fare tutti gli unit test offline per verificare gli algoritmi, devi anche poter simulare fallimenti sul sistema completo per vedere se riesce a recuperare.

(l'esempio non é definitivo, ma a scrive sul cellulare solo quel pezzettino ci ho già messo un buon tempo... :()

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #26 il: 06 Luglio 2013, 20:43:41 »
Tu hai sempre qualcosa da obiettare eh? :D

Fai bene a fidarti poco, ma ti dico che si fa in modo diverso:
Preallochi tutta la memoria fisica che puoi in un unico buffer (oppure un buffer diverso per ogni subsystem), imposti la tua funzione di allocazione custom, ad esempio Two Level Segregated Fit ( http://www.gii.upv.es/tlsf/ ) come new_handler ( http://www.cplusplus.com/reference/new/set_new_handler/ ), cioè una funzione di callback che viene chiamata quando la new fallisce l'allocazione, e ti fai tutti i log che vuoi.

Questa tecnica l'ho introdotta con successo in più di un engine per videogames, l'ho usata per debuggare TUTTI i memory leaks in modo strategico e semiautomatizzato, ho spostato le allocazioni dinamiche in buffer differenziati per tipologia di oggetto (con object caching) e ancora mi ringraziano 8).

PS: alcuni vantaggi impliciti:
- puoi memorizzare lo stack ad ogni allocazione (e riusare queste info per determinare i memory leaks)
- puoi fare statistiche dettagliate su chi alloca cosa, con quale frequenza e quindi ottimizzare di conseguenza l'algoritmo di allocazione per i casi specifici
- puoi avere tanti buffer quanti sono i subsystem da gestire, riducendo la frammentazione in memoria (TLSF è molto efficiente in questo)
- puoi fare object caching, dove invece di eseguire l'allocazione vera e propria tutte le volte, tieni in memoria N oggetti preallocati e fai la "new in place" per eseguire solo il costruttore ( http://blogs.msdn.com/b/jaredpar/archive/2007/10/16/c-new-operator-and-placement-new.aspx )
- puoi piazzare un breakpoint, piazzare eccezioni ad hoc e cambiare i valori allocati come vuoi in fase di testing (anche se non mi piace, è fattibile)
« Ultima modifica: 06 Luglio 2013, 20:52:40 da TheKaneB »

Offline Z80Fan

  • Administrator
  • Guru
  • *****
  • Post: 1671
  • Karma: +13/-2
    • Mostra profilo
    • http://z80fan.altervista.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #27 il: 06 Luglio 2013, 21:20:25 »
Tu hai sempre qualcosa da obiettare eh? :D
Se non ci fossero opinioni, cosa ci servirebbe il forum per discuterne? :D

Citazione
Fai bene a fidarti poco, ma ti dico che si fa in modo diverso:
Preallochi tutta la memoria fisica che puoi in un unico buffer (oppure un buffer diverso per ogni subsystem), imposti la tua funzione di allocazione custom, ad esempio Two Level Segregated Fit ( http://www.gii.upv.es/tlsf/ ) come new_handler ( http://www.cplusplus.com/reference/new/set_new_handler/ ), cioè una funzione di callback che viene chiamata quando la new fallisce l'allocazione, e ti fai tutti i log che vuoi.
Più o meno quello che dicevo di fare io, solo che lo facevo manualmente perché non sapevo di quella funzione. :D

Ma aspetta: che senso ha mettere la funzione di allocazione come handler? Quella funzione viene chiamata quando fallisce il new, e non prende e non torna niente; come fai a usarla come allocatore? Come la usi per identificare i leak? ???

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #28 il: 06 Luglio 2013, 21:34:56 »
spè che ho fatto confusione (sono passati degli anni) :P

Nel new_handler gli avevo impostato solamente una routine per il garbage collecting (avevamo implementato gli smart pointers per gestire le risorse grafiche e gli altri oggetti che non devono stare necessariamente in Ram, ma possono essere ricostruiti a partire da alcuni file su disco).

Per settare il custom allocator basta ridefinire l'operatore new globale. Se vuoi usare un buffer differente per categoria di oggetto, fai in modo che questi oggetti siano figli di una classe che si occuperà del memory pooling, e quindi ridefinisci l'operatore new di quella classe.
In particolare, l'operatore new ridefinito si occuperà di loggare la chiamata (solo se il flag di debug è attivo), chiamerà l'allocatore e farà una placement-new su quel buffer.

In questo modo il programmatorino stagista di turno non deve sapere che bisogna chiamare "sarcazzo_new" ma userà il comune operatore che ha studiato (male) a scuola :P

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re:Embedded systems programmers worldwide earn failing grades in C
« Risposta #29 il: 06 Luglio 2013, 21:47:54 »
Per identificare i leak è "relativamente" facile, ma bisogna fare un trick:

- In fase di allocazione, prendi lo Stack Pointer, somma un certo offset (dipende dalla calling convention) e trovi il return address, stampalo a log
- Accanto al return address ci sta anche il valore salvato dello Stack Pointer precedente (su x86 solitamente si salva in EBP e poi EBP viene pushato nello stack, prima del return address, ma può variare in base all'ABI adottata)
- Ottenuto lo stack pointer precedente, ripeti i passaggi un certo numero di volte finchè non arrivi alla notte dei tempi (0x000000) oppure non raggiungi un limite massimo di iterazioni (30 sono sufficienti in quasi tutti i casi).

Una volta buttati su log tutti questi indirizzi, gli appiccichi un timestamp e chiudi il log-entry.

Poi, con un tool esterno (ma le prime volte lo facevo a mano, finchè un mio collega non l'ha automatizzato con un programmino fatto in C#), fai una query al DB dei simboli (se usi Visual Studio) oppure cerchi file di "map" generato da GCC. In questi DB ci trovi la corrispondenza tra l'indirizzo del codice (ottenuto dal Return Address) e il nome della funzione che contiene quell'indirizzo, corredato dal nome di classe ed il filename (con numero di riga, extralusso!).
Quindi sostituisci gli indirizzi con i nomi delle funzioni e ottieni uno stack trace come dio comanda :)

In pratica è quello che fa il debugger quando ti mostra lo stack trace, nè più nè meno.

Ah, giusto, dimenticavo la parte "divertente" :D

Per trovare i memory leaks:
- fai girare il programma e piazzati all'inizio del flusso che vuoi analizzare
- fai un dump dello stato interno di tutte le allocazioni (ci penserai dopo a trasformare il dump grezzo in veri e propri stack trace) e poi buttalo, che non ti serve (*)
- apri e chiudi la funzionalità da testare
- fai un secondo dump e salvalo
- apri e chiudi
- fai un terzo dump e salvalo
- apri e chiudi
- fai un quarto dump e salvalo
- Usa un WinMerge o qualcosa di simile per estrarre le differenze tra i dump successivi
- Converti questi dump in stack trace
- Fixa i memory leaks
- Ripeti finchè le differenze tra i dump scompaiono, in modo da ottenere sempre lo stesso dump tra esecuzioni successive

Amen :)


(*) il primo dump conterrà anche tutti i singleton e le inizializzazioni statiche che sono dei "finti leaks" e non vanno eliminati.
« Ultima modifica: 06 Luglio 2013, 21:50:15 da TheKaneB »

Tags: