Forum: Compiler & IDEs Compilerfehler bei mem-check


von Michael (Gast)


Lesenswert?

ich bekomme einen Compilerfehler:

mem-check.c:28: internal compiler error: in start_function, at 
c-decl.c:6035


weiß jemand Rat?


#include <avr/io.h>  // RAMEND
#include "mem-check.h"

// Mask to init SRAM and check against
#define MASK 0xaa

// From linker script
extern unsigned char __heap_start;

unsigned short get_mem_unused (void)
{
   unsigned short unused = 0;
   unsigned char *p = &__heap_start;

   do
   {
      if (*p++ != MASK)
         break;

      unused++;
   } while (p <= (unsigned char*) RAMEND);

      return unused;
}

/* !!! never call this function !!! */
void _attribute_ ((naked, section (".init8")))__init8_mem (void)
{  !!!hier ist der Compilerfehler!!!
   __asm volatile (
      "ldi r30, lo8 (__heap_start)"  "\n\t"
      "ldi r31, hi8 (__heap_start)"  "\n\t"
      "ldi r24, %0"                  "\n\t"
      "ldi r25, hi8 (%1)"            "\n"
      "0:"                           "\n\t"
      "st  Z+,  r24"                 "\n\t"
      "cpi r30, lo8 (%1)"            "\n\t"
      "cpc r31, r25"                 "\n\t"
      "brlo 0b"
         :
         : "i" (MASK), "i" (RAMEND+1)
   );
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Known issue, tritt seltsamerweise nur beim WinAVR-Build des Compilers
auf.

Workaround: entkopple die Deklaration und die Definition der Funktion.
1
void __attribute__ ((naked, section (".init8")))__init8_mem (void);
2
void __init8_mem (void)
3
{
4
   /* code goes here */
5
}

Zusätzliche Bemerkungen: benenne die Funktion bitte init_mem, nicht
__init8_mem.  Solche Namen sind für Compiler und Systembibliothek
reserviert.  Außerdem kannst du das gut und gern bereits in .init3
abfackeln und es gibt keinen Grund, unlesbaren inline asm Code
dafür zu schreiben.  Das geht auch prima in C:
1
void __attribute__((naked, section(".init3"))) init_mem(void);
2
void init_mem(void)
3
{
4
        extern uint8_t __heap_start;
5
        uint8_t *cp;
6
7
        for (cp = &__heap_start; cp < (uint8_t *)RAMEND; cp++)
8
                *cp = 0x42;
9
}

von Michael (Gast)


Lesenswert?

OK besten Dank so läufts.


ich habe sporadisch abstürze und möchte mit dieser Funktion prüfen ob es 
einen Stacküberlauf gibt.

get_mem_unused ();
sollte jetzt den freien RAM im whorst case anzeigen, ist das richtig?
Momentan kommt 0 zurück.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ich kenne deine Implementierung für get_mem_unused() nicht, aber
es hört sich so an, als sollte das ungefähr so benutzt werden, ja.

von ... (Gast)


Lesenswert?

Hi,

@Jörg: Die Implementierung von get_mem_unused steht im ersten Post :)

Dürfte allerdings so nicht funktionieren und immer 0 zurückliefern. Mann 
muß vom aktuellen Stackpointer ausgehend "nach unten" suchen um den 
Abstand zum "heap end" zu ermittteln. Bei "heap start" anzufangen und 
"nach oben" zu suchen ist normalerweise ziemlich für die Katz ...

CU

von Michael (Gast)


Lesenswert?

Hallo Gast,


richtig da kommt immer 0 zurück,
kannst Du die geänderte Suchfunktion einstellen?

von Karl H. (kbuchegg)


Lesenswert?

Du wirst doch wohl noch die Schleife umdrehen können :-)

Fang bei RAMEND an zu immer kleineren Werten hin.
Solange du im Speicher kein 0xaa vorfindest, bist
du sicher noch im Stack. Leider gilt das umgekehrte nicht :-)
Wenn du ein 0xaa vorfindest, heist das nicht, dass du den
Stack durch hast, denn am Stack kann ja auch zufällig ein
0xaa liegen. Ist sogar recht wahrscheinlich, dass du auf eines
stossen wirst. Von daher ist es sicherlich besser, wenn du
das Muster auf eine etwas längere Sequenz ausdehnen würdest.
Der berühmte 0xAFFE bietet sich an. Nun kann zwar auch zufällig
am Stack ein 0xAFFE liegen, das ist aber deutlich unwahrscheinlicher
als ein einzenles 0xaa.

Hast du dann den ersten 0xAFFE gefunden, geht es in der nächsten
Schleife weiter, bis der 0xAFFE wieder nicht mehr gefunden werden
kann. Das bedeutet dann, dass du durch den 'freien' Speicher durch
bist und den Bereich der Heap Allokierungen erreicht hast.

Du weist jetzt also wo der Stack endet und wo der Heap anfängt.
Die Differenz davon ist der noch freie Speicher zwischen Heap
und Stack.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Man könnte auch bei __brkval anfangen und von dort nach oben gucken.
In dieser Variablen steht die Adresse, bis zu der malloc() den
Speicher bereits in Beschlag genommen hat.  Darüber kommt dann
freier Platz im Stack.

Man muss allerdings dann den Sonderfall behandeln, dass __brkval
noch NULL ist, dann wurde noch kein einziges malloc() aufgerufen und
man müsste bei __heap_start starten.

von Michael (Gast)


Lesenswert?

Hi,

so habe ich es geändert, als einfache Version ohne Prüfung auf 0xAFFE.
Jetzt erhalte ich Werte zwischen 30 und 600.

#include <avr/io.h>  // RAMEND
#include "mem-check.h"

// Mask to init SRAM and check against
#define MASK 0xaa

// From linker script
extern unsigned char __heap_start;

unsigned short get_mem_unused (void)
{
   unsigned short unused = 0;

   unsigned char *p = (unsigned char*) RAMEND;

   do
   {
      if (*p-- == MASK)
         break;

      unused++;
   } while (p >= &__heap_start);

      return unused;
}


void __attribute__((naked, section(".init3"))) init_mem(void);
void init_mem(void)
{
        extern uint8_t __heap_start;
        uint8_t *cp;

        for (cp = &__heap_start; cp < (uint8_t *)RAMEND; cp++)
                *cp = 0x42;

}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Michael wrote:

> so habe ich es geändert, als einfache Version ohne Prüfung auf
> 0xAFFE.  Jetzt erhalte ich Werte zwischen 30 und 600.

Naja...

> #define MASK 0xaa

>       if (*p-- == MASK)

>                 *cp = 0x42;

Du solltest die Werte wohl schon anpassen. :-/

Außerdem: wenn du von RAMEND nach unten guckst, erfasst du zuerst den
Stack.  Der sollte immer ein wenig benutzten RAM haben...  Wie Karl
Heinz schon andeutete, ist die Erkennung dort nicht ganz trivial, da
deine MASK (oder deine 0x42, je nachdem, wofür du dich entscheidest)
ja auch normaler Inhalt des Stacks sein könnte.

Erst, nachdem du die Stackbelegung bewertet hast, sollte dann der
freie RAM folgen, der mit MASK gefüllt ist.  Wenn du dann noch weiter
nach unten läufst, kommt entweder der Heap oder die statischen
Variablen (falls kein malloc() benutzt wird).

Daher war ja mein Vorschlag, dass du vom Ende des Heap nach oben
gehst.

von Michael (Gast)


Lesenswert?

wenn ich jetzt einfach den Stackpointer von __brkval abziehe und dieser 
>0 ist? Passt das?

extern unsigned char __brkval;

uint16_t momentan_frei = SP - (uint16_t) &__brkval;

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Michael wrote:

> wenn ich jetzt einfach den Stackpointer von __brkval abziehe und dieser
> >0 ist? Passt das?

Damit hast du den aktuell freien RAM.  Das Ziel der Mustersuche dagegen
ist es, den über die bisherige Laufzeit minimal vorhandenen freien
RAM zu ermitteln.

von G. L. (sprintersb)


Lesenswert?

Hi, die urspüngliche Implementierung findet sich in 
http://www.roboternetz.de/wissen/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc#Dynamischer_RAM-Verbrauch

-- Ich hatte Inline Assembler gewählt, weil der Code ansonsten bei -O0 
nicht lauffähig ist (weiß jetzt nicht mehr genau, warum).
-- Gedacht war der Code, wenn ohne malloc() etc gearbeitet wird, weil 
man das auf AVR möglichst vermeiden sollte, zumindest bei den kleineren 
Derivaten -- und idR auch vermeiden kann.

== Edit ==

Ah ja. Der Grund ist, daß man nicht garantieren kann, daß GCC alle 
Variablen in Registern hält und evtl. auto-Variablen im Frame anlegt, 
die dann von der init-Routine überschreiben würden. Man darf dann also 
maximal bis SP initialisieren und nicht bis RAMEND.

von holger (Gast)


Lesenswert?

>get_mem_unused ();
>sollte jetzt den freien RAM im whorst case anzeigen, ist das richtig?
>Momentan kommt 0 zurück.

Wenn man printf() aus der avr-libc benutzt kommt tatsächlich
immer 0 zurück. printf() benutzt malloc(). Damit wird der
Heap verändert und get_mem_unused (); funktioniert nicht mehr.

von holger (Gast)


Angehängte Dateien:

Lesenswert?

Meine Testprogramme.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

holger wrote:

> printf() benutzt malloc().

Schon lange nicht mehr.  fdevopen() benutzt malloc(), aber es gibt ja
mittlerweile fdev_setup_stream() und FDEV_SETUP_STREAM() als
Alternativen.

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.