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

OP #3252420
Lesenswert?

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 #3252444
Lesenswert?

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 #3252513
Lesenswert?

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!
#3252545
Lesenswert?

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 #3253203
Lesenswert?

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 #3253208
Lesenswert?

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 #3254053
Lesenswert?

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 #3254691
Lesenswert?

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 #3254708
Lesenswert?

> 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 #3254709
Lesenswert?

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 #3254851
Lesenswert?

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 #3255427
Lesenswert?

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 #3255846
Lesenswert?

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

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren