Forum: Mikrocontroller und Digitale Elektronik Sinnvolles Debug Statement für Atmel / Arduino C++


von Christian J. (elektroniker1968)


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

von W.S. (Gast)


Angehängte Dateien:

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.

von Dr. Sommer (Gast)


Lesenswert?

1
#ifdef DEBUG
2
#define debug(...) Serial.print(__VA_ARGS__)
3
#else
4
#define debug(...)
5
#endif
6
7
...
8
9
debug("bla %d\n", x);

von holger (Gast)


Lesenswert?

>#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

von Martin (Gast)


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!

von Andreas B. (andreasb)


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

von W.S. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> #ifdef DEBUG
> #define...

Du willst so einen Kruscht doch wohl nicht in einer fertigen, 
ausprobierten Quelle drin lassen - oder??!


W.S.

von Dr. Sommer (Gast)


Lesenswert?

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.

von Dr. Sommer (Gast)


Lesenswert?

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.).

von Christian (Gast)


Lesenswert?

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

von Chris (Gast)


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.

von Peter II (Gast)


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.

von Karlo (Gast)


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?

von Dr. Sommer (Gast)


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.

von Chris (Gast)


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.

von Chris (Gast)


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))

von Seano L. (Gast)


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.

von Peter II (Gast)


Lesenswert?

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.

von STM32F4 Programmierer (Gast)


Lesenswert?

Peter II schrieb:
> aber nur wenn die funktion in der gleichen C datei steht.
... oder wenn man mit optimierungen linkt: -flto

von Möp (Gast)


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?

von dkar (Gast)


Lesenswert?

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.

von STM32F4 Programmierer (Gast)


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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.