Ich benutze den ATmega8 unter WINAVR. Ich will zur Laufzeit die Stackauslastung anzeigen lassen. Dazu muß ich aber wissen, wo der Datenbereich endet. Im Listfile habe ich nun gesehen, daß die Variablen aufsteigend nach den Namen der Objektfiles plaziert werden. Ich habe nun ein File "zzzzzzzz.c" erzeugt und dort eine globale Variable plaziert. Nun kann ich mir anzeigen lassen, wieviel Bytes vom Stack bis hinunter zu dieser Variable noch frei sind. Allerdings scheint mir diese Lösung nicht sehr professionell. Gibt es eine bessere Methode ? Peter
Aber nur, wenn Du auch malloc() benutzt. ;-) Ansonsten, ist schon x-mal diskutiert worden: den ganzen RAM (am besten im .init1, also vor der Initialisierung von .data und .bss) mit einem Muster füllen. Später auswerten, bis wohin das Muster noch existiert.
Da tun sich ja Abgründe auf. Ich selber benutze ja malloc() nicht, aber es gibt wohl einige Bibliotheksfunktionen, die es einem heimlich unterjubeln. Dann würde meine Methode ja nicht mehr funktionieren, da der von malloc() belegte Platz mit als frei angesehen würde. Und ich will den freien Platz auch immer mit 0x77 überschreiben, um die maximale Stackbelegung zu ermitteln, d.h. der malloc() Speicher würde mit plattgemacht. Da muß man wohl 2 Funktionen für die Stackermittlung schreiben, eine ohne malloc() und eine mit. Gibt es vielleicht eine Möglichkeit, einen Linker-Fehler zu erzeugen, wenn eine Funktion versucht, einem malloc() unterzujubeln ? Beim 8051 war das ja so schön einfach: RAMEND-SP Peter
@Jörg, "...ist schon x-mal diskutiert worden" hast du dazu einen Link ? Peter
Aha, ich habe gerade mal ein wenig experimentiert! Das Problem bei der Verwendung von malloc liegt darin, daß "brkval" nicht existiert. Wenn man kein malloc verwendet, dann ist die Sache recht einfach: #include <avr/io.h> extern unsigned int __bss_end; volatile unsigned int sp; volatile unsigned int bss = (unsigned int) &__bss_end; volatile unsigned int sz = 0; void foo(void); void bar(void); int main(void) { while(1) { sp = SP; sz = sp - bss; foo(); } /* NEVEREACHED */ return 0; } void foo(void) { sp = SP; sz = sp - bss; bar(); } void bar(void) { volatile unsigned char c[32]; sp = SP; sz = sp - bss; foo(); } Das einfach mal im AVRStudio simulieren und ein paar watches anlegen: sp zeigt den Stackpointer, bss das Ende der statischen Variablen (wer hätte das gedacht? ;) und sz zeigt den verbleibenden Platz zwischen __bss_end und SP.
Der einfachste Weg herauszufinden, wer malloc() benutzt, ist das Lesen der Doku. ;-) (OK, ich sehe gerade, da fehlt noch was... das werde ich schnellstens nachholen.) Der zweiteinfachste wäre das Lesen des Sourcecodes... Im Ernst: derzeit wird malloc() ausschließlich von Mitgliedern der stdio-Familie benutzt. Insbesondere benutzt fdevopen() es, um die struct __iob anzulegen, und die printf- und scanf-Familien benutzen es in ihrer höchsten Ausbaustufe (floating point IO, bei scanf() dann auch noch character class recognition), da m. E. die Speicheranforderungen dieser Aufgaben das Maß, was man statisch allozieren sollte, überschreiten. Wenn Du eine eigene Mustererkennung machen willst, dann nimm ein eigenes malloc(). Da kannst Du dann tun und lassen, was Du willst. Auch die Bibliotheksroutinen würden in diesem Falle auf Dein malloc() zugreifen. Eine alternative Implementierung für fdevopen(), die ohne malloc() auskommt, ist übrigens auch im Gespräch, bin ich aber noch nicht dazu gekommen. Das Problem ist dabei weniger, dass die Leute der zusätzlich allozierte RAM stören würde, vielmehr ist oft genug der ROM-Overhead dafür zu viel. (Wenn man kein fdevopen() benutzt, sondern nur die sprintf/sscanf-Varianten, und kein floating point braucht, kommt man auch jetzt bereits komplett ohne malloc() aus.) Nein, Link zu den Threads habe ich nicht parat, aber das Thema ist schon paarmal angefragt worden. Auch Patrick `OldBug' hatte da wohl schon seinen Senf dazu gegeben, soweit ich mich erinnern kann.
Vielen Dank, das extern unsigned int __bss_end; ist genau das, was ich gesucht habe. Sieht ja doch irgendwie blöd aus, wenn ein File "zzzzzzzz.c" heißt. Und das malloc() ist wohl nicht so kritisch, wie ich erst dachte, werds wohl nicht brauchen. Bzw. die Idee mit dem selber schreiben klingt auch gut, es sollte ja eine Tiefe von 1 bis max 2 Aufrufen ausreichen. Peter
Ich hab mal weiter gemacht. Es würde ausreichen, "brkval" aus malloc.c global Verfügbar zu machen. Ich habe mal das modifizierte malloc.c angehangen (source war malloc aus der avr-libc 1.0.4). Hier das "Tesprogramm": --8<-- #include <avr/io.h> #include <stdlib.h> volatile unsigned int heapsz; void *p; void foo(void); void bar(void); unsigned int freemem(void); int main(void) { while(1) { heapsz = freemem(); foo(); } /* NEVEREACHED */ return 0; } void foo(void) { heapsz = freemem(); bar(); } void bar(void) { volatile unsigned char c[32]; p = malloc(32); heapsz = freemem(); //free(p); foo(); } unsigned int freemem(void) { extern char *brkval; return (unsigned int) SP - (unsigned int) brkval; } -->8-- Was ist zu tun? 1. main.c und (angehängtes) malloc.c in ein Verzeichnis 2. Makefile erstellen, und eigenes malloc.c als src angeben 3. Testen... Man müsste jetzt noch beide Varianten (malloc/kein malloc) vereinen und schon könnte man sich genüsslich die Größe des Verbleibenden Speichers ansehen.
Anbei mein Code (ohne Berücksichtigung von malloc). Ich habs aber noch nicht getestet. Nach dem Reset führt man ihn einmal aus. Bei jedem Aufruf gibt er den minimal verfügbaren Speicher seit dem letzten Aufruf aus. Man kann damit z.B. testen, ob es Funktionen gibt, die besonders hungrig sind, indem man diese zwischen 2 Aufrufen ausführt und dann die Differenz bildet. #include "main.h" extern u8 __bss_end; u16 stack_use( void ) { u16 i; u8 *mp; for( i = 0, mp = &__bss_end; (u16)mp <= SP; *mp++ = 0x77 ) if( (*mp != 0x77) || i ) i++; // count used stack return SP - (u16)&__bss_end - i; } Peter
Moment mal...wolltest Du "nur" die Größe des Stacks wissen? Dann müsste doch (RAMEND - SP) das gewünschte Ergebnis liefern.
Btw., brkval heißt mittlerweile (CVS-HEAD von avr-libc) __brkval und ist in der Tat global. Ist natürlich kein garantiertes Interface. Die Öffnung wurde notwendig, um realloc() implementieren zu können.
Ja, das hatte ich auch schon gesehen, deswegen auch der Hinweis auf den Source der v1.0.4. :-)
So, ich habs nochmal überarbeitet, funktioniert super: #include "main.h" #define FREE_MARK 0x77 extern u8 __bss_end; // lowest stack address extern u8 __stack; // highest stack address u16 stack_size( void ) // available stack { return (u16)&__stack - (u16)&__bss_end + 1; } u16 stack_free( void ) // unused stack after last call { u8 flag = 1; u16 i, free = 0; u8 * mp = &__bss_end; for( i = SP - (u16)&__bss_end + 1; i; i--){ if( *mp != FREE_MARK ) flag = 0; free += flag; *mp++ = FREE_MARK; } return free; } Nebenbei kann man damit auch feststellen, ob seit dem letzten Aufruf ein Reset erfolgte. Der erste Aufruf liefert nämlich immer 0 zurück, da der freie Stack ja noch nicht markiert wurde. Peter
Hallo Peter, ich habe das mal auf meinem Arduino getestet und bekomme sich dynamisch verändernde Werte. Mal weniger mal wieder mehr und wieder weniger usw. Ich rufe 3 Funktionen auf und danach immer jeweils immer deine stack_free() Ich dachte die Funktion gibt den bis dahin minimalen freien Stackspeicher aus. Weil das Muster 0x77 ja immer weiter rückt, weniger wird, im Stack. Also dürfte doch der Wert nie wieder größer werden. Oder?
@Peter: Du kannst folgendes verwenden; dürfte der Ursprung von vielen Versionen sein, die im Netz flottieren. Ist nicht kompatibel mit malloc et al. *mem-check.h*
1 | #ifndef MEM_CHECK_H
|
2 | #define MEM_CHECK_H
|
3 | |
4 | #include <stdint.h> |
5 | |
6 | extern uint16_t get_mem_unused (void); |
7 | |
8 | #endif /* MEM_CHECK_H */ |
*mem-check.c*
1 | #include <avr/io.h> // RAMEND |
2 | #include "mem-check.h" |
3 | |
4 | // Mask to init SRAM and check against
|
5 | #define MASK 0xaa
|
6 | |
7 | // __heap_start is defined in the linker script
|
8 | extern uint8_t __heap_start; |
9 | |
10 | uint16_t get_mem_unused (void) |
11 | {
|
12 | uint16_t unused = -1u; |
13 | |
14 | // Get end of static allocated RAM space (.data, .bss, .noinit, ...)
|
15 | uint8_t *p = &__heap_start; |
16 | |
17 | while (1) |
18 | {
|
19 | unused++; |
20 | // Mask written in __init_mem still intact?
|
21 | if (*p++ != MASK) |
22 | return unused; |
23 | }
|
24 | }
|
25 | |
26 | // Init RAM space after static allocated RAM with MASK.
|
27 | // Write accesses to the stack will overwrite this mask, so
|
28 | // we can probe later on and detect memory usage.
|
29 | // !!! Do never call this function !!!
|
30 | // !!! The Linker knows what to do with it !!!
|
31 | static void __attribute__ ((naked, used, unused, section (".init8"))) |
32 | memcheck_init_mem (void); |
33 | |
34 | static void memcheck_init_mem (void) |
35 | {
|
36 | // We investigate inline assembly to be independent
|
37 | // of optimization flags
|
38 | __asm__ __volatile ( |
39 | "ldi r30, lo8 (%[start])" "\n\t" |
40 | "ldi r31, hi8 (%[start])" "\n\t" |
41 | "ldi r24, lo8 (%[mask])" "\n\t" |
42 | "ldi r25, hi8 (%[end])" "\n\t" |
43 | "0:" "\n\t" |
44 | "st Z+, r24" "\n\t" |
45 | "cpi r30, lo8 (%[end])" "\n\t" |
46 | "cpc r31, r25" "\n\t" |
47 | "brlo 0b"
|
48 | :
|
49 | : [mask] "i" (MASK), |
50 | [end] "i" (RAMEND+1), |
51 | [start] "i" (&__heap_start) |
52 | );
|
53 | }
|
Hallo, @Peter: ich dachte du freust dich wenn es jemand verwendet. :-) Meine Frage haste aber nicht beantwortet. Soll dein Code den freien Stackbereich dynamisch ermitteln oder doch nicht? @Johann: der Code ist von rn-wissen.de, kenne ich, habs aber nicht zum laufen bekommen. Deshalb habe ich Peters verwendet. http://rn-wissen.de/wiki/index.php?title=Speicherverbrauch_bestimmen_mit_avr-gcc
Veit D. schrieb: > @Peter: ich dachte du freust dich wenn es jemand verwendet. :-) Es soll wohl mehr als einen „Peter“ geben auf der Welt.
Hallo, dachte der richtige Peter hätte sich nur als Gast kurz gemeldet. Das ist der Nachteil vom Gastlogin.
Peter D. schrieb: > So, ich habs nochmal überarbeitet, funktioniert super: Das sind die (klitze)kleinen Juwelen dieses Forums. Dinge die ich schon immer wissen wollte aber zu faul war diese selbst zu erforschen oder zu erarbeiten. Bin ja noch nicht so lang präsent hier im Forum, daher findet man so etwas nur durch Zufall beim Aufwärmen eines Threads. Also danke an den peda. Kaum 11 Jahre später ....
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.