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
Gast
#178636
"RAMEND - Heap-Start" müsste doch die Lösung sein, oder?
Gast
#178637
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
Gast
#178640
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.
Gast
#178641
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
Gast
#178643
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
Gast
#178645
Moment mal...wolltest Du "nur" die Größe des Stacks wissen? Dann müsste doch (RAMEND - SP) das gewünschte Ergebnis liefern.
Gast
#178646
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.
Gast
#178647
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?
Gast
#4476952
Leichenschänder!
@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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
*mem-check.c*
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
49 | |
50 | |
51 | |
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.
Gast
#4477707
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 ....
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.