Forum: Mikrocontroller und Digitale Elektronik Seltsames uC-Verhalten: Bugsuche


von Roland N. (eroli)


Lesenswert?

Hallo zusammen,

vor einigen Tagen ist mir ein Fehlverhalten (Kurioses Debugging eines 
IF-THEN-ELSE-Konstruktes: Codeteile sowohl im THEN, als auch im ELSE 
Zweig werden ausgeführt) in meinem Programm aufgefallen. Mein Programm 
(kompiliert mittels Code Composer Studio 6 mit MSPGCC für einen 
MSP430F5438 auf einem olimex-Board mit dem olimex-JTAG-TINY 
v1-Programmieradapter) war zu diesem Zeitpunkt schon etwas größer (ca. 
26kB) und der Fehler trat in einem Teil auf, der in meinen Augen 
eigentlich schon stabil lief und deswegen längere Zeit nicht getestet 
wurde. Da ich zu dieser Zeit auch noch sehr viel an anderen Baustellen 
gearbeitet habe, fiel es mir sehr schwer die Ursache genauer 
einzugrenzen.

Ich habe daher angefangen, dass Programm Stück für Stück von null an neu 
zusammenzusetzen, damit ich den Fehler besser lokalisieren kann.

Ich denke, das Problem liegt an einer ftoa-Funktion, welche ich hier aus 
dem Forum habe. Entweder die Funktion hat einen Fehler oder ich benutze 
sie falsch - oder es liegt an etwas ganz anderem...

Die Funktion sieht so aus:
1
void ftoa(char * buf, double f, char d, char pad, char padchar)
2
{
3
  char * p;
4
  const double powers[] = {1.0,10.0,100.0,1000.0,10000.0,100000.0};
5
6
  /* round */
7
  f = f>=0.0 ? (f*powers[d]+0.5)/powers[d] : (f*powers[d]-0.5)/powers[d];
8
9
  /* special case */
10
  if (f<0.0 && f > -1.0) {
11
    p = ltoa(buf,-1,pad,padchar);
12
    *(p-1) = '0';
13
  }
14
  else
15
    p = ltoa(buf,f,pad,padchar);
16
17
  *p++ = '.';
18
19
  if (f<0.0)
20
    f = -f;
21
  
22
  f -= (long) f;
23
  f *= powers[d];
24
  ltoa(p,(long)f,d,0);
25
}

ltoa:
1
char * ltoa(char * buf, long val, char pad, char padchar)
2
{
3
  char i;
4
  char neg = 0;
5
  char tmp_buf[21];
6
7
  if(val<0) {
8
    val = -val;
9
    neg = 1;
10
  }
11
12
  for(i=0; val>0; val/=10)
13
    tmp_buf[i++] = (val % 10) + '0';
14
15
  if(i==0)
16
    tmp_buf[i++] = '0';
17
18
  if(neg) {
19
    if(padchar) {
20
      tmp_buf[i++] = '-';
21
      while(i<pad)
22
        tmp_buf[i++] = ' ';
23
    }
24
    else {
25
      while(i<pad-1)
26
        tmp_buf[i++] = '0';
27
      tmp_buf[i++] = '-';
28
    }
29
  }
30
  else
31
    while(i<pad)
32
      tmp_buf[i++] = padchar ? ' ' : '0';
33
34
  while(i>0)
35
    *buf++ = tmp_buf[--i];
36
37
  *buf = 0;
38
39
  return buf;
40
}

Und benutzt habe ich sie so:
1
  char string[14];
2
  ftoa(string, value, 3, 4, 0);
3
  LCD_Write(string, 14);

Das char-Array ist 14 Zeichen lang, da mein LCD-Display pro Zeile 14 
Zeichen anzeigen kann. Die Variable value kann Werte zwischen 9.0, 
9.5,...13 annehmen und wird formatiert beispielsweise so ausgegeben: 
"0009.500"
Wir sind also bei deutlich unter 14 Zeichen - beziehungsweise deutlich 
unter 13 Zeichen, wenn man das Abschlusszeichen '\0' berücksichtigt.

Kann es dennoch sein, das Problem an der Benutzung dieser Funktion 
liegt? Seht ihr an dieser Stelle etwas auffälliges?

Ich freue mich über jeden Denkanstoß :-)

Viele Grüße,
Roland

von Null-Pointer (Gast)


Lesenswert?

Wo schreibst du denn hin?
1
p = ltoa(buf,-1,pad,padchar);

p sollte wenigstens irgendwo sinnvoll hinzeigen :/

von Roland N. (eroli)


Lesenswert?

Null-Pointer schrieb:
> Wo schreibst du denn hin?
>
1
p = ltoa(buf,-1,pad,padchar);
>
> p sollte wenigstens irgendwo sinnvoll hinzeigen :/

Hallo,
wie gesagt sind die Funktionen ftoa (welche wiederrum ltoa aufruft) 
Teile einer fertigen "Bibliothek" hier aus dem Forum, daher habe ich 
diese für funktionierend angenommen und mir nicht im Detail angeschaut.

Es funktioniert ja auch - meine Zahl kommt richtig formatiert als String 
bei mir an. Allerdings kann ich im Moment nicht wirklich ausschließen, 
dass Teile dieser Funktionen (oder wie ich diese Funktionen benutze) 
meinen Stack zerstören...

von Udo S. (urschmitt)


Lesenswert?

Roland M. schrieb:
> Das char-Array ist 14 Zeichen lang, da mein LCD-Display pro Zeile 14
> Zeichen anzeigen kann.

Wenn du String Funktionen nutzen willst muss dein char Array aber 14 + 1 
Zeichen lang sein, sonst passt kein abschliessendes \0 Zeichen mehr 
rein.

Wenn ich mir deinen Code anschaue, dann gehst du bei geschachtelten 
if/else und while Konstrukten sehr sparsam mit geschweiften Klammern um. 
Das führt bei Erweiterungen oder mal schnell einer Debug Ausgabe oder 
einem weiteren else ganz schnell zu völlig anderem(falschem) Verhalten.

Sobald man schachtelt und else benutzt ist es meiner Meinung nach sehr 
sinnvoll konsequent immer geschweifte Klammern zu setzen. Nur die 
Einrückung macht den Code weder korrekt noch kümmert es im Zweifel den 
Compiler.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Die fehlende Bereichsüberprüfung für den Parameter 'd' von ftoa kann Dir 
auch auch die Füße fallen, dann nämlich, wenn der Wert kleiner 0 oder 
größer 6 ist ...

Da Du "char" statt uint8_t verwendest, ist nicht völlig ausgeschlossen, 
daß der Wert negativ werden kann.

: Bearbeitet durch User
von Roland N. (eroli)


Lesenswert?

Udo Schmitt schrieb:
> Roland M. schrieb:
>> Das char-Array ist 14 Zeichen lang, da mein LCD-Display pro Zeile 14
>> Zeichen anzeigen kann.
>
> Wenn du String Funktionen nutzen willst muss dein char Array aber 14 + 1
> Zeichen lang sein, sonst passt kein abschliessendes \0 Zeichen mehr
> rein.
>
> Wenn ich mir deinen Code anschaue, dann gehst du bei geschachtelten
> if/else und while Konstrukten sehr sparsam mit geschweiften Klammern um.
> Das führt bei Erweiterungen oder mal schnell einer Debug Ausgabe oder
> einem weiteren else ganz schnell zu völlig anderem(falschem) Verhalten.
>
> Sobald man schachtelt und else benutzt ist es meiner Meinung nach sehr
> sinnvoll konsequent immer geschweifte Klammern zu setzen. Nur die
> Einrückung macht den Code weder korrekt noch kümmert es im Zweifel den
> Compiler.

Wie bereits gesagt ist das eine Bibliothek hier aus dem Forum. In meinem 
Code arbeite ich grundsätzlich immer mit geschweiften Klammern, da ich 
dies auch sehr viel leserlicher finde...

Packen wir das Problem mal anders an: Kann mir jemand eine entsprechende 
Bibliothek empfehlen, welche auf jeden Fall funktioniert? Sie muss auch 
gar nicht soviele Möglichkeiten zur Formatierung enthalten, sondern soll 
lediglich die (Komma-)Zahl zu einem Text formatieren können...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Der 'F5438 hat 256 kB Flash-ROM, Du kannst also einfach das mit dem 
Compiler mitgelieferte sprintf verwenden.

Was bringt es Dir, bei 26 kB Codegröße Speicher zu sparen? Von TI 
bekommst Du kein Geld zurück, wenn Du den Rest des Flash-ROMs nicht 
verwendest.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Roland M. schrieb:
> Packen wir das Problem mal anders an: Kann mir jemand eine entsprechende
> Bibliothek empfehlen, welche auf jeden Fall funktioniert?

Wie soll das denn gehen?

Wenn eine Funktion (wie ftoa()) einen Buffer als Argument bekommt aber 
sonst keine weitere Information vorhanden ist, wie groß denn der Buffer 
in der aufrufenden Funktion überhaupt ist, bekomme ich das Ding immer 
zum Crash.[1] Ich brauche nur einen ungültigen Pointer oder einen zu 
kleinen Buffer runterzugeben.

Prüfe doch erst mal, ob Dein Buffer nicht zu klein ist.

[1] Es gäbe noch die Möglichkeit, die Buffergröße mit runterzugeben. Die 
Standardfunktionen ftoa(), ltoa() sehen das aber in ihrem Interface 
nicht vor. Machs doch einfach selber. Die Quellen hast Du ja.

: Bearbeitet durch Moderator
von Udo S. (urschmitt)


Lesenswert?

Roland M. schrieb:
> Sie muss auch
> gar nicht soviele Möglichkeiten zur Formatierung enthalten, sondern soll
> lediglich die (Komma-)Zahl zu einem Text formatieren können...

Du weisst doch im Moment gar nicht wo dein Fehler steckt.
Wenn du so einen Fehler finden willst dann speck dein Programm ab bis 
der fehler nicht mehr auftritt, oder anders baue ein Minimalprogramm bis 
du den Fehler nachvollziehen kannst.
Ob die Bibliothek funktioniert oder nicht kannst du auch herausfinden 
indem due in minimalprogramm mit den Werten die zu dem Fehler geführt 
haben debuggst und schaust was in den registern passiert.
Einfach eine andere Bibliothek nehmen wird dein Fehler sehr 
wahrscheinlich nicht lösen weil er in dem Code steckt, den du hier nicht 
zeigst.

von Roland N. (eroli)


Lesenswert?

Hallo zusammen!

Erstmal danke für die zahlreichen und vorallem zügigen Antworten.

Zum Thema Minimalprogramm zur Fehlereingrenzung:
Wie bereits im Eingangspost geschrieben, habe ich mein Programm bereits 
von null auf wieder komplett von vorne aufgebaut und dabei immer 
nachgeschaut, wann der Fehler auftritt. Daher bin ich ja auch zu dem 
fast sicheren Schluss gekommen, dass das Problem an den Funktionen 
ftoa/ltoa, beziehungsweise wie ich diese Funktionen benutze, liegen 
muss.

Ich denke ich werde den Vorschlag von Rufus T. Firefly testen und das 
normale sprintf testweise benutzen. Sollte ich dann keinen Fehler 
reproduzieren können, so habe ich eine Bestätigung für meine derzeitige 
Vermutung.

Aaaaallerdings ist jetzt erstmal Feierabend und Zeit für ein Bier :-D
Ich werde morgen früh damit anfangen und euch dann berichten.

Nochmals: Dankeschön für eure Kommentare und Denkanstöße --> morgen 
werden wir mehr wissen ;-)

von Jürgen S. (jurs)


Lesenswert?

Roland M. schrieb:
> Packen wir das Problem mal anders an: Kann mir jemand eine entsprechende
> Bibliothek empfehlen, welche auf jeden Fall funktioniert? Sie muss auch
> gar nicht soviele Möglichkeiten zur Formatierung enthalten, sondern soll
> lediglich die (Komma-)Zahl zu einem Text formatieren können...

Im Arduino-Forum habe ich mal jemandem eine ähnliche 
Formatierungsfunktion gemacht. Allerdings wollte der in seinem Programm 
komplett auf Gleitkommazahlen verzichten und Integerwerte so auf einem 
Display ausgeben als wären es Werte mit ein oder zwei Nachkommastellen. 
Formatiert wurde auch mit führenden Leerzeichen.

Aber das müßte sich leicht so umstellen lassen, dass es mit "long" statt 
"int" funktioniert und mit frei wählbarem "padchar".

Aufrufkonvention wäre dann so ungefähr:
1
void longToFloatFormat(long l, char* dest, int width, int digits, char padchar)

Und aufrufen würdest Du dann beispielsweise in der Form für die 
formatierte Ausgabe mit drei Nachkommastellen auf insgesamt 8 Stellen 
Gesamtbreite:
1
longToFloatFormat(round(value*1000),string, 8, 3, '0');

Wenn Du meinst, dass das passen könnte, kann ich es Dir dementsprechend 
anpassen und hier posten.

von Karl H. (kbuchegg)


Lesenswert?

Roland M. schrieb:

> Wie bereits im Eingangspost geschrieben, habe ich mein Programm bereits
> von null auf wieder komplett von vorne aufgebaut und dabei immer
> nachgeschaut, wann der Fehler auftritt. Daher bin ich ja auch zu dem
> fast sicheren Schluss gekommen, dass das Problem an den Funktionen
> ftoa/ltoa, beziehungsweise wie ich diese Funktionen benutze, liegen
> muss.

Der nächste Schritt wäre jetzt, alles andere wegzulassen und nur das 
ftoa allein, so wie du es im Programm testest, zu verwenden.

Bis jetzt ist der Schluss, den du da ziehst, nicht zwingend. Denn 
niemand sagt dir, dass das ftoa nur der Symptomüberbringer ist. Der 
Fehler also schon früher eingebaut war und nur nicht sichtbar war.
Genau das ist die Krux beim Fehlersuchen. Nur weil sich kein Fehler an 
der Oberfläche zeigt, bedeutet das nicht, dass keiner vorhanden ist.

von Roland N. (eroli)


Lesenswert?

Hallo zusammen,

schweren Herzens muss ich euch leider Recht geben. Die Funktion ftoa war 
nicht das eigentliche Problem. Das Springen im Quelltext ist leider 
immer noch anzutreffen.

Ich gebe euch mal einen tieferen Einblick in mein Programm:

main.c
1
int main(void)
2
{
3
  Init();
4
  while(1)
5
  {
6
    CheckJoystick(P1IN);
7
    if (m_ChangeVoltage != -1)
8
    {
9
      DoSimulation();
10
    }
11
12
    Menu_Update();
13
  }
14
}

Das Problem tritt in der Funktion Menu_Update() auf, welche sich um die 
Menüführung auf dem LCD kümmert.

Sie sieht ungefähr so aus:
LCD.c
1
void Menu_Update(void)
2
{
3
  if (m_CurrentAction == 0)
4
  {
5
      // Hier wird die Menüstruktur auf dem LCD-Display ausgegeben
6
      DisplayMenu();
7
8
      if (currentNode.Parent == -1)
9
      {
10
        LCD_Set_Position(0, 5);
11
        // Hier wird der aktuelle Status der SD-Karte ausgegeben
12
        // Wird nur auf oberster Menü-Ebene angezeigt
13
      }
14
      unsigned char asd = currentNode.Index - 1;
15
      LCD_Update(inverse, (1 << asd)); // Aktuellen Menüeintrag hervorheben
16
  }
17
  else if (m_CurrentAction == ACTION_SHOWTEMP)
18
  {
19
    DisplayTempAndHumi();
20
  }
21
  else if (m_CurrentAction >= 10 && m_CurrentAction < 30)
22
  {
23
    LCD_Clear_Memory();
24
    LCD_Set_Position(0, 0);
25
    LCD_Write(m_Menu[m_CurrentNodeIndex].Text, 14);
26
    LCD_Set_Position(0, 2);
27
28
    if (m_CurrentAction >= 10 && m_CurrentAction < 20)
29
    {
30
      if (m_CurrentAction == ACTION_SWITCHVOLTAGE)
31
      {
32
        // Switch-Action wird durchgeführt
33
34
        if (m_VoltageOn)
35
          LCD_Write("  An", 14);
36
        else
37
          LCD_Write("  Aus", 14);
38
39
      }
40
    }
41
    else if (m_CurrentAction >= 20 && m_CurrentAction < 30)
42
    {
43
      // Set-Action wird durchgeführt
44
      switch(m_CurrentAction)
45
      {
46
        case ACTION_SETONVOLTAGE:
47
          DisplaySelectValue(m_OnVoltage, "mV", MIN_ON_VOLTAGE, MAX_ON_VOLTAGE);
48
          break;
49
50
        case ACTION_SETOFFVOLTAGE:
51
          DisplaySelectValue(m_OffVoltage, "mV", MIN_OFF_VOLTAGE, MAX_OFF_VOLTAGE);
52
          break;
53
54
        case ACTION_SETONTIME:
55
          DisplaySelectValue(m_OnTime, "ms", MIN_ON_TIME, MAX_ON_TIME);
56
          break;
57
58
        case ACTION_SETOFFTIME:
59
          DisplaySelectValue(m_OffTime, "ms", MIN_OFF_TIME, MAX_OFF_TIME);
60
          break;
61
62
        case ACTION_SETCHANGETIME:
63
          DisplaySelectValue(m_ChangeTime, "ms", MIN_CHANGETIME, MAX_CHANGETIME);
64
          break;
65
      }
66
    }
67
    LCD_Update(inverse, 4);
68
  }
69
  else if (m_CurrentAction >= 30 && m_CurrentAction < 40)
70
  {
71
    LCD_Clear_Memory();
72
    LCD_Set_Position(0, 0);
73
    LCD_Write(m_Menu[m_CurrentNodeIndex].Text, 14);
74
XXX    LCD_Update(inverse, 0);
75
  }
76
}

Kurz zur Orientierung: Die Funktion ftoa, über die wir davor gesprochen 
haben, wird in der Funktion DisplaySelectValue aufgerufen.

Beim Debugging habe ich nun festgestellt, dass die mit XXX markierte 
Zeile quasi immer aufgerufen wird - unabhängig von m_CurrentAction.

Konkret habe ich es für die Fälle m_CurrentAction == 0 und 20 <= 
m_CurrentAction < 30 getestet und in beiden Fällen wird bei der Schritt 
für Schritt Ausführung vorm Rücksprung aus der Funktion die markierte 
Anweisung ausgeführt...

Ich bin jetzt relativ unsicher, wie ich vorgehen soll um den Fehler 
weiter eingrenzen zu können... Habt ihr noch weitere Vorschläge oder 
braucht Ihr vielleicht noch mehr Informationen?

Sollte man an dieser Stelle vielleicht vorne, also bei DisplayMenu(), 
anfangen?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Hast Du Dich mal damit beschäftigt, was auf dem Stack geschieht?

Und hast Du den Kram mal im Debugger laufen lassen?

von Karl H. (kbuchegg)


Lesenswert?

Fehler dieser Art können durch mehrere Dinge ausgelöst werden.
Der wohl häufigste Fall ist der 'Array out of Bounds'.
Also irgendwas in der Art
1
  int a[4];
2
3
  a[4] = 8;
bei dem auf ein nicht vorhandenes Array Element geschieben wird und man 
sich dadurch im Speicher irgendwelche anderen Dinge überschreibt. Das 
ist besonders heimtückisch, wenn das Array lokal in einer Funktion ist 
und man sich durch den out of bounds Zugriff die Rücksprungadresse der 
Funktion zerschiesst.

Auch die Situation 'Ende des SRAM ist erreicht' sollte nicht ausser acht 
gelassen werden. Wird das SRAM knapp, dann wandert der Stack in den 
Variablenbereich hinein und durch beschreiben der (regulären) Variable 
zerschiesst man sich wieder den Returnstack. Genau aus dem Grund sollte 
man in der µC Programmierung auf den kleinen µC eine Grundregel der 
Programmierung auch mal ausser acht lassen und relativ viel als globale 
Variablen anlegen. Gerade mal Dinge wie Schleifenzähler oder kleinere 
Speicherverbraucher können in den Funktionen bleiben, aber den Rest will 
man global haben, damit Compiler/Linker den Speicherverbrauch 
feststellen können und man einen Hinweis hat, wenn der Speicher knapp 
werden könnte. Ganz exakt kann man das sowieso nie feststellen, weil es 
dann von der genauen Aufrufreihenfolge der Funktionen abhängt, wieviel 
Speicher dann noch zur Leufzeit benötigt wird. Aber zumindest einen 
Eindruck kann man schon kriegen, ob man eher knapp unterwegs ist oder 
nicht.

von Marvin M (Gast)


Lesenswert?

Hallo,
änder mal spaßeshalber
else if (m_CurrentAction >= 30 && m_CurrentAction < 40)
in
else if ((m_CurrentAction >= 30) && (m_CurrentAction < 40))

von Roland N. (eroli)


Lesenswert?

Hallo zusammen,

ich habe einige Neuigkeiten für euch...

In den Optimierungseigenschaften gibt es mehrere Level zur Auswahl:

1) Leeres Feld
2) None (-O0)
3) Optimize (-01)
4) Optimize more (-O2)
5) Optimize most (-O3)

Außerdem gibt es dann noch einige Checkboxes, eine von denen heißt:
Optimize for space rather than speed (-Os)

Es ist nun so, dass sich diese Checkbox auf den Buildvorgang auswirkt - 
und zwar unabhängig ob ich Option 1 (Leeres Feld) oder Option 2 (None) 
gewählt habe.

Sobald diese Checkbox gesetzt ist, fängt das seltsame Verhalten wieder 
von Vorne an (es werden zwei Switch-Cases sowie zwei Else-Zweige 
angesprungen).

Da das Ergebnis für mich jedoch stets richtig war und auch alles 
funktioniert und ich damals im Studium gelernt habe, dass Compiler 
ziemlich intelligente Programme sind, wird das schon alles so seine 
Richtigkeit haben... Oder etwa nicht? :-D

PS: Netter Nebeneffekt: Wenn die Checkbox gesetzt ist, läuft mein 
Programm trotz Optimierung der Codegröße gefühlt sehr viel schneller ab 
(sodass es mich dazu verleitet meine Debouncing-Grenzwerte zu 
erhöhen...).

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Marvin M schrieb:
> änder mal spaßeshalber

Man kann sich auch die "operator precedence" ansehen.

http://en.cppreference.com/w/c/language/operator_precedence

Der Vergleich hat gegenüber dem logischen AND Vorrang, also ist

  if (a < b && a > c)

identisch zu

  if ((a < b) && (a > c))

von Karl H. (kbuchegg)


Lesenswert?

Roland M. schrieb:

> Sobald diese Checkbox gesetzt ist, fängt das seltsame Verhalten wieder
> von Vorne an (es werden zwei Switch-Cases sowie zwei Else-Zweige
> angesprungen).

Ganz ehrlich.
Du solltest deinen Programmfehler suchen und nicht mit 
Optimierungseinstellungen rumspielen.

Es ist schon richtig, dass sich Optimierungseinstellungen darauf 
auswirken, was der Compiler produziert. Das ändert aber nichts daran, 
dass du einen Fehler im Code hast. Das eine mal wirkt der sich eben 
nicht aus und/oder der Compiler nutzt ein mögiche Optimierung eben 
nicht, wodurch sich der Fehler nicht zeigt. Vorhanden ist er aber 
trotzdem.

Ich weiss schon. Es ist verführerisch, den Compiler für etwas 
verantwortlich zu machen. Fakt ist aber, dass in 99 von 100 Fällen, das 
Problem im Programm bzw. beim Programmierer liegt und nicht beim 
Compiler. Ehe ich nicht im produzierten Assemblercode dem Compiler nicht 
100%-ig nachweisen kann, dass er gültigen und eindeutig definierten C 
Code falsch umgesetzt hat, würde ich dem Compiler erst mal immer 
freisprechen und als Arbeitshypothese "Ich habe einen Bug im Code" 
annehmen.

: Bearbeitet durch User
von Roland N. (eroli)


Lesenswert?

Karl Heinz schrieb:
> Roland M. schrieb:
>
>> Sobald diese Checkbox gesetzt ist, fängt das seltsame Verhalten wieder
>> von Vorne an (es werden zwei Switch-Cases sowie zwei Else-Zweige
>> angesprungen).
>
> Ganz ehrlich.
> Du solltest deinen Programmfehler suchen und nicht mit
> Optimierungseinstellungen rumspielen.
>
> Es ist schon richtig, dass sich Optimierungseinstellungen darauf
> auswirken, was der Compiler produziert. Das ändert aber nichts daran,
> dass du einen Fehler im Code hast. Das eine mal wirkt der sich eben
> nicht aus und/oder der Compiler nutzt ein mögiche Optimierung eben
> nicht, wodurch sich der Fehler nicht zeigt. Vorhanden ist er aber
> trotzdem.
>
> Ich weiss schon. Es ist verführerisch, den Compiler für etwas
> verantwortlich zu machen. Fakt ist aber, dass in 99 von 100 Fällen, das
> Problem im Programm bzw. beim Programmierer liegt und nicht beim
> Compiler. Ehe ich nicht im produzierten Assemblercode dem Compiler nicht
> 100%-ig nachweisen kann, dass er gültigen und eindeutig definierten C
> Code falsch umgesetzt hat, würde ich dem Compiler erst mal immer
> freisprechen und als Arbeitshypothese "Ich habe einen Bug im Code"
> annehmen.

Hallo Karl Heinz,

vielleicht habe ich mich ja nicht deutlich ausgedrückt, aber es ist so, 
dass mein Programm bei deaktivierter Optimierung (Option 1: leeres Feld 
in der DropDownListe) und allen DEaktivierten Checkboxes endlich so 
läuft, wie es in meinem Code steht (keine doppelten ELSE-/Case-Zweige).

Ist das nicht der eindeutige Beweis dafür, dass ich gar keinen Fehler in 
meinem Programm versteckt habe?

von Peter (Gast)


Lesenswert?

Hallo,
wenn du vernünftig debuggen willst solltest du, sofern Platz vorhanden, 
die Optimierung ausschalten.

Deine LCD_Update Funktion wird wenn ich mich nicht verlesen habe in 
jedem Zweig aufgerufen, nur mit anderen Parametern.
Schau mal im disassembly nach, ob die übergebenen Werte stimmen.


Wenn die Optimierung zu hoch eingestellt ist ist für diese Funktion 
unabhängig, wo sie aufgerufen wird nur einmal Symbolinfo vorhanden, d.h. 
er zeigt in der übergeordneten Funktion nur das letzte Vorkommen der 
aufgerufenen Funktion an.


Grüße
Peter

von Karl H. (kbuchegg)


Lesenswert?

Roland M. schrieb:

> Ist das nicht der eindeutige Beweis dafür, dass ich gar keinen Fehler in
> meinem Programm versteckt habe?

Nein.
Es ist nur der Beweis dafür, dass sich der Fehler nicht auswirkt.

: Bearbeitet durch User
von marcus6100 (Gast)


Lesenswert?

> Ist das nicht der eindeutige Beweis dafür, dass ich gar keinen Fehler in
> meinem Programm versteckt habe?

Nein, ist es definitiv nicht.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:
> Roland M. schrieb:
>
>> Ist das nicht der eindeutige Beweis dafür, dass ich gar keinen Fehler in
>> meinem Programm versteckt habe?
>
> Nein.
> Es ist nur der Beweis dafür, dass sich der Fehler nicht auswirkt.

Gemeint ist: das er sich nicht mehr in der "zuvor beobachteten Form" 
auswirkt. Es kann sogar sein, dass überhaupt keine Anomalie mehr 
wahrnehmbar ist. Allerdings ist das immer ungut. Denn der Fehler ist ja 
nicht beseitigt worden und wartet nur darauf, an anderer Stelle erneut 
zuzuschlagen. Durchaus auch erst bei der nächsten Programmerweiterung.

von Peter (pwp)


Lesenswert?

Karl Heinz schrieb:
> Karl Heinz schrieb:
>> Roland M. schrieb:
>>
>>> Ist das nicht der eindeutige Beweis dafür, dass ich gar keinen Fehler in
>>> meinem Programm versteckt habe?
>>
>> Nein.
>> Es ist nur der Beweis dafür, dass sich der Fehler nicht auswirkt.
>
> Gemeint ist: das er sich nicht mehr in der "zuvor beobachteten Form"
> auswirkt. Es kann sogar sein, dass überhaupt keine Anomalie mehr
> wahrnehmbar ist. Allerdings ist das immer ungut. Denn der Fehler ist ja
> nicht beseitigt worden und wartet nur darauf, an anderer Stelle erneut
> zuzuschlagen. Durchaus auch erst bei der nächsten Programmerweiterung.

Hallo Karl-Keinz,
wobei, wenn ich so sehe was beschrieben wurde und wie sichs auswirkt 
würde ich eher auf Optimierung tippen.

Da fehlt mir noch die Info die ich oben mal nachgefragt hatte.

grüße
Peter

von Karl H. (kbuchegg)


Lesenswert?

Peter W. schrieb:

> Hallo Karl-Keinz,
> wobei, wenn ich so sehe was beschrieben wurde und wie sichs auswirkt
> würde ich eher auf Optimierung tippen.

Kann sein.
Kann aber auch sein, dass er irgendwo undefiniertes Verhalten im C Code 
hat und der Optimizer das gnadenlos ausnutzt.

Was ich aber niemandem empfehlen würde: die Sache einfach zu ignorieren 
und zu sagen: Ich stell den Optimizer einfach ab und gut ists.

Genaueres weiß man leider erst dann, wenn man das Problem im Detail 
identifiziert, verstanden und beseitigt hat.

: Bearbeitet durch User
von Walter Tarpan (Gast)


Lesenswert?

Peter W. schrieb:
> wobei, wenn ich so sehe was beschrieben wurde und wie sichs auswirkt
> würde ich eher auf Optimierung tippen.

http://www.youtube.com/watch?v=iZiO9bqzHzg

von Roland N. (eroli)


Lesenswert?

> Hallo Karl-Keinz,
> wobei, wenn ich so sehe was beschrieben wurde und wie sichs auswirkt
> würde ich eher auf Optimierung tippen.
>
> Da fehlt mir noch die Info die ich oben mal nachgefragt hatte.
>
> grüße
> Peter

Entschuldige bitte, welche Informationen benötigst du noch?

> Gemeint ist: das er sich nicht mehr in der "zuvor beobachteten Form"
> auswirkt. Es kann sogar sein, dass überhaupt keine Anomalie mehr
> wahrnehmbar ist. Allerdings ist das immer ungut. Denn der Fehler ist ja
> nicht beseitigt worden und wartet nur darauf, an anderer Stelle erneut
> zuzuschlagen. Durchaus auch erst bei der nächsten Programmerweiterung.

Ich habe gesagt, dass das Debugging sich komisch verhält (mehrfache 
Sprünge in ELSE-/CASE-Verzweigungen). Das produktive Ergebnis war aber 
eigentlich immer richtig.

Ein Beispiel: Mit eingeschalteter Checkbox war ich beim Debugging immer 
verwirrt, dass sowohl der richtige switch-Case, als auch nachfolgend 
Zeile "DisplaySelectValue(m_OffTime, "ms", MIN_OFF_TIME, MAX_OFF_TIME);" 
ausgeführt worden ist. Zumindest hat der Debugger mir das so suggeriert. 
Auf dem LCD-Display stand jedoch immer das richtige Resultat (zu 
erkennen am Wertebereich sowie der Endung ("ms" != "mV").

von Karl H. (kbuchegg)


Lesenswert?

Roland M. schrieb:

> Ich habe gesagt, dass das Debugging sich komisch verhält (mehrfache
> Sprünge in ELSE-/CASE-Verzweigungen). Das produktive Ergebnis war aber
> eigentlich immer richtig.

Ah. Moment
Das ist jetzt aber eine neue Information.

Denn das ist durchaus normal, dass sich bei eingeschaltetem Optimizer im 
Debugger andere Reihenfolgen ergeben.
Ein Optimizer kann durchaus auch Codereihenfolgen umdrehen. Kurz und 
gut: Der Code, der nach dem Optimizer raus kommt und der dann 
tatsächlich auf dem µC läuft, hat nur noch eine sehr lockere Bindung zu 
dem, was du geschrieben hast. EIn Optimizer darf alles umdrehen, solange 
sich das extern beobachtbare Verhalten des Programmes nicht verändert. 
Oft ist es überhaupt nicht mehr möglich, einzelne Assemblerabschnitte 
einer bestimmten C-Code Zeile zuzuordnen. Was soll der Debugger dann 
machen?

: Bearbeitet durch User
von Udo S. (urschmitt)


Lesenswert?

Roland M. schrieb:
> Ich habe gesagt, dass das Debugging sich komisch verhält (mehrfache
> Sprünge in ELSE-/CASE-Verzweigungen).

Je nach Build/Debugging System darfst du zum Debuggen nicht optimieren, 
da sonst nur Blödsinn herauskommt, weil der Debugger den Sourcecode dem 
optimierten Binärfile nicht mehr korrekt zuordnen kann.

von Paul B. (paul_baumann)


Lesenswert?

Es ist wahrscheinlich so, daß man jede Optimiereung abschalten muß,
damit genau das ,was im Quelltext steht, übersetzt wird.

Ich stelle mir die Optimierungsfunktion von diesem Kompiler so vor:
Auf ein Tonband lese ich den Roman "Robinson Crusoe". Nach der Opti-
mierung ist dann "Die Schatzinsel" auf dem Band.

;-)

MfG Paul

von Roland N. (eroli)


Lesenswert?

Karl Heinz schrieb:
> Ah. Moment
> Das ist jetzt aber eine neue Information.

> Denn das ist durchaus normal, dass sich bei eingeschaltetem Optimizer im
> Debugger andere Reihenfolgen ergeben.
> Ein Optimizer kann durchaus auch Codereihenfolgen umdrehen. Kurz und
> gut: Der Code, der nach dem Optimizer raus kommt und der dann
> tatsächlich auf dem µC läuft, hat nur noch eine sehr lockere Bindung zu
> dem, was du geschrieben hast. EIn Optimizer darf alles umdrehen, solange
> sich das extern beobachtbare Verhalten des Programmes nicht verändert.
> Oft ist es überhaupt nicht mehr möglich, einzelne Assemblerabschnitte
> einer bestimmten C-Code Zeile zuzuordnen. Was soll der Debugger dann
> machen?

Eben drum habe ich ja auch geschrieben (entschuldigt, wenn ich mich 
selbst zitiere):
> Da das Ergebnis für mich jedoch stets richtig war und auch alles
> funktioniert und ich damals im Studium gelernt habe, dass Compiler
> ziemlich intelligente Programme sind, wird das schon alles so seine
> Richtigkeit haben... Oder etwa nicht? :-D

Aber ich hatte ja bereits vermutet, dass ich mich damit vermutlich nicht 
ganz deutlich ausgedrückt habe.

Udo Schmitt schrieb:
> Je nach Build/Debugging System darfst du zum Debuggen nicht optimieren,
> da sonst nur Blödsinn herauskommt, weil der Debugger den Sourcecode dem
> optimierten Binärfile nicht mehr korrekt zuordnen kann.

Es war ja auch nicht meine Absicht, dass der Optimierer überhaupt 
angeschaltet ist, aber wie meine Tests gezeigt haben, hat sich die 
Checkbox (-Os), trotz leerem Feld in der DropDownListe zur Auswahl der 
Optimierungsstufe, auf meinen Quelltext ausgewirkt...

> Es ist wahrscheinlich so, daß man jede Optimiereung abschalten muß,
> damit genau das ,was im Quelltext steht, übersetzt wird.

> Ich stelle mir die Optimierungsfunktion von diesem Kompiler so vor:
> Auf ein Tonband lese ich den Roman "Robinson Crusoe". Nach der Opti-
> mierung ist dann "Die Schatzinsel" auf dem Band.

Eine nette Analogie, aber passend zur meinem Problem ;-)
Ich hatte halt gedacht, dass die Checkbox bei deaktivierter Optimierung 
nichts weiter bewirken würde...

: Bearbeitet durch User
von Peter (pwp)


Lesenswert?

Hi Roland,
ich hatte dir vorgeschlagen zu schauen, ob die Daten richtig in die 
aufgerufene Funktion übergeben werden, das ist dann ein Indiz dafür, wo 
der Aufruf erfolgt ist.

Du kannst auch den Call-stack verwenden, ich weiss nur nicht, wie gut er 
im Code Composer implementiert ist.
Im IAR klappt das trotz Optimierung (ich habe mir mal dein Codeschnipsel 
in den IAR eingebaut) ganz gut.

Wie Udo schon sagte, zum debuggen ist Optimierung suboptimal, wenn das 
wirklich nötig ist, ist das disassembly dein Freund....

Grüßle
Peter

Roland M. schrieb:
>> Hallo Karl-Keinz,
>> wobei, wenn ich so sehe was beschrieben wurde und wie sichs auswirkt
>> würde ich eher auf Optimierung tippen.
>>
>> Da fehlt mir noch die Info die ich oben mal nachgefragt hatte.
>>
>> grüße
>> Peter
>
> Entschuldige bitte, welche Informationen benötigst du noch?
>
>> Gemeint ist: das er sich nicht mehr in der "zuvor beobachteten Form"
>> auswirkt. Es kann sogar sein, dass überhaupt keine Anomalie mehr
>> wahrnehmbar ist. Allerdings ist das immer ungut. Denn der Fehler ist ja
>> nicht beseitigt worden und wartet nur darauf, an anderer Stelle erneut
>> zuzuschlagen. Durchaus auch erst bei der nächsten Programmerweiterung.
>
> Ich habe gesagt, dass das Debugging sich komisch verhält (mehrfache
> Sprünge in ELSE-/CASE-Verzweigungen). Das produktive Ergebnis war aber
> eigentlich immer richtig.
>
> Ein Beispiel: Mit eingeschalteter Checkbox war ich beim Debugging immer
> verwirrt, dass sowohl der richtige switch-Case, als auch nachfolgend
> Zeile "DisplaySelectValue(m_OffTime, "ms", MIN_OFF_TIME, MAX_OFF_TIME);"
> ausgeführt worden ist. Zumindest hat der Debugger mir das so suggeriert.
> Auf dem LCD-Display stand jedoch immer das richtige Resultat (zu
> erkennen am Wertebereich sowie der Endung ("ms" != "mV").

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.