Hallo ! Ich muss eine Aufgabe übernehmen den Source-Code von einem Ex-Kollegen zu analysieren und versuchen das Programm wieder in einen stabilen Zustand zu bringen, es ist allerdings etwas komplex um es hier zu posten. Ich möchte es mal kurz umreisen: Entw.-Umgebung ist AVR-Studio5. Das Programm selbst für den ATMega ist zeitunkritisch. Von einer ISR-Routine (nach sleep Modus - Power down) aus werden einige Funktionen aufgerufen sodass eine maximale Verschachtelungstiefe von ca. 5 erreicht wird. Es werden allerdings einige Variablen in den Funktionen definiert. Es werden dann drei Array-Variablen eingelesen und dann wird entschieden ob eine SMS gesendet wird oder nicht. Kurz vor dem senden der SMS habe ich die drei Variablen nochmals mir ausgeben lassen und stellte fest das die zweite Feld-Variable auf einmal einen falschen Wert hat. Zwischen diesen paar Zeilen wird zwar eine Funktion aufgerufen, aber in dieser wird nichts an Variablen verändert. Zumal dürfte dieses Verhalten nicht stattfinden, da im Scope-Bereich der Funktion nicht auf die 3 Feld-Variablen zugegriffen werden können. Das Programm läuft zwar schon einigermaßen stabil nur an der oben erwähnten Stelle wird eine Variablen verändert, was eigentl. nicht sein kann. Was könnte man den machen um hier rauszufinden was hier schief läuft. Man könnte zwar mit dem Simulator mal durchsteppen aber ich weiß jetzt nicht ob das so ohne weiteres geht und zwar weil das eigentl. Progamm nach einem Interrupt vom sleep-Modus aufwacht und aus der ISR-Routine gestartet wird. Gruß U
> [..] wird eine Variablen verändert, was eigentl. nicht sein kann.
Doch, kanns: Heap-Overflow, Pufferüberlauf allgemein, amok gelaufener
Zeiger, fehlendes 'volatile', ..
Letzeres ändert den Wert nur scheinbar (genau genommen bekommt man eine
tatsächliche Änderung nicht mit, hat also einen alten Wert, keinen
'beliebigen').
HTH
U. B. schrieb: > Was könnte man den machen um hier rauszufinden was hier schief läuft. > Man könnte zwar mit dem Simulator mal durchsteppen aber ich weiß jetzt > nicht ob das so ohne weiteres geht und zwar weil das eigentl. Progamm > nach einem Interrupt vom sleep-Modus aufwacht und aus der ISR-Routine > gestartet wird. das sollte egal sein, wenn eine Variable geändert wird ohne das du das willst, dann wird es wohl ein speicher überschreiber sein oder der STack reicht bis in dem Ram rein. Das sollte sich aber im Debugger nochvollziehen lassen an welcher stelle genau sich as array ändert.
Hallo ! Das ging aber schnell ! Ich hatte mir schon so etwas gedacht, dass es an einer dieser Sachen liegt. Ich denke wir machen das mal so - ich werde heute Abend mal den Simulator anwerfen und dann mal durchsteppen. Ich werd mich dann noch heut Abend wieder melden und mal berichten. Event. stell ich dann mal den Source-Code rein - aber halt nicht alles sonst wird mich der Moderator noch verklagen ;-) - sondern halt so dass man einen Überblick bekommt. Gruß U
Hallo ! Sorry das ich mich erst jetzt melde, aber ich war leider unterwegs. Ja, den Fehler konnte man sogar im Simulator nachvollziehen ! Dort trat sogar das gleiche Ergebnis auf ! Ich werde jetzt versuchen die Verschachtelungstiefe der Aufrufe zu verringern - das geht zwar auf die Lesbarkeit des Source-Codes aber ich denke damit müsste sich event. eine Besserung einstellen. Gibt es event. noch eine Möglichkeit wie man das programm straffen kann. Z.B. Funktionsaufrufe die nur einen Befehl haben(Port setzen/löschen) - habe ich durch eine Define Anweisung ersetzt. Ich denke das sollte event. auch etwas dazu beitragen, oder ? Gruß U
> Gibt es event. noch eine Möglichkeit wie man das programm straffen kann. > Z.B. Funktionsaufrufe die nur einen Befehl haben(Port setzen/löschen) - > habe ich durch eine Define Anweisung ersetzt. inline nennt sich das. Geht halt dann auf Kosten der Programmgröße. > Ich denke das sollte event. auch etwas dazu beitragen, oder ? Nicht zwingend. Wenn das problem z.B. von einem falschen Pointer kommt wohl eher nicht.
U. B. schrieb: > Ich werde jetzt versuchen die Verschachtelungstiefe der Aufrufe zu > verringern Das versteckt eventuell den Fehler, behebt ihn aber nicht. D.h. der kommt garantiert wieder! Du mußt schon die Variable finden, die Du zu klein dimensioniert hast oder falsch zugreifst. Da ein Aufruf sie überschreibt, wird sie vermutlich zu klein sein und damit schreibt sich die Returnadresse da hinein, weil der Compiler denkt, die Variable ist zu ende. Peter
Returnadressen landen auf dem Stack, und der Compiler hat keinerlei Ahnung davon, wo und wie der eventuell mit anderen Datensegmenten kollidiert. Simulator starten, und Stack prüfen. Oliver
Oliver schrieb: > Returnadressen landen auf dem Stack Also genau nach den lokalen Variablen, zumindest beim AVR-GCC ist das so:
1 | void foo() |
2 | {
|
3 | int array[8]; |
4 | |
5 | array[8] = 12345; |
6 | faa(); |
7 | if( array[8] != 12345 ){ |
8 | printf("Array to small !"); |
9 | }
|
10 | }
|
Peter
Hallo Peter ! Ich hab die ganzen Verschachtelungen mal rausgenommen und laufen lassen und wie erwartet war der Fehler immer noch da. Peter Dannegger schrieb: > > Du mußt schon die Variable finden, die Du zu klein dimensioniert hast > oder falsch zugreifst. > > ... > Peter Das ist so gesehen das Problem von meinem Ex-Kollege der das programmiert hat und jetzt ist es meins :-( Ich muss die Nadel im Heuhaufen... Ich hab mal ein Bild von der Simulation angehängt. Kann das sein dass der Stack hier schon übergelaufen ist ? Der Stack fängt am Programmanfang 0x10FB an und bei diesem Bild ist er schon bei 0x105D. Die Variablen sind aber höher z.B. NRCODE[0] ist laut Sim bei 0x1061, oder ? Falls ich das Bild falsch interpretiere wie kann ich denn genau feststellen wann der Stack überläuft ? Eine weitere Sache ist diese:
1 | void usart_write_str(char *str) |
2 | {
|
3 | while (*str) |
4 | {
|
5 | long_delay(50); // GSM Modul kommt bei langen strings die |
6 | // in der Systemzeit geschickt werden nicht klar
|
7 | // und antwortet mit Fehler 314 - Sim Busy
|
8 | // Deswegen werden immer 50ms gewartet, bis das
|
9 | // nächste Zeichen gesendet wird!
|
10 | usart_write_char(*str++); |
11 | }
|
12 | }
|
Ich würde sagen im Prinzip ist der Zeiger hier O.K., aber solange man nichts reinschreibt. Hier ist es nur ein Lesevorgang also sollte das auch unkritisch sein. Ich hab schon überlegt ob ich statt des Pointers ein Array machen sollte, aber ob das was bringt ? Der Kollege übergibt manchmal Strings wie z.B. "gesendet" an diese Funktion (nur zum darstellen auf dem Bildschirm). Ich frag mich aber woher weiß die Funktion wann Ende ist wenn z.B. kein \r\n dahinter kommt ? Gruß U
> Ich frag mich aber woher weiß die Funktion wann Ende ist > wenn z.B. kein \r\n dahinter kommt? In C wird das Ende eines Strings durch ein Nullbyte '\0' angezeigt und darauf prüft auch die Bedingung der while-Schleife im gezeigten Code. Das sind aber C Grundlagen, die in jedem C Buch ganz vorne kommen!
> NRCODE[]
Bist du sicher, dass dieses Feld überhaupt auf dem Stack liegt?
Ohne dass die Definition im Code gezeigt wird, möchte ich das nicht
pauschal bejahen.
Wenn du ein Überschreiben der Feldgrenzen von NRDATA[] vermutest,
könntest du eine Überwachung der Variablen iz einrichten und mit der
Größenangabe in der Definition von NRDATA[] vergleichen (ggf. als
assert()).
Du kannst auch ermitteln, wo deine Variablen enden und den Stackpointer
regelmäßig überwachen, ob der dieser Adresse gefährlich nahe kommt.
Das wäre entweder eine Sache, die in einem regelmäßigen Interrupt
geschehen kann oder als Prolog/Epilog aller Funktionen. Bei letzterem
hat die GCC Toolchain Instrumente, um das zu machen. Schau dir die
Profiling Optionen im GCC Manual an. Oder du kannst auch einer letzten
Variable eine typischen Wert zuweisen und regelmäßig im (User-)Programm
prüfen, ob dieser Wert noch vorhanden ist oder bereits durch den
überlaufenden Stack gekillt wurde (Kanarienvogelverfahren).
Ich erinnere mich auch, dass vor einiger Zeit für die Stackprüfung hier
im Forum Routinen bereit gestellt wurden. Schau in der Codesammlung oder
in der Artikelsammlung im Softwarepool nach, wenn dich das interessiert.
Ich kenne jetzt das Studio 5 nicht, aber schau doch mal nach, ob man nicht das SRAM vor Simulaitonsbegin mit z.B. 0xFF vorbelegen kann. Wenn dann irgendwann keine 0xFF's mehr drinstehen, ist der Stack übergelaufen. Oliver
U. B. schrieb: > Der Stack fängt am Programmanfang 0x10FB an und bei diesem Bild ist er > schon bei 0x105D. Die Variablen sind aber höher z.B. NRCODE[0] ist laut > Sim bei 0x1061, oder ? Lokale Variablen liegen beim AVR-GCC im Stack, das ist also korrekt. U. B. schrieb: > Falls ich das Bild falsch interpretiere wie kann ich denn genau > feststellen wann der Stack überläuft ? Der Stack läuft über, wenn er in die globalen Variablen reinläuft. Du wirst allerdings eher einen falschen Zeiger haben und damit den Stack zerstören. Das ist kein Überlauf. Gern gemachte Fehler sind, einen Zeiger auf eine lokale Varieable als Returnwert zu übergeben, d.h. der zeigt ins Nirwana. Oder wie gesagt auf ein zu kleines Array. Ein nicht initialisierter Zeiger zeigt auf ein Array der Länge 0, also auch zu klein. Oder wenn malloc verwendet wird, den Mißerfolg von malloc nicht abzufangen. Peter
Wenn das Studio 5 die Möglichekit bietet, einen breakpoint auf den Zugriff auf eine Speicherstelle zu setzen, dann mach das. Damit bekommst du schnell raus, wer oder was dir eine Variable überschreibt. Oliver
Krapao schrieb: > In C wird das Ende eines Strings durch ein Nullbyte '\0' angezeigt und > darauf prüft auch die Bedingung der while-Schleife im gezeigten Code. > Das sind aber C Grundlagen, die in jedem C Buch ganz vorne kommen! Danke für die Ohrfeige. Stimmt deine Aussage aber noch wenn du nicht mit strings arbeitest, sondern vom Telit 862 Modem den Status in Form von Bytes abfragst und in ein char Array reinschreibst und dann diese oben gezeigte Funktion aufrufst ? Mein Ex-Kollege hat nach dem letzten empfangenen Byte kein '\0' reingesetzt ! @Oliver: Ich arbeite noch nicht allzulang mit der Studio 5 Umgebung. Geht das denn überhaupt mit Studio 5 bzw. kannst Du mir bitte sagen wie ich das einstellen muss ? Gruß U
Isch 'abe gar kein Studio 5... Insofern kann ich das nicht beantworten. Da aber der Simulator von Studio 4 das schon konnte, sollte so etwas auch irgendwo im Studio 5 versteckt sein. Wenn nicht, nimm halt Studio 4. Da rechts-clickt man einfach auf die gewünschte Speicheradresse, und setzt den Databreakpoint. Oliver
Wieviel Speicher hast Dü überhaupt noch frei? Was spuckt der Compiler aus, wenn Du das gesammte Projekt mal frisch compilierst? Iregndwelche Warnings?
Hallo Peter ! Also Programmspeicher ist ca. 10% voll aber der Datenspeicher ist fast 50% voll. Das kommt wegen den "riesigen" Arrays die er von SD-Karte einliest. 4 Sensoren insgesamt wobei jeder Sensor ein Array von über 50 Zeichen hat. Ein paar dieser Arrays werden am Anfang des programms gesetzt und ein paar Arrays werden dann lokal in den Funktionen deklariert. Mein Kollege sagte mir noch dadurch werden dann die lokalen Variablen gelöscht wenn man aus der Funktion wieder rauskommt. Das stimmt ja so gesehen, aber er bedachte nicht, dass er aus den Funktionen gar nicht mehr rauskommt, sondern erst mit dem Abschluss der übermittelten SMS alle Funktionen verläßt. Das bringt so gesehen erstmal recht wenig. Gruß U
U. B. schrieb: > Mein Ex-Kollege hat nach dem letzten empfangenen Byte kein '\0' > reingesetzt ! Wenn es kein String nach C-Norm ist, dann kannst Du auch keine Stringfunktionen verwenden. Ansonsten kracht es. Das wird bestimmt die Ursache sein für die Stackkorrumption. Also mußt Du daraus erstmal einen String machen. Peter
>und ein paar Arrays werden dann lokal in den Funktionen deklariert.
Diese lokalen Arrays sind in den "50% Speicher voll" vom Compiler noch
nicht enthalten, lokale Arrays landen erst zur Laufzeit auf dem Stack!
Wenn also nochmals ähnlich viele lokale Variabeln hinzukommen riecht es
sehr dannach, dass der Speicher überläuft, was auch sehr zum
Fehlverhalten passen würde!
Lösung => Grössere Datenstrukturen nur einmal global deklarieren und den
Funktionen einfach bloss den/die Pointer dazu übergeben. Globale
variabeln sind zwar oft etwas verpöhnt, aber bei wenig RAM (4kByte)
macht es durchwegs Sinn um Speicher zu sparen!
p.s. Sind printf() mit String-Konstanten als printf_p() angepasst?
Hallo Peter ! Deine Vermutung hab ich mir auch schon gedacht - also das mit den globalen Variablen. Das habe ich heute umgesetzt und konnte doch einiges einsparen. Jetzt ist der Datenspeicher nur noch zu 37% voll - hat einiges gebracht ! Wegen den printf-Anweisungen... Mein Kollege hat gar keine Stringroutinen eingebaut - also strcpy usw. Es ist so dass nur die Kommunikation vom Telit-Modem auf dem z.B. HTerm ausgegeben wird. D.h. jedes Byte was das Telit-Modem sendet wird in einem Buffer zwischengespeichert und dann an die usart-Ausgabe weitergeleitet. Mehr kommt an "String-Verarbeitung" nicht vor. Das macht die Sache natürlich eher etwas schwieriger sollte aber keinen Abbruch tun. Ich vermute auch dass die obige Routine für die usart-Ausgabe ins Nirvana läuft, und deswegen habe ich heute mal den ganzen Ausgabe-Buffer erstmal mit '\0' gefüllt - damit die ankommenden Zeichen vom Modem sauber reingeschrieben werden. D.h. ich muss mich nicht um das letzte '\0' kümmern. Nur als ich das laufen lies war der Buffer weder überschrieben noch sonst was. Da stand das alte gedöns von der Initialisierung noch drin um den Buffer erstmal mit Werten zu befüllen. Ich dachte danach müsste irgendwie 0en zusehen sein, aber nichts davon stand drin. Er schreibt dann die neuen Werte in den Buffer hinein nur der Rest ist dann nicht gelöscht. Ich werde das heute Abend mit dem Simu nochmal durchsteppen. Gruß U
Hallo Peter ! Es ist vollbracht ! Ich hab sämtliche Buffer am Ende mit einem '\0' versehen und geschaut dass alle Berechnungen den Buffer am Ende nicht übers Ziel hinaus überschrieben werden. Das hat geholfen und jetzt schnurrt das Ding. Vielen Dank ! Gruß U
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.