Forum: Mikrocontroller und Digitale Elektronik AVR: Wie entfernt man malloc() und free()?


von Andreas J. (ajbln)


Lesenswert?

Hallo zusammen,

natürlich verwende ich (bewusst) keine dynamische Speicherverwaltung und 
entdecke aber gerade via avr-nm, dass die Funktionen malloc() und free() 
enthalten sind und jeweils unnötig Speicher fressen (304 + 274 bytes).

Ich suche mich gerade tot im Internet (-dead_strip etc.), finde aber 
absolut keinen Tipp, wie man die Funktionen los wird.
Eine leere Implementierung funktioniert nicht.
Auch die gcc-Flags "-why_live symbol_name" funktionieren beim AVR nicht.
Ich vermute ja irgendeine dumme Abhängigkeit zu irgendeiner 
Standard-Funktion...

Hat jemand irgendein Tipp?

Viele Grüße
Andreas

von ähm ja (Gast)


Lesenswert?

Hast du zufällig sprint Funktionen im Einsatz?

von Andreas J. (ajbln)


Lesenswert?

Nein, ich verwende eigentlich nur memcpy und memset sonst nichts. Und 
(noch) float-Funktionen...

von Andreas J. (ajbln)


Lesenswert?

Meine Includes sind:

<string.h>
<util/delay.h>
<avr/io.h>
<avr/interrupt.h>

von Axel S. (a-za-z0-9)


Lesenswert?

Andreas J. schrieb:
> natürlich verwende ich (bewusst) keine dynamische Speicherverwaltung und
> entdecke aber gerade via avr-nm, dass die Funktionen malloc() und free()
> enthalten sind und jeweils unnötig Speicher fressen (304 + 274 bytes).

Wenn sie enthalten sind, werden sie genutzt, wenn auch über Bande. Und 
ihr Speicherverbrauch ist dann nicht unnütz.

> Ich vermute ja irgendeine dumme Abhängigkeit zu irgendeiner
> Standard-Funktion...
>
> Hat jemand irgendein Tipp?

Disassembliere dein .elf Binary: "avr-objdump -d -S main.elf". Wenn du 
mit Debug-Symbolen compiliert hast (es gibt keinen Grund, der dagegen 
spräche) hast du nachher an jedem rcall den Namen der aufgerufenen 
Funktion als Kommentar. Und dann kannst du dich rückwärts durchhangeln, 
welche Funktion(alität) da auf malloc() und free() beharrt.

Nochwas. Ich habe mittlerweile eigentlich immer die folgenden Optionen 
in meinen CFLAGS: "-fdata-sections -ffunction-sections -flto 
-Wl,--gc-sections". Die Bedeutlung solltest du selber nachschlagen.

von Andreas J. (ajbln)


Lesenswert?

Super, Danke Dir für den Tipp!

malloc() wird nur von atexit() aufgerufen.

Und aus irgendeinem Grund ruft der Compiler am Ende des Konstruktors:

static TinyController controller(serial);

... die Funktion atexit() auf. Die einzige Stelle.

Da ich auch __cxa_guard_acquire etc. loswerden will, entsorge ich gerade 
alle pure virtual functions.
Reicht aber nicht, sie werden auch durch den Konstruktor erzwungen.

Ich probiere mal weiter...

von Andreas J. (ajbln)


Lesenswert?

Wenn ich static (s.o.) weglasse, ist auch atexit() und damit malloc() 
und free() weg.

Leider habe ich jetzt den Nachteil, dass der tatsächlichen 
Speicherverbrauch (Data Memory Usage) nicht mehr korrekt ist, da der 
Compiler den controller (s.o) nicht mehr "sieht"...

von Dr. Sommer (Gast)


Lesenswert?

Versuche mal folgende Compiler-Optionen:
-fno-use-cxa-atexit -ffunction-sections -fdata-sections -flto
Und folgende Linker-Optionen
-Wl,--gc-sections -flto

Das könnte die Funktionen wegoptimieren. Definiere eine leere Funktion 
"__cxa_pure_virtual" um die Behandlung rein virtueller Funktionen 
loszuwerden.

von Dr. Sommer (Gast)


Lesenswert?

Andreas J. schrieb:
> Leider habe ich jetzt den Nachteil, dass der tatsächlichen
> Speicherverbrauch (Data Memory Usage) nicht mehr korrekt ist, da der
> Compiler den controller (s.o) nicht mehr "sieht"...

Wie passt das zusammen? Wo ist die Instanz angelegt, in der main()?

von Andreas J. (ajbln)


Lesenswert?

Hilft leider nicht...

Sobald ich meine C++ - Instanz statisch mache, ziehe ich mir atexit() 
und damit malloc() rein. Das wird der implizite statische Destruktor 
sein.
Ist sie nicht statisch, dann generiert der Compiler ja auch einen 
impliziten Destruktor, bei diesem ist aber offensichtlich kein 
atexit() notwendig, und das ist jetzt meine Lösung.

Ich müsste dem Compiler sagen können, dass er keine (statischen) 
Destruktoren generieren soll, da wir ja eh eine Endlosschleife in main() 
haben.
Ich brauche überhaupt keine Destruktoren...

Aber ob es dafür ein Flag gibt? Ich finde jedenfalls keins...

von Andreas J. (ajbln)


Lesenswert?

Dr. Sommer schrieb:
> Wie passt das zusammen? Wo ist die Instanz angelegt, in der main()?

Ja in main.

von Andreas J. (ajbln)


Lesenswert?

1
#include "Kernel/Clock.h"
2
#include "Kernel/Serial.h"
3
#include "AqMod/TinyController.h"
4
5
#include <avr/interrupt.h>
6
#include <avr/io.h>
7
8
using namespace AJ::Kernel;
9
using namespace AJ::AqMod;
10
11
extern "C" void __cxa_pure_virtual() 
12
{ 
13
  while (true);
14
}
15
16
__extension__ typedef int __guard __attribute__((mode (__DI__)));
17
18
extern "C" int __cxa_guard_acquire(__guard* g)
19
{
20
  return !*(char *)(g);
21
}
22
23
extern "C" void __cxa_guard_release(__guard* g)
24
{
25
  *(char *)g = 1;
26
}
27
28
extern "C" void __cxa_guard_abort(__guard*)
29
{
30
}
31
32
int main(void)
33
{
34
  //byte resetReason = MCUSR;
35
  //MCUSR = 0x00;
36
  
37
  Clock::Initialize();
38
  static Serial serial(EBaudRate::BR9600);
39
  static TinyController controller(serial);
40
  
41
  sei();
42
  
43
    while (true) 
44
    {
45
    controller.Loop();
46
    }
47
}

Program Memory Usage   :  15624 bytes   95,4 % Full
Data Memory Usage   :  740 bytes   72,3 % Full
EEPROM Memory Usage   :  96 bytes   18,8 % Full
1
#include "Kernel/Clock.h"
2
#include "Kernel/Serial.h"
3
#include "AqMod/TinyController.h"
4
5
#include <avr/interrupt.h>
6
#include <avr/io.h>
7
8
using namespace AJ::Kernel;
9
using namespace AJ::AqMod;
10
11
extern "C" void __cxa_pure_virtual() 
12
{ 
13
  while (true);
14
}
15
16
int main(void)
17
{
18
  //byte resetReason = MCUSR;
19
  //MCUSR = 0x00;
20
  
21
  Clock::Initialize();
22
  Serial serial(EBaudRate::BR9600);
23
  TinyController controller(serial);
24
  
25
  sei();
26
  
27
    while (true) 
28
    {
29
    controller.Loop();
30
    }
31
}

Program Memory Usage   :  14860 bytes   90,7 % Full
Data Memory Usage   :  287 bytes   28,0 % Full
EEPROM Memory Usage   :  96 bytes   18,8 % Full

von Dr. Sommer (Gast)


Lesenswert?

Mach die Instanzen einfach global, vor das main(), egal ob mit static 
oder ohne. So wird das Speicher im .data Segment gezählt, und der 
Destruktor wird nie aufgerufen, und du brauchst die Locks nicht.

von Andreas J. (ajbln)


Lesenswert?

Die Instanzen global zu machen hatte ich mir auch schon überlegt (musste 
nur auf Late Binding umstellen, ist aber eh sauberer)...

Und merkwürdigerweise generiert der Compiler auch bei static keinen 
Destruktor mit atexit() mehr. Wobei ich static jetzt natürlich weglasse, 
das war ja nur ein "Trick", den echten Speicherverbrauch (trotz 
Instanzzierung in main()) zu sehen...

Danke Dir, wieder was gelernt ;) !

von Dr. Sommer (Gast)


Lesenswert?

Andreas J. schrieb:
> Und merkwürdigerweise generiert der Compiler auch bei static keinen
> Destruktor mit atexit() mehr.

Weil static bei globalen Variablen eine komplett andere Bedeutung hat, 
es verhindert einfach nur den Zugriff auf die Variable aus anderen 
Source-Files, denn die Variable erhält kein Symbol mehr. static 
innerhalb von Funktionen impliziert eine relativ komplexe Logik mithilfe 
von Locks die das Objekt erst dann anlegt, wenn die Funktion zum ersten 
Mal aufgerufen wird.

von Andreas J. (ajbln)


Lesenswert?

Ach, na klar hat static dann ne andere Bedeutung (ist ja peinlich, dass 
ich das vergessen habe :-)).

Und der zweite Teil erklärt auch, warum ich __cxa_guard_acquire() und 
co. nicht mehr brauche...

Danke nochmal!

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.