Hallo zusammen
Ich reserviere mit malloc einen kleinen Speicherbereich für einen Text.
malloc gibt mir eine Adresse zurück, und ich schreibe an diese meinen
Text.
Dies funktioniert soweit so gut.
Nun wird etwas später im Code eine neue uint16_t Variable in einer
Funktion gelöst.
Bevor diese Variable gelöst wird, sehe ich im Memory Browser meinen
zuvor kopierten Text an der Adresse, welche mir malloc zurückgegeben
hat.
Nach dem Initialisieren der Variable innerhalb meiner Funktion, steht
jedoch an dieser Stelle der Inhalt meiner neuen Variable.
Mein Text ist nun verloren.
Hat jemand eine Idee, wie es zu solch einem Verhalten kommen kann?
Muss ich malloc eine art "Startadresse" übergeben, ab welcher Speicher
reserviert werden kann?
Danke schonmal
Eure Krähe!
Es ist richtig, dass malloc die Adresse des allocierten Speichers
zurückliefert.
Wie ist die Größe des Speicherbereichs, welcher malloc reservieren soll?
Der Quelltext (Ausschnitt) wäre hilfreich zur Beurteilung.
An der Stelle, wo die obigen Variabeln initialisiert werden, wird der
von malloc allozierte Speicher überschrieben.
Verwendet wird btw. ein STM32F105, CooCox, und der ST-Link/V2 debugger
Holger Krähenbühl schrieb:> Muss ich malloc eine art "Startadresse" übergeben, ab welcher Speicher> reserviert werden kann?
Nein, wie denn auch. Aber was mir auffällt: malloc reserviert i.d.R. vom
Heap, lokale Funktionsvariablen stehen oft auf dem Stack, ist aber
kompilerabhängig - ich würde mal den Stackpointer prüfen.
Ist dein ganzer Text überschrieben oder bloss ein Teil?
Georg
Ihr seit ja super! Nun, wie kann ich das herausfinden ob es wirklich ein
Stack Overflow ist?
Kann ich den Speicherbereich für den Stack vergrössern?
Müsste das nicht eine Warnung ausgeben?
Georg schrieb:> Ist dein ganzer Text überschrieben oder bloss ein Teil?
Da mein Text sehr kurz ist, hat es den gesamten überschrieben
Steffen Rose schrieb:> Klingt etwas nach Stack Overflow. Heap und Stack werden wohl gleich> hintereinander liegen.
Vermute ich auch so. Liegt möglicherweise an zu wenig RAM. Oder daran,
dass die betreffende Funktion über eine Endlosschleife rekursiv
aufgerufen wird. Dann reicht natürlich kein RAM der Welt. ;-)
Markus Weber schrieb:> Liegt möglicherweise an zu wenig RAM.
Der verwendete Controller hat 64kB Ram
Die .data Section sagt: 2516 Bytes nach dem Compilieren.
Markus Weber schrieb:> Oder daran,> dass die betreffende Funktion über eine Endlosschleife rekursiv> aufgerufen wird.
Dies ist ebenfalls nicht der Fall.
Ich bin mommentan mit dem Debugger am durchsteppen.
Zudem liefert malloc ja weiterhin eine Adresse zurück und nicht '0'
Program Size:
text data bss dec hex filename
28868 2516 2176 33560 8318 Calibration.elf
Holger Krähenbühl schrieb:> Der verwendete Controller hat 64kB Ram> Die .data Section sagt: 2516 Bytes nach dem Compilieren.
Das enthält weder den Heap noch den Stack. Beide sind dynamisch und
wachsen/schrumpfen während der Laufzeit.
Der Heap wächst mit jedem Alloc-Aufruf, der Stack mit jedem
Funktionsaufruf und jeder automatischen Variablen (d.h. Variablen, die
in Funktionen definiert werden).
Vielleicht führt die Beantwortung folgender Fragen weiter:
Wurde die Variable "chr" mit einem passendern Wert initialisiert?
Wie oft wird die for-Schleife durchlaufen?
Rufus Τ. Firefly schrieb:> Das enthält weder den Heap noch den Stack. Beide sind dynamisch und> wachsen/schrumpfen während der Laufzeit.>> Der Heap wächst mit jedem Alloc-Aufruf, der Stack mit jedem> Funktionsaufruf und jeder automatischen Variablen (d.h. Variablen, die> in Funktionen definiert werden).
Gibt es eine Möglichkeit, die grösse der beiden während der Laufzeit zu
messen? Bzw auszulesen?
Martin schrieb:> Vielleicht führt die Beantwortung folgender Fragen weiter:> Wurde die Variable "chr" mit einem passendern Wert initialisiert?> Wie oft wird die for-Schleife durchlaufen?
Du hast recht, der Text wird während der for-Schleife überschrieben.
Holger Krähenbühl schrieb:> Gibt es eine Möglichkeit, die grösse der beiden während der Laufzeit zu> messen? Bzw auszulesen?
Für den Heap wirst Du Dir die Runtime-Library ansehen müssen, die sollte
entsprechende Funktionen enthalten.
Eine recht gute Einschätzung des momentanen Wert des Stackpointers
bekommst Du z.B. so:
1
...irgendwoineinerFunktion
2
3
unsignedintspwert;
4
5
{
6
inti;
7
8
spwert=(unsignedint)&i;
9
}
10
11
...hierkannspwertbetrachtetwerden
(setzt voraus, daß sizeof (unsigned int) == sizeof (pointer) ist, ist
auf den meisten Architekturen der Fall)
Rufus Τ. Firefly schrieb:> Eine recht gute Einschätzung des momentanen Wert des Stackpointers> bekommst Du z.B. so:.......
Vielen Dank!
Ich habe nun den Stack im Startupfile von 0x000100 auf 0x001000
vergrössert und das Problem war behoben.
Der Stack wird ja während der Laufzeit nicht wieder kleiner oder?
Also kann man die Änderung als saubere Lösung des Problems betrachten?
Danke!
Hi
Der Stack wächst typischerweise nach unten. Und der Heap nach oben. Du
hast dein Problem also vermutlich nur verschoben bis dein Heap wieder
mit dem Stack kollidiert (dann bei Stack + Heap = 4kiB statt Stack +
Heap = 256B).
Wie sieht denn dein Linkerscript aus?
Matthias
Es ist prinzipiell richtig, die Stackgröße dem realen Bedarf anzupassen
und löst damit das Problem eines Stackoverflows.
Meine bisherigen Erfahrungen zu malloc() sind, dass dieser den Heap
nicht verläßt. Ist der Heap zu klein angegeben, bekommt man einen
NULL-Pointer.
Ich empfehle, möglichst einen Stack-Test in jedes embedded Programm
einzubauen.
Während der Initialisierung setzt man am unteren/oberen Stack-Ende ein
paar Bytes mit einer Magic Number. Dann kann man zyklisch im
Hauptprogramm einfach prüfen, ob es einen Stack Overflow gab.
Erst vor kurzem hat ein Kollege tagelang einen Fehler gesucht, der
mittels Stack-Test in kürzester Zeit erkannt worden wäre.
Holger Krähenbühl schrieb:> class->object_text = malloc((( strlen(Text) + 1) * sizeof(char)));
Kleiner Tip: sizeof(char) ist immer 1. Daher reicht hier malloc
(strlen (Text) + 1).
Rufus Τ. Firefly schrieb:> (setzt voraus, daß sizeof (unsigned int) == sizeof (pointer) ist, ist> auf den meisten Architekturen der Fall)
Auf x86_64 zum Beispiel nicht... Dafür gibts uintptr_t in der <stdint.h>
.
Das Problem hat sich nun, wie bereits prognostiziert, verschoben.
Nach dem vergrössern des Stack gings einige Zeit lang gut.
Nun wieder die Selben fehler.
Vermutlich ist es nun der Heap welcher zu klein ist.
Wie kann man denn den Heap vergrössern?
Danke!
Holger Krähenbühl schrieb:> Vermutlich ist es nun der Heap welcher zu klein ist.
Wäre der Heap zu klein, würden malloc-Aufrufe fehlschlagen und NULL
zurückgeben. Da Du das abfangen wirst ... kann es das nicht sein.
Holger Krähenbühl schrieb:> Der verwendete Controller hat 64kB Ram
Dann sollte ja massig Platz sein.
Prüfst du auch immer brav, ob das Malloc nicht Null liefert?
Es gibt für manche Compiler Tools, die grasen den Calling-Tree ab und
ermitteln so die maximal mögliche Stacklast. Oft können die sogar
Funktionspointer aufdröseln.
Rufus Τ. Firefly schrieb:> Da Du das abfangen wirst ... kann es das nicht sein.
Ja das prüfe ich, da hast du recht.
Peter Dannegger schrieb:> Dann sollte ja massig Platz sein.> Prüfst du auch immer brav, ob das Malloc nicht Null liefert?
Ja das wird immer konsequent geprüft.
Holger Krähenbühl schrieb:> Das Problem hat sich nun, wie bereits prognostiziert, verschoben.>> Nach dem vergrössern des Stack gings einige Zeit lang gut.> Nun wieder die Selben fehler.
Zeig mal dein Code -- 4k Stack soll bei 64k RAM eigentlich reichen.
Entweder legst du "riesige" Datenstrukturen auf dem Stack, oder du hast
zu viele verschachtelte Funktionsaufrufe, oder irgendwo ist eine
rekursive Schleife.
Eric B. schrieb:> Holger Krähenbühl schrieb:>> Das Problem hat sich nun, wie bereits prognostiziert, verschoben.>>>> Nach dem vergrössern des Stack gings einige Zeit lang gut.>> Nun wieder die Selben fehler.>> Zeig mal dein Code -- 4k Stack soll bei 64k RAM eigentlich reichen.> Entweder legst du "riesige" Datenstrukturen auf dem Stack, oder du hast> zu viele verschachtelte Funktionsaufrufe, oder irgendwo ist eine> rekursive Schleife.
Hallo Eric
Ich zeige dir/euch gerne den Code.
Es sind jedoch an die 5000 Zeilen.
Ich kann schlecht nur eine "kritische" Stelle posten, da ich ja
ebendiese nicht kenne.
Ist es denn noch immer das gleiche Problem - heißt: an der ursprünglich
geposteten Stelle wird der Text überschrieben, der in einem
Speicherbereich steht, der mit malloc() angefordert wurde.
Prüfe im Mapping File, ob dein Heap korrekt gesetzt ist. Prüfe auch mal
die gemachten Aussagen, dass malloc() in deiner Umgebung NULL liefert,
wenn im heap nichts mehr frei ist.
Beobachte deinen Stackpointer. Eine Rekursion sollte doch zu finden
sein.
Holger Krähenbühl schrieb:> Ich kann schlecht nur eine "kritische" Stelle posten, da ich ja> ebendiese nicht kenne
Dann nach und nach Teile auskommentieren bis der Fehler nicht mehr
auftritt. Obwohl das bei einem Stacküberlauf nicht unbedingt zu der
Fehlerquelle führen muss.
Am bequemsten wäre definitiv mit einem Debugger ein Write Access
Breakpoint auf den "Stack Top" zu legen.
Steffen Rose schrieb:> Ist es denn noch immer das gleiche Problem - heißt: an der ursprünglich> geposteten Stelle wird der Text überschrieben, der in einem> Speicherbereich steht, der mit malloc() angefordert wurde.
Nein, es sieht effektiv nach einer Rekursion oä. aus. Nach mehrmaligem
Aufruf und Ablauf einer gewissen Funktion stürzt das Programm ab. Ein
eindeutiges Muster konnte ich noch nicht erkennen, da das Programm
manchmal nach dem ersten, manchmahl erst nach dem 10. Aufruf abstürzt.
Wahrscheinlich abhängig davon was zuvor aufgerufen wurde.
In der Funktion und dazugehörenden Unterfunktionen konnte ich keinen
Fehler entdecken. Ich hatte aber leider nicht viel Zeit es zu testen und
kann euch nicht viel mehr dazu sagen.
Eric B. schrieb:> Am bequemsten wäre definitiv mit einem Debugger ein Write Access> Breakpoint auf den "Stack Top" zu legen.
Interessante Idee, ich werd mich mal schlau machen ob mein Debugger das
kann.
Am 22. habe ich einen "Trick" gepostet, den "in etwa" aktuellen Stand
des Stackpointers zu ermitteln - Du könntest das in Dein Programm an
strategischen Stellen einbauen und den Wert irgendwohin ausgeben lassen.
Wenn er sich in der gleichen, auf die (vermeintlich) gleiche Art und
Weise aufgerufenen Funktion ändert, hast Du eine Stelle gefunden, die Du
Dir genauer ansehen solltest.
Hi
Da heute ja schon für weiße Ware ein Selbsttest vorgeschrieben ist, gibt
es soetwas schon von Atmel als Vorlagen.
Such mal bei Atmel nach IEC60730 Class B Test.
Dort findest du eine ganze Menge von Selbsttests der verschiedenen Atmel
Controllern und deren Hardware im laufenden Programmen.
Ist heute ja schon fast überall vorgeschrieben, auch in Waschmaschienen
und Backöfen.
Rainer
Dies wird alle 100ms aufgerufen.
Doch der Ausgegebene Text ändert sich nie.
Auch mit dem Debugger ändert sich die Adresse von i nie
Nach einiger zeit stürtzt der Controller ab, da malloc keinen Speicher
mehr alloziieren kann. Das prinzip funktioniert also.
Hat jemand eine idee, warum sich die adresse von i nicht ändert?
Holger Krähenbühl schrieb:> Hat jemand eine idee, warum sich die adresse von i nicht ändert?
Wahrscheinlich weil du diese if-Abfrage in deiner Haupschleife machst.
Da soll der Stackzustand eigentlich immer gleich sein. Bau dieser
Konstrukt mal an ein paar mehr Stellen in deiner SW ein.
zB so:
1
charbuffer[10];
2
3
voidshowSP()
4
{
5
unsignedinti;
6
char*buffer;
7
i=(unsignedint)&i-0x20000000;
8
ItoA(i,buffer,"");
9
WriteText(Handle,BLACK,WHITE,buffer);
10
}
11
12
13
voidfoo(void)
14
{
15
WriteText(Handle,BLACK,WHITE,"foo:");
16
showSP();
17
18
// foo code
19
}
20
21
intbar(void)
22
{
23
WriteText(Handle,BLACK,WHITE,"bar:");
24
showSP();
25
}
EDIT: buffer statisch gemach - spart malloc(), free() und NULL-Abfrage
in showSP().
Eric B. schrieb:> Holger Krähenbühl schrieb:>>> Hat jemand eine idee, warum sich die adresse von i nicht ändert?>> Wahrscheinlich weil du diese if-Abfrage in deiner Haupschleife machst.
Wobei anzumerken ist, dass es im fehlerfreien Zustand korrekt ist, dass
in der Hauptschleife der Stackpointer immer gleich ist.
Jörg Wunsch schrieb:> Besser:register char *stack_ptr asm ("sp");
Sicher besser, aber abhängig vom verwendeten Prozessor und Compiler. Das
wird nicht jeder C-Compiler für jeden µC so übersetzen.
Ich würde den Stackverbrauch mit einem "High Water Mark" messen.
Fülle hierzu (vor beginn des Programms) den Stack komplett mit einem
Byte-Pattern deiner Wahl (z.B. 0x55).
Während der Laufzeit des Programms brauchst du nur schaun (mit debugger
und MemoryDump), bis wohin dieses Bytepattern überschrieben wurde.
Little Basdart schrieb:> Ich würde den Stackverbrauch mit einem "High Water Mark" messen.
Wenn es um den Stackverbrauch geht, würde ich diese Methode auch
bevorzugen.
Da der TO jedoch mittlerweile 4k Stack hat und keinen Grund für einen
derart hohen Verbrauch angibt, gehe ich von einem Fehler im Programm
(einige Ideen wurden schon genannt) aus.
Was würden dann die ganzen Stacktests liefern?
Höchsten die Bestätigung, dass es eine ungewollte Rekursion gibt.
Wäre ja nicht schlecht. Aber soviel näher am eigentlichen Problem ist
man trotzdem nicht dran.
Der diskutierte Test des Stackpointers mittels Pointer müßte dann aber
in einer Funktion innerhalb der Rekursion (die unbekannt und ungewollt
ist) oder in einem Interrupt (ungenau) untergebracht werden.
Ich stimme zu, dass das messen der Stack-Größe an dieser Stelle keinen
Sinn mehr macht. Man weiß ja bereits, dass der Stack überläuft.
Wichtig wäre zu wissen, wie der Stack aussieht, sobald das geschieht!
(Lass dir den Callstack vom Debugger anzeigen)
Um den Prozessor zu stoppen, wenn der Fehler auftritt ist eine memory
protection unit hilfreich (aber die hat der STM32F1 nicht xD)
Oder du verwendest die vorgeschlagene Funktion, aber leicht abgewandelt:
1
voidStackTest(){
2
uint8_ti;
3
externuint8_t__StackLimit;// symbol definiert im Linkerfile,
Sieh Dir mal den Wert des Stackpointers in Deinem Screenshot an.
Da sehe ich 0x200053e0, was von 0x200037fc doch etwas entfernt ist -
dazwischen liegen gut 7 k.
Da bin ich ganz deiner Meinung. Jedoch ist der Wert des SP nach dem
Initialisieren schon auf 0x20005408, Dh es müsste irgendwo eine riesige
globale Variable erstellt werden..
Holger Krähenbühl schrieb:> Jedoch ist der Wert des SP nach dem> Initialisieren schon auf 0x20005408, Dh es müsste irgendwo eine riesige> globale Variable erstellt werden..
Könntest Du näher darauf eingehen, an welchem Punkt Du bist?
Schaust Du in das richtige Mappingfile?
Welcher Wert steht auf 0x08000000?
Kurz nach der Initialisierung bist Du am Anfang von main(). Die
Stack-Variablen von main() sind ja noch überschaubar und gut prüfbar.
Globale Variable liegen nicht auf dem Stack.
Anm:
Wenn ich es richtig in Erinnerung habe setzt CooCox den Stackpointer
nicht explizit neu auf default. Er müßte entsprechend durch den CPU
Reset auf dem ersten Wert in der Vektortabelle gesetzt worden sein.
Machst Du keinen Reset oder verwendest Du einen Bootloader, der den
Stackpointer beim Einsprung nicht korrekt vorbelegt?
-------
Der Trap wird einige Zeit nach dem Problem auftreten. Wenn Du den Weg
über die Trap's weitergehen willst, solltest Du getrennte Handler für
die verschiedenen Traps machen.
Ansonsten füge die beschriebene Stackprüfung in sehr vielen bzw.
problemrelevanten Funktionen ein.
Anm: Ich gehe mal davon aus, dass Du keine Routinen verwendest, welche
den Stackpointer direkt verändern und dich so in die Irre führen.
Hi
laut dem Linkerscript was du oben mal gezeigt hast sollte dein SP in
Main irgendwo bei (0x20010000 - (ein paar Byte)) liegen. Schau dir das
gleich beim Systemstart im Debugger mal an. Wenn das nicht so ist stimmt
irgendwas überhaupt nicht. Du kannst auch mal die ELF-Datei (oder besser
noch MAP-Datei) hochladen.
Matthias
Also, im Anhang ein Screenshot der Register bei Systemstart. Das Gerät
wurde neu gestartet, der Flash überschrieben und der Debugger steht an
erster Stelle im Code.
Zusätzlich noch die gewünschten Files.
Ich habe die den Fehler auf die Stelle gleich vor dem Absturz
eingekreist.
Wenn ich so etwas sehe, schrillen bei mir direkt die Alarmglocken.
Hier wird auf uGUI_ObjectID[objectID - 1].Addr unbedingt zugegriffen.
Wie stellst Du sicher, dass objectID tatsächlich > 0 und nicht
vielleicht == 0 ist?
Bei der Initialisierung fehlte das else, war also keine Funktion
übergeben, stand irgendetwas zufälliges in onClick. Das erklärt zwar
noch nicht warum sp so hoch ist, aber der Absturz scheint behoben.
@Frank Vor dem Zugriff wird geprüft ob objectID 0 ist
Hi
Auch wenn das jetzt erstmal ein Fehler war: Der Wert deines SP ist für
mich jetzt auch nachvollziehbar. Du hast deine STACK_SIZE jetzt
wahrscheinlich auf 0x1200 stehen. Damit hast du 18432 Bytes an Stack
reserviert da STACK_SIZE in deinem Beispiel sich auf die Elemente von
pulStack bezieht und das sind unsigned long. Du kannst das erste Element
deiner Vektortabelle auch auf __stack setzen und pulStack rauswerfen.
Dann steht dir der gesamte freie RAM als Stack zur Verfügung.
Außerdem scheint mir die Größe des RAM im Linkerscript falsch. Der
STM32F105 hat doch 64k RAM oder?
Matthias
Μαtthias W. schrieb:> wahrscheinlich auf 0x1200 stehen
stimmt.
Μαtthias W. schrieb:> Außerdem scheint mir die Größe des RAM im Linkerscript falsch. Der> STM32F105 hat doch 64k RAM oder?
Laut Datenblatt ja.
Ist mir nicht ganz wohl dabei ins Linkerscript zu schreiben. Ich kenn
mich damit überhaupt nicht aus.
Warum es trotzdem soweit zu funktionieren scheint ist mir schleierhaft.
Welche Adresse liefert das erste malloc()? Vergleiche diese Adressen mit
dem jeweiligen Mapping File auf Stimmigkeit und auf Kollisionsfreiheit
mit dem Stack.
Wie man es setzt, keine Ahnung. Nutze kein malloc().
Kommt auch auf deine malloc() Implementierung an (sprich, welche lib).
Hast Du eine sbrk() Funktion?
Lt. altem Forum von CooCox ist es recht complex. Vermutlich wird es aber
nur für die abgespeckte lib (nano?) zutreffen. Da es bei Dir prinzipiell
zu funktionieren scheint, sei an dieser Stelle vorsichtig beim
übernehmen von den angesprochenen Änderungen:
[[http://www1.coocox.org/forum/topic.php?id=917]]
Dies ist ein _sbrk() aus einem CooCox Beispiel mit newlib.
Diese Implementierung nutzt wie angesprochen den oberen Teil des Stacks
als Heap. Dann nützen die ganzen Tipps wenig welche den Stackoverflow am
oberen Ende überwachen. Hier müßtest Du das Zusammenwachsen von Heap und
Stack überwachen. Daher hat das vergrößern des Stacks auch Luft für den
Heap geschaffen, falls Du eine ähnliche Routine einsetzt.