Sinnvolles Debug Statement für Atmel / Arduino C++

OP #3252420
Hallo,

ich suche schon das halbe Netz aber aber finde nicht Gescheites.

Ich habe keine Lust meinen Code durch

#ifdef DEBUG
  Serial.Print.....
#endif

nach fast jeder Zeile oder Codebaschnitt zu versauen. Das ist nachher 
unleserlich in der Release Version :-(

Schöner wäre sowas

<Befehle.....>
DBG("Variable =",var) oder wenigstens DBG(Variable);

was dann mit einem einzigen Präprozessor Schwitch entweder mit ein 
kompiliert wird oder einfach übergangen.

Kann mir da mal einer helfen?

Gruss,
Christian
Gast
Christian J. schrieb:
> Kann mir da mal einer helfen?

Ähem... du kennst sicherlich den Witz:
"woran erkennt man eine helfende Kritik?"
Antwort: "Sie beginnt mit den Worten IHNEN WERD ICH HELFEN!..."

Ich mach es so, daß ich mir einen Debug-Ringpuffer einrichte, ihn an 
passenden Stellen mit Debugausgaben fülle und bei passender Gelegenheit 
in der Grundschleife in main seriell ausgebe. Hat den Vorteil, daß die 
Debugausgaben auch zeitlich nicht wirklich auftragen, also nicht so sehr 
intrusiv sind wie ein Debugger.

Sowas wie printf halte ich auf nem µC für völlig daneben. Wenn der Code 
dann läuft, fliegen alle Debugeinträge wieder raus. Ist eben ein bissel 
Editierarbeit und das Schlaraffenland gibt's weder für dich noch für 
mich.

Ich häng dir mal ne Datei dran, die ich zu sowas verwende.

W.S.
Angehängte Dateien:
Gast
>#ifdef DEBUG
>#define debug(...) Serial.print(_VA_ARGS_)
>#else
>#define debug(...)
>#endif

Vieleicht besser so:

#ifdef DEBUG
#define debug   Serial.print
#else
#define debug(...)
#endif
Gast
Wieso bettest du das nicht einfach in eine Funktion?
1
void debug(char *text, int value)
2
{
3
#ifdef DEBUG
4
   Serial.print(text);
5
   Serial.print(value);
6
#endif
7
}

Dann hast du auch nicht mehr so viel zu schreiben, und die Funktion 
macht garnichts bevor du nicht DEBUG definiert hast!
Martin schrieb:
> Wieso bettest du das nicht einfach in eine Funktion?
>
1
> void debug(char *text, int value)
2
> {
3
> #ifdef DEBUG
4
>    Serial.print(text);
5
>    Serial.print(value);
6
> #endif
7
> }
8
>
>
> Dann hast du auch nicht mehr so viel zu schreiben, und die Funktion
> macht garnichts bevor du nicht DEBUG definiert hast!

Aber die Funktion belegt den Speicher immer.

---

Christian J. schrieb:
> <Befehle.....>
> DBG("Variable =",var) oder wenigstens DBG(Variable);

Sowas geht (nicht getestet, aber +/-)
1
#define DBG_STRING(var) Serial.print(#var "=%s", var);
2
#define DBG_INT(var) Serial.print(#var "=%i", var);

ggf. sicherer, aber warscheinlich minimal langsamer:
1
#define DBG_STRING(var) Serial.print("%s=%s", #var, var);
2
#define DBG_INT(var) Serial.print("%s=%i", #var, var);
etc.

Für dich relevant:
1
#xxx
wird vom Precompiler ersetzt durch
1
"xxx"

UND
1
"a" "b"
entspricht in C, ebenfalls Precompiler.
1
"ab"

Mit dieser Info wird solltest du in der Lage sein selbst deine Makros zu 
bauen.

Zudem ggf. noch relevant sind die Makros:
1
__FILE__

und
1
__LINE__

welche jeweils durch die aktuelle Datei / Zeile ersetzt werden.

Das sind so Dinge die immer schwer zu ergoogeln sind, am besten schaut 
man sich für sowas Debug Libs an, welche sowas implementieren. Auswendig 
kann ich dir aber gerade nichts nennen...



mfg Andreas
Gast
Martin schrieb:
> Wieso bettest du das nicht einfach in eine Funktion?
Das ist sogar noch besser, aber dann gleich universal:
1
template <typename... T> void debug (T... args) {
2
#ifdef DEBUG
3
  Serial.print(args...);
4
#endif
5
}

Andreas B. schrieb:
> Aber die Funktion belegt den Speicher immer.
Der Compiler wird sie wegoptimieren wenn sie leer ist.
Gast
W.S. schrieb:
> Du willst so einen Kruscht doch wohl nicht in einer fertigen,
> ausprobierten Quelle drin lassen - oder??!
>
> W.S.
Dieser Kruscht funktioniert gut und wird oft verwendet. Eine Funktion 
ist aber besser (s.o.).
Gast
Hallo,

die Sache mit der Funktion scheint mir am sinnvollsten zu sein. Ob da 
ein Rumpf übrig bleibt, die dauernd angesprungen wird isz mir egal, das 
sind nur ein paar Ticks.

Gruss,
Christian
Gast
anstelle von
#ifdef DEBUG
#define DBG(...) Serial.print(_VA_ARGS_)
#else
#define DBG(...)
#endif

solltest du:
#ifdef DEBUG
#define DBG(lvl, fmt, args ...) 
(DEBUG<0?(lvl)==-DEBUG:DEBUG<=(lvl)&&Serial.print("\nDBG(%s.%s:%d): " 
fmt " ",##args))
#else
#define DBG(lvl,...) 1
#endif


Dies, weil du dann in
if (foo==bar) foobar();
ein
if (foo==bar&&DBG("foobar()")) foobar();
machen kannst.
Gast
Chris schrieb:
> Dies, weil du dann in
> if (foo==bar) foobar();
> ein
> if (foo==bar&&DBG("foobar()")) foobar();
> machen kannst.

sorry das ist krank. Man sollte eh immer mit {} arbeiten. Dann kann man 
auch lesbaren code schreiben.

if ( foo==bar ) {
  DBG("foobar()");
  foobar();
}

hat auch den vorteil man kann ein breakpoint innerhalb vom IF setzen.
Gast
Interessant, genau gestern hatte ich eine ähnliche Idee, allerdings war 
mein Stand immer dass es keine Makros mit variabler Parameterzahl gibt.
VA_ARGS vom C99 Standard ist irgendwie an mir vorbei gegangen :-)

Wieso ist denn die Funktion dem (kürzeren) Makro zu bevorzugen?
Weils sicherer ist?
Gast
Karlo schrieb:
> Wieso ist denn die Funktion dem (kürzeren) Makro zu bevorzugen?
> Weils sicherer ist?
Weil Funktionen typisiert sind, einen eigenen Scope öffnen, in einem 
Scope sind (und nicht super-global wie Makros) keine Probleme mit 
Variablennamen-Kollisionen haben, man im Zweifelsfall einen Pointer 
darauf bekommen kann, etc. Dank Compiler-Optimierungen sind sie auch 
nicht weniger effizient. Generell sollte man #defines wo nur möglich 
vermeiden.
Gast
> sorry das ist krank. Man sollte eh immer mit {} arbeiten. Dann kann man
> auch lesbaren code schreiben.

Ist klar, nur in der Realen Welt hat man speziell Code von anderen zu
Instrumentalisieren/Debuggen und da ist man dann für sowas froh.

Sicher, man könnte den Code durch ein prettify schicken, nur ... .

Ein anderes Beispiel ist:
 if(sendto(sockfd,&dhcpmsg,sizeof(dhcpmsg),0,(struct 
sockaddr*)&servaddr,sizeof(servaddr)) < 0)
    die("sendto");

Da kann man einfach sowas schreiben:
#define die(id) DBG("abort because no %s",id) && die(id)

Wie auch
#define sendto(fd,msg,size,src,dst,dsize) DBG("sendto ... )&& 
sendto(fd,msg,size,src,dst,dsize)

um überhaupt rauszufinden, was überhaupt gesendet wird.
Gast
Im letzten Beispile sollte man
#define sendto(fd,msg,size,src,dst,dsize) DBG("sendto ... )&&
sendto(fd,msg,size,src,dst,dsize)

so definieren:
#define sendto(fd,msg,size,src,dst,dsize) (DBG("sendto ... ),
sendto(fd,msg,size,src,dst,dsize))
Gast
Christian schrieb:
> Hallo,
>
> die Sache mit der Funktion scheint mir am sinnvollsten zu sein. Ob da
> ein Rumpf übrig bleibt, die dauernd angesprungen wird isz mir egal, das
> sind nur ein paar Ticks.
Der Rumpf ist leer wenn kein DEBUG gesetzt ist, das optimiert der 
compiler normalerweise weg, d.h. es wird die leer Funktion erst gar 
nicht aufgerufen.

Sicherheitshalber mal die Compilereinstellungen checken.
Gast
Sean L. schrieb:
> Der Rumpf ist leer wenn kein DEBUG gesetzt ist, das optimiert der
> compiler normalerweise weg, d.h. es wird die leer Funktion erst gar
> nicht aufgerufen.

aber nur wenn die funktion in der gleichen C datei steht.
Gast
Das funktioniert?

Modul-Übergreifende Funktionen dürfen doch zwecks Seiteneffekte nicht 
wegoptimiert werden.
Aber woher soll denn der Linker wissen ob die Funktion nun etwas macht 
oder nicht?
Der sieht doch nur, dass das Symbol "print_debug()" verwendet wird. Aber 
er kann doch nicht einfach einen Sprung in diese Funktion, die der 
Compiler erzeugt hat, wieder streichen.

Gegenteilige Meinungen?
Gast
Bei -flto erzeugt der Compiler zusätzlichen Bytecode, der in der 
Object-File hinterlegt wird. Den kann der Linker anschauen. Dann weiß 
er, dass er print_debug() rauswerfen darf.
Gast
Möp schrieb:
> Das funktioniert?
Ja, sogar schockierend gut
> Modul-Übergreifende Funktionen dürfen doch zwecks Seiteneffekte nicht
> wegoptimiert werden.
Doch, wenn der Linker das finale Ergebnis des Linkens sieht kann er 
feststellen ob eine Funktion verwendet wird oder nicht.
> Aber woher soll denn der Linker wissen ob die Funktion nun etwas macht
> oder nicht?
Er guckt in den Code.
> Der sieht doch nur, dass das Symbol "print_debug()" verwendet wird. Aber
> er kann doch nicht einfach einen Sprung in diese Funktion, die der
> Compiler erzeugt hat, wieder streichen.
Doch, der vom Compiler erzeugte "GIMPLE"-Code gibt ihm die nötigen 
Informationen, sodass er leere/unbenutzte Funktionen wegoptimieren kann, 
nur 1x verwendete Funktionen unabhängig von ihrer Größe komplett 
inlinen, globale Konstanten in Funktionen inlinen, etc.
> Gegenteilige Meinungen?
Was zählen denn Meinungen, probier's halt aus

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 13 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.